pax_global_header00006660000000000000000000000064141732634750014526gustar00rootroot0000000000000052 comment=59fdc3795b55a388cddac631d05b7b0e58c6d85c fluidsynth-2.2.5/000077500000000000000000000000001417326347500137255ustar00rootroot00000000000000fluidsynth-2.2.5/.azure/000077500000000000000000000000001417326347500151315ustar00rootroot00000000000000fluidsynth-2.2.5/.azure/azure-pipelines-android.yml000066400000000000000000000620621417326347500224140ustar00rootroot00000000000000 trigger: paths: include: - '*' exclude: - '.azure/azure-pipelines-mac.yml' - '.azure/azure-pipelines-vcpkg.yml' - '.azure/azure-pipelines-win.yml' - '.circleci/config.yml' - '.github/workflows/linux.yml' - '.github/workflows/sonarcloud.yml' - '.cirrus.yml' - 'README.md' parameters: - name: UseCache displayName: Use Dependency Cache type: boolean default: true values: - true - false schedules: - cron: "0 0 * * 1" displayName: 'Weekly Monday Midnight build without caching' branches: include: - master always: true variables: ICONV_VERSION: '1.16' # Use recent master libffi, because 3.3 is broken: checking host system type... Invalid configuration `arm-none-linux-eabi': machine `arm-none-linux' not recognized FFI_VERSION: 'dd5bd03075149d7cf8441875c1a344e8beb57dde' GETTEXT_VERSION: '0.21' #need to switch to meson build system to use a more recent version GLIB_VERSION: '2.58' GLIB_EXTRAVERSION: '3' OBOE_VERSION: '1.5.0' SNDFILE_VERSION: '1.0.31' INSTPATCH_VERSION: '1.1.6' VORBIS_VERSION: '1.3.7' OGG_VERSION: '1.3.5' OPUS_VERSION: '1.3.1' # flac 1.3.3 is completely broken: pkgconfig is incorrectly installed, compilation failure, etc.; use recent master instead FLAC_VERSION: '27c615706cedd252a206dd77e3910dfa395dcc49' # Android NDK sources and standalone toolchain is put here DEV: '$(System.DefaultWorkingDirectory)/android-build-root' # This is a symlink pointing to the real Android NDK # Must be the same as $ANDROID_NDK_HOME see: # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md NDK: '/usr/local/lib/android/sdk/ndk-bundle' # All the built binaries, libs and their headers will be installed here PREFIX: '$(DEV)/opt/android' # Prevent installing to lib64/ # This becomes important, if you would build on e.g. openSUSE LIBPATH0: '$(PREFIX)/lib' # The path of standalone NDK toolchain # Refer to https://developer.android.com/ndk/guides/standalone_toolchain.html NDK_TOOLCHAIN: '$(NDK)/toolchains/llvm/prebuilt/linux-x86_64/' # Don't mix up .pc files from your host and build target PKG_CONFIG_PATH: '$(LIBPATH0)/pkgconfig' # setting PKG_CONFIG_PATH alone does not seem to be enough to avoid mixing up with the host, also set PKG_CONFIG_LIBDIR PKG_CONFIG_LIBDIR: '$(PKG_CONFIG_PATH)' # Set Android target API level # when compiling with clang use at least 28 as this makes sure that android provides the posix_spawn functions, so the compilation of gettext will (should) work out of the box # it's probably a bug of gettext, if posix_spawn is not available it replaces it with its own implementation. Autotools of gettext set HAVE_POSIX_SPAWN==0 (which is correct) but for some reason REPLACE_POSIX_SPAWN==0 (which is wrong, as it should be 1). # # NOTE: API 24 is required because it provides fseeko() and ftello() required by libflac ANDROID_API: '24' # Tell configure what flags Android requires. # Turn Wimplicit-function-declaration into errors. Else autotools will be fooled when checking for available functions (that in fact are NOT available) and compilation will fail later on. # Also disable clangs integrated assembler, as the hand written assembly of libffi is not recognized by it, cf. https://crbug.com/801303 CFLAGS: "-fPIE -fPIC -I$(PREFIX)/include --sysroot=$(NDK_TOOLCHAIN)/sysroot -I$(NDK_TOOLCHAIN)/sysroot/usr/include -Werror=implicit-function-declaration" CXXFLAGS: $(CFLAGS) CPPFLAGS: $(CXXFLAGS) ARTIFACT_NAME: 'fluidsynth-android$(ANDROID_API)' jobs: - job: Android strategy: matrix: ARM: ARCH: 'arm' ANDROID_ARCH: 'armv7a' ANDROID_ABI_CMAKE: 'armeabi-v7a' ANDROID_TARGET_ABI: "eabi" # the --target to be used by autotools AUTOTOOLS_TARGET: "$(ARCH)-linux-android$(ANDROID_TARGET_ABI)" #AUTOTOOLS_TARGET: "$(ARCH)-none-linux-$(ANDROID_TARGET_ABI)" AARCH64: ARCH: 'aarch64' ANDROID_ARCH: 'aarch64' ANDROID_ABI_CMAKE: 'arm64-v8a' ANDROID_TARGET_ABI: AUTOTOOLS_TARGET: "$(ARCH)-none-linux-android" #AUTOTOOLS_TARGET: "$(ARCH)-none-linux" i686: ARCH: 'i686' ANDROID_ARCH: 'i686' ANDROID_ABI_CMAKE: 'x86' ANDROID_TARGET_ABI: AUTOTOOLS_TARGET: "$(ARCH)-pc-linux-android" #AUTOTOOLS_TARGET: "$(ARCH)-pc-linux" x86_64: ARCH: 'x86_64' ANDROID_ARCH: 'x86_64' ANDROID_ABI_CMAKE: 'x86_64' ANDROID_TARGET_ABI: AUTOTOOLS_TARGET: "$(ARCH)-pc-linux-android" #AUTOTOOLS_TARGET: "$(ARCH)-pc-linux" pool: vmImage: 'ubuntu-20.04' steps: - script: | set -ex mkdir -p $(DEV) displayName: 'mkdir $(DEV)' - script: | set -ex wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz tar zxf libiconv-${ICONV_VERSION}.tar.gz wget -O libffi-${FFI_VERSION}.tar.gz https://github.com/libffi/libffi/archive/${FFI_VERSION}.tar.gz tar zxf libffi-${FFI_VERSION}.tar.gz wget http://ftp.gnu.org/pub/gnu/gettext/gettext-${GETTEXT_VERSION}.tar.gz tar zxf gettext-${GETTEXT_VERSION}.tar.gz wget http://ftp.gnome.org/pub/gnome/sources/glib/${GLIB_VERSION}/glib-${GLIB_VERSION}.${GLIB_EXTRAVERSION}.tar.xz tar xf glib-${GLIB_VERSION}.${GLIB_EXTRAVERSION}.tar.xz wget -O oboe-${OBOE_VERSION}.tar.gz https://github.com/google/oboe/archive/${OBOE_VERSION}.tar.gz tar zxf oboe-${OBOE_VERSION}.tar.gz wget https://github.com/libsndfile/libsndfile/releases/download/${SNDFILE_VERSION}/libsndfile-${SNDFILE_VERSION}.tar.bz2 tar jxf libsndfile-${SNDFILE_VERSION}.tar.bz2 wget -O libinstpatch-${INSTPATCH_VERSION}.tar.gz https://github.com/swami/libinstpatch/archive/refs/tags/v${INSTPATCH_VERSION}.tar.gz tar zxf libinstpatch-${INSTPATCH_VERSION}.tar.gz wget https://github.com/xiph/vorbis/releases/download/v${VORBIS_VERSION}/libvorbis-${VORBIS_VERSION}.tar.gz tar zxf libvorbis-${VORBIS_VERSION}.tar.gz wget https://github.com/xiph/ogg/releases/download/v${OGG_VERSION}/libogg-${OGG_VERSION}.tar.gz tar zxf libogg-${OGG_VERSION}.tar.gz wget -O flac-${FLAC_VERSION}.tar.gz https://github.com/xiph/flac/archive/${FLAC_VERSION}.tar.gz tar xf flac-${FLAC_VERSION}.tar.gz wget -O opus-${OPUS_VERSION}.tar.gz https://github.com/xiph/opus/archive/refs/tags/v${OPUS_VERSION}.tar.gz tar xf opus-${OPUS_VERSION}.tar.gz displayName: 'Download Dependencies' workingDirectory: $(DEV) - task: Cache@2 inputs: key: '$(ARCH) | $(DEV)/*.tar.gz | cacheVersion3' path: '$(PREFIX)' cacheHitVar: 'CACHE_RESTORED' displayName: 'Cache fluidsynth dependency libraries' condition: and(not(in(variables['Build.Reason'], 'Schedule')), ${{ parameters.useCache }}) - script: | set -ex sudo apt remove --purge --auto-remove cmake wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main' displayName: 'Use recent CMake Version' condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) enabled: 'false' - script: | sudo apt-get update -y displayName: 'Update apt' condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) - script: | set -ex sudo -E apt-get -yq --no-install-suggests --no-install-recommends install gettext cmake zlib1g-dev autogen automake autoconf libtool pkg-config autotools-dev build-essential meson ninja-build displayName: 'apt-get install' condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) - script: | set -e # The cross-compile toolchain we use export ANDROID_TARGET=${ARCH}-linux-android${ANDROID_TARGET_ABI} echo "##vso[task.setvariable variable=ANDROID_TARGET]$ANDROID_TARGET" export ANDROID_TARGET_API=${ANDROID_ARCH}-linux-android${ANDROID_TARGET_ABI}${ANDROID_API} echo "##vso[task.setvariable variable=ANDROID_TARGET_API]$ANDROID_TARGET_API" # Add the standalone toolchain to the search path. export PATH=$PATH:${PREFIX}/bin:${PREFIX}/lib:${PREFIX}/include:${NDK_TOOLCHAIN}/bin echo "##vso[task.setvariable variable=PATH]$PATH" LIBPATH1=$(NDK_TOOLCHAIN)/sysroot/usr/lib LIBPATH2=$(NDK_TOOLCHAIN)/sysroot/usr/lib/$(ARCH)-linux-android$(ANDROID_TARGET_ABI)/$(ANDROID_API) LIBPATH3=$(NDK_TOOLCHAIN)/sysroot/usr/lib/$(ARCH)-linux-android$(ANDROID_TARGET_ABI) export LDFLAGS="-pie -Wl,-rpath-link=${LIBPATH1} -L${LIBPATH1} -Wl,-rpath-link=${LIBPATH2} -L${LIBPATH2} -Wl,-rpath-link=${LIBPATH3} -L${LIBPATH3} -Wl,-rpath-link=${LIBPATH0} -L${LIBPATH0}" echo "##vso[task.setvariable variable=LDFLAGS]$LDFLAGS" # Tell configure what tools to use. export AR=${ANDROID_TARGET}-ar echo "##vso[task.setvariable variable=AR]$AR" export AS=${ANDROID_TARGET_API}-clang echo "##vso[task.setvariable variable=AS]$AS" export CC=${ANDROID_TARGET_API}-clang echo "##vso[task.setvariable variable=CC]$CC" export CXX=${ANDROID_TARGET_API}-clang++ echo "##vso[task.setvariable variable=CXX]$CXX" export LD=ld.lld echo "##vso[task.setvariable variable=LD]$LD" export STRIP=${ANDROID_TARGET}-strip echo "##vso[task.setvariable variable=STRIP]$STRIP" export RANLIB=${ANDROID_TARGET}-ranlib echo "##vso[task.setvariable variable=RANLIB]$RANLIB" displayName: 'Set environment variables' - script: | set -ex pushd libiconv-${ICONV_VERSION} ./configure \ --host=${AUTOTOOLS_TARGET} \ --prefix=${PREFIX} \ --libdir=${LIBPATH0} \ --disable-rpath \ --enable-static \ --disable-shared \ --with-pic \ --disable-maintainer-mode \ --disable-silent-rules \ --disable-gtk-doc \ --disable-introspection \ --disable-nls make -j$((`nproc`+1)) make install popd displayName: 'Compile libiconv' workingDirectory: $(DEV) condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) - script: | set -ex pushd libffi-${FFI_VERSION} NOCONFIGURE=true autoreconf -v -i # install headers into the conventional ${PREFIX}/include rather than ${PREFIX}/lib/libffi-3.2.1/include. #sed -e '/^includesdir/ s/$(libdir).*$/$(includedir)/' -i include/Makefile.in #sed -e '/^includedir/ s/=.*$/=@includedir@/' -e 's/^Cflags: -I${includedir}/Cflags:/' -i libffi.pc.in ./configure --host=${AUTOTOOLS_TARGET} --prefix=${PREFIX} --libdir=${LIBPATH0} --enable-static --disable-shared make -j$((`nproc`+1)) make install popd displayName: 'Compile libffi' workingDirectory: $(DEV) condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) - script: | set -ex export PKGCFG=`which pkg-config` pushd glib-${GLIB_VERSION}.${GLIB_EXTRAVERSION} cat << EOF > cross-file.txt [host_machine] system = 'android' cpu_family = 'arm' cpu = 'arm' endian = 'little' [binaries] c = '${CC}' cpp = '${CXX}' ar = '${AR}' ld = '${LD}' strip = '${STRIP}' pkgconfig = '${PKGCFG}' [built-in options] c_std = 'c11' c_args = ['-fPIC','-I/home/git/work/ndk/builddir/out/include'] cpp_args = ['-fPIC','-I/home/git/work/ndk/builddir/out/include'] c_link_args = ['-fPIE','-L/home/git/work/ndk/builddir/out/lib'] pkg_config_path = '${PKG_CONFIG_PATH}' EOF cat << EOF > native-file.txt [host_machine] system = 'linux' cpu_family = 'x86_64' cpu = 'x86_64' endian = 'little' [properties] [binaries] c = ['false'] cpp = ['false'] objc = ['false'] objcpp = ['false'] ar = ['false'] pkgconfig = ['false'] cmake = ['false'] EOF cat native-file.txt cat cross-file.txt unset AR unset AS unset CC unset CFLAGS unset CPPFLAGS unset CPP unset CXXFLAGS unset CXX unset LDFLAGS unset LD unset STRIP meson \ --cross-file cross-file.txt \ --native-file native-file.txt \ --prefix=${PREFIX} \ --libdir=lib \ -Ddebug=false \ --default-library=both \ -Doptimization=s \ --backend=ninja \ --wrap-mode=nodownload \ -Dinternal_pcre=true \ -Dlibmount=false \ -Ddtrace=false \ -Diconv=auto \ -Dxattr=false \ -Dgtk_doc=false \ _builddir \ . ninja popd displayName: 'Compile glib (meson)' workingDirectory: $(DEV) enabled: 'false' - script: | pushd glib-${GLIB_VERSION}.${GLIB_EXTRAVERSION} cat _builddir/meson-logs/meson-log.txt popd displayName: 'Meson LOG' workingDirectory: $(DEV) condition: failed() enabled: 'false' - script: | set -ex pushd gettext-${GETTEXT_VERSION} ./configure \ --host=${AUTOTOOLS_TARGET} \ --prefix=${PREFIX} \ --libdir=${LIBPATH0} \ --disable-rpath \ --disable-libasprintf \ --disable-java \ --disable-native-java \ --disable-openmp \ --disable-curses \ --enable-static \ --disable-shared \ --with-pic \ --disable-maintainer-mode \ --disable-silent-rules \ --disable-gtk-doc \ --disable-introspection make -j$((`nproc`+1)) make install popd displayName: 'Compile gettext' workingDirectory: $(DEV) condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) - script: | set -ex pushd glib-${GLIB_VERSION}.${GLIB_EXTRAVERSION} cat << EOF > android.cache glib_cv_long_long_format=ll glib_cv_stack_grows=no glib_cv_sane_realloc=yes glib_cv_have_strlcpy=no glib_cv_va_val_copy=yes glib_cv_rtldglobal_broken=no glib_cv_uscore=no glib_cv_monotonic_clock=no ac_cv_func_nonposix_getpwuid_r=no ac_cv_func_posix_getpwuid_r=no ac_cv_func_posix_getgrgid_r=no glib_cv_use_pid_surrogate=yes ac_cv_func_printf_unix98=no ac_cv_func_vsnprintf_c99=yes ac_cv_func_realloc_0_nonnull=yes ac_cv_func_realloc_works=yes EOF # Unfortunately, libffi is not linked against libgobject when compiling for aarch64, leading to the following error: # # /bin/bash ../libtool --tag=CC --mode=link aarch64-linux-android23-clang -Wall -Wstrict-prototypes -Wno-bad-function-cast -Werror=declaration-after-statement -Werror=missing-prototypes -Werror=implicit-function-declaration -Werror=pointer-arith -Werror=init-self -Werror=format=2 -Werror=missing-include-dirs -fPIE -fPIC -I/home/vsts/work/1/s/android-build-root/opt/android/include --sysroot=/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot -I/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/include -Werror=implicit-function-declaration -fno-integrated-as -fno-strict-aliasing -pie -Wl,-rpath-link=-I/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/lib -L/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/lib -L/home/vsts/work/1/s/android-build-root/opt/android/lib -L/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//lib -o gobject-query gobject-query.o ./libgobject-2.0.la ../glib/libglib-2.0.la -lintl -liconv # libtool: link: aarch64-linux-android23-clang -Wall -Wstrict-prototypes -Wno-bad-function-cast -Werror=declaration-after-statement -Werror=missing-prototypes -Werror=implicit-function-declaration -Werror=pointer-arith -Werror=init-self -Werror=format=2 -Werror=missing-include-dirs -fPIE -fPIC -I/home/vsts/work/1/s/android-build-root/opt/android/include --sysroot=/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot -I/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/include -Werror=implicit-function-declaration -fno-integrated-as -fno-strict-aliasing -pie -Wl,-rpath-link=-I/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/lib -o .libs/gobject-query gobject-query.o -L/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/lib -L/home/vsts/work/1/s/android-build-root/opt/android/lib -L/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//lib ./.libs/libgobject-2.0.so ../glib/.libs/libglib-2.0.so /home/vsts/work/1/s/android-build-root/opt/android/lib/libintl.so /home/vsts/work/1/s/android-build-root/opt/android/lib/libiconv.so -pthread -L/home/vsts/work/1/s/android-build-root/opt/android/lib # /usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//bin/../lib/gcc/aarch64-linux-android/4.9.x/../../../../aarch64-linux-android/bin/ld: warning: libffi.so, needed by ./.libs/libgobject-2.0.so, not found (try using -rpath or -rpath-link) # ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_sint32@LIBFFI_BASE_8.0' # ./.libs/libgobject-2.0.so: undefined reference to `ffi_prep_cif@LIBFFI_BASE_8.0' # # So, just add it to LDFLAGS to make sure it's always linked. # libz.so is also missing... #FFILIB=`pkg-config --libs libffi` #echo ${FFILIB} #export LDFLAGS="${LDFLAGS} ${FFILIB} -lz" #unset FFILIB chmod a-x android.cache NOCONFIGURE=true ./autogen.sh ./configure \ --host=${ANDROID_TARGET} \ --prefix=${PREFIX} \ --libdir=${LIBPATH0} \ --disable-dependency-tracking \ --cache-file=android.cache \ --enable-included-printf \ --with-pcre=no \ --enable-libmount=no \ --enable-xattr=no \ --with-libiconv=gnu \ --disable-static \ --enable-shared \ --with-pic \ --disable-maintainer-mode \ --disable-silent-rules make -j$((`nproc`+1)) make install popd displayName: 'Compile glib' workingDirectory: $(DEV) condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) - template: cmake-android.yml parameters: sourceDir: 'libogg-$(OGG_VERSION)' cmakeArgs: '-DINSTALL_DOCS=0' - script: | ls -la libogg-${OGG_VERSION}/build/CMakeFiles/ cat libogg-${OGG_VERSION}/build/CMakeFiles/CMakeError.log true displayName: 'Print OGG Cmake Error Log' condition: always() workingDirectory: $(DEV) - template: cmake-android.yml parameters: sourceDir: 'libvorbis-$(VORBIS_VERSION)' - script: | ls -la libvorbis-${VORBIS_VERSION}/build/CMakeFiles/ cat libvorbis-${VORBIS_VERSION}/build/CMakeFiles/CMakeError.log true displayName: 'Print Vorbis Cmake Error Log' condition: always() workingDirectory: $(DEV) # flac uses c99 macros, but doesnt specify a standard, so we need to do it explicitly. # On i686, they invoke yasm with -fstack-protector-strong flag... turn off asm optimizations. - template: cmake-android.yml parameters: sourceDir: 'flac-$(FLAC_VERSION)' cmakeArgs: '-DCMAKE_C_STANDARD=99 -DCMAKE_C_STANDARD_REQUIRED=1 -DWITH_ASM=0 -DBUILD_CXXLIBS=0 -DBUILD_PROGRAMS=0 -DBUILD_EXAMPLES=0 -DBUILD_DOCS=0 -DINSTALL_MANPAGES=0' - script: | ls -la flac-${FLAC_VERSION}/build/CMakeFiles/ cat flac-${FLAC_VERSION}/build/CMakeFiles/CMakeError.log true displayName: 'Print FLAC Cmake Error Log' condition: always() workingDirectory: $(DEV) # another broken xiph project that doesn't specify the C standard and keeps complaining of you don't have C99 - template: cmake-android.yml parameters: sourceDir: 'opus-$(OPUS_VERSION)' cmakeArgs: '-DBUILD_PROGRAMS=0 -DOPUS_MAY_HAVE_NEON=1 -DCMAKE_C_STANDARD=99 -DCMAKE_C_STANDARD_REQUIRED=1' - script: | ls -la opus-${OPUS_VERSION}/build/CMakeFiles/ cat opus-${OPUS_VERSION}/build/CMakeFiles/CMakeError.log cat opus-${OPUS_VERSION}/build/CMakeFiles/CMakeOutput.log true displayName: 'Print OPUS Cmake Error Log' condition: always() workingDirectory: $(DEV) - template: cmake-android.yml parameters: sourceDir: 'libsndfile-$(SNDFILE_VERSION)' cmakeArgs: '-DBUILD_PROGRAMS=0 -DBUILD_EXAMPLES=0' - script: | ls -la libsndfile-${SNDFILE_VERSION}/build/CMakeFiles/ cat libsndfile-${SNDFILE_VERSION}/build/CMakeFiles/CMakeError.log true displayName: 'Print libsndfile Cmake Error Log' condition: always() workingDirectory: $(DEV) - template: cmake-android.yml parameters: sourceDir: 'oboe-$(OBOE_VERSION)' installCommand: 'cp liboboe.* ${PREFIX}/lib/ && cp -ur ../include/oboe ${PREFIX}/include' - script: | set -ex # create a custom pkgconfig file for oboe to allow fluidsynth to find it cat << EOF > ${PKG_CONFIG_PATH}/oboe-1.0.pc prefix=${PREFIX} exec_prefix=\${prefix} libdir=\${prefix}/lib includedir=\${prefix}/include Name: Oboe Description: Oboe library Version: ${OBOE_VERSION} Libs: -L\${libdir} -loboe -landroid -llog Cflags: -I\${includedir} EOF cat ${PKG_CONFIG_PATH}/oboe-1.0.pc displayName: 'Create fake oboe.pc' workingDirectory: $(DEV) condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) - template: cmake-android.yml parameters: sourceDir: 'libinstpatch-$(INSTPATCH_VERSION)' # finally, compile fluidsynth - template: cmake-android.yml parameters: workDir: '$(System.DefaultWorkingDirectory)' sourceDir: '.' condition: succeeded() cmakeArgs: '-Denable-opensles=1 -Denable-floats=1 -Denable-oboe=1 -Denable-dbus=0 -Denable-oss=0' installCommand: '' - script: | set -x export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(PREFIX)/lib:$(NDK_TOOLCHAIN)/sysroot/usr/lib/$(ARCH)-linux-android$(ANDROID_TARGET_ABI)/$(ANDROID_API) pushd build make -j$((`nproc`+1)) check ldd test/test_sample_cache popd displayName: 'Execute fluidsynth unit test' condition: and(succeeded(), in(variables['ARCH'], 'x86_64', 'i686')) enabled: 'false' - script: | set -ex pushd build make install popd displayName: 'Install fluidsynth' - template: cmake-android.yml parameters: workDir: '$(System.DefaultWorkingDirectory)/doc/android/fluidsynth-assetloader/' sourceDir: '.' condition: succeeded() installCommand: 'cp *.so ${PREFIX}/lib/' - script: | ls -Rg $(PREFIX) displayName: 'Show cross-compiled files in $(PREFIX)' condition: always() - script: | set -ex # use ANDROID_ABI_CMAKE so libs can be simply copied to the archive contents in src/main/jniLibs mkdir -p $(Build.ArtifactStagingDirectory)/lib/$(ANDROID_ABI_CMAKE) cd $(Build.ArtifactStagingDirectory)/lib/$(ANDROID_ABI_CMAKE) cp -LR $(PREFIX)/lib/* . ls -Rg . rm -rf *.dll *.alias gettext/ libtextstyle.* *.a *.la rm -f *.so.* mkdir -p $(Build.ArtifactStagingDirectory)/include cd $(Build.ArtifactStagingDirectory)/include cp -a $(PREFIX)/include/fluidsynth* . displayName: 'Collecting artifacts' - script: | set -ex ls libFLAC.so ls libfluidsynth-assetloader.so ls libfluidsynth.so ls libgio-2.0.so ls libglib-2.0.so ls libgmodule-2.0.so ls libgobject-2.0.so ls libgthread-2.0.so ls libinstpatch-1.0.so ls liboboe.so ls libogg.so ls libopus.so ls libsndfile.so ls libvorbis.so ls libvorbisenc.so ls libvorbisfile.so displayName: 'Verify all libs exist' workingDirectory: '$(PREFIX)/lib' - task: PublishBuildArtifacts@1 displayName: 'Publishing Artefacts for Android API$(ANDROID_API) $(ANDROID_ABI_CMAKE)' inputs: PathtoPublish: '$(Build.ArtifactStagingDirectory)' ArtifactName: '$(ARTIFACT_NAME)' publishLocation: 'Container' - script: | set -ex # as very last step before creating the pipeline cache, remove fluidsynth pushd build make uninstall popd displayName: 'Uninstall fluidsynth' fluidsynth-2.2.5/.azure/azure-pipelines-mac.yml000066400000000000000000000035571417326347500215400ustar00rootroot00000000000000 trigger: paths: include: - '*' exclude: - '.azure/azure-pipelines-android.yml' - '.azure/azure-pipelines-vcpkg.yml' - '.azure/azure-pipelines-win.yml' - '.circleci/config.yml' - '.github/workflows/linux.yml' - '.github/workflows/sonarcloud.yml' - '.cirrus.yml' - 'README.md' jobs: - job: macOS strategy: matrix: UnixLibs: imageName: 'macos-11' CMakeFlags: '-Denable-framework=0' 10_15: imageName: 'macOS-10.15' CMakeFlags: '' 11_0: imageName: 'macos-11' CMakeFlags: '' pool: vmImage: $(imageName) # recommended by https://github.com/Homebrew/brew/issues/2491#issuecomment-294207661 # brew update || brew update # brew upgrade $PACKAGES steps: - script: | set -ex PACKAGES="glib gobject-introspection libsndfile pkg-config jack dbus-glib pulseaudio portaudio sdl2 libomp" brew install $PACKAGES displayName: 'Prerequisites' - script: | set -ex mkdir build && cd build export PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig" cmake -Werror=dev $(CMakeFlags) -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. make -j3 displayName: 'Compile fluidsynth' - script: | set -ex cd build make -j3 check displayName: 'Execute Unittests' - script: | set -ex cd build make -j3 demo displayName: 'Compile demos' - script: | set -ex cd build sudo make install rm -f install_manifest.txt displayName: 'Install fluidsynth to default location' - script: | set -ex cd build cmake -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) .. make install displayName: 'Install fluidsynth to artifact dir' fluidsynth-2.2.5/.azure/azure-pipelines-vcpkg.yml000066400000000000000000000125471417326347500221110ustar00rootroot00000000000000 trigger: paths: include: - '*' exclude: - '.azure/azure-pipelines-android.yml' - '.azure/azure-pipelines-mac.yml' - '.azure/azure-pipelines-win.yml' - '.circleci/config.yml' - '.github/workflows/linux.yml' - '.github/workflows/sonarcloud.yml' - '.cirrus.yml' - 'README.md' parameters: - name: UseCache displayName: Use Dependency Cache type: boolean default: true schedules: - cron: "0 0 * * 1" displayName: 'Weekly Monday Midnight build without caching' branches: include: - master always: true variables: toolset: 'v142' generator: 'Visual Studio 16 2019' configuration: 'RelWithDebInfo' VCPKG_REVISION: 'e6dcc079c81161786eb7b052209a2047e79f2c6c' jobs: - job: vcpkg workspace: clean: all strategy: matrix: ARM: platform: 'arm' cmake_platform: 'ARM' ARM64: platform: 'arm64' cmake_platform: 'ARM64' x86: platform: 'x86' cmake_platform: 'Win32' x64: platform: 'x64' cmake_platform: 'x64' pool: vmImage: 'windows-2019' steps: - task: Cache@2 displayName: "Cache vcpkg's packages" condition: and(not(in(variables['Build.Reason'], 'Schedule')), ${{ parameters.useCache }}) inputs: key: $(VCPKG_REVISION) | "$(platform)" path: '$(VCPKG_INSTALLATION_ROOT)\installed' cacheHitVar: CACHE_RESTORED - bash: | set -ex echo $(generator) echo $(toolset) # choco upgrade ninja -y # ninja --version cmake --version rm -rf C:/Strawberry/perl/bin/pkg-config* choco install --svc --sdc -i pkgconfiglite # chocoTask=$! # manually update vcpkg cd $VCPKG_INSTALLATION_ROOT # git checkout master git remote -v git fetch --tags --prune --progress origin git checkout --force $(VCPKG_REVISION) sed -i 's/arm64/arm/g' ports/glib/portfile.cmake ./bootstrap-vcpkg.sh # wait $chocoTask which pkg-config displayName: 'Update vcpkg' - task: DownloadBuildArtifacts@0 inputs: buildType: specific # https://dev.azure.com/tommbrt/_apis/projects?api-version=5.0 project: 'd3638885-de4a-4ce7-afe7-f237ae461c07' pipeline: 1 artifactName: libinstpatch-$(cmake_platform) downloadPath: '$(Agent.TempDirectory)' displayName: 'Get libinstpatch' condition: and(succeeded(), and(ne(variables['platform'], 'arm'), ne(variables['platform'], 'arm64'))) - task: CopyFiles@2 inputs: SourceFolder: '$(Agent.TempDirectory)\libinstpatch-$(cmake_platform)' Contents: '**' TargetFolder: '$(VCPKG_INSTALLATION_ROOT)\installed\$(platform)-windows' displayName: 'Install libinstpatch' condition: and(succeeded(), and(ne(variables['platform'], 'arm'), ne(variables['platform'], 'arm64'))) - bash: | set -ex vcpkg install glib:$(platform)-windows libsndfile:$(platform)-windows displayName: 'vcpkg build Dependencies' condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) - bash: | set -ex cd $VCPKG_INSTALLATION_ROOT/installed/$(platform)-windows/ pwd rm -rf include/FLAC* include/ffi* include/iconv* include/opus tools share ls -Rg . pkg-config --list-all pkg-config --cflags libinstpatch-1.0 || true displayName: 'Cleanup dependencies' condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) - bash: | set -ex mkdir build cd build cmake -Werror=dev -G "$(generator)" -A "$(cmake_platform)" -T "$(toolset)" -Denable-readline=0 -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_INSTALL_PREFIX=$VCPKG_INSTALLATION_ROOT/installed/$(platform)-windows -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. cmake --build . --config $(configuration) --parallel 3 displayName: 'Compile fluidsynth' - bash: | set -ex cmake --build build --config $(configuration) --target check --parallel 3 displayName: 'Execute Unittests' condition: and(succeeded(), and(ne(variables['platform'], 'arm'), ne(variables['platform'], 'arm64'))) - bash: | set -ex cmake --build build --config $(configuration) --target demo --parallel 3 displayName: 'Compile demos' - script: | @ECHO ON cd build cmake --build . --config $(configuration) --target install || exit -1 REM del bin\concrt*.dll REM del $(Build.ArtifactStagingDirectory)\bin\vcruntime*.dll REM del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll REM del $(Build.ArtifactStagingDirectory)\lib\instpatch*.lib REM del $(Build.ArtifactStagingDirectory)\lib\pkgconfig\libinstpatch*.pc REM rd $(Build.ArtifactStagingDirectory)\include\libinstpatch-2 /s /q displayName: 'fluidsynth install' - task: CopyFiles@2 inputs: SourceFolder: '$(VCPKG_INSTALLATION_ROOT)/installed/$(platform)-windows/' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)' CleanTargetFolder: true displayName: 'Copy Binaries to Artifact Dir' - task: PublishBuildArtifacts@1 inputs: pathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: fluidsynth-vcpkg-$(platform) fluidsynth-2.2.5/.azure/azure-pipelines-win.yml000066400000000000000000000247401417326347500215720ustar00rootroot00000000000000 trigger: paths: include: - '*' exclude: - '.azure/azure-pipelines-android.yml' - '.azure/azure-pipelines-mac.yml' - '.azure/azure-pipelines-vcpkg.yml' - '.circleci/config.yml' - '.github/workflows/linux.yml' - '.github/workflows/sonarcloud.yml' - '.cirrus.yml' - 'README.md' jobs: - job: WindowsXP variables: toolset: v141_xp strategy: matrix: x86: platform: Win32 gtk-bundle: $(gtk-bundle-x86) libsndfile-url: $(libsndfile-url-x86) artifact-prefix: "fluidsynth" x64: platform: x64 gtk-bundle: $(gtk-bundle-x64) libsndfile-url: $(libsndfile-url-x64) artifact-prefix: "fluidsynth" pool: vmImage: 'windows-2019' steps: - task: DownloadBuildArtifacts@0 inputs: buildType: specific # https://dev.azure.com/tommbrt/_apis/projects?api-version=5.0 project: 'd3638885-de4a-4ce7-afe7-f237ae461c07' pipeline: 1 artifactName: libinstpatch-$(platform) downloadPath: '$(Build.ArtifactStagingDirectory)' displayName: 'Get libinstpatch' - script: | @ECHO ON mkdir d:\deps || exit -1 cd d:\deps || exit -1 curl -LfsS -o gtk-bundle-dev.zip $(gtk-bundle) || exit -1 curl -LfsS -o libsndfile-dev.zip $(libsndfile-url) || exit -1 7z x -aos -- gtk-bundle-dev.zip > NUL || exit -1 7z x -aos -- libsndfile-dev.zip > NUL || exit -1 REM need to fix the naming of libsndfile otherwise the linker won't find it mv lib\libsndfile-1.lib lib\sndfile.lib || exit -1 mv lib\libsndfile-1.def lib\sndfile.def || exit -1 cd $(Build.ArtifactStagingDirectory)\libinstpatch-$(platform) cp -rf * d:\deps\ mv -f * .. cd .. rmdir $(Build.ArtifactStagingDirectory)\libinstpatch-$(platform)\ DEL /F C:\Strawberry\perl\bin\pkg-config.bat displayName: 'Prerequisites' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" pkg-config --list-all mkdir build && cd build || exit -1 cmake -Werror=dev -A $(platform) -T $(toolset) -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) -Denable-readline=0 -Denable-floats=1 -Denable-jack=0 -Denable-sdl2=0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 -Dwindows-version=0x0501 .. || exit -1 cmake --build . --config Release --parallel 3 || exit -1 displayName: 'Compile fluidsynth' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" cd build || exit -1 cmake --build . --config Release --target check || exit -1 displayName: 'Execute Unittests' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" cd build || exit -1 cmake --build . --config Release --target demo || exit -1 displayName: 'Compile demos' - script: | @ECHO ON cd build cmake --build . --config Release --target install || exit -1 del $(Build.ArtifactStagingDirectory)\bin\concrt*.dll del $(Build.ArtifactStagingDirectory)\bin\vcruntime*.dll del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll del $(Build.ArtifactStagingDirectory)\lib\instpatch*.lib del $(Build.ArtifactStagingDirectory)\lib\pkgconfig\libinstpatch*.pc rd $(Build.ArtifactStagingDirectory)\include\libinstpatch-2 /s /q displayName: 'Copy Artifacts' - task: PublishBuildArtifacts@1 inputs: pathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: $(artifact-prefix)-$(platform) - job: Windows10 variables: toolset: v142 gtk-bundle: $(gtk-bundle-x64) libsndfile-url: $(libsndfile-url-x64) strategy: matrix: default: CMAKE_FLAGS: CMAKE_CONFIG: Release debug_prof: CMAKE_FLAGS: -Denable-profiling=1 -Denable-trap-on-fpe=1 -Denable-fpe-check=1 CMAKE_CONFIG: Debug no_network: CMAKE_FLAGS: -Denable-network=0 CMAKE_CONFIG: Release static_lib: CMAKE_FLAGS: -DBUILD_SHARED_LIBS=0 CMAKE_CONFIG: Release minimal: CMAKE_FLAGS: -Denable-ipv6=0 -Denable-network=0 -Denable-aufile=0 -Denable-threads=0 -Denable-winmidi=0 -Denable-waveout=0 -Denable-dsound=0 -Denable-libsndfile=0 -Denable-floats=1 CMAKE_CONFIG: Release pool: vmImage: 'windows-2019' steps: - script: | @ECHO ON mkdir d:\deps || exit -1 cd d:\deps || exit -1 curl -LfsS -o gtk-bundle-dev.zip $(gtk-bundle) || exit -1 curl -LfsS -o libsndfile-dev.zip $(libsndfile-url) || exit -1 7z x -aos -- gtk-bundle-dev.zip > NUL || exit -1 7z x -aos -- libsndfile-dev.zip > NUL || exit -1 REM need to fix the naming of libsndfile otherwise the linker won't find it mv lib\libsndfile-1.lib lib\sndfile.lib || exit -1 mv lib\libsndfile-1.def lib\sndfile.def || exit -1 DEL /F C:\Strawberry\perl\bin\pkg-config.bat displayName: 'Prerequisites' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" mkdir build && cd build || exit -1 cmake -Werror=dev -A x64 -T $(toolset) -DCMAKE_BUILD_TYPE=$(CMAKE_CONFIG) -DCMAKE_VERBOSE_MAKEFILE=1 $(CMAKE_FLAGS) -DNO_GUI=1 -Dwindows-version=0x0A00 -Denable-jack=0 -Denable-pulseaudio=0 -Denable-ladspa=0 -Denable-dbus=0 -Denable-readline=0 -Denable-sdl2=0 -Denable-libinstpatch=0 .. || exit -1 cmake --build . --config $(CMAKE_CONFIG) --parallel 3 || exit -1 displayName: 'Compile fluidsynth' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" cd build || exit -1 cmake --build . --config $(CMAKE_CONFIG) --target check || exit -1 displayName: 'Execute Unittests' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" cd build || exit -1 cmake --build . --config $(CMAKE_CONFIG) --target demo || exit -1 displayName: 'Compile demos' - job: WindowsMinGW variables: artifact-prefix: "fluidsynth-mingw" strategy: matrix: x64: CMAKE_FLAGS: platform: x64 gtk-bundle: $(gtk-bundle-x64) libsndfile-url: $(libsndfile-url-x64) mingw-url: $(mingw-url-x64) x64-static: CMAKE_FLAGS: '-DBUILD_SHARED_LIBS=0' platform: x64 gtk-bundle: $(gtk-bundle-x64) libsndfile-url: $(libsndfile-url-x64) mingw-url: $(mingw-url-x64) pool: vmImage: 'windows-2019' steps: - task: DownloadBuildArtifacts@0 inputs: buildType: specific # https://dev.azure.com/tommbrt/_apis/projects?api-version=5.0 project: 'd3638885-de4a-4ce7-afe7-f237ae461c07' pipeline: 1 artifactName: libinstpatch-$(platform) downloadPath: '$(Build.ArtifactStagingDirectory)' displayName: 'Get libinstpatch' - script: | @ECHO ON mkdir d:\deps || exit -1 cd d:\deps || exit -1 curl -LfsS -o gtk-bundle-dev.zip $(gtk-bundle) || exit -1 curl -LfsS -o libsndfile-dev.zip $(libsndfile-url) || exit -1 curl -LfsS -o mingw.zip $(mingw-url) || exit -1 7z x -aos -- gtk-bundle-dev.zip > NUL || exit -1 7z x -aos -- libsndfile-dev.zip > NUL || exit -1 7z x -aos -- mingw.zip > NUL || exit -1 rm *.zip REM need to fix the naming of libsndfile otherwise the linker won't find it mv lib\libsndfile-1.lib lib\sndfile.lib || exit -1 mv lib\libsndfile-1.def lib\sndfile.def || exit -1 cd mingw*\ && cp -rf * .. && cd .. && rm -rf mingw* || exit -1 cd $(Build.ArtifactStagingDirectory)\libinstpatch-$(platform) && cp -rf * d:\deps\ && mv -f * .. && cd .. && rmdir $(Build.ArtifactStagingDirectory)\libinstpatch-$(platform)\ || exit -1 DEL /F C:\Strawberry\perl\bin\pkg-config.bat displayName: 'Prerequisites' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" REM remove that path from PATH to make sure sh.exe is not found (cmake will complain otherwise) set PATH=%PATH:C:\Program Files\Git\bin;=% set PATH=%PATH:C:\Program Files\Git\usr\bin;=% pkg-config --list-all mkdir build && cd build || exit -1 cmake -Werror=dev -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) $(CMAKE_FLAGS) -Denable-readline=0 -Denable-floats=1 -Denable-jack=0 -Denable-pulseaudio=0 -Denable-ladspa=0 -Denable-dbus=0 -Denable-sdl2=0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. || exit -1 mingw32-make.exe -j3 all || exit -1 displayName: 'Compile fluidsynth' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" REM remove that path from PATH to make sure sh.exe is not found (cmake will complain otherwise) set PATH=%PATH:C:\Program Files\Git\bin;=% set PATH=%PATH:C:\Program Files\Git\usr\bin;=% cd build || exit -1 mingw32-make.exe -j4 check || exit -1 displayName: 'Execute Unittests' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" REM remove that path from PATH to make sure sh.exe is not found (cmake will complain otherwise) set PATH=%PATH:C:\Program Files\Git\bin;=% set PATH=%PATH:C:\Program Files\Git\usr\bin;=% cd build || exit -1 mingw32-make.exe -j4 demo || exit -1 displayName: 'Compile demos' - script: | @ECHO ON cd build mingw32-make.exe install || exit -1 xcopy d:\deps\bin\libgcc_s_sjlj-1.dll $(Build.ArtifactStagingDirectory)\bin xcopy d:\deps\bin\libgcc_s_seh-1.dll $(Build.ArtifactStagingDirectory)\bin xcopy d:\deps\bin\libgomp-1.dll $(Build.ArtifactStagingDirectory)\bin xcopy d:\deps\bin\libstdc++-6.dll $(Build.ArtifactStagingDirectory)\bin xcopy d:\deps\bin\libwinpthread-1.dll $(Build.ArtifactStagingDirectory)\bin REM xcopy test $(Build.ArtifactStagingDirectory)\bin /s del $(Build.ArtifactStagingDirectory)\bin\concrt*.dll del $(Build.ArtifactStagingDirectory)\bin\vcruntime*.dll del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll del $(Build.ArtifactStagingDirectory)\lib\instpatch*.lib del $(Build.ArtifactStagingDirectory)\lib\pkgconfig\libinstpatch*.pc rd $(Build.ArtifactStagingDirectory)\include\libinstpatch-2 /s /q displayName: 'Copy Artifacts' - task: PublishBuildArtifacts@1 inputs: pathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: $(artifact-prefix)-$(platform) fluidsynth-2.2.5/.azure/cmake-android.yml000066400000000000000000000036121417326347500203540ustar00rootroot00000000000000 parameters: - name: cmakeArgs type: string default: '' - name: sourceDir type: string default: '' - name: workDir type: string default: $(DEV) - name: condition type: string default: ne(variables.CACHE_RESTORED, 'true') - name: installCommand type: string default: 'make install' steps: - script: | set -ex pushd ${{ parameters.sourceDir }} mkdir -p build pushd build # Invoke cmake in the most correctest way I've could find while try and erroring: # # The biggest pain point is that CMake does not seem to respect our existing cross compilation CFLAGS and LDFLAGS. # Hence we are passing them manually, once via Android flags and once for "Required" flags. The latter is necessary # to let cmake correctly probe for any existing header, function, library, etc. # Watch out: Sometimes the flags are passed as ;-limited list! cmake -G "Unix Makefiles" \ -DCMAKE_MAKE_PROGRAM=make \ -DCMAKE_TOOLCHAIN_FILE=${NDK}/build/cmake/android.toolchain.cmake \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DANDROID_NATIVE_API_LEVEL=${ANDROID_API} \ -DANDROID_ABI=${ANDROID_ABI_CMAKE} \ -DANDROID_TOOLCHAIN=${CC} \ -DANDROID_NDK=${NDK} \ -DANDROID_COMPILER_FLAGS="${CFLAGS// /;}" \ -DANDROID_LINKER_FLAGS="${LDFLAGS// /;}" \ -DANDROID_STL="c++_shared" \ -DCMAKE_REQUIRED_FLAGS="${CFLAGS}" \ -DCMAKE_REQUIRED_LINK_OPTIONS="${LDFLAGS// /;}" \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DCMAKE_STAGING_PREFIX=${PREFIX} \ -DCMAKE_VERBOSE_MAKEFILE=1 \ -DBUILD_SHARED_LIBS=1 \ -DLIB_SUFFIX="" \ ${{ parameters.cmakeArgs }} .. make -j$((`nproc`+1)) ${{ parameters.installCommand }} popd popd displayName: 'Compile ${{ parameters.sourceDir }}' workingDirectory: ${{ parameters.workDir }} condition: ${{ parameters.condition }} fluidsynth-2.2.5/.circleci/000077500000000000000000000000001417326347500155605ustar00rootroot00000000000000fluidsynth-2.2.5/.circleci/config.yml000066400000000000000000000056141417326347500175560ustar00rootroot00000000000000version: 2.1 orbs: android: circleci/android@0.2.1 jobs: build: working_directory: ~/code docker: - image: circleci/android:api-30 environment: JVM_OPTS: -Xmx3200m steps: - run: name: Setup Git email and user for Cerbero command: git config --global user.email "ci@beatscratch.io" && git config --global user.name "CI testing" # - android/install-ndk: # ndk-version: android-ndk-r18b # ndk-sha: 500679655da3a86aecf67007e8ab230ea9b4dd7b - run: name: Install FluidSynth build dependencies command: sudo apt-get update && sudo apt-get install autotools-dev automake autoconf libtool g++ autopoint make cmake bison flex yasm pkg-config libpulse-dev python3-dev gettext build-essential pkg-config curl libasound2-dev dpkg-dev debhelper build-essential devscripts fakeroot transfig gperf libdbus-glib-1-dev wget glib-networking libxtst-dev libxrandr-dev git intltool ccache python3-setuptools autogen maven make - checkout - run: name: Build Cerbero and Oboe working_directory: doc/android command: | export TERM=dumb export VERBOSE=1 echo Starting `nproc` parallel jobs make -f Makefile.android prepare - run: name: Link Cerbero NDK for build command: | mkdir -p /home/circleci/android-sdk-linux echo "android-ndk-21 content" ls /home/circleci/code/doc/android/external/cerbero/build/android-ndk-21 ln -s /home/circleci/code/doc/android/external/cerbero/build/android-ndk-21 /home/circleci/android-sdk-linux/ndk-bundle echo "/home/circleci/android-sdk-linux/ndk-bundle content" ls /home/circleci/android-sdk-linux/ndk-bundle - run: name: Build FluidSynth Android working_directory: doc/android command: | export TERM=dumb make -f Makefile.android - run: name: Show directory contents working_directory: doc/android command: | ls -R mv ./external/cerbero/build/logs dist/ cp ./external/cerbero/build/sources/android_armv7/glib-2.62.6/_builddir/meson*.txt dist/armeabi-v7a/ cp ./external/cerbero/build/sources/android_arm64/glib-2.62.6/_builddir/meson*.txt dist/arm64-v8a/ cp ./external/cerbero/build/sources/android_x86/glib-2.62.6/_builddir/meson*.txt dist/x86/ cp ./external/cerbero/build/sources/android_x86_64/glib-2.62.6/_builddir/meson*.txt dist/x86_64/ - run: name: Zip FluidSynth Android Distribution working_directory: doc/android command: zip -r android-dist.zip dist - store_artifacts: path: doc/android/android-dist.zip destination: android-dist.zip fluidsynth-2.2.5/.cirrus.yml000066400000000000000000000012041417326347500160320ustar00rootroot00000000000000 task: name: FreeBSD freebsd_instance: matrix: image_family: freebsd-11-4 image_family: freebsd-12-2 image_family: freebsd-13-0 install_script: pwd && ls -la && pkg update --force && pkg install -y cmake glib alsa-lib ladspa portaudio pulseaudio pkgconf sdl2 compile_script: pwd && ls -la && mkdir $HOME/fluidsynth_install/ && mkdir build && cd build && cmake -Werror=dev -DCMAKE_INSTALL_PREFIX=$HOME/fluidsynth_install -Denable-portaudio=1 -Denable-ladspa=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=0 -DNO_GUI=1 .. && make -j4 && make check && make install fluidsynth-2.2.5/.clang-format000066400000000000000000000031651417326347500163050ustar00rootroot00000000000000AccessModifierOffset: 0 AlignEscapedNewlinesLeft: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: false BinPackParameters: false BreakBeforeBinaryOperators: false BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: false BreakBeforeBraces: Custom BraceWrapping: AfterClass: true AfterControlStatement: true AfterEnum: true AfterFunction: true AfterNamespace: true AfterStruct: true AfterUnion: true BeforeCatch: true BeforeElse: true IndentBraces: false ColumnLimit: 100 CommentPragmas: '' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 0 ContinuationIndentWidth: 0 Cpp11BracedListStyle: false DerivePointerAlignment: false IndentCaseLabels: true IndentWidth: 4 Language: Cpp MaxEmptyLinesToKeep: 2 NamespaceIndentation: All PenaltyBreakBeforeFirstCallParameter: 100 PenaltyBreakComment: 100 PenaltyBreakFirstLessLess: 0 PenaltyBreakString: 100 PenaltyExcessCharacter: 1 PenaltyReturnTypeOnItsOwnLine: 20 SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false SortIncludes: false Standard: Cpp11 TabWidth: 4 UseTab: Never fluidsynth-2.2.5/.clang-tidy000066400000000000000000000052541417326347500157670ustar00rootroot00000000000000--- Checks: '-*,clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-security.insecureAPI.strcpy,performance-*,readability-avoid-const-params-in-decls,readability-braces-around-statements,readability-delete-null-pointer,readability-implicit-bool-conversion,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-simplify-boolean-expr' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false FormatStyle: file User: tom CheckOptions: - key: cert-dcl59-cpp.HeaderFileExtensions value: h,hh,hpp,hxx - key: cert-err09-cpp.CheckThrowTemporaries value: '1' - key: cert-err61-cpp.CheckThrowTemporaries value: '1' - key: cert-oop11-cpp.IncludeStyle value: llvm - key: google-readability-braces-around-statements.ShortStatementLines value: '1' - key: google-readability-function-size.StatementThreshold value: '800' - key: google-readability-namespace-comments.ShortNamespaceLines value: '10' - key: google-readability-namespace-comments.SpacesBeforeComments value: '2' - key: modernize-loop-convert.MaxCopySize value: '16' - key: modernize-loop-convert.MinConfidence value: reasonable - key: modernize-loop-convert.NamingStyle value: CamelCase - key: modernize-pass-by-value.IncludeStyle value: llvm - key: modernize-replace-auto-ptr.IncludeStyle value: llvm - key: modernize-use-nullptr.NullMacros value: 'NULL' - key: performance-faster-string-find.StringLikeClasses value: 'std::basic_string' - key: performance-for-range-copy.WarnOnAllAutoCopies value: '0' - key: performance-inefficient-string-concatenation.StrictMode value: '0' - key: performance-type-promotion-in-math-fn.IncludeStyle value: llvm - key: performance-unnecessary-value-param.IncludeStyle value: llvm - key: readability-braces-around-statements.ShortStatementLines value: '0' - key: readability-simplify-boolean-expr.ChainedConditionalAssignment value: '0' - key: readability-simplify-boolean-expr.ChainedConditionalReturn value: '0' ... fluidsynth-2.2.5/.github/000077500000000000000000000000001417326347500152655ustar00rootroot00000000000000fluidsynth-2.2.5/.github/ISSUE_TEMPLATE/000077500000000000000000000000001417326347500174505ustar00rootroot00000000000000fluidsynth-2.2.5/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000033171417326347500221460ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- ### FluidSynth version _Execute `fluidsynth --version` and provide the output._ ### Describe the bug _Provide a clear and concise description of the current situation, e.g. how the bug manifests._ ### Expected behavior _Provide a clear and concise description of what you expected to happen._ ### Steps to reproduce _Please explain the steps required to duplicate the issue, esp. if you are able to provide a sample application. E.g. how to start fluidsynth, what shell commands to enter, what midi events to send, etc._ ### Additional context _If you are able to illustrate the bug with an example, please provide simple source code below or as attached file. List any other information that is relevant to your issue, e.g. stack traces, related issues, build logs, suggestions on how to fix, links to related discussions at fluid-dev, etc._ ``` insert code snippets, soundfonts or anything relevant here, or attach it as extra file(s) if it's too much ``` fluidsynth-2.2.5/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000026621417326347500232030ustar00rootroot00000000000000--- name: Feature request about: Suggest a concrete feature title: '' labels: enhancement assignees: '' --- ### Related discussion _Feature requests should be discussed by the community, either on the mailing list or via a GitHub Discussion. Please bring up your ideas there before opening tickets. In case you already did that, please provide a link to the thread._ ### Is your feature request related to a problem? _A clear and concise description of what the problem is._ ### Describe the solution you'd like _A clear and concise description of what you want to happen._ ### Describe alternatives you've considered _A clear and concise description of any alternative solutions or features you've considered._ ### Additional context _Add any other context or screenshots about the feature request here._ fluidsynth-2.2.5/.github/issue_template.md000066400000000000000000000032241417326347500206330ustar00rootroot00000000000000_This issue tracker is only for bug reports and concrete feature requests. DO NOT SUBMIT SUPPORT REQUESTS OR "HOW TO" QUESTIONS HERE! Else it might be closed without further notice._ _If you have a question look into our wiki ( https://github.com/FluidSynth/fluidsynth/wiki ) or the developer resources ( https://www.fluidsynth.org/api/ )_ If you still have a question, need support or want to discuss ideas, contact our mailing list https://lists.nongnu.org/mailman/listinfo/fluid-dev or start a GitHub Discussion: https://github.com/FluidSynth/fluidsynth/discussions _Below is a form that shall help getting relevant information for bugs and feature requests together. We strongly recommend to use it! Feel free to edit or remove inapplicable/unneeded parts._ ### FluidSynth version 2.0.x ### Current behavior ### Expected behavior ### Steps to reproduce ### Other information fluidsynth-2.2.5/.github/workflows/000077500000000000000000000000001417326347500173225ustar00rootroot00000000000000fluidsynth-2.2.5/.github/workflows/api_doc_build.yml000066400000000000000000000025211417326347500226220ustar00rootroot00000000000000name: API Doc Build on: push: branches: - master env: BUILD_TYPE: Release jobs: build: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 with: persist-credentials: false - name: Install Dependencies run: sudo apt-get install doxygen xsltproc - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE - name: Build shell: bash working-directory: ${{runner.workspace}}/build run: cmake --build . --config $BUILD_TYPE --target doxygen - name: Install SSH Client 🔑 uses: webfactory/ssh-agent@v0.4.1 with: ssh-private-key: ${{ secrets.DEPLOY_API_TOKEN }} - name: Publish API Docs to GH Pages uses: JamesIves/github-pages-deploy-action@3.7.1 with: FOLDER: ${{runner.workspace}}/build/doc/api/html/ REPOSITORY_NAME: FluidSynth/fluidsynth.github.io BRANCH: main TARGET_FOLDER: api/ SSH: true CLEAN: true COMMIT_MESSAGE: Updating API doc from fluidsynth master GIT_CONFIG_NAME: API Doc Deploy GIT_CONFIG_EMAIL: fluid-api-deploy@fluidsynth.github.io fluidsynth-2.2.5/.github/workflows/codeql-analysis.yml000066400000000000000000000046151417326347500231430ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '17 18 * * 4' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'cpp' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 fluidsynth-2.2.5/.github/workflows/linux.yml000066400000000000000000000077061417326347500212160ustar00rootroot00000000000000name: FluidSynth Linux on: pull_request: push: paths-ignore: - '.azure/**' - '.circleci/**' - '.github/workflows/sonarcloud.yml' - '.cirrus.yml' - 'README.md' env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: RelWithDebInfo jobs: build: # The CMake configure and build commands are platform agnostic and should work equally # well on Windows or Mac. You can convert this to a matrix build if you need # cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix runs-on: ubuntu-20.04 strategy: matrix: CC: [""] CXX: [""] CMAKE_FLAGS: ["-Denable-profiling=1","-Denable-floats=1 -Denable-profiling=1","-Denable-floats=1","-Denable-trap-on-fpe=1","-Denable-fpe-check=1","-Denable-ipv6=0","-Denable-network=0","-Denable-aufile=0","-DBUILD_SHARED_LIBS=0","-Denable-ubsan=1 -Denable-debug=1", "-Denable-debug=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold"] include: - CC: "clang-7" CXX: "clang++-7" CMAKE_FLAGS: "" - CC: "clang-8" CXX: "clang++-8" CMAKE_FLAGS: "" - CC: "clang-10" CXX: "clang++-10" CMAKE_FLAGS: "" - CC: "clang-12" CXX: "clang++-12" CMAKE_FLAGS: "" # clang9 is covered by openSUSE Leap 15.2 # clang11 is covered by openSUSE Leap 15.3 steps: - uses: actions/checkout@v2 - name: Add apt-get repositories run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test - name: Update apt run: sudo apt-get update -y - name: Install Dependencies run: sudo -E apt-get -yq --no-install-suggests --no-install-recommends install cmake-data cmake libglib2.0-0 libsndfile-dev libasound2-dev libjack-dev portaudio19-dev libsdl2-dev libpulse-dev libdbus-1-dev libsystemd-dev libinstpatch-dev libreadline-dev g++-7 g++-8 clang-7 clang-8 clang-9 clang-10 clang-tidy-10 - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands run: | echo Can execute `nproc` make jobs in parallel cmake --version cmake -E make_directory ${{github.workspace}}/build sudo ln -s /usr/bin/clang-tidy-10 /usr/bin/clang-tidy which clang-tidy || true ls -la `which clang-tidy` || true echo $PATH echo $SONARSC - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash working-directory: ${{github.workspace}}/build # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 run: | export CC=${{ matrix.CC }} export CXX=${{ matrix.CXX }} cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -Werror=dev ${{ matrix.CMAKE_FLAGS }} -DCMAKE_INSTALL_PREFIX=$HOME/fluidsynth_install -Denable-portaudio=1 -Denable-ladspa=1 -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 $GITHUB_WORKSPACE - name: Build working-directory: ${{github.workspace}}/build shell: bash # Execute the build. You can specify a specific target with "--target " run: make -j`nproc` - name: Test working-directory: ${{github.workspace}}/build shell: bash # Execute tests defined by the CMake configuration. run: make -j`nproc` check - name: Demo working-directory: ${{github.workspace}}/build shell: bash run: make -j`nproc` demo - name: Install working-directory: ${{github.workspace}}/build run: make install fluidsynth-2.2.5/.github/workflows/sonarcloud.yml000066400000000000000000000073361417326347500222270ustar00rootroot00000000000000on: # Trigger analysis when pushing in master or pull requests, and when creating # a pull request. push: branches: - master paths-ignore: - '.azure/**' - '.circleci/**' - '.github/workflows/linux.yml' - '.cirrus.yml' - 'README.md' pull_request: types: [opened, synchronize, reopened] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) # Use Debug build for better code coverage results BUILD_TYPE: Debug name: SonarCloud Workflow jobs: sonarcloud: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: # Disabling shallow clone is recommended for improving relevancy of reporting fetch-depth: 0 - name: Add apt-get repositories run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test - name: Update apt run: sudo apt-get update -y - name: Install Dependencies run: sudo -E apt-get -yq --no-install-suggests --no-install-recommends install cmake-data cmake libglib2.0-0 libsndfile-dev libasound2-dev libjack-dev portaudio19-dev libsdl2-dev libpulse-dev libdbus-1-dev libsystemd-dev libinstpatch-dev libreadline-dev lcov gcovr ggcov - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands run: | cmake -E make_directory ${{github.workspace}}/build echo $PATH - name: Get Sonar Build Wrapper shell: bash working-directory: ${{github.workspace}}/build run: | set -ex wget https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip unzip build-wrapper-linux-x86.zip mv build-wrapper-linux-x86/* . - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash working-directory: ${{github.workspace}}/build run: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -Werror=dev -Denable-portaudio=1 -Denable-ladspa=1 -Denable-coverage=1 -DNO_GUI=1 $GITHUB_WORKSPACE - name: Build working-directory: ${{github.workspace}}/build shell: bash # Execute the build. You can specify a specific target with "--target " run: | ./build-wrapper-linux-x86-64 --out-dir bw-output make - name: Test working-directory: ${{github.workspace}}/build shell: bash # Execute tests defined by the CMake configuration. run: | ./build-wrapper-linux-x86-64 --out-dir bw-output make coverage # sonar-scanner does not like utf8 filenames - name: Prepare for Sonar run: | rm -rf ${{ github.workspace }}/sf2 gcovr --version gcovr --help ls -la ${{ github.workspace }} ls -la ${{ github.workspace }}/build ls -la ${{ github.workspace }}/build/coverage # The official sonarsource/sonarcloud-github-action@v1.5 action does not work properly. # It keeps complaining that the build-wrapper.json cannot be found. # Hence, use a third party action to download and add sonar-scanner to PATH and then run it manually. - name: Setup sonarqube uses: warchant/setup-sonar-scanner@v3 - name: Run sonarqube env: # to get access to secrets.SONAR_TOKEN, provide GITHUB_TOKEN GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: sonar-scanner -Dsonar.login=${{ secrets.SONAR_TOKEN }} -Dsonar.cfamily.build-wrapper-output=${{ github.workspace }}/build/bw-output -Dsonar.coverageReportPaths=build/coverage/sonarqube.report -Dsonar.verbose=false -Dsonar.host.url=https://sonarcloud.io/ fluidsynth-2.2.5/.gitignore000066400000000000000000000005271417326347500157210ustar00rootroot00000000000000build/ CMakeCache.txt CMakeFiles Makefile cmake_install.cmake install_manifest.txt # Object files *.o *.ko *.obj *.elf # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # ProjectFiles *.pro.user* *.user fluidsynth-2.2.5/.obs/000077500000000000000000000000001417326347500145665ustar00rootroot00000000000000fluidsynth-2.2.5/.obs/workflows.yml000066400000000000000000000002561417326347500173510ustar00rootroot00000000000000workflow: steps: - branch_package: source_project: home:derselbst:anmp source_package: fluidsynth target_project: home:derselbst:anmp:github-ci fluidsynth-2.2.5/AUTHORS000066400000000000000000000126731417326347500150060ustar00rootroot00000000000000[: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 (https://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 (https://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 algorithms 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. * Tom Moebert (fluidsynth's maintainer since Jun 2017) cleaned up and refactored fluidsynth's API and revised its documentation, added support for 24 bit sample soundfonts, added support for DLS soundfonts, fixed various bugs, implemented unit tests and CI builds for Windows, Linux, MacOSX and BSD. * 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 Marcus Weseloh Jean-jacques Ceresa Vladimir Davidovich Tamás Korodi Evan Miller fluidsynth-2.2.5/CMakeLists.txt000066400000000000000000001061641417326347500164750ustar00rootroot00000000000000# FluidSynth - A Software Synthesizer # # Copyright (C) 2003-2022 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.1.0 ) # because of CMAKE_C_STANDARD if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif() if(POLICY CMP0091) cmake_policy(SET CMP0091 NEW) endif() project ( FluidSynth C CXX ) list( APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake_admin ) # FluidSynth package name set ( PACKAGE "fluidsynth" ) # FluidSynth package version set ( FLUIDSYNTH_VERSION_MAJOR 2 ) set ( FLUIDSYNTH_VERSION_MINOR 2 ) set ( FLUIDSYNTH_VERSION_MICRO 5 ) 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 3 ) set ( LIB_VERSION_AGE 0 ) set ( LIB_VERSION_REVISION 5 ) set ( LIB_VERSION_INFO "${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" ) # Options disabled by default option ( enable-coverage "enable gcov code coverage" off ) option ( enable-debug "enable debugging (default=no)" off ) option ( enable-floats "enable type float instead of double for DSP samples" off ) option ( enable-fpe-check "enable Floating Point Exception checks and debug messages" off ) option ( enable-portaudio "compile PortAudio support" off ) option ( enable-profiling "profile the dsp code" off ) option ( enable-trap-on-fpe "enable SIGFPE trap on Floating Point Exceptions" off ) option ( enable-ubsan "compile and link against UBSan (for debugging fluidsynth internals)" off ) # Options enabled by default option ( enable-aufile "compile support for sound file output" on ) option ( BUILD_SHARED_LIBS "Build a shared object or DLL" on ) option ( enable-dbus "compile DBUS support (if it is available)" on ) option ( enable-ipv6 "enable ipv6 support" on ) option ( enable-jack "compile JACK support (if it is available)" on ) option ( enable-ladspa "enable LADSPA effect units" on ) option ( enable-libinstpatch "use libinstpatch (if available) to load DLS and GIG files" on ) option ( enable-libsndfile "compile libsndfile support (if it is available)" on ) option ( enable-midishare "compile MidiShare support (if it is available)" on ) option ( enable-opensles "compile OpenSLES support (if it is available)" off ) option ( enable-oboe "compile Oboe support (requires OpenSLES and/or AAudio)" off ) option ( enable-network "enable network support (requires BSD sockets)" on ) option ( enable-oss "compile OSS support (if it is available)" on ) option ( enable-dsound "compile DirectSound support (if it is available)" on ) option ( enable-wasapi "compile Windows WASAPI support (if it is available)" on ) option ( enable-waveout "compile Windows WaveOut support (if it is available)" on ) option ( enable-winmidi "compile Windows MIDI support (if it is available)" on ) option ( enable-sdl2 "compile SDL2 audio support (if it is available)" on ) option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on ) option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on ) option ( enable-readline "compile readline lib line editing (if it is available)" on ) option ( enable-threads "enable multi-threading support (such as parallel voice synthesis)" on ) option ( enable-openmp "enable OpenMP support (parallelization of soundfont decoding, vectorization of voice mixing, etc.)" on ) # Platform specific options if ( CMAKE_SYSTEM MATCHES "Linux|FreeBSD|DragonFly" ) 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|FreeBSD|DragonFly" ) if ( CMAKE_SYSTEM MATCHES "Linux" ) option ( enable-systemd "compile systemd 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 AND NOT MSVC AND NOT CMAKE_SYSTEM_NAME MATCHES "FreeBSD|DragonFly") 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 ) # the default C standard to use for all targets set(CMAKE_C_STANDARD 90) # the default C++ standard to use for all targets set(CMAKE_CXX_STANDARD 98) # whether to use gnu extensions set(CMAKE_CXX_EXTENSIONS OFF) # Compile with position independent code if the user requested a shared lib, i.e. no PIC if static requested. # This is cmakes default behavior, but here it's explicitly required due to the use of libfluidsynth-OBJ as object library, # which would otherwise always be compiled without PIC. if ( NOT CMAKE_POSITION_INDEPENDENT_CODE ) set ( CMAKE_POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} ) endif ( NOT CMAKE_POSITION_INDEPENDENT_CODE ) # the default global visibility level for all target # no visibility support on OS2 if ( NOT OS2 ) set ( CMAKE_C_VISIBILITY_PRESET hidden ) endif ( NOT OS2 ) # enforce visibility control for all types of cmake targets if ( POLICY CMP0063 ) cmake_policy ( SET CMP0063 NEW ) endif ( POLICY CMP0063 ) # Default install directory names include ( DefaultDirs ) # Basic C library checks include ( CheckCCompilerFlag ) include ( CheckSTDC ) include ( CheckIncludeFile ) include ( CheckSymbolExists ) include ( CheckTypeSize ) 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 ( 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 ( 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 ) check_include_file ( stdint.h HAVE_STDINT_H ) check_type_size ( "long long" LONG_LONG ) if ( NOT HAVE_LONG_LONG AND NOT MSVC) message ( FATAL_ERROR "Your compiler does not support intrinsic type 'long long'. Unable to compile fluidsynth." ) endif () 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 ) unset ( ENABLE_UBSAN CACHE ) if ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "Intel" ) if ( NOT APPLE AND NOT OS2 AND NOT EMSCRIPTEN ) 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 AND NOT EMSCRIPTEN ) # define some warning flags set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wno-unused-parameter -Wdeclaration-after-statement -Werror=implicit-function-declaration" ) check_c_compiler_flag ( "-Werror=incompatible-pointer-types" HAVE_INCOMPATIBLE_POINTER_TYPES ) if ( HAVE_INCOMPATIBLE_POINTER_TYPES ) set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=incompatible-pointer-types" ) endif ( HAVE_INCOMPATIBLE_POINTER_TYPES ) set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -Wpointer-arith -Wcast-qual -Wno-unused-parameter" ) # prepend to build type specific flags, to allow users to override set ( CMAKE_C_FLAGS_DEBUG "-g ${CMAKE_C_FLAGS_DEBUG}" ) if ( CMAKE_C_COMPILER_ID STREQUAL "Intel" ) # icc needs the restrict flag to recognize C99 restrict pointers set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -restrict" ) else () # not intel # gcc and clang support bad function cast and alignment warnings; add them as well. set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wbad-function-cast -Wcast-align" ) set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-align" ) if ( enable-ubsan ) set ( CMAKE_C_FLAGS "-fsanitize=address,undefined ${CMAKE_C_FLAGS}" ) set ( CMAKE_CXX_FLAGS "-fsanitize=address,undefined ${CMAKE_CXX_FLAGS}" ) set ( CMAKE_EXE_LINKER_FLAGS "-fsanitize=address,undefined ${CMAKE_EXE_LINKER_FLAGS}" ) set ( CMAKE_SHARED_LINKER_FLAGS "-fsanitize=address,undefined ${CMAKE_SHARED_LINKER_FLAGS}" ) set ( ENABLE_UBSAN 1 ) endif ( enable-ubsan ) if ( enable-coverage ) if ( CMAKE_COMPILER_IS_GNUCXX ) include ( CodeCoverage ) set ( CODE_COVERAGE_VERBOSE TRUE ) append_coverage_compiler_flags() setup_target_for_coverage_gcovr_html ( NAME coverage EXECUTABLE ${CMAKE_CTEST_COMMAND} -C $ --output-on-failure DEPENDENCIES check ) set ( ENABLE_COVERAGE 1 ) else() message ( SEND_ERROR "Code Coverage is currently only supported for GNU Compiler (GCC)" ) endif() endif () endif (CMAKE_C_COMPILER_ID STREQUAL "Intel" ) endif ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "Intel" ) # Windows unset ( WINDOWS_LIBS CACHE ) unset ( DSOUND_SUPPORT CACHE ) unset ( WASAPI_SUPPORT CACHE ) unset ( WAVEOUT_SUPPORT CACHE ) unset ( WINMIDI_SUPPORT CACHE ) unset ( MINGW32 CACHE ) if ( WIN32 ) include ( CheckIncludeFiles ) # We have to set _WIN32_WINNT to make sure CMake gets correct results when probing for functions. # windows-version is supposed to be non-official variable that can be used to tweak the Windows target version. # Its value defaults to the Windows Version we are compiling for. if ( NOT windows-version ) if(CMAKE_SYSTEM_VERSION EQUAL 10) # Windows 10 set ( windows-version "0x0A00" ) elseif(CMAKE_SYSTEM_VERSION EQUAL 6.3) # Windows 8.1 set ( windows-version "0x0603" ) elseif(CMAKE_SYSTEM_VERSION EQUAL 6.2) # Windows 8 set ( windows-version "0x0602" ) elseif(CMAKE_SYSTEM_VERSION EQUAL 6.1) # Windows 7 set ( windows-version "0x0601" ) elseif(CMAKE_SYSTEM_VERSION EQUAL 6.0) # Windows Vista set ( windows-version "0x0600" ) elseif(CMAKE_SYSTEM_VERSION EQUAL 5.1) # Windows XP set ( windows-version "0x0501" ) else() set ( windows-version "0x0400" ) endif() endif () message ( STATUS "Targeting Windows Version ${windows-version}" ) add_definitions ( -D _WIN32_WINNT=${windows-version} ) add_definitions ( -D WINVER=${windows-version} ) list ( APPEND CMAKE_REQUIRED_DEFINITIONS "-DWINVER=${windows-version}" ) list ( APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${windows-version}" ) # Check presence of MS include files check_include_file ( windows.h HAVE_WINDOWS_H ) check_include_file ( io.h HAVE_IO_H ) check_include_file ( dsound.h HAVE_DSOUND_H ) check_include_files ( "windows.h;mmsystem.h" HAVE_MMSYSTEM_H ) check_include_files ( "mmdeviceapi.h;audioclient.h" HAVE_WASAPI_HEADERS ) check_include_file ( objbase.h HAVE_OBJBASE_H ) if ( enable-network ) set ( WINDOWS_LIBS "${WINDOWS_LIBS};ws2_32" ) endif ( enable-network ) if ( enable-dsound AND HAVE_DSOUND_H ) set ( WINDOWS_LIBS "${WINDOWS_LIBS};dsound;ksuser" ) set ( DSOUND_SUPPORT 1 ) endif () if ( enable-winmidi AND HAVE_MMSYSTEM_H ) set ( WINDOWS_LIBS "${WINDOWS_LIBS};winmm" ) set ( WINMIDI_SUPPORT 1 ) endif () if ( enable-waveout AND HAVE_MMSYSTEM_H ) set ( WINDOWS_LIBS "${WINDOWS_LIBS};winmm;ksuser" ) set ( WAVEOUT_SUPPORT 1 ) endif () if ( enable-wasapi AND HAVE_WASAPI_HEADERS AND HAVE_OBJBASE_H) set ( WINDOWS_LIBS "${WINDOWS_LIBS};ole32" ) set ( WASAPI_SUPPORT 1 ) endif () set ( LIBFLUID_CPPFLAGS "-DFLUIDSYNTH_DLL_EXPORTS" ) set ( FLUID_CPPFLAGS "-DFLUIDSYNTH_NOT_A_DLL" ) if ( NOT MSVC ) # only set debug postfix if not MSVS building set ( CMAKE_DEBUG_POSTFIX "_debug" ) endif ( NOT MSVC ) # MinGW compiler (a Windows GCC port) if ( MINGW ) set ( MINGW32 1 ) add_compile_options ( -mms-bitfields ) # mman-win32 if ( HAVE_SYS_MMAN_H ) set ( WINDOWS_LIBS "${WINDOWS_LIBS};mman" ) endif () endif ( MINGW ) else ( WIN32 ) # Check PThreads, but not in Windows find_package ( Threads REQUIRED ) 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} ) 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 ( NETWORK_SUPPORT ) if ( enable-network ) set ( NETWORK_SUPPORT 1 ) endif ( enable-network ) 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 ) if ( CMAKE_C_COMPILER_ID STREQUAL "Clang" ) set ( OPT_FLAGS "-Rpass=loop-vectorize" ) # -Rpass-analysis=loop-vectorize" ) elseif ( CMAKE_C_COMPILER_ID STREQUAL "Intel" ) set ( OPT_FLAGS "-qopt-report=3" ) elseif ( CMAKE_C_COMPILER_ID STREQUAL "GNU" ) set ( OPT_FLAGS "" ) endif ( ) set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPT_FLAGS}" ) if ( CMAKE_VERSION VERSION_GREATER_EQUAL "3.6.0" ) find_program( CLANG_TIDY NAMES "clang-tidy" DOC "Path to clang-tidy executable" ) if ( CLANG_TIDY ) message ( STATUS "Found clang-tidy at ${CLANG_TIDY}" ) execute_process ( COMMAND ${CLANG_TIDY} "--version" ) set ( CMAKE_C_CLANG_TIDY ${CLANG_TIDY} ) endif ( CLANG_TIDY ) endif ( CMAKE_VERSION VERSION_GREATER_EQUAL "3.6.0" ) endif ( enable-profiling ) 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 AND NOT APPLE AND NOT WIN32 ) set ( ENABLE_FPECHECK 1 ) set ( FPE_CHECK 1 ) endif ( enable-fpe-check AND NOT APPLE AND NOT WIN32 ) if ( enable-debug ) set ( CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the build type, options: Debug Release RelWithDebInfo MinSizeRel" FORCE ) endif ( enable-debug ) if ( NOT CMAKE_BUILD_TYPE ) set ( CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the build type, options: Debug Release RelWithDebInfo MinSizeRel" FORCE ) endif ( NOT CMAKE_BUILD_TYPE ) unset ( ENABLE_DEBUG CACHE ) if ( CMAKE_BUILD_TYPE MATCHES "Debug" ) set ( ENABLE_DEBUG 1 ) add_definitions(-DDEBUG) # -D_GLIBCXX_DEBUG) # for additional C++ STL container debugging endif ( CMAKE_BUILD_TYPE MATCHES "Debug" ) # Additional targets to perform clang-format/clang-tidy # Get all project files file(GLOB_RECURSE ALL_SOURCE_FILES LIST_DIRECTORIES false ${CMAKE_SOURCE_DIR}/*.[chi] ${CMAKE_SOURCE_DIR}/*.[chi]pp ${CMAKE_SOURCE_DIR}/*.[chi]xx ${CMAKE_SOURCE_DIR}/*.cc ${CMAKE_SOURCE_DIR}/*.hh ${CMAKE_SOURCE_DIR}/*.ii ${CMAKE_SOURCE_DIR}/*.[CHI] ) find_program ( ASTYLE "astyle" ) if ( ASTYLE ) add_custom_target( format COMMAND ${ASTYLE} -A1 -xb -j -k3 -p -f -n -U ${ALL_SOURCE_FILES} ) endif(ASTYLE) 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 ) list( APPEND PC_REQUIRES_PRIV "glib-2.0" "gthread-2.0") if ( GLIB_glib-2.0_VERSION AND GLIB_glib-2.0_VERSION VERSION_LESS "2.26.0" ) message ( WARNING "Your version of glib is very old. This may cause problems with fluidsynth's sample cache on Windows. Consider updating to glib 2.26 or newer!" ) endif ( GLIB_glib-2.0_VERSION AND GLIB_glib-2.0_VERSION VERSION_LESS "2.26.0" ) 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 ) list( APPEND PC_REQUIRES_PRIV "sndfile") message (DEBUG "LIBSNDFILE_STATIC_LIBRARIES: ${LIBSNDFILE_STATIC_LIBRARIES}") message (DEBUG "LIBSNDFILE_STATIC_LINK_LIBRARIES: ${LIBSNDFILE_STATIC_LINK_LIBRARIES}") message (DEBUG "LIBSNDFILE_STATIC_LDFLAGS: ${LIBSNDFILE_STATIC_LDFLAGS}") message (DEBUG "LIBSNDFILE_STATIC_LDFLAGS_OTHER: ${LIBSNDFILE_STATIC_LDFLAGS_OTHER}") if ( LIBSNDFILE_STATIC_LIBRARIES MATCHES "vorbis" OR LIBSNDFILE_STATIC_LDFLAGS MATCHES "vorbis" OR LIBSNDFILE_STATIC_LDFLAGS_OTHER MATCHES "vorbis" ) set ( LIBSNDFILE_HASVORBIS 1 ) else () message ( NOTICE "Seems like libsndfile was compiled without OGG/Vorbis support." ) endif () 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} ) if ( PULSE_SUPPORT ) list( APPEND PC_REQUIRES_PRIV "libpulse-simple") endif ( PULSE_SUPPORT ) 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} ) if ( ALSA_SUPPORT ) list( APPEND PC_REQUIRES_PRIV "alsa") endif ( ALSA_SUPPORT ) 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} ) if ( PORTAUDIO_SUPPORT ) list( APPEND PC_REQUIRES_PRIV "portaudio-2.0") endif () 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} ) if ( JACK_SUPPORT ) list( APPEND PC_REQUIRES_PRIV "jack") endif () 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 ) list( APPEND PC_REQUIRES_PRIV "lash-1.0") endif ( LASH_FOUND ) else ( enable-lash ) unset_pkg_config ( LASH ) remove_definitions( -DHAVE_LASH ) endif ( enable-lash ) unset ( SYSTEMD_SUPPORT CACHE ) if ( enable-systemd ) pkg_check_modules ( SYSTEMD libsystemd ) set ( SYSTEMD_SUPPORT ${SYSTEMD_FOUND} ) if ( SYSTEMD_SUPPORT ) list( APPEND PC_REQUIRES_PRIV "libsystemd") endif ( SYSTEMD_SUPPORT ) else ( enable-systemd ) unset_pkg_config ( SYSTEMD ) endif ( enable-systemd ) unset ( DBUS_SUPPORT CACHE ) if ( enable-dbus ) pkg_check_modules ( DBUS dbus-1>=1.0.0 ) set ( DBUS_SUPPORT ${DBUS_FOUND} ) if ( DBUS_SUPPORT ) list( APPEND PC_REQUIRES_PRIV "dbus-1") endif () else ( enable-dbus ) unset_pkg_config ( DBUS ) endif ( enable-dbus ) unset ( LADSPA_SUPPORT CACHE ) if ( enable-ladspa ) check_include_file ( ladspa.h LADSPA_SUPPORT ) if ( LADSPA_SUPPORT ) pkg_check_modules ( GMODULE REQUIRED gmodule-2.0>=2.6.5 ) set ( LADSPA 1 ) list( APPEND PC_REQUIRES_PRIV "gmodule-2.0") endif ( LADSPA_SUPPORT ) endif ( enable-ladspa ) unset ( LIBINSTPATCH_SUPPORT CACHE ) if ( enable-libinstpatch ) pkg_check_modules ( LIBINSTPATCH libinstpatch-1.0>=1.1.0 ) set ( LIBINSTPATCH_SUPPORT ${LIBINSTPATCH_FOUND} ) if ( LIBINSTPATCH_SUPPORT ) list( APPEND PC_REQUIRES_PRIV "libinstpatch-1.0") endif (LIBINSTPATCH_SUPPORT ) endif ( enable-libinstpatch ) unset ( SDL2_SUPPORT CACHE ) if ( enable-sdl2 ) pkg_check_modules ( SDL2 sdl2 ) set ( SDL2_SUPPORT ${SDL2_FOUND} ) if ( SDL2_SUPPORT ) list( APPEND PC_REQUIRES_PRIV "sdl2") endif ( SDL2_SUPPORT ) else ( enable-sdl2 ) unset_pkg_config ( SDL2 ) endif ( enable-sdl2 ) unset ( OBOE_SUPPORT CACHE ) if ( enable-oboe ) pkg_check_modules ( OBOE oboe-1.0 ) if ( OBOE_FOUND ) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set ( OBOE_SUPPORT 1 ) list( APPEND PC_REQUIRES_PRIV "oboe-1.0") endif ( OBOE_FOUND ) endif ( enable-oboe ) unset ( WITH_READLINE CACHE ) unset ( READLINE_LIBS CACHE ) if ( enable-readline ) pkg_check_modules ( READLINE readline ) if ( READLINE_FOUND ) list( APPEND PC_REQUIRES_PRIV "readline") else ( READLINE_FOUND ) find_package ( READLINE ) set ( READLINE_FOUND ${HAVE_READLINE} ) endif ( READLINE_FOUND ) if ( READLINE_FOUND ) set ( WITH_READLINE 1 ) set ( READLINE_LIBS ${READLINE_LIBRARIES} ) endif ( READLINE_FOUND ) endif ( enable-readline ) endif(NOT enable-pkgconfig) unset ( AUFILE_SUPPORT CACHE ) if ( enable-aufile ) set ( AUFILE_SUPPORT 1 ) endif ( enable-aufile ) unset ( OSS_SUPPORT CACHE ) if ( enable-oss ) find_package ( OSS QUIET ) set ( OSS_SUPPORT ${OSS_FOUND} ) endif ( enable-oss ) unset ( MIDISHARE_SUPPORT CACHE ) if ( enable-midishare ) find_package ( MidiShare QUIET ) set ( MIDISHARE_SUPPORT ${MidiShare_FOUND} ) if ( MidiShare_FOUND ) set ( MidiShare_LIBS ${MidiShare_LIBRARIES} ) else ( MidiShare_FOUND ) unset ( MidiShare_LIBS CACHE ) endif ( MidiShare_FOUND ) else ( enable-midishare ) unset ( MidiShare_LIBS CACHE ) endif ( enable-midishare ) unset ( OPENSLES_SUPPORT CACHE ) unset ( OpenSLES_LIBS CACHE ) if ( enable-opensles ) check_include_file ( SLES/OpenSLES.h OPENSLES_SUPPORT ) if ( OPENSLES_SUPPORT ) find_library ( OpenSLES_LIBS OpenSLES ) if ( NOT OpenSLES_LIBS ) unset ( OPENSLES_SUPPORT ) endif ( NOT OpenSLES_LIBS ) endif ( OPENSLES_SUPPORT ) endif ( enable-opensles ) unset ( ENABLE_MIXER_THREADS CACHE ) if ( enable-threads ) set ( ENABLE_MIXER_THREADS 1 ) endif ( enable-threads ) unset ( HAVE_OPENMP CACHE ) find_package ( OpenMP QUIET ) if (enable-openmp AND ( OpenMP_FOUND OR OpenMP_C_FOUND ) ) message(STATUS "Found OpenMP ${OpenMP_C_SPEC_DATE}") # require at least OMP 4.0 if ( ( NOT OpenMP_C_SPEC_DATE LESS "201307" ) OR NOT ( OpenMP_C_VERSION VERSION_LESS "4.0" ) ) set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}" ) set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}" ) # include dir and library link seems to be required for Xcode 12.5 (issue #917) include_directories ( SYSTEM ${OpenMP_C_INCLUDE_DIRS} ) set ( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}" ) set ( LIBFLUID_LIBS "${OpenMP_C_LIBRARIES};${LIBFLUID_LIBS}" ) set ( HAVE_OPENMP 1 ) endif() endif() # manipulate some variables to setup a proper test env set(TEST_SOUNDFONT "${CMAKE_SOURCE_DIR}/sf2/VintageDreamsWaves-v2.sf2") set(TEST_SOUNDFONT_UTF8_1 "${CMAKE_SOURCE_DIR}/sf2/\\xE2\\x96\\xA0VintageDreamsWaves-v2\\xE2\\x96\\xA0.sf2") set(TEST_SOUNDFONT_UTF8_2 "${CMAKE_SOURCE_DIR}/sf2/VìntàgèDrèàmsWàvès-v2.sf2") set(TEST_SOUNDFONT_SF3 "${CMAKE_SOURCE_DIR}/sf2/VintageDreamsWaves-v2.sf3") set(TEST_MIDI_UTF8 "${CMAKE_SOURCE_DIR}/test/èmpty.mid") # Make sure to link against libm before checking for math functions below set ( CMAKE_REQUIRED_LIBRARIES "${LIBFLUID_LIBS};${WINDOWS_LIBS}" ) # Check for C99 float math unset ( HAVE_SINF CACHE ) CHECK_SYMBOL_EXISTS ( sinf "math.h" HAVE_SINF ) if ( HAVE_SINF ) set ( HAVE_SINF 1 ) endif ( HAVE_SINF ) unset ( HAVE_COSF CACHE ) CHECK_SYMBOL_EXISTS ( cosf "math.h" HAVE_COSF ) if ( HAVE_COSF ) set ( HAVE_COSF 1 ) endif ( HAVE_COSF ) unset ( HAVE_FABSF CACHE ) CHECK_SYMBOL_EXISTS ( fabsf "math.h" HAVE_FABSF ) if ( HAVE_FABSF ) set ( HAVE_FABSF 1 ) endif ( HAVE_FABSF ) unset ( HAVE_POWF CACHE ) CHECK_SYMBOL_EXISTS ( powf "math.h" HAVE_POWF ) if ( HAVE_POWF ) set ( HAVE_POWF 1 ) endif ( HAVE_POWF ) unset ( HAVE_SQRTF CACHE ) CHECK_SYMBOL_EXISTS ( sqrtf "math.h" HAVE_SQRTF ) if ( HAVE_SQRTF ) set ( HAVE_SQRTF 1 ) endif ( HAVE_SQRTF ) unset ( HAVE_LOGF CACHE ) CHECK_SYMBOL_EXISTS ( logf "math.h" HAVE_LOGF ) if ( HAVE_LOGF ) set ( HAVE_LOGF 1 ) endif ( HAVE_LOGF ) unset ( HAVE_INETNTOP CACHE ) unset ( IPV6_SUPPORT CACHE ) if ( WIN32 ) CHECK_SYMBOL_EXISTS ( inet_ntop "ws2tcpip.h" HAVE_INETNTOP ) else ( WIN32 ) CHECK_SYMBOL_EXISTS ( inet_ntop "arpa/inet.h" HAVE_INETNTOP ) endif ( WIN32 ) if ( enable-ipv6 ) if ( HAVE_INETNTOP ) set ( IPV6_SUPPORT 1 ) endif ( HAVE_INETNTOP ) endif ( enable-ipv6 ) unset ( HAVE_SOCKLEN_T CACHE ) set ( CMAKE_EXTRA_INCLUDE_FILES_SAVE ${CMAKE_EXTRA_INCLUDE_FILES} ) if ( WIN32 ) set ( CMAKE_EXTRA_INCLUDE_FILES "winsock2.h;ws2tcpip.h" ) else ( WIN32 ) set ( CMAKE_EXTRA_INCLUDE_FILES sys/socket.h ) endif ( WIN32 ) check_type_size ( socklen_t SOCKLEN_T ) set ( CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES_SAVE} ) if ( HAVE_SOCKLEN_T ) set ( HAVE_SOCKLEN_T 1 ) endif ( HAVE_SOCKLEN_T ) # General configuration file configure_file ( ${CMAKE_SOURCE_DIR}/src/config.cmake ${CMAKE_BINARY_DIR}/config.h ) # Setup linker directories NOW, as the command will apply only to targets created after it has been called. link_directories ( ${GLIB_LIBRARY_DIRS} ${LASH_LIBRARY_DIRS} ${JACK_LIBRARY_DIRS} ${ALSA_LIBRARY_DIRS} ${PULSE_LIBRARY_DIRS} ${PORTAUDIO_LIBRARY_DIRS} ${LIBSNDFILE_LIBRARY_DIRS} ${DBUS_LIBRARY_DIRS} ${SDL2_LIBRARY_DIRS} ${OBOE_LIBRARY_DIRS} ${LIBINSTPATCH_LIBRARY_DIRS} ) # required to allow ctest to be called from top-level build directory ENABLE_TESTING() # Process subdirectories add_subdirectory ( src ) add_subdirectory ( test ) add_subdirectory ( doc ) # pkg-config support set ( prefix "${CMAKE_INSTALL_PREFIX}" ) set ( exec_prefix "\${prefix}" ) if ( IS_ABSOLUTE "${LIB_INSTALL_DIR}" ) set ( libdir "${LIB_INSTALL_DIR}" ) else () set ( libdir "\${exec_prefix}/${LIB_INSTALL_DIR}" ) endif () if ( IS_ABSOLUTE "${INCLUDE_INSTALL_DIR}" ) set ( includedir "${INCLUDE_INSTALL_DIR}" ) else () set ( includedir "\${prefix}/${INCLUDE_INSTALL_DIR}" ) endif () if ( CMAKE_VERSION VERSION_EQUAL "3.12.0" OR CMAKE_VERSION VERSION_GREATER "3.12.0" ) # retrieve all the private libs we depend on get_target_property ( LIBS_PRIVATE libfluidsynth INTERFACE_LINK_LIBRARIES) # make a copy set ( LIBS_PRIVATE_WITH_PATH ${LIBS_PRIVATE} ) # this matches any path and any flag entries (starting with '-') set ( LIB_LIST_REGEX "(^(.+)\/([^\/]+)$)|(^\-.*$)" ) # remove all entries from the list which are specified by path and which already have -l list ( FILTER LIBS_PRIVATE EXCLUDE REGEX ${LIB_LIST_REGEX} ) # include only entries specifed by path list ( FILTER LIBS_PRIVATE_WITH_PATH INCLUDE REGEX ${LIB_LIST_REGEX} ) # prepend the linker flag to all entries except the ones that already have it list ( TRANSFORM LIBS_PRIVATE PREPEND "-l") list ( JOIN LIBS_PRIVATE " " LIBS_PRIVATE_JOINED ) list ( JOIN LIBS_PRIVATE_WITH_PATH " " LIBS_PRIVATE_WITH_PATH_JOINED ) list ( JOIN PC_REQUIRES_PRIV " " PC_REQUIRES_PRIV_JOINED ) else () set ( LIBS_PRIVATE "" ) set ( LIBS_PRIVATE_WITH_PATH "" ) message ( DEPRECATION "Your version of CMake is old. A complete pkg-config file can not created. Get cmake 3.13.3 or newer." ) endif ( CMAKE_VERSION VERSION_EQUAL "3.12.0" OR CMAKE_VERSION VERSION_GREATER "3.12.0" ) configure_file ( fluidsynth.pc.in ${CMAKE_BINARY_DIR}/fluidsynth.pc IMMEDIATE @ONLY ) install ( FILES ${CMAKE_BINARY_DIR}/fluidsynth.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig ) # Extra targets for Unix build environments if ( UNIX ) if ( DEFINED FLUID_DAEMON_ENV_FILE) configure_file ( fluidsynth.service.in ${CMAKE_BINARY_DIR}/fluidsynth.service @ONLY ) configure_file ( fluidsynth.conf.in ${CMAKE_BINARY_DIR}/fluidsynth.conf @ONLY ) endif ( DEFINED FLUID_DAEMON_ENV_FILE ) # 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} ) 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-2.2.5/CONTRIBUTING.md000066400000000000000000000063011417326347500161560ustar00rootroot00000000000000# Contributing Thanks for considering to contribute to FluidSynth. Before implementing any huge new feature, consider bringing up your ideas on our mailing list: https://lists.nongnu.org/mailman/listinfo/fluid-dev Contributing can be done by * [submitting pull requests on Github]( https://help.github.com/articles/proposing-changes-to-your-work-with-pull-requests/) or * submitting patches to the mailing list. Patches should be created with `git format-patch`, so in every case you should be familiar with the basics of git. Make sure you develop against the master branch, i.e. **not** against any FluidSynth release. Some things that will increase the chance that your pull request or patch is accepted: * Give a reasoning / motivation for any changes or proposals you make. * Follow our style guide. * Keep your commits "atomic". * Write meaningful commit messages. ## Style Guide Find FluidSynth's style guide below. Syntax related issues, like missing braces, can be taken care of by calling `make format` (provided that cmake has found `astyle` on your system). #### General * Every function should have a short comment explaining it's purpose * Every public API function **must** be documented with purpose, params and return value * Prefer signed integer types to unsigned ones * Use spaces rather than tabs * Avoid macros #### Naming Conventions * Words separated by underscores * Macros always UPPER_CASE * Function and Variable names always lower_case, (e.g. `fluid_componentname_purpose()`) #### Bracing * Every block after an if, else, while or for should be enclosed in braces * **Allman-Style** braces everywhere ### Documentation Style Guide We use Doxygen for public API functions, usage examples and other information. #### Order of Elements Please ensure that the order of elements in the documentation block is consistent with the existing documentation. Most importantly, each function starts with a single sentence brief description, followed by any `@param` and `@return` tags. `@deprecated` and `@since` should always come last. Example: ``` /** * Brief description of the function (only a single sentence). * * @param ... * @param ... * @return ... * * Detailed description of the function. This can be multiple paragraphs, * include code examples etc. It can also include @note, @warning or * other special tags not mentioned below. * * @deprecated This is deprecated because ... * * @since 1.2.3 */ ``` #### Mark Lifecycle Functions All functions are sorted alphabetically in the generated API documentation. To ensure that the `new_*` and `delete_*` functions appear first, please make sure to surround those lifecycle functions with `@startlifecycle{}` and `@endlifecycle` tags. Example: ``` /** @startlifecycle{Some Name} */ new_fluid_some_name(...); delete_fluid_some_name(...); /** @endlifecycle */ ``` #### Referencing Setting Items If you want to mention a setting item (for example `audio.periods`), please use the custom `@setting{name}` tag. The argument `name` should be the setting name with all `.` replaced by `_`. Example: ``` /** * This is a comment that references \setting{audio_periods}. You * can also link to a group of settings with \setting{audio} or * \setting{synth}. */ ``` fluidsynth-2.2.5/ChangeLog000066400000000000000000002164231417326347500155070ustar00rootroot00000000000000This 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: https://sourceforge.net/apps/trac/fluidsynth/wiki/ChangeLog1_1_2 https://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-2.2.5/LICENSE000066400000000000000000000635351417326347500147460ustar00rootroot00000000000000 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-2.2.5/README.cmake.md000066400000000000000000000063001417326347500162620ustar00rootroot00000000000000## For users - how to compile FluidSynth The latest information on how to compile FluidSynth using the cmake build system can be found in our wiki: https://github.com/FluidSynth/fluidsynth/wiki/BuildingWithCMake ## 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: ```cmake 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: ```cmake 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: ```cmake 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: ```c /* 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: ```c /* 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 ```cmake 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 ```cmake link_directories ( ... ${DBUS_LIBDIR} ${DBUS_LIBRARY_DIRS} ) add_library ( libfluidsynth ... ${fluid_dbus_SOURCES} ... ) ``` file src/CMakeLists.txt, lines 163-197 ```cmake target_link_libraries ( libfluidsynth ... ${DBUS_LIBRARIES} ... ) ``` fluidsynth-2.2.5/README.md000066400000000000000000000145721417326347500152150ustar00rootroot00000000000000# FluidSynth | | Build Status | |---|---| | **Linux** | [![FluidSynth Linux](https://github.com/FluidSynth/fluidsynth/workflows/FluidSynth%20Linux/badge.svg)](https://github.com/FluidSynth/fluidsynth/actions?query=workflow%3A%22FluidSynth+Linux%22) | | **FreeBSD** | [![Build Status](https://api.cirrus-ci.com/github/FluidSynth/fluidsynth.svg?branch=master)](https://cirrus-ci.com/github/FluidSynth/fluidsynth) | | **Windows** | [![Build Status](https://dev.azure.com/tommbrt/tommbrt/_apis/build/status/FluidSynth.fluidsynth.Win?branchName=master)](https://dev.azure.com/tommbrt/tommbrt/_build/latest?definitionId=3&branchName=master) | | **Windows (vcpkg)** | [![Build Status](https://dev.azure.com/tommbrt/tommbrt/_apis/build/status/FluidSynth.fluidsynth.vcpkg?branchName=master)](https://dev.azure.com/tommbrt/tommbrt/_build/latest?definitionId=6&branchName=master) | | **MacOSX** | [![Build Status](https://dev.azure.com/tommbrt/tommbrt/_apis/build/status/FluidSynth.fluidsynth.macOS?branchName=master)](https://dev.azure.com/tommbrt/tommbrt/_build/latest?definitionId=5&branchName=master) | | **Android** | [![Build Status](https://dev.azure.com/tommbrt/tommbrt/_apis/build/status/FluidSynth.fluidsynth.Android?branchName=master)](https://dev.azure.com/tommbrt/tommbrt/_build/latest?definitionId=4&branchName=master) | | **Android** (legacy Cerbero) | [![CircleCI](https://circleci.com/gh/FluidSynth/fluidsynth/tree/master.svg?style=shield)](https://circleci.com/gh/FluidSynth/fluidsynth) | #### FluidSynth is a cross-platform, real-time software synthesizer based on the Soundfont 2 specification. FluidSynth generates audio by reading and handling MIDI events from MIDI input devices by using a [SoundFont](https://github.com/FluidSynth/fluidsynth/wiki/SoundFont). It is the software analogue of a MIDI synthesizer. FluidSynth can also play MIDI files. [![SonarQube Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=FluidSynth_fluidsynth&metric=alert_status)](https://sonarcloud.io/dashboard?id=FluidSynth_fluidsynth) [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/FluidSynth/fluidsynth.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/FluidSynth/fluidsynth/context:cpp) [![OHLOH Project Stats](https://www.openhub.net/p/fluidsynth/widgets/project_thin_badge?format=gif)](https://www.openhub.net/p/fluidsynth) ## Documentation The central place for documentation and further links is our **wiki** here at GitHub: #### https://github.com/FluidSynth/fluidsynth/wiki If you are missing parts of the documentation, let us know by writing to our mailing list. Of course, you are welcome to edit and improve the wiki yourself. All you need is an account at GitHub. Alternatively, you may send an EMail to our mailing list along with your suggested changes. Further information about the mailing list is available in the wiki as well. Latest information about FluidSynth is also available on the web site at https://www.fluidsynth.org/. ## License The source code for FluidSynth is distributed under the terms of the [GNU Lesser General Public License](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html), see the [LICENSE](https://github.com/FluidSynth/fluidsynth/blob/master/LICENSE) file. To better understand the conditions how FluidSynth can be used in e.g. commercial or closed-source projects, please refer to the [LicensingFAQ in our wiki](https://github.com/FluidSynth/fluidsynth/wiki/LicensingFAQ). ## Building from source For information on how to build FluidSynth from source, please [refer to our wiki](https://github.com/FluidSynth/fluidsynth/wiki/BuildingWithCMake). ## Links - FluidSynth's Home Page, https://www.fluidsynth.org - FluidSynth's wiki, https://github.com/FluidSynth/fluidsynth/wiki - FluidSynth's API documentation, https://www.fluidsynth.org/api/ --- ## Historical background ### 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 intelligent use of midifiles. The arguments were: - Wavetable 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 environments, 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 focused on its goal (a Soundfont synthesizer), stayed small (ideally one file) and didn't dependent on external code. fluidsynth-2.2.5/THANKS000066400000000000000000000030771417326347500146470ustar00rootroot00000000000000For 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! (https://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-2.2.5/TODO000066400000000000000000000024471417326347500144240ustar00rootroot00000000000000New features ------------ - 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 Drivers ------- - libao audio output driver - Windows DirectMusic component - ASIO driver - DirectSound 3D and EAX Bugs to mash ------------ - Investigate why MIDI rendering causes burst of notes at start Documentation ------------- - Write documentation on tuning - User and system configuration file Misc ---- - Remove dependency of settings on audio driver and other (see fluid_settings_init()) - set loop on/off on a sample (set_gen GEN_SAMPLEMODE?) FluidSynth Next Generation -------------------------------------------- Top of the list - 3D audio output 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) - Allow settings to be loaded before the synthesizer is created MIDI Specs - sample dump - MIDI thru - Scalable Polyphony MIDI (SP-MIDI) Unsorted - rewrite midi file using new sequencer fluidsynth-2.2.5/cmake_admin/000077500000000000000000000000001417326347500161555ustar00rootroot00000000000000fluidsynth-2.2.5/cmake_admin/CheckDIRSymbolExists.cmake000066400000000000000000000066671417326347500231400ustar00rootroot00000000000000# - 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-2.2.5/cmake_admin/CheckPrototypeExists.cmake000066400000000000000000000023741417326347500233300ustar00rootroot00000000000000# - 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-2.2.5/cmake_admin/CheckSTDC.cmake000066400000000000000000000022651417326347500206570ustar00rootroot00000000000000message(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) 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-2.2.5/cmake_admin/CodeCoverage.cmake000066400000000000000000000605261417326347500215160ustar00rootroot00000000000000# Copyright (c) 2012 - 2017, Lars Bilke # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # CHANGES: # # 2012-01-31, Lars Bilke # - Enable Code Coverage # # 2013-09-17, Joakim Söderberg # - Added support for Clang. # - Some additional usage instructions. # # 2016-02-03, Lars Bilke # - Refactored functions to use named parameters # # 2017-06-02, Lars Bilke # - Merged with modified version from github.com/ufz/ogs # # 2019-05-06, Anatolii Kurotych # - Remove unnecessary --coverage flag # # 2019-12-13, FeRD (Frank Dana) # - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor # of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. # - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY # - All setup functions: accept BASE_DIRECTORY, EXCLUDE list # - Set lcov basedir with -b argument # - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be # overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) # - Delete output dir, .info file on 'make clean' # - Remove Python detection, since version mismatches will break gcovr # - Minor cleanup (lowercase function names, update examples...) # # 2019-12-19, FeRD (Frank Dana) # - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets # # 2020-01-19, Bob Apthorpe # - Added gfortran support # # 2020-02-17, FeRD (Frank Dana) # - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters # in EXCLUDEs, and remove manual escaping from gcovr targets # # 2021-01-19, Robin Mueller # - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run # - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional # flags to the gcovr command # # 2020-05-04, Mihchael Davis # - Add -fprofile-abs-path to make gcno files contain absolute paths # - Fix BASE_DIRECTORY not working when defined # - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines # USAGE: # # 1. Copy this file into your cmake modules path. # # 2. Add the following line to your CMakeLists.txt (best inside an if-condition # using a CMake option() to enable it just optionally): # include(CodeCoverage) # # 3. Append necessary compiler flags: # append_coverage_compiler_flags() # # 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og # # 4. If you need to exclude additional directories from the report, specify them # using full paths in the COVERAGE_EXCLUDES variable before calling # setup_target_for_coverage_*(). # Example: # set(COVERAGE_EXCLUDES # '${PROJECT_SOURCE_DIR}/src/dir1/*' # '/path/to/my/src/dir2/*') # Or, use the EXCLUDE argument to setup_target_for_coverage_*(). # Example: # setup_target_for_coverage_lcov( # NAME coverage # EXECUTABLE testrunner # EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") # # 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set # relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) # Example: # set(COVERAGE_EXCLUDES "dir1/*") # setup_target_for_coverage_gcovr_html( # NAME coverage # EXECUTABLE testrunner # BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" # EXCLUDE "dir2/*") # # 5. Use the functions described below to create a custom make target which # runs your test executable and produces a code coverage report. # # 6. Build a Debug build: # cmake -DCMAKE_BUILD_TYPE=Debug .. # make # make my_coverage_target # include(CMakeParseArguments) option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE) # Check prereqs find_program( GCOV_PATH gcov ) find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) find_program( FASTCOV_PATH NAMES fastcov fastcov.py ) find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) find_program( CPPFILT_PATH NAMES c++filt ) if(NOT GCOV_PATH) message(FATAL_ERROR "gcov not found! Aborting...") endif() # NOT GCOV_PATH get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) list(GET LANGUAGES 0 LANG) if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3) message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") endif() elseif(NOT CMAKE_COMPILER_IS_GNUCXX) if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang") # Do nothing; exit conditional without error if true elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") # Do nothing; exit conditional without error if true else() message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") endif() endif() set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" CACHE INTERNAL "") if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") include(CheckCXXCompilerFlag) check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path) if(HAVE_fprofile_abs_path) set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") endif() endif() set(CMAKE_Fortran_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} CACHE STRING "Flags used by the Fortran compiler during coverage builds." FORCE ) set(CMAKE_CXX_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} CACHE STRING "Flags used by the C++ compiler during coverage builds." FORCE ) set(CMAKE_C_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} CACHE STRING "Flags used by the C compiler during coverage builds." FORCE ) set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "" CACHE STRING "Flags used for linking binaries during coverage builds." FORCE ) set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "" CACHE STRING "Flags used by the shared libraries linker during coverage builds." FORCE ) mark_as_advanced( CMAKE_Fortran_FLAGS_COVERAGE CMAKE_CXX_FLAGS_COVERAGE CMAKE_C_FLAGS_COVERAGE CMAKE_EXE_LINKER_FLAGS_COVERAGE CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") link_libraries(gcov) endif() # Defines a target for running and collection code coverage information # Builds dependencies, runs the given executable and outputs reports. # NOTE! The executable should always have a ZERO as exit code otherwise # the coverage generation will not complete. # # setup_target_for_coverage_lcov( # NAME testrunner_coverage # New target name # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR # DEPENDENCIES testrunner # Dependencies to build first # BASE_DIRECTORY "../" # Base directory for report # # (defaults to PROJECT_SOURCE_DIR) # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative # # to BASE_DIRECTORY, with CMake 3.4+) # NO_DEMANGLE # Don't demangle C++ symbols # # even if c++filt is found # ) function(setup_target_for_coverage_lcov) set(options NO_DEMANGLE) set(oneValueArgs BASE_DIRECTORY NAME) set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT LCOV_PATH) message(FATAL_ERROR "lcov not found! Aborting...") endif() # NOT LCOV_PATH if(NOT GENHTML_PATH) message(FATAL_ERROR "genhtml not found! Aborting...") endif() # NOT GENHTML_PATH # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR if(DEFINED Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) endif() # Collect excludes (CMake 3.4+: Also compute absolute paths) set(LCOV_EXCLUDES "") foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) if(CMAKE_VERSION VERSION_GREATER 3.4) get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) endif() list(APPEND LCOV_EXCLUDES "${EXCLUDE}") endforeach() list(REMOVE_DUPLICATES LCOV_EXCLUDES) # Conditional arguments if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) set(GENHTML_EXTRA_ARGS "--demangle-cpp") endif() # enable branch coverage set ( Coverage_LCOV_ARGS ${Coverage_LCOV_ARGS} --rc lcov_branch_coverage=1 ) set ( Coverage_GENHTML_ARGS ${Coverage_GENHTML_ARGS} --branch-coverage ) # Setting up commands which will be run to generate coverage data. # Cleanup lcov set(LCOV_CLEAN_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . -b ${BASEDIR} --zerocounters ) # Create baseline to make sure untouched files show up in the report set(LCOV_BASELINE_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b ${BASEDIR} -o ${Coverage_NAME}.base ) # Run tests set(LCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} ) # Capturing lcov counters and generating report set(LCOV_CAPTURE_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture ) # add baseline counters set(LCOV_BASELINE_COUNT_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total ) # filter collected data to final coverage report set(LCOV_FILTER_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info ) # Generate HTML output set(LCOV_GEN_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info ) if(CODE_COVERAGE_VERBOSE) message(STATUS "Executed command report") message(STATUS "Command to clean up lcov: ") string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}") message(STATUS "${LCOV_CLEAN_CMD_SPACED}") message(STATUS "Command to create baseline: ") string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}") message(STATUS "${LCOV_BASELINE_CMD_SPACED}") message(STATUS "Command to run the tests: ") string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}") message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}") message(STATUS "Command to capture counters and generate report: ") string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}") message(STATUS "${LCOV_CAPTURE_CMD_SPACED}") message(STATUS "Command to add baseline counters: ") string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}") message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}") message(STATUS "Command to filter collected data: ") string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}") message(STATUS "${LCOV_FILTER_CMD_SPACED}") message(STATUS "Command to generate lcov HTML output: ") string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}") message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}") endif() # Setup target add_custom_target(${Coverage_NAME} COMMAND ${LCOV_CLEAN_CMD} COMMAND ${LCOV_BASELINE_CMD} COMMAND ${LCOV_EXEC_TESTS_CMD} COMMAND ${LCOV_CAPTURE_CMD} COMMAND ${LCOV_BASELINE_COUNT_CMD} COMMAND ${LCOV_FILTER_CMD} COMMAND ${LCOV_GEN_HTML_CMD} # Set output files as GENERATED (will be removed on 'make clean') BYPRODUCTS ${Coverage_NAME}.base ${Coverage_NAME}.capture ${Coverage_NAME}.total ${Coverage_NAME}.info ${Coverage_NAME}/index.html WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} VERBATIM # Protect arguments to commands COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." ) # Show where to find the lcov info report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ; COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." ) # Show info where to find the report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ; COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." ) endfunction() # setup_target_for_coverage_lcov # Defines a target for running and collection code coverage information # Builds dependencies, runs the given executable and outputs reports. # NOTE! The executable should always have a ZERO as exit code otherwise # the coverage generation will not complete. # # setup_target_for_coverage_gcovr_html( # NAME ctest_coverage # New target name # EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR # DEPENDENCIES executable_target # Dependencies to build first # BASE_DIRECTORY "../" # Base directory for report # # (defaults to PROJECT_SOURCE_DIR) # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative # # to BASE_DIRECTORY, with CMake 3.4+) # ) # The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the # GCVOR command. function(setup_target_for_coverage_gcovr_html) set(options NONE) set(oneValueArgs BASE_DIRECTORY NAME) set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT GCOVR_PATH) message(FATAL_ERROR "gcovr not found! Aborting...") endif() # NOT GCOVR_PATH # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR if(DEFINED Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) endif() # Collect excludes (CMake 3.4+: Also compute absolute paths) set(GCOVR_EXCLUDES "") foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) if(CMAKE_VERSION VERSION_GREATER 3.4) get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) endif() list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") endforeach() list(REMOVE_DUPLICATES GCOVR_EXCLUDES) # Combine excludes to several -e arguments set(GCOVR_EXCLUDE_ARGS "") foreach(EXCLUDE ${GCOVR_EXCLUDES}) list(APPEND GCOVR_EXCLUDE_ARGS "-e") list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") endforeach() # Set up commands which will be run to generate coverage data # Run tests set(GCOVR_HTML_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} ) # Create folder set(GCOVR_HTML_FOLDER_CMD ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} ) # Running gcovr set(GCOVR_HTML_CMD ${GCOVR_PATH} --html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} -o ${Coverage_NAME}/index.html ) # The --keep option is broken since gcovr 4.0 all our gcov files are deleted before SonarQube # can use them. Hence create an additional sonarqube report, but do not fail if this fails. set(GCOVR_SONAR_CMD ${GCOVR_PATH} --sonarqube -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} -o ${Coverage_NAME}/sonarqube.report || true ) if(CODE_COVERAGE_VERBOSE) message(STATUS "Executed command report") message(STATUS "Command to run tests: ") string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}") message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}") message(STATUS "Command to create a folder: ") string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}") message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}") message(STATUS "Command to generate gcovr HTML coverage data: ") string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}") message(STATUS "${GCOVR_HTML_CMD_SPACED}") endif() add_custom_target(${Coverage_NAME} COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD} COMMAND ${GCOVR_HTML_FOLDER_CMD} COMMAND ${GCOVR_HTML_CMD} COMMAND ${GCOVR_SONAR_CMD} BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} VERBATIM # Protect arguments to commands COMMENT "Running gcovr to produce HTML code coverage report." ) # Show info where to find the report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ; COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." ) endfunction() # setup_target_for_coverage_gcovr_html # Defines a target for running and collection code coverage information # Builds dependencies, runs the given executable and outputs reports. # NOTE! The executable should always have a ZERO as exit code otherwise # the coverage generation will not complete. # # setup_target_for_coverage_fastcov( # NAME testrunner_coverage # New target name # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR # DEPENDENCIES testrunner # Dependencies to build first # BASE_DIRECTORY "../" # Base directory for report # # (defaults to PROJECT_SOURCE_DIR) # EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude. # NO_DEMANGLE # Don't demangle C++ symbols # # even if c++filt is found # SKIP_HTML # Don't create html report # ) function(setup_target_for_coverage_fastcov) set(options NO_DEMANGLE SKIP_HTML) set(oneValueArgs BASE_DIRECTORY NAME) set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS) cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT FASTCOV_PATH) message(FATAL_ERROR "fastcov not found! Aborting...") endif() if(NOT GENHTML_PATH) message(FATAL_ERROR "genhtml not found! Aborting...") endif() # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR if(Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) endif() # Collect excludes (Patterns, not paths, for fastcov) set(FASTCOV_EXCLUDES "") foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES}) list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}") endforeach() list(REMOVE_DUPLICATES FASTCOV_EXCLUDES) # Conditional arguments if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) set(GENHTML_EXTRA_ARGS "--demangle-cpp") endif() # Set up commands which will be run to generate coverage data set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} --search-directory ${BASEDIR} --process-gcno --lcov --output ${Coverage_NAME}.info --exclude ${FASTCOV_EXCLUDES} --exclude ${FASTCOV_EXCLUDES} ) if(Coverage_SKIP_HTML) set(FASTCOV_HTML_CMD ";") else() set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info ) endif() if(CODE_COVERAGE_VERBOSE) message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):") message(" Running tests:") string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}") message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}") message(" Capturing fastcov counters and generating report:") string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}") message(" ${FASTCOV_CAPTURE_CMD_SPACED}") if(NOT Coverage_SKIP_HTML) message(" Generating HTML report: ") string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}") message(" ${FASTCOV_HTML_CMD_SPACED}") endif() endif() # Setup target add_custom_target(${Coverage_NAME} # Cleanup fastcov COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} --search-directory ${BASEDIR} --zerocounters COMMAND ${FASTCOV_EXEC_TESTS_CMD} COMMAND ${FASTCOV_CAPTURE_CMD} COMMAND ${FASTCOV_HTML_CMD} # Set output files as GENERATED (will be removed on 'make clean') BYPRODUCTS ${Coverage_NAME}.info ${Coverage_NAME}/index.html # report directory WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} VERBATIM # Protect arguments to commands COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report." ) set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info.") if(NOT Coverage_SKIP_HTML) string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.") endif() # Show where to find the fastcov info report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG} ) endfunction() # setup_target_for_coverage_fastcov function(append_coverage_compiler_flags) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") endfunction() # append_coverage_compiler_flags fluidsynth-2.2.5/cmake_admin/DefaultDirs.cmake000066400000000000000000000077101417326347500213720ustar00rootroot00000000000000# 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 "${CMAKE_INSTALL_PREFIX}/share/soundfonts/default.sf2" CACHE STRING "Default soundfont file") endif ( WIN32 ) mark_as_advanced (DEFAULT_SOUNDFONT) set(FRAMEWORK_INSTALL_PREFIX "") if ( CMAKE_VERSION VERSION_GREATER "3.7.0" AND NOT CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT ) set(FRAMEWORK_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) endif() # 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${LIB_SUFFIX}" 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 if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD|DragonFly") set (MAN_INSTALL_DIR "man/man1" CACHE STRING "The man pages install dir") else() set (MAN_INSTALL_DIR "share/man/man1" CACHE STRING "The man pages install dir") endif() 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-2.2.5/cmake_admin/FindMidiShare.cmake000066400000000000000000000013301417326347500216220ustar00rootroot00000000000000# Try to find the READLINE library # MidiShare_FOUND - system has MidiShare # MidiShare_INCLUDE_DIR - MidiShare include directory # MidiShare_LIBRARIES - Libraries needed to use MidiShare if ( MidiShare_INCLUDE_DIR AND MidiShare_LIBRARIES ) set ( MidiShare_FIND_QUIETLY TRUE ) endif ( MidiShare_INCLUDE_DIR AND MidiShare_LIBRARIES ) find_path ( MidiShare_INCLUDE_DIR NAMES MidiShare.h ) find_library ( MidiShare_LIBRARIES NAMES MidiShare ) include ( FindPackageHandleStandardArgs ) find_package_handle_standard_args( MidiShare DEFAULT_MSG MidiShare_INCLUDE_DIR MidiShare_LIBRARIES ) mark_as_advanced( MidiShare_INCLUDE_DIR MidiShare_LIBRARIES ) fluidsynth-2.2.5/cmake_admin/FindOSS.cmake000066400000000000000000000020431417326347500204230ustar00rootroot00000000000000# - 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 ) fluidsynth-2.2.5/cmake_admin/FindREADLINE.cmake000066400000000000000000000016351417326347500211500ustar00rootroot00000000000000# 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 AND READLINE_LIBRARIES ) set ( HAVE_READLINE TRUE CACHE BOOL "Found readline header and lib" FORCE ) endif ( READLINE_INCLUDE_DIR AND READLINE_LIBRARIES ) 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-2.2.5/cmake_admin/FluidUnitTest.cmake000066400000000000000000000106551417326347500217310ustar00rootroot00000000000000macro ( ADD_FLUID_TEST _test ) ADD_EXECUTABLE(${_test} ${_test}.c $ ) # only build this unit test when explicitly requested by "make check" set_target_properties(${_test} PROPERTIES EXCLUDE_FROM_ALL TRUE) # import necessary compile flags and dependency libraries if ( FLUID_CPPFLAGS ) set_target_properties ( ${_test} PROPERTIES COMPILE_FLAGS ${FLUID_CPPFLAGS} ) endif ( FLUID_CPPFLAGS ) TARGET_LINK_LIBRARIES(${_test} $) # use the local include path to look for fluidsynth.h, as we cannot be sure fluidsynth is already installed target_include_directories(${_test} PUBLIC $ # include auto generated headers $ # include "normal" public (sub-)headers $ # include private headers $ # include all other header search paths needed by libfluidsynth (esp. glib) ) # add the test to ctest ADD_TEST(NAME ${_test} COMMAND ${_test}) # append the current unit test to check-target as dependency add_dependencies(check ${_test}) endmacro ( ADD_FLUID_TEST ) macro ( ADD_FLUID_TEST_UTIL _util ) ADD_EXECUTABLE(${_util} ${_util}.c $ ) # only build this unit test when explicitly requested by "make check" set_target_properties(${_util} PROPERTIES EXCLUDE_FROM_ALL TRUE) # append no-op generator expression to avoid VS or XCode from adding per-config subdirectories set_target_properties(${_util} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test/utils/$<0:>) # import necessary compile flags and dependency libraries if ( FLUID_CPPFLAGS ) set_target_properties ( ${_util} PROPERTIES COMPILE_FLAGS ${FLUID_CPPFLAGS} ) endif ( FLUID_CPPFLAGS ) TARGET_LINK_LIBRARIES(${_util} $) # use the local include path to look for fluidsynth.h, as we cannot be sure fluidsynth is already installed target_include_directories(${_util} PUBLIC $ # include auto generated headers $ # include "normal" public (sub-)headers $ # include private headers $ # include all other header search paths needed by libfluidsynth (esp. glib) ) # append the current unit test to check-target as dependency add_dependencies(check ${_util}) endmacro ( ADD_FLUID_TEST_UTIL ) # This macro adds a test that writes its output to a file called # .output (in the current working dir) and then compares # the content with the file given in _expected_output macro ( ADD_FLUID_SF_DUMP_TEST _sfname) set( test_args "${CMAKE_SOURCE_DIR}/sf2/${_sfname} ${_sfname}.yml" ) ADD_TEST(${_sfname}_dump_test ${CMAKE_COMMAND} -Dtest_cmd=${CMAKE_BINARY_DIR}/test/utils/dump_sfont${CMAKE_EXECUTABLE_SUFFIX} -Dtest_args=${test_args} -Dtest_output=${_sfname}.yml -Dexpected_output=${CMAKE_SOURCE_DIR}/sf2/${_sfname}.yml -P ${CMAKE_SOURCE_DIR}/cmake_admin/RunOutputTest.cmake ) endmacro ( ADD_FLUID_SF_DUMP_TEST ) macro ( ADD_FLUID_DEMO _demo ) ADD_EXECUTABLE(${_demo} ${_demo}.c ) # only build this unit test when explicitly requested by "make check" set_target_properties(${_demo} PROPERTIES EXCLUDE_FROM_ALL TRUE) # import necessary compile flags and dependency libraries if ( FLUID_CPPFLAGS ) set_target_properties ( ${_demo} PROPERTIES COMPILE_FLAGS ${FLUID_CPPFLAGS} ) endif ( FLUID_CPPFLAGS ) TARGET_LINK_LIBRARIES(${_demo} libfluidsynth) # use the local include path to look for fluidsynth.h, as we cannot be sure fluidsynth is already installed target_include_directories(${_demo} PUBLIC $ # include auto generated headers $ # include "normal" public (sub-)headers $ # include all other header search paths needed by libfluidsynth (esp. glib) ) # append the current unit test to check-target as dependency add_dependencies(demo ${_demo}) endmacro ( ADD_FLUID_DEMO ) fluidsynth-2.2.5/cmake_admin/RunOutputTest.cmake000066400000000000000000000015331417326347500220060ustar00rootroot00000000000000if( NOT test_cmd ) message( FATAL_ERROR "test_cmd not defined" ) endif( NOT test_cmd ) if( NOT test_output ) message( FATAL_ERROR "test_output not defined" ) endif( NOT test_output ) if( NOT expected_output ) message( FATAL_ERROR "expected_output not defined" ) endif( NOT expected_output ) separate_arguments( test_args ) execute_process( COMMAND ${test_cmd} ${test_args} RESULT_VARIABLE test_not_successful ) if( test_not_successful ) message( FATAL_ERROR "${test_cmd} ${test_args} returned error ${test_not_successful}!" ) endif( test_not_successful ) execute_process( COMMAND ${CMAKE_COMMAND} -E compare_files ${expected_output} ${test_output} RESULT_VARIABLE compare_not_successful ) if( compare_not_successful ) message( SEND_ERROR "${test_output} does not match ${expected_output}!" ) endif( compare_not_successful ) fluidsynth-2.2.5/cmake_admin/TestInline.cmake000066400000000000000000000011501417326347500212320ustar00rootroot00000000000000include ( 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-2.2.5/cmake_admin/TestVLA.cmake000066400000000000000000000004301417326347500204360ustar00rootroot00000000000000include ( 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-2.2.5/cmake_admin/UnsetPkgConfig.cmake000066400000000000000000000010411417326347500220410ustar00rootroot00000000000000macro ( 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-2.2.5/cmake_admin/VersionInfo.in000066400000000000000000000051101417326347500207430ustar00rootroot00000000000000#pragma once #ifndef PRODUCT_VERSION_MAJOR #define PRODUCT_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ #endif #ifndef PRODUCT_VERSION_MINOR #define PRODUCT_VERSION_MINOR @PRODUCT_VERSION_MINOR@ #endif #ifndef PRODUCT_VERSION_PATCH #define PRODUCT_VERSION_PATCH @PRODUCT_VERSION_PATCH@ #endif #ifndef PRODUCT_VERSION_BUILD #define PRODUCT_VERSION_BUILD @PRODUCT_VERSION_REVISION@ #endif #ifndef FILE_VERSION_MAJOR #define FILE_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ #endif #ifndef FILE_VERSION_MINOR #define FILE_VERSION_MINOR @PRODUCT_VERSION_MINOR@ #endif #ifndef FILE_VERSION_PATCH #define FILE_VERSION_PATCH @PRODUCT_VERSION_PATCH@ #endif #ifndef FILE_VERSION_BUILD #define FILE_VERSION_BUILD @PRODUCT_VERSION_REVISION@ #endif #ifndef __TO_STRING #define __TO_STRING_IMPL(x) #x #define __TO_STRING(x) __TO_STRING_IMPL(x) #endif #define PRODUCT_VERSION_MAJOR_MINOR_STR __TO_STRING(PRODUCT_VERSION_MAJOR) "." __TO_STRING(PRODUCT_VERSION_MINOR) #define PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR PRODUCT_VERSION_MAJOR_MINOR_STR "." __TO_STRING(PRODUCT_VERSION_PATCH) #define PRODUCT_VERSION_FULL_STR PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(PRODUCT_VERSION_BUILD) #define PRODUCT_VERSION_RESOURCE PRODUCT_VERSION_MAJOR,PRODUCT_VERSION_MINOR,PRODUCT_VERSION_PATCH,PRODUCT_VERSION_BUILD #define PRODUCT_VERSION_RESOURCE_STR PRODUCT_VERSION_FULL_STR "\0" #define FILE_VERSION_MAJOR_MINOR_STR __TO_STRING(FILE_VERSION_MAJOR) "." __TO_STRING(FILE_VERSION_MINOR) #define FILE_VERSION_MAJOR_MINOR_PATCH_STR FILE_VERSION_MAJOR_MINOR_STR "." __TO_STRING(FILE_VERSION_PATCH) #define FILE_VERSION_FULL_STR FILE_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(FILE_VERSION_BUILD) #define FILE_VERSION_RESOURCE FILE_VERSION_MAJOR,FILE_VERSION_MINOR,FILE_VERSION_PATCH,FILE_VERSION_BUILD #define FILE_VERSION_RESOURCE_STR FILE_VERSION_FULL_STR "\0" #ifndef PRODUCT_COMMENTS #define PRODUCT_COMMENTS "@PRODUCT_COMMENTS@\0" #endif #ifndef PRODUCT_COMPANY_NAME #define PRODUCT_COMPANY_NAME "@PRODUCT_COMPANY_NAME@\0" #endif #ifndef PRODUCT_COMPANY_COPYRIGHT #define PRODUCT_COMPANY_COPYRIGHT "@PRODUCT_COMPANY_COPYRIGHT@\0" #endif #ifndef PRODUCT_FILE_DESCRIPTION #define PRODUCT_FILE_DESCRIPTION "@PRODUCT_FILE_DESCRIPTION@\0" #endif #ifndef PRODUCT_INTERNAL_NAME #define PRODUCT_INTERNAL_NAME "@PRODUCT_NAME@\0" #endif #ifndef PRODUCT_ORIGINAL_FILENAME #define PRODUCT_ORIGINAL_FILENAME "@PRODUCT_ORIGINAL_FILENAME@\0" #endif #ifndef PRODUCT_BUNDLE #define PRODUCT_BUNDLE "@PRODUCT_BUNDLE@\0" #endif fluidsynth-2.2.5/cmake_admin/VersionResource.rc000066400000000000000000000020231417326347500216350ustar00rootroot00000000000000#include "VersionInfo.h" #include "winver.h" VS_VERSION_INFO VERSIONINFO FILEVERSION FILE_VERSION_RESOURCE PRODUCTVERSION PRODUCT_VERSION_RESOURCE FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "Comments", PRODUCT_COMMENTS VALUE "CompanyName", PRODUCT_COMPANY_NAME VALUE "FileDescription", PRODUCT_FILE_DESCRIPTION VALUE "FileVersion", FILE_VERSION_RESOURCE_STR VALUE "InternalName", PRODUCT_INTERNAL_NAME VALUE "LegalCopyright", PRODUCT_COMPANY_COPYRIGHT VALUE "OriginalFilename", PRODUCT_ORIGINAL_FILENAME VALUE "ProductName", PRODUCT_BUNDLE VALUE "ProductVersion", PRODUCT_VERSION_RESOURCE_STR END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END fluidsynth-2.2.5/cmake_admin/cmake_uninstall.cmake.in000066400000000000000000000015551417326347500227430ustar00rootroot00000000000000IF(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-2.2.5/cmake_admin/generate_product_version.cmake000066400000000000000000000102031417326347500242520ustar00rootroot00000000000000include (CMakeParseArguments) set (GenerateProductVersionCurrentDir ${CMAKE_CURRENT_LIST_DIR}) # generate_product_version() function # # This function uses VersionInfo.in template file and VersionResource.rc file # to generate WIN32 resource with version information and general resource strings. # # Usage: # generate_product_version( # SomeOutputResourceVariable # NAME MyGreatProject # ICON ${PATH_TO_APP_ICON} # VERSION_MAJOR 2 # VERSION_MINOR 3 # VERSION_PATH ${BUILD_COUNTER} # VERSION_REVISION ${BUILD_REVISION} # ) # where BUILD_COUNTER and BUILD_REVISION could be values from your CI server. # # You can use generated resource for your executable targets: # add_executable(target-name ${target-files} ${SomeOutputResourceVariable}) # # You can specify resource strings in arguments: # NAME - name of executable (no defaults, ex: Microsoft Word) # BUNDLE - bundle (${NAME} is default, ex: Microsoft Office) # ICON - path to application icon (${CMAKE_SOURCE_DIR}/product.ico by default) # VERSION_MAJOR - 1 is default # VERSION_MINOR - 0 is default # VERSION_PATCH - 0 is default # VERSION_REVISION - 0 is default # COMPANY_NAME - your company name (no defaults) # COMPANY_COPYRIGHT - ${COMPANY_NAME} (C) Copyright ${CURRENT_YEAR} is default # COMMENTS - ${NAME} v${VERSION_MAJOR}.${VERSION_MINOR} is default # ORIGINAL_FILENAME - ${NAME} is default # INTERNAL_NAME - ${NAME} is default # FILE_DESCRIPTION - ${NAME} is default function(generate_product_version outfiles) set (options) set (oneValueArgs NAME BUNDLE VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_REVISION COMPANY_NAME COMPANY_COPYRIGHT COMMENTS ORIGINAL_FILENAME INTERNAL_NAME FILE_DESCRIPTION) set (multiValueArgs) cmake_parse_arguments(PRODUCT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if (NOT PRODUCT_BUNDLE OR "${PRODUCT_BUNDLE}" STREQUAL "") set(PRODUCT_BUNDLE "${PRODUCT_NAME}") endif() # if (NOT PRODUCT_ICON OR "${PRODUCT_ICON}" STREQUAL "") # set(PRODUCT_ICON "${CMAKE_SOURCE_DIR}/product.ico") # endif() if (NOT PRODUCT_VERSION_MAJOR OR "${PRODUCT_VERSION_MAJOR}" STREQUAL "") set(PRODUCT_VERSION_MAJOR 1) endif() if (NOT PRODUCT_VERSION_MINOR OR "${PRODUCT_VERSION_MINOR}" STREQUAL "") set(PRODUCT_VERSION_MINOR 0) endif() if (NOT PRODUCT_VERSION_PATCH OR "${PRODUCT_VERSION_PATCH}" STREQUAL "") set(PRODUCT_VERSION_PATCH 0) endif() if (NOT PRODUCT_VERSION_REVISION OR "${PRODUCT_VERSION_REVISION}" STREQUAL "") set(PRODUCT_VERSION_REVISION 0) endif() if (NOT PRODUCT_COMPANY_COPYRIGHT OR "${PRODUCT_COMPANY_COPYRIGHT}" STREQUAL "") string(TIMESTAMP PRODUCT_CURRENT_YEAR "%Y") set(PRODUCT_COMPANY_COPYRIGHT "${PRODUCT_COMPANY_NAME} (C) Copyright ${PRODUCT_CURRENT_YEAR}") endif() if (NOT PRODUCT_COMMENTS OR "${PRODUCT_COMMENTS}" STREQUAL "") set(PRODUCT_COMMENTS "${PRODUCT_NAME} v${PRODUCT_VERSION_MAJOR}.${PRODUCT_VERSION_MINOR}") endif() if (NOT PRODUCT_ORIGINAL_FILENAME OR "${PRODUCT_ORIGINAL_FILENAME}" STREQUAL "") set(PRODUCT_ORIGINAL_FILENAME "${PRODUCT_NAME}") endif() if (NOT PRODUCT_INTERNAL_NAME OR "${PRODUCT_INTERNAL_NAME}" STREQUAL "") set(PRODUCT_INTERNAL_NAME "${PRODUCT_NAME}") endif() if (NOT PRODUCT_FILE_DESCRIPTION OR "${PRODUCT_FILE_DESCRIPTION}" STREQUAL "") set(PRODUCT_FILE_DESCRIPTION "${PRODUCT_NAME}") endif() set (_VersionInfoFile ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.h) set (_VersionResourceFile ${CMAKE_CURRENT_BINARY_DIR}/VersionResource.rc) configure_file( ${GenerateProductVersionCurrentDir}/VersionInfo.in ${_VersionInfoFile} @ONLY) configure_file( ${GenerateProductVersionCurrentDir}/VersionResource.rc ${_VersionResourceFile} COPYONLY) list(APPEND ${outfiles} ${_VersionInfoFile} ${_VersionResourceFile}) set (${outfiles} ${${outfiles}} PARENT_SCOPE) endfunction() fluidsynth-2.2.5/cmake_admin/report.cmake000066400000000000000000000221421417326347500204730ustar00rootroot00000000000000 set ( AUDIO_MIDI_REPORT "\n" ) if ( ALSA_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} ALSA: yes\n" ) else ( ALSA_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} ALSA: no\n" ) endif ( ALSA_SUPPORT ) if ( COREAUDIO_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} CoreAudio: yes\n" ) else ( COREAUDIO_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} CoreAudio: no\n" ) endif ( COREAUDIO_SUPPORT ) if ( COREMIDI_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} CoreMIDI: yes\n" ) else ( COREMIDI_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} CoreMIDI: no\n" ) endif ( COREMIDI_SUPPORT ) if ( DSOUND_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} DSound: yes\n" ) else ( DSOUND_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} DSound: no\n" ) endif ( DSOUND_SUPPORT ) if ( JACK_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} JACK: yes\n" ) else ( JACK_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} JACK: no\n" ) endif ( JACK_SUPPORT ) if ( MIDISHARE_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} MidiShare: yes\n" ) else ( MIDISHARE_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} MidiShare: no\n" ) endif ( MIDISHARE_SUPPORT ) if ( OBOE_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} Oboe: yes\n" ) else ( OBOE_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} Oboe: no\n" ) endif ( OBOE_SUPPORT ) if ( OPENSLES_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} OpenSLES: yes\n" ) else ( OPENSLES_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} OpenSLES: no\n" ) endif ( OPENSLES_SUPPORT ) if ( DART_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} OS/2 DART: yes\n" ) else ( DART_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} OS/2 DART: no\n" ) endif ( DART_SUPPORT ) if ( OSS_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} OSS: yes\n" ) else ( OSS_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} OSS: no\n" ) endif ( OSS_SUPPORT ) if ( PORTAUDIO_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} PortAudio: yes\n" ) else ( PORTAUDIO_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} PortAudio: no\n" ) endif ( PORTAUDIO_SUPPORT ) if ( PULSE_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} PulseAudio: yes\n" ) else ( PULSE_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} PulseAudio: no\n" ) endif ( PULSE_SUPPORT ) if ( SDL2_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} SDL2: yes\n" ) else ( SDL2_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} SDL2: no\n" ) endif ( SDL2_SUPPORT ) if ( WASAPI_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} WASAPI: yes\n" ) else ( WASAPI_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} WASAPI: no\n" ) endif ( WASAPI_SUPPORT ) if ( WAVEOUT_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} WaveOut: yes\n" ) else ( WAVEOUT_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} WaveOut: no\n" ) endif ( WAVEOUT_SUPPORT ) if ( WINMIDI_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} WinMidi: yes\n" ) else ( WINMIDI_SUPPORT ) set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT} WinMidi: no\n" ) endif ( WINMIDI_SUPPORT ) set ( INPUTS_REPORT "\n" ) set ( INPUTS_REPORT "${INPUTS_REPORT}Support for SF3 files: " ) if ( LIBSNDFILE_HASVORBIS ) set ( INPUTS_REPORT "${INPUTS_REPORT}yes\n" ) elseif ( NOT LIBSNDFILE_SUPPORT ) set ( INPUTS_REPORT "${INPUTS_REPORT}no (libsndfile not found)\n" ) elseif ( NOT LIBSNDFILE_HASVORBIS ) set ( INPUTS_REPORT "${INPUTS_REPORT}no (libsndfile has no ogg vorbis support)\n" ) endif ( LIBSNDFILE_HASVORBIS ) set ( INPUTS_REPORT "${INPUTS_REPORT}Support for DLS files: " ) if ( LIBINSTPATCH_SUPPORT ) set ( INPUTS_REPORT "${INPUTS_REPORT}yes\n" ) else ( LIBINSTPATCH_SUPPORT ) set ( INPUTS_REPORT "${INPUTS_REPORT}no (libinstpatch not found)\n" ) endif ( LIBINSTPATCH_SUPPORT ) set ( RENDERING_REPORT "\n" ) if ( AUFILE_SUPPORT ) set ( RENDERING_REPORT "${RENDERING_REPORT}Audio to file rendering: yes\n" ) else ( AUFILE_SUPPORT ) set ( RENDERING_REPORT "${RENDERING_REPORT}Audio to file rendering: no\n" ) endif ( AUFILE_SUPPORT ) if ( LIBSNDFILE_SUPPORT ) set ( RENDERING_REPORT "${RENDERING_REPORT} libsndfile: yes\n" ) else ( LIBSNDFILE_SUPPORT ) set ( RENDERING_REPORT "${RENDERING_REPORT} libsndfile: no (RAW PCM rendering only)\n" ) endif ( LIBSNDFILE_SUPPORT ) set ( MISC_REPORT "\nMiscellaneous support:\n" ) if ( DBUS_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} D-Bus: yes\n" ) else ( DBUS_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} D-Bus: no\n" ) endif ( DBUS_SUPPORT ) if ( LADSPA_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} LADSPA support: yes\n" ) else ( LADSPA_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} LADSPA support: no\n" ) endif ( LADSPA_SUPPORT ) if ( LASH_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} LASH support: yes (NOTE: GPL library)\n" ) else ( LASH_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} LASH support: no\n" ) endif ( LASH_SUPPORT ) if ( NETWORK_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} NETWORK Support: yes\n" ) else ( NETWORK_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} NETWORK Support: no\n" ) endif ( NETWORK_SUPPORT ) if ( IPV6_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} IPV6 Support: yes\n" ) else ( IPV6_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} IPV6 Support: no\n" ) endif ( IPV6_SUPPORT ) if ( WITH_READLINE ) set ( MISC_REPORT "${MISC_REPORT} Readline: yes (NOTE: GPL library)\n" ) else ( WITH_READLINE ) set ( MISC_REPORT "${MISC_REPORT} Readline: no\n" ) endif ( WITH_READLINE ) if ( SYSTEMD_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} systemd: yes\n" ) else ( SYSTEMD_SUPPORT ) set ( MISC_REPORT "${MISC_REPORT} systemd: no\n" ) endif ( SYSTEMD_SUPPORT ) if ( HAVE_GETOPT_H ) set ( MISC_REPORT "${MISC_REPORT} getopt: yes\n" ) else ( HAVE_GETOPT_H ) set ( MISC_REPORT "${MISC_REPORT} getopt: no\n" ) endif ( HAVE_GETOPT_H ) set ( DEVEL_REPORT "\nDeveloper nerds info:\n" ) if ( WITH_FLOAT ) set ( DEVEL_REPORT "${DEVEL_REPORT} Samples type: float\n" ) else ( WITH_FLOAT ) set ( DEVEL_REPORT "${DEVEL_REPORT} Samples type: double\n" ) endif ( WITH_FLOAT ) if ( ENABLE_MIXER_THREADS ) set ( DEVEL_REPORT "${DEVEL_REPORT} Multithread rendering: yes\n" ) else ( ENABLE_MIXER_THREADS ) set ( DEVEL_REPORT "${DEVEL_REPORT} Multithread rendering: no\n" ) endif ( ENABLE_MIXER_THREADS ) if ( HAVE_OPENMP ) set ( DEVEL_REPORT "${DEVEL_REPORT} OpenMP 4.0: yes\n" ) else ( HAVE_OPENMP ) set ( DEVEL_REPORT "${DEVEL_REPORT} OpenMP 4.0: no\n" ) endif ( HAVE_OPENMP ) if ( WITH_PROFILING ) set ( DEVEL_REPORT "${DEVEL_REPORT} Profiling: yes\n" ) else ( WITH_PROFILING ) set ( DEVEL_REPORT "${DEVEL_REPORT} Profiling: no\n" ) endif ( WITH_PROFILING ) if ( ENABLE_DEBUG ) set ( DEVEL_REPORT "${DEVEL_REPORT} Debug Build: yes\n" ) else ( ENABLE_DEBUG ) set ( DEVEL_REPORT "${DEVEL_REPORT} Debug Build: no\n" ) endif ( ENABLE_DEBUG ) if ( ENABLE_TRAPONFPE ) set ( DEVEL_REPORT "${DEVEL_REPORT} Trap on FPE (debug): yes\n" ) else ( ENABLE_TRAPONFPE ) set ( DEVEL_REPORT "${DEVEL_REPORT} Trap on FPE (debug): no\n" ) endif ( ENABLE_TRAPONFPE ) if ( ENABLE_FPECHECK ) set ( DEVEL_REPORT "${DEVEL_REPORT} Check FPE (debug): yes\n" ) else ( ENABLE_FPECHECK ) set ( DEVEL_REPORT "${DEVEL_REPORT} Check FPE (debug): no\n" ) endif ( ENABLE_FPECHECK ) if ( ENABLE_UBSAN ) set ( DEVEL_REPORT "${DEVEL_REPORT} UBSan (debug): yes\n" ) else ( ENABLE_UBSAN ) set ( DEVEL_REPORT "${DEVEL_REPORT} UBSan (debug): no\n" ) endif ( ENABLE_UBSAN ) if ( ENABLE_COVERAGE ) set ( DEVEL_REPORT "${DEVEL_REPORT} Coverage: yes\n" ) else ( ENABLE_COVERAGE ) set ( DEVEL_REPORT "${DEVEL_REPORT} Coverage: no\n" ) endif ( ENABLE_COVERAGE ) message( STATUS "\n**************************************************************\n" "Build Summary:\n" "Build type: " ${CMAKE_BUILD_TYPE} "\n" "Install Prefix: " ${CMAKE_INSTALL_PREFIX} "\n" "\n" "Audio / MIDI driver support:" ${AUDIO_MIDI_REPORT} ${INPUTS_REPORT} ${RENDERING_REPORT} ${MISC_REPORT} ${DEVEL_REPORT} ) message ( "**************************************************************\n\n" ) fluidsynth-2.2.5/contrib/000077500000000000000000000000001417326347500153655ustar00rootroot00000000000000fluidsynth-2.2.5/contrib/baselibs.conf000066400000000000000000000000171417326347500200160ustar00rootroot00000000000000libfluidsynth2 fluidsynth-2.2.5/contrib/debian.changelog000066400000000000000000000013261417326347500204620ustar00rootroot00000000000000fluidsynth (1.1.8-1) unstable; urgency=low - Update to version 1.1.8: * fix build against glib < 2.30 (#202) * fix dsound audio driver on windows (#215) * fix a bug around `synth.audio-groups` setting, which caused improper multi-channel rendering (#225) * cmake 3.0.2 is now required * compilation with clang is now possible * build fixes on OS/2 (thanks to @komh) -- Tom Moebert Fri, 13 Oct 2017 15:53:00 +0000 fluidsynth (1.1.7-1) unstable; urgency=low * OBS snapshot. -- Rui Nuno Capela Tue, 5 Sep 2017 20:00:00 +0000 fluidsynth (1.1.6-1) unstable; urgency=low * OBS snapshot. -- Rui Nuno Capela Sun, 19 Aug 2012 23:45:01 +0000 fluidsynth-2.2.5/contrib/debian.compat000066400000000000000000000000021417326347500200040ustar00rootroot000000000000009 fluidsynth-2.2.5/contrib/debian.control000066400000000000000000000022071417326347500202120ustar00rootroot00000000000000Source: fluidsynth Priority: optional Section: sound Maintainer: Rui Nuno Capela Build-Depends: debhelper (>= 5.0.0), cmake, pkg-config, libdb-dev, libjack-dev, libasound2-dev, libsndfile-dev, libglib2.0-dev Standards-Version: 3.7.2 Package: libfluidsynth1 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: FluidSynth is a real-time software synthesizer FluidSynth is a real-time software synthesizer based on the SoundFont 2 specifications. Package: libfluidsynth-dev Section: libdevel Architecture: any Depends: libfluidsynth1 (= ${source:Version}) Description: FluidSynth is a real-time software synthesizer FluidSynth is a real-time software synthesizer based on the SoundFont 2 specifications. This package contains the header file required for compiling hosts and plugins. Package: fluidsynth Section: sound Architecture: any Depends: libfluidsynth1 (= ${source:Version}) Description: FluidSynth is a real-time software synthesizer FluidSynth is a real-time software synthesizer based on the SoundFont 2 specifications. This package contains the command-line utilities. fluidsynth-2.2.5/contrib/debian.copyright000066400000000000000000000022101417326347500205340ustar00rootroot00000000000000This package was debianized by Rui Nuno Capela on Mon, 25 Jun 2007 10:42:40 +0100. It was downloaded from http://www.fluidsynth.org Upstream Author: Rui Nuno Capela Copyright: Copyright (C) 2003-2015, rncbc aka Rui Nuno Capela. All rights reserved. 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 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 General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. The Debian packaging is (C) 2007-2013, Rui Nuno Capela and is licensed under the GPL, see `/usr/share/common-licenses/GPL'. fluidsynth-2.2.5/contrib/debian.fluidsynth.install000066400000000000000000000000561417326347500223700ustar00rootroot00000000000000debian/tmp/usr/bin/* debian/tmp/usr/share/* fluidsynth-2.2.5/contrib/debian.libfluidsynth-dev.install000066400000000000000000000001251417326347500236300ustar00rootroot00000000000000debian/tmp/usr/include/* debian/tmp/usr/lib*/*.so debian/tmp/usr/lib*/pkgconfig/*.pc fluidsynth-2.2.5/contrib/debian.libfluidsynth1.install000066400000000000000000000000371417326347500231370ustar00rootroot00000000000000debian/tmp/usr/lib*/lib*.so.* fluidsynth-2.2.5/contrib/debian.rules000066400000000000000000000041221417326347500176620ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # GNU copyright 1997 to 1999 by Joey Hess. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # This is the debhelper compatibility version to use. #export DH_COMPAT=7 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) CFLAGS += -g endif ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) INSTALL_PROGRAM += -s endif config.status: CMakeLists.txt dh_testdir # Add here commands to configure the package. cmake -DCMAKE_INSTALL_PREFIX=/usr -DLIB_INSTALL_DIR=/usr/lib . build: build-stamp build-stamp: config.status dh_testdir # Add here commands to compile the package. $(MAKE) # the build should fail if the tests are not successful #$(MAKE) check touch build-stamp clean: dh_testdir dh_testroot rm -f build-stamp # Add here commands to clean up after the build process. #-$(MAKE) distclean dh_clean -a install: build dh_testdir dh_testroot dh_clean -k -a dh_installdirs # Add here commands to install the package into debian/tmp $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install -a --list-missing # dh_installchangelogs -a ChangeLog # dh_installdocs -a # dh_installexamples # dh_installmenu # dh_installdebconf # dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_installinit # dh_installcron # dh_installinfo # dh_installman # dh_link dh_strip -a dh_compress -a dh_fixperms -a # dh_perl # dh_python dh_makeshlibs dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install fluidsynth-2.2.5/contrib/debian.series000066400000000000000000000000001417326347500200110ustar00rootroot00000000000000fluidsynth-2.2.5/contrib/fluidsynth.dsc000066400000000000000000000004761417326347500202600ustar00rootroot00000000000000Format: 1.0 Source: fluidsynth Version: 2.2.0-1 Binary: fluidsynth, libfluidsynth1, libfluidsynth-dev Maintainer: Rui Nuno Capela Architecture: any Standards-Version: 3.7.2 Build-Depends: debhelper (>= 5.0.0), cmake, pkg-config, libdb-dev, libjack-dev, libasound2-dev, libsndfile-dev, libglib2.0-dev fluidsynth-2.2.5/contrib/fluidsynth.spec000066400000000000000000000074751417326347500204470ustar00rootroot00000000000000# # spec file for package fluidsynth # # Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via https://bugs.opensuse.org/ # # fix build for older distros and architectures where _fillupdir is # not yet defined by using the old path as recommended by # https://en.opensuse.org/openSUSE:Packaging_Conventions_RPM_Macros#.25_fillupdir %if ! %{defined _fillupdir} %define _fillupdir /var/adm/fillup-templates %endif Name: fluidsynth Version: 2.2.2 Release: 0 Summary: A Real-Time Software Synthesizer That Uses Soundfont(tm) License: LGPL-2.1-or-later Group: Productivity/Multimedia/Sound/Midi Url: http://www.fluidsynth.org/ Source: https://github.com/FluidSynth/%{name}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz Source1000: baselibs.conf BuildRequires: cmake >= 3.1.0 BuildRequires: gcc-c++ %if 0%{?is_opensuse} BuildRequires: ladspa-devel %endif BuildRequires: pkgconfig BuildRequires: readline-devel BuildRequires: pkgconfig(alsa) BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(jack) BuildRequires: pkgconfig(libinstpatch-1.0) >= 1.1.0 BuildRequires: pkgconfig(libpulse) BuildRequires: pkgconfig(sndfile) %if 0%{?suse_version} %{?systemd_requires} PreReq: %fillup_prereq %endif %description FluidSynth (formerly IIWU Synth) is a real-time software synthesizer based on the SoundFont(tm) 2 specifications. It can read MIDI events from the MIDI input device and render them to the audio device. It can also play MIDI files. %package devel Summary: Development package for the fluidsynth library Group: Development/Libraries/C and C++ Requires: glibc-devel Requires: libfluidsynth3 = %{version} Provides: libfluidsynth-devel = %{version} %description devel This package contains the files needed to compile programs that use the fluidsynth library. %package -n libfluidsynth3 Summary: Library for Fluidsynth Group: System/Libraries %description -n libfluidsynth3 This package contains the shared library for Fluidsynth. %prep %setup -q %build %cmake \ -DFLUID_DAEMON_ENV_FILE=%{_fillupdir}/sysconfig.%{name} \ -Denable-lash=0 %cmake_build %check %cmake %if 0%{?fedora_version} || 0%{?mageia} %cmake_build --target check %else %cmake_build check %endif %install %cmake_install %if 0%{?suse_version} # manually install systemd service files install -Dm 644 build/fluidsynth.conf %{buildroot}%{_fillupdir}/sysconfig.%{name} install -Dm 644 build/fluidsynth.service %{buildroot}%{_unitdir}/%{name}.service install -d %{buildroot}%{_sbindir} ln -s %{_sbindir}/service %{buildroot}%{_sbindir}/rc%{name} %pre %service_add_pre %{name}.service %post %fillup_only %service_add_post %{name}.service %preun %service_del_preun %{name}.service %postun %service_del_postun %{name}.service %endif %post -n libfluidsynth3 -p /sbin/ldconfig %postun -n libfluidsynth3 -p /sbin/ldconfig %files %license LICENSE %doc AUTHORS ChangeLog README.md THANKS TODO %{_mandir}/man?/* %{_bindir}/* %if 0%{?suse_version} %{_unitdir}/%{name}.service %{_sbindir}/rc%{name} %{_fillupdir}/sysconfig.%{name} %endif %files devel %{_libdir}/lib*.so %{_includedir}/* %{_libdir}/pkgconfig/*.pc %files -n libfluidsynth3 %{_libdir}/lib*.so.* %changelog fluidsynth-2.2.5/doc/000077500000000000000000000000001417326347500144725ustar00rootroot00000000000000fluidsynth-2.2.5/doc/CMakeLists.txt000066400000000000000000000033451417326347500172370ustar00rootroot00000000000000# 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} ) find_package ( LibXslt ) if ( LIBXSLT_XSLTPROC_EXECUTABLE ) add_custom_target ( doxygen_settings ${LIBXSLT_XSLTPROC_EXECUTABLE} --output ${CMAKE_CURRENT_BINARY_DIR}/fluidsettings.txt ${CMAKE_CURRENT_SOURCE_DIR}/doxygen/fluidsettings.xsl ${CMAKE_CURRENT_SOURCE_DIR}/fluidsettings.xml WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_dependencies(doxygen doxygen_settings) endif ( LIBXSLT_XSLTPROC_EXECUTABLE ) endif ( DOXYGEN_FOUND ) if ( UNIX ) install ( FILES fluidsynth.1 DESTINATION ${MAN_INSTALL_DIR} ) endif ( UNIX ) add_subdirectory ( examples ) fluidsynth-2.2.5/doc/Doxyfile.cmake000066400000000000000000000211061417326347500172570ustar00rootroot00000000000000# 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 = "Functions for" "Functions to" "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 += startlifecycle{1}="\name Lifecycle Functions for \1\_linebr@{" ALIASES += endlifecycle="@}" ALIASES += setting{1}="\ref settings_\1" 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 = YES 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 = YES SORT_BRIEF_DOCS = YES SORT_GROUP_NAMES = YES 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_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-v20-devdoc.txt \ @CMAKE_SOURCE_DIR@/doc/recent_changes.txt \ @CMAKE_SOURCE_DIR@/doc/usage \ @CMAKE_SOURCE_DIR@/include \ @CMAKE_SOURCE_DIR@/include/fluidsynth \ @CMAKE_SOURCE_DIR@/src \ @CMAKE_BINARY_DIR@/include/fluidsynth \ @CMAKE_BINARY_DIR@/doc/fluidsettings.txt INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c *.h *.txt RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = fluid_*.h EXCLUDE_SYMBOLS = EXAMPLE_PATH = @CMAKE_SOURCE_DIR@/doc/examples EXAMPLE_PATTERNS = *.c EXAMPLE_RECURSIVE = NO IMAGE_PATH = @CMAKE_SOURCE_DIR@/doc/images 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 = NO REFERENCES_RELATION = NO 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_EXTRA_FILES = \ @CMAKE_SOURCE_DIR@/doc/fluidsettings.xml \ @CMAKE_SOURCE_DIR@/doc/fluidsettings.xsl # FluidSynth specific layout and style customizations. # Comment the following four lines to return to the # default doxygen styling and layout LAYOUT_FILE = @CMAKE_SOURCE_DIR@/doc/doxygen/layout.xml HTML_HEADER = HTML_FOOTER = @CMAKE_SOURCE_DIR@/doc/doxygen/footer.html HTML_EXTRA_STYLESHEET = @CMAKE_SOURCE_DIR@/doc/doxygen/custom.css # end FluidSynth styling and layout 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 = 1 GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 350 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_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 = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = __DOXYGEN__ \ FLUIDSYNTH_API \ FLUID_DEPRECATED 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 = 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-2.2.5/doc/FluidMixer.ppt000066400000000000000000002140001417326347500172640ustar00rootroot00000000000000ࡱ> \^  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[`abcdefghijklmnopqrstuvwxyz{|}~Root EntrydO)pl7]PowerPoint Document(aSummaryInformation(_RDocumentSummaryInformation8(h ^/ 0DTimes New Roman̵00DArialNew Roman̵00" b    @n?" dd@  @@``  ;IFXK/B3 r1@ g4dddd08%pPp<4!d!d䎌`0<4BdBd䎌%*? %8  ` ̙33` ` ff3333f` 333MMM` f` f` 3>?" dd@,|?" dd@   " @ ` n?" dd@   @@``PR    @ ` ` p>> ld (    6 P  g1Cliquez pour modifier le style du titre du masque2 2 <  0D   vCliquez pour modifier les styles du texte du masque Deuxime niveau Troisime niveau Quatrime niveau Cinquime niveau4 w   0 ``  ]*b    0  `   _*b    0d  `   _*b  H  0޽h ? ̙33 "Modle par dfautD  08(  8 8 0 ;8   Y*    8 0d u 8  [*    8 6 0;h   Y*    8 6$ 0u h  [*   H 8 06gq ? ̙33 nfy(  x x  f? 8c?@   x  f?1 s s?p x  `? 8c?0 x  0e0e     ?B C@DEF& A@ A1 8c8c s s     ?1 d0u0@Ty2 NP'p<'pA)BCD|E|| oo@@@`P  x  f?8c?B x # l?DjJ? B x # l?DjJ? P@  x  f?1 s s?   x  f?1 s s?@ P  x 3 r?111?`p 0 0  x 3 r?111?P x 3 r$!?111?pP WVoiceC   B x  f?D1?   x 3 r!?111?p@ D WVoiceC     x 3 r!?111? WVoiceC   B  x  f?D1?pPp  x 3 r?111?p R  x  `Z?1?` x  `D"1/ VsendC   R x  `Z?1?`0 x  `"1o IpanC   B x  f?D8c?`dB x <D1`dB x <D1dB x <D10@pdB x <D1dB x <D1dB x <D1B x # l?D1?`` x  `#1@0 GmC   dB x <DjJdB x <D18 x  `d#1`  GsC   dB x <DjJ0PB  x # l?D8c?00B !x S ~?DjJ?0P 0B "x S ~?DjJ?  2 #x # l$$? 1?@P  map MIDI chan-to-bufC B        $x 3 r?111?`P0 B %x  f?D8c?pB &x  `?DjJ?B 'x # l?D8c?1 *x # l$? 1? @p  map MIDI chan-to-fxC B       dB +x <D1dB ,x <D1  -x  `D%1 GmC   dB /x <D1)Y 0x  `%10-p GmC   B 2x  f?D8c?P @ 6x 3 r?111? P PB 7x  f?D8c?` dB 8x <DjJ0 B 9x # l?D8c?0 0B :x 3 r?DjJ?0 0B ;x 3 r?DjJ?  B  P  csettings rangesC    y HSPF1 p  Mappings MIDI chan x to dry buf i: i = x % synth.audio-groups MIDI chan x to fx unit j: j = x % synth.effects-groups fx j to dry buf k: k = j % synth.audio-groups C C C C C C C                                  y 3 rS? 8c? P>  NoptionC   B >x C x?DjJ? B Lx # l?DjJ?` `  y TģS1+@ dFluidSynth mixerC    H x 0޽h ? ̙33r  S  ՜.+,D՜.+,(    kAffichage l'cranffiaR  Times New RomanArialModle par dfautAucun titre de diapositive Polices utilisesModle de conceptionOh+'0Q px   $PowerPoint PresentationoweoweenacPoi355Microsoft PowerPointon@!PP @`S7@@)c7GPoM  R('& &&#TNPP0 & TNPP &&TNPP     'A x(xKʦ """)))UUUMMMBBB999|PP3f3333f333ff3fffff3f3f̙f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙33333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffff3fffffff3f̙ffff3ff333f3ff33fff33f3ff̙3f3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3ffffffffff!___www--&TNPP &Titres des diapositives 6> _PID_GUIDAN{E279BDB8-8EC0-46D9-9D16-CB6E62751E44}_=enacCurrent User $fluidsynth-2.2.5/doc/FluidProfile_0004.pdf000066400000000000000000011454001417326347500202210ustar00rootroot00000000000000%PDF-1.4 %쏢 163 0 obj <> endobj xref 163 18 0000000015 00000 n 0000000677 00000 n 0000000796 00000 n 0000000964 00000 n 0000007137 00000 n 0000007159 00000 n 0000007202 00000 n 0000007285 00000 n 0000007373 00000 n 0000007440 00000 n 0000007508 00000 n 0000007594 00000 n 0000007667 00000 n 0000007700 00000 n 0000007792 00000 n 0000007867 00000 n 0000007999 00000 n 0000008071 00000 n trailer <]/Prev 310799>> startxref 0 %%EOF 164 0 obj <> endobj 165 0 obj <> /Contents 166 0 R >> endobj 166 0 obj <> stream x][6~h8͈wo^_b$c{`>0_'d,?$"YJ-uOόZ D*~[E"o^Nj;8&o~ySx-8gZ~S0*66̨i8|+S9 eRR-IeR2ɵ?W`A3m:60bp.AIZ+a.bmyٖ&vKUyMWDp-ZGAoAZ_, E^Qp[T)YH: g-8./ ]̔mx봵*ϭVa ?f] r?_P ͤ1Ž@ X3k䔠gܒzJNSDVKjQm5{B_W/%6&@ZQ;กϹA_įX>8Jr/y0kF`7^rsg(?fYɑX:tJA% gE~o#@O:3Ob|އ/<Ϣ/h0=Zɓig޴M}v ߐ56bj_vM\ 5:4 pٳ*Cc;ʏ,>>ns$CRC_Vº^;,fЎb+h|o4$|_7>+VwtƠS( !!"/El"|:7 E&eQ^ - YbDaWq~p3y@9sVrq71"ο礞BV ta5t B;bɨLmɭ8:2!ڡU@1lakaOP$m?Kc8"=/WiV6Dw#>;-SmE©ǞQhA[h,=(p J٠qSf9<-f56$um]׿t.鈳:0%|wC-{d~xwΘtVPgnG8>57\oVQrKD-y%_9RN7XZhlI4>I>)礪4_TLZjbl҅oF҃ I-2'R}&^y"&gQ}D|8=ܻt"hȅz796 &RhMC*Q-$sͣ[u8 }F+ѷ8-"r^׌l@T3i7ilAZ Cd=g>ە}>+7(5 k{AqQ^vPGXެҫ_2jP, #1z@jk/B젃EЗs:˦Q3 9ATL d@B0 VSQ2ؐ8aL&L~ب!eȖijyZA#{4Rޛ!%E\\iB[a$rġ\b "`YWMS&$ǰ#yZ+NƃTnWM\Cv?>9+9m_~2p>L[ ~b]a_)Z BX9%I:cm&3BNHU;6(,i-]HАHбVR"qu/ :hl ,C=D'޶taBR֌wIOx4H=6qV NerFrDj4BÍ:g-Qs NE;DH[X#B? C1[DNi%҉#hjޘIG!1[.}9ʲп!0"ȸ?!b[ۃN ˬQu+ PV e`#JZ6? 7sP \ѦОjM>3 #wY83zߣT"*7)p^ *}(5O ~Iq3Ze95HOfk_,9^̱WyhEm5:}M0HkXQɢV#k ҅ ^no$(=׾ϦHi6{(Kg|l-2vYq$7E y)z Z5`2^dh/Z3t€2Q_ݑgj֔8}`Lrod_,$DJ(zHn7C;ɱm:=[Ev|yh 55$#['o!)v@NI͈qɫuiÞhDY;G :(9 yFK[9Cʜ0%kZYOsa" T.as;[;)j->ܚv'FUB#\S/^Z_ m)͡nK'OC%#M-xw 's?{yu^8]Fhps [ia1erJ@3C6jΜ1kUh]r#οt?Ho : W$!7dtњommL9##f㶼U/$2!9E_vX4;`}һNl./AU#}'=½ 9,G,\qɛMK$%7o 1L]%p3NWJ*:CKfCأc|!CN9cϞ3rL9zmClĨ Bj -!vQHr95%&3gW?4j'+gNG.&]>@2y*l鼷8}IcM1&L_tp_{#'INIXϊm&2F72MZ 4Ig(%}sua9iE&׋Flɔƿ%űIm_x5`Oެbo7f^ +hrKvf ?`E'$ّmQ`+*qy0%OF1N<3MinB>N^8 ڹr{u^l]d.){:6f6rt]{NgJS.ږuq c<~G?:?D endstream endobj 167 0 obj 6099 endobj 168 0 obj <>endobj 169 0 obj <> endobj 170 0 obj <> endobj 171 0 obj <> endobj 172 0 obj <> endobj 173 0 obj <> endobj 174 0 obj <> endobj 175 0 obj <> endobj 176 0 obj <> endobj 177 0 obj <> endobj 178 0 obj <> endobj 179 0 obj <> endobj 180 0 obj <> stream N ÍaCb.M7mj!lȻ'ø"B"_)"aD!Blhe2M6YLS)2e2e2I$R)% 8 %Xl x<~`sGlj\8-E7zT%Їqq:./.I8 +?P\Gmye:@q bq endstream endobj 1 0 obj <> /Contents 2 0 R >> endobj 2 0 obj <> stream xZYE ~_/n}D%"$^-{d"~{Ƴ;Yf_=*)dٍG?jjg>nA4VJ iLU9/j~8{Zm6Ĩ3-){ܴAR% >ԋi!8Sf̿W!VhrJ qV\T>j*U5r;kjԽ[U11nI'd! '~rhR +[\`OF9dL>D}6yOe$JQ{ 0]r( K ^o ޮ$UcƼosNJg,R|(T4U ^;WRlW3Rj w1!I4:kF, $*O@X*1zF) eV!YyNYqѳpɶ:|JXjcJ2KAI]gN'щo`!g6P71L(zk(_(ѝ&qgꃩ\P{*z!!~āy 9!9; @/ E+b$eú@Pub;*n HIBجwjC|@BN8ʴ:IMuMwdA \uq~-kNImEz2gsژ6∑bUJxw4E(Ƹc[턵}(?|L"H0{?bƤ 0\' JK/u?d*:l| ubrYi9k YͩeSd%db8 F-u3ʗ%w Al4(ޒȒVޑASG0\>nfP_F50xthtݽڄ^6ؙ#ނ~-NBnr[̉Y+ Nvu1o%Θ$,-Ͻ.:ը{кfz~!"w]0س K}SWmVcX^n>@_7us17ˤIKzs#-A,o ,& "KOsan>(|t7endstream endobj 3 0 obj 1763 endobj 5 0 obj <> endobj 6 0 obj <> endobj 7 0 obj <> /Contents 8 0 R >> endobj 8 0 obj <> stream x]ݒܸuګ6M}8qEN m5hgwfE*~$s|$G#iSt!$~_i6Bn7^~?͛v3רgxƯ_mhZ>ku;#6i8al.ޟ'z!|%͍a_>Lӯc+Af9=ڶpH {8^eMNz:lGՉHȷlӴ}/}}? 6QʋVN+~9iNBMM}]8S75[۹> >QNr?N=uR*5QNzJk=_]_;ڌ{9z8W<ƽT1=idt7-uI~ſ]0.͍)o{4a4wGԜ1S/dL#HaS;pE%ق6.#W銞B|nncUhۈIQW)i~Z|}Djլ4g9sC0Q I@j XLf?_hóݛzo%0}jT>.^Ud(@7 .8 l?І"mUz{i[9࠶qNsd4<,<284 D{u{E"x9kFa0$WӦ'qZS"}R)*s1RG$Vp-7 gdzW#uS)MlIyTW_%t~LW~lʥB2mb2#`s2KI^N GHoГ]Z3@9Ϩ W~ie52Wg|#W#\+ ߏ*ld; ØL%)]W+ ƟFE;i 'pMF>lZ [!ν/fVh9#M nz4T B2D P V|[ϡ94{0!ڨACάhA^x4 "0:+'8h/FՀ. hܑ@q4 Ldz POĒzOvTTI:4E)sao,cUB(e6@M6dIxLWleX===¨5XD;YȣDS2umQD;1i4${7 q"75uOdLbnpғ>H#m1t)8q5Xo4j<4+h4"z,r6BYt뽽}?Dꥩj"m%PВ%g79 2GyBgbᐋ6ZLL&d Za%$>v:GulnQ?nk,O'3JHc:rU;(fJ^sN&(7I<*\eO#DT0oݢIؖÙSRJڠe a=P6۽N>!t2E-,TcGX 4pinY Iq@/GUIߴ-]cӝ75EID&5a&kS9sfkH"8N[N5Q!RtfLb9TЍTLV G>36E#/wh.~ܑ+ 0!WJqJ7orv֙c9NI㢍!)٨z1OT6Go~$jTeDI-z8ba-oEcs5 䖛 13|7[<®{ҢE]U9G ytx 2 L# ,Tيfc^ž'<q0hYYo|ʿ:5EOIښ=Cj4dž%N r$/zG jq9V@F}2)[bƸˊoh&j}&Z+8зU 4v2 prhq@ 1Cr6LZ56qNFÕ̟{JzԒjYrg2uzs%umi^ђ,^/vӍ+{k Kn8ǛϦmloPzRHNf\NWJXdN`N@V%҉fS,ۤmJ.k`KoB:QcI&r.(V2K$sCfF8$*d 2W G=Vg)'2O/3f:SN3C2jܣ/Qx6 >pPv48K7[4bLZfO%:#.\\A=dvb4 kDJEM-Kǵ4~FN EeIIÂ0(.>;7cKaP-Trꉧ.:”&,eZc:p%f,;*ߗܗO@్WKZFS]p*\\ߣS 's=Z$yFw[8ClK g Llvk ߪōL b3~q`Q+Md r?Sc<"u@,uΥɮ>ޕ0.hpncO˹ Қ93ֿX8 "'1 %pƫM1b$Wq+Ntr=$Zɯ@]S ktˏ_iA )P$|*bI,ik>l+38(=1^, EjNH*pv@ ?P3 k16ͧy0Y>Zlʴ(A)Šj.G'vf!E,(+Y¸rYT!OI-I J!!^J 5R nr!+ϡY;kSj?_L{##h6yv EPb3[>ywt!ȫvŁ!orr m@yy2STG)c„ 8SfZ+Ҥ>αjX-ZOXТy,zC:yBoiYpoP; a U#Ȕyc[de1 Ap.J沼k4*Rѫ<7131 _H͆y?C8 oSPq'Uh]S(܅wܵZutMS=#ћٛ&Yq!.,'/V p@ϩ{R^C%KĦ+JZ9|Wpz]QS?R0Vk]eV|B?5V A-Ϲˍ@i1:*S=UL|d3HS{[ēs}q## ZcP1(oUZGeϯqTo:>]zntC07l|7/_KCK?kkԎU0:_aZBW?6%čGՔ㭕} 7RާS?Sj9(CB#e}~ jcvm~R@,oJsWhj]t 幬xZA!Rd=}JrHi kx0=>^|$Vy54zLqlNgلƏ<0xR"U02ΓЗ1i]_z}_Œ3MĢĶ 'Y'TIzx鴃b ~\O6nF yJY0IZ/n 58B8 ug\|~a6€W:.}+Z(\/,!%ryҘߥTE䅑kʹQIS 'jbp!EX6Mjwz +3m6q/WTEx .1`rFnqMuyR0O~zą\Ċ&[S ްpHubcIIuIa)u%X glY>Ͱܴp0@fj2g]:GM4]PtWTij/_sY-F9/e~"pd*$E⣞tkХO1 2u^(X~AE؜T!Ζdsr^ sЩ+͌W5.+'OWӀ{v/Z?RfjOTUx&QΓ*#YcfOCIehTۅ;ߩ ?$껃v飱(dӉ $^{|ܶ~HQ&lh_I y7Kc7oՃs]x^7iqPzb{Etyn|i 6wh9)߇dCٶf#J7҅/? K٭q'9@I(x3lw۳r騊(-( P:OqMVUZX}vdv>O;e2]lJ1[C -2} UZ+KiFC癙3_m^Pu8NgBtmwȌLIT@}0kSqhdġǟ$WE{"uF{R㟶ZR5/u7vʺ (7yuxX_(IHuNBoPmx]+f`=x?.v®l\NB~%Sc6j“6-UQ{Q^<ڙTZmM&vXl> endobj 13 0 obj <> endobj 14 0 obj <> /Contents 15 0 R >> endobj 15 0 obj <> stream x]rFvSL*N 8 g*z KEDrMR^'7Ir@9̌(q@7>_릮xpW~I'mÿ?4j Mw]׵iOҋls:oxBxnґ8;/{=>0YGY5L[^< ͦ֎:z.omObܕִcշ]GBt'mins>/ԼZ&Sq!JBJ'bx7(ݱowfuh,Hc x3\͸Tk8pgmZX K6-y:atj'n÷ŏnXIpB@ @w o |Q0@Bre}N!)|6I7w(DxRB\seZKF4:h[c#:p? 9 3,_YVn$>A~1 4''C/{? "Gx~1ҼGpߒ:R[FቮUQ.po2k pk g־;A++ ` J(K$i4GPP`_%4#Hm"9ے/_xQm쫦cUnξ89vͫ]xI-}l6.d%wW;/gI zS׹:h PW4v4:)vK0 U;Mv@G1DX.فu[ty\jxeG\,+8)N(ݛN=M%=#PjЩ}lʊJwiϱ|aѸUe ]GYKjqn6oiɶ{ZQ>|}q]v&k}qemmXkykM\+# JLoN@tt52%<:E)Ҡ8_yk,+1w(U 6{rtrWfGRYuD"+.t]Fl~hdO/SΩLCB=BX+IM P2&#_>>7,sA"ݹ=+Eׁf0}h7d}_gJGmc( QVi~KIS$vY=`(K>I?3(F= (S>U8daWʏaW:4.#gm^>z'z\+ tSѧO[>41ME]uak;k` xh-c?/c1˰tG1uޅ%f% h7޶z>ERF8[$ƛ0сyOo0Vzm+C877볓?64/-=hlM~g]tYT"ФXkk-?¥뾡MiǾu>ZOIjm;EqwX;qp+wKj#Nb߈ ߨ1>";)gz_Z{υN7ƞ\]Xl3JC8C:T,TcY9xG%%f6qSQ66V[ꕣHrҸ &6C.{LHk5ktkI+UZn OwR&HXD\_IO@7hw \뢂 e!1V$p9<x4xˠe&/7WNM #I# l/c~f|Y'0E$=aZLPk{/$q.1q%n=yd'Gm[ 拴z&) tsrF5B>t]ޢ>',qqOCby~?]M+ 4]ųt攓81wqR`K21S3 <onqW[dyabA$dⒻD.qj}O`2*G x"tVQͬR.4}m8r!RG!jcɟ *$K-XuDg|d#W<}a-^k Bx.AG RA˩DD;EfH0,o0&ȺdmSX,Ivo ꐈi`]#Pg%uq 25hƈ.p'Sr8fzޥouVHо`,9 m0y2_%^s߄[3:>?w·{ar(|˸e`;-_!9++5M?5eFw]@X֙_tύʠB~pq7 ?n!P-6ŭx>W.w+ӳ4|mֺ6ݾn9l6Ő.|Ñة|}uMS"dݭE \Bk+Q`~3b w ˬ?fF4G>yg*=) 8gca(hL1hH1ȋ+f'hb5cƺvcR}\O!R,s5Rޫy'Zr1yNIuL''W&7qv O(3jF@&wZYoivKa?|ʁ(@sQ32-=(ڋ]$tm`{ep \hCN]qH"W4bd+r"6 &&L} Ѥ"7?(DUNXPLG/@I$ !N#-a DR9޺}I`)Wqi6#旭VXfatg @ 0 w='sn `$wp6IE:tGj\KRoGv7kuҺ#nbv 7ԵKS5l:obS(^q:$͘l_l6NDRhd˟)oG[ ?Wu@{WΞ~Vd~wϹ=$Fڹv^ AtTa6Tj>*6"\ {HBN^9{yW{qusa.jIx9]LC/e>ҽ9D5ԡȺhq/CRHQWeT(B*M@N;9z^rpJ`qh]ܢ;Ǔ/Q?yB|ԭM:Đٛ+{?4[lTNJ d咈!㶥Zz9 ?mS(ץ*҄/4յ#ﻩNOSg'4I \q9,CxqCp%r餑R!Tv7ȩ}ʅ8atd`Bj@$>Ș]F.(_Cx/" R\/Iv:IPD[`PVJ H"TORVTbEM0y*Ub7#j1+Ip=#Pd7s*zXH$*y$Oܘ;VV,gJ-Z'+VW^?kFkH 7C`׿ +$N5|DM},9D+[uRqaudqON Xas2d1i<-z>+s>l[_>ό펖Z9J59*f>X;8sfl\"h6%ɰp|.wAwBKJnD*sfL/]gA&+$T]F/$!%ظ3Gܕ0&kVʻLSj-Ywgκ4ϿLUQ䳴$DC. N.[؉ub$`hVXd6xTr#RY$0'mK Wqow;^s:b,Q^3S$ږv !!:[Aǁ)sasADT؄z6 O%(7{8Ϻq2FѧT6s SN(+C L v(BOǴ'ޛucиw'g5!aYܵMAR #Y=@y\ԤxYASuw.:PXWrbTãMYhNd\e_<{%@v .Q潔uѳID03HaVVfX$q" ՈeU Q7Gqu7HTt5q9|z$&'lOUY SM!LN,,zC8M"bơIŸ`._p0_X"yƅ{e< A->7+Ua)1`Sik`f?73w`&Ճ|;m)S}ғs1f ʙʵ"MyEKEZcszccrMW) QȾg^WJpiq|'n} s60vpR߬$ŕRdf&ٴ\6Sq.C@d%+«~t*5s__'w jWky^A<#ϴ,ʨ"t('M7sf YJ!SrCb]OYQ8c0?Ɯ:%3'Y}Da"^)/1v$]!.Rq OV )lw\ɵm x9L0${TTuW-2T>4Q7JZDk'mJȟ9Ak0l89صqokaA}kE eWJv`.ss?3ɔ]MRe&;Z|w6))1$}Vp?~ )sQ!ˤ),R4}~r[o4klx1*@ pc:z;egv4#=IrT]<.+hXbd,Zç?5/c06! 3 C3䂓8rU5Ȩyo)dž8(?gHYC"I/4rl,k25*rn%ǟyt3a.#X |L0 T}DP=x";-v%dVxRfQbW30Qm%Jq2?) }?4Cry^3n^!HvAR+3c. ވ'!oI-B"oJx(3vaET?UBS"!J]߼}lW; !+u1. ڮ.@ԑP@\iYPP\īa\qNktZ}HLp:wQ95dWdJۤ)խ̮eҊbj_?&=; d^ /:dJ:k1䲥˵0 ϗRGqT&.]>G #s$+܏3,tlHn# hodw+ʱNp+cg$h~ />kyBЖg(ukc-tj}0Q)]>h}u\KCt~ZgAY|1T_ݹ˜K`({\X#W)]_f9oa',H]p#(jf= A,C}uaڣ 4;͠Llʭ@)9-+Ë$&C?(4Ʃ zkrx~5T+Wrp7Bӷ߇;|2\AU]b@weM@H ypk`^mW~ 0ڬXV^.w^3.JϓZY("d+ d 1%=@1ְ4NsXU*u%GxIoeS#4IAx& 6M HQȗyT=GvmLCZsҋȞ!:WOQ% wd帉MTA wd G7ֹ92B1Z{endstream endobj 16 0 obj 6890 endobj 17 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <> /Contents 20 0 R >> endobj 20 0 obj <> stream x]r}Wli(ؖŊдXP$cF~% ݘi/ ;🳦Vzğ8x^o5W[ o<}v. 3v: 󵷳|lqֶ A/!;ļoE[nRMZOEu۶_c23la]riQywyTvz}lFRV]GB;kXmj{aYaձbrMb[Zjhlpkݿo-օ:Ms|TZx}ڴ%YV4+mY`7nCDʪTwۮ|tmhc&u@$K癴da0Y4 0فX Sl;u(^a*u.ILK\1V ?c{v`y;)6t$~?`S;V@qneKygMhgzξ_nmMZkk4ɘS1/1ED:#8!%/iqad~֤*#cNp2!FȟhP4+rLlU ZO)'|Mw1ڏǜN RǕzDvU6H[vNOD}S9c#I!2 B.4~f:m%)El;u4(qLZ(^EID䣘DଐRС4=c7(h(_#Y2ڂ05]_M *'Fkän~|[-854HՌ}: =dV=9. J U.Aa,N[kqL& >[Hz15Et8̚jNPy ;C\LEyQ9>#{*2k/K$3"dR7i"d.fuȬe`i51*jRulɘ(w@r[jpjj4_gT H Pjp=NJpJMs]Põ|,yH՗dR7Xc}_5b ؇ [9/uGHbNA0-۬uL6$2ܥɆ&IHf4Efg?\'GX`3K<³mgؽIT7#-4R3)?xߔ/$fY6ġ U)L ,ѺyU(uVMYE _ Viapگx2~2V50폴Z66aZ\]yP^JKJ|qz'X,*{8b*΄v Nv.agn~ ,]o1cSd*R d/0/} kzMK!篱ctP02d_ Ui=!76ц2C y,O(" y 2VDR[!B^à`qT}u( {^g 0FKLț9##/GeCl;/޸ (<,z־ݛ1оCt:XJWʙ2>u]1^8wS5{)js+eS/)QSi~dbCo7!a@1lS-.S[у a&%ljz80;lJ.xKw׀?U>Ae:c@̔Y)bmLt%w3 3uX-78-Vנ#+ Pm䮯ŎRTK pF=`yj#~ CW6ΜK=9ؑMw)<ZiE߉w&/]wh2[pz_;7j4l[$kHN12|:M#FvT5aC7īm5;+CܛSr2 ?/ 030a^X &Em3׹O$s%>AI>>J&I!Q(.>Jdq"$27xoDP8{[+kKV_ʢ+az.$k0=5z|-ZL4CJݬ&^^{sL;!SD7 /H3iCEY'WtMn7D]Tڗ{-]DƋD +Vq~iO#h%wz Hͻ#8R3 ҭВiGRw7+0-F%!:*9thcњ.xMFϰ _Hf0<$ի5ۃL~qE?^ ,!k]uk技% R `+c w3abH-d!x2JjGt&{IJF#q$YoL$lTx:r|gl~?\˺RI̔ҔX&I߿GI4[#/}ߡi U!o҅(0bNK'n@0I u0S5J%@Fhwm+ZP׊]ټ΀/GՊĐ ɲs, 8ȅ6no{zI<{#=@4IV7陸,5~$Lj_H[̐KxаGң F b N4v(=,&5ž% Xt[Rx+lZ&̉7y|ܳH:흁WpI(9~4&\v~ ڃ:+/$}F1URHy&-\ZV~{˲l(鬛Mo "/EӼ+.=d)  IZi =G Vw!;j8UMHzxЃ<~*QtE*-}>CB$=7Gt[F?%M{{!YvD+@XKVL˃\]'`߄XNSnE^̓40?*5A1A{ob NcS{Bj0NxaϘx2{>j0%lE3]ƫV/MY_Ѭendstream endobj 21 0 obj 4812 endobj 22 0 obj <> endobj 23 0 obj <> endobj 24 0 obj <> /Contents 25 0 R >> endobj 25 0 obj <> stream x]r7rϧ8“wUa;ڊw]Y[TERQ%+\ɫi`=s/Zym"__;W}7G~V/ՋOY}^+ޮ??껔zUTs~ѓ߮U/wzm;bT7gto+;üoכR:~V}z]㗘i2CzaOʍP3KV(U-@0&@O[ BVo% =j2dxT虠)nP[vGuH9`8HXC'&/ LMP4ZZ/d&"*v(5kGa5ANAͺsZZͅPLN4)˾Ο/g :Ī(&j=H70rtmZ]4`SKu*_ABgb2L߫ 0,J(=`kdM&>N S20:h74F3 ER4u8yM$[hSmKev˭[iGU >;z1]4̈gwU5UR(CV^Yat/,o-bs۲ e], վ-i@QDMaZ FEEK[Q.; 6\G?@C~4ljVu[K*{|o2L)d[xCsA%PXl{Caa"j8Vbg4Yr;Gi L撡l1g7sɈt~`D&k3"> Ol4&6>ʏfC@eXpXD>ks5ڡՙCPtP^5Tfaq83K|Ib= ³e6fQj%r 1Ug'2 $@7-9\Aĵ0+0jʻ? Jz+v|tNy8wSGW告},o}[PNλ|*pɴchj_ܴB=\r ^Nm8,)/5XzG\y!>m$ʤsWQ֝"rNP]ҕ9IcU1d Yl7L ҙ%̞!ߧ۬6{W7H :G;wdy^IyV&g^\^ ?\ hM.⨢V2J6<& A;V6 Se?Z*?\ 3yq}Q[(H1+:@GW" .u$߶Dި: U1%;p>%pWb$cS4*PۨÈ͋~W 2<ƌ֊ 3?qKoњShӰM`t=dX;΄TZZjqC^trjkznVbބeFdrt%*Y;״{IHb6C,t҆$2Fsb@O#Q^6 d&{fȠyؠ0,Aģ#]x:btIFw59I6qmؑm*\913b 2*8HL OJaIȧ|q[Fka;h,gNv*Zv<<%{V!o<%im&kseG(pkkoD^6UX * #lSC<%@BTQޝ:eB ?<&v(tZ5Tk% IY ?F 2EAհ'{00If13hu?K:x8g+*pRΰ/]>Tu`/ 3ei3M2t4 f0NwFqs;7pb~&RϙH-,m̔K#=$É4l];nNmardN\29E+qg}C`vX$Vܛux&_2CJpoϲ .bmҏ ΅XIK"*9XWbGr[nzcزx3>,m&RbxO#  ԅYI_Jq=9 F&+maC/(cޯ$+O ]@mX~I+f8,~ՀlRi-ot1Smbl8nЁ){lzx쒪i4y 䜉:ka/}a|HpQ]IrؗK1۷Hp}7wMqk0M[)%Ϋl[P|R~8!x6&[=Uy\Tiϛz7o-|O&,R` ;,يʮF$#vo( 4ޙ o-p#aIhj7\ Nw6Y)Ƚӈn>M_lY]vu;^ϝ_"2\" ~'p2<\7 #!76G| .fw~3SW\x쪊uoY5AIJ@S :p|V%eG[G{#T-؋@mx*4NgKx֨"ˤѱf'O:>O:)lK{8'TvNŜKl:O6A ѣ"-,!*ҊEQӯO7f-0kzy&l8)#;MLbS9>;Zlex?9ٺVÃe_cQ:}q/2F2}=X]Wyte4[Ow$rjx*gv穙)d'X9rI|̚i^K StTTlW(`9ÂYfn=üL,,Qxz]t|4|CL^ē3oAhPOPЩtC;@v}{8uzQ"dka.\s[&jzs9Lc!6U}QMdn||nRր/X[GG3|`P-S}*|,9T(!>z0=~<0{,?ѐ O-$nAKLJN'Nef=yc#O`zo{sj˖R瓩@LB; 62 .t,|m51͠9_yIJX*z]'إ[7 ߳܀dG6J6HvqՍJvr,@AdTь&hN:! |8%òKn!#px],6뾳*)lqv{xǔ KȴKȿdɀ0<`Kw[@#Mhygaz4]4ǘY{D.628alÀzH(S(/0~v2ɾw ʕ59+AX5,Cմa8V8Ij6fCÆuJW*@o^o,Y FƗEH 8%RzJ3PB,c|zm>QGI{R9[}$]?&f*3vn):t8t[Xz!<6/&:K73.m8qY 8ҵKևrAf `},\$(jب<Dlh8DOӥ`DeHWK'YFFDӈF;~&x5u^bV)[cJ|z; ;folb{:;bUmxV#`bKе`bsj=b>BBr>p 1ۮMW"H+V/8%Ŏ!T%`@Z(pmn&:% b_7|īGɨ ֚P[ {qaK+j#$WmvBn!-HR88>q#x24aDqkFKȀ;8ZZ}رpRDŀG _ g`Ո;ge{WXCCFb@i5%(8ZWcoC&HFk͍endstream endobj 26 0 obj 6153 endobj 27 0 obj <> endobj 28 0 obj <> endobj 29 0 obj <> /Contents 30 0 R >> endobj 30 0 obj <> stream x\n}W,  {Ab˄M.K#%骚ݥ(IzТ]Sk^uoqy}Ӱzy{֭^|[u4j;:qֵ)% 㨕󭷫|jq7mmQk,²Mhr)7k]afc3WX_USYr*T* ˾kL\z6\TqonU j(zg Mcoq;vXvT-g.u:vA6!6:4>۔bks|hS |W oMLMIy}ۥ_4&]'=n WwrXq m5 ~AK@.vho M˹YX/i+}b0"lZ4]٘p7C'M>*Î*tG?؝_5Ъ.ұ1YWXxϸj ,c 7Xe}|X"9GTEm،;u9B|A{?Q8{é83zؗQ2ry``ȴ).LB/lJX;hNi")b%8% {D }x)L(xl8+է$M{')'=i6k՞x*RY4@dadMaaf-n*R)$$}S&/sgS6y*R$?zV6*ݞ26C>5ٰ\QH 窜ϻ^bU?1QskR@(a'Жma2k{P9[s! T9/^?ޢܑ-L6oU1>cRM>sB!HV({ԇJ4jBӓ!cd"C%FcBfk&}@]hv$%ͥ敠 IRX{FMh.mtԃ \FA\EB$(-*ǥAOA}rvg4 <Ԗ5}PaE..Pʦv~3^ %J% 3QƵ>+LArq; y=6 xf ) {|?CNJ1кN DڨXš>[U}@Y 9?̂+p+ AZًpizAN}¶2R@%Uѭa枘H3Qw⦹b]ukx=/vhDyͭh~^labqxor%LhnXINI&yIfRl XȇR?; 3Ue!yĸaPm8 9E>c!\ mF-=>{I=h fzѮ@AG>@j8&'KXRgcPQimBϰĪUq=&𪝸1Dx|HdH2 诪k{??ybn2ێ0$RI&12 ruMY+:ٻM f`Ij.=H됥$M(z%  # tQZvL'@8PT=\~P<效]vثz2GBXJB=a촕6~{41Udz'IGGiX ]ކHLS3_³@@Ϋ#wїI'hܓO'rhM1ŠT3ސ Ǚu"f+sŠNS1s,4Ɯ_N I+Ynu=;7l;ݎd9w; %q|p@BHέ9!SZ҉ۧA e_lA1a&i#l AVt8}w /؋EbEԯ.%1 iK7N0؅ ީrk-}+6ryz';C(mK‰q;pe{uo(Y*ȣ62u~yڢ,R빐֟H"\iHrH֗n#Iۈ~V }zIJP9;}K<\:ӕ {(}|l'U>/Length 5364>>stream xytEfw"(*syO@}YEPvs "(2p<+2q %7 ٸ7otn:}2S]_OW%?fv'$,ؙNݵ˯zku~c뛤!Tέw>_3:amzw5u)PueK\W)(7sܣ5G*_\DOI^w?<~>^?>-CHǫ[ވ#1c h´&iaUY }tfܙˮk%\̕(|j/Z0dC'fG2)ZQL1C)M jfMת(݈qa !E(lxRbډYGS񬭒ď`m+Vus82yWRQлK&ZW35D['z[uJf5N k>e'.H%*}{Ź9&^'aJ8`'=.4pgM.)8<1UfxɅo>KUM*T5:=G}WeeuqETo,E7I9׊R^gYJ#[b=y`b[<_^h2@J-|^9il#~^syuUwҽN߱ejU,Π|elɪp{mYmDl"h!Cw#L{V W򖏸Р/,uu:[+x]bHBUZzy\׽{_{q扢g>zSe{?"}]gywY^{z8&yFxQbDJcHᴏKV)>*A1Y+qEDi|qgük )KS"KۋG/rq09c_%ܘM2鳹ã^w"r-@jUs]&Yuk̗AfR/◾PPT5FJw ^}Ɲ2UgzG12^^m2Êw.W*nW}ݠOƐIj[Gt3i"#< ipzj2Г/sʕLMnB&h޽_7Hq\]deNRz/`aJİ)*[lr=c)wμܦ_q=}W%%NwNߜw=Ӏ#|(u낏hV9#‰ɯBE4 u]Zv'gkdyz_\;O2鸯Y_`ԞS&^'M}X GK.U !lKI==mȻfUߜՃt![D7I6*3:|r\qKb^!n4ubsY>TE2HV+Y|7\ϭp󗥑3.˜Ox]**RʫEkeRʫ"uO3d($Ą;$*JR⢯Lj6}N^uG^ޥE{ߧcK^#|d!ޅa9.^"rez=/7}Q>fъVeCeS>x.DjOL_.^&⑞J=beZ))\){[cTPb=byBTxVVIuh f,K;R ɤ^kE3ńיug*^ԕ1/&}.;kVAc/#?t? ufgQ1c1skv?9Ls?v||3f$7w.Ϛ9ǎIO=27BZwmVJe}ld{wm$EJR Y`Y*曇/2Ulﯪ8s>wi4kuOP&T낙ׅ:7qw\^^м٪ପпM,M{0^tN./u.;x Z+RU;yu{i5Ui]0v &*.}s,`X ԑoc΄Pww u ql?萭0xP7`Y+eK'a\+mՃ+:(ؕK(R IzS2ﳿ&&`K4㘴U##d6~g'XW5@_U(WztB%A` t\^6 @9.b| :IgV3ow^n| ZG8(Щ bYluv4=: סt{U -ǻ u~Qp_h][ò׵c/mhp),8%u32*֞X_ M+hU%{ QlŴu)^_{qwǾy=OZ_.xMbdf𼞳+u|6&{ڮau_u^{Ϸ,s[(FfwF?>オV AJơ؏;C4Fk8,;BfеK`yn_[/`离;Ty M/H {x7[7㏠lWע;\B- :qR㏐J&sx?gP;HT^OlSXpV^2C;^W]`:yxERruvϹA@@ϯo?#>>. mbz#W#6x(׃`x;&-}s>a endstream endobj 33 0 obj <>/Length 7740>>stream x{Ž{vYvv1QCԜ$|qoAh@ @#BD#<}(O(4ȊhbDHAA,̭z{U==UuuuzzTϴS=+?LWV.f)@l~z;%s~׫wTJIJIЗ/Nc5WE "˼7bMJDlK%/_qn$ry›3&fjx%`JIZz: ;DݕUā=>uz+x^WmhJ_8xgNo x=}8']OTOjbux#:! 7O9JP^'̅,ukV12+շ ^(H>Z+A=x3G^?$&^wz]飆FD mN^uz𺰠 ~c`o#UlӜ]Brm[Qt8a::x^i‚&_q/m?|oKꤒI4m[?e { 鲫h;.w\ґoΧpmں0OjU)lؑraitjڼuGny>{NO3%ט%W-2Vmon -;۾x^~sOO*N}<;{^3tWԸ=2cmNՀݍGV7מխLnǭg>6lh\}Mk+َt!ΏSԃI1c^$5vo1r'mǟv)F,+2Z٢0z>?n{A8m)Ϗg3mG|m{ߎ/ygf6t0₲^S^,05{ Gu&eʳ՛:WѠ~h._:Pӳ (_k̑Wo%{}֕=\RfV*{;=j]uRS{s5AVnr.'YwǷ~l{~\jcM~~P*ӏ6 p#juRzU`DC>'|wo7Ue ݔ~y[6_ߞ^}%| ^g?burҾ[֊ɗqiY>c@xx R5(2Od6ޔ-//zUJ-xN Z\^w 41feK.dժUaÆ^:{tq>^˝^ϾnkE<օtihJWtϜ.#>y||_޷^n:/s 叏(7o߰+;Z1oqo=K^2jgmlb^9 :`KL|7ѽ^D_j3 /:4_\6m{ͷk^ڊ"o#p=gL]}1ҟ]?ߔ7x9#RmZtm3Jjˋ:K޻ԾxL+e5+&v%lӋMkT\o-ً|zƌz+O gTza|m`Զ}GW2rH fmZ9MUӜUMw>)7Uy哶3fe߾J>_]T#u֯? xɧ^Wk%b mh:^?~__Όdvg.nښyݐ׹^:eP퐁/<|gzgXR`D;j>nލnFl9޽{ܓK^<|>ud⋍5=SOOfA.'lӳa&9\vyo{AY0bܹlQOY~s娳J#_}UgTͣ[qKk 7ϪX]TzÞә&=?J7mo=scj+q jԳvٻ[[/{^^h{{^76?{m!}ù,[Ro-+iزiGs6{e{~\ϑ_)1P8kPﵜ֯ vƣןVup7旊:t~#+׉IKuG/N;CzsE]-[m/}ꗚmY[ʼ߇j[|OuFFt[͗W}K{X^h)ĥvg_\oY\u'/mK^<gۛ0\m±\y)zvx^*ߔY/niMwPwx^U +9S0Sx>멛2ׇ7_wXw%]k~V$M-74쵹;??j|uVz:)&N][=:;?S_Oʙ7h]y/Nzc&{>o#>sޏ7'G`n6[y oSxsoL*b(gk7XczA;-$g_Z+xؑ^>u=u3PŝRjjiTJXk|b]}i%kr:כ5u3ñNuohv‘C??*jYÊì=uMxO_Zy%^?lm={6Kn*uzNkґ=V|YQ|uVz:)fN.ѭכWd<~y;}Š]& 5׷.ɾݛ^\%[y כ3:cעe){n3W~װLwqSLl]s ]Vkϸ|l̮ TUw8@=YY+lx$)ݾ?46mܟ^Guq9췻޵O769Ԓ=HS7loC Srv[ZYX[yGunƒ : ^'v$--,ܾrh{JywTpw~>[ê2`G)ǙO>{nfR՘sTw5A>< { r"/5gӽΦ3kߥapo4l˂qW V[a^87l}i53ZK3.ZϽ]3.%eV ̏G-?[^Ɗ֍`~&}E7{n.3;kI4}U8smך^hx>z~]٪ yfCukͧRo[xɧ^Wk%b>|~osO.MvdJ9FW͞<4 #JM&x ^U:wd7M׀a D_`Un-91R>f^~k&2 B+A=x^/O+?)u1J:xɧ^Wk%buR3x^p#juRLx^ʼn{}6V:ȍ_YDG,+շ ^(H>Z+A=x3rOB2j/ N3 B+A=x3rx^;p#juRLx|'^_|'#X[;C6}*y=‚Yө^_޼!SZMZEd/$ʯ\”^PZE 7%Q@`IRR}x 9hpY]~5ķ_Q}|+rb AS;WΆ۾Mzs/lC.z_BLpsMngRd "W۽UϠZI+jzIzR׵}xJe߾p@zݖSj^uGע˼mM]pD/ɓ5 u-l?7KTX(r<|P]bq (4x(upyݚU:_&ON Ĕt: ݻ^H-JRgt8:P8`X@:P8$c{7_HqN6 Xr*$`CLxf^K: ΙFuJUq&wt~垤o:rgX1>!uY =epWKm Q/>p2:^ ,:[_ Ap:]Eq|Q:|H ܁ӯ{\$,?rzJkE1D/P8jE'/vnQ:-;I|=;SĴvY4nOaE%(tS^صvP@씒3q^J+~{H?6J^wֻҘJޯ~._e-(^F>j>m=Q3x=& =:e+bta^J}VEq(3V^ %q~5y[: Q_LC"dTw!^x\T{AF QGQ|z9F&b}QĮC4Tg7!v!O۝+%QI|Q/JK}fz 8R)*5j/PU:y%r^ zgG'}i٦MTu{nmLႛmDf>#7" 9#N[$pG.;<+918PxfUH$` GQujuɣAOWJv>Dqx=x=k>'P"e_xR'؎&"n׍O($E(}C%k|EJ;0ȿ%/ N Rga׈%%B难|b'(7ߨq,ayyj(\CzNəx\Tg$>y$^. ]v=u}t .+ͫέq| ruIpk鍯Z_HiJ%^wosrq)0'EЃzJ==սwd|BN)U"ZAbyss/qs|=߂:3RiSO>5^+aŏd Vҋ뀾&:7|x='=Șz:Džҵs4zn(_RL%Lj^qĆIiɛP$ȓk踳s e|w۩!Vk>H:<4L'"/qSgK[_TNQdŒn*zyO Ahx=)Û<^Ks-z=!ZlûÔw$ވ~n#ӷ PC:9U8mqq NGQE+:.lC4}ߧ<\#߇twG:>z;r|>8|}#B$<}5ГR>έz2:u ׽%Y^wR6܃&zq∶uJy]#Op ~>j>zaA7ynޭz.#zX^SFau'ߩ-$ j^+ā5!fwzprh4>H2$א@Py")39ur}C5#<ѓ_4S^3c5z.|;|!D7Oc5j*?N~N.Hh`spODt߇|C^eJJ/пG5|J}ɺ$^; })O|_rևDQ\$_adQ.&~EՈSg}x<)q|#TݵHS5Sҋ<&|S?IoΰU$e5h /QLݑTV͇6qW7J8>o׍S) CBn!"oM4,Ioq$E_~8ƍ@|^uKbtAn,bz5؞mz'|5|T5x=kΟ7ףx_!l}-qWIl/M_Y)[6΁pp7IꋠtFi$G)%,7773lsL$e^C|!~i~[N 7V~盒;E521:4lzx'E X ~v@ E"V`| D^:uH}xxv:uX/tC@]jnh{VSFv >_gjwIz9 Rr |({s@n*z}x/'^^<,\2|5^wbЁ|}ڽuOGh endstream endobj 34 0 obj <>/Length 9283>>stream xyսǫfa@ B513.^yQhTGFG\Qb$戈 I41[;:03 nu5=5Uw[kW?@ͭ{wos>P̘>3t}Xxv߽FQ?%Y޻t}I֬ DUa.'>%)%=%I˟ER܆kU*] {y^4\9ȝy "%[ZR=W o΄ST9)Q)^&-ʉs-h=u8v>Suz):t]]ű]]WKAסt=ؾ+wO│Cסp-)ҡz$q涏ulL׵tݠt]t`kI]+%]'ٌI׷ww)%]wn)wXV/a?Dn, t[\ :: tNw.LȄoy7U[< aU5haunq-u: 0!>b[cНۣwOUﭵOֽ]7X۾c9W>;5'Z5POmP5xariG;fkz G۶\S"BYvʩGN;vR/kzx~.]Q:eKeY߱ʍSJNt !.z][RCJ AI6SuJr[iũvM?PõX[eݲ[[?heGyWUnj+7w'}9ȺPOnl]&<3V 8ڟUeM?eDי/8X6j4`ӛU]~6K{a/5bmd]}iߪ;CWխjk;Ӈ--,aԆZvUߪ۵ǭB\Ϸ JgRzvˠ|+\Kt^)A>:fueRw妷^x@Ɛݿt5LKBNuѩu=DAO/ 6=k9ui!{Tۥnx_03qbG{'DD]zЛ-;3F?vvjk\f|WUV© ~3( N2][0躵S=~oy{%K؟w /Pl^>s t}7;^좂uxsPL/԰j;C Op)_2%^>ӱj(';'sJ}EX3~}z|i=lvDXKA]^3?g7mhɩ?V=0hv~浰S|o#[X/\~;O}ǨWn'&ڡ?QySg~Qߧ]㝧?kZ:7,zs[{}a6嬟OQ/)H{~^Ui{;4ons2{M~Y,1_7npÍ;=Fb׭wuȺiuIU !i޽a]g/_sF8)o}uf :ߒ"WJNA]\Z^dDm3 gdδQt'$"]:W7oo9p\3ouxgxgcNtϚg:֍LYOu4]?ØrBn>yqaUP[A/F eSb]wY9S?ELQ³3m?ׯo}BΔG=p%ɟ̼:w}U='&ȮyW뎮?m۬ s=iwDuڶ3qV;BwW2fLٿؾ|MJ-ɷ\f0Q߶ڱ㼃uvqб-x }Kկ9bNtY NH ]wv@ėңuG{%]o[07z K=bmWvڞ~q 9>p,_m=y;Zn䜮νwpo }PSs[ă'+稸ۏ^}o;/~+f58~o.zNksYz닝ӞyB˦6w?X/O׋! M=An%^\H[Hl빋[ mǃ&nfF{M/b]]R݇gvs~TηH䃮lBU_ ٣ugKO:&+V{5FLusnY;e錢;:;Xγ.|b/}k^{1Z=(n{x5oͲw}KH=z'^uYCY>kjV\[x_rӉK[]??=2aS`Ң-v;^t] ג"WJNKJss]׻tgBvjp^L|}џl= Ecܺu|V#X׻0uG5+8暥?ndo?w @73Ԝbn>*|lڷol3jԥXkzDCuΓCv5}YGty\.ޯU-Wlڝ?f>=c~ޯNufyJ~zy?w7x$|u_],]'V$M͖݊{n_:%s%w67}{Ghm'6v{{~ٯyG4NpPKqX;ۮYGL+y/͔N!7?9>uzݏ֫u>3n~վJn]ӿ60پN}o{n]g0?l5'oZyY/v|Qo?#Rו(~]IW6%]@יb?P-)ҡz$)͍~<ɟsҔzԌݸw0S,k9l+[/}Hנ߲־{dRunA N{lVZkXa?au=h VGםc)u'|s:^1):#zt] tdiex+et]Ft]y:: ג"WJN ]ϜAס\+\Kt^)A>:&tNc8]Cu Z臣ZKnP ]NµH䃮lFSupZ8 tb N^:](%]'ٌ\ t]t`kI]+%]'لt=i`_חL"P~h%[јb3Fnnu=z$UM;C xn#^JU['LPs_U"/[_[-{GRMh>rtWeBY?/SR("jX17G+ qI.bP"]B_ L|nm]/u'17?% -=\}7JW~VJָH 1hW:|]thnוKVi~rm^^J ZcGw 4D:uX\ix@Sw}żB'\D,jE Һnk=7G5u$ e?2#%˭=Hܠl$1t]g:NK:fC Ww5Ot>Lu: Kף؏H(E~}PKC9ՒQf K׃K9꺖/an]tb?tGn/!Y;%QN(vo%~*툚dW6JtJ`pnKw\D^Ǡߌ+u$bA֗(=:f4OIt~)$x6~q)\-ס . Y::::::!_w׽$MO%!sBN;衠SrߥŸП ~3%;.0^ww/YMf\d9zYE+uz=Dխ'[}fE1W: ]63$CJ!ý82%?qON<,0DƣsD+$r1Us՘"xݽfȥcy/*/&qՏ(^e~n$v7;KHuXt]?!O}tD|ۑ3q2/hNuW)!E׵TXޟ)%+M_b^`HMTr jQQԲt/Ⱥ#]z{πvU a?\%2̌|hJb?+8)hV'*LN_=&]dKsLY;1:' XK_u ; ^u ; ^u ;$GFew=x>妳DIҕ>_١5d%Iϔ@e-Se6. H"-ҟt" %ҵcPK6Pꍁrr3$uI6=ѭ_ğ.uWuz{'t0^B$x]yH}J$nt6KOsiR74ʘ|=ewZxߔ~zr%N]z̷ 8]sb"*Dhixs&DCp.:Ռ q=b-RK.EDKܟ.M@W QҹJkwwYSqsDuAڔ u<3w=|Z@oasVIKn?0~+w]W[NntKkO}˾[T?$mzu}#q\)UJ)|=]k&3+wdfzr=5u8[f%4ԕdJHt]21EvX\DuRvt:с $E×Z]rI:w[! o;q#ڧ b_wNS.uhMq8CsVI݉t6ŭTb_.ēYO;(=QnKVEvc(-%w18"$ۨU|3tEX1Wb\>.zEݽ}eٰIEU',Hq '\HQ%^@p:!y~HG:ґtxxxi׍9_Lϯs*R,ȏ"QC1lyxOԔŸhkPolUV<%^Om:n~?auZz!סC=V.:*jR6+U:n3k&?ѓ_0\$EEWk챊w+eMPYz{އ瞠DN;&?%(^ks;?Ui{EoQA߈EUD.yȸ'8_Oz~(Z\|sIM\~QاiOYJ{S8.Z.}zpۮrTvQ?S:L ~(9 ։ nдҎ"WDʟ_d_i*q7up+UoX.$n=uNg>r.&wL<^DDt]w\kJC SȬ:y."2aMt]n')]7)iݝHGJEq A9W$S??~P6J2DNWuO-,~ vbQX+z(MKm=tK"J΍|m/&+qQwJ~bugS5g$QgxDzpWGZ_.2j%hDGD&i~yҎ~N\tl xj+wL?:to^9p5'UG?trJ)wQ)1x(!1n`\D ν4$^]z KRkRS2to]Z(q4uҖsz`湛u]׾qDu L9fL*/UY4Ax/H~Ÿ+NX<}9"lSE 0q%J8uJ7G%NpS<)o۸+ӉԺBts9Ei;3%]z\;Q{Ed:"dhuH;']ol˟uJ>< [ƟCxއ/I[ԭ A}Τ#u D=7:xi&}su 8L-]B]QQB]O1]9}҉O ?i} endstream endobj 35 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 38 0 obj <> /Contents 39 0 R >> endobj 39 0 obj <> stream x\ێܸ}hX9"% e6Ĺw/=3^G'^(*%{/ ⭪N*òR-zq.=,ŇU}G]/=MRJQE)u#S/4BBРҫZ9چg*zuڊJjW Y m]MVWp=Eg!u,e?־VtB*!b-0~j%i;tnY_Z(r 狕+Xovn-ǔg{rlPK\;]ڒup< '|R雞ks'_~mEat_ Rk,Agn0xwEUe 0.uh"kou%}ۢ՝Z ]`vjE?_ $ e]vb ^0GQfZ '.=@-T/y)Xg t A]X\8ѓp>DC`^C}靶U^zUWTEFTq"DhGO7 Ч3O.}OU=z 3 c[%Hث 4o?3>ߢn}ؖ*[J08f@TuVCFz AMDi]q`Ɓ_*u$y Ϯp(\ȴ6 -c( NZD?NKC<(9n)Ϥ#-1dlQ#zϲ[1YL{; 31+x/nc=^iw gFϛ=?ZX~FdȜ6#q"6~c8JB)ךb]Ug @ix 6QRX__(CYeF0v31:Ԟ}+'smzy'\)AQY{P{O[x0f5 &8bq E"<8=baP]6RUmc2۸a2=Ȕ@C?BFd0Lm ipZ1( ZKqds"$6Y2:ҐoP=rFh!98ˬ7ں^S_lqx6ZpZuhcϭAяFM o _~-:Ii)=|DO=:&=R!1$SӴ.gMHV:16+ #:د̶IJ ];N9|4[Bn M\0X\nr&HG5} ~m0;,XxX1Uʊ,uoR,gJ 9~@N.aGf^'BGNp3 Or/!SuZW?jLu  ^wE8p.PSxoߊuk$o7/[x9X1)o``8Џoޥe(*dnZcpMhZ](3I!_Mp-Dl%{?Aʷ ѫ$?)udUjܲ2'WEG@瑞' ^Q٧(H-bEiyZ̟S=&FuĚ1ݴŢM_1("F{OgOIC$L^/7n8FCB Y8#Կ,؁,HB\zd&S&sX$\\*b{cs>*㇖ J#T5yD"Twq6<0oyБ4&Q[$7&1z޵YT?tM}#dF2kV-EOaD "9 ;hE[qQjL3G46fg_2J,!٨Fr*Z?dG2դ)ĞXXAuޥ-ax:kb_Fs٘’'G1nt|"X45Ňe-l^RF-THرʶCX!x64~?m endstream endobj 40 0 obj 3497 endobj 41 0 obj <>/Length 17307>>stream xy'EUQ+TPUb#KQuFܘimDeQpAW@du9AQAcͥZV-sP@ j67{UƖ~W7n߸&6:Ê֒%.;ːβH޶\zu]΢XOubb&=]tt]c]]7&Bwq11 4&w93^㎵Ō'se,=]tt]c]]7&B\\^ܡ7(~ʂMӖVݳ__jMog?nx]>w>W#1٭Mg/gɌ{7\gf7zNR/?|Y%s~^ҙ'~}=fpNOguϘSe㤗f<)}OrxڭML|OιaPpÉX>1_?\1X8awAV,uY|uTS\t>k?k@ɼ_y3}P >H9n:PWN 름\?9us?n9u|0Qk,WsيU_/?u7io=&&[6njbComJ?:DY\uA:,BC\iy.ԍ_\s5?>kvzD=vRJԧhR>M}S%~\ypt99vC1oe⇿olew}eCq,̡ ߲fpգn{~{ /qަ|cc%SLJ{2.m~FSdek>u]Quexִ]Ok>r?칏r ٣T9Bm~cƇC}1z|iwCf]ho̜ ^xs5*功oyǥC?ُşm9ue z}Θޙ/۽k> Ҹҡ=ީ?\@ׇ?m~e~K8my6:k_VFg_wC7H]/I|Z?`V1n{s7 .X4NO ]΂3 "S둮׋!{x}hh/}stWz N1}h鋞2l{kCoã\Ғ>e ֚txAͲ/+־E/{a-_ko?}ҽ&G[]saW'sCz>+Њ֒%.;ːβT -n&uz^N?,yAõy WYW ya-W%k.ZhD_'?>p*%=1M=_u.Zwk˳&_<;jM-{+z> SzVES)׃ICvC]ww<eqm޶hR׿wƒ8>iO;w)}vF?m{iE? /ۻ?Q'U]/t%G=g;OCuWpiMxG6g-ԟz9uƲ4wi#Yppz߼C_uT':kg]^ k޾Kg~l@x3Hh]_Wlw]V޺u3O|o:Voj{n ]/)Uym䓃n9HaP,ɧru}UV*҃Jm&*Eff՗-[QJO "JKd$[v>$tڗ"Χ B] 4Q?vL%]N#b2_F4-Rz!v&-Ta.knfg x_1]#n#N$]W-Iח,?`5_gtfQ&:Z]/E!b]wx+\r"m UG^Qv,`P*⽞uw̐ kM$g͞, q~OwME@&b>ƮSז!u&><3w2IxQx?^}gz<||> ?ɜnA<ث{31}b˚z@x={x>ǞOeOǂXkX6m{ !'S0-0_5)gč׋fi]"YUv$xn#R+eE~d RiӺN8Uq w<#Y?F`Zc\Ӿ/I}ϧ\eB⟧D8T +lPj8\\ 2m¶OyDZ?š\ ]oԂNW'dHxU׫ l j}H8ߦi |?Gh +QCFwz(?+<2`3Bk\nt?]_7^GW[yktjk-^p5a z!;Kn8ׅ#uV;}OJ<Mv6ՋnI<&];h;D;3?y:_`gJŐ#u6j3s] PcBҾt\Q0Dg3Z@m*E{>$ZvBk#cg"Q:_8Cv:q'Ӭ5O*x{f:뼾RuNLt]g"jy:·Fe 8QOZ5NSi֫s^ʕu#o Ev8C_#1u~eֵeւ5aQ/,T4ٗiSfub/ekqA]TMu3 #.ZCA5颖F.lt_nЖ5QYm}[/wRW[4&JL[ӓuL( -8_ JwS]2UY[nF?TnLW [{jY442GjO&Cq2-o=ڎO#d_6808Pb翵 q`1)A@@@@_Q{HG:ґts @u? ^u? ^u? ^Cﯧi(2_iTJ9MME?}Ɠ"3m ]S2BjՇNဿw9Et$^'[׵uu!Z誖R3iNlWբ i!RLkv]u2mvJ)Ǟ* FF ܶhE9z/a7rSt:g#OC9^ k'O\'یcNS=:?ӎu\f}c[A{,oF8a.j?y^QBk̭uUƹ̫ucF4>RױN:G8ZoGZ_mM4Prlui&|)~u]sbj3nV٧ N6n`4U9Emh议k=l}bhL%#s@E%pVn]J3Pmݠnr!604YX'MUBuN3:j+l:Gyt]۞-S)s Kпj::#ik(dNHV>&뤔\~S&%s 7]Y|~n]u]C*u6}88RyԘjfS~bzs7sƫhNʥK+x:sqqCr_7e-W[M"I#is^DLoM9JM_0-F1)38}j{s`L:j\a|g}x0/':::Z~B*ґt#H7@@@@@@vu_+q@*WktۯCZZ4>[G%ȏ;ܬI[KPzwDK1aәle2rOyiz_dž0ͭ_ۏVL!D>H8/&|omO]^JF(]MVΈݤ-Lw;%z)ӫ:针Riq!ʭ_~tI#QQczպ!1ҵ~vmZ#st/GDJwX'" xntlǸtrno[}U]2mD-!=Twz[_۳꺚45]of*jv G~irP-b;԰|@+B՗)03MMFO׵ ]L-:ܢW9>+|t=ޔz1d"4Kr4:V,QqH?x&d;Ad?au=Y;gry>҅%kfkt}]< +8ӹ19SM4/کHuݧrBX:jbvumb=]j~k$׵Ji©܆}mtz])$"jt¥!6|T3LEuZ)Ptu2,E픂V_5]|o5:RW8F:&M`.s3{A;T ^cMuľ_u7h-X]h CA u0DZ8w^ sD3tp'v@v u#z= WOuwx(CO`;z,w :wէ7QAƚtIG/zOvhu)X] ts0zH5tv u0$uMtXe5n3%v>Ö&WOצ:F_i@a @@ƚ\슟B u0te]芟B u0te]芟B u0te]芟BKx]}i:/ґ ]MHGzjbEn~Xӕ~W %Zp5]Y')Ђx5]Y')Ђx5]Y')Ђxx}?k n+޶wD?u|AcuC"?M?.J;Ы+>!W˳KM*t;^6b-T"ĐD/nͱC I3)m}ہOTc\Ej~3Hh|dF vgg<]ϧ-[NILQ/\:碂zҎu4]^k1ӹZ;Zw)JuAvg4Qu=v'>vn>up4~D:%{^S&аüB)3#8:WHS=,_35nľ_c_d$gaWLSAp5X%Ek:==u]Uz :>C^o-_rk~)Rmũ&]s}c>Ci:sih]@Y3OZ4 gAKZl3'~κqius:juBECo0EYǍo c/C?㼡sj3tP!iOHHu=xPGڏz *D=q QJgDm1]Mpa2yg;h%WMg*Kp >뺨yfHٽvhk d\P7c#>uV+Fxy|A\~3~#OyOJIKݎ׵'"V Aji9s|'۳McjAU>=y>)_-:dSyZT[ީXuuι>MܦSH/7֯7> uv=k|uSNiڡsإO4sMz#%Ww"K+!z=ݔY{ŢsP_c.6ӍnⰮ2x߇w:u]k8Ccщq+(f0J@ }t>%e$z[]9.:QznY $Qu`^gn*/:'=f<]z%A};Ǘ C&.̍oE$cہ?qvtb(؋6: u!\mX3'4 @e]= zz5x&G) zeJ$GS~N)C~z{=R?"TAry=L.z(Rtl*|90_ɼ <1ʭV#Px t&zx_"_ԕͭs&|=S}s~9ggjpUwSzq"-C4["Ѣ@dԥIF:u߇g]y?Az}} q~ٟz,=#r ].jq "Nu̹C77^V׭"EH&O4|I"Ps鴫Po DVͫ)יY ] !h8R>IZp25 lCF'7_VB!TnMϪh=z-(R>*YMGM!]A]'d-ߡ_EhSKG|-]NMC;At__Z9ӟLRD9]gkS9x퉹u&v-kӺdJ|MDz8bi\?pad*m_O}95Nvi렩w;pG[oӢW_Y1A?$c_Wiatu8K  mAEnu&};`]_kEZ/ڜdzC>|~u#j-3QY<*:wejQ{ӕ֗|O5c_=]QnS~`t+V{?/ 靊xc^eq"]5߇ۗM@ifxu>Os4`~u}OgB7*ˬP*{OM-^2q?d>fJWɿVLdN4Kuga8y>}[}SND3~ċum:gPUzh89|^Xօpad*֎}UktEv:<>Z]$N7aMtGoJw_>-ٽiSNڊPŶzC>įO.cS ƁT~]grC6kŸ qOSz A'~-?hC9<8|S-Bflۡx.Gx=Z?}&ݎWTrcdX(OgDI;N0VcєYpޖIiQ}{ uOzv<=:g/vq߇g].-*n }D{Q*d>j|t>Yzs5[]TMy爎@C\QLÍn᠈jm>AZaXcw|E3u}3juy)t2QF'7#|dKM6q(v}֍W騛}>>|O(]'Āc5Yٰ.ozŦSuOQ/|$ףTB>!EA0d\PnVOKD~zp9GUfPHK -әb$nA]HFDQ]"u0rGK\fk8DSlT)MQ|>J'b@H$z#j8HHJk dA>#ǚ!HgJ﫮C^}Ept*bûDuuQYzV:چɟ8M  Y|j?v utaKbE+>nkBڰfOiry!N~ym;ĆnLPC>Yl ~T^8ñrYdyu"c'Mm 8E]Eṵ[X2-:R$Y|]m>`cMϤ身euK龫Z|u'u6HkR;&#&ˢҐhB;oBCt)L"릣 /y}FtAFzmg=5\:ۺ@YӉgE1[Ҷ hIgQ1"9ѥV6ś>IZp=G>qJ[KiOQp`?׭tRd*j;K[X'>|O(]'d㧴>O |=]e?ukQu]ZnDuYVh}xw]gvFp]חuP7uAm3:Mdo>RcxpW!BIZ#Vs WL ?M.ꦬvڥ1rXǵX'SiM%k$-qs`r8*j^)BW!דSEb8z['L.|/nѕXoig{iks! ׳ZwV@;}gA=j:x@Eg unu=Sq0"ץ{yτx tX}оXX?Ĝ؜.p EWMdl9c#5'0HaЍEVørn: ]C\EǬ8FzR-u]s5fGZe8l#5kA;|TʿmB76OZļhY4Oځ9Bs_%^/HMTLpogukzaTTM.5Fu/R| QyՎ7<"mr[uѺ_8[:kj6m;z+jIG+_.mStDUי]rik(dNHVN ~u<;lC'[?PucHFwB}ƥHz=EoK׵ g5]wzǴ9c_^뜾=_|PMsJ[غ>:~Fj羒DmxJط cr2I.:A@@v7@3ґt#H7@@@(YO=ԓ@Tͫ/)%dJȥHs2W ]2%]w!ؿϯsZRGF$hayҴǟt:lut=2i~ŸP]ϭ^iv[+<]_bm>˿hҩ[ܪ=KMl&-ƪ:A;bcEfv' ]OLj]o|>@ C{?NW#PG/OcUzT6~V&8 m7}S1>rW{㿭m^CӼǃ]6sc#Dd8ʩoj^uögW_Z{[?v}cSUD>~9E곮NNypGYm7ٷJ|]/t0/{<Qtݴn:ևd>AethKZ_ih+.x%P}[)|?u:qnJ2P9vujFT+ƾjX!?vV||bp۷Upt["ພmW$t=`?EKAZߌtn ԏrs LL1îVVFDRĬ)!N~V.[?T;~fvԗ-=vX׷Q٤~|s={n:Dz;[;@-|@}Ǔqk躅qx2n]0nOƭP_cZݗ빙v&V'tNXx¸sQd=na;[;ǔ|uC{;[;ǜx@ ;[; R7ktkM$g͞]r:7_nR٭el65#m?RNm՗S4g+gm#61#`>Z)4=EB^9Q+zgY1gH?C\vVn(Z7~d(?͐ +4m^[a旦7 >M$u֐]'ix}KFJǜus4ݡDbH1sc.g20KubԾ|:R9+N9^}G|Y1kϱ'~$*zo5u*~w|5g|Z35@n:j̸9LC*"|Kz/n[ucg ^}M V0Bɶ [X3U-$\5U7HuqSzfξߔ4 C4o/~u?@Q3:HICח,?@ͳ۪?\CdzCG^8_i^I{] @LKiozxȜAdyx?u?{s@(燮rob / mF]o1];t]dO O?kA endstream endobj 42 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 45 0 obj <> /Contents 46 0 R >> endobj 46 0 obj <> stream xZ_o O1/vN[\w5Yױ7n6y(J"ڏVkt[7?J7VWIۻ045(횋V9g9A$wQ0)S2G賝n}}v5>hc[|l7N]Ά:y搫ƏZmw}n>n}_LZZ)Y_Uβ${r~Ɩ~尥Lvo\uS]4&r\\ֹpAݎ\NtYwcJjʢ}ZWk wp^lTt+i's Ig ܘ~Or?[08tr&fQܖz>YEm:|t~| s}t`%MoH1ArЃAY톽e 3 /#w R8r/U ND0vtYM`Z sγx=Tڢ2\KLx qQtKB[ꜸKi3NN)g r'M)R}`2+ : < w/@HS.z7Vȇ:@ء%0b3r^o#faӅ@Sןpr29t{/>N=m0nFAG9rZ`6 /^U?U=y,AZ | {X^ʈ3:H9>/Length 28479>>stream x{.YU߹OߤBw4(881sfQgD%jD#bbK/`pDh 2xC4O5q!8ϓ(y@lݴ}:v{ڷUUݿkk}֪}OW~^:#ў7^G;Go@o.{W ].oS!,iSV\i+aFU=E]f_5|yHI)@sCYY(V2CLgOч$OΚ[N :Eװ-Awi+a]ςC׵':t]Vt=9]]kO th_O~T_1?ehe: ]_t]`%i)S]םEԃl;osxn:AUn8 ]_t]`%i)S]םEԃlvn3G]ן,} ٓ}\ztu A:t]t,؄{ . g&L^r_|⿾w.a!ۇ'OW]]OCn@ɂM7?alx'\{OWɶy|7>=sExpƯ{;^qܯgw 7^}þ3n{W]~|>_ګ3>^oNzqo8Ϻ#ן4OW,w:ox?t]tx8]tW^8歷=俹=/KZp!i?1{G?#/}p =~Xҹsg|u^uϝxヌޟboe:t'}%W=Ꮊ{yuܳ|I3WW\|?= t]vt=_{LK,Ļ^7d.>-L>CB>Fu'Կeg@׏@E벳 ] HKt=<(Zt?|Oٹ_wZx+n|~٫.?\k?wx}-xg?}'g1?S_[9VI?eo~e:r?rtOkn<.7Ϸ=i$]7wvߧ|y;T{{rhGÿv׃-<7?[CN[xٙ_/?Hu?}É;9~ɯ'~W{?G?v~hAuu9Z'[}+o^{ާ{5?o)/A<~ĉ?ΥG=rF.unN=!~Ho~m'%|>(S+Nmf);IQu}Ǔ.?G~;%׿ w]s/oD޿g~8i?yu!|D/+w7<A];>ڻ>^vI{?}+"{]?]΂GTpMh$XN' uW? w~l趟9櫿=?GUo~pS_~.)sϾ6O-?OKOtw>=\POT N|C;:_{ 2]??}N}dg=DTOf?r!ɛ~/?t`o?Ƌ/]͜sÏ<[pz^r?>qy\o+I$OnUƹ{3]=q^t˿UwG-_>܇ع`^܇]5;]]{y.L;'N;IOWۻN?O[uoIC|H:y~@훯OdȞu\kO7]{ -ѻA:t]t,Hv$Τ7G~/s5'ӟ  ?o?}?3>ʯし{?G|ʹwwť?=w.t~8v~Cn9kv.9k{kϾ?"E.{>texJ]G{vw/xزj2u.Y p՟LVig᱇ Z`5>% TލO{S_]ϩ_PL*?|y~@g ]t_'RFnt=U3uA]]' $-eʡ뺳zuM:t]Vt=9]]kO }t {%LuYu:tB+IKr,t]d?|=@&ֶ]@%֠BuYD=fs] t]JR;]لt}c3}fcP}3>>"_^!D?xF_2AU_Ju̙On9 9:?L,׽ ي|}-o`r;s^X)"DZRyl}T!vJhAO$;\m!Sr3/dŅ ?;BǠvo^;:E&?LO^h|=t=\(%z̴+/?"LU Q`ko[oyl.j21[:ȒO,]U^k;BDz^0Q'Ү…6/IϾL'lj"Cծ% ]_QIhz9%:7/Y(SQ'-X̛~Cuy7./4WC}Vl^xn g~E,OVqN2ev1aS7R7>)v _8yTAX-Y!Y(P:F7;$y\z0.??~)Z~ WuDa094\ٳg G}wubsJvf$g\f//$RdB_N ~nӳv r?:8O6%Pq%u` v{eޛ\t=9ɪGf;ud%yCLau]B2]0cޯwn*J:fv֚'y7͗'a :-#fi7yoڐV9jI??j;UKKeܨ^Fsz5O|=Зyv%qH9/78U6LPC'hC .`,||||||;m?)69+۹ U ڟ~.VZXyC_o:-[6O;Ȯ~ʲa>ג1>4BY֖Wj;d7:Ef>B6*tuQm[[%o)Zk/tk)zu]pV Z{q2\To:4")4+ih+/ڲ\a3Nz;_5o>Sg%37Wcv=b7-;cvkcBZ"յur~V[I3WMuM' X u$H;^oxk_7ىzr%""C *hKTt$j3\<ݜ#kœq&;|3;x;%Ap8^]O_2#c gPv%q{|\/`hl#~x*y`3=Alv@lv@lz!0!Qr(Džv@lv@lv@l_F-Qejdr~'3~ nQSj7Y~(d~9K}r@Mċ92_ϹN t}{|}EVvm]Cмpخ y ߮Wˀ|4]W`ԚW t%%@]tu`;@0ϡ(G9Qr<.@l:vl:v_+RqEFq2G>{uBgt\xMu5OFoA}zZJSmŌBgۭ[B%זozkY ʬqY8fR?U,H vL|ߜNuƕ3*Zt`d;(-(׳Zv5-/L  q~v_$CHbbb;||;Oj7ꊐd~(O"!ͮG_6^UG4*7쓋 n;]/[Bv[^t_I+T]ֶ$nP^+k¸m5Aq 5: _qᔁI5UZc-\Ew%(o]r"Ϻu}NO]O\b\Zة>o+1tvd{v[}x(%n{y^k7&mRr]:]gb5dslMZ.~7]o=oz=~yyV[֥llt#k@/ IvV?CƅT"]VKo uYDžyy2ql5jY"锶݅dr~^ QI}I9?f=ƕ! X]ϥ ?qCMd[J<(o\tx|0VSO_$@`ア.vM'w_aA&$uk Am C:5u8L C:5TYԇ%y!X|⊰3`+CkGgwJO{dv0|RZ]in4OcƁ[\[:5tdt}$Gd/t] tzSPld-t8vMg]S{k?[9ټ*\s%J ǧ'K@7tܑtukrs-~$ukֲ/OzuvZ @O.@Y˾? E޺h5\q^Cy\8?Qrq7?G:5k_" t욵 k`]$]}a-~$ukֲ/Ozul:vؚ/~ֺuCxÏ[wg"%|~|SXד:~Ť3Ixc"秵aZ4)$&Z/Z֭" v@jyyw 3%:9ߣԒϔ՚ƁoN[!?񑴨Sa̫vhY nj'>^N ]On'UTⳲ'3$qS]S]W-!fH>bP&$7ś^.5B o\m%Mׅ%.aq(Ǭߵ{A~kOC]T2N{M\AvAEKpU3HB!Ls$u<T7BnղCF)U;Z 3~JHz Q_L. ^wRu]ճQuvxN8j/?d]~t]~ZkuD~c'k tL¦}zy|  o9EyD oHa@='6UL^`f`g=_q:um|%A0^6my˺R❬ ?_uw|ߺ)`j0j~CuZPC0UQC|o$]R!̩F*[:1jmEבxu5]g A1y>p|=X9S<29nN8! :.l9̇nǗ0g6z"aZW!L7gx?֡CBfߣ[Tyd  -0bT +uT tNfZtjz8evB2dyov|<Jܘ׎t]J͍/c?Oŀ2[ϵ<6Wk z:יq߲ 5ec\4u$¸߂i3?=Nę""à0Ѷ$ PԂO6xwk\I Ύ̢C#BiTSQzI<<)\%<>ir 8HK*ە Ϳ"ޛ[T()Bk?Յ5>'*`ɠ'Y|{}oujƇ⭿6Gk3F"w\Z$u=40n>OKލŊ(ao&/lQ}H!kK}`.)i,'oY/O"/ۏO2ys%ΨU_8 ̺50Muɪj] wo Qݲ3iZyͷns ) iuHrQSI˼*x TAIꗃ/Տ_Lu.SM az]딝ŢDuZk3[4_^WgK7ufk@Eef]zTZD7ҧ'3lyk]7lto׹ ۭKr-~R;W1N`3[TMtg:! |?Z'¸Fg\e\J)olo7TnPAXT'|ybSP|3MĿ}Uϯ7Pk/_ozw:xk|1Ɵ*(*wsE+힓y}^佹Aꊞ+K^95c:<6F);r80NI-rz2~(h+u˿F]w-)|5Ψ^Nj_b5^l~q*q($Q e&ZIx=:Y'VX|3Ds36)VmqB8Wy9߅+uIkHԲv:UMf;uĖqי:ZR K6>¸ijvVO5(ď{G?1 US̍<ENUVrx]OK|7uZ~$ku͘_ד]S. QC]]LBl0v:ǡ!CuюK-]I P?#G_-6EI̞•kŻ_+? &W.Tu֤jQTy*oBR֥wauŏ^OÜ yA:f*|gþDu|Z:?Յ5>'*ܫ]ʟ+ ECyNىA ]]3^ZPOFSR uoϕZ?8T: r6$ [4lN]O:.|4qnB7=I]wd>3sGq0Nrm>S.Q%6Mrt?tEUZSeisoc^ ]UMdIMoIKپ|?] W]צUt} !C~xEJ=A Z4F]u?Յ5>'*O!I}dZĉ^h>_2K=;O9*?SZh|)jb^] QP >hNj}@ϯ7lv|f-Zz]lσv?O8sWVM8|.)|IbqE (yE@`9[*2}Z ==_ݐ62oy0S".V9qgZVKz[| >\?A׳Ȟku7]7_'9u?[^t*]NZ;8G䄇'vvDb2p>.W&/ukPQr-\Rq׷ l9}W̃a^ u5;D~&㓵:8I Ql<bո~--_ד Hlq{oyODRם9J#]'z)z-UhT}먝Y;Ae9} ~x^Gm SjaFzTzC?NPT~ 7uU؎J7]׎vnH2k9DIى}3~R2.榲v:`K,Iv~ u{Tg=]¥4$Y/N'NAX7Q_B?ut?tn4Q ^>>*|fY<_ _b{/dNm8:{fg ߇0g}|Q S|wz-|?vَڜiw{Q^X(q فbQ]ܠ+GӐw4g~2t|< |Djsz꺼]ќ8g9b.ǡn(&T<_Ndzc :S-yS+_q/,fp]}+ٯc-n#E z /^k~HM3a`,cG ݕҿlzI?yWг%DrhόyQ/Jg;I;] mR5]H"y+hg95?TdYMmO}9π,~~$@u$3TA>.I y-3& 3@=k gd\"'H7A 7 ]sE6a%oa./{f}s.;;_]>N=g.0mnPe kyeU~Qa G~d[WC%:W#pg0d+qu&ɦwN<"L%%a׋|}L>uG-uZhLt1S/Pmtc137T9_o˫,ynɬ '%͵|} eN3\Nׅk6/~sjE9W|LI.BA\/+yqr6>JWb%IzWK0}/|>qX5:Alv@lv@l17 yP(G9QNDžv@lv@lv@l>ˍS.=EG0.k+;δҟ^Sb.Ya?خq\ٚ+ZVjW|JOwcNou9chedgiv;q\}sDϘ';vSrB%^uyu]q>*td^_QV> ;}ɡڃKƮ~ѡSk*ٵֺ~o5LQ]Hz*41/?6ʻ*oZ? /Jd i.c]d/_ϟU޴-W:`zkwn&ϊ %r}0]WYwj܃Zww> Dϭcg*j'j\b~Om܄&Z!~Րq8GfuO!.O2-\ha90G%u8.M+ Vۗ^7k}m2g>解KlJ3Fڜ#iX' WKtе(kFe`,U\5aܙRq3pgjSv\3|N1[יjcK_ma]׎K2(%S֊ON{c*TZvFAxAQ ٹLrqvrrZRخ&$~ڶFqY]ݎDߺco*B |=y&QI}I9є>_?9{%=_LвT3C|NngVx?Fدe2_ޟv<1OR#\[/_@9^>rzs zw-/03m=doq@Aao'{ 6̘ϯ/[_a|=Ol}8` _ϰ::v_{fY-K 7tm.3oxJΣ5 t6q0zI֟l|^Y<$ͼIKk@➺N\XyUӋ-:wl 0RW3/U-q JZV>5o}O^u>t]l_Syӱm^>}.Y\̍ko>){xש&Ruqm&Y+Rȗơ/֋[]kɎg] H6W'32.?%H&`~gfA Ȯo]bS@7q#aoኩsM;(k[ׄ =/ Hş =뺟^ G'|hv|&B_R=躤u}Nalǡ܎u-_<*]o:"{M/ZV[Zt]2.?~cCRCtN>S5co-a/z|g3.eunq\>?: `H%{!w|_gB%c|<7i-jwj)ot|(ҫmTZKJS67g>8_{̝̜׫zJb݋)JnGq' CYIu@k]o7^W[9lI/z5F>=gq}d}j s_Tn~GbrJ sބ!Igˡt|0q~4/>忖qS}ܮD7Uk>DכG%&4q!5.#*;Y!CYEŭ0jI.^6.ԮJ[Wq盠dWlR}zz|}NrM>.DדzmbqʼN}uU/cSZe_xlJ䔷[r !z%\3:ГNho.m_o%^pz:A!ԺHU/ʾ[ en6g?Muּߦ םG亮kU5Gnjt]O @]Jč_Oʎ W⽪ޮ͓~g'ɕ, +ʫ\.Q]NR%Ƕ'/<\+3񕰈LvH$ٹ) Bt=?o|qȰ$ek+7x"5B^Am#}si׿=nIU:::zs(G9Qr\RN _:::~OUDJc}z>}q.nP~yg\aKW}tI r|=uyzy*;[!յ$^489ViaJ='ۻ:忡_֎ms|R036< d|vG1D%zV2.u/Ƨ|EKxuɽ?I}rd>sq@,B;ً ۺ<yS]4A]ay*Uߐ?޷Q M6N\$1£Sz'Q|gg[aϝl=j7,ڕؤum|jClu*6rάӤ?s>lK'IU𞯇ټIV;^]Oz͟8>-4ŖB%]2nD׳! x K 5@gv>QVz>Iu k׳F$a*u|We|5Gu"tc׍!% `IAz TSԥV%Y#t}'/÷uG|-鯷.$!%Tɸ;`sU!6%%DLO]>iTt0܋Zv2^.\rH1ln2'gDg KR*A0o=O4]0`7vJuvKy _POPu`; _u`; _u`; Ds(G9Qr* '|||cuï~Ҹ"U8#:dU]V:.U~D܁'>=-l橶bB3u[vKI9-*VYoqB~.Y켙95t==F-]o+gTȬwPZPgl!k0[_z;_gܥnPTPͅOjw~E2$$&'LJʼY|vKg+"z5mex^XE@r>h(tlu:8_S7E-}?jK^ĭk-[3(u=4[a+.20IC}żu>dKNМl6S}@'jW8(cXα:Ut=i>@ͷ@]/7Q8?'Jtݒ|h}Mȥllu!j7u3#۵t]nz*zt>|# KvG(9^8$nlj -E޻L ] [דdFOkk :8E)m &n4"r%0z+SCTA~ ΟKBN13xP;.!߸< 9-at迀HÏ{c]|!$X~<3:5[[ݿ6 ! ukq@&$i/H>5?̊>.+.Wm>0[iZ|>Ӽ !NIiNﵓ[Ck]4_7zb~VQWާ1J--ݿi uk:J/DI8,_`@כ]0|.d-]TTCuHu]|:פ/?!l(H2ye9d?a%^v붰靯T3z3J7h1=ߣ\~tzCy+ܻ u]<6l XJs so]_Yg0KA!.:Ar&O(]OC!>*B%A2Oq!dz:1jmHjr0%_k?UnQxC\=_qwޜyv>yji$Ẏ3WtS˳M'M5}`a?iE~wU0ϏεouH$05U>O{˅'vg qqPE^[.izqbK|]h!.ouS;p2[}׉|z-]?D=\؟†fW{~x]殊[֫A'yOs9U+V8dWsQy붆 F+(vfOA7Z_?m|QG8aD%.3H.Su5d#S^.iQU5Quz-Z>' 3?u?%YZayk1τv-dz,ukE7ua[jNϪOum|*uyN6h1{~󵺮WJvB}扟}\m]׵S~ƋoTAx|lvq(ŴOQd_W( b2D/$?y uJ2[čf˅IwCy`xqp ]=Ww_6_'j5> כ^;]3^Z#TݾTk~p|=G~jΟl<_b^Gq`ϼ)};I#9KdֺRޢW?Wߏu>.{%z/-W<ǡI;~։*/y+ hYqο8|l[jdHu}Ngqq>ߵBԖau֡ɾ0.Q?)Wv[ uFxTE2KzaE6. vq \kSMbj5> כ^;]3^Zo4Gd}9Тv<_y3~UsyS1\>gu~˟+KZ\hzAY~v14!CH?_W|~(h+uKY및=,zyOa2c~ϙy䛀jZ̚B]F:(Ϟa`NO VWyޢszҾЙ uq^r ߅!J4¶\Og5Tmhoāىv &(AٙO-vkŻ_GvA+nuݼ$%zv]/!Upx o {{˗eyxg!-Ϻ^`>v٠#*j erqiU.ODr![NC_ĸ{T NG߫6`'Z늷R iIo%C0\ΐm96c]+YkMDz?%-|֔j@]w͏%^a>NQK)rWz6]S׳Im8QK7ƧP+Vnv;Jq!o`?C<Er;)|uV:V&aZ.&]|ʟyjڵ]ǜ#㐴 ]|ݶ),N;yC9z4Ȝ;/2cK'%SFE6:PޢaszҾЙpdMsS8h/-ݘH?'ꊐ|ݖ]5I^R.7|]%יHV;]gĀo),G8c7x eOqfQj]7Q~}&jKtݰn躼6]u>G ̋P6&pnV~m}2/%kZz3u?Յ5>'*O!I}dZS=lb> SP6c|Vv>3'Ϝx˃le7Q㥊C7"Zgg-}~WLZ$xzӋ`'`K^oy$G<_o{-Lh8?e|Q?O !ШGC\꯷UGIf ԾG-̆%>ΨGCuˇAoq/0t~!Oʎ]Dׅr.ÖtEOJijee NP$'C]0o u]h ]V8 ȫ&$qh6~jXYr-\r]IM._߶-L]1fe_\F&J;R,:>y:_dh!ꁍBԃr[|N]O78#ua;1AtnAHBAw󡑨A 7uF (?/3:HTN% =zYv\·v*oKЮ0Vn11I6Nny]j=2vC-zPz?çe\Յ5>'*_?nnn*kU,=&dymE:/DgC٥Gd<$ ~HէP9>m&j5> G%sp|=`uz?:W1vŠQ~<+3햔\8zb-&_w5~bER FGnL]T$fEaW oIkpyqX;׫5.$@Xǩ\B/ c*'רlzG}~u}30J0Fƻɘ&$nĆu+邼N֯7|?vB]pZ(%nw>HI\~NW"ؒ>fw qrC(*(%WSx/jlD-jMղgu)ɳK/愻QОBTKׁClA׳-JBut uy9t} EKt]j^;v|m;w'.?J׵K5gd]9cއgFh")Ƌ5I;]Ѝ\/Zt!7u !5{=3gRjBgVudm|~q'9K+|Ϡ0{Nnᰇuϛ?U% }Z">އ| ԫfSOkAe!޿B}\Mשǵ/g+Yg./X|\.-yyH@jvbJ/,6m:a._6HoNօ7}suV7?%}[.0mnP/ryakmΤdȎ#?^|a-a}+!^zPγvŹ/JB<_3iмp?KuX֒giKa.TS;uZhLt1S/PQbfnFשru|7=U.- '%͵|} eNSJu]]EZaI.BA\/+yqr6>JWbP!}=Tpߋ[/&K˘:Alv@lv@l17ിG9Qrsy\8|xlv@lv@lvMJ8S?npOSR>L[+e[ ;-ꂜ%]ǵzevͧTڏiaJy7֫~QSZ;VAvvkkǵ17yKو+z"m7>%.k.,t]O]dzKOF="~"%sp63 S}?vGFC44ٜ;jX]8 QK3Lb7ԡfV_ yeK[~z,q9Ϝ7咆$jGZ6 $[ɚw2&|KMdօ:\ϪujpՊVzŅi#f6+wg%::7S.-&}Qr MC~ou\%}H.鲝ZՒqسq|}ךBQyW4h1ul. ,)q/;"DҾPwgeeSׅGïBKJz mEs\pڗ*ݯ=dZW:]kS&!g8oG_& in*(c?[NyLm|iGJl,Ps+_ÔuBSlJ"Dv 2qڥϣT~6D0?Zk G7+ЎZ  |?<.g0?kQB(::|܂Qr(G9UN _:::Bn~~3b%?5C\ğ֬b+[ vkڭN-𒯻'?]vTܯal2CQ2UkYMrS V~gfpQ/*UUu8 خ=8+UzgzÛO@ċܺ$c_la_vfSOsT_ҴPhJۯ}u ٿk->jok~:̆O=?kԩ1z])_?upD ]+ߏvOi_FbX)YSƝ9]!7ú<y?vk:1n+:Χn줟|;:ݿfu/ъb\2l4봷yN1v_B]5k:ogtj|}DI%jq+/Gl-/:)/mBm kuOtk'9Y]?P/' zדg(ԗ3.QM nWR+#q -KUK:=$VkŁnZ_!?zoǣYci.5bu(:gP=>[_agz@ |=Ol}8`ÌsDžDmq7?q7?_K v3m=doq@Aao'{ 6 {[>[_a:aGwm[?:3Ae?I <q.0rā8T|8_UsWW4{#dBk;ybSx?%sRI+V|;y5;֙Oݠ%_,Owao_VָAq vZ%wodpa7%_A);fyoaLSzIL}.%};IuFO|SHn Cֹv[pGcGv >%yHUo''nx>x? endstream endobj 49 0 obj <> endobj 50 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> /Contents 53 0 R >> endobj 53 0 obj <> stream x[[o[~ׯKQ;iEQt}.Y4,93Cqt6BjmO?'7_: B!nᨐQnʉ.ˠkʮ -(y@i J!("lVZ at 0Lv3֚-o΂arC8— Lb-f,yydQ|,ZX gMiYivPzi3x" ۖanvn4Z=:+D!"p6ELKVL=x{iriHhAZgdZ(BUB1t&ף"fx(Nn?.$t狮SY.6dpuFVc/a֏>ѩB8iK'; P3)=hD>YLZ3TB2~ "Zsf< ֒;Fo'\ .$A+ei%U}1U:rEgD>zt?;& kF ð^$#e颬|3&/׹}A.DGwMN!UP>Vr%l:A;rXLDm qXU> >$`Ēh IKs8 ‰\N-(eZ'Q }$H | +0f|L!gc6Jv[ؔil Z)a/Cj:W5 ڞ8n&PrPBm6 C=N[9Vi T֞{ (sGfpR&}NGD:"p!޴֧V^7[/wmoU/'XR^Wc͘g L/yV\1}ɟ}~a XSmt,9)2{:,ck޴4%nt! UIC;aF'y&]f.PZf?:o}[) *BhWtω69BVϜ @uNcO4SYrJVO}p.:zUCk)1z\ETpG++ "=gs.%h>MZ%XD@n-LAB}lŏA/ꅍ!Kq,9}B dED8~p@0k}:-eixp~l N><P~" %.{k肗^`?r<sU(\asc0AUD3 &L}(8@Gk 6UǶgZIJDZ:/ׇe쬍>/>㿜6"&TI3Lޛf Ot1 W+J#WҹfC(hJD.H,Z~i]e%hJOĨzquǀ<Q1g"VG* O:7XȆàޖQs3mia^ǴWi0B[6tWm{x&"'>Mͱ-So@bӒL&&1 ^$6isx yo6%N:sW/)pv2EV?J͕t@.p-.F䃾:.r `}dabC.zYxsWYgEx|G8;4!n[m0RjM5bCg̐0ObrtA9.O> ֲՓE\ S\&ge+*KMWd_-Z-~ j{q_G{vQDei8=:kг|$z*{OL@ke=}ΏA!<4R9f|Mu ]uK@JUsJ5B_<OQҐQ={ʒMr/p޳3&8֡/Tt^:z颺 Z2"q|;/&&endstream endobj 54 0 obj 3189 endobj 55 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> /Contents 58 0 R >> endobj 58 0 obj <> stream xYKo7q|?|H hCR-W,mHo̬DIIl=8)Ggd+ıO7C~"uxW?׭'ukm8t@ho;|~D\Wί_iMu]9KwKdx-0aRiL Y9鸗߃|QTNY k>:,O!NsLE%.tBԼLQQeӡ@Nةp(crAjDfVnR2 n*}D[I9N0U>pzy?Vd~D v㜻?Xmw]Mh+%=@J-+ZOдJ\RddpJrO]&AF 3[鎕%$g{fO6q#XVsD 67cwzulJ# ! @4˾`܊=bP!:qkRJEsOC8+ۑLhYUEx(dýЬpn&ْgʗO0ln "y.- %|0>Ofmf^'04AQܦ͍g8@;@uNIٲ{+s![kC%4(l-aR@2v\njQ)Ig[κ6 5$dH1E2 \Xqf;SYµ&U_jZb/; ˑx F0[]VݥW.g45+jU1:Nvc x20v6O+opi9w7Hx'q-{'Ήz_wUiSdN<'>(+ B"mu,׸Lu=Mendstream endobj 59 0 obj 1295 endobj 60 0 obj <>/Length 22801>>stream x{oIY߹O+($22fz&^DUĖm :`@h4(AYjb!8k%YMxmw{>ή]ڟ[S[#'^kt~ڞ@~絿tB?I~eC/xZ 9$%^TeWryvQ=)~y~J`K H;?e^t^ߊR\<|ܘ)%e#KG/ů=OqH#in%n{Ǵ尃[K.t]q@vu t=p ]߅7 3-~9?OQ_oLhe ]GAVz]z~۱Oxﺝt= w:~2ŠR]S]W\H;͜߻~u}}ScRpb==8\'*UAםGk@ut]-BoQGN&O;PO=r_roN m]w]wDu躷 ]ԝe w0<+Ͼ#xGl#oxأ/:[)/{y}e+wyಧ~zv#;~]txer?صs[~{_s˷s>;785{3o\Fo{M1㯸suGt]S]WlT>B͛o𰤮?OO=iS{wa8ocO3|Uv?~奏}׼C w==zZ~Oԉ2t_|W># >𲋾摧|^sչ_cy~orza᧝K_ O;?v\{aU-y~N=,]wq]@Uu]E)]/ꌘ%)_!_y Ҿt_>? F} '(]˳λ~gO~_;|| N͂ѽ:~ Ksuw t]e]݅[tfHkt}x@gcn{韼{㮻ݯG?r루Hq-7u{wyD>}ѩyp剟z{Sv?nk._u8OWrُ-??ßg6z\|WpN~k(ӯ~%Gvx':!{j}],_}]?m/]rSn ;_x縴r]/W/:Co/}wʇ^3.>Η~\og}摱]pXڟ|/s]9߹ƚ` @׽`hd-E׿5wINw}壢t}N]?#%}]W]oNc}7=;ozM/=]늗~[k^#/;=g9y⇞SoGG~3enȕ ?w 뺻u>,,]?bx(I׏;Y}ooej/j?c;w~ǟo ?.G+̟|CƟp]?T):]M|]CKq~gx~(ջ+C?}~/\;w>|N0=ܯ3N{`Eq]?a-?߽yyހ; t[Ѓ^Oԧg g>}kݝCۿGwћ\^Gw*;1)qwj ]7ˏI]=7zo?;?7/ ]KOzm|?ۏ>[>_N>/|^?4tdWϻ{1NhxR?xաV'~C~)~}oK/p}(t^?)y^?D!ygv3 '٥#yO ~ @׽6awsd^zq>뱷>N;nKN}~uye}pS*owC7Ru}7ҝ-8ow|7?\B>o89xWq\PzE_q_q}}?3|c;-t\pq-}߼o}i;ߕ~~uwt/9/?w.@à)wu]q]߹w쏯{CnkG}i?+vx޵u}p{ p/7w|<ׯ:>==Ђt?sxF7f0~Iw?}I]<a8t}ߝӗ6smW=4[忽k| MiӇmtOr ;w7)wu]uvoS{i&NG|G=[i}}kzñArԺ}sK.t]q@u#{Ý7(րaw8xw<|Ç?:]|3>-{lT\o=b ]"O gЩf Pie\λ c6Z XVCH弒 M*. {O/oóº+r}e#uw8u}'3iҵu}Q77E="^]6Uy>~N:&iGp]4bok|gVB{۱O =A9@,7(Q3E`1_c7D·*K/c?Sw:DJZJ8y2u^WF}d8=9ߐ}\Zyv f& C:{s(={D; O9Y] "$'?o\7>E\{^J꺾p+TȲ3CzSAh&<_Nk=h'.9RK|ds0iʃycоjc_BsmȂ%f~? WlG$+4(r! &A]+(9+uq*Qz}ścG&~N8siAǡ&JR~*Wys{q"giܙr?.?_nb+-D qOg^_ەv}B^D(yWٮsC0pǮJ3 }wCJ9%;K8մuݷ[h=| ]%֝?L^[`lJ Ꜽ[/A~G]Qj5Εc,9{2X~Qlˬ))b'8mS< 񅖳] /+]<+_|~yU[rUG(ș_y$6'9} y [lF|?h u~:@?p^y8u~:@?p^臺_7ylm+z;gvHs$UrROJM %v_-Si={?ftӈrlyv}KloťٕJmTZ6f3W^'hdT|/pW XvseJTD_jW4dFXn(|:,{6sQMΈ;CK|$4GK'y= _&s:o!/v=/8󠙧~Ʈj.y˦Jg}z5i"5QY.ڽz@kW(;AbS'xv/oS\1_7 >شu\JQȫrzLp!mqX+:5rx|Qwy>jnISYju}uBQcu6s]vUuV\'25OCP}XIиnEa\p/%{U4Or*r%~9OFf QEhKbqTپ]> D*ʧL52?gpvr`%R<87</'zo+͹I]XF`1~x/EU,&$:@?p^y>7GH9SN9ky8u~azշ5e`6G_]>i<ÞZ%9e"MXO ~?y}(S*'Rz$HP2+_]o;k'>]}}qȫFτ';ڷrYkjS,g'M%(z4[")Ě,O8?e>wN`uMtٽʧDz$B>9ȣ%u][|[μK.> ~5 3.4; 5fr0W[cS3rt=V_U{㒥HIRu[reG 9!MD̿> 3ٙ9u9:m Bϖ+;yx+^i>] ,v=Hd`l5 >fbcku!m_&~oHط9]Qף֍G]9e9hUt-T^Wve}AMӳm]Wvq~+ 9(0V5A#M0t=a]j?ᴯ]`췩J6HC~/t=g\FIof\tݹ(}+ȱ+;}ͥu u7z(Y)Ͻ߯dr6*_y8u~ד_\3J" @ޚ˰:AOm Z .t:5zu&ʓ?/Z(^úۗ~_eՎWXjۗo:tjޟkSoK y]8+'LZl*ިz!6oJ&j!*_g׷7/cx˝V` zba#\`TWX˴l4~l\k˭-`0E9~%:y}]M̈́y2s5GB_v`TW@39Wv}]]+o b~opn)]ov.еlg<|^ɎE(׌]b- ^]Ec^Q!֊dnmXwҿ Et`.벝t=Afr^7ɣ&_5 " |y% n}?5If>m#?ӖJyPY\כxqu~Y|g34bm ^ܼ)\1Rڗ;VV9"fb~jt&xIS_NʃoQ53@gW'MW2&Tӷհ=#Ф_|Zuȇ:@?p^9\6Z:z&[Bp^@dky ^6wl- ߯0m[]y]?g֌vNZ4 x _R[?Ƥ/n)̬]yfwK~˶ MQgWYy֜]RUKUͫ?ȏwyg_2you^y8u~:@?p^y8u~OYK9^{2lwWeg6|w>3އ?+5a67.<ߓꯖlMu_sxig9;>˥]e7:fJ6tuVxVK uH\ÙMjQ{k42x*~^\+Z,92%lvLhkfm^zu}OAZH{#Q,}^7gs>]~Ov}]=UṨ`}gDse >GR#եtҼf TyOʎ7َgΞsyX?cv5RFty}ƴob,ۍڿ|^o=+b{)]}Ab;˗|)x.tN%抨<5$YJKۂVez>::i ת^vAtV+! kz$hX0.*n'9q?9ύ '#Kf"{4%_vp8BNGl.BtiYfKSp&ؿf x38d;9Ihtl|v)ai ƛV~Јώr$` . ,#|?"*Xy8u~K?#r)r5vu~u~:@?p^yۍzŲyK~{zᣯ]gPxYWaO_|YEWibN2&'mYἾ\rzo)o=E(~n쯮O˷JGb.8|{Ut]gB\v[rL5^|uͳv&ђdzp=-ObMdm'2;i}Unt&xvj^cY MM!R˒.-j-g% ?|S39Kywɩ9 *=eqR$|)-v䃲# ӐYm"y] _x˙SK̉:gv6yg˕sؕҾR]]yWvcSjtˈr=^iBgyx2M9}*a`a FLV>;QMJSqN#,gr&,GvkWRA%jq)-oVM I+uȇ:@?p^p3SN9S+ G8u~:@?p^V/Bv|XKcY oM[ ere6{o-ބvkYy|:Il}RI]o-z\a݌K2j+Ww7VS:5c뵩ޥ.`-6ob@Vئ}h,fwB{ y}}x<=]n;&`ϟl}GMugꗪ0i7}_5N\j~_qտ[zN]} dǢnkƮ_R/^F qJkEgN6};W__"^j0uZS3Z9IQX~?<޾ޟ$3^iiKX<(,bv 8:h?A׋3Vr^w/qVn^ה .L)D^+} ~3n 5Us:/'rPKAӷ(㚙 Y ^Uƿ+\ߦAj؞szh߯@>:y8wl- y=nxc8| m</t [ĻMⅎYgH]8ҚěVn'y([n'R) G8>x@1|`kx@1lm>6Z:z&[Bp^y6t^O{KM}ˬ kF5?6]F ;"Tqi Qw&ϔoEZ߭/ |[9)켱c>[7Q \`_c}k}y]gxZ<9\F$%[9V.Gͩ.o[So,OxzWJ`G5Z}̈matu:twj}fdoy}k7qh)*7Q`Z߭q^OGJԹ;j [J'fDw[}dE5:Hl=:@?p^y8u~:@?p^yjןpŕr2|?q.*lN~q~?+5a67.L"Lԋwk/)lU{,ftӈrlyET̖F̘]FΪjnik8Sma6U anL *(=8ښY<³׺@m]_fp2DyݜQ=tuY>y}w9 Wm碚v v䇖Ε1 )͑R :ieHW|\*%!>]/;d;B_^:UK?>cq.:yg캚ܮFlxۈW?OGߘ-RCLrQ!ԾvE'Nнؔ>ޠ?[f>}$YPY\Q'# kƚ0&T{+o#y9|.ud]TkDםzlbvYNDݮ4nf|}Nn驿?;2Q Bvsz+gǮB~,Bu]lںf.%(U@9u~yyN86n8] v_ua9BE)o) 6WD%OeR^j* GuMS_ؽV Zq< A]UXKc%AFm]O !*RWM$'R']_ם3addvIY߬U^df .?ZqHC(0M4Kr| W,3o|Ǟl'' W;.%#cx/ ?5M]ة[&҄`8+חY Z2Ur-EG%ӯrٍu qSvZ(]ؗ~?g[`kLX zۮ}].לՙƫ6u]RyvN~$1Z],}^G%2yIi\_>BSs{?>ʍVNWݫ|z,kI)[Q`>Boj\c&g~ p5V>.95#Gc5\% \Q0.Y/e].1|PvDZsҔ;M$ko9sjɟ9S笟ӮӦ0/ls>| yPRyVمbףdNV]QӠ]si&6(]UjBo~7;}u=j(/-]0}u3[>__EKBU}lWַ+4=kue'Ґm c%YS4Ҕ ?_@6٥NJ5 &*~t`4Bsef%_KםˊgrS\ʱ^뚾=_|lP}sZm]nQ+ Y,OO)g`ϓYe—<|,!H5sg'jTiJ6nI_#eTvU3b,R܄ȀNBq_|Y*h?`^-5N6EC7ԣԸb|^óɂa|y2ip^yο{r)r}vu~u~:@?p^y=Eȵߞ9y)r 䭩x+a [L4f/śbM7S3Z97XgϾ'_ϵm/TL~SR5^>MbkFω+y\+wV^{}KX}I?:/m ְ'v>;UM Kx5ժظLA~Ƶ/omNS^?SחuIL'3Wst=!(hGVKx=seZ߅>0/y?Qu&m/zk]vƃMuXԍr+6Ut=_+hA1BiLƹu*PDK R.YKtjF+u36Z:z&[Bp^@dky ^y8Æi]sYQo?ivu>16]vyy9x}SSZ Fqg(Nyc(;W[r'&;rͽ>nhi--#^ƃׇpF^&xlZy*źs >:%m欿<)5whD}Hzr^=W҃$Ib5{}^r5Bz-y}$v/qy34l>҈l:X_Uc6ޡrͽQ.gkk뻏]~Ov}]=UṨ`}gDse .mBJsT~Nwl"!>JIOˎ7َgNҏX ΃f&.*56Q7}GenaHkz]aщt/6z|7OlgO?v|'cxj 'uB+j|Ƣ隱fp=_4s`.^NJۈ/t^7qK]7YF5ub.Q+۾u=*ikӾ,c[z/θLBݜ=ޟkK&A]K2 yP|]/_y<[=NEt]XgFo_[!eQwy>jnISYju}uBQcu6s]vUuV\'25OCP}XIиQ[CJ{Uv<ɉynDחuL<]R7+D٣.Bv;mOjw?Revʧ3L0\*30Lphƛ&'IBNeK HX0޴OF|v<轭7&y [ vY`9Wp^y8u~Xn`!SN9S) GvE/E׼:@w^ltQFEމ(:@:@?UnTx+[۸ }:w{˂.jM{ vꖉ4a=)n/~6 \rzo)o=E(~n쯮O˷JGb.8Kz:*3a!.Nl-v\sVgu}>KI;ihv %2yIi\_>BSs{?>ʍVNWݫ|z,kI)[Q`>Boj\c&g~ p5Z3Jz&d# %K붜?&ʎH7rNCrgu-}-gN-3'rsu~-WvGQ4!V*ϽNV]QӠ]si&6(]UjBo~7;}u=j(/-]0}u3[>__EKBU}lWַ+4=kue'Ґm c%YS4Ҕ ?_@6٥NJ5 &*~t`4Bsef%_KםˊgrS\ʱ^뚾=_|lP}sZm]nQ+ Y,OO)g`ϓYe—<|,!H5sg'jTiJ6nI_#eTvU3b,R܄ȀNBq_|Y*h?`^-5N6EC7ԣԸb|^Њɂa|Њ@>:@?| =SN9SN9rp:@?:@?C+"olW9HT0_-Y&j^MhwթzIQ)}GHؾ*vzU۾|W~ m5S3^kSoK y]8+'LZl*ިzj;TTׇOؼ)`sJW^C߼p.ws[9m5뉝ώpUoR5^M*b*6.ӾyЬ_fqK5k[/)>TϺnW3ɟ? dj'ogr>AS p^W:'R[u]Xo͡k;xٖ IIQ~[Kx-(9C(:8_}z\Je;kzNhE#vaZjƊ0Q!0Ԙ'H۷hL[*Agq]oGMA ^d}ϰx!]32󪦾\pdJi_v&Z[Xueә%M}9 _*FT 2_ٟ6] ʘP-OV\6CV A]t=@p@dky ^t=nxc[ĻMⅎAlm>6Z:fH]8ҚěVn'y([n'R) G8>x@1z&[BǠ6wl- | m</t :@?Э_ox*1LwV8R&l{M4qMOeք5/޿Jd?]|ƛ(TyAӿox_dҧۃxV2j K^i=`&>|oJf4ҭFrǎظZwkĮo=t]4yuM\G-(u}z,sNOQWu]xZweXkzse[Հt=u#}\\vPo ٹ ^&N9YqE-X*I*KDK[mQOkՃ֟]Κ>λ/mߵI89Өf¾>ݨY-SIԇxKk:\>Py2޽}#ud;:WD׃o u?{?λǩ /uaz;+<<`c̉W?_y8u~׫ݨW,(Z/|, 6ʃ>/ ?5M]ة[&҄`8+חY Z2Ur-EG%ӯrٍu qSvZ(]ؗ~?g[`kLX zۮ}].לՙƫ6u]RyvN~$1Z],}^G%2yIi\_>BSs{?>ʍVNWݫ|z,kI)[Q`>Boj\c&g~ p5V>.95#Gc5\% \Q0.Y/e].1|PvDZsҔ;M$ko9sjɟ9S笟ӮӦ0/ls>| yPRyVمbףdNV]QӠ]si&6(]UjBo~7;}u=j(/-]0}u3[>__EKBU}lWַ+4=kue'Ґm c%YS4Ҕ ?_@6٥NJ5 &*~t`4Bsef%_KםˊgrS\ʱ^뚾=_|lP}sZm]nQ+ Y,OO)g`ϓYe—<|,!H5sg'jTiJ6nI_#eTvU3b,R܄ȀNBq_|Y*h?`^-5N6EC7ԣԸb|^óɂa|y2ip^yο{r)r}vu~u~:@?p^y=Eȵߞ9y)r 䭩x+a [L4f/śbM7S3Z97XgϾ'_ϵm/TL~SR5^>MbkFω+y\+wV^{}KX}I?:/m ְ'v>;UM Kx5ժظLA~Ƶ/omNS^?SחuIL'3Wst=!(hGVKx=seZ߅>0/y?Qu&m/zk]vƃMuXԍr+6Ut=_+hA1BiLƹu*PDK R.YKtjF+u36Z:z&[Bp^@dky ^y8C{m\jOMk;]ҙ y.ij ;Plbs~Sڬ_vaUJr}30{%g]*/}}.7Kw՞%ةى2Xo9vb4"q(\Ze5ݺzJ.itGߺ[]y=xs Grt}fԘu4t=z#^j^JrLH>wdJώh,g?&̶`>w>|̚B^C“T._pFڶ|^y-4gbV+浯D./AYW&SΗũC c쩮G=R桝.t]FԼ-BTpeQ_k^t :~UY+^9Np5-u]Nk^j^;o˗ 1QkwݴϻF籚zavI^ߝ KC&?< 4y00ߕyCxahߋf1(_iMQu-uOtyiX_pɗun)vŰ3iD}H|iXsIMQrw>:@T;7D`1߯ߗ;ž@O+7S:}}t]bo |k 5tlm ]t}mr __ endstream endobj 61 0 obj <> endobj 62 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> /Contents 65 0 R >> endobj 65 0 obj <> stream x[nW4IWfo  A0Id-uKvŲ iUVU5h6#mak2zl g&q Jd$A颱mٟgD!M/}":dtak]nݗ˒]yo]<֭uyiڐjVV em9C|&M:>oIg}ҧV"2TfA|֧7^$V*Itae+TLZ:=7~-Vh |׺|o*7%RtOݭ4mU.KJK ݴF$ne;kC[HdPlZ U<83^͝:M9W:IVnf߀BM 9k!zEʨJ6j'Xh6T}%bLqLv?n!vA4D20YR~kN(.qs(6}$O}>+f?^c2M}]UH0,~iNCb4<5q m[k}fį6(M+`-P!YxR5[km"s;2 Tҵ;D'.p$%nJΙ޷r薆XN-27dsfmqM\:x*5\a]80&CO$b/"J=_5˘4zlrp+*kQ>֍X|D!XԄ]OMt e2# 7'@Z_yD:MLG(] 5'XLE"6fAX(#a;0 BNG:v#@Ӣ`,-60QlVp9+Kt=N`➃^U8>r>%^M%R.QP~'J7\;ypx"톛==:jM?ugea};ձ>Lşc-7I^:~b34 gB+./gFLpK}2 mْGw`_9rZV]Yluaqr[ǠVb"A،(QP9Qx8wfwmP'FC""ɚ0k:&qi;Q/;k'S6O7PQ!`4rT9jnؘ,3w[)cr,]k_U¬ pm0k71 9*YA/K|+j`<_7LƸkDUe ?&ʖi"{2Ȕ((y(mceNNNc,&馧uP4ZJJIF[@"YeY2y(~˄A=i_q-nmU\!2'0p~`Ë9'7̨ﷸNc/5ɿ=zphEwD GR @*ƾ@SKgp.NDLzօg-"}e-9ɡ 6?I8@EG8Oõ[xpSs[OR0bo;bP 2v}>V2<d^;'؟yIw߅r118'33 P u)}:or8 Х^)bj”>-4V-#G}_20d42ljϸEd08~2 & di2Sݫ$m%2Sv (G./ NKRϡ2xT7 fύ p+=l ThB LLɻ#˸~qӪ%>}s:]:~)^Xx9Ϡ/hS^|CXL4׀-*q璓w)r*Mb6ߑw^l<},?_ք9U sp\nَ>{l|'Z uIb^cؗ3$䱝M?ēEuaM#U\;*5u.aCdm1aALS>h~Opl乷mM /#ѧAZ2>lJ{GAI%u0Uxl Pk㰡8/[endstream endobj 66 0 obj 3048 endobj 67 0 obj <>/Length 5015>>stream x{՝o遙 (]IdcͲfWTD$˜AB.FD` XEhDr$BpIs/f{^3LꮺUVWU?~hnWO[Յς }$;$ĮuA?&;瓏x8)Oa)~:ثLZbK_Ť/8}m&/YY\<2`6YgIb{4yXɛl/;a$[ron7'V+e4$6]އ.I4Sx]/8xb ^Ex^ ^XuцKu<2?A7Eiz,:GHuVz:WL/G?SR WnQ #Y,Z1\1=]!v7Q_:}E[q{]%|* _Yxݰ:A@x^O^g.O_.$9ٲг}_o&Y1 u4u悌$=$2¦.:9q~aG^[=ԑuՃnߝ(?|*nՑ?rGdO\Q5rxݭ 6z>}uץW"mǝJ.*ȥmrb6p`iUCs׼^\]/~1z\6f{O~mpN`JF=GJu3zwsW .Ai+{V ^kp륕uKx}d{[nʮ!C&WIWEu*.ȅyFko떧>^zyryi-߿ ,hҦ*߉ b<|ȕ񺴮L^YLYZiV*:W@x#Z1k:y[;^Ѷ䣽F]__Oߎ3fϞ=͓$/x}hGwQZGwG;e]-!;jJUEYt=*Z[iyU i7H}r{xEy9g{Vݱjg"h/+^?vgXѓl[uX<|晣++h<2'/RM_@m:2\\\!9rW=vtϰ,-UnkBetͤFurͽ+VƊ|"jWN)j KCߐy~/뾰<w#rq /u4uN4.WQy*v.W,T5:1z=#>S9rXOUm Wߺ.o~xi ]\O<:m2y6P3wV z-U KNK[|M8MEqt)MDtNn>^WC'Sٯ]3|?] o#}#]?YE#; (cf8^tW{ـn7VY>o魽!L>^]Fm?״xn/v9H_Sgk'޹4/|gnp[\uVNbծ }# y]Q2z=i07ڞҢv|QvsS@޵1[}DJH=/m?⪡91r>Ww+Mn)rc"+ղ #eBٓ07t^ _=jZ+l(ƜQoyŗ y .;O|x=R<["y;<5|n^Ӏי 29ew̗EiaB[)%q˓hs,)wyyux=fO׵u5Vxەy!.zڲvO-k]伇uux? x6H:uctmp<>c{˥35y'0I>X=oBU"rzxK]?*2@<ߵ׉}c%7}޾vwIc;pXCוz}]-=|AxWubt?fN^Nwɯڕyxʝ7 x&pؗ#]fx}_'G/Octi{z}koO=}^z}kmsiTHO{ސsd$f%O#hB>po:fZG:o9z9U:+x^mHuP9@"f.mL_؏2p7A)uen$֣3N=FP4ZZyu}3)#u3R}:9uV:.ڐuf:n0rx]^2-Vux]!vԱAP}6Z: 0rx]uۃ@1h/:  ^^WWu{=ux=x#a$X+F=x+&nd';{};ϻkΝaו擝!Xp>WNA,T^߿_5j;ts PYy׎:lzRԮv^nKу_z QL%SYQ< qǴd$ j}۸8,Ez<d0.Su:`X $5P@ux><xm4Dcp;WRpuO$Oʀ2zҵđS&#s''1%_e/*1Y+#`u"2?3/^I0ite #OdF $ *纳>94iji~(Cvm>]M,_.XíwxGpzuʆ%'xG1lEM7ș'LŲkG潛o/hG?}QKΛ>z]hW_׉tf-{ݑ]"C:noe+y>H6`2Jda85d :qpАR㏔J&`?^2y6cCyR}J<<x~vUuyxUZD`]_j:xHq\x2~9 sF޷>.gֈ6Mq#WK*.Ivzd';x=)>^uw 5WR endstream endobj 68 0 obj <>/Length 9359>>stream x{ENdd`âº+cW5jT4  ݨ$K@\AX+!J<*{"zN <$03IffnI}?__n4?K.XZ u}bwG{bX.g]]Sv &lG;OmwUa&%)%%I"cgwnC*Mخ=g) jzEk0WMfA:/A伤b+\Kt]nWwQ ֌JŻj랛 ǫ&avЕ֏St]Cu uN];ubt^ ]-3tgyMZ|MĥuH+\Kt^)A>:fEq{xhP_C]ʟA):t>: ג"WJNYO\0kY:=ZuKu>Q,unq-u: 0`Ԡ6&9&G}Tm=m{--ttc]]&TB]3Az;~ֽƽ>jcPеqxyn6NYٵF_.eOןvfWG۸7 ^5?;>vN<枕ힾz6rظlf36Uw~V`fN;u tt=](%]'T'M-ɹuӳA>g_ Ш\ۂ0h)®l^9,w-=?|Co/A]^.Cp0a!XzaO^:}\H˻-8ۣNnspHc_|>;N |FO^IVqצM>cl}(yfo{/lM+]O Bi tsIt'Qـo;]'BNu.^~Cȏr-]uєjdu~[W5ھW'\܋-O8?x~ ӢϿ"T7Tjt=N2]3dt=-iBz[3!.>UVs1y[Wo0cT[k1l]TCW';D ǟǺ_hUGwyc/DCn?q!cgL_ﲟŢ>!=9i\-h٣u(__|OM!c<Ϙ 1]݀  5]?{^Jx១Z+zȼHi۵W㠮QcG^Ok4t|kjdw 7@9uuaBt=ύ׃ƥ$dw{LIxiu(hޙSO|O->ep8.w.x1Oߙꎡ0jss=/ǤxcDg?=?o\~ڸƞuOǯVvY]_|lم &R;RuK#CP-i>l\&~zXH=ܸ}:WG> ?8yrԺ=J}s=It=G'}mc!;#z>Zz}M[:y1]݀ O% :ct]￳Ӎ?o./oBuwo߹OZ8Qoʋ<zm]NfO @NW>]wՏy'x4T@]azT:ȃ<;vՏ:xS/ .|{ݗuZzΨ?}n!o;޻PÔpU}]_wєuEONI:tDn80o` 쮌_) ;?r+7Ϭ8h(eu==;O|Oƞ=OႎŻor t3Byx-](FO&4\뷞1?Z~iNJ{<&sE 'w{3Joy}WTxGdpsЕ?߱`'iiځcp;\xqrXd찝Gv:I:TK7Ϯ/&.MB[0}WwN ,Twd/?;ح0ZZ7Y|oN N/] v?m$ gFOVGc# 8ppu{"I(ه˝nTjF)uA0nBt}Oh ot]](u+vA饠u݂tN*]sp-)ҡz$u:.:t] N/] {W.1X׵'nP ]NµH䃮l:sYqZ8 tb N쀮k䃮l:uCס#p-)ҡz$uLG֔4-xu ^/CXP~'`P/`)U׬.qn<W]V@YĊL|dkH{RiC Ve+r(Ҕ̱d]/^m@s]h– ?3v]:ʵNb3~J)BMim˵J١,=5(_@H9ϣǒz ĐD9$)j10zvm]גRtbR!\z"1ii<ϯ#3ͯ|X 6xD}zŐ$n,\z @(yxi8|uǭ7Q6|JO\?PÒo,uw%]wq~hR'rSnWؘ*)Cv {*CWzعqow?Wu?#WhҎO{;rX- bz0DZsZ[$S&_$>C1N̙{8vgYkWzPx3m/:{el~YnV4vWYY{t'=VcgD=wvuMkՊ䙛W%5\u'O{xdZ Wyxi_5^t=u]NH\M?.Yym ]q먺:*Ob%r/Ơ"$W%@l%]dG }J_(jII齺" la^vӜ`5 Zͩp?@S6^$ZSA? ^Cgv^\W/uoO5%J֜hӬDn0O$57t?\BEʿ䂥z":O lzRr {*ptr۫W~zAWR*]2xLS׉ŕv t?u#kE躁DbE&fwri:wYYHsrk7(.I,Q҉vb^;jo5t@1NB[Dh|`sabEE9litzdlGiA.s%eeKK3꺖/\.e:I5ϒRk@oi&R%뷱ruez >):6Ȧ\ʈ D?utOUAT?'{I۸Zq)ՌfC+yGKu[O7Y" R:q η̐7Jtj+ɔtt]2I }K);Ntyt.|^~l֠ ttCeuZǧȎd/RBWtەO/n(Wjq/knk҃~s <~3ux]gPr'WAGJ;ҋ8j/4UKu=qQ{8Ѫז7l~.YoSuҟRx]MVAyNKʽkJ-q)dV Ocm(w2 Vtdr;e麁Ne%^Ow")ArH~~ldrqqlfK~ i]׽k}]>ӟ^_un%IO䧤K\"ڗN4R[D?(w\e:E/#Ro~b2v hR?v+G%{2&ɦTo\:u: ^WuI * uvy.͟UV{UKWWny^I ɤ/xHWGMTVi{}kNy.j;ݚ;|Wi߷y^I#2h|#tMޘuzK_6`|fמG%W5 SE"8m|\e&k؂#uֵRkV)&e1~$dǽ9e\$=Ն~ux=[EkuM|ܛ}SETJ+pW ^M+WN{)E咧<|Oʙ]/x-}A.ڕg[I%z{%vjytǗk$lAzzo)5뒖3sL[@uhx0^wz@Ikߓh?KSWqs]]/gNqyծ桼TWDכz9+"Htx=sp\A$1`.$!)ziU䏣t+4QgkUCv):Le|Oߊu]ֿˑfiq^Wn5.yxⲉul4hvɫpuk|]ϣOGӵKJK,'&}?ZQtBW.H+21+MutȤ:L,E'*K:!7uƛvzul`Gv u'<}?A endstream endobj 69 0 obj <>/Length 9724>>stream xy՝5t MC74Aqa>h&jHp?Q A#jq!&&qKr!; ttUS]]u߽uky?{woU+m@3sƬC?'ijO-ko?>+{=,mÚ5p*,6 ubٙ[yvRŷ`/6ϫ=5 2V:/缠|+LKtCn,Q Jj4ǫϡRBUb؁KA饠uՂtRt=t]rN/]|!.z?y@){>Q[ ]N´$I䃮l&[>˲Kl}u])uz_+LKtZ)N>:fB_M_]:=_UKuò i' DYJ@)@uunB.t}ָV*&s_p뇮3+]]gCn@׹ q&kG7^o݆5S?vzo5~"U2~D;Fzѻ 8á[s~0zUJeO9]U'G ~dc Zk{/~hc5^şRtRN9v~iEj?}CrMէ_3= _'mǣ ޿,@rk>𖆱 UvSuЀ:ǹw?:MRև"v4]1p_oZ)4nȀ˘Bٖ$uR|u͌zݰVZI~ӽO;0-VY]vnYìNks޼cԁgmBG?p'9Ⱥ^|TOzJEoVOϜ [-#jzPÓv潎6qU㧮- O)u_S꺆]UlwoӛG Al^<{bi_`w̒u})CSd<;(ޗ ^W5˭ ߺ' ˦o+V߮yn?nzڝE~5dPP=+%\׋ww] ]g[aZCJqAI6s M{yy\):~z~ilƃgv^> 9Ey z񒐟#JڬCY3n zu[٣)5oSQNIk%m]E7't\mO3sc?m*V(ṇ*_:t~PO g䂮 Bi!f?HSt)?ut}7ec9fɒ%{ /ӼbA/nA]wEJZ-}~'TêyǏ^:\N AVىr=ϴ;smKN:ykwYkHuֻzO-%iwuϺ8ضyszv3oZ=9'_JG٥&>SOMw>ʷMC _nw8d]QS֯3b [D]_ڋ{*!](@ ' 9vΡS+=EJ^h.>:;W/wSݫgۉ⼆Wlڸu nܱΉ6q;ny}-FUMU-Kگ--[mErWDOyn u\޼qϼq͟yz:}҂{O{_'|.Fk< qġ{.As~kA _o}Cݸ<]_\:|kol<`lm]E[?_>}jޱ[ݐZA]-#{t |#ߪZ[W~?gL6;w=Pm-zxgċ)ԺoUmvV!hb|CJA׉ S1]|n CW6~,ޣnnX*}c/}k^{ѳ(۾xx7oͲwus3On [C~yiq8`SYxsg瀇'v L^n}+ZaZCJqAI6uZٿo}zNlY\Ndq^M.9ً߯7݇]x__v_rszǢюZnQ9Wwձ%X ;w]mK8`=ۥ6SR&}1êF]޽?п|M|Ou#θ޹Q?tƏ7u2{SqH9ϲ_|j4u_~yţ~omM^R١<5HL׭rE=!;t@tiu+~.)jѨ 1n! ʺ$i:1CMX=QwKGꫝf~tzyUx^W6MlAބP,m> 7|IyL9ϲ3ڕmJ&f~vO 4@O7?SuJ?CAYӼnV R{\3 ?eц1ciT!l*eMH7_m=QV`ʔFG㺮]/tqYuO3 vS{ߏQ>.@4[-_KE4$z3$Cw ҽi v.w%] b~$N ;)p~JTԨWՎհ)Ü6JK4oEpOީsF1^Oi}vj7VZ'^Փ`S9zcvӂ7B^UTZ^!b7SQ):P zߜ7ͦHC-7ԴD*R_fřH}9&jJi ﾿| ?ݯ"GR_AD7nr@eu=zS$SM;C 3^T['hLQ#UXץ\`sC בOXi<'~Jospzy;H8?@GSP#/P,kjh\\525Q:2?OJE4TV du?R]l83C8% %=}7o}.{[zL+zŧzyH_(%=kLl:Jx'LxkE5)_=]gN2#y.^fQiu]u%?.Uڛ]*b:^Ihjs}-IDZi;q|A.5s%ieJףK9꺒1/K\.i:A6IuTg^)J#v;l~Je ؗ6wH_cpnKu\xӞdߴ+u$bQ/=:f'tOF? b 73H JTA:::::::u?Q~K;_XZiބ`:AxK?'П4~3%;.1^w2,6γlTVQJݞ%QzU+%fW߬аwc"&ڕNʽo3>Cb~ݸ'P* h@ɔऊnGX=<95 X8SkIL*R ELUm2ފua#{G;W_pMjuQJ /*2w.%AFjS#u_*' -X?OEu}iC\t?U7^uMFpIu?ZNo޾_VFM0Q4XaSg_Oix2]J3G2/eiuF]z01)]75??#g\׽?=躮?gkZ2nvCdfI'm@ iҺ4TKikOcRbv̞kAI~kr$:s%X "gPJ7rvdfk01zM`p%Of3P@3l!8obV*o=(ubC☸R?5j/y^~{0x_@%-`s1W6$xxt~=_t#HG:)D@~@~@@~@@~@tXȬqX!޳i#^f(=i~^ԇ2Kmw)U]'K w]2-O ֛q,?3N z2[AJˬ]H -O^؍uNѨ%Hgib&@cqy\ȩ:EAO(vT  stU6uޟtr$;M'FI AץQ`ާx7NOj F=v+BC#7PxX~MAgK/I꺠ߔ֋`EYw/w(G Hb͙(K0SF:S3躪jUb H.ы/źp?ākQ*3yhkwsEYSI~f˴)XlqfzN58Ls^JnQ80C[;K-RQp:ϥ؄5꾠i_LU-ĪW|S]G6f)Ҿap\(tF1#C߯j"(=tGg+6H:PcX׉CqQSCUIKdJ?>kd:`'ꮿ4gS׉T ,t=ʼTވOKי pqۉ>Put2vq;뗾35lZܺq!?c缒t7ŬT`_$iO;H =Q~KVyvc(-%8"ۨU|SH:$, 0h+0.^^2h_E<(2J>aj^a~ʂ'Y `~ʅ HVuD::y\sHG:ґt#NtA|@~@@~@@~JCqzfv~SbQ~YՈ-S a$,ݠJkFUY x=ꌒ'r7> F!+qOe^~}qUkԕ) Pzķ5Y*H /#iQQUڬ{X{3{&T0I'/Î Oj~?RWZԸ^}~P7}^qK{^2)7=>Ly?xm`騮CiuJgGp KtVo^l?P/UU.q S@iES잆?Sz8뇒Sc\MIK/S{,쿸ywAq[~X {y?Ju&p;ߠ [ ;& 1✪j{UGSuq*5(Q^-t?u:kG?e0&)]IK5t*@Vu'R_q7ԏLn}?qwBSo ~̎Kb?jk} ]7?+ެ 1?JOI3E/Ϝv~{%oc~2Xq%&.KT?m7vLI$^fqʘ St}OV_@t:x%(}ʤ9J[hoeRix]B2~@{AA.[TZ? ǤzI'dOW/=5?fÉ.YE:%=x]B碽I r _PiL*^cKV&h/1%Tz@{+J@~@[Ϝ1ku LS lΧ/q J{))H~3PEwk ,=p,KZc] 'GtwLoǝw4SBM>ϙ+y<wxE:bu|$L:H74ӼnA:<HpvO-!@20{u+}sv x߯u :qb $CV__oDVeKC2m9E~+@G~!mWv :0t=mO+}lw&: endstream endobj 70 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <> endobj 73 0 obj <> /Contents 74 0 R >> endobj 74 0 obj <> stream xXn7 S֙"' -Тƻv~8^UzH$ǫm"@}!(CHI{W3.o.&p=kT>bOd\6\4XHfwulj^v'm:ÍAIЦ}-~zϵ1z=,pkP2T~:RD% isREmAcѨF C;vRql/^Zd7ށ_A)v3tY!~o\z# Tn9B;kdCnCXˮ)guag,~MfE]6@CP؁0܀j24[߾%cWD,JkkbH%9.>%5S)%4zU ^YRZ`S02RTj|@qQ6}-Ę_'>M$tsVI!tʇ g2bKoZπyJ @ʒ0 1EԜ=,!$yPU%Y { ķ 3B`< 4sAK vcxw:%2.& >e쪯1 -JLUQ"X{#owMѶ❸|xlʑBh1o?^zxc7!`?s__K»5%mJ bBr WjM4&G?n0'{8$kGP&9q9`w]I'xȗ ]&nCYm8#? |M)S\PiRXάぉl\{Bծ䍄_b0BztIE^\@S:`jCq  Vzs-7s9C:yi5;d L(_{> p8v'ꐽj}@!eb9FƯWcEqj*j>)8QE]^Vbi)7)BM~34ϛkfdOHŌI%w~M%JlLϛ?Aendstream endobj 75 0 obj 1064 endobj 76 0 obj <>/Length 16274>>stream x{&GY7 f2$haQYaeq+eW5jT4 !Pt&Y .AXHVDC+U(F BIHLff{gz珙W]O^/W0|ʕZqu|ӟ=o m6p_mʊpxiSіYEhbpH;:{<_5y.Hg@vv=/:/r^mjS.{?n$v!a!ǜXE׊Fܢ{,vuts]ן'V:Җn{ut=Ī]_m_sw㷼3Y: b)GrCU6w~n񆙾t=R#Buthu%O9vّ㡽fۗ_гԺz(>rT]]Du躳`39T+Uc 1+|cׁ=zؙuAuAt]׻; F쭶^<`Zyh_m8/~_zOZ۾g>b1w:;n}37;'zww^;>;nzg~Yto=}ۦu=N5v^+?>'|v]7{׻n^uϸdG7~כΛ{=! ןz+=t]<娇l'm>[y[.uŁ;1_W%UzK;ϼUȬo<㉏XGv_]sfGju}kZ^qM=$w8Oqk>ɽ\6?nvUϝ?Xc˷uXtP?g|ɡsW9m^|iu//gs/+[wV]WDugt}YYP/'y{7_]V?4ktu(?{.|wo?c:>!_GW~&{[wV]WDug ] sHkt:,?cto|SnaӞ}c[]] GÛC]u]?^_wšnz#kזy΍gnV?}Ugv^_zIo9mkgq1;v嶯x'xˇ_V֤}_ځsN7r5`??pӬ#^t|7_/mwYӎt]vgݹ|]iBԗ+OOx!ٛUl]7e[9|Z=Б w,Yts|7o?y굧gM>;-v[} {_ws??񊯻 {' 4?U7}ݟ]kҾn9鸕ECW}joOի+w ?||X` @םzU%Kk1W'ILg} E_.eޥ땘[u+߾'7;_[L~욝o;C޷^t'Or ܾ^wLg胻|\/Y9[vv⺗= Rx}g\_W+9#oY\s^&3]?x΃Ҿ?ڳ_{X~pk i__ǯէn ő%YrSOX?qgҽ Q?]/~qT:u6d~፟~ʑo|3{/:7~}{|˖ V?{C??|n-gTt]e]ם$hij+H/ey>]_HRw{w-6oz?v}ZRxSkW'kN{Ïi}ܫoO;ațu}ˮkOzV㞻H笽~stzew4w;ïsݳR={1έs?_༣Mrߴ?]|}鏞g4cb[?~?z ң:w]wX?Ѷ̤ܜ3B>k|vӏOln}֫~߁'U|SuWn]ms7bu}6w=/]owϏ)?7yhiߺ/o#M/ǮCOy}mr΃3-_[YϿt{3ퟕ^ ]핛wa[u?zY,t} Anu=k~yo{G?k-?]v/ܱg]S+[sWuUgq~Ə xѦ}O] 7?{Ꟈ_=Hݏ.{:n皖~Lڿg={[<{O~{XUyI؅7=]t3snщ?VXuً)܇ǜU;kΚ˔{sg=f4KUl8OιYߑ}'쿜x՟?p;vz M~GքߛBgzٝޥVjjUΚ;"qؘ"qpx^Q5.RyaC=t=lZ躳`Nt?)躿-W䞓{ut= ]NCu+ŠՒ];Q]WDu}[;9Bntqq ]ut]?t]ajS娇lfϹHBC[AUu5t]<娇lfAuthu%O9vto oۏ_tݎ|}=W\yL=7k?a)rP+.R۷oȟ{y5蜵߮աrhS/ ?;JuӅWz-kUG(o4 A>z|zuc]}e}xo91QuoԢˠ`']t:ê3Q_hH{)S/t̫mތ`Rԥ=L|~:Z%)u5a}WAu Q_iȚK_p~$)E=yu}@YxIZHK Ï7ɒQ hu> A}9@!gwA;iq]0&( Nya@w 3E#uح+Wn| (^(Gt?ȇv"$~+de\ ˫{-LSZ7^BFɛP9s4ƕ5NZh;-UcuuuЧhJW^fַ2e]=k|C7N5~*q[D} _=7(?MɕB->k|Cio{]/ usܧǣwm u]:!߄gx5EㅮL\v5TG!S­hhB*2.Mˎx>.Q v?B"tCQCBD7q? ( ʟF P Ço*m/ʹ._w/}D/2JWZ+~70znWKfϏr 8Ҏղl29-n 1eGF|J#E,;$Eu~U?1E4as]7__PFwsG~өe?{M~Ԯ%.Ic)]gg]j\R͟I?:rIi㔈v"t\n/H|Wլ:8 Az vƎ7MW^\Rg$E?('"%={`joJ_NRzR^nvAZWkv M.R/NC(za.)Sו{(~QybϮ4H\u%ُ׹v u=tU\uV/)qzV)חձxC'ay";獚Usx sz(ڎx3zo]P%T*]o3.C ?3/K_\9Ǽ 'e<[U)IbǻMS~/ ۰ u[_hqqqM=^BEWu$h6UrɾUIyn]~ SlA.|`<u@0:x _|`<u@0Cit/96Gt΄vB\OH\ϥNˬ;zCO:9/[s3'S{rQ̘ǵ/ϵqY-O)jҾEԇR)*]mT֖j}[wJ!Wt6EL'jC-82%\TuVZeOKFszu}U EmYEh |ˣrmwe/\5jַF$ؑoZZwF&tiN?^'gDDZǥilGg@˺V~ЬP?Cv5ReS>Bt={ļӮM GenP2$WsݸAjWtx R=.'t\ֲ?# ź4sY^4㫹:xCW5hrc-D;Xo>J]ӍkV=68^إN4nVǫAY28eLe}mr]շBO1C|tIG  >ظ}(w$y tL_qW\y$t`p,YY뮙I6P( EBuBA0uK~U{SN9SN,\@0ut`<]:xG׳>(G,O֏~nc_|tk- ϓw{}/>?;5M ]MIvǡY8=z7[sBAU>% Hd\vc?".TVƁwvn eg{s\#=6℞h섶kb5uL]g*];){ae YO$n\_^B}{?.4ʍVxsn6U=N)9?$ҥ ^fE ox Z up%H͙6K̇QWJC5"S,EJKYM97e,}PD\ސ4)V[y_ fMu'rYijSXlu=|Zoy-2B{+R`hݠYr!6( "ԄBV_Z[:۬AFq\yլ0e9_RzZ<ݑve-8\))Jׅ~@#.S]OX+u~"엩J&HAA2.x_nV>ؕҾPu .կuݵ) g~+}^h-OuSFr'ʮ/zڹD[oU_v D4%ۉ_9"mTvU3BLRk#V; qq_籠]1GS͓IQM j^1?`\dA0?`(t`<oG%/(tPVWWg }i{ί%r[D} \)r)rWY|`:x _|`IYw=ؤƽ*,bu=tL??^Z/j\~Oj"G?xM\qq5[~ZV1}>V}xym+׿i0}=\tj-p\ƫP n_z~_Cʸ5sǛ۟k\z{ Q׻uZGS3b4\mߏv`dW@3.^Sp^+[Eǽu^OPtݵ n꺰֧d9dPMv,Dfohh/:.j4Bq湋~/몐DSMT.K#tA)z5 ޹"L(0X'-uwݶ@g~&b}Ag?wXk?Bד[S4B-%_/c~TS_S.biJi_ovuҵ/&*gjVg4N8v+Aӵ(j ^^濵+ߖG2&T uazG2:|S=|Cp#xxaĐ{z i2~ ^1wLF &SԿ[HY4?7,\PCrpAi~/rp]4Z?/_0@dj@0b=Lm=4Z?/uS[;M #|`<u@~7ʫnߗ zήFip.Oqّƥ~MKи7Nqo}mzz:[B>Gzs1]c`#V^;^wlV wW/XGe]OcK{;gs)lh_Mgܫu]s륍Ԑovs qO\׭{ǽ>eeBEM[/4z_K ʉA\*Zƽ.xVk O?-CZa"_SNݮ26_N&u nߏ+/|9svD 4 ]q)dG8&]꒮p]V]o*mԜʉw|!_S;yu^ֿ~\_)Ӧ6.y+9^ןz'1G}%S" +٬*LCk޸P?Z~g6ơ~Vz|d0j :xz0&:x _|`<u@0:x _|`<ןq*p4Du^_Qkk96βn E5z[#7-;wkT iYo"?xM#rYge+u?hijt^ﲩTYo!=__bCsi&U\n7(nܠv+l:Avv:X~kٟb]9܈K, ]/\fY+OnKu7zF5 uyhR/R'NW7}ek2񲾶UG.~ ms J{?^넎u]lܾVJQȻrT [ꄫL$ֿ$޾:^.撨5DJSۄf|p]_DAc}徦/\fZg:O)CU* 7=^K^%o7Γ6q? .n] Gʓ\.B[٦iOj~ן0hFT)8_ |wvtBVeCH7޸OF\vmAm&9;׿0B ~I |`<u@0C%=B)r)\Sn. _0:x _|`+']*0_&|~vUk\'{1H#?Cpz׻ٚZ2r)IpG%?]uq.BD2v;`(8? ߛ]虰'Dc']\٨uWe꺾?SI9S& .HHw?jlKb,ԏȟZ޷Nhe 7綺jo\cY DBqJ[/]oAEom]t}]q WZ.?+ۜiy|=CVhW)GK"%q릜?u>(".^omҔ[-׼|k|m۴k) u\ٺ]>ӈ~PηT9_Ɏ( ݿB -EC%_A&6S(:]6M mzM 4_5ܺ=N†u]kPt]scSC3#e!_Sun˱6=b]rDyi&^ycp['_Y{8/(jgh^׍DM%fouf|%7jy^f奛yn%AW3uUI>v7o_ UN&u b}m5FT^Aj22Ǎ~rs \mA\tc:m.3|кKcB?(u|}NiAV&^K5u)Cq7j !NlNˇ  L=_7pHعrT>WQzz;~H5 WVu)HgKu1A0:x _|`<u@0:x _!W.hGyݞ NȬ;zCzRMɒ/LԞydfZ,'ASjigCF6 qkK;%f&Uz2s2\;8  ȔpQq7'gD[ k! -jͭ|P(j*jGSH]5cF^]3o,k}ઉp_TӮ5"|SѺsyH6RN2'g:_K7َ/fL?XANC WH]zMx a 5kBQY. \n7nPD"Ȏ׽.]}^Bo-3"PK;qޚE3v&\aJR},yzGzGuuMBtݪg& ӕMxu=?>}Y/}]fSv\CiR9y/ׁ.:7A]շe.u=r:.";/%Q06 zy0ެ?u=hL&B7nt=h֙S}գJƍ_ӟ.;=ᵔUv% Hd\vc?".TVƁwvn eg{s\#=6℞h섶kb5uL]g*];){ae :_G-2zrIlݸ:ZSV\iVWm{,ka}SOs~6NIrK]-hͺ.2.4J\Kge3m֗0{S|uȪmt=T_#*=eqR$.uݔs\@ MCrk%`omT}"w~v6uΖ+[ףuJ)zFf&hI~G+w]WѺARClL P/Dzٗ xηu$\YQ׃㢹5D]7Yar{T/PyT_ٻ#5M7Z4u]9[qA. C%YS5R >ށG\dWn,oGE/SוL>?]o3/e\tݺ(}6+ȱ+;}͡6Ct]3v]_kMYC˭S(CLԏ'ߛ#ռb~ (Ȃa~P((_@{:x _\)r)rWY|`<:x _|`<uPJ O,Gy(r/[Qf0lN7'7oD5]N5(%_/B $ONo>Iʸr+Wgo:t´e6J/&ofA?cxwLzrsr1wdC x>h[;q>M?Ҿ6qEϫ^ƽ6sok]X`[嵭\CosAeG8q+B*4}iAk(on~q 6~E3D'_FjuگM͈upG~?՟)^AL2{Mu0z]l.:y?Au&mO~[1B5ٱ嚡WhޣzW фbf ŵ3ZK.B]O5Sl/]ЩNr\7x0oPcEuq[X~P\׋Fck]O?oO||ЏQM}M)}uՑJ*Gw\Yԗ; Ovakxzsw[ʘP-OץouhO)_uAÍ&SCajxxaĐ{z i2~ ^1wLFL?_oPzn!f$޸rpAi~iIkvKdj@0bxNˆ!_0@dj@0b=Lm=4Z?/u@0!l(}_*9vLÍQfw u^?w|ia\q41u:.G|}0;^Y2@mTp4O/m.4] zkx5kQer /=9ޤa]КDZ]7GӚA)t=V>1y(ԯ'E!D^L'_/MC֌5^:K7zD %_BQ֋fV Aƻ$H̖X/x]is=Fz'ɸi3x-CZ;z$_SNަʹkzoq]bj#s/ ]nؒFd4.s6$E=r]KI}p%cLh9EwB}6Z^ǫ9P]QSNi4ziz^׃[5nE׏rt)(^F5:͔0뗯u[^B8^ d]O |suz0&C|]~'7O=16ָB&W.βG4V+*zULWhȕ >ߛa("_#|_Ǝ\_u +C inZK.2'a@qiTl]tK>')J{]P;qqJ\yZqyx⚅YN7w (.n@nJy]o'cpqu|} ]9_yŕudKzþƟ tQeyqhUy:@Bdt]^'啱z+q5脵]YNxn5啣ˉJ uQ@r<iMdku=md88,d(]S%mM,`$t]=u7_G[NY,^܇G:Ҿ*u^vp>W{n:@gyxP_5R~o]hLo}}t\^BO> endobj 78 0 obj <> endobj 79 0 obj <> endobj 80 0 obj <> /Contents 81 0 R >> endobj 81 0 obj <> stream xWMo7K%echOM[R8,Ɏ#%˙V\/p8y u .U/]lv7UwߴGכ]*^J.L c4đuܙ>HǭW<9h;\3ÍAɦfKFϵ1L nh#{40XŚsRȚE]gѨs#ma80^(-whrco!gWVd$`aVV?uD.MG#_]r' ).Ѹ+~lcfu!SB=Ezg׸3E ?qOzP}(ڷ\ )5vzp1%ყ`¦Jaq#&gdqd{h!m>]r]CF&BJe&D\%mx 6pZi]8Op8P4>#:̈+6&,k+littQu[0VnHOT`0Tۛ|"X9?ʘ)H3Y#R%"T'wCOm1:=4A@r "A-79Lع 'ęV-֕ 3֪AZ \ho]aBܨ4GϕMuX@Ϩ}ыC!Kـؼ!Z=˿@h#OFx C{p J<rR t" &x9oy)J%aJpVp9zU rݫLdђ;_~PwcG$+tKGL?)Dq| =endstream endobj 82 0 obj 1053 endobj 83 0 obj <>/Length 16527>>stream x{&GY7IL$d V8,*+{,nvE*F$DnT$"H" "Ɋhae=??}t}ǎv b[t}6jpe}H8K<䴩hˬ"ePH{ nyk0!]Z+Wz^t^ۊՒ\(ݜICZ+C+QgՇǜXE׊Fܢ{~,vuts]ן'V:Җn{ut=Ī]_m_sw㷼3,t]G׃+X-yu>t]_qqn3]ut]_b)GrCU6{o+wlpow`U;Nܴq|Ήk5?{ꖟWl{Ewwiۼmny)O\9ywZt=,G=t]eP]|ҙB{Sz_j ՁY^U'Uiw}Q=\={?ٿ+?vj]_ڹzWt̖[\ۜ'65۞c}] ?nzϝ[x)_8{g3W}gq؁>!ˣo?e^ _=u;q]_ 뺳u7L5^=$?ctS뮻nӞ}c[]]i Gۺ^.Z~k9~q|mG޷z⦵royI~N0+|fu^{f/>g[;̭_}u^wI~~+_wN8OvnOuiz#u/>Ga;?3n1z7ugA^5|Z?nf>Sٟ3uiyuB]^U׽^ ۟x1q9"_3fcWz۹?/{zI>>wl{g>;Eܳ턕|њ]6߹i [+?sQ_ wLȏ=]59麜ע?=?lZH{]%w+CmҾٿێO4·#K^fڽWpӝ{KԢ]c%i__qlȞÛ>?=#_OgwמC/zHo@u>~6}K=f[Ψ ; ]7Hzio+HI<^{_w1W|xEC5-ϹmbvǮ_O%Z{>=_s~Lc_}_#o?s_tN_}YV}G }vfY7;z|M]_;gnI?q>?][^u[7g^}o/<}Kع;gv>t=t]snP}xj}s>7R%?sXL}g/*Q' f>G!y!hk|ێy@G-ut],~mI9GgC?kzvG]W6]~;n^~h^):V^-[w܍X]M]wM]lo}]cW>-7xCor;t/w&Ok?_}oS{`3sn=Z+lJ_|cًヨ)܇ǜ8kΚ˔{sg<[f4 6Tn<6WgȾ__N<MA&#kyut= ]UPUutf*u}g `CtN-6f|1^'wtϛS_W!;;|Fףzpot]k]wBҋ?xOD1oU5C,t]=BGut b)GrCU6ut]ߖn{ut=Ī]m ]8 ]G +VKrt=,G=t]e3_~@jm]wWA5uGp]K]G +VKrt=,G=t]e]Ў@ v]ɝC;޻z=]~L=7k?a)rP.\;woȟ}y<,Z檙dH,$q^oFԊ<iMZ/NZdJO$ZVu:Tv!`Gz*P]eW^]&Aԟ)%_|^.z,/_Lٹ=sϝ7 5ZtD@oXu}&z2 ;JǼ /D)akzmkX{?LlՄW\_ Df!Ӑ5=5?^p~$)E;yu}R3J6'B`E2FOF]/%L}e'z=0<>ɥ8缪bƵfJ K3jy=3SN_?Rכh~2k}G޻+镘')ߪN~>K&3PƕZK.o[%KȼEI5;0]L`rh\t韎vH5=U?D8߆P>3>GoWṯh,լuBw]o%8ԸokT'xkG=w$ٸ)uy [0ٔ\9+MוWC{?]㥼aDF@|ychw/Z5š?X4qC.\]}s{VE}@W:*n﬷:CsM(4:WSE?Knh]l<,;CBD>9(( ʓ&0 Çn*m}(ʹ./w/ 2JW:+7~~U>^uەc`;|]s-3% D7υrט2##o^RBH :*ϟI0F]_ț4&Ah RN,Go—e5i tw?]y묎+RKqs5'_G.)mѮPM~ɞ7ٶߩvUV0T,T^g:Fo;4]zsAG/ŸtT~*Wury"qk+a:JMJxAڱOk^_zە//4Jr; ]rL]Wnz^G զW?z0 }|^sId?#\*۵%o.&}VsBYzxǡZ\_V" ~jW}X-Bߟϡ9h;¾UviBchPt˸,i}H^9.d?|9)fML;mCh}OWh]ߖ}oPCmEӴ~+xkWj-z2#AcMtY_HJ4OsztG371bSJ7 dY߇pQ0,|`<u@0:x _|`<u@0>I{ 6)?Rls&l4zxBB%z.uZfC6x xaԞCi?sze|9]xqՈrhyM!U=J6tUmTKJSBm.xIoSTY;)Y]ŴA* 99#jY hVhn] /xHQ[VQs8B;_o(5ⵜy]gY 7WMv v䛖֝ѻ ]ESY4l6џVgqz&Y9}e+u?hijt^ﲩTYo!=__`Csi&U\n7(iܠv+l:Avv:X~؟b]9܊K, ]/\f䡩kzf'l7ڥXx &RכjUϼMNiA+5zP}VNxY_[j\z?`vzr=_}MBo6n_kn%(]@9zu]&EwD_]c/eosI]^STLmx2X^/\1>q_^z Z7ݧ!j@]ÎW dIRυz_#C AIL\![ۭlŴc'T 5[>SefyOkM4[@r񎯆Vxfi;d;]:u֡uqo\y'J^#.;.6ޜ_$I^f}B0:x _|_!SN9S)7 ku@0:x _Ynx#xG?q>ڵ I7ʽ>,/>?;5M,]'{1H#?g 5'd*_SڏiJe7W]G府 8eepu qBO4vB5O1嚭:-]xU3k?e0]{O$i\_^f~DvGF+ch9Us|[*Z$zӜSzSׅ~ Z/|˅7wDŽ M$pYL%A8_wjF]׈JvO>"o\)/e]7,iA9qzCҐZmA}'X[5kk]gߥ]Ma]诳9hFrRJ^Y ,t?ZE׽{k0ui,wS~ ^ejBo!~/^-t mv}#h.A-լ0e9_PzZ<ݑve[-8\))Jׅ}A#.S]OX+uy"엩J&HA_ ]2/c\tݺ(}6+ȱ+;}͡.Ct]3v]_kMYC˭Sjg~+}Yh-OMSFr'ʮ/zڹD[oUj_v D4%oډ_9"mTvU3BLR"V; qq쥄]1GS͓IQM j^1?`)(Ȃa~PP)%_u0S]ze_H^sL;%g)r)UnԊ<iW&d 0ut`<]zs?=sz)ȡFoEśð:Ɵ,Ÿ'?x#bur'Ruڲzi#~eAQx*}MGUNXz]M..LYfa\0IbRm=À=6q +[[ל\s 'Z_ǫAKEKx]4oi!ߔ]Mow+z^ 2ƷU{]J?柃0.m7 氯'tz Z.;QMKx5]Uq5OO~h\2C9g.^?˨ScuZGS3b\uߏv`dW@3.^S=p^+[Eǽu^OPtݵ n꺰6d9dZ.tJr+4BAt=t\+hB12ZӥsAU!̩t]3GTRtjtY_9awGޠɭ':q[X~P\׋x#~'\wh>[=RқQM}M)}u5J*Gv\Yԗ; Ovekxsw[ʘP-Oץo=70u{zn4Z?/tNˆA=Lm=4Z?/tNˆ/5ͯRn֔'ƕ5I?-7 kJx)ה53E\GMGNˆyɋ.=]3;M #fuuu'h >ZwLF=w$ٸ ]-;M #|`<u0|}gƋ֣ޗ2kerÅ~/<Wi5. k<.)T*ay+W|:TBD͇PU⭐۟!eӡn{Yׅ/xir]nvxE>U7\Zdy>JO|e*|kBhjj`yxǷd]>ު1"3}F/QaQ||?!%ӥq롭zиw3Ci:g\565˙#&"__1|F )hŊY:^rהuOM户惷z&t &2x&!_\&±P;V#w/K7s; wjY{\y|Kw]|}{h;zhcƱ߅iP&ͪۛ医w񞫹)UN$B#a)zV.4j+Xu]ke͡hmYڸk歫B. ak89DփYvd rz~6=q4Ubޗ^ѵ~KwaI2;)4"юL+_7S:x _|`<u@0:x _|`<\39R˺a[ٞ+\s;ȣ;6}{wq?gC6jO,xLdjO|1_ok1^k5ZaSH {u4R)*]mU[ƭ-ҷP8^eU_/d e|=pVzh1A)Jo'nNΈZ*Cx:4[#ȭK:^RԖU(כ+z 8x-gf}Y=Uᾨ]o}kDugnmBvTuy9!?MDz\vv~Vy0,xY7JugݮF׻l*|[g\sڵIU!, Jjvw7] N{]%x:gDXwⒽ5 C׋f|5W/yh~Y ۍv)>A1a.Bɵ4\~V9e} p05]EC5"S,EJKYM97e,|PD\ސ4)V[y_ eM'rYwijSXlu=|ZoyWidj ݏDwrwuoy^Lm4].B}[ȪW| ]Guu=hH8.K:oP˨f5k?LY j*+{wd]٠V+8z+9(ץa$kʽFuaw{TVJmeҁ RB׻ l|(]n+J rNi_s]׌]uvcZSr~(JA_oZ#SӔQIk㋞v.Qv{UW㭼]vc4*Mƛv"Wa]̷P?+/ºȀNDqy\\7{)}oĖTdR{sS|WOX J}x.d,@GJ;|`<a~o{ϔSN9SN,!_0:x _|` [O?hR{BS7ޚ.VZ_yZ_'U-o>Ia[qdWدrۗtD[ETso zY*1L냡TAu0`Mjܫ-Jh)tg[ל\s 'Z_ǫAKEKx]4oi!ߔ]Mow+z^ 2ƷU{]J?柃0/m7 氯'tz Z.;QMKx5]Uq5OO~h\2C9g.^?˨SYINZvкG^;BS2+ Wf}}]8]땭"E^:'T u]oSt`2|]&;t\3t 7{t]| 5PLV~FtiŰuUH&s*] :բ|ى~W\~w Jzzr񺈻neuqm7X{Gz~GCh~l7jk\,L)ӮW6V9"İRU g7n{?U?~e\-S^;=+0{|eWƄj~.5L}[~ؤ@wJ:t|`<{zn4Z?/uS[;M #|Nˆ!_0@dj@0bzs )7 kJxšҖ5IkuS%i2~ ^1ajxxaĐ{z i2~ ^1wLF :x _ar%?G/e״9=LEey+Ghq7k?^PuRJ1?4IwdZ9[% iR6-yq)WWiN9.#p1xG6ϭ%4Iwd*|_BhjjjͲwћ߅{Dn];xr>S#tέauWj{N㒵Bkq.hC딼OJוnc@> IinGvCʛ=[?qyrP57tN y;O87ڑ˅C_.fvs7s堼 C\w񞻌;5uWfܭwxy1pXB]nBCtKt|'H9NY3Ebuu3^x}ަD3[ڸ5j !4 e*y\T]/cVӎ2^{4V~woM.}BOX]Nqq$L+_7S:x _|`<u@0:x _|`<\39R u=^_;ȣ;6}|wq?gC6T|(LԞd%c.c8je9< &AVKFiRzCUڪ[[o%)67|(ަy!3'uCi/L Uz;AgwsrFղVѬܺAn]7^/ EmYEh |fȋrmwe/|\5jַF$ؑo*Zw. ]ESY4l6џVgqz&Y9}i\]b+u?hijt^ﲩTYo!=__ ,֜vmRUH?*v!ݝ jWHڥz\O`cFuixp+.[0thWsu{Ҏ܄~?_ixv]ʽ%])_ob(uI?V[DfatҸ[gg/X岯<]~ Snk?ݯW*<:'=$t&`VB󸻮^nyQ'\eR]$yO%={\BPv$fOV]O5oƛu}폩$Z֏z}JPIиckeGBP'Jn']Jt?}|ݺ*G)7+'F3]rlmMӎ՟Hv*7+_ZlZSw|5z;4Mӟ޹' Ye_v5B׭["֑1oq({(L{sv` n l#>$y :x _|`<uK~U{SN9SN,!_0:x _|`< g}QZAXn|sC=ծ|V(_[D:k.Zm B-WGA4-U?R=BM`тhV.-K]Mf˥ؘ3t_ /Sz YCjoHn; EsI juݬf)_DSͿBQ}elVַ+4juG\o!4 dMHQ.Lxqzj_ͳaL]W:0A RCCxߊ7͇au?YqOhjF[TRb+ԙ OB eG:I? c2 UnYݛh(jznwqaZ2 [%Ir}07hI{UX^ -Lzrsr1wdC x>h[;q>M?Ҿ.qEϫA}?sok]Xs`[嵭\CosAeG8q+B*4}iA5kY};4lE]݋guj|]75~45#I.ZkGVJx=3ʬ5ø u[ĺy ^J]Y? msN3LƜ dǂNk_z/^Fq)׊h.mv2 It=dN벝t=BZ7;Qӏq +A [o_Ou1^qm5?㶰L3;5Fck]O?O5||ЏQM}M)}u5J*Gv\Yԗ; Ovekxsw[ʘP-OץouN)_uAÍ&SCajxxaĐ{z i2~ ^1wLF0_ozn!faMi~o\YXSCr4?rMYXCaj׹;M #=Lm=4Z?/uS[;M #|Nˆ!_|`Je=vL%_/}-T.vE鿾d]w}[xݦ7IrĮf|ݕ&3l^Sox9]иw3,+/:^DK Ⱥ$ngxeB\͕?qh,ݠWbCzF= \A.g&_wwSzX*;++t~!"7s;橃C\w9zM>Kt_j'a|}ຮ?ggx]FZGU~>PJtK롷pJwDz|@Y3Eb Y*}xьkL~Y)5j 󡙟 ]^D_aGbZ?f3H2z]{K3/qw+1Kx_jVPh'.Uc%;)4"OhGS|`?w>&h}A2~iִR|];傟ʓDq%Qi6@}WB!kRDx,K\Zzq^5 WkuZ~Y&?(T?t !KWםuL_v]~i]WYu}.;R|Aqu4W2̷3%I^sLU.]O啸^Xךs*5c\ˉJ uQ@4"DZSlr g3OPJTYD^zA'sgH;@t}ǘuE}x7|% Q_Gz~!MQ|ni3iozEP8ٿ熨|`<{sa7\>P./yu@$]?G,>K]\#>}e>jQ endstream endobj 84 0 obj <>/Length 9818>>stream xy&U}f߆WP8Ide00QgD%JlЦEHH" E#4;D$y6io~[VwNZz??:w~g_mȶ_>梵#:M{u'u?5ӟ<4EWXдKORN‘.C).MA],vtީLuWӬ=5Rkdrr=otހyC{V,f͙n٥-58%18U*Vܧ`9^#~]Cl.u)u.t]^ ]G] F:R^]Butݵ`T/s?)/ѣSAV,[)M>t]d]>s}ƺ?uR:O]XQZn4u͚tW{v髙dw7.ϟuRb]폢}_'FNuAt]k:;~0z 3}_mhˢ]Ww]+ t] ]?jWbY^/:s7Y}}dct{u?raG/q>+[0m{qpw~ⴧrnɿ,N²>im cV/n7wۿ޺77*xͲgmG7~~Is7_xټ٧oFץtݡ&.R]C-}O鳔LA{-v>6v3O^GW.?kٶ}ߑw?y0X{_({ғJ.^Ϝڤn8Oi fx%:ruֺkV?oc雒]|y7*Q{Κ,[꙰ac)߲Rz .2JBe/M$dS|t

DuzB~;-g/Xo랷|z}Xo_?of/b{7/IN{'d~d^Q -{&N6&'jxG/uWorғWW.7-N7m\q'_Í;S{^:y3Fvߕy]QeB]}G-W~uv|qyѫE>5[WNzc5~t;D?do1_~zQKmkzqXM9uS>eUv];u ݴ?Ҟz\EӶ.YoE ~}?߼:{ׯe{$g{̻?3Ϧucמ4+|aۜl.KϺ/.9_ݶБ о5sx;-=t'zȦ:'GtHFV> t=߷yX?eBc]7뉨v?92J{缿ڱ䐑?9~N-=骦l"~KLp1Mn4|̶.:_vGo۱EfD}Ҷ ]OAEuY)t@@]J{T)])tħz"ɵTn><ՋSnM+R_x#ƾ흉w"(O7߻~M˖f|OI\?}OB7-7yb;N߽7M|pbK>2]}qBO{F!]O6[{Fu΅Ǐƒn˿5q=_E䛶<򼮟 2;uI)t@{GSC_cgOjp7?Io<+΋7||l~%qO?ps= Oɖz6^#{ΜWYg೺>m'sܷ={},=0r7um򉶔b$411ڿ~[_lٿ}HuG[spgu !>~늾O'-g.Ȧ9/|uٱw+f= Qǯ_Ǩ7/j[^>'3zѓc_wX婮__˄?tc^0oΈ?NX+_c_٭vuR躤>Anyُ_gp(sWGG|+W}x|/Ѣfv䩽/;u=R=.:}W7nHǼM|mձɭ?[>|gHn[Θ5NhgX%36[F:uNOϋ)t=77xF췴o[5W >m;\}աHJ?a)Ju}Æ;Ð8Sg-l-:Efud;$L (r,Ғ̉d]oX\#eT+toѦ<վO}C3u:t=rTw718N>i(C[HRt1iy1ξIy@t}Ѳnp.ʜ ): ǢlYם$ͪAazBVtCt/vI[vba_h$9]ωzB*@ <<* ϋ ֗><4Kz@io"}xh>!@Ċ|}\d|;S]8UE*>Dwߗ{N~}xh^/VK_"wϫ=7_H@ٲa'Q9k4.:ju}1ep]uP]ڨ0^w3spJVuUo+x3N긿Pu uТ& my> J\]=іF<uߛSoﴛ}Z%fupz{)U|?N( G6]޻!9=EP-y׀+^tjE7;BǬw E+oKU_R=ueHh* Rzj@hyo<-$WZSCTM=<<%wkox~yJ@Sꤪx'Z^4&ӊB6yjp@Tt={ʔߍ޻+8Ix»̻v<0wgnv{vL\235)o~F5ؐ99%e'Bo픜oMCh?u+d*WUi+Z(Y#uwW?-GAM3WBSUBWp՟2-%Q̉tzl=eF0OC[$rmtjJbB^?VH?^!_TK]uԜUpzk^GյPrt핤XcE'ic7u\ ]Xiq097tn0O 47t?,_ם'F!t=Kp zZ(t*k|z!WU*]{4Li [~_5GҦ]?%u7Ax^rId=c8֫L`m!1B&Jz]Q{Z%[6ʚ߯Qt̸ ;Ys#RXYRz2lܪ0^ώk1N*,!6F&=JV*t5-xVm?gYaVQE N^{m%3uReư'N 'Ur;NW:QWZBzt݃zNbTxV-Y޴ucFyM͑w12"뢒z-21_TT\H.-#աdTqBVڟ:Vt=|31pׁb/Ar麮z+:l*|%^yb8rsZHE.(L7 P@ Nvvkut?KڱV"9>]J;3^I!`?[qrү^o^ǂ%^SE9}Nzzt]%3k"th_X\\c#<)Bו2+ҥRn+ەEx\4'z˜C)iouR,7.oߺ]J0y\^C/ZgkZ=vATfH'm M麫$Hhv4x.e*x~iW(%^#!Hn06SzsK&+TӥhGO@j#[*E2WԟE%uˍR|u+78$YоՈΎL?Ƚ7eejk W@tu@:@w;^/e#$tI't%uu@:@w ^ݡx_7 k'EaS?W'~O:_na@2gjIV]WOK=֓ϖ@^в2{ n=2H!%Snvcpu]GtSv3.\%w]۠뒱 {ʏR6j]O$CsWiDe|ʚRVjoMyˬ[NTe^,xk>uvc$-2_8" ˨U|s3H$ (xk0n]^ hE!7<*Za>50dAa~ТxJҖxC:@wh{s9=N:餓N:bb:@w:@w ^݁x;tuЖx:'(+5[[6穧$T@{@^zk:-zk'O\ imkou\C=6(qTu{ UͥWQWt*USzLebb.x9?hjb]C?4cC5Q3;^}/;A#M?W^t@;f]*ӡVKՑ'~UQE?XLU#oj|sUoPOC1]I3߇7h r\CkuNǧΎa-J+ WrmW־x~_滶kPƽ_u{';N]TO{ N5ףlY?I2ѴՎ"׀Aϊ_gjq7z7qo:/ǩP[ nuzE'3t9^7d3;TМurmkuo#:.յ׀SŸENmhv72 At=df;M麇NhKDI?JֹbQ-/d{u%H[*uP(jC׃'jh>-~̥gJK .HM +]Z[X-L4;.QeS:c%o2~arvjhF濲u}KZe̐OݩFs?mz-@y:x;[php2l@{[': a;hp2l@{[':L3ȾےbbB~ĄI?M/&&OK$@nasip2l@{p°w8~a- @{a x°w8~a:@w ^CoqSRKz/m 81,z%G==1D$JSU!1l:u]h;HvwApCx]}o(RUƣm_:P5=gq7#? eymW ]+S"ãs^y(3X^aYmi)3$OIio^tK(m<4COtUC{<*e%e K^U(ti{<}_ endstream endobj 85 0 obj <>/Length 10350>>stream xyUuN:t'DPT (3gQ! "((KsdC("Q76gcsžNҝNwTTW~ֽU}?$շ]~W۫lg_''oO']nP?0g_^ӧO%f&5Uxt&.6. תl'Mbyޤ4yr\{z^p-)垫wٛ3ֻ]Kj"E1X1*_{nR38ASt]Cu zuN];ubt^ ]-es.wƍ߹W+Mנt]t`kI]+%]'B׷t)ceO/uCסÁp-)ҡz$q5,H㺮[7@XO:Q!]N1]݀ J>͜UFy*=zAo.Z@)@9uuaB)t}f71WvݻeC_e^}Zonݫe֔kwL?u2i윷v3}`iVY+{ۃuLom{a{Km{cFV_U}hz`5QRNR;?4Tj}_~0}嚁\_kLןl{|y?kql&2TN[Vׯkִnit^w/?%p7?|&^)EAwt|}cb.ߴR*]caߐ1p=-)ҡz$I3$9;׭?1kvϛm}koy{f{i?6/x0w|d]VطK__j=mPȫ?j|q`ciqw2Mn5oD}QĖRwuhmRӬ 6z#.|l˔ M2 ;^[i9cY_s܄Kh2>|gǯ\;ouU*/ϟ{F^',u_O7_7:wD/Y[Y)Ժ^ur|: ג"WJNYB]o+O1޽ŧQ6giߛqXH).eQ׫ԄA!,l?}mɷoJz(u_sTPM ϫN VQq=L_ #~_xj, &;cÿv=jkJ)g0gM^ڮ3zgPR=+%t=tdN+]gHz\"Muo'4}\>#.]yG>SͫV+ޚPq]Bmpc0Vê;Ϛ2:q\p9[ϚtGM'^-=yXKMC]?m^["}OGmz=Rsà԰b]ww^~#)ۦvLlGsu6wkj#?Q~gʦ^j!>5㝻R1hi[ҭ7>x}B>e™8w~>X&ջ۽E= V?7fWw_u(qX܊9el;7gѻOܻ~޳IS[.\|k\d+jgzk`kO_OS4uP}k߽7zηH䃮lP׽GDLt}ﮅ~v/ᾢkyab$"]:WׇmWno_=wx:]w3'oUSz_=溍Eh mܳ }t M@AFՇkeO]b]hO9w:-~@x:>vWB:<}f{د_+2\}>ƾFĂH{u߭?.I6 CnJx=G{w]AcBiE{kgn_k^Y=kKO\&с$ۯO%/=}/?ooqc"iu?9ؼGQ>w~7Cס뚥ĊX-qiJf >Jw롄#]zpW?g[x;@n[~ǞzeN-(mʶ|BC-jrN{Y~']syw{_cO[7qF<^Ǩ~sbQ޳yNkݷO#;^t}م|pO`ϮwWǜ׹~9;ko urNg-`{m mU1'{c'1Wέ09߈n@uR|uM::<7^j⣐ݝG}媣[|^@{S=nxo;u:|$Z/]#W}-|ɇ~h~0=c;0P J{}SwwZWKʵWOnI;gqqC뾖'=<N9]n^'~essI> ]땂+B]_끡?@N0R7/Rc@ןG?u&tiu=Ե\vޣk;EHsGN`_ì,3:"q]S୼P=>4=pH}5v[ud<6_'.%Џ|xn,uWj/+w4q~,Sb[uΓCvNZ׷iEt=|\.xmCUKuyVlW=C^|c뻆Wਗ ] Oу/VM <]mꆮC5KA׉IS˥b2^;%EL[?8ݕ[Ξt柽s׼ڎ?x^/?K3yk;յ;ux:ǧv-ϟtt]ߥ wi=wtL k{yۥ?u+JK3e~ !\tB7h橭;'>_]VgN:&ty;v N߸^!H,X8qt {Wd /ٳg\FXu^~%:'괐h(@ G"QjgE{{&"PiJPgkk[tHs]hB=x4"Jj'(Hr[׾r$zš(R B=Mimy@S$UZ !zH o锶ȞJ<^^wEqs.Qb]#:׭L$Zun)ut)KtJ-뾨 iuRNgv^F:V#QKy~Un~ezϣ%Xq~[yn\%@IzBC"i7nXճ[չPyxiF(8.,J@)q"Zϫ+MYq^;`_,;x@|E^RzF><}069׎%:].{n>|OﺋHvx޿;{_@T@:><P_'(^@Ju~T,~qy=2 @ip|gpw_yG^GeB!㩒E4׏>$`+fhT~֮E"`ہ*U8s+rﹱ͌#_c)ihҎJy|Ģ]ӽ$6r~Wd /$}^o~Z5X.l:Ųnqw=lh="3?buP&6?,Hu_N^5t1kS$'񖵟f~﯋)5T+ޒgv Ste[: Za^Aן4ME4q-^W4^uS4ͩY)~n+,'/w=uݓ.r'Dː'XucJ\]f %JȎ>D%"$ꎋ+DҜĭh5+򲧗7{@@\8t"@a)"8%u[nӹyM\1.(<}NO7fUKfϗ4VNm.|d?.9utN~K?ukmxT*w]ϸ)T$7R:+?`Z>U=>4M6^|JmE7^FJ4K))MVċx"OID ++lU[bk\D2 q>^빭y">1QV:.OsA68(u}6Ӣl lO:ۈZzF9 7omVJnk\$^ɝd&cЮnO8<lk \|гqG)pqÝr?t}WYRTdz QL]'W!7=2>,M9"]\AAyxs'؎|X/v]?DQrO)/bu.Hi.뵣C#{|q3h$5Oy"'Z 1Y?B]Gv$뎋mk+}R:'?[R6J߬Qt=͸ԣkaxk= _rx=>)ST*(NJ͚n~J;YR7\]=x鎋hڋLx9:$zF"%JOy},͓{<?W:7Wnu$DnzpC@ ^u< ^u< ^u< ^u< ^u< ^u< ^ʃ_׽$ڦ'uNf+ ?%'iA+I{O"k~P?e0^k1r,[A(ت7%}ać(]MdǥE]]8^$)J__)u]ϠT2Ɂ)IݎNǝu%y4KԵZt HBac Ҋ=Fț/*/'qEr]R2?E;ܕQI4TRdWaП\g&#gl:/rK:]n| <^`ht.W+gջ@-WhQۥtDKo[4h.CC9{&/fCT+1n^c\c[](^Ý{)u=N6NV]꙲Dxb:Uh-R׵lڗXHƋmWqASXzi!?_\w͗ҁ,zk@]7l]/%VWF@wY22߇b%,& K:P:P!x}ϋGt#HG:)lbu<:P:P:P!x5"ƿۘ>ߓ+}V~REuizbcYpr׳Y-Z2.q+#HR?Er7WEKt:kǠr՛2g{s#=,=ѭ-¦s&$tU1uޟtr8SL'FY EוQb4^D7NOk$A=v{z(cn&˵0)Ov~&XKMx̷4ǝqh^qIIthxs&%̇F#x]it]W_ *{Ć[l\ŗr]gM'8fU69MEtnuMNη4T>w7z6%<[{<|:N 8lsY)JHizaʝFוPh .r)!6] =sT݂Srq$9o֛kDžrJlT=:,ۚĽLWnRuFV׉ClpeQSC]I+J%?:d:'n S׉4 z.t=ͼԒޔK׹ g6h+}N;ǝ~<PEiu=*B\%~:Rt7ŭTb_.$YO;(Ӡ=/QqKV~ hPZJ4%7c?qD(HQ맕tIX1Wb\>.zEdѾ C|yP{s|zak^a~ꂢ|'Y`~HIQu:P:P\{HG:ґt#]& ^:P:P:P_,ίs*R,͏"QA\>&A괎O^J{ 8.NK ]mW>~_붫^=ﺽlPEigO=T>z6f48Nu_v$q^ =c_d_i*q7"q/W:OG^[.Z$n-dO]2lrǴ s_MTEu]{%hUjQL!Z~t湈|.+`EmMf[.TN#\7HPOLn}?.avBl sD?za=$ﺽpZi@׭i>趷(:~LRS%.Ls]Z)ZX-L;.qQwخ) +!ϭQo^I):`=#bq:S@y@@>\ @{F x@{F x@{F x@{F|_O= lbHD{Đ~&Ot61F;E{F_Whۘ4Z? ^Whۘ4Z? ^Whۘ4Z? ^u< ^Ci*R&~LMuw_3^#Ŝ,h/h2(o.^S4]/["ug3$"ᚢD .hѝ9yN_7Yۛ(HZȽ |\lݣXz4_b*?tѢk/<$/> endobj 87 0 obj <> endobj 88 0 obj <> endobj 89 0 obj <> /Contents 90 0 R >> endobj 90 0 obj <> stream x\[o\Ǒ~0j~ћ778[&$ (rH1Hߛ>]ԙhfX^]խVbj%oH>Ar>VR¬N.c4bGYV'~ǵ7k3<~VBFh?hic![~2plֺoS;z3v[Zo"z]);xxg_cj5"qG6׆߯7bP1xU-( F{: zhʛөG*]w:f\e|6:5? ]#n"]Pu7#Q/AUb ͍|a GLɨۆ%x֦BSA#DQϵA A AHAn\IZؾ4YWpAuSNL~@ɫU"/{a,m #Txd6*WKOG֪Ucnh:KOhqOdsH~ ?3 :cwr_3QwM־b@p!5MBDM[GQ}tH p0hJY!CSo%k`dUS>O#}< _N~<7Frch. "!#rjpsOmH-lJ W&7J~(+֢?yd b`H12 ygv]q<;G0+:uGN-njY/:ycD1S,AqK=S*%֎8B3KDNov/L:"}n=5YQg>7bn ш+[Yx\ e\}הX#]3 [+:{ navàa[vSQv(F/0FI@gct(J*1Y׌Էy-`GɬYNɂoYŪ{)c0Sb`pT&QD}~FYL)\{Eً }KUU~y۾nĎ!皿! Rug]-gMEp(}0 ~q@f0opMDK s8s>q4[; ;q2IB<ƕqI! 4/bw-,m_D~)BUydwTέZAx-Iw̐]q@%Cz/] @( q*LL뗉J E@+/ד4BΰȄ!X4"':h]6f1Qz{뉷y֟qԽ4t_ZBh3-,VYlQ7 HQW 1._K%'!格pFHkfXw$$QzK1o)q2)f2l/FE89Ȁ+mʯ>n._jLTTE>6+S#8\`)s9OiYVbBJ33ZqD4&^]gz\ J*6XaC_–:МN S Ns,/tq4L/ɘQ3ɜמ\0<:#q\a;N%} up>X4)`kG5z׭ .+$ӎsIsdi w :` F:щ~bJ$:}^L(o LֳpOؾ̓|VVq[ #m̐JfH_IbȈQ$2*BTtѴZ3g9PM?1PШjY3.qU[{60Lqf{ 4uҠ&nG`uZ/5ĦlݽeF@44Û{:{zkHhMʒ4[lhiOlhl2V ʦZGP6uED)!"i172u!Q[~4+kU'ŶGY=&Ҿ˭f  JՏ47lɻIT'at[1,Νlj9Y;+Lijjd^c1 LˡCjHbF ]ڣ9^3vOO#d k.=ҬHrs4)5t_-`bE::BOw]NںC#W2|Fdà2YO-&h0ه NG6wLle57!GA"|.3b&5VIa - #gVS@Ǿ\b!GzϽG -1=uӕStlu:xsS'}fwT _"cCjvǾjLzAZ%kW[loZ -Ia#Iqkm*_bnnP)b3dP5eN PԯRS$2z70(7%eדo<UǢ W%vh DA@#[hp̆:/Fȑ zR\~PMFZqxZ:\E&7% nZs;沭~SW|\3Ű ʮYKx]3 F,5hLlUh~yްy[H1K~Q8CK_9ԡ [f,6&#ˆz˨%w/\z&︇3ezL+1KD6*\2I)}X_^i024 2x45A|xll~ Kf ^ۛ~!}/ӇUÑá y*a( pظKS_.>7)N3'qu$QRF9#:X AO fC/YfTVp4U/ t=KWgX9] 4kE0Ԁ! fC{$<5IfmY nTT76"ʝq 9;nW! q"X5؟PI&J"FS|~K<әqL[ ?5בo▢˿LܸAGݛ>QE| % vdZ%Ey>z+]t+Urs5 >#X'Cʠ1_sQ`li2  "+j9P/ ǡc(g2v!B:y~/:ٖhY$: S)}*+p'? BXW&@WrBO1QՑƭ6ڵ6cK18pi*5V4e\AG2m^PaR6=/rD})鄔Id"=T*_ U̵t?a-eel XkU][]dW;^y'Kh1_y+ۆP$vMpEkӭRD?@Ow$ny> t!#x>| <+O<4x>K|+< χy>D!*`!G,<-4@l|V`SE_e(@Hjꅤι֚Sa87@m=xK 9QTa)p?mM*hDlGC+:\qK}S;o'`W/EYʵ'a0tVm 9tX5dr:5-YA^.3֩[ڵ3JdΕ|%j!jZkkS]/Ol׿$ڶ8) ͝f& tk0ݡQmrrل ~Q1B9$Iژ^oĴ*O޾<~N~ ;->G7A"K_*oN W ߖ\_6oחV d7ixE5{k}%LP[cRAH?& A"xq5ĝWz/ B^#}k/ڸHZ 18oC~ҍ_mc:~czA|c{=ϫ@|6|4tO:_soloA]D 7*VȊEumf,D qe.85SJL^j-UyѯɫJ_ xL)w0CShzXT#;Ƿ- w:֪ܽt`$AYM8B7*1"d󁫘EN[ zEm+)+uXWq>^+0Nܚ| 뚓cMߵba5J4 N1Ŋ8i0,*˘Rs%q]{v9^[=|=.G t@<=mvv葥P<]/-KVFt@O#O"}*\ӳtrt@zY:9zNn~yz9۲tr/#Keef6[f,3G7 t@,},W6%ލP4+fƤL”VߒWJ^endstream endobj 91 0 obj 5493 endobj 92 0 obj <>/Length 10658>>stream x{ENޓL&I&6$"geE+*䡼A ǂH"y!Hͪr$ΓIf2Lvߞt~U]ݷGS]߯]MPֿ̟'7ׇv &^mgsUa"%)%%I"cgGnC*Aخ=g7) jzE R"3y/]K r^ ג"]z1c+]R.ں&=st9yBu؁3@RunA]שc]W삮KAס tz>sϸkCסÁp-)ҡz$Ywu %?uRu:t}8u%E:t] td3#]#fe\:=\uKui5'*#dYZ@)@9uuaB)t×(Z9"ۺ$-ttc]]&Bgx;{UoZUv=0'7xfs^yNXӽ}V;}IMcg?iO[صZ,eO߼czkSV C_vX^;320|u7@WVGUT_K99H9iP}nի>~z~1]>ʈ#={Wnlu&2TN[Vkohִvivp7%pw?yF^)Auw\!K\oZd̅\|Kt^)A>:fAue Iε+,u}>?͵vϛmykuyf{i68w|d]VK__Wj=mPȫ?j|u`cmaw2yLn5wD}QĖRtjeRӬ ׿y딙#.zr M:^n9ScY_w܄ˎh2?|ǯX3owU*͛_qiϥ?OXzQ#v7w:D׮Q[Y)Ժ^}r: ג"WJNYB]o#O1}?gS3n/8U}2|$]kB~ koя;7&=뺯>*(uƄՇyNY8Ԟm+~_tkj,k'3ccz}jkJ)g0gM^ڮgPR=+%t=tdN+]gHz\"Muo'47_=#,YyG>쳃UXHMwMz(MTnv1^agMS8vgO:SMl;ڴi~ ~>ۏm .xgM .q-/l͡RP`_6zfmOouXע'=ui~aPjX{;]W)ߚ;vLl ˗&>mκMO D]_){{L-]שּظuZgtGKӦwl-ECW~):hsa`Wmjw2b7k zsmW[m/.3GaWf;zɁ{>{{0ijKesÒKwQ+m|ۭ{nz0#M5oͷ7?}Xk@uR|uȚյO/,%WtO_}-=Ld^ta:9Wzh}򢮻my}yYLo+)~tSs_"s~iʄʕi֊ u_Ej$N:ͷ~w+K^_skv%[lz={wCQﮞyPCQڑZW oonx ft 7u徽EgML$'V|b<];ڂ.>:έwϭ%]K`Cs;kctnmhcH Ag9378 ur^턆oktηH䃮lBuv)>Z=h/\st/W=w<h.|~ tɹux״rȭU_?la1r}pI롖'н%v盯:ٗkޛ{6O=! Z8vRtŹ-c(X6UjK:u^):"i*tuxuo=z`/;658\Gn1u]ٽF?v&tiu=5_v^wt=8q21L?ſ YCׇYf^zޣ(^߮zKn:z=r_18mjk|>+躦%E:t] td3S]ȭW1 .tϮG/ޣcs1ʻa+Wgr_oBuFi͕=k#kΟo<w/{'w9 _ΎRt]{_)k^}IM_>/A= W)׭:I!@t}3p8pESE>.~q܉||ԋ˷mQ3]C۪9w>}S7E}Sr}i;9 +}M雃 <]yǮ醮C5KA׉IS˥b2o\%EL틛?8ݕΙt?{}뗮o_)xu<涝r@:ϗ[_}k۾;L:yByxCS4Nkc:&O ݺCߥ{8nf?ΐ@.<[G4_oFlz]/.+u&c _oz{ Qu_}ړ4Z֚7Y|GV N/]v!UZfq;֯?c_L;Sײiy$4F)A>:&ttㅟuu<+\Kt^)A>:&ttN*]sp-)ҡz$u:.:t] N/]X$cktݠt]t`kI]+%]'t qZ8 tb N퀮k䃮l:"]Cׇ]'XZRCJ AI6<]1e\c_ruz||W-Q-|E?/[\?x.#: N8wP~b/\=]eAoѶ'nPiJPgkw܊mu\\=P%Jc<P_'h]*]y128KK\2{=4^MwBC0RpnHCg魼G*(V~D bbPYX/e~Fếٽ xG.LzA7H7i+^ z=B;@En{Û]ƣߕ>'2'Z>/37R^~18Ie+eY+y /׸ߛybD·h:ޢjj1'к<ꔼT'Td_)Cnt] +]9;gȷ(I}G]Ytn^C"Niqh=Iu=e4*7 VNn GҘ_Io\nq gu=zsP!˝!.\LuJj|x!j 8WxBLO_>_[׽E<*=Q:@v<{ZP^ѪxOD)Q:EaeMj_lVƿ?e8b=5N$9{<&Jg4dEsN-J]?XQ67Ǔ6yQc>ȷECmJ“^m+D H$_#J+V5ο/:lzQr {*pǽOn:]_*땟^P ⼠wt0=AubqyO+8ڔ#uy\t B߇:w0YHgrkL%"f,Q≔t1[]u ^AhkDNVҹ3% 1:8.#c;yŶO㹕>6E-)R7 ʖzu-?_"7l/ysKֿ Z)*CIcŎfMqA7OQhi߄}eP]ZG/~ { v3/GǑDψU9D)#oq~sGt9 8GBDR/~n 48y|z(Xu<`:P^@yz(Xu<`u/d)I~b`:[AD?m3hxEtZ?ioID}Wq44 ,\#"˺VL hI_i2$JnDWqju|kqWDf=W1l{d9n]3( j{reJrRE~gA] k#<)R*u:R)PcؘDz=~DqѶ|\ܓ".JWĎ%wfTNm&M#նGd; ?U'tDnwHڙ8N;zv~ΫRx5Dם#kh1-}%ܸ `9|s9_>J$qtuټJQg8NΗ Q;(g['IcVt=} DYQwH)b'1-]5n-k͏ٔ%թ۝2 yru]Ioial\Jz4>+*du<`:P^Cu%?/!ґt#H!X:P^@yz(X!_7k\'E1|M%'/JWP?|i^5kd%le|bcYprXg35[e]f"VGiDnԯ7Kt:kǠr՛ҏg{s#=L=ѭ-¦s&$tU1uޞtr8SL'FY E^+ia'eb*)ʏaJDSrq;{dJo~ZI'z%"W?J+`<' E7ȧW'.(q '^(z))zz(Xu<t#HG(M z(:P^@yz(EY_,ίs*R,͏"QA*^G ['v㚺w-^z3:(¶3ZcE׋;,V!~8rdrm_^*}uJz5;0D {X9?ȋWkΠrlw`L.t=c^'Dן]I`'_7k/T8WDrtۍh_TvPU.ϫ7Pt\p13scx]ا{*:>Ev${)_Ki ]ҍ+n^n;PxqK5]ڟx̜*(r Gg?~(9 it Ҏ$XzGJ뿼"JS8]=^{ǿy?Zu&qoQ%m|NgW&VyD|s(.J~y#~[ S4JR ^ nWr~*eLMOѩ9ys>S`h݇+1iv@`юۘ4Z; ^Pb^WhmL/(1X+h6&|_O= lbHDflbHD;MgC'E:%M z]A"Ƥ x@I%uv< ƤF;ochxAz(Xu> endobj 94 0 obj <> endobj 95 0 obj <> endobj 96 0 obj <> /Contents 97 0 R >> endobj 97 0 obj <> stream x]nGrϧ8ȟY~ $Z8v&K$H/WQR}C^YUuǍڈ{8pO~Hl^}< N:|aO/iH9 yHL1F#8rc&H7Yy4LƇ oL+^v˞ow~x_L{ ϢDz ]lB VegǭFJHd8J5NYIo?;Lʲd9tk8R&N8Eyq䖩..۵ކɔfO M;rZn1XvDiQzOzl!'=~!LLstj2<4$o{ VjBD5L؋\`IOd]dF%u\t݉I !L(8R'fUBH"di47 EK4 }Cǩc)Xm/}߇VsowK&\CJ\0#7#A{\2} w@ܰc<O8R>C]keMԆe^tDʒU:j"es.(tki,x(vHtE".[b Dжc4eEQ|̤Pl f_q!uB&9B mو9M@Pi+ы("!m.  vRf-'/B8Rt qt&K!Rfgf&[/+3)"J ]eL"E6ej x ooK}I۠ǥ6%Ң.m v&szl"WH4ܕ6qB٤{Էgx{XyVdZR䇐!|wxwpw'Ga8rQ>gV$\Env\ttY&y0i^RRv"Q0ݹޤ}Ee}Q##Ĝ`֎fQYeeK۟Yj9&W${{P`㭻2DYzfC|9JF$| JN.!%du6=qۓFoa~gYR6sz@w/v%qF| 8Da`bՈ粲(Ahp&񽹾_SK^qtyX,NGk\g#.sKܞ I??iʦk=-bA \q$ )1 X'.stcj)g yd--e ^gGx gz !Ñ!aVդVD|K;a{K[/-fȭ)Jo\тd'82C%;0OC3[Rs&ue[zk ; lQ҄K;_3|kFD$ũ dY+ fMg |W o`a͞n` |8l<NHroϻij2"NSڛݥC.}X[(|!{=3]!z00x>o)|'eO <8Sꬖ-qwXbpNʈDz.{"CO|1+|+amȧ4+d{1TlR>)ٗMsVyA9=Us0< H{> IBj%Tl* 7{Skt@A]nl^$\E\^lt>0 {]TCSDWk hw儨 G &.\dNcv à;nBݐ#-UĤsF1uދ`K025 )DH-| %pQ=b6xOa-kTCmpkc%-9ަt5@_V3m% Zi8չZG;Im e=gt%G@)hg -Ǡ#SYt9KGݔC;4w琇 0(9|0͂@4f%Q9qpUkw?SV _?fg/, 18ɖ˫(hl_ Lw@V@ gB9ߢ]b/|+aMDC Fu&tu1b3lM 42Y5=mg㳟%&"HA9|q? .R|̟}ۓ̓p=euE0wsw( *N3 4aff0[(wh3i4[nIځfwL\ƜS0a7aZ["|Y8"z!oRER\,K'ۧ\?@ #V)Aj*׮1vz@:"t,͐ԮO#! &9XFYվ wG7 JOAtȏ4U8*ʘr{=0?k,0\22]&O? Zr\7@"tܧF/aE+oŷ;F TVV$SZ kBã^]_ƙ_nVUc QX~wz /W#a}_5pߍU-l`wxX?@ 'PT{cQ+H{[I2n#K.Aǔ(* Q7v׃Fh68rW`E[SX3+⁞i:,DAORБ'^vb{VNygo2vb"SfnYa=rq.z 0ƨՈ1"^w$>[>FbY=6SG&cʹK#ZA*eyMo(b}sþ\\B0]c@kGV1X$ԏäޛ,6{Ӎz|˚'$U|L}zc )Ɍ6O?m.5o}OoO/Gj#զD?t'fh_:h:8@-Ptҡlm̟:Zg31J9~J?篒KpEGHTB;6F={i6O t)oqX2kecMvK 4}I"&~g3)}Sa; 1nNq;ĺﶻdc<9& ʦ6Fiu>?#I_:BZi6`s].7rrŗrRn_.r|])W-wqYy-)k!5^j{Ŷwr'ȗ&a'ѹ/R| \ܖ^rF47(Wfp5l[)sƖ d^-^Ƿm/g `zrSW]?jѧ MF%d-4zEc[Z5N9ÞRK@a BZ+pPĵKm1K}JgVY]h 6N,بCӡVirj 57ZgD_/grcaYi#6p󜖛8~ѷyRdC%'T*+;k>@gs@V SzV՘= U9eRJ48M\~m3&m[+,+ ~J&K&+!%,\W8vcg>Q-^g2?({V-#kug}!нKi ^ASUZ-U3t8u$=Q="臩N92z#4F |S:V? ,S>lo3p <%}S<r}%>OEq(-?8^{^S]kj󵎣VS_@ud~~w3gp#O}ms_'qos۳†}8X{[)d'Ƨn7n$seR3ZQ##WO܂lBY`֌ y%MX=ʖL[NtGIWs]3t(/E_L&B4-MUvchPQ2ɠoz_kxH:2M`XZաF3F٘H e ʨ^\/{cwQPI%5Q)YG49&=h~*:w՟Y8a4/x/35}2UZ5{Yhڟ|tHe7L1O{wĦӯ! F_ǣ #W3A; &zA$E,1官n %Ed} vgVsѱQ9biXb#M;Z0/p9)w g ~%!d Mqc7Q&D QX(Vo:5hS4z9LUJ#T˾|dd}!uׇSPkLuP^"C'Pkw9I-Wv i]j߸e!;*MS[`p,MPz_yErf%#lr$Y g֩'(vT&wKHW4GnjcbKަ@dIKb}- N=!TC!DOR0^!x`~F:p5.TS{&Muk,.o<&=*)4\ثay%#{X_҈i^z5}~s0r(d8t .|<'E_i15X(S2Sݷ^奌(cjSQZ+J^z5~~U~d~zaX1L zWEP40s_ꈍ0[$ey"}ίjՏN$ 3ҩMM:b^T2ߺ'k/j; 6t/팣݋suX_k.oqNcLO K\iKqN9b}R9uyY#jՏ,o6֋ai?PDBuʬ1*/VʨLʈRiuyY#ZcS gKS]&S(xꀎ ;EǛ/*U!U/MBk\Z]^GExZnM|u~$$:PAKHTZ1kP^ÕB21[s} ]Zʈu^tDzRgģY^ ,2RBL+eFSzIqj/F"_ժY<|ȟhCȆN8a}hA̛^{C.L+ï.tJ7GDgúڋZ#=UmՄ"su=7S?9G5xs S!K)> endobj 100 0 obj <> endobj 101 0 obj <> /Contents 102 0 R >> endobj 102 0 obj <> stream x]r}WL9uib=9e'qJbUbQIťMR*_@eŕMŖ4 t>}w88}}pLWMuy݁]ǧ[MX⠩aUj%+Tݪ7߯Ynj-ka{wNFx,zѵ`kԭVzu/߭7Zb"V١ ` zΝs[m}35̬kkf5[]7U+z>ǣfHƣaugNmO\ښI[mљncV^>c&lårJ6GJgunRF@f}ga|„]7 ekHՍ5,` Nכhjr#wГh:">OFԶN p!TuHrtF x98 CF  dr{&K3<]HE1Ed!t#K!`z;G VؙHK f%V:BN@n=V<'@"är)!)cBF4'dl iEs=n {@}f9GNuj5貛a!䑒a(FL.R>^tyN> -B5N^aɚxR?D|8Zqݽ(B z@<6A*ɴRVHx > cJ؁(f$,ΔX>F- V w:^&I dGH} 0ˆD ughE!zI$,0<}1 XҲ/A ΤT*yT=Ѓ+qԺm=e2]却nPGYF>' ) **~̏#L!j2j Qw!3Ϝ+@!" Rn6Fcq2xŷ1Ȟ17+XA'N׈^*H(7hf!kOaNj&ꙄZ\}Kgs%Tsi,,_eȹz#dHħ!ai=P >T3#N%>u[kGBH7gϮJ4n) `"nbQd=ncy98o/K+rcgDuˈ$;ZPA߬A3>J̨qs䜷^i  H^aC(F k/.νKb'9:"F6VT@ 06_Ӳ<4Ivϡ3L^ClmF B3iSWA)ɠH0y%Q7BT??ʜ)ג׃ǘBxx8C1f zgP ̈#S{vo[Șg3O&'/B{+D5>ENt!!bAKk|Io6 vH&уKU'ڝ2Eڒy1emkPx#srQ;VQ<[ -݈ں.s U3j;m Xc+JgFs>v4?8CTYΥ㔽6z*-UY59NHY)QY +âlseMLӴU&ᔩ+uI9{j~UǪNcPIZ#%ea6eckBfƷa˅M} Kt.]EĎ \⼷sN `y%O9հ=EZX~ч{$5N[")wQEs}Qq_Xrl9E+rb;d٣ZܢܳZR!ZGdIp'bRL'1$JWn)j,L$ h/f9虺1P0W+C8I:z$*Lv=.<΢Jȅum`,vfܽr~Z|OvpdQR0(,v;!a!M] xTn# 3Ggx)ZYl&݇1 ]sQ7ZsOBy 'w.D/.*+^8N*}pAB=KJx^9̓y3{FPݝ%}~CƸ]"TFvpuC\V'-]%0˂ zE Vs7;$|W7zxaJ@Z\/?:,X_h1uRݚr~TBA E;AV_:;юlCVvpE|ߖY:b1d%ջ잰4mO x\vxAhqK$V!Hȟ">yۼ] #P69nW|ʽ PƲR m4K}@*E~93'ܙte.٣ pvńK|dj#F".لeA| 5[A>]NZA'oYܟRNg{g|wJsCH0T>FmѭQJ-Y]|vtiitӡޒS-n%̩J7?|,lȔSҸWS](iTSa-&`dg >¯Ο&é9: X疇{ 8zgq}T̮!NCKґ".i_fZR:[g% *qD;I7(G=5ŎQ*t(^s*sG@0A[Q"')O{oD Uxar70듒6i+8% Gܠ%zpgzQ?AK oͨ-iѨS- u5NNX!BZ>Gԏ. "ݘZ䣓L]m_<XܓS(K_a?* :AWӪDiXӴ?XM Vz1# Xf|S|,Ɩ;XeăZ;;Mb#{ -T lIk&8R+t%rhezOz]KdzH?:leNk7dIJ 8n4eZi xN=Wh ȧir@I)~u?endstream endobj 103 0 obj 5237 endobj 104 0 obj <> endobj 105 0 obj <> endobj 106 0 obj <> /Contents 107 0 R >> endobj 107 0 obj <> stream x\[s~_Uy44gwkKR؄!e]_,#7tOC#tQYY ٽ&{}6+׳_g՟ne?cE)݃YY8d2 -3tt{4{:k 䲐Z6}Bc߫$_B0CBmy1FؙA{5N UUWQ. 4LYf'9sSVR02"іYe-$sUw>Lla+v_yrwi;R"([g4VoXߩY%rN=u[}i)'4ҡ\ ^l|㝜[c;G^Qn ?qS䞝*"*PJ/ʂ;pK-?^*wx[@Kov}Ln O b 7 6̟@OAڏ`Cc 0ُ2x{qPMlQhԳ5tֽf^e1YBdo  JLǫ~YXFb֒G?gy|hǐ@}dz(@<}=5-D]` nV{^&u}bV<t'g`_(`S M0QNU `7״\4~ߍ7t- i3?Q6Qk.v]Εoz=N{?0jrmkSV35e#h5S[WX~cj`km. |3!uBb9 lPB ⪥縷q^M/M^BT XhX4b2eUL-2fkb]ZSH{sAz* 1<ղX0MG & ic`y(Q(`/KD@yRY PKpR ֎&~95=iD$Cz5 BX$%gf#d!l`5I[@_ op, ]Q"4ȭ]0;`vJt2&;ߩ!ܾ߶@v+@<ٞׄQrK5`®51^!mOmsQ+`?:KlRz{,*Nف)L[GTm(W C)[u.- cX'6c!sKsT&52cy!rY%$^81+:VM "O:_Τfۼ|IEMrލN"J!D=)OA&9hqxdzD!%r:!sn_'\礨@[t=JWt"}˽FOUK6Փ{mks3PHXF,cQZ`a'|:o6Ԇ|v'^iJlJhFOWǜo`/Pw+P8ix,@*:)ׂǁ'OǗ]"IOCiMsUUHv(0QmǦj*HistuN;a_u%NѢzexB7i('AAB%x "%赍U*qWp-TvV| Ѧyᘉ?vNPl(Ci$$Ѡw1 )h`e2<.,Z/Ig=gUYTlBX0 U-g0;9Z_P9~E꘍n6yRQL*uE?Qs<`Q"$>lkXySmIx$}SP`^PyC꛽RE[ǥ6Ys1ؼjX7a?e} A$ʿh&ܭ)Yrcb\I]Sθkv[_yJthLk[p*_Z\}=*Lʗ^D[Ć*?f&C>sRJ(}ۧlimߊ%K[u#Vݏ{BtNjOU 1:H*Dʗ~^^UDе 8򖶸В)(J#*[m)"9dau ު\RuՎ6K,:L]+YkkWaK]|M5>^E@Bfk^ 5:IL[o!o:og/"F(bFZTl9!7~5-aJV” Vme["LXj tl=qi2%Fk>xEFנ?$c79ܼaAa^ /QGHx"4< g[}/endstream endobj 108 0 obj 3510 endobj 109 0 obj <> endobj 110 0 obj <> endobj 111 0 obj <> /Contents 112 0 R >> endobj 112 0 obj <> stream x]Ys~篘Jkk1QN*R.ǖY˥ZKE"HN1!%/ n z%)[)K|̠qu}bf\TG/>}uä=G/WM9UuxrP3k{:jZ֪5muלd:[+1|uIQK:Mӽo$o]]5իjQLkճt)njۏBQ&<;NZK4V+O7Ś ƭ櫓7mD.y+X?l-E:Z7u_+[m̊-7qۚ]7Mp6֕n SkZoq|w6ۿ/?wLXz`jhBG 5!RNrh4WƊtrS+MBp/(!сƠ B`F/<ҲVhe8vFPҮHKUN-BU6#du *`Vap>S3<2VAԙe$A#,޻`NKcq"2 ,YAs'HlP?˚AEh}fe+a=a(wJ6Yi`R?K7S7s $Sϯ0p UDqmo  tLP5dsOԛ䖡ݪ HAA@vKфq#FB)icHkۀB@0hḅx\Na%^j f&MQEn{EcmBY)]>K(Fa(1iI*;{Cfg]Ɉm5Dti(SMp1L^1ULVGt:"EIoxX3׸"|A1017P;G*ڻJ*riI(%|L2KpYB&"ߥMBέ G,H!-k9]68=] O VE;wg2*E 0>FכeGR9Zm08Kʕ2=yS+՛c7Y1{DOL 9"a?:FN7q6\NVurcmA 9фG%Ljpַ|Hw%tL9z'Vnv ]^SppMlüR{BP ?%NbH|Gѡ>On1"+WdqrQnfAn糉6x)+\C]-׭\]ee{ZZ!ZfL_@KVQd~TnEJաhپtXi*$AbTAQ򒓡@@*\s*K㋥2&=J2D&fB 7cAB/r3e8L9b"eHK@HD@`H l? Qq ~`+gso#3%;O}q>%Ia⍶%ED/(64cӲz<;25V3 [xv}?\qz_q N>;H8XXI2v#;o蝎rd,=3F;} 0.Ss\ u:"~X<J{6ٙgJJ IU"{?@7a6C91<((/ ;y<WYFR5(h#iFsa3im͒gS5mR=GxfXO%n%#@W0خe60ΖTjQ{[J]H>0U&=qֈ.ggE9 B}j<[<7UI:4Q!8:ƨm?{]5CIA'FG>Ң:F'oSC96L=4*u\ÍElc*W;cO ;o\evYD_aTE#(DIƲ?G`I)?oKBj<38PMrN- ; Q@p,ʂ;S轔i뿽gH ͐YT(}_?a]wOy^?c@6urN̫uq(i5Vsiy>)kzbĪdXͅAsBa9Hw| ki,_ḽkP[}+*i0Z`IL}(aFýg]C:qfM;gk:|# )p^|i2 7/+ASxI> > endobj 115 0 obj <> endobj 116 0 obj <> /Contents 117 0 R >> endobj 117 0 obj <> stream xrܸ]_1뉯t}勻ŏO_(UfqBᨅm,j+.NN^-R4vy2qޫtSk|uXUZXuq6s V.E ,thC,T,~ҕ4ˏ^fJoWkez+KΒRO۫_4&TʄڅPfqz v*@[vy5tX*;+ JǑm'+mR5e;lS5,-VMeU wM/JS=uX9V>u>v+Wy@+G,0ѭ0fnav/|KðInx fX h@=t,sjӰ26bL]l$~*W3g8_i6hyٝ֝J4vUuk/ {AEX-^cm+kUT!^w'5 haZ;`zW'o{: yZ{`\5m4BDyS1ŪmY# N{`UCPuߒ ml'k=]n,;cG>i_ŖycWXs ܠ_u0v}~ Y:Z$R ntx>4tͺkGg]gh^$‡> Ws X!yKtVмrQVg[nz!KzU]'V7$4Z/_!aNp'/'kcҴ5L |t<;K& 'c +}p AJ:SA"C8u ;)QCð9g6sl&@5D{9V&eP'bW O~󯭵 ;A&^fvs$h>_coVk H%=bJqJ2ĈGHONxȡ-3-WD~RNݪYӆБSa51BpR 0>UHu%oLWmM.:W-YfgL/\(afƆ[$#Y i5~ݟ ߈f![ @ tN蒆KduF|t'#BxIQ66df!  $.a):M3%I1t&gN@6@U,6_MH; JGQIkobX$*J4b}&[F XS/&s"k_.(Jz)cEj j@S-mYNmcbWԝZ>pH=ڲLΗeWHg8fSɈs9kJG&ڨ^%B^Ҧ"Mr>T\D?dCVxl,)'+ *nݢN/Sy1L& LPhjoyوXf䀘CLd9x>|xnG(;6Ll(uuz|!~=LiZ?E%<5E)_(ט&Qg&ؑrUQcs[fesںl9(}f Vb藖d')$ҳYf9tJO˜ؤ^wl1Z6p^팏LE7=Um 9M9klw9.1Q0>%yU#F*Ч9>%r,Guhaճ|oT/А >^ZLq FIL3&(ְD%gTMQ,#M!~WGDhjfPj?峍5m@<݃j4^&MaOY\vZԐ\aGt]ڡJ5+69 +\ڋ!K=~.̬XyO 9ɫKa+`1IəJox"(:xgfF" 6吙 UF; Ee7 rX,0b|Oo"k+ 6V>sA@נ )P%FZ31(q;:2,J9̈́x3)oт\tX .>z2R.$[QcHMWNiBYf]6ERi1SKLGü)i3 Yk%Zgќ8Fn0Ã{%YzJ3sWod"3ϻ]48!KTAiCwf :V ;UѢ)ݎ%_-E{R2`m4\ȩ,~t#a=tY{ 2cgCvb21E|ia$Ч3#b4CJ}O~/K[luݖc Pw,I:0$fz@|Q]g&L^$LhEZ@' Z2 w|Tv.T#П!ST"4qhJUCIԎil⣊[8(YUJ>H,ϖ~vGUֳ#Y5j[heZL=VCe d!MH%x:[73ߡ̑ݞϾe 8~/Wm|!7&|Ct3A*2Y–f(u@ wm!NQW B6+إ^$ ]+[xOńg{#Zw%Ų-ݚxE#O61];/ vendstream endobj 118 0 obj 4871 endobj 119 0 obj <> endobj 120 0 obj <> endobj 121 0 obj <> /Contents 122 0 R >> endobj 122 0 obj <> stream x\[q~_ȋvzy@dž z);f+{)6beCd D_.oϾ?쇳0oϗÿ\0H9 3\\1hD#Fg hpq{faAM+Cmֶo~Ƹi-h>78`v>UbY*eжmXhc"#mauj\YH{W鼭OOc^v,>]]L8Jspv4NFVrmK@ogz!qڎFD] '< <2Ng5/"k*"D剸BT=B+l#;ȓ$7 B = &#qO [Mk>GHg4{"31x&=SN4M$M"TR'{XF>ц׈3zǹY j=Eqvz U"x R!j@ Ge4.ތ-uWt% `WeaLL}?b%wȽG(WcdWbH&3A ]嬞UM;,n*:(#ě< h[H))ѲMR"Ĭw= J,d9 ~"{#`=wbb=b=v'$7iɟ1;" Lf[ck&"+B3R$DDo&;1PVu K[6­{q;$aNU:*FMj f*2{x>Zꘘ9Aƅ+G{6ڌO(ᠯHQ : .>)9p|=.) KLkvz'Ka,#kBșH ]RS}Tz^|eT=Rm0:>G r^%QI\ַ#7R:/Ճ/O ~;<5aG0xװXP2 Ҭ>}F,yfq1 fAJڂ cs,.<L#6L0ŲaI4]0ݱ4@quzrNJW5÷P8!G<0Ow>ˑ*/T{FZI(QvƬ(UyŤzӂ3opȉI*&HI? >-;'9 $KD>ۏ]&SH&'nzN{RyO+E=p`P`o얊AgB7N¼C6"CMTI@O#dA2eR*Tr~S 0$?{ Qg53*3`m*:oz[P:;+G W _d~,ӘQ8wjF⽻"O+e[|RdD{W>TjV[=^uFIK]}ϼM\|^ ߃.!G?c?hN({<ârorīh X )j2`D\&TݜXo󷠥T#O,c*_N`a؃(ǟeὔ6GA/Wx/Sua.bUt?cV,JCʞjb}UsjfTFSE)tucv1mE"b&VH/krm[n0:ZjspQrևZKJm-KΥϲJ`F 3jh7rdiq>_e8m RSc{׵CK籧\ⳡ.b32)lUptpFǧEכ9e$[m߳?O}qCc>h\ͣ87^W|.ЁpWLӀѾwN}%{MdH-NT8Ś9rcHFs)]_E櫸Mf}YtC~NwxEoZ?G#pC]6W lZ6{P8qg}c?D+2砀~QWarg, 9?#wW`SNFAҋ!d긅?8iDzu:[Co[mR+pz>=[7Ŋ&V$M)voHHLz79j| !Vؙv꿫O<9t l=$93>)T-7!Rg;ණG%-TPxҐvv=Jȷ'ڣ֩2x%Ɏ]pls|{4J|SJ~|`דIlcQyrx-}ζkoh#7sLmn 6nzϷGoAL`<Ϸ(D-:v=̖rf`]55ئ?_'Neh_063X?T\;#aPEp>"8+^Lok =7}KD N 3;Eridi*| c+# _'e +|+Z6 ٔWr2B5X+1`^E"KiiOE*KR@,Lpa*:_raF7^3+=3!砅sysSzҳ~IaJ0a~+=os^>B/^-/iml<1ZM5 OʎtVe~qF'6{$}$Z&NkIA깜QM WM5X Cؘ?SJ^p~aɒS XA(i}:I$E/t! ?aK9_b*O9|{wuJz@'7 gEh81:vS_;K9;aH:Uua!ӏSRvlA4^36|Qhcr1w: -;NH<f;19) *ʍֽ: :%R}& \76RY}Ul[{7QtQ0;YD%m@̎֋Ъ,ۈDͯ+ EW3=sUvIڍ]Ȏ9oзk9/яoCZi# %? wp2%-tk/gMz_G}k,}@p޾G0뵎+:'xհ<_]%Ws~oK'# /Y=|_:M{K;Η\OΗΗΗmˎmOIˎo<_|N˶|vY:|_cd Y: $> endobj 125 0 obj <> endobj 126 0 obj <> /Contents 127 0 R >> endobj 127 0 obj <> stream x\rܶSxi$H"MVO"쬵Xʖ5*y$pqUt8?Lw'O^4ۓ|vyJ׽f=UۙRY^^1~LY]ZUg;fBeBW*U{[VyYPن^.MV*mBnf~M*kFpƅI Ϣ4 PB*–m̴6U1QU|PELKZWh}_P?+zw>UnBxRT&S-c2S6v WOOZ׮2TltU}5mm,7vU4kEQv5],0l+{>Wtk:2BF(:ԿL[^].j1lz@ZA  M8H3AzЙ:Jy/[uj\wj6eXzax>#.IٻNP>B/lk՘D̑g&V#¡15qO*x#)m)Py=&R-ݺ7uØBi(mʺyyWR~^QyuE[HGLjZ}aUnW-fjE{J.6s(wC/&9  5Rd+-tI( o70@#襑u X"XTp͐kDuobB%1FxjRN)M)A>@!/=я%mQI[P"@ߤE'S$eDC^2,"SAi5ohlٻ[k(+k%:Ds(j]|YЙ˰A< , ٣o±֍ЂȺRE&lkUz&֕}MEf骕!rB@v%H0dnin)a Av;Uv9QPy+JD6?t^׏1Pݭ}{ Xbcx`MV;ފ; mJBfX@+ۀފ I$'/ /h:Hv#gϭ}h;)vgߟBߋz*U"OY=e,Zu. ;MKBg̬8|XYi%}+I[+'ݑ©䒪͸.(dgt1-uyYȚ~C0V4UQPmkj/N>Abs*fֻ>1%{ mWAĊo@ hDhu;uuC>4[궈,/:0h-$lPZ QpVy}t"nK ʴ;8R`9T[ȧgabURm-0{D7M`%8 Ҍ9I(2(,(F ;q$OhJ?<X85„!e|&;2QP#:WV⵲R>[0b²Tij|Ήi'tro1-ZsySjĐ!K]`,]hOM:ϒLODŽѢlk[* $_.]yYG&`4Lو9ˬ/[s<- @A:Q*^J>,N:_" c,È-m,$}!cP̄y1ӝeACyF ]1Q&+[ATv$RQ~!gRxrYEbN9 E-/m#_zi$N$Ƞ\\X]45 aȤ򖠐a@ID֞"fX|ۨo=Xew# i3IZN0+`7R3_JFjRa 9cUE(ԯBϾOmQ W9Ce3dGZgF'X#9'BkU銳  Nj Ոt 9Ų+A X~#Qΐ+1GØPUn%8'JhDFa腍?ӱEpp 7ɫ-aB7isSzl@ Ai+Cq ,= eUmuJ? x`G#IQ8Lp&g]պ8)a9~B)(2ީ$A 8Bdu l<`E`%4u؇%A!)C#`Q0+4((鍭~Cր78SDќ.[x<|y 7Ansm$[[-r NSC&=,8ήK)l@ Ut3fM$KvcǤk iz58 ;ڎZ@|ͩmaF5: ) UvEm`q%LF m$wsb8>NH+Ib vu4:] Ejz9-Fb3xiĶj f vX01, :5(ǫXRqjZwz6D~ 7rΧ= T\;<(]נ$g]&SsCV}No|Vx΅ѠnXJn{k꾓CvZ-HڻRծuCe[}7cO y0 jX'$Z`;H(xX| k`> endobj 130 0 obj <> endobj 131 0 obj <> /Contents 132 0 R >> endobj 132 0 obj <> stream x\{_BPD ,%h4uZF$WER;%;|~_,9%0cy^MBiW~zy?)W0O?]Ow]REi'hƖ5uUQiUӓ7窀ͮ涰l mFξǶslzQif?UY8_ ~/lwf6N߃Ft0MդY(gJfmbn gjUnJjv1_(WT6h)>[,>ݴkհܕO4Ѷ)m 4E':SMUܹ*[Յ}VWe e-GbReS;oR[)==!S}k5L6;$$ulpъ:#>g3 Ni Wħ آ. lqukm7n韡_6eY:wǩB-9 |e t{X,<4Ff־[]IQLOx;%a·)]VE0 k2 ؿ4 c$8o}}0BV]ZNêTPj=zN"Ȃ%8oJ9=='TZ s<\D<&VJp~ؚ7z^镥`uzN4;j= m#F#žbD/i<ۏ$umRjdbaBfן >hM ˪+?U36o8'/'i 25<}\uZ Ϣ+fxj=݌)>3Cփd>~3'\0{~%5M|% _bc:x0A%fa#'E ʕnG/[/:2Z?FEJ܉0F:@v-!ZM9KapSGڨ\ͻ=-ΑWkl'A{x4u9@''|3dUF={.%`CYTkt/ ʹ$"i(WfÙpO5bn)I.)> (QJ,o.Cd2JKH=}*IkwsNR ;1ʇnq*m-{.<;x\#"d\i}NϗM-/P󮬶YlZm5NׄWB-X4CEf! ||Y}5U3&;YD!rxEZ d;zL8%G{q.:u\  cr$CtVi6pVMC#/}c\ ݔPa _DuZtky 4[1C=#)Qw66`ŸO@ccL+FϡIG'5f"Q` spʑ'trch $ǷöKm/Υ$$ܤB2M 9$}2KFߏxFX S!yB&'H(JmT*-WrMaѤ1ô & Oӊ4G@딲ZՔ[GQ/yb6)9E1Bi5#Me\ qu4q=nP)Q]9AfftS*MɃ{drR3܂Z^;JG7(:+yR-*og4gvoKE[, l6'ZLXbQb}M7+I@×X!,E:Ki+C٫:fۤy9̠D5!TjGƈ7^8̄5`8OZ|";>1O7]UXeT:۔D-(ǃ$F&;2ePS6E ajWB@Z@RhR1L9 v:[nU c^}fkvl0b:g[$lEZujx]Y_Tl\*:ϛNLǢNvX!="*!$]+aieBme#Ljژ bCnb\X:wKpE[Ϙd-0k=b!}/*r+j&+]X-+:|?l< L22wƼ[}md;9l7ôΦxOad9 zG,<˸#;Go@(O=8nPفeUΤxYy | QXff[+]HU|EmM&sg&t,k)ݨhIʁ20d#$E2ҨiWDR}(|_=}5O]PEpW]9/lsv.`V^;8^]K5uim'Bv%kP6=._Jԩv~IO܃_sDr%nM&~ҽ!|'Y+A*x 5DP U)㸬*2\UGòH{ +"ںT`!Á4|ۻ6ma|SƖD4D4R;jd%QM? W~cD7l"{o̞e5pIًGo~dTL-3T3q$넕ul8&)] r2k ϚU S#Yc2$A'x=c[XFx0[ |ڱAx;ȴ9T7Tˣa&i(pUjd):O117TcX)NTq"@~müaⴶ̵HLJ/ -ױ74H`e|7$J" {jۢ~bf#gy6.O/҆02-,VZkE] ."+n踢)|U>*M> JA*ʃ$sbb`/O%{+|h-~\}*yB,z Gw#-Pg?diپp$+ę]c+ =^SO[sL(r36sedadD X1ɍ<)(6 .;nm!rbendstream endobj 133 0 obj 4066 endobj 134 0 obj <> endobj 135 0 obj <> endobj 136 0 obj <> /Contents 137 0 R >> endobj 137 0 obj <> stream x\n8hQl^$7n8hHQџ1Yۉ-ٺ86F_^f#8Q?DpYp *^T8|s[<ةOw^҄3>|fReU/NvmۺEcK[/ec{/w-ZfyWgtes;¾+jvPU8떧Xեs1gعdWaZێPͰ Uqw} S6M[y PTS˓b6a1VUcU˖sâ{u[]\ۖ^uZ`0C҆iqìmmv5<_`Ck?G~FaߏKb _Vk(l/ R۱/0ϿLu ^u)j!ѷ8u^`nb1>IUz0G01[%.`/Nv\<Ņ_`[}n0{AkoTڵ1wsM1jxlDB^jR~nu„}l]m g2n@仍; Abm0fPk~] T1!:`5ny@.V-MSY%y9HKyAI99<P5e[''LP-+졘v$4C:pIRp:q%l&"-ﭛ zzaWQo0I ":J9NT`,L\ z%5`6h `?2iFjWճ*jcO.=?@W4ϻ*bɦM˿S'49OT0AX~NRՕEx_{􆫢9*m>GԻqUrmxgIovߩqJ,xd K* *a8ZB鷈: *~2@0JRja;M0.w3Jav=a#}N3'ZMa85zεH+0+ Rt;iF+WތM <*m|E `f0c<'Hk {8MW>ϤI@,)"at $ ީ;a,㶅N )7LY9 qvY29)*6V5gw`D}GGIg|( 8P= )E|qkgy)mXF(ɘ=( =t:>2E}>jKD%E8+$wIfOmYW1;猆,%M>|}.Bla 9If,+lYl]Oz/gm=aEj=z w9@a$D4izj]MMIl'8 E?P QRʎdSFE+V?|\JH/rHr\! u!gқa:k ˫]%{&$;e sLUU'!o$z~'7jOf.LH[_|cLv^00"x g%E=zH`TlGCFxC-ZsIR!dbw yHץH;r5솽,,-O rN}H9ĩfrk5T~.YnPBw3:Йʀ:c%(gs佞D9y&jm%fp(Fٷ!iA,˾g;a%Ъ шŃ~ɏzεK+j|98)# ]UFx]& cZ.4 iTQd.1TNB)=BO,1xѓ9w(~XJݦQcP85~ v8 Ve;5YA顲$b߱$wR'[!>! CY%4U'3H~xJV!S[+ra!k 4AKshJtѭ cs~FӤy`]RBĠ,""Iq` 0CB+Bo1´^JewBoe;\[W/f߾uf^U]w`ż ~j)d.4FHLC< 7XcDV `_~QOtzQ"H=Ƭ tWN|CJW7n%,_^fu♁M *iݦJFJMOd)Hr)lJ s4NLщa5ҿu1']eEiv9ۜ+!B<YHs=nr{G{Z+\c {>lPCR˓._K,x~[8g4zs(8.$DZԾO{[H5(ڶljqpi[oBw endstream endobj 138 0 obj 4504 endobj 139 0 obj <> endobj 140 0 obj <> endobj 141 0 obj <> /Contents 142 0 R >> endobj 142 0 obj <> stream xi%E1P Q<Qlk5]]GC!*ANva!s1~oOYUY]ٝE"d7v#ʬ}RԕjwlqpU[lo=)݆Pb{qUhSpT'[wg]Yڦna?ʹZ~TuS|UλZ!6yh7}B逅Ue[iwOSnvVRUrtetײ4繏PF:g햺?Vbnb{ToLZo,ծn M6rвoz4BW4 5ޥF}5"q;vaiƃ5@cҍsY`7弅s][=p:nem.MdԾ#]GF 2t6csT UMc<1,ղH;+)B׈O QkQlJ'{^X7>ȑ6êfg ΄N'x1Gl%6>Dnږ$@D@3I$[ۿb L'$5L_NE)zd_i7}i~%i[jL2}%vZuzۥqBq=FLsyANr^u kH&FW;=y40e碪ا;M SYb.FJ hTuv26VLd2"w ƔkNJWխf ˆ#0Sux~=t˯en[+V>Uޢ'f+IK'HrеNcU܌'9@tN$ɖ XZIEcr! HfpI$_Իwg\"歮F5+1of^.7*of4cm T6m9\ٟa꺅Saƺn tSLW%mwewƕ؁!e3l2ZwMU @8hll7q ! ]kc}qjZڞOuLےPT1k~6zغ\h҆o.uMq{-klkgup b Hw.䢶 RMbN.kQ;#AK&& & }d}$xl4-q}kG#BdI YBuzMЃd˂U1FA/2IS*tlj+ȿH Q"jtk\B:" }p͡kɔT41tL?O:É0Ԯ&Dg3$jm$4J`nhn!Ӑgx!HJdְY)]5d ˸T{lStt#\fj6;e3^*Sۨ-(RJ/%A58Oق}0Y8v_dsJ֊0rĹtX}@.AvNbq&gFIG<%%34D9O껸Ֆu||mmoQ6i ߚu*)APD+ -8)0ZFԊͳ"z̺$t!ٙdSFHI=LM os+L.i5/C9fz/Y@4J&67piFhldp t;x>f[)cM bTWٔdA XxXLPW BQghԙm3Vy_iݢ$|Z셕{0:1In7-+X3@ISE/9~"nBbb'VBWfW1Z D 9RĮixHt\6K/S~d)u1zA%ዥh !8MR!qԨbˬ ы0aʋPnq nσʹ!FLd6hq3n?+S9x$w1j!Sk %p6(LX)rIpXr"Ucr#m%gŅ3 2侘ބp{ө&CR!ԑőY,1H1>KẎeV +>|SU7p(IB%'㎝ǰK&7k"5kTCBk<8xbÄ2OCWժ5m7,+d7Kup(d[euCy0~\xAa~O>j]|?Uֿ 'D3D9 Dޟ}^Ȉ3w]umŸvOqQ>kTDz&|f񦎣%R+woW[Zj$OG,u t7]t7(ւkV} ⠤qbl%1d< )0auܘ~8ʯ3Px h7xe"I/ }~i*} WJNM絘 $4mo==$O8.H]wTVAYZ@u'MD[0>%*V+נۇs^ Vx6WkɁ[c_ P,խ7}o h( [De8+w?@Ռa.a,٪lְnLnG!{J}V<(gxeVکގ`^2]nVNwEJū~ϊ}x]pnX@IZJx L#`zTvŠ#VgRY&@PH='i0}3**g1SAwX0ePR㌩T%$$.EwZQ @2BTOWRq~x증5N)'3k->Jɣ36%3j]e|}P-D rV@'@i)Tǎ&mЌj| )]6mEou`+'DGJv_akVSWtiKqKny55f.D$ՙpԙx+fD&:|$>Nd4I1!&V.y]5H97"yFm QGBU((>./U`ދ-MH2 25aE)w$L=+$iS?-5 pD6"Ë(~bɊdM ~Ԯt`3r jvEÍD_ƠsxYY0ESI‰ST ͔wr2=͢WhsU:Q"Lt ۥ5Ll$}>b8!2ͫU8ր"G3oR.[#S(r *␉x*LSk v(~hG?LwZƥ1o:W !$}Ɍ($ȝ20łzv,'eSiUCs,tJ=wa7xEK19od{x[Daendstream endobj 143 0 obj 4562 endobj 144 0 obj <> endobj 145 0 obj <> endobj 146 0 obj <> endobj 147 0 obj <> endobj 158 0 obj <>stream xYytTU}KڳPTiR KM` "G"FPn F6[xlԄQBa2cDP/RV)NB 2spYg0xЄP!|RRI熃=t~P P ;)\%$2&ɸ;g[{k1&ck-^245^x+n#DnY}NX=w0InFɇ/2R˗# dw I6qpxNיxg#7|!(nض)H$,ӂ(^!::.U3֥Ԑ~F s tiRz2~+aY? ~"687dPg'˯Y>K,|?RȔ%_8eYj3ʞ|/hˌی #F1[<mƛ%yx Pn*Iv9\;Cp!gPN:!rawѱ}ҶkbO; ߟN@rF)_6.ņ/3ʡQdḥdr;EV,+!z;s֒IA xzYQ巈 e xF

h~XԛeD$2C3Ύ5ιt`ZIbIl/yQTi$BRA^T*O)O*sb?ߤ }j 1@n5#,:Źu"E>b;bWz"JPh"Ul6R GǸt2̃,Z,-Ó\Wkkk)`%. [M.7$&$mGU)%:  PP^0lj~B .=Z}wMF|޺ɟ~էK[Uh( cYjYgٌf@FiƴĚ=of>> Nuv*vP!D$I+ZZΫb6V" mБdEfUX2#uY&?&B@$"/9; ֑O5p$B8%pQ%?R}ZG_'~G 匲$^e\˯dZ,Z@ժhZlǠ".F6 ЉFhbG;\֤B Z[PckޣG9&~)N B}w'4;3#h,g& GkM$=Y6x~dTrk8 ːn3n DGK dj?w#6?(7c$^$qr3Fj}Ƞ]\筓8–-W3utW>6 /| mGDt>"[ n }}sgdc:^_b/ϱaROa22q!0زC:Jq1-[O7]mݻvЇ7nK WSW"ɱ?%u'TFG Km#Yo!N+> !(qng,%TMG\hlו '5Wփ0HLdP>hbz?ԃ͑_yHĸ ͒l0`X$&w}%EU7OVZ٣!Q0L"To;}He(_~#V2)F+lcn]8wBQ14ֲwHD-c-TD|'Ӓg)̰xfd J>Gzk6Yp#^`hHrIϖ|GF9EҾ8OqϼO>]D|'c IS j=Ǭ΁rޤ":SYb NT/h+ݫۤcm "U(fez5XKÄ,Cl*Is`|Gi'Yo-;P]GXͅ l]R[cY2PB<|JRȤVStjD7[q6$`MԔvv47qj=P38%t"]Sӎ%a=z3gv5 Nilݻ|}2BE#e08tTf@aN*F̵[m$Sh:L gWcWrFL86H6P0LJng66j>22Ll#䑜PA PX/lX\]C|0"Pո Ck[#hͅ$0sL8^W^'E<>P2nY֘4``M6[z…~# /U^W20T099EC 0b6}^GZUI'B%J.DOM5ѐy\l.t*2o 1H %eL2D%ĜDB\"_"rҖ-xz#Y$QAR;{ٖgU];R}pI$LP5>“: zzՖ 'ByȵuZO*k)U35ё fM>X@@R_vtJMj ;;zJ)LJDr[v˗e,U(;Gp//탪lIͩ%9EUMǝxɧ^ =#{z>R xpi}9zDaS­o뜰%p#/~/ZwgVtbAɭc #'zJp֐wz WW3P ">` YVf}w`XgEsimGf!=i"PÂئ^W#Qr^mX$5~GͱoOG$ NAI$)lr:.2$2^OD݉W( `KD;XH+ Z=wk)ĦIim|jWh_M/K떞GQu2,2,d:Ɏs5 TϊSa3=_rD"\5pzS7Q@C4'ub\MQI@RpT{.fqhOT,2E7xP7nVybsqnqqyѨ7x̛tCL4NQ*ڳIw)0`n;Y-DzW?F-῔_/_4I{~cf'7g/;sޔ h NLS"O]}PYXp 0 a6x(0ʠ 0wlcsr 6D D|+U FWUSu"un F)y2\,$I{' )ǽ Z9Ip%U/cYe~z>Q_{ƞJ81> pkV =_mYؿo۷ ῂ)+T.a#Rƞ˰i?%_ endstream endobj 159 0 obj <>stream x]=n@SpfkK4N"QxY, /")Rt~9iuNyǩ kϏ5SaJot뗪9k![n>0/Ci}S^rU.ÿKm'.Vd8e֎=;;@q5iݳBz`g{=[=m=6 .4R*bE*VtP^%XѼcXѼ3XѼSXѼk+7TbE*Ŋ Qh^!ŊP^d(V2`O&[Glu7t˞KUǺ*ڪM%m2/zT?R endstream endobj 148 0 obj <> /Contents 149 0 R >> endobj 149 0 obj <> stream x\Yo~ؗ;wa |ȶ;A;q@KZ!#@mgjvj%`6w3j3S-YS{Rk˿_>هiullO9gẏ6YҡazB׺_C5ʺK;"V?7Z>8Wµ1FoX9O*c9JZJԝ`ݓƶggeNeMsf}l4}`IXrXj{Ό%?g\n˳E̹f6!C}(cu}#dPb*ntlMv{ifc ekuK0>* +Syyk˫ΕN c_PֽGyNk7Rl67H&:Xd-_cM?*V? (j`X4U~{aai|t} ,%'ePguUsH[ɥ:# kR.ىӽf7əU<jw@5r2'mץ|2m;ӢǭRեɢSBRA$MYީ'$E-abNV񄍾K#i+fƀOEs=T#GY|FҴ(-yUKIWg7fdDꔙ dU[5M#=rlD2iN, ~DYrfd<{ooA;lt冊W[c-XLo-3Y|(Z3dF{gdp{2h_7ZA|Sqb]O xWDX6:w%B@ T;h!:g?h_T)i0zOs6Z!S&痢 ~L< 9m@3i5hfA qfi]mFL/`msLL#\{%Hj^cmiҥdۘz+@D HXlj{k H1MF2a^haq!$ BR~2X #Vz2k1tND8ƈpJ+&5-y8ErҐ{4ω٘o|}8ET, 0#횏<.v2& F/SȱpjƓyEG UHP*O0v0Z`Ct7_5ȈQw9d͑YGHd]<+]#%ATu+^l#îWvd>/ =X0b OH6&<4A) LwU-Rd /ZO=To_2I8զ3!Zxd[) r-ǗhB4-7bld()ڨEM t!MΩS%Rdx= Qژ( 81Y0%)7x,,F~RpCU 4հkKxCi_P\Cq!}A1Kq[N, @/-4YZRK1+^DPMBYpOQ#̲"1 KZ xM`?hܡ L=?rEwO3K$b+:EdbN=2/s\D)Z1{?f D PM1 8d>HT1Oo,|9'Xz,D [P N6%E!\[u㻝Wra3h0K-jD Wrʁj8r,1^й!$(\%Fp'U'G>u%Ɗ *Y_bMu% ZҠ3Y~ G^;XA0oov3h@`cѰ+u Fځ(ʢ9-G5l1Q>J fyad@;_UPQ:5^ ڲ:Y%+>+彯pb r]i04nb W'`}ڎ}6ykKn`ZL ۼ\k7`4"vc&w\1ZdZ=]}XJ斬N==qh[wE[ hb6 ^p W¾l?8p$f_>G~9; rɒsKE`~C,Bk&)[4MaKf %:)?-G@uhH#Gq%5:V^zueHiu+E6ٴ(ٕTI`u W!^JrGBM(ؔ,ICXB$' ^O@`_+uRēg!"Лh[4W)ڀDr"zguϰ&X%=0hgCò{ ^ 啀ODciS;_<%+1;~&-_fd$IW|QnA#ۙUL

cV IPp',EF^^JwQ 4sQ%%zQ~=\FkC;tC;tC;tʗm#trktwho-$PT*@%fDȵJjou]He!V1̔LUMw+7Qg'k|R츶п/,v#}%䛣rA3t@a-ѣ}P'eWlkB~$3!tWRN~fAAkRJdCe:N!^x 1ծRc;TtEچ9͏W`7v `f0s3ɑt |Z(&~dWƔms\jw!~Dt&_z躎~Cm'4f/0 ] QI |f4-ͯ J; >ΰOOpWznMAh'Fgt[endstream endobj 150 0 obj 3992 endobj 151 0 obj <> endobj 152 0 obj <> endobj 153 0 obj <> /Contents 154 0 R >> endobj 154 0 obj <> stream x}N1 <ˤXc'qHPPB{܁r:~ ^%˲hfhN3 L0Z#Z)B5ia)Bᄒͭ=w>1)Dئe ,1K[.bY}l:tiۂmtE4z:Ű.v#kfu &arUl څuUg访0>*rT*~S'k*6;4zsU7glendstream endobj 155 0 obj 260 endobj 156 0 obj <> endobj 157 0 obj <> endobj 4 0 obj <> endobj 10 0 obj <> endobj 11 0 obj <> endobj 160 0 obj <>endobj 161 0 obj << /Type /Pages /Kids [ 165 0 R 1 0 R 7 0 R 14 0 R 19 0 R 24 0 R 29 0 R 38 0 R 45 0 R 52 0 R 57 0 R 64 0 R 73 0 R 80 0 R 89 0 R 96 0 R 101 0 R 106 0 R 111 0 R 116 0 R 121 0 R 126 0 R 131 0 R 136 0 R 141 0 R 148 0 R 153 0 R ] /Count 27 >> endobj 162 0 obj <>stream PDFCreator 2.3.2.6 2018-03-12T02:10:55+01:00 2018-03-12T02:10:55+01:00 PDFCreator 2.3.2.6 FluidProfile_0004Administrateur endstream endobj xref 0 163 0000000000 65535 f 0000008409 00000 n 0000008569 00000 n 0000010402 00000 n 0000308153 00000 n 0000010422 00000 n 0000010453 00000 n 0000010506 00000 n 0000010668 00000 n 0000016381 00000 n 0000308214 00000 n 0000308291 00000 n 0000016401 00000 n 0000016433 00000 n 0000016520 00000 n 0000016684 00000 n 0000023646 00000 n 0000023667 00000 n 0000023699 00000 n 0000023774 00000 n 0000023938 00000 n 0000028822 00000 n 0000028843 00000 n 0000028875 00000 n 0000028950 00000 n 0000029114 00000 n 0000035339 00000 n 0000035360 00000 n 0000035392 00000 n 0000035467 00000 n 0000035655 00000 n 0000039438 00000 n 0000039459 00000 n 0000045024 00000 n 0000052966 00000 n 0000062451 00000 n 0000062483 00000 n 0000062537 00000 n 0000062601 00000 n 0000062789 00000 n 0000066358 00000 n 0000066379 00000 n 0000083889 00000 n 0000083921 00000 n 0000083953 00000 n 0000084006 00000 n 0000084194 00000 n 0000086808 00000 n 0000086829 00000 n 0000115511 00000 n 0000115543 00000 n 0000115575 00000 n 0000115618 00000 n 0000115782 00000 n 0000119043 00000 n 0000119064 00000 n 0000119096 00000 n 0000119160 00000 n 0000119348 00000 n 0000120715 00000 n 0000120736 00000 n 0000143740 00000 n 0000143772 00000 n 0000143804 00000 n 0000143847 00000 n 0000144035 00000 n 0000147155 00000 n 0000147176 00000 n 0000152392 00000 n 0000161953 00000 n 0000171879 00000 n 0000171911 00000 n 0000171965 00000 n 0000172040 00000 n 0000172228 00000 n 0000173364 00000 n 0000173385 00000 n 0000189862 00000 n 0000189894 00000 n 0000189926 00000 n 0000189979 00000 n 0000190167 00000 n 0000191292 00000 n 0000191313 00000 n 0000208043 00000 n 0000218063 00000 n 0000228616 00000 n 0000228648 00000 n 0000228703 00000 n 0000228735 00000 n 0000228923 00000 n 0000234488 00000 n 0000234509 00000 n 0000245370 00000 n 0000245402 00000 n 0000245435 00000 n 0000245500 00000 n 0000245665 00000 n 0000252980 00000 n 0000253001 00000 n 0000253033 00000 n 0000253121 00000 n 0000253289 00000 n 0000258600 00000 n 0000258622 00000 n 0000258655 00000 n 0000258731 00000 n 0000258899 00000 n 0000262483 00000 n 0000262505 00000 n 0000262538 00000 n 0000262593 00000 n 0000262761 00000 n 0000267594 00000 n 0000267616 00000 n 0000267649 00000 n 0000267714 00000 n 0000267882 00000 n 0000272827 00000 n 0000272849 00000 n 0000272882 00000 n 0000272958 00000 n 0000273126 00000 n 0000278390 00000 n 0000278412 00000 n 0000278445 00000 n 0000278521 00000 n 0000278689 00000 n 0000283254 00000 n 0000283276 00000 n 0000283309 00000 n 0000283385 00000 n 0000283553 00000 n 0000287693 00000 n 0000287715 00000 n 0000287748 00000 n 0000287813 00000 n 0000287981 00000 n 0000292559 00000 n 0000292581 00000 n 0000292614 00000 n 0000292690 00000 n 0000292858 00000 n 0000297494 00000 n 0000297516 00000 n 0000297807 00000 n 0000298028 00000 n 0000298061 00000 n 0000303220 00000 n 0000303388 00000 n 0000307454 00000 n 0000307476 00000 n 0000307509 00000 n 0000307564 00000 n 0000307732 00000 n 0000308066 00000 n 0000308087 00000 n 0000308120 00000 n 0000298139 00000 n 0000302791 00000 n 0000308364 00000 n 0000308889 00000 n 0000309144 00000 n trailer <> startxref 148 %%EOF fluidsynth-2.2.5/doc/FluidSostenuto-005.pdf000066400000000000000000000517001417326347500204610ustar00rootroot00000000000000%PDF-1.4 %쏢 21 0 obj <> endobj xref 21 15 0000000015 00000 n 0000000612 00000 n 0000000728 00000 n 0000000891 00000 n 0000003903 00000 n 0000003924 00000 n 0000003966 00000 n 0000004047 00000 n 0000004117 00000 n 0000004179 00000 n 0000004246 00000 n 0000004314 00000 n 0000004345 00000 n 0000004419 00000 n 0000004507 00000 n trailer <<0FE90768DCE541E07A85FF8AC578C1C8>]/Prev 20969>> startxref 0 %%EOF 22 0 obj <> endobj 23 0 obj <> /Contents 24 0 R >> endobj 24 0 obj <> stream x\r}W[8U&e_RvlǛډMojk7%eܔ,ߔH3!AQ )d~pƥqЧу׊3!+vɗ]Wy'_+!g!͛vDe,2cɗGj2 )ɕ5.h5k3j$sGa PhI=,(iiYĮU3= 4 LXmAfT;)1ˑ4I' YMNFژI̤lk&+Ja1t&r üQX+nEm4{/b qiGo1q$_Jxt-5LVNruƒYo} K?&3zA%^!:'5'8%b] `OQrܧ<ԅ|CC.)st _b66N] Z¡!аi_{~O3tߔ}(jy_j~u9K\WB ؛j|JI`,oSj|]}Icؼ -MK>֌h4@"ccshEJĸ2{+3"ѴPQf1]ÛRnf\ \7-Ds 9YMK_r1~@r4dx.oPTOwz2,B䝰KK;ެZG"FZ`'x|PtEm"(r%.U!Lؔ\q nRw\h]<cHX{X9Ӧeڴ }ќB9Ŭ}_s8u cL+ x#[sSg(tO~E Tw nY/8S`T{{!ot4p&tK5nĥй!&R6wiJ`)RFxG0W*[m MK3ớ%Bgk᷵&!IAV!11(}-S5Ua|5Xim=AGYE-=j?NwR?:ca@׵ns )Av -{Fb.rٍw\\߾{pf8dh c[˥+h8v.x"^˱1>&s,p&V8C;v..p(Yzj_D f,,Vhҟ gE>RBߐOϱP"ygԝ:){I){\A \*:ثdw?[w2!O}ٽN:tn g%YWnV^.cU)o-4!c-frl.yc5{J%!xP;޳%ϰ4ww\ٻkkS@QS795rÉw8{$ tī6: N.Fx4{-@ Λ0ϼ[yMcmM}h֚D({¼ɩ\7&ښVXP{|ڏ:6{Ɉc:e 92 ~A~ E#4Uk~HLJ2Db;P_%wdǜ\殒,a.bK>wQȾC*s@Bgk\7{"{V|9+ ? qzi7AoxY}׹S\k>> =endstream endobj 25 0 obj 2940 endobj 26 0 obj <>endobj 27 0 obj <> endobj 28 0 obj <> endobj 29 0 obj <> endobj 30 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 35 0 obj <> stream  { gaˎxve(= M endstream endobj 1 0 obj <> /Contents 2 0 R >> endobj 2 0 obj <> stream x][~_!%R 9CL6R lч(diƖ6{q_ֿWr9~sV۸<;Wҿ-LEn?V|qsEO/o/|e !\\ȳiʼG,tΔ^\yJdBߚR!\}Ufjbj]fUUbUay~VY^FBj]gMrTM)߅htYE \ li$/J^B*J,Ud\\.ruoMLoSm*uMbeN0f`3C3\B6|ro]:ϔe)BQe< Pz m;R^E˷nXJ aWٞ_*]HX&k⟗NBb<)*ż.^?hih,7G3/ѧRU"Y%T&y e 0Bw1ʪa;_m >Īn#u}Pɍgs˒C׾*xa5Yث(~'/G4OB7;57=Z]xJfnR7Ɯkm?(;D=s0-B @iئwrʊ2 ':Ԉc9@ -pl+ـyYv ZeA j&w9Ҩ+ 82kDPְ?[ŔmQftR;q&Efi4YԪtgK7ucd2@  [("aG_?f O FhzyDBMd²}ΛV7G.iOS hCჇ Q N&"MM&źG\kBm6}mCV)u&hxJۅF+չ=C{jwt;j:x ;E|:٩F"]e%ʬ1Bvś3x,=3LJLT yJZ23'V>ϷZ{ {TۏMw" `G~U4)?Q}6|:z엸.q͏ \Ŋ7oʹ2hϳ/1 ?S_RM|Ta?g#3ŠKvdqrL8IH򎐸iqB.)` a|Ngr T(cgA"nrFw1.=e{d<&{8txI:PҭNag;tdɾjjP#E%&S|# B[v f~ob$%28@=*@`kF <1DQxz9BSDVܒlMZzE&=`<-x;S9 Gf⋮GRބLzendstream endobj 3 0 obj 4556 endobj 5 0 obj <> endobj 6 0 obj <> endobj 7 0 obj <> /Contents 8 0 R >> endobj 8 0 obj <> stream x][sܶ~ׯ>eI׼t4IXM't2kie)%Œ|e{H^biA\qEd"U٫?ԋw''M"ԟ>{TvmY$Mڶ-~lQVIU,JjqU\>W"OEתH-պH.Ji,[$6YʖuL嗪g[;&?UmyYd2*Ums06:s̓m6_,דSMRg˻:MM[iCõzW)O* j=Օ,7|n nݾ[eiRֵෟۼcMӿC.~q5noxcFvD?ث٘3bi,/'5E0Tnnlk{p#-%tB6UMm62ڮeЃ^-{ҜI[]e3ʲ\^ٻu+<;s?NeQQ~D#P5zjʦd'/:XX MUͰ!$r[oq6CM1A8S'M1̬>ghD?h+X¦apa3$sF)$eF0IRW795WmxMNF9δ[:"q:۵lḟ˂U73l~bO&$*|@"Xg!:*e;GD=w{#8f f#iLDbU!sϿړ% (àpAABz]ݷ6L7M\ 9[ EwՐX=IתNDфLh }@uw_9}eNW*Ӿ25iY)HnFP9l};L+-j*K:FX;o9["}-J&JJr3Kj]P&;yY =tyzmg94:n7t_"]/OO?)|QUT"͢^ޞ\ [l% g#u/Π>c< OLDTTGنچ=.4S]`y~lIx^mkݶ (` g~7NX#zzu7ۘԲQcZ YJX#$s1=E%I\wKHH ёQV\6,;)w5FN֞0R߲FJEM("yAb[#Éڤݙ ɐ%hEA!Ի′ל*$ mJ>u?Hz_Bi$")s{zM؜5N.<|l4Nc$99S;38:p$@AБňi:0y4o/E;`X9ױ1߄ILa۹N ȫj4cobX\s zilt 1\5Hk"A}=SW`>S= )FabdM <`xyC#A h19+\!v݁ߵ;\H/pq!_!tK_O]qsu45$҄݁9;OY #G1 4AJAfKÝnXqFÒZv1UG}9xLLL׍~jO,g3ۑ-۬BbüE;ZZKKP0qY:ES _t' 5ktHѓm gsL<2"i[1eq4.;6ǾC0.Ӕ8,I\od(@B6w 5ߠd rCILP-Gon.X( ϭq,+yF􁌗hnmCO JU"kq7eRx7ICԨ Ӹw:d5/t48#.+ *d3s'?D"09NSv:[51?w}+ Jo$GR6mIs'od+L\d7Ds|m0j8id.>FRZy)1 ,{2Gm~Sl!iNqߺȥ:Q[v4 RVngr HPqC ,c5> 6CyGv 0sxQA-U94>nOQx29J$8j23)SXM@VwR EP]|6dWx)qXű)P~̮ݵ]9@ ;k߰/z kuC{dX %hJDzh5\L,/:C%Եzl4qž&xl!@5ܘBT|c\^e¬ o&`XWif$ļJV$,͇=? b,YSvyf,=T%O: IŁSr"K`4rG%n1qZtt } .<6e${>͎iNxC6qW25LGN]qlVP*Eq4t$_"E{l]&m:5Sa4 EW=9C H"rD\Mje:ARp'`Ss!j9b-6gǙ'Du]O5u !&s꯮:0tP6B$zxߗVΐttfZ6*n>YB`Eɨʅ@[1nn y:}h 3 [c(3Yl ixE%idSԆ?`} Pȼq4],4\&r)c)20<#e grݟ0$ф[Jlb]ci!D}.ˆn*Pty$:2-&a䱘v>k&1dž6aHsfXg1%N@3CCGyj(Cmendstream endobj 9 0 obj 5047 endobj 10 0 obj <> endobj 11 0 obj <> endobj 12 0 obj <> /Contents 13 0 R >> endobj 13 0 obj <> stream x\Yo~_@2xM6>`ͱ6@`44(MVk]9X/,Ū碮,7^Oz󤭚F3?-jU]M9ꞎ(*Za*mɻKQ[5TN5˙~ oOvr'|Pjr0R;%H,,8(YXQVӺېiihUj$!Mi&RJZQ̬sdʳNfmB;!}(Z| ~K!+L> kd|m+H9嬮k?{mZn a/n@?%Ρ#4f*+0lgmY`Z0̏Ψ;=5qgN >nխuwNo`GyN'dK<ߪIZ$rA'AﷹteNO8>۸|'4#3(Uك5(CLעm1ho~״*Ru"&|(gޅ՚HpK1j  +Ǟm~QiIc2@̠F*;yđWp| B5ݦA(PNJhW.֛6IT~DgT;Tx ,U)mcZ=ϋ~=4+F஼dB:P e%H{drm۷=Yœr $C*J7 \2IDWCV2̌7i JD䆍;X{4{RIyXfc9UEvό**(*VY@zkN%^+Ј ;?S;]+j=h Z!Iqngx ³ޑ}3΀!d^#1tDGj../4>HbkcHe6wYy{4%$Ю:u8ȴS}^(-\؁NtG!5,lS l,[d,Ҭ+Wy`<'4ˡ CDI"QD]н`z#y 'u.O'އ#L%tZ< c'p3s\F+Gc>ӝѸ򷐜ZhЖҌUtoGA1d? 8̳8 L\ C!/dgJI Y)B2&ʢG߭>cdɩ;4F'xgWÏ~vDNSx^rMmPtۗàO1Nk\j|p~ۡ]mlcYbr6V'3(72( { r gm$ėvܚ,J9,{kPh2ZiN[ArBЎ)I瑑`8{/)r ^0 "qMH%>I єhZشւi]rܾ_ߙB}!١ܿL$߉~.nL }`]A&h Ba&6z_]l{4cO DJ \,IL~ CK~!4WjK)@w9>VRUYߏ{[sa+5lso/&91~:PKqh?+ҊԙRzaU` )*J3 7_z[hm 44BuBŕ=M2oer$9H> τ8K*tYqwk;}4 XPx ֞bjM2L bbt7)pVi< biO *hJ $\^ s4`ŎFČ>fi]+$MP\ͱ& V}8=߇Iג./vy /K#CgdpC~p MzrG_N@po>+ +he ώoy|cv<4U}z)ơȊ^C % /YԕRi { i[sZ^rxdh>2 B^HKM?{0>S\p(HlS4%?2D`Lm1T =OhmT;v2^`]Pz?qߊendstream endobj 14 0 obj 3266 endobj 15 0 obj <> endobj 16 0 obj <> endobj 17 0 obj <> endobj 4 0 obj <> endobj 18 0 obj <>endobj 19 0 obj << /Type /Pages /Kids [ 23 0 R 1 0 R 7 0 R 12 0 R ] /Count 4 >> endobj 20 0 obj <>stream PDFCreator 2.3.2.6 2019-09-14T15:17:54+02:00 2019-09-14T15:17:54+02:00 PDFCreator 2.3.2.6 FluidSostenuto-005Administrateur endstream endobj xref 0 21 0000000000 65535 f 0000004664 00000 n 0000004823 00000 n 0000009449 00000 n 0000018629 00000 n 0000009469 00000 n 0000009499 00000 n 0000009560 00000 n 0000009721 00000 n 0000014838 00000 n 0000014858 00000 n 0000014889 00000 n 0000014941 00000 n 0000015104 00000 n 0000018442 00000 n 0000018463 00000 n 0000018536 00000 n 0000018567 00000 n 0000018705 00000 n 0000019234 00000 n 0000019314 00000 n trailer <> startxref 147 %%EOF fluidsynth-2.2.5/doc/FluidSynth Thread safety paper for LAC 2011.odt000066400000000000000000002173061417326347500245330ustar00rootroot00000000000000PK)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-2.2.5/doc/README000066400000000000000000000007021417326347500153510ustar00rootroot00000000000000To build FluidSynth API reference documentation, make sure you have Doxygen installed. If you are using the cmake build system, change to the build directory and execute the following command in this doc/ directory: $ make doxygen The latest generated API HTML docs can also be found at: https://www.fluidsynth.org/api/ Even more documentation references are provided on our wiki page: https://github.com/FluidSynth/fluidsynth/wiki/Documentation fluidsynth-2.2.5/doc/android/000077500000000000000000000000001417326347500161125ustar00rootroot00000000000000fluidsynth-2.2.5/doc/android/.gitignore000066400000000000000000000000121417326347500200730ustar00rootroot00000000000000external fluidsynth-2.2.5/doc/android/Makefile.android000066400000000000000000000112461417326347500211750ustar00rootroot00000000000000# # The public targets in this Makefile are: build, clean, wipe # # What `build` target does: # # - build cerbero to build glib, libogg, libvorbis, libflac, and libsndfile. # - build glib-2.0.so and many other dependency shared libraries # - build Oboe shared library # - build libfluidsynth.so # - build libfluidsynth-assetloader.so # # Android app developers are supposed to copy all those shared # libraries into their apks (per ABI). # PWD=$(shell pwd) CERBERO=$(PWD)/external/cerbero OBOE=$(PWD)/external/oboe CMAKE=cmake JOBS=$(shell nproc) ANDROID_NDK = $(PWD)/external/cerbero/build/android-ndk-21 ABIS_SIMPLE = x86 x86-64 armv7 arm64 DIST_PATH=$(CERBERO)/build/dist OBOE_BUILD_PATH=$(OBOE)/build all: build .PHONY: prepare prepare: checkout-oboe checkout-cerbero for abi in $(ABIS_SIMPLE) ; do \ cd $(CERBERO) && ./cerbero-uninstalled -c config/cross-android-$$abi.cbc bootstrap --jobs $(JOBS) && cd $(PWD) ; \ done .PHONY: checkout-oboe checkout-oboe: $(OBOE) cd $(OBOE) && git checkout 1.5.0 $(OBOE): git clone https://github.com/Google/oboe.git $(OBOE) .PHONY: checkout-cerbero checkout-cerbero: $(CERBERO) cd $(CERBERO) && git checkout e9f18b3b340de1648d885a0542d2f06c3fcfffe8 $(CERBERO): git clone https://github.com/falrm/cerbero.git $(CERBERO) .PHONY: build build: build-oboe dist-oboe build-deps-cerbero dist-deps-cerbero build-fluidsynth dist-fluidsynth build-fluidsynth-assetloader dist-fluidsynth-assetloader .PHONY: build-deps-cerbero build-deps-cerbero: for abi in $(ABIS_SIMPLE) ; do \ cd $(CERBERO) && ./cerbero-uninstalled -c config/cross-android-$$abi.cbc build --jobs $(JOBS) glib libsndfile && cd $(PWD) ; \ done define run_make_abi_target make -f Makefile.android BUILD_ABI=$(1) A_ABI=$(2) $(3) endef define run_make_abi_target-unsafe if make -f Makefile.android BUILD_ABI=$(1) A_ABI=$(2) $(3) ; then \ echo "ignore failure for $(1)..." ; \ fi endef define run_make_for_all_abi $(call run_make_abi_target,x86,x86,$(1) ) $(call run_make_abi_target,x86_64,x86_64,$(1) ) $(call run_make_abi_target,armv7,armeabi-v7a,$(1) ) $(call run_make_abi_target-unsafe,arm64,arm64-v8a,$(1) ) endef .PHONY: dist-deps-cerbero dist-deps-cerbero: $(call run_make_for_all_abi, dist-deps-cerbero-one) .PHONY: dist-fluidsynth dist-fluidsynth: $(call run_make_for_all_abi, dist-fluidsynth-one) .PHONY: build-oboe build-oboe: $(call run_make_for_all_abi, build-oboe-one) .PHONY: dist-oboe dist-oboe: $(call run_make_for_all_abi, dist-oboe-one) .PHONY: build-fluidsynth build-fluidsynth: $(call run_make_for_all_abi, build-fluidsynth-one) build-fluidsynth-one: mkdir -p build/$(A_ABI) && cd build/$(A_ABI) && \ LD_RUN_PATH=$(DIST_PATH)/android-$(BUILD_ABI)/lib:$(OBOE_BUILD_PATH)/$(A_ABI) \ LD_LIBRARY_PATH=$(DIST_PATH)/android_$(BUILD_ABI)/lib \ PKG_CONFIG_PATH=$(DIST_PATH)/android_$(BUILD_ABI)/lib/pkgconfig/:$(OBOE_BUILD_PATH)/$(A_ABI) \ PKG_CONFIG_LIBDIR=$(DIST_PATH)/android_$(BUILD_ABI)/lib/pkgconfig/:$(OBOE_BUILD_PATH)/$(A_ABI) \ $(CMAKE) -DCMAKE_INSTALL_PREFIX=$(PWD)/dist/$(A_ABI) \ -Denable-floats=1 \ -DCMAKE_VERBOSE_MAKEFILE=1 \ -DCMAKE_TOOLCHAIN_FILE=$(ANDROID_NDK)/build/cmake/android.toolchain.cmake \ -Denable-opensles=on -Denable-oboe=on -Denable-oss=off -Denable-libsndfile=on \ -DANDROID_NATIVE_API_LEVEL=android-27 -DANDROID_PLATFORM=android-27 -DANDROID_ABI=$(A_ABI) ../../../.. && \ make -j$(JOBS) build-oboe-one: mkdir -p $(OBOE)/build/$(A_ABI) && cd $(OBOE)/build/$(A_ABI) && \ $(CMAKE) -DCMAKE_TOOLCHAIN_FILE=$(ANDROID_NDK)/build/cmake/android.toolchain.cmake -DANDROID_ABI=$(A_ABI) -DANDROID_NATIVE_API_LEVEL=android-27 -DANDROID_PLATFORM=android-27 -DBUILD_SHARED_LIBS=on ../.. && make -j$(JOBS) cp oboe-1.0.pc $(OBOE)/build/$(A_ABI) dist-oboe-one: mkdir -p dist/$(A_ABI) && cp $(OBOE)/build/$(A_ABI)/*.so dist/$(A_ABI)/ dist-deps-cerbero-one: mkdir -p dist/$(A_ABI) && cd dist/$(A_ABI) && cp ../../external/cerbero/build/dist/android_$(BUILD_ABI)/lib/*.so . && cd ../.. dist-fluidsynth-one: mkdir -p dist/$(A_ABI) && cd dist/$(A_ABI) && cp ../../build/$(A_ABI)/src/libfluidsynth.so . && cd ../.. cp -r ../../include/fluidsynth build/$(A_ABI)/include/ build-fluidsynth-assetloader: cd fluidsynth-assetloader && ./ext-build.sh dist-fluidsynth-assetloader: cp fluidsynth-assetloader/build/x86/*.so dist/x86/ cp fluidsynth-assetloader/build/x86_64/*.so dist/x86_64/ cp fluidsynth-assetloader/build/armeabi-v7a/*.so dist/armeabi-v7a/ cp fluidsynth-assetloader/build/arm64-v8a/*.so dist/arm64-v8a/ clean: rm -rf dist/* build/* external/oboe/build/* obj/local/* fluidsynth-asset-loader/build/* .PHONY: wipe wipe: $(CERBERO) for abi in $(ABIS_SIMPLE) ; do \ cd $(CERBERO) && ./cerbero-uninstalled -c config/cross-android-$$abi.cbc wipe && cd ../.. ; \ done fluidsynth-2.2.5/doc/android/README.md000066400000000000000000000056611417326347500174010ustar00rootroot00000000000000# Android support in Fluidsynth Fluidsynth supports Android audio outputs by Oboe and OpenSLES audio drivers. If you are new to Fluidsynth on Android, check out [Hector Ricardo's Hello World App for Android](https://medium.com/swlh/creating-a-fluidsynth-hello-world-app-for-android-5e112454a8eb). Android also has Android MIDI API which is exposed only in Android Java API, but it is not exposed as a native API, therefore there is no `mdriver` support for Android. There is an example MidiDeviceService implementation for Fluidsynth at: https://github.com/atsushieno/fluidsynth-midi-service-j ## Usage `libfluidsynth.so` and `libfluidsynth-assetloader.so` are the library that should be packaged into apk. The latter is for asset-based "sfloader". By default, "oboe" is the default driver for Android. You can also explicitly specify "opensles" instead, with "audio.driver" setting: ``` fluid_settings_setstr (settings_handle, "audio.driver", "opensles"); ``` ## Custom SoundFont loader Since Android file access is quite limited and there is no common place to store soundfonts unlike Linux desktop (e.g. `/usr/share/sounds/sf2`), you will most likely have to provide custom soundfont loader. Since version 2.0.0 Fluidsynth comes with `fluid_sfloader_set_callbacks()` which brings [customizible file/stream reader](https://github.com/FluidSynth/fluidsynth/issues/241) (open/read/seek/tell/close). It is useful to implement simplified custom SF loader e.g. with Android assets or OBB streams. The Android implementation is in separate library called `libfluidsynth-assetloader.so`. It comes with native Asset sfloader. However, its usage is a bit tricky because AssetManager needs to be passed from Java code (even though we use AAssetManager API). Use `Java_fluidsynth_androidextensions_NativeHandler_setAssetManagerContext()` to initialize the this loader, then call `new_fluid_android_asset_sfloader()` to create a new sfloader. If you already have AAssetManager instance, then the first JNI function is ignorable and you only have to specify the manager to the second function. There is [an example source code](https://github.com/atsushieno/fluidsynth-midi-service-j/blob/a2a56b/fluidsynthjna/src/main/java/fluidsynth/androidextensions/AndroidNativeAssetSoundFontLoader.kt#L17) on how to do it. ## Building In this directory the Cerbero build system is (ab)used for cross-compiling Fluidsynth's dependencies for Android. The entrypoint is `Makefile.android`. If you are looking for a step by step introduction guide for cross-compiling Fluidsynth, [you'll find it in the wiki](https://github.com/FluidSynth/fluidsynth/wiki/BuildingForAndroid). By default, you are supposed to provide `PKG_CONFIG_PATH` to glib etc. as well as oboe. There is nothing special. However, in reality, Oboe does not come up with an official package specification, so you will have to create it manually... unless you use `oboe-1.0.pc` in this directory as well as the build system set up here. fluidsynth-2.2.5/doc/android/fluidsynth-assetloader/000077500000000000000000000000001417326347500226075ustar00rootroot00000000000000fluidsynth-2.2.5/doc/android/fluidsynth-assetloader/CMakeLists.txt000066400000000000000000000012421417326347500253460ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6.0) project ( fluidsynth-assetloader C ) set ( fluidsynth-assetloader_sources fluid_androidasset.c ) add_library ( fluidsynth-assetloader SHARED ${fluidsynth-assetloader_sources} ) target_compile_options ( fluidsynth-assetloader PRIVATE -v PRIVATE -Wall PRIVATE "$<$:-Werror>") # Only include -Werror when building debug config include_directories ( ../../../include ) set ( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L../../../dist/${ANDROID_ABI} -lfluidsynth" ) target_link_libraries ( fluidsynth-assetloader PRIVATE log android ) fluidsynth-2.2.5/doc/android/fluidsynth-assetloader/ext-build.sh000077500000000000000000000006601417326347500250450ustar00rootroot00000000000000PWD=`pwd` ABIS="x86 x86_64 armeabi-v7a arm64-v8a" HOST_OS=`uname | tr [:upper:] [:lower:]` ANDROID_NDK_PATH=~/android-sdk-$HOST_OS/ndk-bundle CMAKEFILE=$ANDROID_NDK_PATH/build/cmake/android.toolchain.cmake for A_ABI in $ABIS ; do mkdir -p build/$A_ABI && \ cd build/$A_ABI && \ cmake -DCMAKE_TOOLCHAIN_FILE=$CMAKEFILE -DANDROID_PLATFORM=android-27 -DANDROID_ABI=$A_ABI ../.. && \ make && cd ../.. ; done fluidsynth-2.2.5/doc/android/fluidsynth-assetloader/fluid_androidasset.c000066400000000000000000000062151417326347500266220ustar00rootroot00000000000000/* 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 defined(ANDROID) || defined(__DOXYGEN__) #define FLUIDSYNTH_API #include #include #include "fluid_androidasset.h" #include #include AAssetManager *fluid_android_asset_manager; fluid_sfloader_t* new_fluid_android_asset_sfloader(fluid_settings_t *settings, void *assetManager) { fluid_sfloader_t *loader; if (settings == NULL) return NULL; if (!fluid_android_asset_manager) fluid_android_asset_manager = (AAssetManager*) assetManager; if (fluid_android_asset_manager == NULL) return NULL; loader = new_fluid_defsfloader(settings); if (loader == NULL) return NULL; fluid_sfloader_set_callbacks(loader, asset_open, asset_read, asset_seek, asset_tell, asset_close); return loader; } /* This is a compromised solution for JNAerator for that 1) it cannot handle jobject with JNIEnv as parameters, and that 2) the returned pointer can be converted in the same manner that JNAerated methods. (Most likely my JNA usage issue but no one has answer for it.) */ void Java_fluidsynth_androidextensions_NativeHandler_setAssetManagerContext(JNIEnv *env, jobject _this, jobject assetManager) { if (assetManager == NULL) return; fluid_android_asset_manager = AAssetManager_fromJava (env, assetManager); } void *asset_open(const char *path) { if (fluid_android_asset_manager == NULL) return NULL; return AAssetManager_open (fluid_android_asset_manager, path, AASSET_MODE_RANDOM); } int asset_close(void *handle) { AAsset *asset; asset = (AAsset*) handle; AAsset_close (asset); return 0; } fluid_long_long_t asset_tell(void *handle) { AAsset *asset; asset = (AAsset*) handle; return AAsset_getLength(asset) - AAsset_getRemainingLength(asset); } int asset_seek(void *handle, fluid_long_long_t offset, int origin) { AAsset *asset; asset = (AAsset*) handle; return AAsset_seek (asset, (off_t) offset, origin); } int asset_read(void *buf, fluid_long_long_t count, void *handle) { AAsset *asset; asset = (AAsset*) handle; return AAsset_read (asset, buf, (size_t) count); } #endif /* if defined(ANDROID) || defined(__DOXYGEN__) */ fluidsynth-2.2.5/doc/android/fluidsynth-assetloader/fluid_androidasset.h000066400000000000000000000030471417326347500266270ustar00rootroot00000000000000/* 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_ANDROIDASSET_H #define _PRIV_FLUID_ANDROIDASSET_H #ifdef __cplusplus extern "C" { #endif #include #include fluid_sfloader_t* new_fluid_android_asset_sfloader(fluid_settings_t *settings, void *assetManager); void Java_fluidsynth_androidextensions_NativeHandler_setAssetManagerContext(JNIEnv *env, jobject _this, jobject assetManager); void *asset_open(const char *path); int asset_close(void *handle); fluid_long_long_t asset_tell(void *handle); int asset_seek(void *handle, fluid_long_long_t offset, int origin); int asset_read(void *buf, fluid_long_long_t count, void *handle); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* _PRIV_FLUID_ANDROIDASSET_H */ fluidsynth-2.2.5/doc/android/jni/000077500000000000000000000000001417326347500166725ustar00rootroot00000000000000fluidsynth-2.2.5/doc/android/jni/Android.mk000066400000000000000000000020071417326347500206020ustar00rootroot00000000000000LOCAL_PATH := $(call my-dir) TARGET_PLATFORM := android-27 GLIB_LIB = ../dep/$(APP_ABI)/ include $(CLEAR_VARS) LOCAL_MODULE := glib-2.0 LOCAL_SRC_FILES := $(GLIB_LIB)/libglib-2.0.a include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := iconv LOCAL_SRC_FILES := $(GLIB_LIB)/libiconv.a include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := intl LOCAL_SRC_FILES := $(GLIB_LIB)/libintl.a include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := oboe LOCAL_SRC_FILES := $(GLIB_LIB)/liboboe.a include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := fluidsynth_static LOCAL_SRC_FILES := ../dep/$(APP_ABI)/libfluidsynth.a include $(PREBUILT_STATIC_LIBRARY) LOCAL_MODULE := fluidsynth ifeq ($(NDK_DEBUG),1) cmd-strip := endif LOCAL_STATIC_LIBRARIES := glib-2.0 iconv intl oboe LOCAL_WHOLE_STATIC_LIBRARIES := fluidsynth_static LOCAL_LDLIBS := -lc -lOpenSLES -ldl -llog -landroid -L$(LOCAL_PATH)/../dist/$(APP_ABI) -loboe-c include $(BUILD_SHARED_LIBRARY) fluidsynth-2.2.5/doc/android/jni/Application.mk000066400000000000000000000001071417326347500214640ustar00rootroot00000000000000APP_PLATFORM := android-21 APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a fluidsynth-2.2.5/doc/android/oboe-1.0.pc000066400000000000000000000003361417326347500176600ustar00rootroot00000000000000prefix=${pcfiledir} exec_prefix=${prefix} libdir=${prefix} includedir=${prefix}/../../include/ Name: Oboe Description: Oboe library Version: 1.0.0 Libs: -L${libdir} -loboe -landroid -llog -lstdc++ Cflags: -I${includedir} fluidsynth-2.2.5/doc/doxygen/000077500000000000000000000000001417326347500161475ustar00rootroot00000000000000fluidsynth-2.2.5/doc/doxygen/custom.css000066400000000000000000000077111417326347500202010ustar00rootroot00000000000000body { background: #eee; } #titlearea { background: white; } * { text-shadow: none !important; } /* Reduce width of main content for more readability */ div.contents, div.header { max-width: 60em; background: white; margin: 0; padding: 1em; } #titlearea { border: 0; } div.headertitle { padding-left: 0; } /* Hide permalink icon in front of method name and make whole name clickable * instead (by invisibly streting the permalink over the title). Not ideal, * as the name can't be selected anymore, but better than the distracting * icon. */ span.permalink { display: block; position: relative; font-size: unset; } span.permalink a { position: absolute; width: 100%; opacity: 0; } /* Hide "libfluidsynth" root node of nav tree */ #nav-tree-contents > ul > li:first-child > .item > .label, #nav-tree-contents > ul > li:first-child > .item > a { display: none; } #nav-tree, #side-nav { background: #333; } #nav-tree .label a { color: #fff; } #nav-tree .selected { background: #666; } #side-nav { width: 350px; } .ui-resizable-e { background: #ddd; } #nav-sync { display: none; } @media (max-width: 767px) { #side-nav { display: none !important; } #doc-content { margin-left: 0 !important; height: auto !important; width: auto !important; } #MSearchBox { display: none !important; } } .sm-dox a:hover { color: black !important; } #nav-tree, div.header, .ui-resizable-e, .sm-dox, .navpath ul, .memtitle, .sm-dox a, .fieldtable th { background-image: none !important; } #main-nav { background: #DFE5F1; } .memitem, .memproto, .memdoc, .memtitle, dl.reflist dd, dl.reflist dt { box-shadow: none; text-shadow: none; border-right: none; border-bottom: none; border-bottom-left-radius: 0; background-image: none; border-color: #DFE5F1; } .memtitle, dl.reflist dt { width: 100%; box-sizing: border-box; background: #DFE5F1; border: 0; } .memproto { background: #fafafa; border-bottom: 1px solid #DFE5F1; border-right: 1px solid #DFE5F1; } .mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams { background: none; } .memSeparator { } dl.reflist { padding: 0; } dl.reflist dd { margin-bottom: 1.5em; padding: 1em; } code { background-color: #eeeeee; text-shadow: none; color: black; margin-left: 4px; margin-right: 4px; padding-left: 4px; padding-right: 4px; border-radius: 3px; white-space: nowrap; } table.fieldtable, table.fieldtable td, table.fieldtable th { box-shadow: none; text-shadow: none; border-radius: 0; border-color: #DFE5F1 !important; } div.fragment { padding: 5px; } div.fragment .line { line-height: 1.4; } .memdoc dl { padding-left: 0; } dl.note { margin-left: -10px; padding-left: 5px; border-left: 5px solid; border-color: lightblue; } dl.deprecated { margin-left: -10px; padding-left: 5px; border-left: 5px solid; border-color: orange; } dl.warning { margin-left: -10px; padding-left: 5px; border-left: 5px solid; border-color: red; } table.directory .arrow { height: inherit; } table.directory tr td { padding: 0.4em 6px; } table.directory td.desc { border: 0; } table.directory tr.even { background-color: inherit; } table.directory tr { border-bottom: 1px solid #eee; } .image img, .image object { width: 100%; height: auto; } .setting-item { border-left: 1px solid #DFE5F1; padding-bottom: 0.5em; border-top-left-radius: 4px; } .setting-item h1 { width: 100%; padding: 0.3em 0 0.3em 10px; box-sizing: border-box; background: #DFE5F1; border: 0; font-size: 1.5em; font-weight: normal; border-top-right-radius: 4px; border-top-left-radius: 4px; } .setting-item dl, .setting-item p { margin-left: 10px; } fluidsynth-2.2.5/doc/doxygen/fluidsettings.xsl000066400000000000000000000051651417326347500215720ustar00rootroot00000000000000 /*! \page fluidsettings Settings Reference */ - \subpage \page \htmlonly

\endhtmlonly fluidsynth-2.2.5/doc/doxygen/footer.html000066400000000000000000000000201417326347500203230ustar00rootroot00000000000000 fluidsynth-2.2.5/doc/doxygen/layout.xml000066400000000000000000000145071417326347500202150ustar00rootroot00000000000000 fluidsynth-2.2.5/doc/examples/000077500000000000000000000000001417326347500163105ustar00rootroot00000000000000fluidsynth-2.2.5/doc/examples/CMakeLists.txt000066400000000000000000000021301417326347500210440ustar00rootroot00000000000000# 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 include ( FluidUnitTest ) add_custom_target ( demo ) ADD_FLUID_DEMO ( example ) ADD_FLUID_DEMO ( fluidsynth_arpeggio ) ADD_FLUID_DEMO ( fluidsynth_fx ) ADD_FLUID_DEMO ( fluidsynth_metronome ) ADD_FLUID_DEMO ( fluidsynth_simple ) fluidsynth-2.2.5/doc/examples/example.c000066400000000000000000000034111417326347500201060ustar00rootroot00000000000000/* 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) #include #define getpid _getpid #else #include #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); if(sfont_id == FLUID_FAILED) { puts("Loading the SoundFont failed!"); goto err; } /* 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); } err: /* Clean up */ delete_fluid_audio_driver(adriver); delete_fluid_synth(synth); delete_fluid_settings(settings); return 0; } fluidsynth-2.2.5/doc/examples/fluidsynth_arpeggio.c000066400000000000000000000112421417326347500225220ustar00rootroot00000000000000/* 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(void) { 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(void) { unsigned int i; int 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); /* load a SoundFont */ n = fluid_synth_sfload(synth, argv[1], 1); if(n != -1) { sequencer = new_fluid_sequencer2(0); /* 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); if(argc > 2) { n = atoi(argv[2]); if((n > 1) && (n <= (int)pattern_size)) { pattern_size = n; } } if(argc > 3) { n = atoi(argv[3]); if(n > 0) { duration = n; } } audiodriver = new_fluid_audio_driver(settings, synth); /* 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_audio_driver(audiodriver); delete_fluid_sequencer(sequencer); delete_fluid_synth(synth); } delete_fluid_settings(settings); return 0; } fluidsynth-2.2.5/doc/examples/fluidsynth_fx.c000066400000000000000000000112421417326347500213420ustar00rootroot00000000000000/* 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 audio frames * in the buffers. 'nfx' and 'nout' are the number of input and output * audio buffers. 'fx' and 'out' are arrays of float buffers containing * the audio. The audio driver fills zero-initializes those buffers. * You are responsible for filling up those buffers, as the result will * be sent to the sound card. This is usually done by asking the synth * to fill those buffers appropriately using fluid_synth_process() * * NOTE: The API was designed to be generic. Audio driver may fill the * buffers with audio input from the soundcard, rather than zeros. */ int fx_function(void *data, int len, int nfx, float **fx, int nout, float **out) { struct fx_data_t *fx_data = (struct fx_data_t *) data; int i, k; if(fx == 0) { /* Note that some audio drivers may not provide buffers for effects like * reverb and chorus. In this case it's your decision what to do. If you * had called fluid_synth_process() like in the else branch below, no * effects would have been rendered. Instead, you may mix the effects * directly into the out buffers. */ if(fluid_synth_process(fx_data->synth, len, nout, out, nout, out) != FLUID_OK) { /* Some error occurred. Very unlikely to happen, though. */ return FLUID_FAILED; } } else { /* Call the synthesizer to fill the output buffers with its * audio output. */ if(fluid_synth_process(fx_data->synth, len, nfx, fx, nout, out) != FLUID_OK) { /* Some error occurred. Very unlikely to happen, though. */ return FLUID_FAILED; } } /* Apply your effects here. In this example, the gain is * applied to all the dry-audio output buffers. */ for(i = 0; i < nout; i++) { float *out_i = out[i]; for(k = 0; k < len; k++) { out_i[k] *= fx_data->gain; } } /* Apply the same effect to all available effect buffer. */ for(i = 0; i < nfx; i++) { float *fx_i = fx[i]; for(k = 0; k < len; k++) { fx_i[k] *= fx_data->gain; } } return FLUID_OK; } 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 = (float)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-2.2.5/doc/examples/fluidsynth_metronome.c000066400000000000000000000102641417326347500227350ustar00rootroot00000000000000/* 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(void) { 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(void) { unsigned int i; int 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); /* load a SoundFont */ n = fluid_synth_sfload(synth, argv[1], 1); if(n != -1) { sequencer = new_fluid_sequencer2(0); /* 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); audiodriver = new_fluid_audio_driver(settings, synth); 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_audio_driver(audiodriver); delete_fluid_sequencer(sequencer); delete_fluid_synth(synth); } delete_fluid_settings(settings); return 0; } fluidsynth-2.2.5/doc/examples/fluidsynth_process.c000066400000000000000000000100761417326347500224070ustar00rootroot00000000000000/* * This is a C99 program that outlines different usage examples for fluid_synth_process() */ #include #include #include int main() { // any arbitrary number of audio samples to render during on call of fluid_synth_process() enum { SAMPLES = 512 }; // ...creation of synth omitted... // USECASE1: render all dry audio channels + reverb and chorus to one stereo channel { // planar sample buffers that received synthesized (monophonic) audio float left[SAMPLES], right[SAMPLES]; // array of buffers used to setup channel mapping float *dry[1 * 2], *fx[1 * 2]; // first make sure to zero out the sample buffers every time before calling fluid_synth_process() memset(left, 0, sizeof(left)); memset(right, 0, sizeof(right)); // setup channel mapping for a single stereo channel to which to render all dry audio to dry[0] = left; dry[1] = right; // Setup channel mapping for a single stereo channel to which to render effects to. // Just using the same sample buffers as for dry audio is fine here, as it will cause the effects to be mixed with dry output. // Note: reverb and chorus together make up two stereo channels. Setting up only one stereo channel is sufficient // as the channels wraps around (i.e. chorus will be mixed with reverb channel). fx[0] = left; fx[1] = right; int err = fluid_synth_process(synth, SAMPLES, 2, fx, 2, dry); if(err == FLUID_FAILED) { puts("oops"); } // USECASE2: only render dry audio and discard effects // same as above, but call fluid_synth_process() like: int err = fluid_synth_process(synth, SAMPLES, 0, NULL, 2, dry); if(err == FLUID_FAILED) { puts("oops"); } } // USECASE3: render audio and discard all samples { int err = fluid_synth_process(synth, SAMPLES, 0, NULL, 0, NULL); if(err == FLUID_FAILED) { puts("oops"); } } // USECASE4: multi-channel rendering, i.e. render all audio and effects channels to dedicated audio buffers // ofc it‘s not a good idea to allocate all the arrays on the stack { // lookup number of audio and effect (stereo-)channels of the synth // see "synth.audio-channels", "synth.effects-channels" and "synth.effects-groups" settings respectively int n_aud_chan = fluid_synth_count_audio_channels(synth); // by default there are two effects stereo channels (reverb and chorus) ... int n_fx_chan = fluid_synth_count_effects_channels(synth); // ... for each effects unit. Each unit takes care of the effects of one MIDI channel. // If there are less units than channels, it wraps around and one unit may render effects of multiple // MIDI channels. n_fx_chan *= fluid_synth_count_effects_groups(); // for simplicity, allocate one single sample pool float samp_buf[SAMPLES * (n_aud_chan + n_fx_chan) * 2]; // array of buffers used to setup channel mapping float *dry[n_aud_chan * 2], *fx[n_fx_chan * 2]; // setup buffers to mix dry stereo audio to // buffers are alternating left and right for each n_aud_chan, // please review documentation of fluid_synth_process() for(int i = 0; i < n_aud_chan * 2; i++) { dry[i] = &samp_buf[i * SAMPLES]; } // setup buffers to mix effects stereo audio to // similar channel layout as above, revie fluid_synth_process() for(int i = 0; i < n_fx_chan * 2; i++) { fx[i] = &samp_buf[n_aud_chan * 2 * SAMPLES + i * SAMPLES]; } // dont forget to zero sample buffer(s) before each rendering memset(samp_buf, 0, sizeof(samp_buf)); int err = fluid_synth_process(synth, SAMPLES, n_fx_chan * 2, fx, n_aud_chan * 2, dry); if(err == FLUID_FAILED) { puts("oops"); } } return 0; } fluidsynth-2.2.5/doc/examples/fluidsynth_register_adriver.c000066400000000000000000000047171417326347500242760ustar00rootroot00000000000000/* * 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]; /* three iterations, first register only alsa, then only jack, and last portaudio * ...just to demonstrate how and under which conditions fluid_audio_driver_register() * can be called */ for(int i = 0; i < sizeof(DRV) / sizeof(DRV[0]); i++) { adrivers[0] = DRV[i]; /* register any other driver you need * * adrivers[X] = "whatever"; */ adrivers[1] = NULL; /* NULL terminate the array */ /* register those audio drivers. Note that at this time no fluidsynth objects are alive! */ int res = fluid_audio_driver_register(adrivers); if(res != FLUID_OK) { puts("adriver reg err"); return -1; } fluid_settings_t *settings = new_fluid_settings(); res = fluid_settings_setstr(settings, "audio.driver", DRV[i]); /* As of fluidsynth 2, settings API has been refactored to return FLUID_OK|FAILED * rather than returning TRUE or FALSE */ #if FLUIDSYNTH_VERSION_MAJOR >= 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-2.2.5/doc/examples/fluidsynth_sfload_mem.c000066400000000000000000000037341417326347500230420ustar00rootroot00000000000000/* * This is a C99 program that demonstrates how to load a soundfont from memory. * * It only gives a brief overview on how to achieve this with fluidsynth's API. * Although it should compile, it's highly incomplete, as the details of it's * implementation depend on the users needs. */ #include #include #include void *my_open(const char *filename) { void *p; if(filename[0] != '&') { return NULL; } sscanf(filename, "&%p", &p); return p; } int my_read(void *buf, int count, void *handle) { // NYI return FLUID_OK; } int my_seek(void *handle, long offset, int origin) { // NYI return FLUID_OK; } int my_close(void *handle) { // NYI return FLUID_OK; } long my_tell(void *handle) { // NYI return 0; } int main() { int err = 0; fluid_settings_t *settings = new_fluid_settings(); fluid_synth_t *synth = new_fluid_synth(settings); fluid_sfloader_t *my_sfloader = new_fluid_defsfloader(settings); fluid_sfloader_set_callbacks(my_sfloader, my_open, my_read, my_seek, my_tell, my_close); fluid_synth_add_sfloader(synth, my_sfloader); char abused_filename[64]; const void *pointer_to_sf2_in_mem = 0x1234Beef; // some pointer to where the soundfont shall be loaded from sprintf(abused_filename, "&%p", pointer_to_sf2_in_mem); int id = fluid_synth_sfload(synth, abused_filename, 0); /* now my_open() will be called with abused_filename and should have opened the memory region */ if(id == FLUID_FAILED) { puts("oops"); err = -1; goto cleanup; } /* * ~~~ Do your daily business here ~~~ */ cleanup: /* deleting the synth also deletes my_sfloader */ delete_fluid_synth(synth); delete_fluid_settings(settings); return err; } fluidsynth-2.2.5/doc/examples/fluidsynth_simple.c000066400000000000000000000036301417326347500222200ustar00rootroot00000000000000/* 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-2.2.5/doc/fluidsettings.xml000066400000000000000000001054161417326347500201070ustar00rootroot00000000000000 audio-channels int 1 1 128 By default, the synthesizer outputs a single stereo signal. Using this option, the synthesizer can output multi-channel audio. Sets the number of stereo channel pairs. So 1 is actually 2 channels (a stereo pair). audio-groups int 1 1 128 The output audio channel associated with a MIDI channel is wrapped around using the number of synth.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. chorus.active bool 1 (TRUE) 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. chorus.depth num 8.0 0.0 256.0 Specifies the modulation depth of the chorus. chorus.level num 2.0 0.0 10.0 Specifies the output amplitude of the chorus signal. chorus.nr int 3 0 99 Sets the voice count of the chorus. chorus.speed num 0.3 0.1 5.0 Sets the modulation speed in Hz. cpu-cores int 1 1 256 Sets the number of synthesis CPU cores. If set to a value greater than 1, additional synthesis threads will be created to do the actual rendering work that is then returned synchronously by the render function. This has the affect of utilizing more of the total CPU for voices or decreasing render times when synthesizing audio. So for example, if you set cpu-cores to 4, fluidsynth will attempt to split the synthesis work it needs to do between the client's calling thread and three additional (internal) worker threads. As soon as all threads have done their work, their results are collected and the resulting buffer is returned to the caller. default-soundfont str C:\soundfonts\default.sf2 (Windows),
${CMAKE_INSTALL_PREFIX}/share/soundfonts/default.sf2 (all others)
The default soundfont file to use by the fluidsynth executable. The default value can be overridden during compilation time by setting the DEFAULT_SOUNDFONT cmake variable.
device-id int 0 0 126 Device identifier used for SYSEX commands, such as MIDI Tuning Standard commands. Fluidsynth will only process those SYSEX commands destined for this ID. Broadcast commands (with ID=127) will always be processed. dynamic-sample-loading bool 0 (FALSE) When set to 1 (TRUE), samples are loaded to and unloaded from memory on demand. effects-channels int 2 2 2 Specifies the number of effects per effects group. Currently this value can not be changed so there are always two effects per group available (reverb and chorus). effects-groups int 1 1 128 Specifies the number of effects groups. By default, the sound of all voices is rendered by one reverb and one chorus effect respectively (even for multi-channel rendering). This setting gives the user control which effects of a voice to render to which independent audio channels. E.g. setting synth.effects-groups == synth.midi-channels allows to render the effects of each MIDI channel to separate audio buffers. If synth.effects-groups is smaller than the number of MIDI channels, it will wrap around. Note that any value >1 will significantly increase CPU usage. gain num 0.2 0.0 10.0 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. ladspa.active bool 0 (FALSE) When set to 1 (TRUE) 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 can be found in doc/ladspa.md or on the FluidSynth website. lock-memory bool 1 (TRUE) Page-lock memory that contains audio sample data, if true. midi-channels int 16 16 256 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. midi-bank-select str gs gs, gm, xg, mma This setting defines how the synthesizer interprets Bank Select messages.
  • gs: (default) CC0 becomes the bank number, CC32 is ignored.
  • gm: ignores CC0 and CC32 messages.
  • xg: CC32 becomes the bank number, CC0 toggles between melodic or drum channel.
  • mma: bank is calculated as CC0*128+CC32.
min-note-length int 10 0 65535 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. overflow.age num 1000.0 -10000.0 10000.0 This score is divided by the number of seconds this voice has been active and is added to the overflow priority. It is usually a positive value and gives voices which have just been started a higher priority, making them less likely to be killed in an overflow situation. overflow.important num 5000.0 -50000.0 50000.0 This score is added to voices on channels marked with the synth.overflow.important-channels setting. overflow.important-channels str (empty string) This setting is a comma-separated list of MIDI channel numbers that should be treated as "important" by the overflow calculation, adding the score set by synth.overflow.important to each voice on those channels. It can be used to make voices on particular MIDI channels less likely (synth.overflow.important > 0) or more likely (synth.overflow.important < 0) to be killed in an overflow situation. Channel numbers are 1-based, so the first MIDI channel is number 1. overflow.percussion num 4000.0 -10000.0 10000.0 Sets the overflow priority score added to voices on a percussion channel. This is usually a positive score, to give percussion voices a higher priority and less chance of being killed in an overflow situation. overflow.released num -2000.0 -10000.0 10000.0 Sets the overflow priority score added to voices that have already received a note-off event. This is usually a negative score, to give released voices a lower priority so that they are killed first in an overflow situation. overflow.sustained num -1000.0 -10000.0 10000.0 Sets the overflow priority score added to voices that are currently sustained. With the default value, sustained voices are considered less important and are more likely to be killed in an overflow situation. overflow.volume num 500.0 -10000.0 10000.0 Sets the overflow priority score added to voices based on their current volume. The voice volume is normalized to a value between 0 and 1 and multiplied with this setting. So voices with maximum volume get added the full score, voices with only half that volume get added half of this score. polyphony int 256 1 65535 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. reverb.active bool 1 (TRUE) 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. reverb.damp num 0.0 0.0 1.0 Sets the amount of reverb damping. reverb.level num 0.9 0.0 1.0 Sets the reverb output amplitude. reverb.room-size num 0.2 0.0 1.0 Sets the room size (i.e. amount of wet) reverb. reverb.width num 0.5 0.0 100.0 Sets the stereo spread of the reverb signal. sample-rate num 44100.0 8000.0 96000.0 The sample rate of the audio generated by the synthesizer. For optimal performance, make sure this value equals the native output rate of the audio driver (in case you are using any of fluidsynth's audio drivers). Some drivers, such as Oboe, will interpolate sample-rates, whereas others, such as Jack, will override this setting, if a mismatch with the native output rate is detected. threadsafe-api bool 1 (TRUE) 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. verbose bool 0 (FALSE) 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.
autoconnect bool 0 (FALSE) If 1 (TRUE), automatically connects FluidSynth to available MIDI input ports. alsa_seq, coremidi and jack are currently the only drivers making use of this. driver str alsa_seq (Linux),
winmidi (Windows),
jack (Mac OS X)
alsa_raw, alsa_seq, coremidi, jack, midishare, oss, winmidi The MIDI system to be used.
realtime-prio int 50 0 99 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 portname str (empty string) Used by coremidi and alsa_seq drivers for the portnames registered with the MIDI subsystem. alsa.device str default ALSA MIDI hardware device to use for RAW ALSA MIDI driver (not to be confused with the MIDI port). alsa_seq.device str default ALSA sequencer hardware device to use for ALSA sequencer driver (not to be confused with the MIDI port). alsa_seq.id str pid 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. coremidi.id str pid Client ID to use for CoreMIDI driver. 'pid' will use process ID as port of the client name. jack.server str (empty string) Jack server to connect to for Jack MIDI driver. If an empty string then the default server will be used. jack.id str fluidsynth-midi Client ID to use with the Jack MIDI driver. If jack is also used as audio driver and "midi.jack.server" and "audio.jack.server" are equal, this setting will be overridden by "audio.jack.id", because a client cannot have multiple names. oss.device str /dev/midi The hardware device to use for OSS MIDI driver (not to be confused with the MIDI port). winmidi.device str default The hardware device to use for Windows MIDI driver (not to be confused with the MIDI port). Multiple devices can be specified by a list of devices index separated by a semicolon (e.g "2;0", which is equivalent to one device with 32 MIDI channels).
reset-synth bool 1 (TRUE) If true, reset the synth after the end of a MIDI song, so that the state of a previous song can't affect the next song. Turn it off for seamless looping of a song. timing-source str sample sample, system 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. prompt str (empty string) In dump mode we set the prompt to "" (empty string). 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. port int 9800 1 65535 The shell can be used in a client/server mode. This setting controls what TCP/IP port the server uses.
fluidsynth-2.2.5/doc/fluidsettings.xsl000066400000000000000000000247161417326347500201200ustar00rootroot00000000000000 FluidSynth Settings

.
Type:
Boolean (int) Integer (int) Selection (str) String (str) Float (num)
Options:
Values:
0, 1
Min - Max:
-
Default:
Real-time:
This setting can be changed during runtime of the synthesizer.
This setting is deprecated and might be removed in a future version of FluidSynth.
fluidsynth-2.2.5/doc/fluidsynth-v20-devdoc.txt000066400000000000000000000112721417326347500212760ustar00rootroot00000000000000/*! \mainpage FluidSynth 2.2 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-2022 Peter Hanappe, Conrad Berhörster, Antoine Schmitt, Pedro López-Cabanillas, Josh Green, David Henningsson, Tom Moebert \version Revision 2.2.5 \date 2022-01-16 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 https://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 https://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 Disclaimer This documentation may be partly incomplete. As always, the source code is the final reference. SoundFont(R) is a registered trademark of E-mu Systems, Inc. \page Introduction 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 is publicly available on the internet, 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 https://www.fluidsynth.org \page deprecated Deprecated Functions This page contains functions that have been marked obsolete. Functions listed here will be removed in the next major release. It is therefore not wise to use them in new code. */ /*! \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) */ /*! \example fluidsynth_sfload_mem.c Example of how read a soundfont from memory (advanced users only) */ /*! \example fluidsynth_process.c Usage examples of how to render audio using fluid_synth_process() (advanced users only) */ fluidsynth-2.2.5/doc/fluidsynth.1000066400000000000000000000205671417326347500167570ustar00rootroot00000000000000.\" 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 1, 2022" .\" 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 \-q, \-\-quiet Do not print welcome message or other informational output .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 (synth.verbose=1) as well as other debug messages .TP .B \-V, \-\-version Show version of program .TP .B \-z, \-\-audio\-bufsize=[size] Size of each audio buffer .SH SETTINGS The settings to be specified with \-o are documented in the fluidsettings.xml hopefully shipped with this distribution or online at https://www.fluidsynth.org/api/fluidsettings.xml . We recommend viewing this file in a webbrowser, favourably Firefox. .SH SHELL COMMANDS .TP .B GENERAL .TP .B help Prints out list of help topics (type "help " to view details on available commands) .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 set synth.reverb.active [0|1] Turn the reverb on or off .TP .B set synth.reverb.room-size num Change reverb room size .TP .B set synth.reverb.damp num Change reverb damping .TP .B set synth.reverb.width num Change reverb width .TP .B set synth.reverb.level num Change reverb level .TP .B CHORUS .TP .B set synth.chorus.active [0|1] Turn the chorus on or off .TP .B set synth.chorus.nr n Use n delay lines (default 3) .TP .B set synth.chorus.level num Set output level of each chorus line to num .TP .B set synth.chorus.speed num Set mod speed of chorus to num (Hz) .TP .B set synth.chorus.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 .br Tom Moebert Please check the AUTHORS and THANKS files for all credits .SH DISCLAIMER SoundFont(R) is a registered trademark of E-mu Systems, Inc. fluidsynth-2.2.5/doc/images/000077500000000000000000000000001417326347500157375ustar00rootroot00000000000000fluidsynth-2.2.5/doc/images/fluid_mixer.svg000066400000000000000000004542561417326347500210070ustar00rootroot00000000000000 image/svg+xmlVoice Voice Voice Voice Voice Voice send pan m s map MIDI chan-to-buf map MIDI chan-to-fx m m fx buf 1 fx buf k buf n + + map fx1-to-bufmix / replace m s s s s Mixer MIDInotes Synthesis map fxk-to-buf Audio driver Hostaudiodriver Hostaudiodriver synth.effects-groups = k synth.audio-groups = n synth.audio-channels = c s s Fx units fx1 fx1 in 1 in c buf 1 fxk fxk + + s s in in/out s m s s s s LADSPA fx plugin(s) fx plugin(s) fxin1 fxink Legend : m/ : mono s/ : stereo buf[1..n] : dry buffers fx in[1..k] : fx inputs fx_buf[1..k] : fx outputs APIfluid_synth_process() APIfluid_synth_process() in/out s Application settings ranges Mappings •MIDI chan x to dry buf i: i = x % synth.audio-groups•MIDI chan x to fx unit j: j = x % synth.effects-groups•fx j to dry buf k: k = j % synth.audio-groups option FluidSynth mixer fluidsynth-2.2.5/doc/ladspa.md000066400000000000000000000423661417326347500162730ustar00rootroot00000000000000# FluidSynth LADSPA Interface The [LADSPA](https://ladspa.org/) (Linux Audio Developer's Simple Plugin API) binding can be used to route the FluidSynth audio output through any number of LADSPA plugins. Please note that even though the "L" in LADSPA stands for "Linux", it can also be used on different platforms, for example Windows or MacOS. Check the "LADSPA on other Platforms" section at the end of this guide for more information. ## Configuration To configure and compile FluidSynth with LADSPA support, make sure you have the LADSPA SDK installed (or at least the ladspa.h header file available in an include path). Then compile FluidSynth in the usual way. You should see `LADSPA support: yes` in the cmake output. To enable the LADSPA engine, use the `synth.ladspa.active` setting when starting FluidSynth: fluidsynth -o synth.ladspa.active=1 ... # Quickstart Tutorial The following walks you through the process of adding a LADSPA plugin into your FluidSynth configuration. It assumes that you are running FluidSynth on Linux, that you have some experience with running Linux shell commands and that you know how to start FluidSynth from the command line and use it to play a MIDI file. ## Introduction to LADSPA You don't need to to have detailed knowledge of LADSPA to use effects with FluidSynth, but knowing some of it's concepts will help if you want to make the best use of it. If you have the LADSPA SDK installed you should be able to use the `listplugins` Linux command to list all plugins installed in your LADSPA path. And to show more details about a particular plugin library, you can use the `analyseplugin` Linux command. Here is an example showing the details of the `delay.so` plugin from the LADSPA SDK: ``` user@host:$ analyseplugin /usr/lib/ladspa/delay.so Plugin Name: "Simple Delay Line" Plugin Label: "delay_5s" Plugin Unique ID: 1043 Maker: "Richard Furse (LADSPA example plugins)" Copyright: "None" Must Run Real-Time: No Has activate() Function: Yes Has deactivate() Function: No Has run_adding() Function: No Environment: Normal or Hard Real-Time Ports: "Delay (Seconds)" input, control, 0 to 5, default 1 "Dry/Wet Balance" input, control, 0 to 1, default 0.5 "Input" input, audio "Output" output, audio ``` This output tells you that the `delay.so` library contains only a single plugin called "Simple Delay Line". Most importantly it lists the input and output ports, which can be used to set plugin parameters and connect the audio input and output to FluidSynth. "Delay (Seconds)" and "Dry/Wet Balance" are input controls. They are the parameters that a user can set to affect the way the plugin works. They control how long the delay should be and how the dry and wet signals should be mixed before writing them to the output. "Input" and "Output" are audio ports which carry samples into the plugin and out again after it has run. Mono plugins usually provide one set of input and output audio ports, stereo plugins usually provide two sets. But there are even plugins that only have a single output port and no input at all (think of noise generators...) Also note the line `Has run_adding() Function: No`. This specifies that this plugin can not mix it's audio output into an output buffer, but will always replace anything that is already there. This will become important again later on. ## FluidSynth Host Ports Just as LADSPA plugins have input and output ports, FluidSynth provides it's own audio ports that can be connected to plugins. On a standard stereo setup, the following four ports are automatically created: - Main:L - Main:R - Reverb:Send - Chorus:Send The "Main:L" and "Main:R" ports can be connected to effect input and output ports. They carry the main audio signals into the LADSPA effects and the modified signals back into FluidSynth. "Reverb:Send" and "Chorus:Send" can be used as effect inputs. They carry the mono effect send signals (as determined by the reverb and chorus send generators for each voice) into the LADSPA effects. Please note that if you run FluidSynth with the internal reverb and chorus effects active (which is the default), then those effects are already mixed into the Main:L and Main:R channels. Fore more details, please see the "Signal Flow" section below. For host port setups in multi-channel configurations, please see the "Multi-Channel Output" section below. ## Creating a Configuration File You can configure LADSPA effects using the FluidSynth shell, but writing the commands into a file and loading it at startup is much more comfortable. So let's create a file `effects.txt` with the following contents: effects.txt ``` ladspa_effect e1 /usr/lib/ladspa/delay.so ladspa_link e1 Input Main:L ladspa_link e1 Output Main:L ladspa_effect e2 /usr/lib/ladspa/delay.so delay_5s ladspa_link e2 Input Main:R ladspa_link e2 Output Main:R ladspa_start ``` As the "Simple Delay Line" plugin only works on a mono signal, the configuration above creates two effects: the one we named "e1" reads from and writes to the left FluidSynth audio channel "Main:L", the "e2" effect reads from and writes to the right channel "Main:R". Please note that we only specified the path to the library `/usr/lib/ladspa/delay.so` when creating the "e1" effect, but not which plugin from the library to use. This is possible because the delay.so library contains only a single plugin. If you want to use a library that contains more than one plugin, you would need to give the plugin name as well, as we've done when creating the "e2" effect. The string to use here is what is called "Plugin Label" in the `analyseplugin` output. ## Using the Configuration File Lets start FluidSynth with ALSA output and the standard SoundFont, enable LADSPA effects, load the effects.txt config file and give it a test MIDI file to play: (You will need to replace the `test.mid` with your own MIDI file and maybe change the paths to the effects.txt file and the SoundFont) ``` user@host:$ fluidsynth -a alsa -o synth.ladspa.active=1 -f effects.txt FluidR3_GM.sf2 test.mid ``` You should now hear the MIDI file played at a slightly lower volume with a one second delay effect added on both left and right channel. If not, please check the FluidSynth output for any error messages. ## Changing Parameters You probably noticed that we did not set any values for the "Delay (Seconds)" and "Dry/Wet Balance" control ports. The delay plugin specifies default values for these parameters: 1 second delay and a dry/wet balance of 0.5 (check the `analyseplugin` output above). So when you don't override them, the defaults are automatically used for rendering. Let's set different values now and set the delay time on the left channel to half a second and to 1.5 seconds on the right channel: ``` ladspa_effect e1 /usr/lib/ladspa/delay.so ladspa_link e1 Input Main:L ladspa_link e1 Output Main:L ladspa_set e1 Delay 0.5 ladspa_effect e2 /usr/lib/ladspa/delay.so ladspa_link e2 Input Main:R ladspa_link e2 Output Main:R ladspa_set e2 Delay 1.5 ladspa_start ``` Start FluidSynth again and you should hear that the delay is shorter on the left channel, longer on the right. You can even change control parameters while FluidSynth is running. Just type the `ladspa_set ...` commands into the FluidSynth shell. And to check the difference that the LADSPA effects have on the sound output, you can turn them off and on again during run-time. Just type in `ladspa_stop` and `ladspa_start` into the FluidSynth shell. ### Port Name Matching Plugin port names are sometimes very long, because the plugin writers want them to be self-documenting. But note that we didn't need to give the complete port name "Delay (Seconds)" in the `ladspa_set` commands, but chose to use a much shorter version: "Delay". When specifying a port name for the `ladspa_link` and `ladspa_set` commands, the system will look for any port that *starts with* the name you gave it. If there is only one match, then that port is chosen. If there are multiple matches (meaning your port name is ambiguous), you will see an error asking you to be more specific. So the configuration for the "e1" effect could also have been written with much shorter port names: ``` ladspa_effect e1 /usr/lib/ladspa/delay.so ladspa_link e1 In Main:L ladspa_link e1 Out Main:L ladspa_set e1 Del 0.5 ``` # Signal Flow The LADSPA effects unit runs immediately after the internal reverb and chorus effects have been processed. When no effects have been configured, the LADSPA engine is dormant and uses no additional system resources. When at least one effect is configured and the engine is activated, the rendered audio is passed into the LADSPA effects engine, the effects are run in the order that they were created and the resulting audio is passed back into FluidSynth (and from there to the sound card or other output). ## Effect Sends Please note that SoundFont designers can specify how much signal each instrument should add to the reverb and chorus effect sends. When FluidSynth renders a block of audio, all currently sounding instruments are mixed into the `Main` output channels. In addition, all instruments add their signal to the effect send ports (`Reverb:Send` and `Chorus:Send`) according to the effect send amount specified in the SoundFont. If you want to replace the internal reverb or chorus effects with a LADSPA plugin and you want to honour the decisions made by the SoundFont designer, you should use the `Reverb:Send` or `Chorus:Send` ports as effect input and `Main:L` and `Main:R` ports as effect outputs. (See the "Example Setups" section below for an example on how to replace the internal reverb with a LADSPA plugin.) Please note that FluidSynth uses a mono signal for both effects, that is why there is only a single send port for reverb and chorus. # LADSPA Command Reference The following is a description of all LADSPA-related commands that are available in the FluidSynth shell if it has been compiled with LADSPA support. - `ladspa_effect`: Create a new effect from a plugin library - `ladspa_buffer`: Create a new buffer - `ladspa_link`: Link an effect port to a host port or a buffer - `ladspa_set`: Set the value of an effect control - `ladspa_check`: Check the effect setup for any problems - `ladspa_start`: Start the effects unit - `ladspa_stop`: Stop the effects unit - `ladspa_reset`: Reset the effects unit ## ladspa_effect ``` ladspa_effect [plugin-name] [--mix [gain]] ``` Load the LADSPA plugin library given by `` and create a new effect (i.e. an instance of a plugin). `` can be chosen by the user and must unique. `` is optional if the library contains only one plugin. If the optional `--mix` parameter is given, then the LADSPA engine will call the `run_adding` interface of the plugin. This will make the effect add it's output to the output buffers instead of replacing them. The `--mix` parameter takes an optional float value `gain`, which will be multiplied with each sample before adding to the output buffers. Please note that there is no command to delete a single effect once created. To remove effects, please use `ladspa_reset` to clear everything start from scratch. Can only be called when the effect unit is not active. ## ladspa_buffer ``` ladspa_buffer ``` Create a new audio buffer called ``. The buffer is able to be used as mono output or mono input to an effect. Buffers can be used to connect plugins between each other without overwriting the host ports with temporary data. Please note that there is no command to delete a buffer. To remove buffers, please use `ladspa_reset` to clear everything and start from scratch. Can only be used when the effect unit is not active. ## ladspa_link ``` ladspa_link ``` Connects an effect input or output port with a buffer or a host port. This command can be called multiple times and will overwrite the previous connection made on that effect port. Please note that there is no command to unlink an effect port. Use `ladspa_reset` to clear everything and start from scratch. Can only be used when the effect unit is not active. ## ladspa_set ``` ladspa_set ``` Sets a control port of an effect to a float value. Can be used at any time, even when the effect unit is active. ## ladspa_check ``` ladspa_check ``` Checks the LADSPA effect configuration for errors. This command is also implicitly called when executing `ladspa_start`. ## ladspa_start ``` ladspa_start ``` Activates the effects unit and inserts the configured effects into FluidSynth's audio rendering pipeline. ## ladspa_stop ``` ladspa_stop ``` Deactivates the effects unit and removes the configured effects from FluidSynth's audio rendering pipeline. The configuration is left untouched, so it can be started again with `ladspa_start`. ## ladspa_reset ``` ladspa_reset ``` Deactivates the effects unit if active and clears all configuration and loaded plugins. # Example Setups All examples assume that your `LADSPA_PATH` environment variable points to the directory containing the plugin libraries (e.g. /usr/lib/ladspa). ## Single Plugin The following loads the delay.so plugin library from the LADSPA SDK and instantiates the delay effect under the name "e1". It connects the main left audio channel from FluidSynth with the plugin input and output and starts the effects engine. ``` ladspa_effect e1 delay.so ladspa_link e1 Input Main:L ladspa_link e1 Output Main:L ladspa_start ``` The audible effect should be an untouched right channel and a slightly lower volume on the left with a delay effect of 1 second on top. ## Replacing the FluidSynth Reverb Effect If you would like a different reverb implementation than the one built-in to FluidSynth, you can use a LADSPA reverb plugin like the "TAP Reverb" from [Tom's Audio Processing plugins](http://tap-plugins.sourceforge.net/ladspa.html). Here is the analyseplugin output for the `tap_reverb.so` plugin: ``` user@host:$ analyseplugin /usr/lib/ladspa/tap_reverb.so Plugin Name: "TAP Reverberator" Plugin Label: "tap_reverb" Plugin Unique ID: 2142 Maker: "Tom Szilagyi" Copyright: "GPL" Must Run Real-Time: No Has activate() Function: Yes Has deactivate() Function: No Has run_adding() Function: Yes Environment: Normal Ports: "Decay [ms]" input, control, 0 to 10000, default 2500 "Dry Level [dB]" input, control, -70 to 10, default 0 "Wet Level [dB]" input, control, -70 to 10, default 0 "Comb Filters" input, control, toggled, default 1 "Allpass Filters" input, control, toggled, default 1 "Bandpass Filter" input, control, toggled, default 1 "Enhanced Stereo" input, control, toggled, default 1 "Reverb Type" input, control, 0 to 42.1, default 0, integer "Input Left" input, audio "Output Left" output, audio "Input Right" input, audio "Output Right" output, audio ``` Using this information we can create a LADSPA configuration: effects.txt ``` ladspa_effect e1 /usr/lib/ladspa/tap_reverb.so ladspa_link e1 "Input Left" Reverb:Send ladspa_link e1 "Input Right" Reverb:Send ladspa_link e1 "Output Left" Main:L ladspa_link e1 "Output Right" Main:R ladspa_start ``` Start FluidSynth with the internal reverb disabled. (You will need to replace the `test.mid` with your own MIDI file and maybe change the paths to the effects.txt file and the SoundFont) ``` user@host:$ fluidsynth -a alsa -R0 -o synth.ladspa.active=1 -f effects.txt FluidR3_GM.sf2 test.mid ``` You will hear the output with a reverb effect from the plugin. And you can change the reverb control ports with the `ladspa_set` command while the MIDI file is playing. # Multi-Channel Output FluidSynth is capable of generating multi-channel output by specifying the `synth.audio-groups` and `synth.audio-channels` configuration settings. Explaining multi-channel output in detail is out of scope for this guide. But using multiple output channels has an effect on the host ports that are available to LADSPA plugins. As soon as you configure more than one audio-channel, the main audio ports will not be called "Main:L" and "Main:R" anymore, but will have indices added to their name. So if you start FluidSynth with `-o synth.audio-groups=2`, then the following ports will be created: - Main:L1 - Main:R1 - Main:L2 - Main:R2 - Reverb:Send - Chorus:Send If you want all main ports to act as outputs as well as inputs to the effects, then you also need to increase the `synth.audio-channels` setting. # LADSPA on other Platforms LADSPA is a very simple plugin architecture and only requires the ladspa.h header file as compile-time dependency. To build FluidSynth on non-Linux platform with LADSPA support, download the ladspa.h file from https://www.ladspa.org and place it somewhere in your compiler include path. Then configure and build LADSPA as you normally would. All information in the above documentation is valid for all other platforms as well. Just make sure you use the file path format specific to your platform in the `ladspa_effect` calls. For example, on Windows you should use ``` ladspa_effect c:\path\to\ladspa\plugin.dll ``` instead of ``` ladspa_effect /path/to/ladspa/plugin.so ``` Audacity provides a large number of precompiled LADSPA plugins for Windows and MacOS: https://www.audacityteam.org/download/plug-ins/ To get the `analyseplugin` and `listplugins` commands on Windows, you can either compile them yourself using the LADSPA-SDK source code from ladspa.org or install ladspa-sdk via Cygwin. fluidsynth-2.2.5/doc/polymono/000077500000000000000000000000001417326347500163465ustar00rootroot00000000000000fluidsynth-2.2.5/doc/polymono/FluidPolyMono-0004.pdf000066400000000000000000007570651417326347500222060ustar00rootroot00000000000000%PDF-1.4 %쏢 239 0 obj <> endobj xref 239 13 0000000015 00000 n 0000000577 00000 n 0000000696 00000 n 0000000864 00000 n 0000004351 00000 n 0000004373 00000 n 0000004416 00000 n 0000004499 00000 n 0000004570 00000 n 0000004633 00000 n 0000004666 00000 n 0000004722 00000 n 0000004840 00000 n trailer <<46EFD297739A7D0966097558F2DA68C8>]/Prev 248660>> startxref 0 %%EOF 240 0 obj <> endobj 241 0 obj <> /Contents 242 0 R >> endobj 242 0 obj <> stream x\r}߯ط즼fp˛ܜȱ-3)9"([")MX~/Lwc{#w% ;On~ZWJO?$&Gͤ7^>=~TUW !uߎ[vꕭu*(И3H5nٿm\hE[9L33f~=oz]Wjgb+W`4//le.b?xguאjnYͫT.efϝ=D]i ȯMfj~}NmNMduN_s؍F-Y0$:+x|!((pMU ڋb 1&r0U[$ښJO]*0Е1r߀ژQE 佱jv?0qI%nc Ƈ*ǵjC ]G<1hUcL07jmy)9׀@]ӢQ;@`y%UUeՖs]5m>[7Ԃ Vm`wy-={@iGEMڶ(j3avSZmh . Y7{ 3}S"N˨M]NH2ASPޓRk5&|vԕ-PL)>rfvy;f۪,+x)[4i#dka"}ONlM'8u>N=h3bڵIb.ɨIuhζq=g?GwiJnDrp=kA dɄMu04w'az<#oˁ>2#sYCa9>k~n&PT{Г]^$D=a!R֕($B95~~LV S9fh;QԜ,lRdld-: jXE+ILcihۆV?W$ʜa/f\c?'qMhxoзG8tGU׺^-5X/t/#IyIjR? @"%%8չ\Pe3a\cxL™k!Ext?' Dk`3)5CIZUv6 pEϤe-a )4{6Z^S= w|ٞm }FRr#iNIP;?`Y?lɥlmp=`زsPH)RQ2FH%*Lx|g@ Q$2bG$UqP wnJQOx/HɎ(D}7G˻ }`%)hL@Լ<bWA$Ht>PI2NVgɸ $>c#B-9SYmRIf228zSZ*jĢ1hĐD\{ !毴Qy+k] xtD,4 1>C*gQ~"墸8anԓBrn\> .S8p@@n$s]IbHOSdN٨mWQTrC?"IEOMr6W>j锻)uǘ1_)j9RLYl*g1HB*dFШ9Ml%qіe0lKDY7$O`FDԂ']f+XV T#{97uM 6÷͐N`*ݔ&_ @P1{)%䟰HϺDn*9XT)"!9P?wG+[qLjx2!&D]2d3Gm J{]]wƛ",>?ySaW֊0{P:7I4X^HAȸȮdRą/8dYKdb3և"1%Ҵ!1) %Zi cpRd\*s+e2ߊG2S k5oƦ,e%uEpfhyg>ൌ ~:B:L;Ȃw4s7 z"Ut5٢Z,2/i|6Ru픙 B!Te(]Q$<^W^߶vR1rD+ce-驐'ظƉ}4 M*؝#Yp(gS(fYR:z-!+Q!qDE>tY$b@}W~˂UWAs(łZMuˆB@y& zѳ9D]_u Uu2$Ttx UxKh1,2vZЇclCWR;ӗJϒRwV2kI o':pe X,}2SA]Dw~ɷ| 7t+Ii97DZ<1/)+F*4\65;Tit'W o/%vS==/~G}^{e]K 2ǸkO^dc78Xt$]4/ysIQc(1w>4Z_V EWx)Yxv?z@XAK&QQLJ _(]:keʭzX My Ghm+YNU"p2gW]k[n tG߾yoendstream endobj 243 0 obj 3413 endobj 244 0 obj <>endobj 245 0 obj <> endobj 246 0 obj <> endobj 247 0 obj <> endobj 248 0 obj <> endobj 249 0 obj <> endobj 250 0 obj <> endobj 251 0 obj <> stream `o M=VL8׉<"iĆriN,!YmSjH̐Mb5qB;#ą+jX[UcKQ#""33  "3 33 W%VeeeeeoVoVoVo%VVVVoVV]e]eVVRURI)$ Pc :G ]~,i&&*G*Hȱ,s_; (ܴ|:#U{ms#6t?-C&g)e}!'LQXП!'"p ܊{ FPNi!uo"Rb.22D hk T endstream endobj 1 0 obj <> /Contents 2 0 R >> endobj 2 0 obj <> stream x]_8OQq/[1%Ҽl003,4;K,D1 ;=A;ɶ..DeI鴔?e*"#-27{﯎7H=jL,eZ+(0TǿsÊǎ,br%VO Zkɗcwճ,֒dZY j?0+|Il}?TjY(Jb/It%僕f?IF+'-ώluG_%oa"u/ BARy?>ۑlD酐vqiuu߳2+XuvYN'bX~DJo'tG܈eZMR(.W^hFN²uYǂab"u!urj'/buP܆YC.y߇gY&L<ɩS;q2̬e~I[J2!4@GT@ 3ECmHOb X 2]e3)_ZXq.iWS ᱝ׫%Y1mdLgD+p:RW+([. Ќe xG8#C'JK>G (H4}Fci :#QINͤsW,ġcMoeZN2% &:ʲI]'AuPSl,R_"Uh53{O,BRt>I~yh4?Dx'8L/.8at$=Usj3~JZntա'-&nzo5S&)?vS, 7΍|E%:L1 4LD-2oUsM}c譙 |I^U*01)0_uE|mGgAvScunjajFẦ/ʭO0,HZ9QG\428ϩɴjQRss ewA5QCz$}頖JGCs@FuHQ]kQ3Xb$0 0NTDv)de_IXxG5jqx?x5)ᱜ_:@vfqK4GЮiRV}iV`xa5@؅%VE!7,OС0 îBq W d MNc$7י$z)Wuy͍KZ@PJxԤV=F \ITTUHOᙊ GxV*}Uk(DI|1Ro\ZJc@= i;Q I.ն.PoRP{ZM}IG"őkZGZuYى5Wޤuo|5١~c05B3H%aXn1F icW~9vI:ِ/T ]@Qr~J>Z<9r{/LtY'W Z-ЗIM[0mK@$S4Dӌ<3Jm SrL]4;D ty͖f&Cl`> @$b|0ɛ }P2O.Qx٩죍NVx)eD?5VTMpw@E(@A j ص0/RCa7af}a|·M@:XC8Z(blf56!M{{?@㞕eosp}.="k m>͒HfƉN_Fpvas)_7n CjaLq0Α3NmPmexD 5:5'YˍKIJBC'aJHkI0YV, )N\4p>nJ +;4zpk&ze,0Dm3DAͯw>.zɊrpAK* F##lmȝ>Ga3-C*2WDZc؝vex}v\jW$Ms9w>ojgV"p!y0t7)Ћ (58SL@DGX'H$4I3|+-rOR'} DdbOSRS+oyMG@>^tnjGKܣ^곉fG2'INI$=FZzdm+O?P$O*b.ͽ63.b<D6ꯉ F,m[;ycO'@qk>ئxyڽZ]}[5TWNl2ɁO߭\:O\5/n47iϖ-Pؒ$9/̬rC!4t.gw2ǥ-Sp}{c􎡢r"L9 !PsyM5af餝Q!R5[=5¨&:G5uz|= /J_6 %><;1Vah8"šL 7['|s&n,[B^#'.^c-ȝY!-lΞ^Ͱʚ5J{pbv -F࡯/Ki*yCRQ3|s$|{-m2` \\a&9^rJZrJ#y@t\ }68q@cb 7t~ 6_+qG!<ɬ4HOx“JHAvxK;`"D,FV߂Ǭ7]-a:mő~5ʸUɃBZr I onZJXwj"}Rs:ӂo*C]+ tr+Z㶾`D_"gfLi%lPٓG/kUӣٓ{ pLμ;;Ir Ĉ~7̒(Yʷ Qʥw0 %ZI_u~~T+@\iR]4(*2x1ryv4(!7(ZHItjt^ogB!gPQ0EJ:!n &=/@%>P3Y3K iD1c WEeO|>7ƈJ[x^g(Dn$+i_ =x/F:'3Dg* 5Fͺ>})8#zp6^ޟ$m XU·=LNj2y@ۉʃo:I]=A*8g-n{m\ܠk땶-`A?i~b+T5-96>G]bi Ʌ3Q>\ " [ùB0%=b\HZ;5E y HwX$%: el Aΰ0Kf[p'b1&#GkcPJ#0Ę-0ͭqQ1 &ΠddN B&-$=H !J6EUZ,Lah okv F8_洰xT"eIf5wQid,OeC%P~N@Gʪ}Xa˟A'쏔zc[xK"Aa+` L'@C;@4!g|F(j?LL:"sl1B`H~n\Kfp* dҮur3U{]{Yl2"^Э6Y;׃*YsA4?{Rٳt[XlˆIq-Y|.Py`apxRx()P,=I? HQf#<'X0oX*[LsR{OS'/ï}>o=V#YMzdzZ-53hZU=Bs Q?N=QgñG$M{1)}@nTu8mS|g^8gh=Y=1*6U$#NY*<2o0j,T}L +9 (tgtR8h G"<:pUɤ54S2g 3u>;n{bDGM n-^ Ǽ:bNQ*ĆZ7FBSx(_zk| a/Jx60ҙ3PD\7LW7rZhJ%lVibR7gVߵ;}h5i7!mI8n%R< 5p waPqSзom3i;뎉ΛNHٻ? BHgt}I'`c[''ɑw [f%~/U֦ovx w1vB i;o; w|g3i~u:#tqM qƝ kw+}طGPmhMc!瑼SJ/􇕆¥|ᗮ]E ǿV"R1DHxR7rEhe:^5up7ZXu<#O$^gW S2:n> endobj 8 0 obj <> endobj 9 0 obj <> /Contents 10 0 R >> endobj 10 0 obj <> stream x]ٲ6SJyg}T$8)WkI1J,ŋ/V.֊Pğ!)o|ס,KZT(C%YH\%t2|qݕ/Wpk8R.酿ե!.DzP„awq& {`Q_]gZ*U-Q1 _;#W|/"VkG|9"c/8UX.VL-[7ڟߓtͿf4,XqES~Zff;ܥJΊaCIw+TU=#^hP;;,7ʷ?1q9JSxlhy>$Ÿ Vc&3fx|p.SiBY R d+E&׵p<]୮:SkVw$~Ι\Ʌs5Ez PCZ_P0-ᔋ?<\x0~Ib6fıjzz3581{/>S֛sg˚~GaCHtD(Y]pApXgl. 7 g}ko,I=V-:jO D\(}1 Vgbl5;_Cm$u͇%|)2)>VP*bgv!00k# Iy-¦}l7~E } XmL`AxKhiGځ62Hc8˿0JaI'.{,/Yo%dЦH T~29$fwt]J\Ay'V( pCWl'jUPv">-/e{Y{ѱEmP1.T =8Q!MTوjr)NY R G'X`; vπmn+iUooQ.VɰV "Rlw!p kD‰J7@ Hi"U6f┬h 5QM(/L5Av0@i="ƾdMԒ⽗{ U_Sa_I/+j`TzTcRD} @Ȝxkfv@G2Ur)nsq) Y$ sf!M/H3q=} uT_G(`E׽] 7J\{l&송xQ ?^}AUԬy$o:[bQch <$`qӏyjŀ{UAr(g›'1d Hy{`S(musC@ťAZ_$$N8V@ʸ 4{`,Ά~sJQ\buJؕC/S]T3d cFk;f6??; cx]G>C wwÇT&Ä\Kx Ԋ }kdHeG^:K=0W'fƊTg/Z{L4lvr[{"ަ-rE}' vE5FʱgIM}z>!i)6+9ֹuTUk"lÈO*cEaRO3~4 , V-=$-R+u>ݹ?)ScrLI9_;9D>ޒF1I/ v!@v)

}?oc"e&)N&mB}Fɉ!Hj-K`% ܂KMȿdx `+` |D{ vKF,:vrm:i/a1lN4xHqdC?,nɇϑڠh[H'+8]0dq@ ~*߫;9FRͽ(oTZKf'y;;}He6Fȩ"V9[XԚX͟XM8cpb;`VCi[P '0U"+LEqH (e[HjH箇89FP;"ݞVlxWqH*#wp.ˠZ`l)Fm%Xpܷyq).eX\P5_{.N35t k?wegӝ;YkD%j+eRK-ش|:>sx[&ʍQB>4xh^b~މ5݅SpR]Y|Q '?˶ʆuDŨ3G̴FJQ}?``UoEA4wof9O58=~r Xh|;:on =lEKG;0eե-)ΰ ?ɉ5= "8+K9#8mhd .Q:݂]gϵ 5ޅ̜٤#~ ޳8f,g:@qŸF8vf\HcXfC 8Ԝ=rLsΚVmS)Sdef{~(4=9NnZoנWhE:w>xSح!mm¡& Uy?2[<c5/j-ۿ 9Pz1LŤJS=xsSI?e(ĔsCS*;&7aS, nE燇u'dfTam@j}ӝi=Ng2SE-5皚Keø6՚v l\V4]x j_3Cz~smNη"n)XUv .@N>yV`3yɓ,ytnbx9??%U<^*3cQh~!l^=o"\經-dv3|qȓ&f.nUxas2Dk!2,R8&WDŽ|Kk8lm]НA8Q;g eA9;ĉ.۬EW~2b}һH}zۧ2G0 8A Q!` #zg_1? 3{g9G0aCa=8{gLJʰ B%eTk{!KPa$:#O{:df{P7Ӊf;(5t8'S%x:xT*dX* ᧅȼ2*Nu[ZN h-Yt)yoU6ҎYi-m_>,T,4MZYgx#;7nNpEtE8%{= C)~m(2~niJΕ'2IqJoFs poХ#+E.ͮd)ʂ)*Us3D8EH^Xj) җ5]g)腈) }KGW}`%KA$;%[y|z5u\~L"S}__zybڮlob#1 oI!x/чz큃P mau'=@ʇuJmx*<%S],D \ a0UpS J:>ee2')Nv:}'*3yCEs&%e;(im:9Z&u4?$.u=Ox95V ZY`t_uq{X] |H(rC4ڻ;:eqJf~ӊm(2cV~<dXMU>m%<9O֜V<y,=,rk;V+d4OR,_|t◕D:ԁ%Tع8Kߥ=KB Wܔ)x8, ւE̮C%N@B"E` ~) >^PQ0?T D3W:_Y?xh┌l毳8O&]U#ODx*8$œnKJ't?Rx]o XXoVP>FÃA`=ܹļq0Mwr̊~ 0% I:>_j1ou#NXݓ4 @idS_T446Ƌu*Wfol3H8E$@x" y7mK`c호x80DcR ֎xÖafh{&'Nɘ^?jk:4(?QF&>)cCƆΪ'$Y0U^0G "1,~jzهyCv>%YSb(@@U޺c}"?t>"?"1k(=:.XD :sRx/ZRP^dZR k5ymΑ> endobj 13 0 obj <> endobj 14 0 obj <> /Contents 15 0 R >> endobj 15 0 obj <> stream x]ے+(V\~'_vS)vf(,@S4dY"@`>,BT˲./^`7ry ݣW_\GEoe4*~R¨e-Lr۵(RqW*Zi a%W_7j ZeY]otQZpka}qӾGZԫHRU%V ۵.lrm_T**+{TˋnPGVdS2}>Rk݂*B«..nAuAٺLU8 zݯEU8)A|߷ڮvFGTHK'Z8X[+Ëj7䏨_3%iH9v\l{l|M 馭Fet#-F[za 4sae#0ʍ2}Eb|GN$fP૯W*L!E} ?qDa/,֕ۏ݄ݸ3e߃NlVGlcZ)!(rH ~V AGn#j7Py%޾}Gz%_I[LD/upP+E`7s~ t WImjvwiw7X(Gl1=AzX'ĺ8&dנF7q܊!hhH^7-n;n^nq2 pLǃMXAaD iRchҍc1.zQhRfzyZ7LJ/J չD PL#D"D_r_a%~$"q *g sbN1,.Y\zNL*O"~i!{ﮍo:7P~KޯЌÛ:g_7+KӈxMuMSꬵqKgQ,W J>4p*hzק6 Y#(hewɉO0NTB Nã$"Vi]$Lp>Q:_N6hK&tcܱ?[Fz>2?u% J\g= 1"Ą;b9w_koEqw _^0/"ξ¶@‹IpnɃudքc|ˮe~\qp`nT>$4uE2v-MEYvƍeMw~XoC,oᰢ)yP+w1QuV\<dW0ğ PX w kI֒5Xʊڇeίޥs"]!/Vt]hظCFq~Nqsy17ݜ!` GVY_E:tQ^nRN7)A4.%v8|;<7 rĈ[η@GxKxt8CyzdT_'HsOϕI*oPPqr#ĴG<O{B $`5DSTJZp19fޯƿPwՎhFH0 [℠A B/'XF"8>Jtbb32`L>L~$0tRwBbArAJCTEeB B>1Ԍ#ޣ#,S@'fTI# /Bsl)w ]7E]'ob"!$zQT*6dIECtxH426!64$9A[̙]B09r-'ىP5`Haf<Լ1!y@ds+fe 2OJun| ^FT2 .rG,UL' 9#>Y="b<L4hy ]v38]MősR`Tx)U$%dPBٓ6'%Fb)C"9P!:C{njWI?>9gC1r< v ɣ`szHHt?Bg0=GzbYΘI$aYpVox uj5s;h%F EPEX `)XLw,J,Wg͎59WAא4kaS>ey5\!onT[SR$C571iighLK5dH}%υdO#iX(=5L|s8@Iv9FUzqt,TwBy}7TSz!P9,򹂗!"߆V#bSbJ}aܤ@eѡ0M[V׳B΍C#L lTםխQo=iLwshMZ/@,ܒiImu7\lnz|:>"l 2O\I!xe 9v "OWrxg .Lנ^pL>{2jU͗.e'دW@k8Ŏ]Va ל,u&&HA}'y&8)4<J|zlU˨eEGph*\2cqRVL+"YCpP˓jk8>zZ}9dž\;yF!Jcp㑍O}nrn؍# =?0ZpQ7d&/[xH.1_03Pƶ}b@ B]uYfj8?R*Hda\>7%?ٓiLRfA0H?\DZ:`:A9cH4`]d]X GU%:}kd"oqH3]:L!$kZDǺ"p6Ad%Zy,5vDZwlP3&8 %fi$tB8_yLjª$0*Ireq@0IFfZP+n[>0e09xSZiЃሽb"]1AhBt 8 $Lr)5a+"@hO:3/ ($ؿ,Vvg BCsec6 ,`{ {)v{LU-iKQͳi_,B>PNBj*E`r6)-̋-/8p=rʉ(|;,tpeלGh(c^u8ԋ v~>L0!7mY69ۍW֒c' X{K,ᘯ']y6{&RT&'M?@'͈uU+Gΰ|q>*%K#jRqYYR0v(L/ǛGidPasU; B ;;x{cTl#؎qbHPeQϙGy + fC5ɩ a'7S,ݪ;wy>S`Y;rDdC)3ہHFpT{HóѠCan66@UpCEgEଈiceS^_5N~jElD0N99OLDp\v%')`J30ԭD@χꢲD8Y4rG]h0RƧendstream endobj 16 0 obj 4484 endobj 17 0 obj <> endobj 18 0 obj <> endobj 20 0 obj <> endobj 21 0 obj <> endobj 232 0 obj <>stream xW}pT?܄Ҭ[HBHdwMcMH]ԲL$H!jU&mhU|KPt(Lv3BvXTcMRb[ۙ3ν~R! nl(.xE:E۹4wmG35{& {OsRq5mkԾӒHL+m,3͑OPE6w9);"Z 7vVزwnhիVq߶h)!i /G -G`bL‡0[? ~YMdo"! 5c#bIvҶ.6 ފx|d; oXwQ]plGYi%]4_;W qdcEg ;%7` a-vLv yXT͠ h߇ *wȰ >|RuK~RV~dXY>)[rA2 y]#9ELctT "4BX4QzCVoUm rq׈|9ՌŶf_}b*C! ZTXtӨ _n[9:>XcpK]F(,5f03HK^\i5ݪѨ'5ELY YEkĚȍҌcEByNC'S5q< V.[u3)j--n/(*2FS74ePBink{=D 3Zm\S\2YزL_aYm#`}M7_3{m`nv:>>=!y`~$zC4~:p,28m2tKN_x"=.jCTs sZUXHԘZr `]|l͟1aћIr1I\234O>Ȯ=\]J&< 9A zVS-FPU DP E$ Bύg  2E  g O' ϋ􌠽 eT&H$ b!~a*aapGR]r,bFQ;5^w5jUߢWWj4]BLrl-KԜZ%ih4zx*1`h@n~` bR*S6* @2,+0v8c ćpYã88B]WΔV8ąR9@jiHqK9xJooN[Tn<(vvV-vlq>限B/Te*ZV2I`Ҭϸ)gbΠSNQ甤IWDM|<ƏFS*Kpg3Q .CzԦkB&B' Vq[ aŸE?^gN p M_XV/9{po\KzO-f[u3k endstream endobj 22 0 obj <> /Contents 23 0 R >> endobj 23 0 obj <> stream x\Yr)oRa҇{!(&4ET#c9Q Hj?@cG./ԯrX8ys'7{}s^z7/i;n젩0[[=k9x954Pf lҵpVB9N?T 7M;Zq?Vղ36.C*~aZΛ~ Tj^KkEG}ejuT9)_@YK'f ;9;<=pN)쐜0pHϥTcVrj>BiYw%jL-^Kun'CQ7dB_U ,icK VXaؔY*ɶb,5ʻ~P!uK}Z _m ~RTZY~'$&mٶ[-ӾkBx: u3J4˃-ZJgn> hw}o:UbA8S눵o5P'XJ[dJYvN HCɄ<;R6a.~}.Ӓ̂}sSDpmdl;ȖdmR<N ZJz/x Au/tvC('qim /q_2HTZc@)pmX:0MZ<'Ue.{w΋"ku]3d2Y5MY+ųkrdp#NSiɝ7=Qr7G#8ehuMc)\3L9!S삧2~B.eZt3e,͐N=Gt;-S%9H;aXQcvZ/ar涕 עvʯ WeN_7 #!لTք=sp^q\WfkvXoX4uZ'NިB 9*_8 4LPU7M͐Pڬb!Fq5F* H70D/|@Hⰳ0,Q(ZFwA8EWq?) {L.RíJHx;p[[{22!]$ҶaXu8~ņh ¯ ۇEZ?u33&T6ύսsZ(8Ŗд6mlQa=BN}Nx\EI ו†YmgelTRq64 e=-ۡl+23'QC.:D';&v҇tZzR C"!^GJSF c6ǿ+a8@?ޒ9R% Sf 8``/`<8$np{,5d/P|%DЈ zKE 9'et\̀7iS4;`|=^0a 0 +rho@h52~xqHz y#3K/uY1 OHTgYŠ0"?*7((Rt5_q S`XYL_Fŷ0;Ӥ"t{v|1'vW '$X7߆N*jo"YzH+N nBg ]z7K4r6~#vLNؿnX[?]dh0P:Ǔ {ӴWkùh% "x2Z1OE6Ff>Y, 0IX'yE_jn[;VdAA؂}&0C0Ďy\LjgW =I8 b6C<(Nn{$QqMEFFz3Ph^qq.VzAj@.4 ctƺfSS75]~SţLX)JjOz9/YS\UK)#a05|Y{XԺ.ǐ(=!R:H27ˎ7X:q!cR@B:CVBEyLZ2ߗG7`a%5SOs  kԝ%ѓ?mm ޥ[ hEx4 s-Hz Km lt ௄3KZQ #"g 7ʲ#6,⑘T0Xs{?V!䑸)CmFGky6DNs+ʜL V[d;[g%~_-,µj`ǔ1dߥR,!KCD ^(֤:}CNz:4,,.'F&y0}feu<&J0xQ[ I녚脥.`VI_8ϼ-)O-s3yg\s^Hm2!ػ]KQjdKYaeZ_|w Xt{L^A穒1.ׅ%/yKC2}iI_L2߃w1Fu ).,~KThF4C^I"D}V,3QbiI< 眩[ Ήq*sae`ۺRУpɋ́ݛ S'UW|$t['7j3U^ d ΝOAKAg+qKk9ty Zt-IBֽ%s) L"kXHşRH7\ =RNۥ61"}yl?,?\#{C 4s :S^PRDb,1tbcigaRD#1@~p%v>eJj> !sŖz$\}(LtOWq$f;~2RtWFX4߶2#PLȋW)H0ݎ]FF֒JJr|Lp)q*4 NLJo#9>JXa_m[gđO\8.ޑv"Nb"r^p[Ϫeҕ;hXxnxg's6}̌h0L(en7o'Kb8D4F9d&cyJw9Iy [|*tM1GEѷ^Vuz]3G<웡5 q3Lv4n'Tv]4N&09ֶF?qU$ \?1rɗ7<#B 0F#);gӷ&5/ oKFeDb7c*T*$=lLFKC2 hA2LT,i %4К0ݟIE;N$pA=ӛvck!Mo+-lء IK)*X:g:RB!k(&Z9URfSjCm6LqX rmqLLeg|Qk F m% K+V#׎5;# =TZ1%[-لlWwIdcflJQ hwT[3|y,u:J)xz q'tRu~svUZ(ɮ+焔ٜd?u] m*y: /ӓO BSH_H0>RM|q/{|;#OI*Ϻ. }!Gh=S0..=qsNVܓSoKڔi "=QM9Ϟ$/]3u#Jmj7o*[ Ma> v'1sB3{Ӗ?m}+FfLhXN|usKo؍ƴbgc?Tpue3en隦א> endobj 27 0 obj <> endobj 28 0 obj <> /Contents 29 0 R >> endobj 29 0 obj <> stream x\Ys~ׯ78HsSv;JRTJ;ٕd7/ @7pF |jTw~iWw'~VBUR4fuxvЈM3ґY9ى[;x]*jQ!mot^amuXZCqzIZ3Y-P}hſF:pjB֕dOy ~W[u*\\juxrPuuRH2Y#k t4]iݴU ٵ$[R]5BX}lu`ih2TC6HSJ[J8 .S,\F'Xxi-,wG:p7؂й׍ֵHM8:B*^ydJ%~P,]ƺWtVi$K۟Q^k׮z_ea+>״{b(Ujok ic tVvu{zpYʌ|q{~^뜄iƲ{Q6Βz,(Cϲ\{5O-j,$ͧ~@+L|㬢gV6!&V<_$s NT u4e ={s?aúYk2 ØcnCxwbr8qQVi*uz}3n\X)kE״$58hO&L,/g`_Ų]9p:OʙqD+KVQA_t"ONFM!pXqR)ށDEq&*&Vz{3ډ? gpp';$56S\4u`@c5 IY hF,|`U)g<JC@<Ċ("&AM6: U`0+4S&"I()Y2N`Cr>Au!]-r^_enyI4l!`Lgͪ`158oc^ BGS fj+ۜPcbql%( WC>77ⲵ>E2([הg`JD'.k8^j{XT,WXHlNR^$AfbC-+sPqtopƘcQ^O樐7=e2kʪבϰpE5pʨmXgY:`b45.da-f[0Y`6iP/^c\?#XfE >1+ݮejxC2OJ7]N63bq8W[/frƁk:l~x.W=07$n do0@K#̦8ԁW}}37t{kفqm-t#+,ry8A#MrJ4}%DCc^tȃ5|78ePu<PlK I%G$/"NSE;6zBFvC;^ mQ^t܎A̾tbnTF&zhn/0r; .bZlE,C4Bp&)?'^]ZzVnXgsm~_e#$g*R*˳̸WyM%L%Wu4?͝# n8^{Vo,!Wm0 ~{wd{fp >%VSXΪ̢eCoC4O W=v-L;KدtfEli4p ^qfCACs\Sc$?aH1_vR,d,4wwg#ә:d^f7@WxVhϢq=&J C]7/^f/a']7$)N/kWnkpŤQd"l`יPlY)d_e̫< a`Aiڕ?5dc6XjFIf]lY܆)`y}Bj;; "džR(z>r1%f@|^eE(%9}b>2D:RVVjypiHmxl6Y8OՇzô.upAB&0*qxENS9{:O`Ϸ0#<ك )"o5:i4%( \%7wj&iǎ )DQ(MhdATDڲ7Kw͡Ǽo7eH=).3cG_&ϒ 9ӻ?#~‘N[>S8m>b ﰰc m=Koh6d(j%K>O-@h|ZjAd1G'@s],OodV`呍Gėь/3<ֶʉQYfCdO">@p=.y wR7x 'NӈܐBeuFeӑE h,q%NH ?;=oyOau>̤>ʀ؃ RB1 [\VLϘ,w6!:4[{T99|kg$dȬ\33b!}d;Ej:a Y:endstream endobj 30 0 obj 4638 endobj 31 0 obj <> endobj 32 0 obj <> endobj 33 0 obj <> /Contents 34 0 R >> endobj 34 0 obj <> stream x\Ys~ׯ[fR;q!8ljZ\Waid24HI63l%p4F׍Fs-JMQ\8:?|qqPe_'Wg5Zjƕ]T]t8:xR+`PnS7.\9彯Xe_.W*}h6ץ4tX*Σâl R%ז ---e|j7?90Qb1/ |%1jʫŸ ue&YUh0g#drU|zqj b/~$5JgXzב&Z>++Jj.UCUtUUKcn^S%+~y D4n8YGM԰_/+jg]1pE#XQ/26,&+ms?Y¢jN$uLHuEH`>VJnJREABz%HŜ1F9"1캓 <E2P+[3(6K&a]Kjڏґ;+-O(jhlE3]Lѵm`*F` oP-)Cѹ6ߑTDh$ՐN+?X, [ 귣$73%`h)K4TwNnPZGVҷQ b 5:dgQOe_/[UxaP(_ZxiXQys_cy8Vc=`\2jTiC~$ѕnh:`í%u4N=l=Gl?_=b$Z@b(@MuxsNSVib;غƉ]C=as "kܩRDFi;(_ۼ36ɉfBL@$T{ {ԭ83ݙ d;$TpTBepzJЏQ5Ȗߣ$u9f*_ a ދh*p|*`5uw_wpIp::k/Gv۵+7S`~ }h&ï Fwumpuʻ n_DS }ӻuu}+Le^ѻw|_m_""Nn*t<͞T8]o\]2tSHJa설a+z C=LNr|^Nx<`tW^xWtaKX<7x^bg|\IBmcs!;bg`:gZ6(A8|>U9CO=EoO882̑[Mu|욝xog@Q՛9WfN;k;Jm6)6ZJ߱Cj=򘡲*V3\>D 0gHݏ$͐"=B!4+>-etz Bkݧ)UΆD)5KJ5eg!X81H|tާ"]S@7 iI=!Iaiqم[{lv>W)/R1cv$U]g?/uę$^ Kņs{s&BtWebvZ}68';! I$,3Ql2][9sY]nnsJ u&GkOM/0RHkηHQ^"vbF֣x1se3ƣA> νΤ8b~ٺL Ґ#ce헖C8Gʊ`|4I;zU/^ $D 1c* (2xBQ ^jlHRm $ `j{ϡ<%r\å y#@wZR NH p'T'zMs$%ARVWc=w)!8e  `mwJb 3Z#r0o`)i7/ =%qDk8T'&]'ǧI`Suc[sI|&?g鉩K&2BM%%!ַ@J/#ڗ~~~of(ɚyT2a Iy|s/y?^|zVG=ݥo-*C{$ ,_b)6#Q60 QݘXa$D)I* 1;7mgC) y~IobNKgq:bNqc5xvte>lZ+43l灝MDQ[E?/38ȋʌOs<c"a_(2_dU&eZelONX 3,U ]x̝}T!>HavwLwpK4"k0 J3[J e]VЅ~8EO;A\h(xJO!ګ=?p[Ďqn8~kS"I S#9̆oF"zM|Uzw}JoLL|'|c^KU{?l*_kvjendstream endobj 35 0 obj 3242 endobj 36 0 obj <> endobj 37 0 obj <> endobj 38 0 obj <> /Contents 39 0 R >> endobj 39 0 obj <> stream xr}b߼ƒk!C)9R)"EJdbH&{i ЍAw\-#KK0Fc7Byo{GLFw^\O؈ʁ=FgI"f芰v62w`14?p`NOU?utk;,lTT5XL J(m;nޗhz`##!Э3ii*R d׻G>Pɮ"ʃ9mڍLE+𪼽M&42gaw7کO}T1"Fxo&|X' Oq = Ï3\k *NOқx,ocl1'%flC%SRvzZlD1X4哰tۄ gS]4yX$7%W)|?qΣ'mSQܒ0 ? folt?xq?ݻҰz1hhvp{)U6|kL >M'dhwc;APJU3#!4c%su1:t s> Z~X,\OA=F+1S2 uSˠ$ueN_9K]7Mɪ#VIkJL ' o\nI]kLUmKuSAb b[#&Cp4x2Vucȑ,&NYwHBU~mPYDb#rq01`Ge2$FloE*ȕKVrɗ&ۿtuzOROj|k[R>r\c"u.4Uϼ Y5K 9ad?m: } X<:Di{:/%G ҂ 1<(=U!yBVSׯ]Ɇri.AE'8'v~DaOh6kB҈>Š\_Yor"U$lטO؅;^D6UbPT\gKR_eˣQN/zErY>z5"L)HU)Kcpi76Vc pu֖6vYG4LvSc7vk[jZkMgp Ni0iJ #f}f£Xvߞ^=`q3g/U^ m.~q")oؚjГ=:Zk/ufE|{g/M:6ePp{xJxg6S4.ECS'-鷰 <~ ?@9[wEԁ޺f-6"KS7fJtM** {']-:8*0xEN2./8#?s?&n9=B OhQ2geULJ5熩;ًuk͆^4A-̀ƸVz^=B*bLBpCGЂfvkendstream endobj 40 0 obj 3624 endobj 41 0 obj <> endobj 42 0 obj <> endobj 43 0 obj <> /Contents 44 0 R >> endobj 44 0 obj <> stream x\[~?bsg 0EDh:zf\&!zR}TH|f!׋OWF'ͣ#)[a ƺ֙&HZ]._~-Tv,2ڴ{'+ ͅoVk $Y^Iߪh"dS?$dSK)m? #7+]~J˅U^6kojN˸:b=xZV2㒺bRg%umA.\Qҵ"kRQ.,?Y4|F P^̻27+)Z}ק|$>u_֡|[*JZՐbmuJf踼NÐ贤Ja[o ltZtm3s@?+}y ~tZV !L؉:.+&K1аBBta=l w캎T:w'3/IA>瘝KYI>cS/g?cDS^-_', OXNVљ1h2.SAi{sCZcemPxn]C_?,Q9j=PaPޠOأWze񚇓3A'|8|{V+:-QYoPjg)h| ԯ,5Qؚ?{i];[W0=L0"W?'rN. @vHs g.E$"fI=ʍ3!OF?49Ł=b!\7Y"|R>u}]DG[S&a`ιTU-OZ˥Sμ]#QTsCDQBR+[I?RfJ抑MlI(ak)]iӠrva@ZzFn9r8ׯEr4uidp~sXIٻfg<C-&{aRzՒ|IqcIrY 4-`L ffEqCavʷ$ϰIx :9ElJXxԨ܃nsYjװV0aVrH9i\"hMh{vSۊ#iq!6%$;WȄB=)5^c8 1OXajPmƨ @\/;9<>:dQEco+AyGoOh*1z(E `-_0ĉ8Ry{/z~p1fd >cvƮL;\a?tCi~B38ǿ,6ץ9"czHz0S((]|U쒵2b|I>yy@%[wR#|RjgἔH@:lc#oIZcVDjJ![HYsOՂw>GGMDfff|I2I#BgK+9Dc'qecݟN 4M4NJ|HARH^(C>DlLKH.QE*O _`^`wK$bST'S[v3:fN].3)lU}w'@rjZ3 ivtA )#Ą\MDСA}DɐO$͸"G ?%Rgl9K9̓iC;zdʅɟWb %)n,n#?i/]>3Z}ZF \툖cP9W{ɝ:q^s^ݬ}Q5=<&?N \sqbc92=XSȮ9zvI$<;Jh I~ y +\`pq'S7䉔}e0k{'v #Y,٣;Mv}26 tsMǿ9m-7X;)i3E7[8RDCm/ IG7 hCXYpEpݰ;EQL;= > O Tُ Á]lRs Š{3Cz/5%mAͨi=swR~Z)&q)?CW=ƼfN0O7ЫPm-z^6t6ln~br*VRK.c)-$ad'׈!x%wF:诣FJ>WUÔCew|S1F; s*7K'nQ<Tɬg!I;rü)W V8)2u:ݝeJĶ^.:(nYw/Cnu_1x6+}qb\؋+Fac>j$?Zf컙~!i_R*lL93L.J$hL|zAܰeg([a!^tֿV!KגɺefU $r&;@ yWB#>:qYdȖ48q 2jYċ0jѱQ;PV;gp3.ۭJNTPǎ>ۇR7HSw'й3^aԢu0n?cyD/7sgi@njc "^YׯjeЎ51+)xבDS&5>~3r8٥ŃdF)yGH7GUR_!B E*T i+Ayil6=Eb2 ŞgEKB0.bSS̽1bc q+qWSȥ c&9^x]T,eS|VD#t+:rq /&Ӈ여f{?@J)уQϢ&r7Dk㲼*$sIu]7^YT?XPBCJWA \}ZDbCq|T֎-&1;>PD^>-z &A:9 D@SP;6jaG)d_q˾7uYr@ܼ_b ayQ8#=ݮ:"c\ݧ'B4p;µJ旅j\s5#߱TLke_k۩*5"nR=k,,knsEO;4d'C=FȘ4i2$$g۽\_'XcN*d 7 e: \kRdtEC:Vf`6:j_ 3 m@ dȺ Y ĄBj0y}CyjT_Kq.샡/II9v7ߗYÊtn-btpϋnaJ\X όR2<0e_A{x*դ, VN DKٰ,:$iie1aqelL9ttY0<6߷o"endstream endobj 45 0 obj 4608 endobj 46 0 obj <> endobj 47 0 obj <> endobj 48 0 obj <> /Contents 49 0 R >> endobj 49 0 obj <> stream xn}bq߇<V|yN^&+yog*.AZ k=fڨ6:j"cuXb;6@B96/{l ^,Up_k[7t*ib=!p ƣnqԫ z}oq5=lucKx 踾8)kdxfny}\B`,@R^9C g=lE', xJAp GH_1֤9 A 5i7{ T /^H2j\Bb:4T7wtru|498zrWo'4$&16V٩m1cmsW HoC Zpa,~U1nP`H쳱VKe[dר.J'yU:a `;CK.3J6|Q>t:* $[V{Z%\kX4A^' 0 #]t F +I6KP&2o6@c+5ȉ݅JQĨ2 )c"aj zwXlEUiَP+Aʍ.ZO R ޑ~|}g}6aKL/UZ_?C޾<7 C"d? (b^!WƧO?t2AuWޖV':I7kYB^BV1=DdΪy"=@tFzlqZ-{l]MڪX$7Ht(C!2x\Rp{(}-<ɶGzvl!}<̔x#UMYUJnuuDD22O8l4T&eT<}3=afC͐glJN2:*s ᓜQűNzݲY/2ñO6CK?c74יʙU^aP$hc|mH9Xrj`އ&:K: QD[R%s.Vq+Lj#> : 0(5_2{xHtG<1fY^hS^ ]ke}N!|eJ` .j"jWXqa"k2}!4I4\%Aƻ{bĐ }=Q?+dRH F,ObzCh>/27Đ*BYTԣlݘ 9E+n]Νbo,'3G!"6kߦ?SF-|{&qBjC68ҥTLK FltÃ| eD'P[=>)*}ʽt&\w)Z< O8byƾgc ɦ{Զ"3z$8\ *6ה 蹒&4w)E=ZP{d1Dμc|+YI: ѯaQ9F/¬0$})/؎10\L<S1-8'v '˽A?}̡:X>OQ'y_x_s:ҫ~ y1. Ki֍MKe+RZuFt UzC-]wY ,Ř4Xןo*CUI'\ 襾+ d#iኄn4L@唛E_ k@R%WZ̥CevTـUz+r]@Hd' c=U9֣e^r}U"%h->ՃMpNu6SКPjv" Y_-ogJ'%eu(iq nu@ Eu Db]?t:] kw m/H418.HeINQBu`}'v L'{vp!ˊ9U?\pf -w‹Z&0T@X* r?(z8u>0JRC[1-q>tr8Ta?Kp'[9+2| _G{HwTp ţxU.{s/)ɗrwK[zHL@=$L;+9uZet qE< OR.:a2KMc%ЪO; ,i`X90[ַ(C꘹TYZ9*, Rvz}ѿ_ҋ63+F0_ 4.J;Th&?R`; o cvr{'L/p%Xçէj9!RjtmoX!‘C}R=Xg˃=wqe6kEpϤs:Y/8pk{L@n^!Qq*;fGTE8YU͒KGAb\`>X:S: EOޜeo-t+-)c f-׸םס66}[LN*~Y=95Z\0qMX>+`)wd0zEfVi*?qr[I(6c#A-V_}#3XׁdQ(g:]Fe6u-X\} h\db.h2%Oˇ+*mWr%N} غƕ)C+f:XJX3z':z:x`\H崽 dP(LLe[`.(T9Sr ?'}ջ[H?.3 Pѹ7'wu]4 $*a*.⢚Mdyf'jJ}3` ,`9kb> n˟7p:[.1IF ݟQJUMEu>єIχz>|MO0V4n0~62tK_F|%?JTw|%C=op|M&L?[}a*sV^mbgOC  4Qr]DJt"/cd5:eҐ-mM544Z&&e15g9" KcKeM@xGe]?> endobj 52 0 obj <> endobj 53 0 obj <> /Contents 54 0 R >> endobj 54 0 obj <> stream x]ݶO!9*I-i"IdۢHc='w"yN$%Cq(haX/3H%ٯP7g]ge/_6OmUݐ5ƨ?q} Jh"[!g_~?t-=Km?I+f ZQ-vO•-VtO%SKX3ߓ pX<˽"`SwZq8Ќ|t~'K?m$礕!F7gW0<~M@-phXD1/,<{M\' ʷOs--x~QƏce%Uщ]Jv<-pGvF?n)e2 |t]}.C4!׀PL}U&A#b|Y@dx,+YsI.3. h lf. [\@<$"P#Tt7}Axw?̨׻Jp iCK<] &: kgS132T8ϰ!6/mǜZ?rqG|G%D M+0h?f聩+DOAF6X\RL.'X'TE01kl=~Hk@^s_Sz1X"D78M~*.oVIK5* L̓O%t;##Z <!^ƥl&tv5mwqD5B[{[[=,@_:S֖ )  j,6`?@o\3ƾ186S'qlr:FE(ԜKXNkbF-} 7PnߵVqRtf~ \{ι8UzCN}6H/{'Z[y8$<~}y`35f8`A!}|Dl#'XY%Z|)>NgB4 7gRIا1 "}lD}.90 9iDc6 KȾg+`G5auD2A2!CXQ)O B3TC.bMSUBI q`(HDko< l6}$́rIk8}c O1ǭ'"/@ufŢ>7i9幟̈́'فҙ|@aĎje|Xy,8b> !ӍG]%=D4_΀YUsG Qo0Kb`DG)fxPva##ZM6Kr(up"u+ԏ$FC<|H f&_L唘W3Fxph~Ӿy0|P'v7<6"t>7;4 {0d1 J;y{@ܕ>EP BC[r[< w5?xrvUާ-'*`mjbFGoc6b*2>qM!s@6"#%2P }^,f3hdzi,)`4)`,9A*^W7L8)߁-  3ͬ&7ՂhYbef"zcgK(BX3froպ(}a~] Yf! Y5Am8%K}P}A{d$Dd†9^-P w`s: ٴMoZӅ߹9ȍr{ DRٽt3'oqK,d2\rx+g:GS*nt*q lv1\N-/ZF7gP&CD/ϼluF?&뵿<#3NݒŚ]uqY_-\$M f;Ǡѩs1E-RqogBh xVRJEj"X#\B(t"Q@ngmoHb]7g\m CNWxi\Id;(O>]^&oReG9^/2-5$<j;Dmz@e*,q`e (;L(7ڏwx@2wYVHsN`$cZA*\1ĥ/B*e>VXD_58SbѲYu*Mb٧RGb5jWB$W4e(rE3reg/{b boBC}A] SCʹ9ot:pL!ߜ ^6i"Hz $"iaw-߁ dEaiݵ\nT{:F;k!I 6$Yk_W̌(e4z\lHhJ0 6Y|=Ͷ6NsQ!\w Y2)c`C]N"(DL`E`Cl-7`C;4Ry\̘a`CqQ4{ (ؐfe_, 6Yx aX ,ؐf[A|SԏlA5|3'S{),s :QR!ZI7UJ68AkFJɆtGh%ݘ/T)ِ $*% tc>QdCSn/lH7pVҍF ZRAZI6#UJ6$8I+JɆtGi%ݘT)ِn,*% m\ !{nC࿯we}©hA:Hc^řf,t%0hIYꥯr} >_Vs;endstream endobj 55 0 obj 5097 endobj 56 0 obj <> endobj 57 0 obj <> endobj 58 0 obj <> /Contents 59 0 R >> endobj 59 0 obj <> stream x\Y~_ӌi7oҀb1lX> ^*Jvec{)d83+oHEvW'痦ko:7>_>yj7yee[>|~|~Cmú9g$ #XtecnnNW?v-׆)?koXnfZqMSwiLl2C +-pֿIwa)qƫϲYcpϓW\ lslF)GnPl+ޣ] fЈ | ΕcyCu_6eLuj~GRiBPO49y؊xoa`b7[;`NOш QMO87^}9Y~N s/L*ZAC܎h[Ck˒ZG8WڿAhʍYP#m/>ɫ̼i)ܦ==DI3J$fp??H K$k= ]{d ң{Vy=|tX`} &/reFᔒC)zΣT>y@SYu_F^9 B麮2Nj(Н;g Ѿ/f/KuP4G _Ob?%FR Ť5HCĨ3ߒ\UPP(HʰoBK>d{@g !J?TЈVc$2ל~a3Blb?ʍ:5@ji[cl@j;X_WnZuk##Yƾ:Oo0VC!C Jܜ}Y}yHnz8Y)ZcM6+# gNCcVz\S]Djk<v?*vwp'yִgXٟ,(7a^Mz "4a!AJܳV˿0  8̛mI[kof\V#G ^v{xj$ h/a`ÌHf6y`,R([4cj!.XЎOՙCEL;#M) YI9# *zW]_]p-t1=;s {Gr9!mvgW܆RXW8bP}(yB.iL{#`(ђd$سgr %& M̻?fLcx >+cZOD /(6I4usdL9K^?\-J )W5@ygt4$; d4JI,Js*CoEϪ&cW@h\@WhٴBK0$ݜqW e[J=Q6GcKp'Xƀ(qh|rTΥR^e-^`X 21Gp Ou](C/FR )6n"p"GIZ4m>^꣍Pț8u>o"$KA20F5VH}*]F QP9UM:dO`匋聎.T3;3w%wP hV8hhSDI%ǒ#95'[8cD?l;"a˒KF2UГ>ӌ!,JE[>"Ǣ$! nut$švf*9xZlkg&5u2cQ)`:*)-jN o4bobs4qxʦ8}7ŽQ R^P Ro+/X1fM{T3eAIّ| 7 93!<Ȝ‚" .&>ЕrBtKp_ RAFTV${)~8KcW0P=5=#0Ccr8 XkHsrFPlJM/ѡ%)J/񥃢,wH{jswU栂an&,I{3+X= >y72"f/@l$rBY+mTÍ/2a?^ ~FeuF޴R7%z|ΣE&PF*0ӓ'+ʵJI&߂K7LocYq]m<|N/kar]kb =!JY!,8 NwD*1C?q"3w v#ԝ'3yr>~(: t..=Syytkx(X ^SR2b9ɗ|"RÝ)Xv88iιlUu hڀ^^soNjKWC6:暅2erUQ)'p§ U#;'am7uVE>h:d T,x;{CGֵ 2a2^֬uYJ҅Nt-IǒnO$]wǒnO$]cIuҧ]K.:Ӯ%IG>Ztv:Ӯ%ISu-HG)Vk]KnӺ6%$]+M%IWNJ)kSkIbڔZt>_Lw{ܚi/C槢l2fo:.Fw6ϯ?_ѝ/?(tb_@(E 4&a㟤J?~GGkQ{: Vx7["6"? Qnendstream endobj 60 0 obj 4072 endobj 61 0 obj <> endobj 62 0 obj <> endobj 63 0 obj <> /Contents 64 0 R >> endobj 64 0 obj <> stream x=ko$q+[fU_l^>پ3bsFD;ImX^HvUd1! U,+W ۏ'_}WO'Ǔۏ߽M+c^:%)oVjYSf?lN7)~MklݰJ閛ͩښjM-1\TWImqäft#pI]3^,n89v=7x'\ :T_3ԛ(ո_}/}=lv5<9uKUf+(ծaX6LH^ݩí7v|Y׼YA^)V"]W`PcL~wQJgvVւ㖷]Qd25]Ъ4lvZnhgTYl}'RU=E* ˓`oewZo\n_nl'+ %Ʋ{n#c {h\}GVmnfnLj4{FZVFJr飧DjtOSi&Mi4ZjJ{-bw[mA-tUB1w#'0 ^W hLޱ8MDz⭟1^B3L?⺇9u+4}XV"`%| "cQp>"_e+)('"|P6VvbkXQqmqg-b$]w0[@РOy-Iu4jtCT!B2VYIPsY-p3o =ew@i]; #d<7jRҗec>˚hP}Q8SZϬb͗SNsìi eT$VW7踝9s2u'Cuaˏ0v4~7ɼa%{drDCG+ dU,#ޏ:FzGF~ﭴ`#7$YU67t-m|H+LdֈkLLq3>C8gӕjռWDc._YV3~*L3#+-2ՅWT .A2}ݠ-q<;YJKO`S3V>M'4 9dqڞ@[my,,{ *H?@"hYX٢BKl"$ <-΋dBI֔,!cr KZ֞һ&rDqq Z2PƠ LoIL.[[22țAA A&X9RX<36hPT-荺 K҄ӱR6+=q%O`eY$=el>ʜa~_Ъ=i]YManmG)f҆)k /祼u)h:Kqn=CP0YrBc5@>CF:%JeW󂓰rByTΥo( Ў1M 2Hlg5kj2Cg5[ԁfN[f:??o!Q{ױcYzDlϫoy:X!CnZ챦D3#h_>SCk: l;6]1`C@jÖeP,~m Z* ˥CJ hF]"W, ˳Y"o+秫=(y袒7 %xL:DBYNG yheOqs`B'!8=^ǟ]%S-k=*0`2ohPf6]BWqOE{+oC֬syD ATj4iP*yUNOA1CBR0ŸV`u9`"^P Lbؼ(+a(PT l;I0zNT+bױv? BLўa;/M@2/@%| <Uav9)ZMI)-2SI-c7@YeE]^rdD(|u~ů)R}N9&o<~e,ˣn1<x%uoaQ)Kؖ:=;^gٴfK3kai+O9WMjਹ/n=VO0sa~3)`pGwlr'cy`V'^k( @,XO<2'w]AѱdHAAe%)WaEql#7Cr̹hK(k0] ɢoyP4 NwDrdC8Q )G_<"i:=eF2ԕE@а`OC1EI(;i mL*ٛA]/D U8>nXe0ѽ0I"݅t)&pC A&9'(d/Bp5A\rKYwԴ]NM}Je x`%Sk孱tTkfj`Ev./yNn#%MKWR%"+D`҉0 *-띥4HYG%`1Ԟ^e:e@A\Ga(oɘi'h|BG_ t;| 7 SU'K lE~o9]BR 'eݺ8lCyKxXRH LAG]J8x׆7vg-ͩh+a@%\ܠΒ_'ha$D5pOD.H -Di TY=bHloSu،M^{b-NZ6VHhi]>V>OvrUe<~sdR=*)O;H$}rWQG84823-;0sXږh(!FTs*=ﯫhl !#^E^z+*Z9METUԳyq) k_K2FVa[T"[J7C_2&VP]: (}44ĸK\0v3'¶]kN?2ѱ(n4M(?-]pCi$8d{/yd4 ((/Ŕc[$KbI0ԗSQqi &eC.dETu,:l^K#dv9FBыc6MeG~.rMxtF SG%:狩Z0U9G m[Dj090g܎ 6IhN "S ^Z"iqm^B:2NN`Hsye{[M1;W<~ }1Cz.eIFxM a.,\0 wkbWՌ#Q|\wcɵՠtymDWj,J)s[|1H{b Ee]G_H?py18_^'!i9fb!.ĢI.H;[>L2eJ,Wy1rH {Sɢ lȐW \^iaGB2<5r[Rc8H%C.L.ߡxs5^i|wʇ~\#ᒋoTL(d: *_*ӟ$yzf9JBaJ47إ 3.IP1JrksZ[+顟@|B~ ųr}ihF~3P mk5'.KEF([&EnJLjLcdB}?pVƯ)Wٶ+>:^CH܊4˂b)~.(}{IY > 5(DibKE}X-'V wh8T 8ǯހweJ$,B]") ygi:IvOYpIxv|å$k*0Fˍč%o8m7R>J x 3"y| =*{JBz BB~#~'L; H߫j=e[E}Sw>l4Z|0]>.ѽIKz"ߚWkraAmDoxCv,z˛2 ,B>F6!BQzeg-w_|/rK,0iA]hC˜t_`Ϛ@_bFv5pAlo T~- 1xR<7$82!f 7&{$wC$0pHZ6X87*UBD%-4!ESoϊY_HM8eĬm~g <%_(pz|,wG :d,;rHa2(0zp$zd_ '0 %I=e0Pk½(}'y3t.3 F(v3*&x\C#gNmU=׉"ItBC,Exwxn<73!GKt=NGMEp( fa1Bޟ\ɂRli)]B7ɮ(JoN^^}$cle3 ayv[EVjB(r5@͋PG%@Z [4jM]^i"1YT7X&_'m;b@L A0WLE!&tߟ%1eMWqy:!Ъ+QFV𤯲7H Q0KAeqzi+E43t ÒXOg#adGTzW6E& MXP @LO'v\tME 4=Dyw܇> endobj 67 0 obj <> endobj 68 0 obj <> /Contents 69 0 R >> endobj 69 0 obj <> stream xvܶ]_xO6nIGGV[Yrd)McH%)Z` ܁qQo8}}᷶z^t#*^ilut~~)҆U9n6/֛ cqVf3mhVO\-^}Z_l!vz zBFƍI-\U7Y!dtmџjMMQyMk{~-RqVDZdV1)p&n8`<_UMSYq_TҨkʫ5)F0yVwijF[|麞K=oaG¡a_~;${EscEV̶]Ik$B6@īlRt_x!<0^';V6n>M1ޒ53uKqw?#yJ:^3'I HҐK+N_;^~y>{ û!`}Fe;P-$ EZxK]wF%o%H‡g4iщxN 1Z![帵%H֙wd ]o*}]9r@fIp|AŸmjkzqeY;L 2zB[@i'AhE'dل=؀4Ɩ>RoI[2\Ȩ{dsd!a Re++M &d,̭q,c (`<1]a4ύuV+;Zf_PC?{`#׆k/)Cq<:ҴD [=u~DGDBT@ rNAm"@ 'Eߗqo[0o\'ozf7 >t2 !,°pn:FhV $Ы 比g78#x<w } T̐mZ? `Q!4E Ğ!!8I0H227xBP}bAD .%qN|'Dڠ;xOrͦ%Csl$)+H/7#Xt6i  36en}Q-vaWѹo nqFCDI^d4)|5]S0iN$G1dVm|!}!̫?R}4Eȍq5s #o~R*=cxy;$^% ֈOL[]T !E< 3oPvLK]L܃}eZtekk%G%rÅrp7Iu81w>%0dbIj,TiXOؐ3[K _ y =  kS$Y m=şJN+9e$w'P- G -?˸"qs:E3Yq Q DdɗOcvO j2حY4/Kʾ`̜tSM qQpn<\N&ɟ>Z@E;>cYǩa!6iRu[>OSn'B|]0'b89gkj4QBtI8̐zÖoYr1_PscYlS^~S'NbJWɞDEpj%u[h([q/?*~jMZX _{Bf :ZJXSs0+4xO[XA@N7L֫6!qv.4_uAAM\ 05V]л1mkZ1axZ}ig{ɑ2ИZ+ y9vȻK7uκV0kAAc8٬ bvd^m*s3(ӊTdOJ$\#;_+ 8T޵>*F'@h,-L|~.E)7ӀdА8)fZfgx1(·,r;ac_ QpkJUz~l/~9-8t{=41^׎\ ٚYADIUaEe澦QpqFr)l*X00YR 9fT>DWyiY'ME9A#jMB:yA'"ht[$S%SB)>L5q'%X(09^fkf$׭ApB&:GZ7+dJ,˲T"ZH;_/Xtorå 4:n}w`9!oT.-}KGYwuÎn!!Vv(/X[֘(a%{sD{˲ڢiILJ*<+ p+/i%V՚5SaXھ(TБJ jŸxdYhR޴<1Hc1 JJ+7."> xŶ\Mh Db<# zw\r˽@h/a.M.fI,*3 H+ygթ.չp՜wOsS]u;ƟW)3oҥX|Hss(ӳqTġO K&J^Sz"ku 9g'%{bC`ߒp1{Þ]P0;X>|0-|WR0v/ M f޼"ǿ&a),R5wRDHDɳ Pyf|.L'zm8l ;[Z,J? h2JڏIByuBl 'ohgv|4֓ IU`=Fu]b]٠Wk/کA_`1:J_FBm&[{np0xm/t@9V'7A~=9t%.{6Ag0ӔJd!~DZٔz%AQ-ꋚeM[> f[j-?Fe9oyx#4^OXqTk n[- PvF=L袷/:,WA-QG"FX\'b5Lz ͈-h03nQuWAc8Ӿ#[~Yy<"|`+13@{`YmQ [<ڕl>Ϋ߶z)gAs9 CsdD7x>_W!2ƣ_f\l(BQ$dP88 n3:y=a2!`]D)-endstream endobj 70 0 obj 3945 endobj 71 0 obj <> endobj 72 0 obj <> endobj 73 0 obj <> /Contents 74 0 R >> endobj 74 0 obj <> stream x\ms_ɗrx?['St2NF#EzI.p`[4ԙ_`Xఋ}yvMI?ɫzIS ɗ0֚ߒRltaRzr~sbl^W\k*J\UM5};S_gsSY&|;chH?|/C#gg_t%sGDejay qE{f]Y>3 @jztiY~Z8ǙvWO>rig3:4tW>b+Eͺsvw{E%pb5nx/Ʀ)/D7 1(z5{b:[v/[ۚ{+` xBB$.)M A!v-LJ.޺wPne2ƒsBV0$z"=Wq0U{@A-k>*цhaA$4PQ)ƽ g]k#~Q]c@#hĜNg9WUE{vrzuU>#U>4t^#9tr' WLi3™=l;'w .hf;LPԷ"ęAjZo :1ϙH>m[ˈ \&b3A}04h8/q{ l>^0m>Zim2O dva,!%u:&`H Y<I@tJ낲B6&(8kҀv t#tS47 p>?D>Qp+m@6뉙.O^%w6 `uuiaNmVRc&$;DH:s+6<z7K_b ^TcEl Ch(:"36 G =C.5 tO QwxqαO"Ǎ݀!jóќ*ϲEz]>>^nuD=J:gi{RI|5֑5\g]F=Z1ZchD23Cq)D xE %yYf tK (F)ŀWjK0Q HgA.n\1nS~@J`%ۤ߶&ZKI=c+=Ht|PECh|hxc䵵ZD˘[Sul= ]bb3ͤK~ "*Ԟؽ"#|ٹRȌ\j`/! QϣAjE2͋%R(ȓϐʼp'Bjz tVNvp#x|zobO[&욶HM xxu3uE:R2M[} h R8)6Jղ)އԋֻ —UAE+Y;mGUSI~,uwIWo[!g54]ܽ[͞8 (oT^X(]h~gύmm{+ MCJNa1TN"n ,Gw,&e'gFj}r(^i %Aސ'R]~˯t2SNbzP84LC?NA}zIz3eGw1Awۿ)!L50@FTzcw./3f΁)a 4}pvwS{wz8k=(dPVi26BC[ߜplš@l<\l;[%LPl(CWi7LU\3iV]>,WE%g~Png-JVե/J#e4YyYGy??kEEw2ؿ*A`&E.n }p@Y(*0֙{3T7Qendstream endobj 75 0 obj 4094 endobj 76 0 obj <> endobj 77 0 obj <> endobj 78 0 obj <> /Contents 79 0 R >> endobj 79 0 obj <> stream x]Ys~ݔv48Sr\Rd<),/ `4=Ŗb@ 4;g/Ng٫L'?.\>9fZgg$-lP3T7w˾JAϿpee o p;XH._-Lj[|0Lp?{JZ \t= 'e{x,CwoM( ?t혰|v3DM-ATK. cI.՜v>8Y,yr~^-D' `ky6J`̈0 E;=ԅ[Bi^ۅbH Q \>E CAM_a`wy$Zxt7H'~EvigJE%$t< F*yi 2Q.k2v" $K S`). CV ]EL[2j` T%q:I8ӹ{M㱱wu|q%o 2K`3wx}?7`|:O}͋5|,i xT1bC?U]"[%՝V+F~E =O6̽=7Y&[S2˱ҺȞs)'rfB83*z + Ϩԯandkϑ6vdbǒ0V9+4Z2|?jngn%7ye@\aI!;^RT􉳫0A|3 DwּÐdXNHG=C7/glJ %VEmPG1ь+DMj~:r5}iP/sep{Ic 5Pt{8vPL%t`40Vťp.}FÃAh@ z 3;=8k7:c`*L6JߎeW%jQ]Gi5R1ӅAy==j;="޼͹ &at֐S^VY.dS9_f=z)&a] 09Þ$ L "ca/`*~nWcBOROs~m/kgh,#a~ mҼ:8-88wxc(\EVV'̜Hs╦ܞ;n&OCNaUڶUڶaӰM 8ocdo@=T&F^,v@4`8S0l뺱wɡQDF;D X83h}iGFX=V?]赥6n ^x,!*/Yb> ]&'VjO8m\&[ѼܲAack󤟱sH7W}xmI PENbG!!ײ.MAAǠ^m(#p`y'(t6C!F1yE+ݣ)sk,4*YbXjrhd *Mavz:U\H&:3^E1z;g) r0*օ.{M/CَU+>҃i|Ђ{cu*]=1p]Qxk\º~I֕xEO}>g75ʅz yxG҅ )#DABc* %#}HWj>KD4(}~̕68-hB>$8qrӐ5b5n-6@G18m׏!?_P?]nu^xd?Ho2 *+oi!F?8 Ӎĭ%y* 8j8q:;s gVd29P٢:~s~ fIAY0;yr1 㨀Vm'P .{p } ɬy(2.A4a Uʄc4;lJrtX8zz0p WTON96ZȥSrӨU8Tr竴?;- c| \,نǚ$ukW:`]6L;յP3\x i#nbOq6㉳^(T$} D`HM5`KSIԏc Z!\@;DמvRИBػ".~x?.3,j$a(@GaNWaFZy[!O>1w8(ÜGb^il^PekuZ[Fb Egc ǾAƹRĒ҆7J.[2U \TG&5鄦/&GQMeEbjei\ sz֞7|CyxkFZJF߶b\H|y=i]YߊM5Yq-w~`<AhH8pٗ^$ܧ-_JtG V}˝FRKb\kONP[6E! A<ӻτA?lIj2x|FǓovz0!U0i1ݏDx^MbuY'LHށ-uSs!܄;UUyv( 1&Bx-:WeJ[A]L }2tO$e aR<ՁNL=g1|ڀKegh 'Wy!~)?r-E 3+ymf)W/i.smI_X~R#Ro1aN!;l n|`=$m< unvz&u;ND}ƄPcub<}޿+r@0L`acشk99 GEPPf&XA-|~!ŠUf:GT X۫HQbJ3EaKI9]NUn|9b왑SdCy[Wj JܼuWi7YSk.-&ݺ7nZp ihy NB c)x~}tA$y GmmT'l~+/cRowaƂMj[\2QI>kR% $;[A@B؜'Pٸ.Puk3- KB˚4ei0wD7E Zk[|5ލ%JH.}P6j^ #'tEYåkTʸx6u7jI:|0 ^* oRz|)MP{Н+.FOfGA^QV\Qx$޷w=;,34bK 06ٙJZYc> endobj 82 0 obj <> endobj 83 0 obj <> /Contents 84 0 R >> endobj 84 0 obj <> stream x]nS&xl)4A]KErmK%%q_Wr8$?r8stALx7}) !7}9zuyqwTn^}g l>=](zMt)fs㲐M+vnV;Qm'q]t|_kE/TG;q'f`? 3Mf+r/췲EG;slJ?I7mܜ uczmv< }N6+8N_uB٨s4;kK=P pn\n-\ ϗΛROpIp ?-up߰ח gD)L~ VǾϷ055]VjR/kJ 㴢‘#/QHalHZuѥQeU2}WG"nq'~J}=o]0[unM NwEu]\MVvm;v~ѳ5}߯=tU_kݗ}5@TEYtXum 9֯ 0<.@z}5 /<4=vǕfTjW y=y!;| ƣ@I p{à1o-ʦr`29kn yXmJݞ t`|R2 0}lF{՟0A[H^Xj-k AHa_715@QTmgYoh-@n( 6LiL7J)%"B s4= 7)^iϑ*Z孩F?; Xv쎥wD+bB=/ps=3Q(!g329yRZatU+Q1((f3z3d;f*4& o5U]Jܚ*sٷ-(hAjX4ct:俅qmk0 d׶BfPs' 3uI(WK> ؎i~P`(s,=!(m &)m ߈kaFU8@NI"$O=3*t .2uUB Ptdy2Xou1ag\n_6v06p߅T&FcG&0 ,Fk&hoD^o}FGɪ&;H]"(xJ7۞׎'ĜHGoֻЄB(B< Y9/*A(N?ך\1WKHK&4;7axS68>' ISBVD'hbkw"<0/GB}lQP_asڝ =^D^|!B:MnfƲ:TF tuOXld׌b%dezWˤ00~Zi"GkXey3Ϗ~gbWtw*־pV4&ʁ;eg|o>86P곩 |qw.W*m93 acv>A7ޏڒtiJ{J^{ cM}Ȁ?9C9O1J,0IEx9׳s%iשؑO{ͼ0?J&Qw\'1R6rͧ~28`Y[aO@3q r/&g]/"@xۭE ʒ9~;Q]o>>Hts.?VΈҝ9L'v2 7=PnށM8E“F(%@;x.B8-zyQ8h+?R[ȇOJq.0.XY Fޫ$*g9ci?>)) \肦*˶skѾ"͕#y" $_hHZ-S N73 GWH\~㣨/XXfVB%s („4wHvE^Wǰ{Y5uݙ#D߁Q{B]S]ݪt3I}ڊ[K}^w|wn4l_((A0wS-qUTzkWVqÂWu\OUx$rMWV Gmvm)Q{ <ݞIH]Th=Ž`+Ԡwj$\hmzf0_trf ulhWifOvr{d#ʍ}MUin^]$]1?eK|W<"0+/ ͊DƖ̇*{6$72ΔxTʻE1k\턯 4!;7C/S~e)Qs)D98̭ 7Ąs_WJUwھp;SrTPtZzOɔn 6J07n91X;״`R ~ՕhpDf[| 镞8MZY_*y12z%Oe<#6Z͓h-ͲF46~:aMDy{`UvI ֟*UC+BkTT:h=.a `$A6@12I+01{GLL䇕1BYň~MH$]0l ]S ^ĿCˌ!4D_d`Ef,L:ԣ%pw踬`&BM.0S0uQ \/4GC,͉L"_>p_nDw/gJ6Vm#QQ rbFTU(RjMʂa:YK`Y?: 2u=[6E#4Ym FO`/K(3HVG2p}enhA=KI';5*֤֘L}HzIF6GYfn˸&!ITE& t7JUd)DaD3Ea4i ضHrhKLҖF,:ыI 9vcaDՒL\HT<|:6`Mb,dg'H |h݃riF zX  <0(g$:1s K ̆to@zwEK'wnЂvL.6-չٖ%%|akK3FaGХ]A%~,#lؗU)i^J}k]P7XWn=:BI&G&[e~8YDbq3OhJ1D杻2W^u<}S6|zM b^:^{{w L$G#YP84v}SET'}Lj,pc^#aעyjŮޞv6]xӪN9hQ1CtoJg&|_3Q3&\0S\#E^Ř/6o/q.#ՓF -(e;_x{۫@R!M=wQ~\24_UZ) Evo&di5hxRA̧w.ˈlɝCHuhS@fHZsX`MդlR+i_nk+A8F0ŒY\qӑ1"11Yv& Uk(K<9dN$s`$\'xɄ8q؟'/GΠCvm&L}ld#Qs# ipxe&J7ϕp?#!b̽ 0q^bTγ1XUГβFZ ^LqDHٍ k%{syMk=2SNב:4<_e?#2tXxt7 \2n]Dhf߇%yTD4@vQG9tE=As|C)(1T*aS2NXjid *YRJ>HS|҉Nd d-iי Y cM]#ņOrcK՗̨2o.IfMg(g֎z^@BCC_m gBTCF,N  W+' ~Y(QŮ0xnқ W?Pf`׺Mc#Oa HV5M~KË;)%n(R7+x^2+ȋ&б_-2v/. „/r&y8@N~F0MT{$ z9)`1)Nr7Qz!݁zO#8"4\BE_P-h?#1MCͤȭcf޸n0"뜼 'R^7d,{>Rsb}㦞)A c&|OF\){ שɂ"8ڡC|L%N, M3y)*\ٹp&"+/Gcݦ}ФydلB?ƭ8?}˕1ia;#Ցs$oF@ )R̥HHX L: {Eκ_c {S L~*Pw MG SXo> endobj 87 0 obj <> endobj 88 0 obj <> /Contents 89 0 R >> endobj 89 0 obj <> stream x][s嶑~ׯ8嗜q;dwgIG[8H֚Hc v wt}]1|x{wޝvgݱޞ')e(vJwZ,ӝһ'6LWYAI딱|ؿ>ܪo˯S4CQRtô}hh_]>45qYݹ}WqlY<r'\ C7٥<كRڗFCkAK tZE1GLh.L}DоeQ乂5 Z%cpny |7{ E-"x.@IT8䧤ߤrnYrwjqpkDa Rs!Bt@km82L [k+:o'CFAq\Pek)돎nP*HeV$VK|`bQb\#(]yRB17U8G!?4a_8èV}x6-k>mTݷe@FhQQ@DT8'B |n[TŸ('~!e-lN59imRv}GYAtDRVL*[J#SL3cK|-g#J0&"ScS8)/ -p3-\nCP˂8 ƃh\{.8HMV8>x9XGA8&U1$9jpC,n) =eKX0~GٴˌCeqt lu[b2b\:EP^ (9M,*a[i1߁cq1PnA?s+9D xh !\XZB ~y5̸0 ^}&eݤ'Wו2Uqd8B ٹU(fJ$6S+ݗ!ZYr^0H V%i6S#rjjYE]!rb)t%U=䨍 Dg)S< $lz|VQt=eP&{~r'!%`6VXc~Ma7NT`zZDJ ;L1MҬ2k6)uM%Gd^$Mc* W,Gfc4Uߗđ g/*?i]A~Cɹd\ ku5G}ZCk 7^VAԒԮ_s7N!}j/42>gǍcD lw7)ށ.Zc>{cxke2Js#XIt>*JQBe' t3:XIx -$)LCA3zfͧz'O}Vo!Hxb#}|J;Hvs6N FbLjN*l:E7?܉=s6 y0+'E,ZvC fx![Zv =CӌоWX;ӧG=tD`փ8|Qԩԡ{x`1(,bӗc3t 2,Z4dW.ÎɢCKFܻqOK&&sn"b)xI`@8!/$k aHM܏FW :genIH|HiSaJΎE-B9?ͳ8w:*ƒ+Nޚg%QC9Ybb͊NisGx0 t0L`ez^u!R3L4.P{$&+#0 4hF/M3kwG lP9\D(ɣ(A{)Er~^LOzC a\XJwEA=7.d9y ؿi͑^^aL[v6VRʋ4y3E)UZ"`9Aa4,Y[hI-c [*m+JHM80Lz\%HD16!zJ[f鬪ҶuV-(>8RBJ^} gWk`1 ubK:Vc)|,s,; fJK"5mw?+ևX hM:g\/ӔfH,wdڼh༛NNwL %…^I,u $z]^ȅu̚S Dh/n,TM})CpY *@yKuh҄8l]ZnQqQJ/XiVgQ_<ĊqoP"Vr&hzZ#/*V` qE&@rj,tQ&޷npf@6mnjrf6`uwT~ftө~i\?[(X0?Y U*~']=-nevu~u+U>>iŴ: >'SWNoB']2L 5"SyF UẌ́*B?lAM5*cbȟr` wjn0TV8KV}ݰ~_T2f,dt;KDΩy㮵ƍޅ)@G,ܾ덩MKksg2!5a$) OB9;[-8_Nz'MaJIԘ'RZ_VCaYGmk}ugפUt4dubEveAń8T?zjl2l!~$ H!uvbG7iN~\',=@GQEA:85Qs+PD+u- y-&6KYөvm4ڎZ۰$cf_|e+MVIE;SR)lJsޢ†D[xo~x/c&RLyOY- X~^2N4|t_攷zpc)NTr7;7G7$Lzo?kvGp2^h)9| *}C#Ri {e<i֗z(MV E Vnjxsb-Ѿɏ`Y|c.9m1[#`SSLjn}94Ӽ't,W}/jD 'z$aoaR'pS"HߓiN}f /Sky!\iD0,ݾ[O$Gt))J L' 1Z?Cj1bS- "4}^:E{?I7u M~J&EISr)B9 [{wAO0u>G_UQ7SNMDd?3qݶXZ8lCS3 UbaF牿K/Dkj!fU֔ĹirbS$TBH8 Zr)1/.| =#=Vl@bH)$LD.~6q|6zZ38}@\ۃmq*ֻJڛ)3:=@#~:̯^t{Ao-p"~S\6o^e^ñ|F[ mD9GdV>JRv'jK?3Avpv+h]4ԱH!nۻ2ez*`gZ;^F 0MĻ cfvp,<:^|[NҳHY!=)9{ǓM$mendstream endobj 90 0 obj 5631 endobj 91 0 obj <> endobj 92 0 obj <> endobj 93 0 obj <> /Contents 94 0 R >> endobj 94 0 obj <> stream x]mo_ߺ[UEtEhI(>5>suCˇZgy!_?q_Q<ޮ^Uuzu꨿R-تQQG_9+4k3{ڮj nqS5o5 ?nu1Bo4ln~$yl$t3^qߺJFO6ˮb[뮫::97/hݦimٯ cim^cfuxTӴkNe]^mWͱDg>dyV-3fc_NŦky 7]Y-r 廍, >9HjRRH3VѫȣnڊK rs ֻ^HtgxyMDV_4:iV Z\s~`sW \k44n!4/Pg31ƕ#s9E ZՑ;+ШO, ~0И5l6p8&=e7;¬=BޣH7SmB_ЏãR %9QNA-*^^2˺:D t lAXf\lS59npKv\ PßȄ o@s@Ph17ėa'W T~oQӷخkFDۛVO"2*a`=3y~+ktӈ&|6M+p}uM&xWz$s&jnb;!#|7FDe,Рވ1<(9|3# ;JqN #9vIʑe^mGYᑻ95YNcAPk,!cK7nZDUL )Ij3n&S4ш Vnt"D6y'7Evul_7Wtu]o,0MrM\Cޙin~3 |k'꺖>߯Ѽ8NJVV`no"Рxhn[Y`>Uӈ ¤"w18 #VR n8xv<ȋ~$C˂8(<#+0+߳d= XCUvr+j]z-yr'H *Ћ>&s zV#iseX7`Ÿ?*",FyQV'yF0ܿ@3&cw$m֢ ZL@@ς8wDw)m+F(]2k @P| fׯOZd)EcG= L-1G h :/B#^FcR눢sXT#)qϿ@4|o(MBF@C6/ 6jP" 8 Ĩ)CaoA SwNzr٣LE\%`، F,$'>m^pãc_|{2;5 v BPzSugjpD(]Cmу SwpUTSտE:d(r} AZAOQuFd}'rD:ơ-BFvZ"pGtc6f'|uj4; OB O`>X0a T XV.u&RAؙxRW)jpǾ׊җrTěp\瞨-r+W܁Hn` M!# bAQ!fJb8 PuLg~D5 ̕ 嚁JrQE36ׅJEx(,]wV G8;nJo\HH1L2^<ՑpDϏXE<.,kP$SKpǶ;Sq$l` $!yR{ 8bIԐ̏G~ԝ9~QJnlU|K7H:XFwE'6U":D*]LLfĤo4d N_ZfiNi9q|5\+4|(1GA9Hś"sti:(}@,";e_)y_u$fy홪2E/\FHIZ5dtjψ{^S>-?$zx;/rBP?wC6zD}cUg%<$^8y^sʖ4Ik]S+%Ws.v2COm>/s@sOQ$A5/\AIP+Q—"Gn9#7 eYC,dIfha Y)>hHH6jD7s[9=d h!DHiU]tdeSrGiGqi1gWd?6$UD;+6*֥;div:H <%%仔SJium ^&gm8!@V%i۶Av꼵`gAsۮbo# uٓngV OI(>`SZK%uKS >읷Ԭj,NOJ~\XGIendstream endobj 95 0 obj 4566 endobj 96 0 obj <> endobj 97 0 obj <> endobj 98 0 obj <> /Contents 99 0 R >> endobj 99 0 obj <> stream x]mܶ~buU*D@i Hƽ(좸W;}|..$rP/{^z·Z33 y?UesG_T7GяGm)7\>z~b+VZk::JRʮ[MU˚nV'^Pl7j-7e[Z\_l+1%V\\lTT:h_ۂI3̶UnD[Uו8ٲ_gx}bu8%zQZN.nl͍鳯] >E_{_lxrp*)6MUW0T\?A,&X;͇ w< RPAFU7.?Ԃ/Ӯ- em,j3MȆ\7Ԁ/C$C8GVw/-=s(:97furs Z_5iJT0~| N~z|_v8N -=~ A5ld/T{鷻@U_͵+NW~ .V%շtA pۢ:LwGRU#fbZ}< +d? ͵!DP ?@'AlA`Ȩʊ64c1U5CcV"x걲ԈJݭɿ'1Sբ6V`6 0cuQ_53J>\~_>Mb] ޔ vb#DLf0C, &;ՈflŻmZ֪hc͖]Ut=CQIE{WфD%U5?C%ki3:>Y } Ɯ&M R;؜ *lIbSi_G½AHb%wƭUMf{Ћ}KA[gǍ/\jBթbw Ipm{3 i.[~%2[;~ܥ75^f>#aS(XUeVO0@Ń O43P7Szt4fԕy$`Ua`/+.MNl.0(y1yNڀ DgYo^8d_;@E%IBxEn%E0{77 gH@H0pշfqsN\h/YYptj=ڽm_WB3\9 0) s!Ck6.XH/ oJ$-8T~ : ,0Ž`g RAZm"S{ %4 +vMGSp9EDl$Hf`n6fG3p-ÒDǹ*%ܡ9(=k{`0CQH>8-u+-Cs|5\*9GH""0l3b99A1B"LI (WG;Hy.tIjrw!42B7'f#3ZKKq71N_V38m$.`l 33eW6à;̯4/SkJ2t`-5=vܫCک؞9vO"53HfnE0Z%SC2C0yͶYQԺ ͓M&TL 3|}S`#R|DO)>SW˅ig1SςR+6Mx86l+L326j+ Ò 3%F%#3qaX0=lXT!Hi-3ȾKGUÜ!IWѫU^LMjqۧ?O%?,Can g"Hj:(P~ͫ0U[05! rٱp>,5O19yCf %҇ͥ HtBeH^sI&I4X19C0Yf+v`T<(1uՃAP+c.9Ȱp]rwº+6+6iHtCVN Y{wWLBOdi #TC%f0YZ[GHn Z ٓRK@2Er ށtr]_SG. p|aĴ3LQEtL)tP4[teB6g Є=/JP)Vʅle%p 8h#";e@KIh@Z ̗Vhkeix-|=$et]9s9!x'AAIfXo`Tc,gFKN`^C|h(;럨FPɈ; όKgNa$sof7 ]Z2B8vd(>/`0tOӖIȚ3*'U#.~g, ;B)mi2y,8{E.-;܏]֐KujZ0RSJrh.rpw xcA{djYJb%_BЌ}̿0XJ Wi⚮=} f#vl˦rj"<+Y.]zi\zK7FF8@/ ~}gE?Zmw!bX`#; OIwҳDaܰ=%Z!}QX: ̈OOʒ3MlWW6`&G#{/D{L~nwmNDOYp]i%G:6R\Sx=,:O#d| 5S$AzŸo'igcIZխ5Iqєb ӊJ0Sjcb۶ )5sFcR9?a^ȼ=j09%n+-f ]!=>r9,2n- @Y.cQnTzrPrph>Wy~L7hsPhFW|,s@Og$=">1>4F@&"v`E%[" Yɺ3y?cd6J:ch9}&+եUGNom3|0uOif;1Z-V v6$9)+ ":vut0#5E oa{?Oy<]FP>A`Aڂ=mweW..ϸ@5͌LEh{e:?]nGʼ8bA4m'21a͎)G,%2h9"|)%vQ0p(s9J5_NAr#㑜G74MYK5zf/uj^Dendstream endobj 100 0 obj 3992 endobj 101 0 obj <> endobj 102 0 obj <> endobj 103 0 obj <> /Contents 104 0 R >> endobj 104 0 obj <> stream x][o~k(RMm4Aql~HJ^II$?b7)CEQ~MYjS\㯛ͳ''m!7l_|rfQ]5'}K)ՏZZmZZo^|,*݈~nۺjۉnڪqwZmYKu]_wM t,8~FKZO$xWBTZuOvU8+>_+q01i~fF|i~guYmySKSw{[3RDmimSӪמǸgSCOX<02Mѩ]k撵]< Nn>Kػ'Ug-=~k[{^bů޺Iyb=rㅓ/˾tqYŎ> ǐ@7+t`hzRѥnNեVnk=m~-fم߿GWi=<US6†rE]NIvw*JS̿<ܖ:72:3Bz`͜Ard}@qJZHk=ֆTxMO̍CF;Z3SNؼ@A wU' ^=hns /V\MpF}' a/a/8fMʽV6 &UAW[v0JcU/ѸDe-l[AGyh'h$`fc| o][Tj6+YifU8F&[,ɬN9mp>4.*eͮԆ^1+|gͫ0!nbݼR>>4ؗɦnpZZ=;/=e׸|>;;Dz ڜbXGd2O?vywe^Cm9C[~?׽ک5.3gOӍjnuhuE#,)Sooquogԛfnf+ {d~qa(&e=J1j.%psϞ3c@i *x VN@w`0 ǐ\e=PKQB[9FX] "j~MBF u9:Lm]1CÌdA΃}V/DOZY`ё,3 #ulg@329)p>>|~xJΗP]kY)M I{',> U5<zlM3۶W%uY Q bϼk0pބn0`6  vLYގ1:ShAotfJB.2X?4z4ڊ{ 8FAOK\W;) ŇK C¬5cJ 1ď%nb#eѴ^di D*ߍ}̡Is$DJgquY) H DHMHvfi;7; i=>*.x1z jV8Bebu <.-6iQmq!}3 ጃTDZsV]KMęr8_@FǽtY'+EgTvi(>?OUň:52)/M6t5^]>ꈆKOB$ °j] 7΄ޑx{AX!Qވ%b bA)m[iTCNte?(2ϖQH)]& 4SV~2X.vo2^dt7#!0:2 ׼In{da`]s7iXN˜l#7>ē(jJ copC oh>`WJ'\}X9GG uguzFROsJ Y/SݿVF sjVO2c)1ˤT$}g.Og@/~Jo~ P}"… 85ޙlEGKȲhlrϢpr,&pSm&?C5~Q^]QS.|QrɃ:4!&ay7Y7$C`O&Leʹ &SsMedj'.wous>"LzvD$E]MTq|eǤrʀ墳wd,N(6Sbk%+q\7! ״ 42F=hXm3M}iC|6f43m:W._u4cdDLivNWc2IRf8 bįδcLWceD x^K*kʈ&H,`W.jpF2 [߂X+<(JXuGQL`Qk 8'q\1 CXo}d'F?vO&5WP3F)Ra4]5Zu/w<C! !uE\ҜSם8fTڇX0, @6ă1 ,La czi)CB,l[ɈRC1iTHF]S33J͌c1G;(MxO>!Fžec @:4Bb&+6tC07邴cH6#ݶSO-??-@C"gSTKԻS =p,SxKHar$4 T ج8Q*B;؁, +0HUՠ*"$[uMQ6\%v&u8:xT*"S2 ]޼L= Q0d=w.Vc߄})PB0ֲ֨=S >J[O'k0ir\ *Ȫwe!E&hCٕGTygXCq4grK"Π/C}Q,{쯉-`8VɔqG|4iaO3Xi֖2|*K> ?uHB0ehc~r_Lg }lvf{V`VIzv.#qP%!Lޅp.GgMP0bDt gߣU+&bo(u љ 9c :dZ3+`G ft4\;D>m‰u5N0ɽHWOZDzMqZ:ⓑlŊ/Rw2`42w`e~U`|٬a@˭i22>aӵuω? ;9," YNX,Q5ܭ5߯BskdBvNa\ /$QX"Ưfkr46L hC=f CǨdM w WҸGDDx`xM:J0 ɊF -W`c;Tf>;&+v&'Ŭ8w8NiF A;fo`BXS8JsE'& C?%M'{=LH|KTB/o w;m:SsyM;3Iԧ g'L`wbIxu(UDbbs.ԥKmHc ؚ*9Aak R>x9u+b"CSVLyꌼ#s1LędTV~z C}g:ikTE늶6D9wnB!Jq*#f !#%=ͷK?8sit]&='stЯN]gk ƒX+u;#'zsSDľ-x6ủ/5_s+o⟕H炛XͪC9EzNI6Sz+Iˆ55Ԓ\E$s~JQgs*NdM@̾VjUv9q1zw#&q%fK$۠X6aas,4 I<ĜSn*>m,C@t"#N)A 9_  hȜ̑#Lj*\FgZfhJU7f8]Xi :PR~s$3O클o)g4UVē~K&黔?zsa BdK{>m1\) l:C[ !s'1d͘"-&Yx6ZH)7L[񟵘K dB]`k-R>tFmendstream endobj 105 0 obj 5348 endobj 106 0 obj <> endobj 107 0 obj <> endobj 108 0 obj <> /Contents 109 0 R >> endobj 109 0 obj <> stream x]rݶ}Ws:MM]g}߷'UUUcbW7YSfwyguuv(Tκ\uUQתpf}Q?L?JkcOmSMe>Wu>8\shNTgEN۾z;7ufhy4m+pZT:VCjKݴڢ+϶izgz ލzN7T_a;q rPm7|̧g'_T5e׾5}kwN^&ReJRIjR${8WDB9~, y?w{Z|M~&(\1=Ej l'@Ś;}"whijiv/;9՗s7ó8}ܐZBf ^PEvp ξH(a3+ čl0$q]ч_\ɪY_ĪH/7rEٟєg YPTzGgYi`U|8batC#G6=[}؞fg[s2`N9ͱ$ Zi((SC-^%IÇ\Xx8I\kMC  $:u^NҔYљ#跀90Jqa"- m_1s!%\{z,5K&$Fғ;ΧE 5"DHɡں8(qN- }D/ T"IHqQW5JqA^\ y^>b^֐.D2=#)@  k֮y2 u=\'yhXv\.R˴@ ~{65A  :#jiDVHS.]Mݐȡ%N])=MD7= 35>$k2ȣ:C+|<Ϧ1<8.4jrlm,Kܷʴ`,TT]U ;;"3}kRprÉ~NJ=#UG4@{f&qqIa+y ):Yc>i`̀o=VBM/KS:c N2ZX(1^sG_Tg26aߤȦ\|~m,%df?@_/X1C'qUEäXyZKGI]^,BDhF׶Xd )!x,q%U) EiZ$X w<m5lmV.dRS` Īcfb7/ɮ tzjeVռQ!zGq]TLG$V r Ÿ`ZU )ѝ"wK¢-d8-VTU]B&g >I&u /zn+I[@6y&+r$4Wۣ X0fit5 @#-Ep (b 3E*@Q6^i-Pu=8©ҪJ7Ev^Y3? Z 3LiQdR5}IvL#1c/T $xraqF<~mVVU}7>?K od5Pun)2]UU[/󌃮6m+u7AiWYٗ?G,-CaMGRJyR*4ILh0}! \hR~δЅzWE#_]936&{Z-\CE76 Z рghN;NSxk OmɚaC]Q]s5'6y#{5OE(놀/r@ײmjqXGEB0TQ#pPkK61nHExƒ(3%N/x+LfdT>ᝋĵFye7D[i-51IKGuTuu'*&QTJ H86 U%V[HpD\vp94 Zn%6wG&?Ѭ@q+y<%$VJ9$S;r!]Da?a&>98۱RpWR,s丸/5mton5[|mD^EL"XS |nE]\KI^hq0^%b*1 @3{+C/DH5ӷ?[1-ӳ$JߙIъ̱{ϘPv=TG"8ɟr Y#aWF4X;qxHZ80DxXyX\ꁙv;K#Q*Ʉ3+"-6pQ2pD>,"jyJf}E5 hlۮi1(hzl$qz=="_v|(=0:[u͑4Bh7GNX5|)[sOBHΫYS3fI}'Phj*bl*~4@Sg/ . EhhtH*\ҽ?=lzd=6~Kʠ:(bӠi XLH3d61.!Nʼnqxi*Kf̕ UwT}W0 j;|CFRH0I]V6ƒIpJ cH"w8T=5X站%΂,ђ9ä́PMj#??s"P-A* $LtJЁ69;<RwL_$eHnޞ_0բ)!Z4VS<9ήX̯РP䇵 (oqqӫǠؿbt(켪) )>!B*IAB6h,&D~%Jgu"P2"MTc;)cK㝪1,WdyL=„=x=zSƶl8 Fko6%3OD]xj<] )Ɖ#w(9:/(b4:e]znm$XVmW7[̟p~1 P=_Pca?zړޗӄ? ~GV96a3!W(m7, 9wd|}KTfÑe|y~5 SUG7TeEO=~wl#"XO0QTm^G o@Zznc=ɦM5m ա*m{K3_#MSYendstream endobj 110 0 obj 4321 endobj 111 0 obj <> endobj 112 0 obj <> endobj 113 0 obj <> /Contents 114 0 R >> endobj 114 0 obj <> stream x]Ys~n7˱+N崙r=Hʔ-H*Q*+/ Ơ{qSu M0iG_gL#7|z56Z9q?I)lúFug~۷ 4Szsd5Jn_1-7j)Wn, bǤ`_-N{ߓ0Mٖ3N>ˡ;oی8,49an<R/ٯ ϋNJMk'v{?Vu~aRk[VF [-:-{%\@R, wDht- 6&:bmRzpS`~6v7PxɆ`71R7p&/\TOKrY_ @:7| |>_x^<0"L*)^߂\ WN ۸6%]ӶZ܇w=4{Z>9w,w<(@o:apʵf^en~vҀyѪ1Jaq &T__?Γ0PeD&PF#o(\q_}ax!9m-y}P1QGd1b`x5#6 5P/ FxMy!bX8M#$qnHGw",! yplHȐz1+}4ߍwXPUwT G(G#" OAn[h$5ixf#6kb삛B1H/s\7nQ@F 4zݹ *WX<0cLXk{/çWP|0TvWE#Q^HKeCi[S΃NBѢ3hIG)% CXȶ;7m(°$;: % )us16_i :I'?P4=*Dd ъhۡ5zETD32TAl:&*o P~37ϡq$AJ@,tpǺV8 V4-ץKBtF}ȔMGMlgkXudk^oZ35wv~u@@s )m ^Dy>RbF(g*CۧK2 \|$M@Zzf^%COHR@Z튽9K"zs6t>ҀQ RRGI7(-P^'~#S@x1p?bѲ:pkS5çJ7K51Hͳbq}N7>A-Bߠ 0L#"x9Xj58ɜL>SmI:5dTY''1Kd2-ʤ^jiQxD ]G] ~C~y!ɄuQD3%$ (IEtHZrf>Zwds%7}l逸M[B8`i([@UA?MתQH~4-7,qr +YMqIr"'^"|~Ux"Gm2/&,l~F S9^Dފ*|Fwp&IJVoiʔfpG-yPV39>)8ͨiOGHQqZfYq)g-8`3|V0|wU>]#؍ f ct'.O1ވF)+{9t+_Y"Ya|3hgldt&Ul=<;d`TQ0q8*9ld537Q$IX4Io1{GE1j=@@ ea5FcD4~kָv&fOA ,?~^S(q8ߝJyYYZx')=H 7󴵪sJيVWN7Z?#.h*x.#k.'u! L-y )\g9~jJB.T _ F<,V d!hF$b 6:*Eku 4nל{^ŽKOZ(s+m tO K[ah*.)=QB qO(] Y!)bl:i湸:~%'$"TVX:YDQ;չ͂grN>RTkO'15P3sQAvUM ,ݧ#W\]A?_ ێ9WJ3HO;2/{J8II\v\ܵ1` c>N 2]._~]?/u'8ӈ_MkS~yX& rJ`ךxZ5'LpҤ֟1yIgf^coSzL3]\DҤڶBxgBl_d3ŊG;{XKW,;58{]HtAĨJxp`Hwxtv hZ+q sԘW%LBs@:;u\BaPX Q`)S܉<$Ohs sv[ k\N"/ 2x0}^u_6U+ǻ;ߖZ}Y MYB};lin[Sz"m(0c^p~WG/ !b/nӂ[b E:Db(ťSgԀkrەToG>\)Z}њ|O?)#S37q{Hz &`\C#m(j'?{lK6GTg4y¾{d6$zX8њ'[*5رN9`>He&&'85;{kr0%Q؃GW6H-nr^F42~ Os$ƪt~ת Gk&'ʰRW;9zZz6C]+d?=tV yqgHY>}ӄRG0f=$ H^/!3Hф c&7yJ(EϜ#T@)vf|q)͖jS54 |ӟAo[3TDUʃ3A\$髝\&sV~rk]_SjY?çNٚn?t;4և.s84_ dG,Bp[%_lVՑ~Y-h?Y)dAY#Z33V ,oe1;tK9 mB>u ZhuM0~7#j49/QgpYzgImR9@Hj챺x{aAfhpԧ9{s[KaɸJH1JrC( 7c.1Ȟưj ?ɕr^p 1)v;2 <^czM 1X⚬/jdIp(OSj2!raPB|hSP^gܣv~Tob)*H=$8f8CIhAC.93i',endstream endobj 115 0 obj 5486 endobj 116 0 obj <> endobj 117 0 obj <> endobj 118 0 obj <> /Contents 119 0 R >> endobj 119 0 obj <> stream x][q~_q~w@o-YbJyXdLR]oK4Μˮ i4쏛yb|3ߙ͛y#; _x>lz]͓sl^]+)e6JOZn,ӓқW~8mդ9NǗ}ìDS5ޢ XHt s=؁~Egk ffAjѰͼ>o gxԀFL2Ӗ;3ᰠp&DBGn^:ak9}19enDHKWY;! VW/5DEE\XNI(۬V%u,nhVѥ&x&d@N>-I:y30 ˞vPvPFT.]UfكtA R^7:>N_wAiu eX\!,߽:4|#u1au]G"MDz>6$hh,L4b9[$ 9o>_UqƛSuC=.EG(_QqlԜ/Wy3 V;Պ)H[*4CEQ_Ծ{LV;xw%<WEUGp׾` +N+7؆ z%E=̕^Z9^MIoA*amq:MDd6ApENulc\3[!Bb t. wfo..l`xO'QJp2@d\ӛ8iE<ۆJI#׹FiZ>zJTPNx8x_^dq9}ׁmAB/u}_گ_ءcu.U@Dtr`'ߠAH^,-}Q"Ei$qnj|]fwj(*N-sΚaN5j":fv"Kju-t6)тiHU6|?W.գZPأ⩷Đ3c r1,w*NGDE{$»d 3ڌ(vpp76$+j9z-$9)4lhWY.:Y yҚy(1kt1Y>Cz<M*>F7-\{`~*@E+pF xb#_$ V幡lhpW ="!@2`#32p}o0k%o/H.)n^%@&A5A="j&a)^ftLpVhfXbIA_>lR6SHuD1`a3Hx'I&[us6prжӮ;eؠ^ GD@f S;U+bG'TFƻdqni@63%m ZS sVM܉*5Dt`¤q{a+pK|׊K8x1[pq%"ORz37֏q$(q^8a$lRt3]!E l΅4tG|V+Mo^-Ƣ6%a N7)t ^óԳ$ד+ݿ7GUu? \4"#u\Zݡ۲M O~3< `Cf$]Q.ly fM̱a3; 7B1CnPI#; Ri7BBLҞyja J`.mNr9'!6v;~tY-Ul1&{)/ N5UdPwPJ(h @_풣v/vݠ:b]Ug7FZZ!(up-1*Oh}85DRizT{ Q+,]ϲH DMaM|Y#W:)nU㢙( +HpsVi>ǕwR\^?+ĢySV 68k6T"4rd܌"uǦq" b`4-U#dtYbN?%)c~I_R1|ҼIĢݐ~_ 쓯ROw?q:#|e6hʥSa9wDzdgɡ] ?~KϱV4Һ7+0~F _RSr飽I]*HbC2 ?Br8 2ˉ)颾bq.}5O%Lqq0].b9/LI}>sSe>R2ŧd몇tƅ68 ~ :Ht ?lk'YA6"jf_t#XW>={8Dp}**vq2_5%Hȃ#{WkīiP(z>^j֥^Օ?]]PAjIϿG+,}_ZGW?Dt&Q6.OG "J`< ?ϲ8ȫekߢRHV[H@ ެ# `z gfgQIzHtZh>;iz=KxHC_13"Pn7C/ Ï[QՕu= +jsEz7?uųv;OF2$şӻ0TɭUj3Ar!&R̓맿`)ːB-l ͖9e LêJ=x V*HHxw=CJ7 ցj{ڄyȆ_?~lCB<%P:j {ͳ2+JE д ,f3:9,!rIzx4H /]2|__k8-fqVJImb] yP&<_Eh4Oڌ˸,fo4Ti(Xi Coʛ>1N'Y俫bUǠ%/ K{̰l8F5u}@Q(nʏ_d'>IBPzVXy]ߒ3U ̋sy~tVX8Ӡ6^` jPG@U 彞 O f(}/`~fTȒG hn^|K4_hTt6W=W5rus^3nMs E΀kKIϙ"(MM(!vaܫpdʝ dZLoҚWLܫpȝ xj/ &c#1HYD*QpI~GBt; Fzr2xM8x,ڤm»}٠ ,_՜ @Q_PϱȢVz3\`d{y:ӳm׬I5D&ZʍRN36J " 3<i=o~4om=xg}}Ρix+8ROW_ ~B;wp!śΨM`O{hfkɤѽ"}2b&z$VĮJI z@ quH,fpw$xB գЦťl pi _CyXgzKGaĜY,(?&2,lvp[<4}]!2M>!J.a?'^ C׳nZ+'lneC}dr' H?!ݫDOȄ`$bj8hiMax] w7S8ߋ{r{K9M49U)KeCk {!`EBe$=Z"ý_aYʈd6yT'kwP ֜+6vcÐ{ w-i=ǺR7^I= 6 'Q>8*ʆ{(!+fY +U% (L4ϩm`yMp7дLA3(q(Q4c:-? tQƳ"FxvxE1'bO{4Mbr)#Ff'ړ<:Q Z?d{RyQF8c'jOetIyȣȞU[[Zy).M~G5Jx,q0SV:(Dc1?-ڋUE@ NTMgLQ w5m,q0SV:(Dc["hUIhJ<%|.(TJ?Fퟪ^:G de?+Ƿ~?h<.ulʐ~[^|ii1#&0}K禘(E%9'Ђ8P녧m8">+ ]|_ u}CJ{krkQB7s5tf`բRS= >Y%5UOсV\{T&7L`IZ7Rp,Tp =r+E,zC%F dMURF^BhרRyZ(,`wU4_=à-}vF>\]MF_[d(7bn_<cHZfzgZE5J\5̀Ѫ y~,iHôVWЏG҈T18_Dc͟7WGpe<~?Ghendstream endobj 120 0 obj 6533 endobj 121 0 obj <> endobj 122 0 obj <> endobj 123 0 obj <> endobj 124 0 obj <> /Contents 125 0 R >> endobj 125 0 obj <> stream x=ےq]K*IU$"_ĕ@*E.t7f0X2h?ѺWO~߼0o.pQQ~~SZkBb:p(6c:<}u6GK|NƐ.c.^~quŸ//O6F.yuՆ\~qeY)'_^LnU5κЦ5 *;O~B=Z_׹cu/`NaW1&yݻ%O-,tZs^Z-tMl l74/?oi5&^wW /'OdﮮL]|/~>S+U=c5*7YQ70.g|=ϻ3B(K\tlW>#V6t YVG٬Ԩh^=}9osisY>gu+'DA5㯮Q(nEM۷Z;frhHoy->]O9䀻0iWKFc@G9;Mϯ*\q-W$lؗHV %DA/_85t;| CYfNv$;Մn*gÍM<ЦaEz`zM4 YpKאN~MNV[;gs5\:͌_$ƭl6Sozٌͅ{#eTRp(P̂S~e2]QJu9YV^=LV*'y^R(f_z+g^3 dw2xm\9 KyYd"aZE^W A銋)_^}mIw@:OZjux ցl,gW=z:澴\wgܮ^Qkr6nׇur4!8R I5[~%w^&ֳIWJ3Bk "<53\8S't4dq~M X2"$ t""ݛ9ӖTc<D'c}cIwQml٢sh+[BuN#lROTjj"F[ " Џ52Kz^'s\iK[UPnM 7! X0t3p:83QŮ] ֵ )_1ݟڕ_vCnO=ly.UmvtomsyP{<{e˚ϧQKj}βk:xDUO;Z]^{\kAՁi^Ljq+ )=PT98]R3۪뚾T^V%6KyV0qcOnA|Z>Qe!/xbCVO%a1<7k|Dx 5L/uG,am.r gn;߾LQyxlg~ͩh mU7Jwce)Vuܮpa\~o WGb.7jռ©Zg܁,"a]=զv._=erЌ !T6~˫|sj*Sd޾NوBia|Su^vŔ٤*@sLejw;\Rbq SCZ~=@c Tػoոit w 6ZgNR=O|s^k֠|{Lus!E[rrSe_Ο7im:*IgVe?U. W۞[}|LkRÛӰ?$'h&&}OtKEѬw dզdU _ӭn^ {$41Ǣkc˺>i4V]]G>Zlyy%?{DXJl5ڜbv^LB9#ByRb_ O\i={Tܮ#V+o͎ 57>ρ#왦gaP)iPp5M:9N+oG+?PuB12T/hkDhMQ%WjtNSyP3R3욊}qm`ه;X~T|6rVbǩM|7vg{݌j6&l;?O\:l;.'}wFy] >X u~qa?ǧp6h&8q<7pϸ'nۧIWV[΀/UzSQt$ K0wM{|67<ݣ4,RO8l>mFyv퓰/hjȌݘ.?̞~|:Ydc:}4R|+[+n2۪?Q+!HF~įx?0cү.ˋE/ZA5'/axzW?d8pw;l ܶ&}A00F(*30cD: dT Ȱ[01;z༾0 " *LLdA"V$#1sX ADU+8rg D>+,Xgy^Aϭ;Ǿ.9^Kda4c KV@\%x/ ,|x(>11'n^o5;5w7qowpЃ w@s`&!"c#ϝ N,Ucw}@T1q,sl 2bgрG"wƫb7TjTE< UĻBG>6 |qbB@czE1'ޘs?vC@?tF a"-35i0ъx'f+Cg57Ł*!&Z[h':;<+,)l>숪p(L Ka* 4g:/,\( r߫z3g:/ $!#{UH0(۽֪%R}Q!XHba;iE \d301Dq ؉,[#K/] $U|F%E-K&5DnXBz0I+*PDmeR3sAk i:jZ"JQk,# 2XUP= Yk:EQA2N*E[#5q4#RUW`UKUAbi[XeҾe]`IXq {{tk``0XY 4暢\YuM@V!`Ѩ"hYn+ |W6%yLV-k%=/BڀAcfYL67kMAumEhYo] lcym^ޜ5b 812Hƺqlހjf1<9 ۾5`pu#o_EQ*^| B ~E FY$=Ǣ!00PVPɊw:vreWsea 5ϠL?RcY* Xv{CBʞ2|mx7;c Jςεe$?j[$-l.BgzKPrtV18X]1 'owc8_Oۘv_ۊPC+/8&t_Sɸy^{Gg'OZog pQ \s#ӅC)d",Z7oC#YJHKk&l$nt,EIM۽c՘c2sdo}ՙFE^&tirWӛC"wLV%U.*ʄ*+Pu't(TmW^0y~o*QўT s f=b3IWDfB%+z$ ^$#ppǓ̠x'a7Z8# @|S Eۿiz7"9">UO/) à[[v1ȴ!{ 2i0WeGM碝6͙66/B|c]0hPE`0zήmZDE[PFm^5w@1'wBٌ`,0Wc[pBI{cL \Jy[b-U[:PܶmokeYY%y,3OAPrSu|$_*f{mɂd`#r稳z&%U$|J4[Qef@J1$(THcQ0s|5Fc`"JxOʋ=O* H1HY=iz$AIkT*{JNz<#0$ 5* lxlJ{O=Qa*2Ulz7'jRb,W KpHQvGsnCw*0=-q$$F@4Tk0V#c 3֝(_}b40;`ZtͫX!|]PT"c iנ\!0zם}-[10QD^ůX.icCz^\@Vڭ3{ DP(28Q;,Pq4#GPGl<ߠC G*!r>vj1xY[cյґφw_vsalΣ+1C LdEsT5b0ͦaOR]觘zuyD\ sਅ7Ž.uU=ne5^NYr*Q"R,EȲc&Sתg A pkQA;ΐ!c,x lMCLKk:WUI @mI0 y0 ӌj}ؔeEK$WɈFټYЈP y.>tn9v`ҨȬ 8ieFZ7<|V= A20ݞ= 2?&zv*+/E ,9uhlzKT,tb0ܙb!A8\> ]^/(,FhoNh `/,7@5rkP nt-϶6E$ļZyAFǭ!5桠UرM`vJԒ!Pbn\ *5)$g  2&U +HtT )Iy}+ }# s=R ky5w`_w8tJֶ%\@9]SΡayHmXFR@Jfa Z3> Uc4W;nm@4uըO&Zw6oce4VOqUQbDڍr2—~ojEJ\#hX3$wpLdEsT O*p(/_r<,cء4PIy JOuX2Vy4wR7{r=;ö9'8W/6z90O=q/RrBomj1>u nolsdҌJxcy5V`8gނ^'s3Ys& 4q;4f8fG:'`eЌը0&t6z 4VHce4/oNcUNLTcPtUW9 =6s6BJVDdӱ;>Uvb"A{jfNo?; Rendstream endobj 126 0 obj 7863 endobj 127 0 obj <> endobj 128 0 obj <> endobj 129 0 obj <> /Contents 130 0 R >> endobj 130 0 obj <> stream x]r8rSMIY\ wd)ofvT*5 y[Zwɫ&#;UoXG Hwu%䮶凓pR~>I_)o=33߉۝=Gnt;pu%NnvQ鮗TW}-{'Hh-ӮD  s}K8S*㩽ksz=N5RH.gC'ˉlJaw C5ٕyxIfkZvjgSa<,z?t%lh`::)ߒé4'!SyE?_xYwa_ܑn:r-959~PpSs=zMt::(tkb'?ޣۢW2ގGҫ< F[+s(fZd7(7;ygF[6&ZW0zE?BdƽV\us뫺6TȪwt»m^3WrDXv9u3Y1eO׫w\r;].:;/$l%/J$7| }DhǭJ۪% ~_1',PC"wx롧_i8Nv߼zfVjVwaѝ^]a cn)Tꤪzk pԚЇS~6uwZI HPK+Ť9WOelEf螑^/_ 'z0hᕷ"#Pcvg!.CmHy{۵'ijPogwg1.|Ŭӽ2ȍ> #R GXY[0ɫ<.zxZhC0aNg4θ>C?9̀Jy\?K<$ 6IV{vsoF<* 2݃P~ѫAf c Ȑ[.Øjrv&ѠB. 'aO__bljj@-)Щ8r!b=\0rg8wlWDՏA2hfMX'X;Pa ; Z蒑;zD>GơG"B( n31.wd=1&VsƑqCx`{PCj,A„sc"" dT/qP q_ y"VO=f*X lT3.H>K@G >&mT2>Ea-rfnUqJ^MDȬ:μfΉىnGW%KA<^@G1]s+C+y| b?PQa fQ 25CT}7mglЦ}=pjtU>4?\E-4f>epWnEVp]9u$x"1<f7Ns*d@r?=8^'g>fcLl$Q܏`x8 q[j}2 jg 6LE Nzgvlâ?LJ_=yz,:3}~fNE%7(lԎbTz9>;r~>G0+ uǎ,HE7h^`YQM%0K4Z{~W_1uu.ii=II`ڸGayw-j3u6>oj%ӭ4g5]G;!37i `VWXx2TSDu̢mgjrQ+10h_7>0V$z=q\,sI3}退 4, {(2q"}EC2k7'g&qI=QQ[1قjjҟ\?lU9W0"[g~k$yTOUW!شdY12Zk0DBaY@v^ `xVsK0A}>IH;#)œٰ61UM2BD&9 q(h*جu[Fٳe;΄`٩@KOM'B_e9!קlDŽ/mIl%.g WB;5}w?$֠~@WaԺK\~sC"0AI2m1…Va6NAI`Qh,*aɈ>ObzM2> +A'u0gEj\qVn8*s; ؄⣲@- 8:WbI6Ck`(B " x*_b^vMzRU>n73d= _x O`-ViTeN9Uqe6Xc9w4CF~:S,'>kKmXqL+J rtx-7)g0 7Kɩ4  "&bW`YA; a4^ o%?,V*xqD> ] g^Ueb!W%Vɴ#* Y.,lñaz'ꨨaet`,c0.G5v1vҡ\tfthB`Bh/BZ*Rv& @y['\2ѶLajy^18:]>?ٟltOĘuV &Gcp$[.έ. `g_.ÁBe3'_|g=n!8*א޸!cc•n\k^p5#kXď3xKzӞmMҺ5/vpnmퟪyT }Y%~=d A]Q'2ip8{Bn$:6;O &$6 Ђ2l/C+f^G;$㗿IcC欜ش(BP:h0%:z1+bF[3Lhqh3rqQ5m#W fL-1v:譕$ެG&rn7S IͰP |ڣW$6bw&Q¼BQHeZXŽh,B7~]IҮTg݇r#r"CYf犸:R,3@ZLX4xZa2N82d[ IhyӍ"F@Ҩ^Bǥ[^t~+2.Oyfr*o#ԵOJOK6z KSf/yHK'!!5r$s~]:e~?0^_۹FYd :fpx.@+FKC fZb({㝘Ү)Ι35F IUh,7 kK^ifwsO5y8v]HS{IJk1M9 ٫һCOЩʥ)'؞8%ENzjYӮc9R6d<ѥ*[*r;u, Y5mjPҔck2 5Rfm@{D_6.9UF"U 89rli@(yKr2U$rn'P㊳ RKH>zA C:#EÙ$DLy;Ŏ vyiq.=T(9pvQ*,so Ή%̶;f^0ۄ&dbM],RY'wnW[8ᔈ/@+B.ڄ9JַbڍFܡ gqiA@Ayyś&evv9O^L\6 c] cr`,).R?v'h Ā' O(u*PLJE@T16TBB E\>0ke=%3K`\y++!,,d¸,fU$PSs[?SЏY8y^P$>>u#-q2j'տN'%!lOZ/h"I" "ǔAO UuqVn/`mdcyS3FKi#I,*U5DDaAIHBGpJK Ƴ(ʻJe)wZlH犲c:'z{.AR0^xF*ǯDD`G -%9: \j|vlY1'NqCyh <^z@]aK+/'?ֆoṭ gxԓ,gW%'ox:K4sá(n_˺Q"Yn9'*˲Xa2KLdKRE=yVWPQ#h.+ B50)d|]f}45Ix xA="unL`+i, #Ao2OQeYJI܈~Ġak w; s7?֒3s|ݯT_u1\n t )ml[RJnD\3m& -_U 9]Y7!_\?+)4" n5yAk[UUC绡?ؗp6ѶAb?%| 4,%9/㉘դ\'Q[0wR )#:knn1}/0&nỨ a9+R {<*?^De7~.r4#ߧԓ?Tl'`^L|If>:x?UA-eC]d7gxo}]'>o XeLZ&l-}1|Mヾk!4.:J_\zFUjpLA LjB۹ e5 쒩k#RksSssFiL#)#' Y(+ZaR#!w(z%*f<~)YڃmI~'uE|c┽ca3ְw%LgEk-n^NL+OsI\x@İ{H}6i;V+q6ՁK͇w hQȢ& Bǂ;E!|Aj6=ڰUYArk{S?7~B}ٻba))</{~e |.>y ׏2^(Ą00-{O,1 3Af U%EM\2P!%el,o ѷ3>b0~)#@o(gܚ=`+%сS/7TÓɀ-K]/nȲ%\_(گ›Oa1_r"놪m ߈YŹ8M8~|ݽ|J!jqbDm6ͩI\B<34R$ s9f+0' % W:藮e9K4&(h Fvq5-֞㊝#F˩ La!3'OI9.˙;[ W86Zl5fAV揭YzJJ+C,'&cIf٤t>x:s1huIIendstream endobj 131 0 obj 6083 endobj 132 0 obj <> endobj 133 0 obj <> endobj 134 0 obj <> /Contents 135 0 R >> endobj 135 0 obj <> stream x\r O1KѻnI.%N$R);$[$%g'[=CRKe0^>,ӯ'1KWN/w?qӳ^_ϧӯOR!7_:rK;;_9 fNwFۣlp`f//?4.tV-gc\Y_h'Nq @Nΰyyƴ:0K a0Vs{c,q 3~։kI&<Ԏp8V}t^:v^/*Fw+<ڛYJ3d$mgWC4:ln1&sW{0>M^?Cg&狪p]4gyV8ϧ.P[ osՕUr%)l`r/'_ub(>Y0㻸AZo5ދMMgh'cCybhje3˗^饬-χN ;Mqۿfo`Fb]`BFDBV(t@kqm{^yezg#WXMw@xh`o߮56;x{߭lڱ^P9x-vZj{[# npu=5m'!B7tЀÀFfCc] *X~Wut@<'"h0{dgli৴ 9U!Cn'5nD#b` uʼtd64zgtu`-˺uVwV'`t V٥|׈wϒFHr=w3rjnouP2.9w]R^ y`u%KF(gqnLb"t0a~OOOU gO Bӆ׌"m^m q6/GB+oўpVqZ!nkYb8 6ٌSwK4~Vl^a_XvS[@^6ۣn1X¯1듑 `GfU-levŞnv w-};F*7Niڑ͗K,X%ؖksw-*-\'[iazy>R~{6Y ";:=yD}qmoGM,1ĉ+Ou:PUu. 45r"b"[qESl՝^Fנ{ʣ[1V-KEn|hȎ8jݬ:qwlxb6[nUp88m}aqlæGI]Om3t>U@‱'[ͻ)&;x;[3, O]{_V<ȿ%Xn|rڢn|'gx7LЗij9K=\ٍk̇դ0fvBxt3(=髝~3@˝RT).h5m2qQD^|irҠU%uAYH"e!}5y|Pf{MxfuG͎@(I4[:2qNeHPHGu-2[J"BBm6N٨&if,ZCٴƗ@tSW26Ѹ)'h 3S$U՚QŴfhu%lM@l Lke՚ٲj͢5Ha*I"َ!֢ 5ZsH[f'wQAF~ w^HQm`h+_$/zC UBv:19'ѯL:P9)cEJǖNs)O>v[OF #V9IH5ۥ-\8jlK( }OAY)t~JcRIڏ#%:,B(~`}, ʩB>ү 뻩[瀍է Q=p~ѿW3((Y Oņ_dbeU89q>b}Bm9063 X%vocǂ,dFH(k>uy˅)˵n36\ ̀u/ !AE;W" {)s%X&m }8n9[[C@ tA,{ʈh v)mrCzNU.֦HR$*#.ȋSi"\ 3 :DvȍɊ͠`P-f[ClJqwATLBYPɦ:Hh:&#q4aETϳPIJBBeDJ*"9@ZI$WXG| .7 -2Gy9JP~fub) վ,t n_ijv6jXega8Թaq!U!XhD<2,:\[32Kq~ u tQjsL&` .]dw&Y%Ҋ]neL1wΘdYׇUҤS_Oֵ VZN1AW $j} 3; 5endstream endobj 136 0 obj 4070 endobj 137 0 obj <> endobj 138 0 obj <> endobj 139 0 obj <> /Contents 140 0 R >> endobj 140 0 obj <> stream xݽݒ%9&vOeuu42kՎdk2.jZSU5WЅ^Nć_?#2#Z̋8p:H8 ?||~7͛K(?||>_·]}jx#=~a~io%xw{.]ژyo߽oWj-׷쩎 ӻTWjć贈2})?rs5\sy߿97Tc/{~96ߵM`K=}:/6ӧiye:)_Zڶ{Kx|Zӻv风0WeyV_U}j'9s~ 7F ~eWZ>o=0Rma<7_ Q.?.WGm'[iro <,}xs,+^>pޭəC_Cx{{'BKOTr"꼹rxEr۾_?bW\xJz(>~XCOh,tCmVzJ5-v"Vbz Oܫw &RpSr/ץ]kv9|8RsO-OU }{wB×YHeUxJ`b⒐Rl'dBAC?Uܱǵ\YVr|w"Z y_Z>+'C'JWo\*.T/j'~==ɧs{ÂFkց4H1_ Mmp1o>~hue,>:^oIT^#t:Z_Y(x={G^z @Kӻ5%^ңlព*.] YDOܧ.<9Yl4 ꏝFliVHOckZ(>TsQ'֙}Ԇe?.|Ri%=]O=tO?4fr_jF|ᬿd1$β0d'Fsuh;7 ᏟҲ?;C ljb*zi5 K@<>8Ѱ\cX'3iS$[!Gg#qO.~gx:?qrjgfGQ'O`tk-|t/xn]/߭?=连g!_nr{x-er]||ÊY| c"e\}vi߼ʾVDw TUw4̍JzgLzwf;f9 3ImfwIٴItmsRMۜfkI6msMf5LjLz*stImf1̑7D#v+M#"?p V [.DV^u%ys@ׇ7_͛zIy>7߽6b^:=*ݛJhN-.i7ivv)sk摂Ϟe% 1},\&ŧ˚Yʹc!+̬T`ڐR'ސqVde$e Q q]l k i---m֊OZ~Z'hV^&AʅG6|\^Pe1iyHv~p՟o߿뗴m;`QE[ߒB؎oUgt8ଁ V>˾y)DQN0 gy8.o5 ȯ5>C6=R/r3`8k__yw'PF䓖?S6vj*D/_K5'{俾p ?[KJm}is$},֫k,?-g|r?? Q{4fn`RQa~.⊫keFg۱_;)]Ҿ^-]ҶPy[MGJ^cX?$Yuǰ/aڦתr7m (\pMM;dR.}&P[i;b们2H_HknQ)&ːV=!PPӠ͝e|J/}@mw Nx˜H3[]{EhI.H/,nF߫޽ueh`PWҺЊ̀1c]P!T÷xI"YQ[v!s5nGy ;+,̖ԫ^ȐWAk P25Vo|H$UTZusAd̔o!J+S0Q:?a\:[tau-VgVwibv7v<]5m8*#ukm&ejI͎9m-[Xʮ)]&ah[tPpD01esUL50iR2&Qd8'MLMzG=ěG)ݤ7w:}*Dģ+&A.1~:Leah#R4?0en--݂ ÒL)cq@)u;Z"-#{]-@F^^oQ+ZFuoYv7BZ@&bo!nBΤ E+ZZ#-K(3{$H}l |TkkU\iٚ7 n>6 7Imy~xSi6^Ң2Q&$Wҡ >{v=%طqgQ] |&byIL%gRM0 x8Dte\>'v7 w y󐶆$oI C,40~g=2$+ad]Ma%1"]`bbG Syb|6ч&^7aܖմ@Зوbl^aشe5,w{aHF/ RelgƱ:}hRSTuǦnNeul.Bf`!CY6} n(^RjKؙhˎMݐuRcH HSlbSm (KɱdU] 5Y'h@z5:)œ&s֦b Xm 33c 4ɥ|-e K&4jiTH'ͨJ9 [5a6;d*^5鼦%tS β#օViIny71_cMzZ@hGNd;Z } bjDkZ6#l^HHЮ-A}KSƲև|,uԶU_V@ -h[nR47%Kʉ16__Tw鲍<&2NB,Zzʔp=A'2ڬ&QLzݒepCQp> 0gljgyɫ؄VmBj:5 OYF$&^j ZmSFGI)֩PjؕM&:la|ۘr 0ABѰ5+kz"N R#Xz_,j(CN5Z"&RryɫpI1.NbfnV.ADk`KPaoMH-1Pndd[WeYr-n] &,ݞx>օ֬E@#]N7sBI2ӥޮ7gJV1 qaoǸѣ5=(6d !`7 2d_uUm)`Ͱ!H4Ǧ Ʃ=`Wz0%Va86}6}plm86Qf*h#dʙ5u{(l:ݱCm.TTxuZH6X:96* $ N,-'T66a2e&ʲ iZXcm؃86U V%?*6y!Hmg`mꢺ@MM 96}f ؤ]amj\ Qv2U#6^W ^mqұ z m86}f'W3]4M}ZuS~ױov6B}N Zh- ხ*ZEBZ<)(/Sњ1Y9GE>5uXǾ|3iΞ6Ozt<{}Oٷ|ms;}ۘg6ٷ3EbPxapɎm8hD`$VKF nM˘ lR;vt1̬u#xނsGGG-ndf *7W9 S_Ф6t׮ͬΆM cCkЛ zql(uNn ){kUlk׉2W̃:z=[1l( C|GlWv\Od3%ap Q+m+ q|7 +1OMel?*/X=W9v6@ wd͙ᾕ; 9g=cJټ`Ř+kvz0oxd5;BM |iۃߔYDiz x}*)N{*|w䝯~9G؃Qaz]Cfp6r}%Ѱ`8١:\æ*%6Om&lv{{e1p3"cwXͰu;25b)pzf0󇮽`75}}@xYUp2v^cRfeLpzC#?#a(hy)£)7OvtN;/f % DR F X-Z" 73yЋc2XW8& ƽ~Yl$3KkUe#=)=\M,JT*:ډQIL n6硾c!FeOCO3F(ܰ"2VZc7 d)V񞵫®&7[Nn} jo-\yfcs-3`1`PzXϨYY XF{䜗.>[G+d PF:&ҹ]CS9;s sdSʵl,M#X5X܇֤H#}220˫+ 2K"ՈUg3Uc>hMFjqq}ςe3UNgi􃀉S82 .0yfZBYXGA^KL0\g5++(#us),Ti IR$ML;l}_ׇ#mn>RMRGmф 57EMps*|ڢ ^knBP &Tvbx ut<:WjךP3혆 ò.JΈj ΚŰa:;1G4.rfB]`&Ԍf& 83f U] 5#P3F 5#pd0fx 5Όs.˾:r8Tc̻Ig-aYWmg ,5F75+c3N|$29p?NQӝfq 3vtcG8_l(fo"iD\GǦC? [8d&2VF>`,e KwCx)m tcйGu!]t**%ߧNUT#&q$EVr'n>Ke1jX53VF3pkV+(vS\yscԩc{Spq+e#bfPg[5:hM˘,4 pvu<k7 65@րϺ{+kw Ky 5:ߡ5-c+x#gjoٻ%ð-bm=қ~fYYX_]r G3փg2֯G2N[UhA36A~gZ$?~Pfʍ۪"/쁭e#bayg,Qs"ִL {d2PϸBs=f5KgHkXd3K筅$.}^*tܐ0іW \4),N۹\\ DD~PaPn*l{(rb/j/f;r0Fa5RkV+ЈTj2ڜHeJ*ў%, 5:hzW|PзG(t 4Ͻ~~3KO1HAѱQ 9J>X,VKgP-uGk]AUJJ Y1(VQdYR@#}NҖi>ܚ8c)lW4>`5% L)jXhS0I0ԘFe%bX.LPwzSt&k7!qݰ-Ip6ܚU!Kaf,eUKeA`,&f2G >W,evWu36\RY&;{N{}KeA `\RTĽ6KeAPlTr%`# Jmʂgf\`>X* | Y* .sT7KeAqT7KRY,KeAsTJ-A ʂf,lA6b Y*KeA`\JT.pKe<;! aRKe8kr|OÆe-'Æ,9v {JT{ U:,frTRY`$7Ke =X* g&PKe,T.,TǮRY`$7Ke,`c3lN, Ke,FtTRY`B a#4l7փ =X* ' 0Y* LRY$ m{^,:lp`c= "_'lkqKe*ˆnqbL"(29{*]}9lA-hu_M*q$hJnʘ-/<OIkDC48s q+AiXJjtњ刕5d{K&,l, [ዏ$wvpp`^#vj7n yk\͖agYh:c ܛUcjDkZ:+(_s=|p]E9r5t;7Լ`b[+n &-6lϫ9G3^F-ǫeiXLz˧5TcA Yh\w/\e tSe-/5yXhIRj3nqF0jeQ+,ZgaJ? VYOC0OW+| Z-"#S%V FqGC)cr\b#>+enx% V/YDuIis)]f:wV?Uz4ˑjY d^QT1-u ,/gmǶzKюtT7M̢T91U6 =*{Lٝ/'i_>/o´DTm='xnK W!o*jOΫ/B=2]O?TJūaO~9j,ɧ)P0&֮)QKtxO9]385S-\pޫV55 ^ΘtdG0g o4OnXb]r d`?u9as5g?]Yk7||L9TS5_q;Z>2z\ qrɤeNHɤbdc/93뵳"F5d=^S_h_&sx3uYNūT5pɾy@%p!yɫA͞BH׶},eSeS, |bkE˗\VOkF;xFq!E䑳ǵhJex^* TJ&J"Z/D l/*J*}aUZ D s kC5+@(i|ԃ22jco`HL#Zђ0r8xDDJ n9Ѽ.N[p9ԕ."%3`X%&3ˤ[͢!D^oE auoЊdLjtq^ q8WJ+V0[+$CA]F(Zj]1] \ .TV )9 vM]B3Rzt=/ u< :8v5ǫc$euGL :d z/3~R3$FkٷAn>@ @gO$N7m7es}3 --դ.nEJ:cDkQO -&kV7u=\ȑe-bH@>jf55-kc { \eTYNhZmJ Y&Kj95M3Y6b`k"X5 @Y]5+K+Z^*~=VbdJT ?)Qe,3ֺi vY1CFT?\gD-yŌǣ/͈Z?V2֬^)#j>F5#*-fQAȌ![(K+y"hXݲ<1jST3(eS([}Tp (jeC"nj\|^QϹyQˡj@87i-zbXL5>=X؛UQ- o 0ՅRJ{J,PhjĪpunT #T#ʮF%#Hzw9neP:(YV4et0,֭][њh腃Dt~4EcDTOI.(uz12XAAǀCcd꼟82ZFdxEHMSpkZV#V5O''s]鼤a ޤXR| (WՈUg7Kk1ִlD1.99uCE噣[L8FNPNYSJ12VCrkV6"W]K3X(*ɀ&1|/y[\Ao~0D ?"{ " "d ZŠw+5v2Ce5bw2'Kw+;ZF;d9g^ y+Cw{#P-xÉB.ꋂs6~P9ILKZ4cXlJjj@zaX;^^%d.7r>37̈]As$hn/aFoZ8puza(h <:ua5+%`=hN| *v<1S͠O2 [^7~XFiPHU3u82As3#q[l:F0R)#PF̠K,\5-iϜ3қie[zڛ~F>hJ*j s^%hntM,l.<;˖k0#ln67,( p.lZ͡lX[)DM6֬Dr-%㿐ڼ_r5lrFyɫI%:Zn(/9LKVڢ1sO-4ꛡ!@nx\me[X[mFLQ֬U-} T3( "dLzYX%n jRPS]tV_%f. ` :/yN8[[xv.s݇ss]>EܨB0ڈ͠~@ݖ^E".j7MVc6P [EGsT`];diD"IQEtVEkq,/ʱl5ԱJnW%`y07 ҙsֆ!mڶ8 Jg8\ "9Vٮ.UB R=?BaaӧٗKc:װk K6qFe^Kc."a!cPN\O8fe=b>gyc@o JP(eK6PM^,.㰰2K)CN5Z=bK5sm!u][MBY@>"e=b!aP%bܚkҹH.lq8t6;yX|\ @L`i7Ffeq!Kyiv9:]|*4&n$):Q&qMp5^r]Rɝ{)v 5EXMWǭY{b>(ْ.窺}\(ZCHD*,ryЁWA`|E#z&sssWǨ1C6C,,?sa/Zȹ p^0|EA^[% ^ş~ y#U~>:p5#Ǡs:Gs\/&Z,}GK{ jHŶVb۪Ů}[]i=`һ]ۜD{6ٳbR}sLzY#fԦڬCyvXPHA٢2B*Nrgh|YTgƭH Z@#_"I$`W|xE")$åh*2:7a\֒gzE!G5x[Wi,.F ]"Y|8/y礼fq>:9֘L{I| BhAX$o ,'hI¤ν I.ӌ9 QAI&C8Crqi. %9`X @(,gVF8Y(u)cƺ,(/&L-D{+?*$54 YFH^]쥏 iBf{^]z>*tк Vr)uf1[HJ͎ZjQZJ (5If:X/}*(A(]f٭t57RPZo2YqG  Vv_hNJxq\QRԴ 锛4M%I-ARQ} RlԊzFNr1Q C~r2R߃0d%ÄLkd,œ-LSt(qaMHgUǁ˴!KVg{/EX'uj׹Jɘpu{B*ڛf5ܩl G\T9wNK&bG8`uwsEg̐;[[ 򞋺hEK:cD8L .a֫ܺ@ۆMT@Ч6RUW ]M[D 5FͱwPYas7ުFY *KS)vaԛL _zækT'@H;%`cArV԰1-`ɧ_Uji2*ձǦn(MrmJFJ x}Slk:DNG/;v:!B%RLz\S &sGVb X h8KfhKZ@bMڝSchSRs;k!x^3s*±j ¥ 懲 vf Iˁ)-A/JmtPaVNZj=}*5jKkZ6#W~/&3f+/ƀZuJdK- e$l 5CMZiYXka` ^FrkV"V5bgGz# ,ݧ+W)jf{ޱROb_Oە1{#kx^,v g(^(S" 6H%`-8Gk/>`QGSqO`j IĠ&Ud Ѷ,G5".5$ltC!(MP"V5".2gN5~'9Q)y,P/N5(CN5Z"rvMt+)j~^ 19y1a(`iLM*U, [FL~-ְD-e9bw<eV[КՎwa;mrS}Bؖh9,]V$t;ͨUj'Ͱ;96َc٩솫xɰ*jAn^Pu2%4Zώd>HhذRLǦϜ>ewlmwllW]mSIsW߱; "Bv3^w0;hf "z/-#6ȎM倝0l}Dbؤx'èkOYm&C9cSeg`ͰIe3XnϬxV*Aܰ'ކ wPH6" Yסrj6{Ql"%|sL8p$J;;U>W&`24> g [t,<àZ&hnhX=' вcM!t )% 3&Tz")󑒗[=%YcvO& DwGd jt5P9`}wDk%tю}N.G߼POdܚۈ{dVdr`yTjl*j׻aÉl8tkzx]5^6ݩaبl86]&m|Mzx}Z mvFe{=6g!!E_cʷz6YadnѨc%ez-R5-)`yFB'JSAfo|u;{i=S0}:qm}<+|q߄|,l5Q_@*)hǭI!XFI/$fe?~͌Ӊr,"DLHM3 `~8d!Iv Mql\r1lܜ*FWM257w6Ln dvwS6^]h=dL &Y$;pCbeI*Ivi&Stl3In$;ʌcH6Ο;G,AP2 |jp/2xZ2VijR:+xڅWHW|27 w7S'm&Y9.y&l^W @56ȋ`2)5fbnM@b=|0/o M)AƩS5ZAi2Ѩ֤HƉ¶qw8qa9lJ Lz^@eW*⽖5*.^Vt?X)g^V7.в-bm&қ%5pkZV"VWcp*GS~ƺXoj">z!Țt:ħqaC$AI*l4>`5μo~ $VE- d`XcњUbwh*x3s^ ACpmqqFnMJ*%Rp,5?![> bYgp]ٰGW8Z>:glpbظYuĆ۰qó:6CQALBq| 6!Z> q:Ѧ hY> ǃMY>CgF<0u:6 [vdž3;6펝s[|G|ݛ,])`,F Y6l\tgalvxg}&X> F欉;ztd͍1$,Y`7gr, ,gr,Fw|qY)Y`7{|RnY>--&`,H[aO'wg,e𥗀VGkL5n,^ðqi39v +z|;;g5z&Gl3@K|\¹DlUQ,ؘDQnjc{ ^( -߲1@)òXLXepJ5[2СXF [Sg'-|҄]Ԙ>IKiζQ|5.iƕ};\qUEB, ՝~j\UWٻ`Mr4d@D5ҋU>V5ڡ[Ӳz]#tcשfPڇ@)fTczT3(3V5ܚkoWTC$K,mJӇt}PWYPkKD5~RfƒmADFzGʣ`ٲs)[ IY^hq -b1c&QZ2v.9祻8EM<7ؑnkC'q&J~=%WW Z9"2ǔ2% K4\i5+QO!1l/VA}(nG|ㅷu&xpnʔq歌0oey+,[gaJ? VY{;{u"< _. K*{k|.To0eXLbh%~iAj ҚWra )> endobj 143 0 obj <> endobj 144 0 obj <> /Contents 145 0 R >> endobj 145 0 obj <> stream xr_1vbَűe!S<x8/13; .J(QC:.g{_k>N7<~{n{o~WFΨqi}Y'^q^+3K;m\w g/0Z 5s;b_ӂ?\?'Y#<@v:V87VqNoZ+'zy:ͷZ,3Np1M/:nv3Cki^ H/՝Zbήyimb>OZ87߶<,;o4eU@,@NVb&T<!pˢYh4ph_ ,T(1hra/k]VJzUr=g@J.i `ox)s  ɫ~Q!^º@o~SV4Fkls;ډ RzV'm ͹ {vCmv+ diйhD+ޣ1Bu0ƻ~4SN5wx_)2ϋŁ;Bp ]2_ !*@ R$qC,*)bhTՒ{ 8wt q֠E1 ț@ U7.o2ZuASa'x~H_Q.T9@VyڔKH/voPvNi'7]7d*ôަ,K\N]\؉W#K$<̉[mMMwrLTE 6ZSWzɗvWBUGc4d0g)kPmQ4I|Oh`"D y" D!YH>'TRTYRZiEX=$ !SˆD͢%mT7&;$ z#RT"1\.I4QAi "y;v]0JQf"+>X̦I'9xS1},rٿ=ͰG*J0o@9pPP׎S]E3@cw4qճNtdR+éhŋ.G [25YT/J0ZYyX@݌܅񹈶77Ix='j!8Z2E50\qaA\Wڍ uV.+tgPS %xh @dܖjl6i˛1Z 4X&m9&4ŋا@cm? Y7!e%^dJT`:~M؊: olNdia.M__U"t++Zbc^3׈:QʿχCש>[!1)˞b=7eJ`2TdI|8/1 `@$m4 n)`9&qZPa5q.W}5I 8W9m-}Xm+[D?&Y/؈8"uœ؝Zdo\3LO:k$]ڳk#A۰>C:o@f@vGGf4xܡ j#s 98$Y#; .ztVkaquW:Uqi1EJL`K+HN'ѝCRx_,5/W/[c٦w8]` YinJ̦ W/$W%\# kxYk%Jw)E;\m f_#4MJW_]F/8H"iRWg&f,SѸgE8]0^L +ݕ3~G$ޡ_X-rv2WSi ˧ Mfc1mФh6s}=|m=rS;W 45z{c{߶<)MwVzFE>ki^2;-e^{~}px%p_>/Mڪ T3Wި+` A 2ۮýMjG|y*? ~).'\ i:evҶZm}N,= >~3n[>-1U?Aاv|:!c\:@Ͷlգ=+DVsVSis DH̤tK#NDaA7Mh &~$mRE?/>Fy ?]/>w A6sn s;2SK{Sm>k:O/ K .O6 YP]HR?][w Z -GV}@ZY!q !=W"-G7M">(AA2O(endstream endobj 146 0 obj 3082 endobj 147 0 obj <> endobj 148 0 obj <> endobj 149 0 obj <> /Contents 150 0 R >> endobj 150 0 obj <> stream xr_1Ӯ ㍁nT8ЇRQ$E*%re>\Ҙ,~He F΄lxg׫Ou݊7W-S_ ϮN`iμ9yFZnheV7zb͖3i0nemm3~|55?ɯa$z1/ӂl2[~arVmIys)duߚi1''[IP:9^aeA' 9=<LXcڵ]:fdNYw^{׻z#+7pӍaiXyӯ­ok5W2"}뷅Y_$yZ8:X]&<=!htI}&F?\;  Zy8nZ w9R@x$x</|qflV oiۋ H7q~^.;p.!@Nw#IQKdN7cZ\tڋpݒV̗9rvT2_9FWH+ObbZ* pRQx4M$Amw@R^#PEl„,M -cp_xC[iZwM'`[+$4oفX/2BZCPH=p~9}h_ƂiHy=OԢ3oGgF&Nb^i2<)d Nt^$\vf9hw[i[G"ct~Ic \3vY|m1?ac!3^3k˳2VXv^\zo/09)7䤎Ri:ig?RX~L? ~ ;o'L΅X* wph\s ,V!o ۼ[67crzE9cDr&gě7y}?LgC,(dI 7$z6:,އ;hG/ or4Xt2bm pJiCIrrrO8o=)iE p$eDLz4ym/rrG%iFV ᶃ~܇X~ KxU(HҒs7?A[ N?!r 7ܒ|y‡Rw2`k!3 UQ+"G ׭1{VP`US0xPM=DafYg*  M qJsiIjؔ |VAgznC-J_tkP<~bwHef#VnR+z-IDR<6܊Z !)ňU|_Q[ߦ tE]Nѡc^vMuy1!]"NYԆ]ߟn) y|Ž +zp('"W X"rqD>..<ke!Ŋʹ)eҧW0!xMEFe=Kʶ)LYӥNq9xY-}^I$󕀧wQLxyugȂM8M﹠V9l+Td(u>"qBAƐ&\\7.X4fX6w]|"הxՒل2{92d\qf_z^£8J*+9|?x =^zsrD[qhGxܳGm&JYR1͘,#=oG7,d}qYxz6ߥ5tg L gbQx95HO!>t\ 09NiAYLӕ2U1a)Dm{XHhG.7"8\][HA9C'wen1}Eۮf`%T*8]nڲl ejV;BQx=2}xUrA(IZɷ3,,_};M 4B=^{T^`kEL!¤ӊjbWV1dD}-.J>St ܢΞ3-UbqOn8O?X (QwW qfPltIKR2OL.tGbq) OV~F5~?b}!XOB<np$&T`l1z @`IVGAMnJq!J=}B/A1D |}F9^0N𼫠KuĄ^sҥ\cXī'+e>6:6uEL@@CY`;"[SMy[-l\j'XR<'hQ-p~Fh2"R6ϝAq/]~_Ai?Pw`TFsMF~֠> endobj 153 0 obj <> endobj 154 0 obj <> /Contents 155 0 R >> endobj 155 0 obj <> stream xr_1줴#oTű%TT#"Jp3Diر2/Vgv[.Ver:I}WmOi@~&;d僚#'5(!yW߶5  ޷vWIt"e w$A`? sE`*.RGP=L:# ;tӌ0ɊOmkuR0LX, a {9H6IPM 4@f.͕;MoV穵Z` qU<7 !ۤzEɞ@fmj,w^,`b06\w ʔ( rS`V;tv)ueڬ)`/ЯS:]27g`+/A492+@-ڊ la@PHSYa^^oX+ [k܍`vYIf"sP]< 9?"}Z5y@5тAyYc l8ܺDG/x HX{kZuP?hJÞ&,#z5.,Hy<`=Ǜa;vi7G +RYsӫF0ިՂlc<ǻƆZC@$~gzb os 0A3vJBgv;NǠ5)XVT!vUF Uť(pE!x21D9R " ؀okO(оJנ,5y-Ip/i)9xg4wJbp)8!lO1'j65{` mk|#p_C%0}ϧc"I;Ϩ(OPk b/GI>?0Td% /-4gI9вSE쏁IkiP v1I^P4^E!v-k>._8S8vHhS %`F[3Lm%pT )4sn5Kt9X)jm^k)|Y .Dųҭ"c=tַwb`4(p˖u "iXFRlw Eoò@{»օ'ҟJt/ju6/ rOaI6A@ ži,v{vCs.K7ǺK=$)*`PEdCR`KꥷY&/w*A8<.0gͲ&yODzerϭ#1Ӷp͙z)T'-Ôh601X*! 6/9H5V8 Y0T}ggUduOb;텳$* 'Kp;F7t\00W"oS΄h9%".c)nruV g^49$ryT99%1xY=F)A/\L V}/jJ*DdT>p3f>aG?/,E+ ׯqL e@sʋL \N\ TW$fEd|X\k~RJVY!DzirhkT^gy򺷘ftY& 6ix^"zEY@[p;郊K4C2ٶA\ xRQlz-3w Jfr H X0t!s1 } ')(JWئP u8  ")XK^ I<+Ln6e^ނb$= ]ci7j='I#R_sJ4zTx'3IDr#r>G2`*ws\8a $y,0V3UK/(.I1u*Jx&`  ̗RwVUJv E3qHVSc@\yqsd{i+bҦ4K7X_3_L'9)u:,uZD.ɵL) a2v(f}*f2RC34)zJe8UsЮ*q4QLMF.ݿǫ,jPm ,]I ' !11ճ(1̕xOh]1N'_,yJLQeݣKk[ #dwe_ʂ':h46`"jnTSmfq* FIG?ŀP[{ia8~(:]+CYB$}{V*PG#sa;fߤI9xa A;$4}|' _ (i2ۢzʕ.;`92ToQ =ouJKO m2\/ ~%BWOnWug055pN֢,u ;rs"@ޝ߮H'x?0Bİ!KDWkmU߮*5y&usU@^ vC.7ێgn CKu7&%0e> endobj 158 0 obj <> endobj 159 0 obj <> /Contents 160 0 R >> endobj 160 0 obj <> stream x\Yo$~ׯ /3N@^vv `1:Vr,idi&YdgcKA,<:bϏ3Q6#?6NF.#q׿٫c(鬵zvo !t&UPI5;>%TVJ]NjRv0#O߹_KY*-=ux‘i~&8|+qCpOe a97j|z񟎘vv͎9-Tp:#"3Zgoz.Rtڽoq3?O7l:b,^n UŔwIfz^-D'1tX:Lj&k4(ރnzv#j+8hakTϮ!,֯MZ_Q7k?c9"|,s8E/gRwJ{}%l_$-wXX2=j ߰3#,˸)Lؓ#uN;x8T{y1ㅧ(1>ȭ@zg MnxV,S@;Y`[(JǶޭ7zo9!DOoѬʙMt4hSʦ".1aȾ,:Lof~^jU0&΋ԂCjvxl`e4=ް7iu`LӋJHnmE?A);8Q51}DmV0Nm6ؼs[>Gj.2߃'` }Vp"$ ^'dG) 6M #yM7`d. 9z :^MнzLIΞ{=W!Ehg{.sa8Cx²:g!-D*q^IifIdq6K뉥cǺ(%sJǜjG~>Q94 @LNG_ ~0#5!ݝ}8HN-H ɭuyr@7YV^=H՝LOsha45Nv=)~- nN[Tox {/`́uslXNH[wB%k2 ٸUI]wB:exu´ٸ{"G͙GNxhCL.r2>>UoEK,"FŬ2Qʡ-}^2]F$oPH@h-&7>bFYSs-wnKRP \$n2-I YF,7w0m#6[(F@YIv=Zy\Fg)_)!w1TrGfqzMvRj_Cu, kNdtԚBzWކADvZ SG *Qz7@MhBiRIcJp߃ALj> bdBwF2)v;|u;5Ը| aUw3~`3L Mf33 Q)1gi /g;3<-2D ٠l V=i:d>COBHq =m9ةG2U0_ M*ѭ_8 "%u]WcUB $A' a!~G\F9 OAG=l!"ޣŨЂMG1 QӠ>̆ܭ+{[88XnP%&YwAߣE9Yn9]ha yX5Dx$3'aFkmp~;xiJ&JB:pIczo |dBCG-!y,쭂{Ij ,ۖ"FKe/X$Zp_ڗL=Nŕ`Rv8s1D2 _p&%o=TmQrE j  D&¬Ք3xLbk^aq/;dIaQ'h<8w8j{e hxL;Z81!80^\yiBM bH_0xVcĜ??!?n85_ Q6WD3l :=@G:J9? yc7pX|:3 SIǻO0QnFq6f0Wy؈e LyU2gߣ~(090ozv :Bh26]#9VlԒ 1@r0 Ԩ n0J aoy ƒ~|'P}i%)kBIyjn&[ I9_w!P7kc+"~v( }Σ_PkriYf8Mh~DS[|]ab6L@ UX=̬"\V)}b)_Ag't漣hPX7ɷԑj (f\xTB"%Dk[e )!jliỴ0I,Jv^wcҝQgC2V@Zfd38YDe2"ɩ"r!}HЉ P-(æ4(D]*10w,V7;+fN!=HieFl\(qH %pl{iqEDt,ygH8f04 @V"aX* s ie03<LlE x1Fe^sF @ɴ^yLBkhq,鬔'nYLW,YJv~/_.%j6o+<@CMdHώuJfv7j4Ќu'M\ܨQt`ЉN=4yzzP=aI]IIQUECh[+3ѫO\{s@^pz-4{CJhLS`l4x,̑B0 g*-Dq{?jN(.&ob%_aPID;W{?W^>e[4qp2 mDCf2@۩T>4$cVWNQwےs%-55 A[IԕpGS]+pAӀ+]%h+_03ڻ;B?ڧl^e'YUtqW^x_sB)`@;7y&ε Ufq0kꧣ/eFr~2O 7<~~.w~5 yAr@qѨɍYՄ>NX.WM^4XEK6ېJM(`;':a4aiH6 i' Lj*{?vfYUO1.eq 3 |b` }Дg񅒳@O?y*L1 iM ǸȞI4V8oSpQVL#l= W4G9ؑǬTiͣҦ&GѼtSȺ\ Ʃ^Bk.ՏTGZ`շnO1U=V%Ԓcw n R3tD n!EvYUd.TUUΪ'gXCr818d F)Bi~s5I<ZYr[ OX;x$R CR8{ aZm[>]GppgV?tFL401?aʐ)CW:~JuJXcK6U^@ud}x?KTF` zq#NJ%C3wɦ<U!0ʂ"񉔾D6/X'{p mi3AC՟gn>.C Oˆ*S7d`,~fT@-LJ9kPAnIH_Sendstream endobj 161 0 obj 4199 endobj 162 0 obj <> endobj 163 0 obj <> endobj 164 0 obj <> /Contents 165 0 R >> endobj 165 0 obj <> stream x\Ys~篘Ӯ;܀+yp$%8>RQ\N\RKcf4f3R(?>n|˷US3^5P8G?Xlv>e]dLkM68ûM Q޵9 _k %X#@ =j{Fւv[FIlcBiM/]~7pP7 B/~zH& i|%mZGşgևk%iIvYpE&o$Q0tY1u(e[*=-Gpޮ؆>:lϣkxqt(689=8@lV72g[ދ-kp<i-k^[<`AzC_`_j|m25 #WB#y!h( e&E"^Ržoh0m"~6J&ӛi,SE/*(Q`dLQ}2Q|! aeHu &lÜNJhlm VWiu1b” uW[ kHb'[Q|'$f#=hQM.aDvW,Z+.Ahf e3`T&9s-*rJL#zɍjI9LC<؎2-M c|f17Á!>Sp6cGMT,~¶rmTl?t֔A= B-|oI(۴}C->Tگls}{ Zx`gFn îm5th2gƝ"*}ߓBۢa{*R*dnφG kC#dce cβѡ5>f)e+~Y`( [|j!¨2eϪEydW 'dh vhT3af1\S>&&+O4H7%[mB9;&Kv0]-zp- g{'1=̊HMwXe_eh|5D-@X|RР͍ZiZ B7 L,NDZe'ʶG:]Q3A;붿r ^tĩfm݈heKNM oNn%u/ı- NϷgI9j2h jzؤ"(ett:]ϊK!s'!@ ֩;Ihf 'MAx#Ȳ 㳒rSӒ  B'~c.,V-^#iy/pgtL&i,F^giR\P;c3POzK'4E-lO e~eJ@ RWy|jY B.Rhw+ {l,vQ  *\ ꞓLNbA}jЖ #=Iazu(ey%!Sy8 7]aL[v{#1{Ƙ]q6uZCX(?SJLR~UO##qjpF]|3۴_=GJ3߀R\i#Iepf4@k#g JlvX ,Kb3(V#l#IːLE6 srBμg.M'Ect/ʄEg#* 4J(p 3[JJ /$BG m$=t)F6b"p8l^NU -F3Y5bfX񧣄uwIa.wl>l$TBSܧS8brK7s[QV0DzkX";-,:,@6 VY6so|4su{;015TR| u"~}UF{k*2iӸ=`3&<,ُ;n}HZ /,c/j ]oGŐq=VVrN oï ]V#".la'PnCe$x& o ~r5םsiTq`_W'wnzʧe qY Shƿ#}=ˍYթyg^pNXr2K{ccW9W΢Yuhs E3ↅg9,#CrZ 2f1'fRKSTBq7D3Y%b_#NˍxHlEA@a,=ai:43s?,I*IT O4 ;?1OO=)+d8F핂d|r P0 =/ִendstream endobj 166 0 obj 3536 endobj 167 0 obj <> endobj 168 0 obj <> endobj 169 0 obj <> /Contents 170 0 R >> endobj 170 0 obj <> stream xYn6}߯$%om)4 6I:S4ouHpE12ùSE:*~={JW3R>Lݟ sj*J:kg3O !ZI)Q:zvZԴcJSgVJ 6OVv0# LԿ6,f~i1[{NZqغ!pxV0&i_u-cϙO3&lGZmmgY5_<&r;`* HXf@E64]Q9tFڊY|͎=Cj4L&eUU-zGmU:~ݴZQi ,r) $M-#,p@?hx),4;lX/pc2;w$gD70m] p7%8Ou@w]L+D,s"1N଎:8L%r&:*Ef]׮ .Ze-zcLǰJ CPn)V-ݐcoʉU}AiQTR/ϰNrS mÇɆE/XaO4@D/J&}!u83Y`BT07M߮,=LB7%8{ 9;+&J8s>`×-Jm62Rx3b-Yw^,y:Jl1F1WDbؓW d\5Y9ij3fBN˅ M=8 \e?nRJ %,eiRq0`=.sݏHDURwa>.t@wݰNRBuoSZhoNJ3s tM&tZ ] lo2q@̮@BRt"+@oa=![P+ A:)8t)_iY:rX[PUMW)" vz3X.rt 1E28 W2]j5sY/@DݤԹD~7gʁpN 0x tǽf+ZGUIh4ԳsLP_?D2]3ƍ+a-Fm0Cb(O"|~$<,V(+G*&AģaMuXxTQ@X:M G*n|=ߋ[x.r E.-3D6yC.txxttٟa2'˱_?GPO7'KhRgɾV_Xw+쬳n`$bW8 'ʈkOR4j<`RT& 43xj5Bv["Ժ'4{Y!1< ƭbv[d[z-KO2|QR2UGT+Ɇ Uwj~`N3C!+.գS9+P+DST1wŶ@ M> =aS=mnJӿ)0,̪] ge_p_{}/ָ/`(Ԁ|zŝ_)n͑$1`NJf?NJa{Cn\{+Z J7+0+<ͪ {QRK# qf~0eM7h'H'DvxVz3Y=  +?M,FYDpDIis^bWu)M:* Va D}y*sKyw$dlpO }Kw@ONxD F/ug#Bt ^` ܭH(w5'B ݷ;&OcV@Z3|îm=~>d/g]?endstream endobj 171 0 obj 1967 endobj 172 0 obj <> endobj 173 0 obj <> endobj 174 0 obj <> /Contents 175 0 R >> endobj 175 0 obj <> stream x=r]q;Rq(.r1?Kw=z%.<3=KLOvjf_{8z{ڽ~ݳX#|>%4M;sܽxFO9jw>,W'j1!j琲wߎc2'~I$^7'q<_4SMG#`e dhtwq,#ۣ1m$d{ oh-wr6 ]yc8([Vhw|46 P$)7~mr.}zwls4$-h뎮{Σͣ\Щ?~({;NE86SHu&^샘 O\I#cJvu*8 a3 ll&=|ӏHHx773in_J$ѽ}?#1<$/*ttc.#8qwW*+(GÏk)!Id+yXbGr%bp ٥{oҠ@qpÔw \3N;1`g $%c$8֎DW^Z$2jK1oD,K[Oss=H?}29!60 2趏1cAwi>ƍ]G+(-#7dq01M?`u:ͧZhq }Ӿ1 ܘo7#O|c:өfGRa|W;F<ӢS;R)Gv opg^H>q-X꒡ -еx~%-Befr=lb`?3Fu=r&-$CD?| o:!Ml[|RU}`>vM~m%2wUܙ v҃t#J˂԰`!N4޲8䠐4mBwR8)enc)M\#r)96f%)/y{ܲnJI&ΰH޴ 2v:oh ?PCK)T-`,WBf-65^&e8ߦvI)o]{>RJZ9vL{#Gz'he{,T6دX\ LTiuXw;\b1St~(y^X26Z"~fua,s¤Y}%/*Rϫ-VԆ78EWv;hk)Ѽ&FP~09k6o 穠#u薤}Ϛlȷ?~\Dx7@#odկ{[5%7Qiq|fn1~XGB${,P,qhp3 .@ܙNgI.0S1\z3KZ\ע p KQYVlX;y=?4~t`xo}*ټET@u|k_a_ 7ByjzHQR̉<y݇{Px5ȹ۽~D{ZzwحGw[n%OK׵z)"-wϦF=nn7KQ#t"ۊҞsMg:BgSPw$boECb?$؈~Fh%xj6]a; v4jib4#ڻ?GOtW *q8%r\)*D^edB8zngzvݏgKNO=Ys^`#,;FhJ?d,f cґ*> dw =i'TXGrcf|M,Ɛ_zA_/]04>ק$5KcsāpxbH+[,]ܚ&ž4r5`_z"4pR#49q),%}tH:2b2+ӨEJ,.x^J_a14t4#|&B]7lyT{Y6Gj&W-D 767RuYو4R4JN^yR}{ ʟhrӤ̦<H06&3HI$ !o;nqχ&oy20ka4LqmE~hss/Ȣ~`/tbZk. <`5&s2$ч$sVjIol@-;1YH܈Нlr\+tANZsyfhM}Ӄr&6RB93+͘4?A`tLW QėltTsR`% ;sbS/(p7L.6s\ۊd/VjϣFBO Ǡ=ovG$QԘ5qr\yFwr}=-n+ip+Qx>lp5|GgzQ/vW' ${`Y^(4_f0s|d73$9Lmp jPC.^n g۩ZA){?MXhplqj6̃yni"8P] e lYt~"3VG إ2̜؈od v|qwf@!<)a!6D9B|q\ڣ ̈́]%]\Ja*=q; r󝉩(yz1f0=;ecܦܜ(6ԴvҢ.XsWoyft7̪D-ʱXQ+_2OQ)CRπ7<3ֿnvgPt?]Ccwd8]Ec9+X5n n tf-61>OҀ;+oN\q ѸK-^[hy1: ;I7xA9[^o+^F0@BoF:$>lyQ>YȠ{VܭW1ciZ ~*Fj09 ׳l7roOt . ܎~|;@cS=-aVa :C`<ܯySD/[/ms1y3nyyǯDqն5aF{ORo>h*ڪc~MdA `}qZP$)f1N|Tݴd%\%i^Z%H=3=ل2؉oNJY9c?׳.^HQ&X{؄*yšBt03Xe"1W!⛢$ 8Ì~,0vةF?֬l5)0@&m:E$ . ]m1O} 8-ZO6-xco{K3s<~I?u)u4([ IEm]0$L@AOw*s//RIhDLEܴg&%݋oaL JKRD4B~R gJʈؐ¨VbR^KxF zJXb)WU."YؑG..`*rbL@5`c/DYBRї^^}ۅ2[n`J$놳p*fg lǽڪrg #B=[! j2ZѹKh53E2b$:?;#BŚaG%p0Q7b? rUx^UqC{/*۪򼪐ŪjK ZqxU޳VLKD&VBg Ľٖbprѷ](5z ق.aCY';&zQ6gQB6[# G>R)L7Yu.,SeÛmuP_r>/mRLb{=X9?U$0BoXj%D@Lb, 3E}ig3nʋlEDr b~NzREb3^A`(QѪ OV& /E]Yji҃ %jP`K%#@ I82B~Ung`KSo_t\W˼(T+Գ֨ jSJ [q $ux/,28o3,`_ ~~-b ZK;46;k$$`xWM:$o#hp9-,QOX[R\8NœL X?Gll$jgjYk{"JJu$j4sCĹՖ"-.c8!,Qql>SÎ~A)h'&qӈ]}8u {اԮ'W42 WncERNgŏmR+Gƃ%fMwM$~8va7cf];b/xA.ע8gcAt]jқ0%{VO2$$~@݄UDz6ͦ0g{[3wL` *E$ʹ@Oi.q~7ۺ mrP> ՞ă7\'0:nYSF9[6Il9ծ0' ŀ p~Z)͸DeTʁb] eZ _ylZ- vt<79 C#1[SlaP-_- iǵEVS5NL/Y&dZ4iĞN8EsKD/ ߵ3ED>Jq[ sxSH<~ WjLO̚4Ju{gIT˚6tY}wH;ER=DWJ=k_M~lF"S?-OŪ}mɏކިmpm 6BI >2:{XB=AҢƟovՂՈɸL?YOE}'3n uͿ')TR06m7M[ȜJpʧtVnMnĕB d7Up_[1+2{k; d]pȆw\q]P)xY-^э-7Fэ-Sjr6&)z73 {pmP֘Yg1}*Jg@нֻ9赎J@[Ç,ЖD3/OqRAfȲ^ryiZԼ̫S&ޑT},DP- ssb4'ƞ(: ա\iNQdu' B9kݬaÈȀ.T!ຑp`ů#5Z2<Ȓ-Hh|ߦ/OL|V-y4azߥug |5'?\В6KI9\轔Y٨w)EzRd q BLĈdVХa>i끥< )MO>#yYp<.h0VٶNr=IhVL+ʢb^pnO| O?;p e\ƵQ&nC' WVJJL+T,IuPP=pL,` CU :TTFh#-Ugk0CeQМkD}ę^bVIXۻk4;n~V&j>B'd{/N'뻋Í3PVoFlbo#M ކj֟i^7_?}0[_ko1+bTh|1*4/9FEƧQQ5y0jR‰&5h:PC45 sVjCsnĪ3YX<gll SS"m mǀ3,wɰiپelr ܾ)){5j@ qZ&CI)/~/! 5#jjI@Lp*E7Bg0U@ |{/_`{`jY`A-4Yfk0£8uSF?J5'*FūeUVayE䪢WĪjK(WXV& -}Ӂ"mvXPw$bۋI+XIMQjZNŚ1rӹ F<#)LXFʛeuDH`G%p,[ j8t}^i{15DYE+2J/gOhCg o4=.uqNNd oz^?Qɸ+ɸ*3hLƇaUҏD=1|qՔUv@ދ aXUMݬXwê:bU}D)U;9+[FejΖ;6L2u_ajdW\t2ݙ4*6))}UԔXoi5sPe:s5( 6Wz&tREB-󬽂#v+UX8ũ2>L#~֧Y%oD?R7=dm4MEarCAy|e#,]ﮀT-T8AFۨ OҶ51$jfuPpPޠ $Fڻ銬 3MG-쮙"|xx*@a Y#CIHzۼ^["xc m%MrԶ1i=޽Y2Tbi1{Jk>׽we͟L EnʖHjѪO̾m:-ZrTǶt ȁ00 N4spڃj@YGb}5Kad\ݢ!NxOb.oaQ6e(Icbfbp|\)/=}nZVkwrʖ_w6CElqHzxч.T=+t\^+}{?<+Jtendstream endobj 176 0 obj 8447 endobj 178 0 obj [/Pattern] endobj 179 0 obj <> endobj 180 0 obj <> endobj 181 0 obj <> endobj 182 0 obj <> endobj 183 0 obj <> /Contents 184 0 R >> endobj 184 0 obj <> stream x\Ys$q~ǯoZvHAK<`) YwVw`a{32z~^ Ż>\|&:{X}s M݊{oWI)[6Q+ͤٲI˵]Zn~VO X 'P?nv\YKm/Lgp?đpfAwLp1|Vi:4 O\z?yK4hmBmij.fA_FO ֗-q¬Pۛ 0Kן⭇}`WmFݠ%َGFz}F3jRP=7*g1nFKX0Oɞ[ش7o*9`hD74LsVH"&*إF F7N%| M}6/z9w!Av0H#*<v2/ƐdH:ɍ@ 3K-ڒ:Ey s}N(l ^ BO{RC),ZG ,og9fz<#ҡ)AL zH;ȓ ?ӉJm;bK|H~\"Er@g: dRM&Ln a*$4y m/Za:FdzR/$cow}rU,xe&^RX)[Z׾>B*AȱICa1 NfKPgo1>A+1ցEmb?Tt18lREMhaf&/?ƩHm<&p)ˇzO]O(S@$I9ۥ gDs:@g*9ył?C.l !bAPECN`-~%Rca!KUBU^%:ON>BMR^rK{3%ãޛ?Yk#  Zfr3xћpO,ޛSežwd#@^8;$ w/#rr@>|'BZ+`-1605>h Of!Ad($&,<[RȥI "Bn;r[Wm467Øc[qg~1/թ k(lYNG}Gr8&J @_J8.Sa|Kӄƞ[r'Ee.uoj>V鳘TT[wBlSuuw{# t ڢb[~̒rypvP+t듶NY4NmyI^qA ǵ3mBjwL!B'to r}p!♪|,9r1WTaf㰼aw&&*aZs z /$iw nQ 1A(?`^}iD*xN+W?\)B~[ܻDw{h 4Ȥ56JU4WXJ&tى{ k3?iH?3<~WmxB$C0tA rl'm]$$2 %+u 0ԯr旲D0^ GRO<b*PPL:VۦqgF/i0<ҕ4bՕ4[Kd1 xg*+eU񀰼FE`}\2X'#e5ndmJ}T>ri ůI \YHiDzH{ٔi,ޢ\ky`؄_csKC;wt'PۨzO@Ϝ/)SJT3n5DڸwuUÿjq59z`:hKe_q~;FؠE>w1[RzEHˆxt`c(C>|^W-N85AlV[xy@`.bDmvpU;)--Qշqޛ'ۄ, csمe IU v9Wбy sR(2ЈyI6O#b=!!)~څs OpbN`9إz_nR5Tȼa|CcC|ͺ$^/փu;hU]y^~1/m8 T<kmLErř-"H=7󑴟/8dlϫ}ѠkёH_jP%"7w M; j 2&Nt->楑_{DN?b,Gt:b>~ J\~e;STy ;Ϲp\tARTQ[M:TI1MD[HV}˔<+ydz Y=Ynrb^җqWlh ?鹢VZ0O]IXMH.]I7'ay"Jz0iv;0>*j;x Xuŧ7haXsNhZ!R#*:}®u_NZTXIP0V4 "[1^eӐjo+imWK@+plqH+A,/X A2oxCMp4lzdOZRo(qK:A.)^!;,,w|Hq)HKh^R~74R_(RO[CwQre/ @"endstream endobj 185 0 obj 5184 endobj 186 0 obj [/Pattern] endobj 187 0 obj <> endobj 188 0 obj <> endobj 189 0 obj <> endobj 190 0 obj <> endobj 191 0 obj <> /Contents 192 0 R >> endobj 192 0 obj <> stream x=r$q~(8u|(!eXK&..K{κf >lwYYyWVXWNn^}m#6]pfb~>{oGI)[I٨q3k39llrmymϛN~ O\kse}kn0M5dą0t 8gJ1,?X!_3~Zg/קxFMhki'}ga; i37Z0TOb!fw\1&m{Dl]5N[hXhyPn0JKV;yS^XJ㷭-jpJEvᡟxWF(P= *PvEjCѫ}(dG_)+&# EĀZ \md r>1ɀ6 [_"fCϷ9{~;vQՌk.~hYmY1h; miaE( -HhˁOw+*Mꥨr3C6n(6StQZDIAE)==DM`?m\XTh捰nf9eRxqV\f&fSv^*YGV0$1;iD,(Es<,鳳%&ycX **3 nTsw4y^4 3O!w4ΨTzpߡF ˳y=BU+>Eoz6@PN\]͟ff;l~7Ee[SUC!gXIZ_' ]2zAEHم¤Sv)B*QVz>Cϱs" ן@|1OR>6s)T ~L(&*gܴ`_(,YzXR/%쮫oM%RQBʍnЁuH"I`d=u(VĦ p WaGET "L\iYw..Tp La{gm,(ifDjp,RQEa҄zQhmR Ƕ rE; F:I$Ht$H~d!걀IZo=-'$jbQM7 Ӕ|l3j6P'Lq ̯   M =ىZ? ; 3Uh~_ -=$ 3-pI,h:X8k Ep[5 V.ڶ;U561E0Fb#>>ⳁrJ#si tZ_g`kpʑV^QZ,@cbZ B0X8ͱB.O~|ïHFb6Fidh]Z*9"xǃoy'|)ݗGȴNJSqo_1n@uho3r#Zʙ nk8RB5 Ң>PhEmOAjkL\6E1K^[0(`6Oxь+;b-:'8} *ۼ^7m=r?RXc :u`n91ysAVc5CQȎxߤЊ#lu2KD``9\5VG?TJ0biVl1+7,^( SYh.M'4mOhPT4 )#xȣy0ږ@A"$ҶF=X̽Y(q hqƃ$zȭE{oDu+;T=X&ȳX#if?he^m:QFd#Mܨ(XO<'i-;">s4 Mc] !m>0 ڮ廊8bY·{Q7eŽ5Z'zT~CtB`>"R)Jh*z؟J)7P&3[eL3|a{ͺ>YFlY\Mfx7CM65& ױ|t(Xއ-`@ kع |cƕa=㮤+|AO5;ؑ޲a5 y]9xSN)--I P,Z" YELēԀ`MS-aF~c^m*sPH%WZfCOR".trRbS 'e "gD8puBڢb_ۖ`V}RUJ Jn;%ws}DЏp^'KH&`^\_@}9o쀬ZHT2EqH/B[p`TO$nwm_D2')fL,>ի㠐/-sɂH[4GB&%u֙ƚ~bD*I3][9)W#E!ұZpA"JEޟ6= 0.G0];RMxċ p|؛bƊh'bcN,Mv3ApeTmXJrVWga2҈#${˂Gztʊ֖BQ=Z_At|lcm7HDF2KlM;rBʹj+sj:90cZ{8L+A Ogb79c[Np6^Ny,J=bzɴiխԁ0d |Ecg[ YS¢uEUI_">ʵޛ4#V5`7L[j꽡vRF ]~55ChU:xuh{?8_U<`b%4Æ2I 2ϻP^}p00~υG2A[ n-*2xqB\S1gϰU{pyܿWbf/ihb[NđׅH8 Fjk*v59m"ϋŀj5 O5C 3W79rC"\#owgLavȷ)8o4=A*2ͱJjrK I6ڶ7H-G}a @~'?pVEj\[W1"ilQgwT Wv]x9䨘#"B^$^ָ::[G@9uD}hs(=>֏!: $׻?>ڢ*u_bG;>9ѓ>9T~w|Pbrzf![4C%O(>32/O,01cuTy>pmeȼbDC?Bġbu ~%ŊhT&K=|?{oldYÃ,z~՟39dqs%g6ba_>ۇY~03kf.|R1G$F@Vi)#6}>ĺP*Ȱ$ .[d I,H6sA= pR TZ \߄vB &'KaN漰ˤZrZ&+E^޶ l`d;"<s%8ϛZ|>n ]03<pr "7uͥZj,[!&-e`gX,=˨ ЉW]LFyѼ2-}LRA~55& O_PDwfףqm[I:t92"[b -$ŇՌ`CM\HH5t}+T|dݼi_pܑ3g햳v@Fǯ:8VD ^x |54B--z]H/ijEQwW֨ !W&׹rexȕIȕ9ľ :| pL5:h $X .ie!#Xwx A(k.P<] ?.|,ou66 >VJBV)<}a0Eƿo#^xe&Lx6cE˵j+i3öVa#a3HY/P-Q|^!$]Om&,…dtypK"\H{^B=SbV].5]- r*c#EkOZÞcʱ`5c; HίB`׊^,tk96Xq:-QPnC\Zq W&16[7"p?w)T%B `&nk&83ag8,0' n_\ nwuP[lZj8D6ht芚r|{$byOFëMc0^%l%XK[&0Zn&h&lb]{T 2~ 0O)+:z!i?{/QD!kQk܁Ks-?KCg,b~]K-p";͇Ƭ"-(ET8o.z- A`{Gj\ڠPK@ nq\@}`Z `e;>Ֆn<<ޭCu=( ?v<2!F<%6\~q桯c(Jk㒬G')˓1I PbXLtw{&J%+7Vp9ˆA2v9nL"@*67^#DK]ǜ osvˍ݉nP&Ҧ72ڤZ^WUL<*Pv";^;r b`%KN+iu /iĜVii%$jME8C/eD؝>Zdendstream endobj 193 0 obj 6374 endobj 194 0 obj [/Pattern] endobj 195 0 obj <> endobj 196 0 obj <> endobj 197 0 obj <> endobj 198 0 obj <> endobj 199 0 obj <> /Contents 200 0 R >> endobj 200 0 obj <> stream x][q~_1{\:ز] S.RgE&+.I!+{i33sJleЗٟid|p4t7?v`3Ë$ 5ؠ`^w4rm2NI};6*cnF;q.~OL)./ۛ1i\/¿2U=-pޏ$ͧЕ8gسrg8ҍLao^ ԛ)}}jebg0+4s.JnGƘw"t//QWUP8xP<.znk=瓤7~qPS&.ّ$Z R7uQr)YZتSN8sʙif.p&,M_vW3DrJʌ/ϝ5M=z~WAq[+f%9ieIg8 4 Ri<;+aoQ@0Xղѹb'K-MUnȒ'DQ1긛N9Z46i q0@s=ؙz|楛;†xqSQ3 ow\h2e41w0u;/}8ɋH vCZ-#Pq'%t.<1f\C+P0aUKT:ì? pi^uy7%WqS+jNۂ02 dn,|F3Y{YK_lAlЇ)xjRX_T +(a|Tlʫi~i#* Ju$1 (a/X[mqi;t>5`,OAXr+4My_RR@ԑ^>d^HCK&X*xKAϝfѳ2^Q*APb!yabB( IC&$X`X·`'uTg fWC,ۋ|'ـFklqNGLH/Yg΄}(EδtkX~:! 1Su;t7ED޾d;u!5 ;nFu$qh*G¹d%&ȝ’7l&^Rdpؾ#e^v +ЎfRFP$#r 7^F}:?nH qY'͡vpE2?AKc=G)]rj/^f`<^p3{?JSYy%sֲr6{+fijM?:UOcurTЗ Y7 mשgp $!x'H$kk%oSqux,Y7PVnhr#8֥0( Қ'͚ ic*k3(ָ+i#*( OQ=Zsf)Pi e\rqӇSSB=th&M o>>Y\jX~ɉc$H>ۨ$$|Ppo'JqQ{Q'_PL>{z^QJ<#:bDs~gfpٹYDYM7zY29oBV,o%+Il+&"]<b8 mN5Kyj"nC{UYR8UfYjԓn, X&xqeXvԆ+:wWTL?2ApH+F:'V(6!J5n,xp(j'#iҶ5L6y$G)7ZW2wN%ˤ.e!?y)CpЅtg]6_>/Ѝ`m+K6卆YowºsZ 3w@`B͙qG{i֥ ?{}|wa-jɥ,3쀥H;!ͪ9p~W'n20#l$+M0p ΗCdgLÅdM6ͦvHnjn rQioXBeAE,UYkZh+SZ3qC?[ѽA [\7'}nKs:C\-$w#Ka%ew5c=ޚ  KBd,Ѭm6=!I#*-HӞǚcǸKܚ>X; I),iڪ'qFm6h ƾF_6^#8G櫑9xp=uZRNĶ̮v[FdZ'E'(];Rv3H@J/VQ艟'?P:4ţaږyxc&MNJNm)0qF8<.zXLluR3Ɨ-2,1y$1MwM}/crO %oxh G u5>6b@*ePnɹݢk@- $nA)#*R ՆoH*+nP4#*RZJBʏg{ܖB .Ș<$9p)쳗  wծv`?ɂ0Lm` ۣgRΩ\~B&|[Tk63N0 ~}N_Qk.e#Pq!Qh]j4hFfiai~쭐uKcg_:{{|]F =^Y-!nFHwX-.teS@$+OBqGP#+Sd:[{L?|E?Zyܣ_-CTsuLey+b⥷7>`5;3Ox(dCSk&}GKf̡&H?G=Wq\~+hw~s0GVF=^h]#4nP$L?Ua^;ѩZgMqQSl$j &d1`W ʃ3-V(fP"*fYh&ՙvyQ_*R=U㷴Jw;XT3noY~abcz.㟃$|N^a!iVH iPT)(-+Qeg פ畽974W1&op`,[ +F _fXUM\"Zh|^ڑzdn܂ *Y~)A'>.H )RnXٓ8Vk R(_m3QxWT{!ůS{k0j4_Rl$1Lb X1]P DNi'ߪIOJr:IA$w?Y$R,RN Z#}xYx`,i apDXQ̲w 9N%GNgzNg>R6VAgәl {i?UԉU7+gDq*_W {5Nv?o'3 d|?p JF-4nLf5WxSƂrLEH_*/yMAi\Ia֭>-"CIƾvVWv^rkP 3(NAr\&ŕjD२Ik> endobj 204 0 obj <> endobj 205 0 obj <> endobj 206 0 obj <> endobj 207 0 obj <> /Contents 208 0 R >> endobj 208 0 obj <> stream x]Ysܸ~_IY qcswsXTڒWDZ& @@1t}]ӵ7/wyvi~|ju0 9܍%)X5jl Smӛݟ?;t-Wzƕz?׆_NtƇ%\}8:0iiؓV>p_~7qnpUcmeDO/K. 8xn"-Ͽe: | p ÈX!KZ*ĞiD0`o/6tf)Y,dm@nчX PR ?[[Ī}A0Oq)ozc/nQrPez8/9c 9vbEҽtP|ܠ`0MxT{fH|5ʟb+8sjgKl֤ Qas9=LvIEkGL?aie;.TjV5>wکUczr9<$Ԁ q89@*zFF dN @ܞzy=8%^7!"hn (^pP9 40%Pd6Bd8/PvCa{$4 HZ4pSۗP|2YؚӎN4IXĠ9.P}_ӄPbKb31#I}FQ SQ8H>B<{¥킈e  \R 6bTZO yG+1F2Bn2g Ks|XR?x 5Æ/cSW/^FڬZOTu4̯jR3IX9}>+'Ƒ.GR;IiC cծ \һ8 5ˮT3~ A`=pvw2+:+z7[p(;OL6t]k4.bN7fDžx*[NIm, 6`Et&jFBc8ipm<}0FϼLT ,E_<: -DS%V45B2w]=Wʏ,3fcrT$3`He T!;ԄT$ I~.j(&BeocT"#ٵŽv:$5F&qUH-aIm>.pQ)d6JCYP!C$"z5)[Z])[3Ut K8*ȜJ`B![ &a,ZK N$% #2RL!ka6Ϻ 9 &vW-lY@p@g#DTĘbV3#S_Qr$i*JJHUY!Mcp {겶(0}a O`A'tFJ@[rC 7`-u!b΂!`[ײ~^TjhOqc)|EL>n" qj&2~PQk+QbH8*B6PZ-mt+d ;$c%D82H[;VJ"֎]3Աv}{j1mWؔ<+ˌYVd㬞Mib`! &9Iҡ2U(*,ptBlBq$)(\ȀY Sl>3!D*y5+] @JmC.7N% 6 \fpAwcECMx8Df6^QSϘZ咢;<1l/%q$JT"߉|p/ƷQ"^򓗉uC֏KoMՈH*D3qTHFD[ٙ@?EtB۪?:pQڙ/c6țbx lz;ɾDY*d=pOҘp_Kw \HwRn 6ؿ 67OQ) :Yobq:ن'6 HoGaa1=Ѳ3e22-:pK˜3,_[ A|(AOcN : ymbփѢsr6ԀLؐOA j>w%z pg4:S}u+Sqr%ᶓj62B-!SR(MuQ 8/OD nm)+x1SyuD78UǞH&Bx'7GlH:qj\b{Z Q ǯLEB5ŬuNd%x.5U?Ĭsl}u>ΘYb^Ar>`@Hq2`*ZG2Zd5#-Uo9?ł f贸:ɪ"6ŨG#c@`! P9nn˕OPi #%nB]D'/zeC_dɥ~t=kVB'֜@1+xp]J#iݳQ~c(sǤN%k> !D0jrCdՌ҄v)k2ɗήy:QU3S{ la@.%[ьS~lE\&@DkI\Kk*b5֛/9nJhn~%ɋo1d{1:w尐ߵ|̍4RK5O,D-J{?á&~-}m?j3r ueo7<7R8*a/Ax"1iy69 | MN]ֵ|q,e3/Ux^yo -- 3dlP#MO]%dGFą&/ePRL [^ ͜x͐rbxLa$/xe_})gP'Zd:ܮO+m`2Y:B|=΍:j$!N i+E )mQCeH5[jyDwJ;"G2|:a ?X zu@p\JgbpHvF(PQ& I+ a 3&wŽƚfLA  V\#}OA6MoFth?ة cǛ_dշ+q䪮$.Sb˷'NjBt㰪o$ zngegg,@eUNkk=Qm\$+<]ntCL4D]P.f^Ҹ|tGuu->RѲ}M4k>4H;UҺ~u+}jzVg5nkkS`tם]Qw# K7l0pXpxvg]4{w蛷{X: l4g vPSV\lFA\I.`t.W҉x,8xTiA:Q.H:i^y6TWz,M"߭ͥj dNeQ2o:*< 5)aHxendstream endobj 209 0 obj 5205 endobj 210 0 obj <> endobj 211 0 obj <> endobj 212 0 obj <> /Contents 213 0 R >> endobj 213 0 obj <> stream x][q~_qȴ\,L\NRvܥɥd ЍƙsV#JJ* @v n7g۽x6^*Iig8N9v0vv1:a`=207\_~ OpCڅk f*䬢//bKʏP|LUQ }ֹ9s>kVNz3 T] wϮas8y;`02#s˾˷|FNg8(k`0/f(s~vXVwn/aƊ5n'F%_\xw~i}Z5Zmj];McMM y4y0.M=̴G m{-b?o-4jBL^ϟL;Z)N#LX3@iϼ%{``:n` 4!%}{lTJ"z4M@d!WiuUnR6R{[M:@<1LUXl Z2ϡϰ]FIV#uh4^ңV+Ve3q$sniAJ̊F6m8R ViuЭ3IhaYqXF)KSnoahaڡ\&/U ۢs$PVapzeѮ X 5KN3 +Ŵ,xnNi~"Q5uHS|K#U$3个^%;ՒJ ,lbK|́Ve rӀȒ`w ^-ȩ&Q iIޒOQh ;Y{M5mBoɰ?A`aLV <#A0۩Zv< v(WTk>o04ojccjN~{Gqe0+1hm_4qh=-.")>^\K/L;Lc7B+Q~9DfNevIt xP^F ߧlޡN &L̿W6(oj݁+QrTrמu}Cz]N_S 3u-hF !ѹ+U pK.)_½c+t_8 dft[4(:hopsD{wۓ G,^& 'ވE SG'E6cjŬXJPd~~Ox`_!GP$Zyl$k;}h e>ҁHD2tO"Lngʎ>^23 9ۭsSUi85Z}CtԐ@xk2n3 Zt7넹$[[a:=MÉpZ'%Mhsû娛K\W{\͜, 54y|M Z<&^cܼ " wNbyѮƴ+d9U9U5¢Rb":J. Vy U32zPX .^$\삃̚$dyƗ3^7ʡt#?bϨN>9| F˿׀ ORJૌbx#"7H{hܞ*Ҥ"}~O+ Ϫ)&g'NT6BbLx0s[_xaV?ߟ'OFu؁@qፚ^JOS+0dӿQr;q`?h+d ח%-OM }駐2| NWRy;)[VqL0t%|{.вgN(ϝ}/ӳ``f9u0PvfX_+ԓy,ͣ4BpK菜'c3{'N^c:b@qD{ e~<:Il܅«%~;r5GNӲ[BI9V4B܁D4EIIHEtT#G dNva. A;x#Z!VlsG %RouR6ٹ*f1UOE^ `56Q#?V>aFi#J-RYA 9I)?t4@O˭F,6a ""K.!5LhѬ McɚL;A_g>~UsXeKq":(4}{eĬ-?ا}۩-)Je]8b2(ڻIlvI竖9ٹ!3bZ hȘxjΓʒwe eYgj/A^㱦+8X&VN:ÔdlW$V J5^T|ܢRGS+ Ef^gzx"R[XCҡ~[וΜni'CTI1Q)dz 1)Z<+xsKL*(|~[Qg5zJ`׶|ޅkKZ@Fgٚ>; a>Klѩ&70mv%fuVѱ&Xm: $-.uJuM~7/]uJRbB ĪH]ph%Pq -ǎ+&#pԉL N@G~ʊHPݾ̹l_uϚ~Fi~!*ƙp >_t-CbKLjtwuǮǮ#4bp>[b/"(';#Ubye!@͑ˆ@j} &Y aL:@it!Aj:H[RX>n-0TԅMU/ Rڍyi)Ey$ͣ4ERZ]SlNLY/ S: L?k(ʫ)Oq3*KeCHLZ30_K*Oي )e)h|tbAol["S_qkhAgOʼ\hI|BIZ}eL$]MR=F)*<^;,O۴PZAy4k')Lۏ[U=U<칤J[9O7RV45z9-NmOvʣלix2|'eG鄁QLt[VՋcH~YiQȽ, 6 Tp%m+ (^}^{U@D?SQttTV%:֢QiM1#Taы0h)|xPazG4vS"a TJ (2HCL pHnAɣ]B`RHT4&gY ;7Z< iz=ΐN7 ?,[ |,ēŨz(,RL.R--uYT׃xIDrR@4tK`]K ҉"58#W:4}+5aOWrӣ^#'\H|<2e4.l[P1\S 48~:牖)['TC㷠NfAWw'FLijGK^I iHO@+&v~;,[ ڦ58E$ͽ!H[ҼD5=pL ~:89r)U]TW%MDED Ww2wae[eM. tMp5sMϩYB]KDJ4'mYsH^[v:)[DޮImf#.fͲ)p'kQSfDe",1H$^vL$g< mH^A" H,Rtl1h XNDKTs*,A?g wa廨S6Q6SrƄDwe8Dp"JT|"ZKW%DJQ"7W<_C1#9z#'F;iHI:˔:& &%G:$FM$1ED0$a,XgP> endobj 216 0 obj <> endobj 217 0 obj <> /Contents 218 0 R >> endobj 218 0 obj <> stream x\Yo~_1;w6& &~Z^RkB$LW_u}U=_:6p1.n}o ֽXp?F/n/O8;Z'T;m:͠Mwza~a,v->2˿| _1oĵj~eϕ,stvKIWt 0gg?a{qf|[!uB(?p黕~;Z]v\qWb{6~n{ЅzD%b#ϛe=%'|_Q!}A|`vkwsdX";|e=0&}Vj|KlH~C|'B=ȉbs\;(i=+V2"3, !#]LG9x]azc[^#a<_׆FLyG!>i@H&n UCBGTJ(Dq*m^ ư m10,(6(#t`O \$`(nPuB^y] \`~@#/c>t\Hg \0 R&:#2wd!c{ CBw OJ@N0]|Ѐу AA+1!}ABuC/KB j0˝MX*{"t_w00=*!(,P(}B~꘏3/JmXU|t,ɮ0mhկ[f%{-+PiґFy (p[9cs&*}>i/}c &+q)q]ÙQ0]+ {8Mk+ʕǮOL`"n_Ԫ˨7=]1~Y[/$>?Q甍JEdY2-X1 {`wRDL=2S{ i%{!|SgiA+}/D!unE!N :׽^/r) -5r >Awg6Gra 6PSD V/gj$}/{^lr6*6!87y*A+:_fd2O6@*Ubz4xgP&{QE|7VvY|pP>OR ƥ#jTs!a0_U)g3+\nfV>Qs\Em|$ LF^9:meax5w Ӑ9 m3u,,O,`es:e볜uložӮ\!2Fp#uYAºmzz94wkp0q,42y0i͙vcST!o7(Ix100\(ɋ6P7P[ٙb*J?xcnnKOl {/i^uRP9xLshMfڴtWȷZAD W& ho"N5nI,&WJMWd#sט')Ϻy)c`fO~S[WmK U镜U)-]  Su"8`;]8փs^U-U~[u>Gw"Cy͖:֔V%Floµ2S#ln [i& |qS͢{scť7 !>^qE'Dh]v%qvU'  Bt,X|ɞ?_;jIɅMʍ(>$X}W>s!QJ פ3}ftwEf!-װ¿␈&KP=ʯD˟ɘd`䯷=XFw!B| ٙ*)Rx=S£Mܷ P ّÖ{v ( v]?$Mi6W&8۹ |"T3TwՁfڔcI^Kʆ׃qm wp/ lNÌid^oB_c\c,-Q D I4xW*Q;hΛ±bc $&`*%mņ"wx F!Dx{**J4Ev\ %1@ q#@ =KDYCϛJPIY|IYAq("T+_E mƱNN  ĺ&(=.}l~F2m1tѳfuBasQ.Qs*x ykqWզV;Nq$7m%$$$Ї<'c.'cLfQLNyo{K 5 > W穧`ۂM GɁ1b9#)ɡ=*(:duhD[TN^FcXsMl=HI)=!R,~r ݃p1VP[=7Mk/ mdy@T !P "HJ1c-'۷NLڪirvNkRz!sLHgZ"pfe~%+!:ݐC%rTj$9!RG6Q| \> endobj 221 0 obj <> endobj 222 0 obj <> /Contents 223 0 R >> endobj 223 0 obj <> stream x][w~篘ƽMG;f(ҐH9ۿ{(LP#K/(U_݀?7fvËwg_=1Wͫ-p|~~nϠad5g/ҷ`'-7 ӓқgcy|UJ/Kݖôf7~S-oKi1si&moJ| Կ^s`,L}rn$|(Ԇqq0x L+>B]RJ={ тzhXEU7E텘V)8nLLF*` qy?P 7W#~ZȦKO^DJ(&lrgg9o`0n16^ҌZ&.T>6YluH0qƬtk€+ s ^^zupS-㓴ᶗyPEψ6L< 0>LnP݊Z=X`U kL?{׹pS:{4l6𑟻QEoѳx7^8a'eN!#b̮Q>,`:pR&o(Ijd(ZЂ>hGpE_J۶Hm`BUaQ1Fu(I-P+Fv$I55O,Hz:(Cs)v\p!C\. k+"𧗨`5pIX+=Uж+k8S0@w4BvYSk>N.DŽf^]kxM}{瞗Wϔu9]#GϏ&rI4k=H!+Fbى@ & =&ROXNJbG.AwQjΑca"u;!d*=TُbUc+qtfZCyz{r!l!orwM{Zј|{ x[43ӈIg@D#b29tb%Ցmp \rk͑B }v#-4wh <>oͫ4h1+ 6+DeCJfq_1Z˨(چ!1!VD?8Kl6uLs= =.^bx8!V%!! v)CBdTN^ 6FfVa4YZ~L)A~ n"lt9 W"uwQkUG~́3  GH9,,; >(>.W#>nfYX1U;@&)<ܦ#)1P7%v|J ST"̓πFedzՂd89m{kziDok: ѩP\4H2_0 iSs>Z]C\9;"<q%9H $dzr_xla#UQȪ byM8IG$cNv 9q& ZtTE[MF>DipD86ӧؓ*OL'7[P"VYu Jl^Pw_{Eԉݫy` @9E=ˡ%7P9rhkա-s}^juvJkV|_KHב{KFD>Zy B 6yƵyG2uP*=%7K ) m4 m}QԴ q,́ }{a}?4kJ\IKRCLm H[s_ULnŽ =a;o”%@CNblȇ/wDh"Vv 5 " j4& vA;ܜp۷[:t65̎tF 搻ԣnd-̪1}%hӉ|<._e„ ۘ]eJ 18 .# :\ndG;_ZYޜ,RDW7i-Xe( c95[]Zgp>:n֛0I{smҐ^,JMdPh=w(k\iL@dwd'9C> %{@H-u*$H.}97 $ ԳK<4i kqE}5 ?ێ(Lj9k S_[&}}S)}JX8ߠ[d92}@1_{cCD "Ew+}Zn@踺mxnuJ|`yd@*FBhvC*LvHΣeou4/GL"D030TnAv |6ȐTɐǻ7js'#+`ŁO<鷺nd$GuXɍpM2H"Y?{lZNAk̈́ȕb#ryE;XQ/1XAb] CϖRĝ,xraرo&B1H:5PM>cq;XNCq7XN>w#v;·nCqP/w>w;a E1"úȩP`MVNkXQW' T%$֝b& #Y$eUJȣSlNP=QW{% t-AS`OmhǭC2Q  R-Kl%\:G}+/RRL1Z++ʰ2dLӫݷ9jkҹ6W[IVۿVlƫjKeOV:߬vdFk5ӭtBft*&iD H-Ωfv)q{bO;^ހ3ـ8D@6ةzXc1ޱGUy$pM 61Y5Z]mg1,wu&Y'\4L8C_D`tS; _Y!vBj#@sa )9` +vwF,\\O;F,2qx݁BG]@bpWԖKn >/>![;LeԿsM-X72IUWڦ?k|'|$ЖTwa\Aw+,sB%DF!8O ,7xB?QؗaݧvV|ܛ_|N _ . ^_˒qƝ\|(,mCRcYvCoL=)9A]li 7qcZz0QB=zε/D.k@yo3\ʴqiJ?f;(T%8 $Xzx!Ȍ' Z/eJ,9OM !rqw ;=Bv: ֩S}}J:cu4F c  gyfr %ND6|ѰkHjM9J3)tviH<%X +0NZ֡XfH_,m;P,babxnݯPŌ3$Ή:INw?"Nʨ)\Y:ǝF u 3uMb-| b`.{ ]͎Bɷ7ي@_Zzx]!XR_AG3UH1ˮ>>hma lW/O37iFaWvʾ]]Q90EImt1rY+9ȃ2EPN+]UWe9_lBt!??%_zQhPωq'/I-S7Q퓛(Y-q5'<4E,b|ɝteKDk7S8eNK&2}'~TT^(5'O1ٜB8O{sZ{/7ބ.{㊉M(7ބ.{썁I*qX78L-08Mvbendstream endobj 224 0 obj 5554 endobj 225 0 obj <> endobj 226 0 obj <> endobj 227 0 obj <> /Contents 228 0 R >> endobj 228 0 obj <> stream x[ےܶ}gRr(qXٸ*SjDYEIc9Z%7ݧO7ϊ\ȬWMvs)6/7|B7m3Q]5śZZe.>ln_nDl붫TNUnbm{$Jw&j]lklrK|g*Bw/B >+35wF.e훮;]\W0Zo^첦jseJow2N8# ە\j{\){dov{},h Gx()| Ʈ7{;+# ]=K˺#r ~KN]/+5Пل`~Jꍴ/\R`ˏ{wњV@|7Z_ԕָAx.UVjR8l>$M֩N ͭEyߋ[ G ?ۭbTsQڅuZF6* hWt_ǏNO.=&\ChE ;(/`'̀ n?wz6(LLAðrw¼@;-5U^U!:6 ,-Ca CSv4'^DBq-D/Q6*v(h&5`u.j̟1_ľ_9))vH 5B߸ݿpϬ2F|/P.}[5 u 6]r$3^~NȶiF;]ky<,0*'JWH%wd\%&;zOvm.d%D$hgc'#J[ç) z+пdT0Ԏƾ51Rf}hTFtq$ʬFi [@JFVxSsKoI!Qp!I:~T,7B^\I/" ta3D'=9wޜSkJgccb!Mt2nCZؓ,{ֈ]{Qy C]0I`⪇E-rgCkbpô<~^,@ GxтƆg0xu]Za2颎weM(ɞͅS.A՘oTLW6c%P8ȂM0K*H &X%zm -pX{.P-Ex)_%WΞB@ji܂{z00@ Ϗ<>=_8a?Zu΃ͣ6˦ Hqq?~WQJ0X؇MCbB!&3-/_m.~yz F\2fcWPVdk7-AҊ`8>RZkAk|at0ꚓ-a(l`s qNG]>ߔG!*{JQ&vx >ݼD P(JIqӼ-v6DYh>^O0ʗPz6<0K'ӟ\n܏d><#H 2F2,_׏K;u^y!7ӹ)fN/u%hXP)a}X _ H@ia,tǤ71k6'Y':#bDZ7N>fgQb6~yz4dWeMvwy$Uk/BxIP9#4A3MUp~pJhGB*k*2>߃La1MQB3;oo +8YҿDO9}-._a 뵕~{98o'-8rk(YLc4Sẉs<̺=SSxK;Jρå58S&32] f RdK'i'L@yb;1/L q؂zr~ -i@/O"]y7BRwiHb٦ĕ C!ȲޘƱKK":ޥ)l5^`U材TA%_.HBW,[Dud.{XsU\SeH'&XTnET٪範6,"/77 Mps&m}*(@ "Λ%t>)gS{*COp0;np{:c HATz:A}bSZ%5C,a 19dtc!f^s7"2X$|T4}y"udؤ4D1QyYqC)r /0G;4xv!57[?7 /1`={?ω405&VnsutXwttbMOxCGQ畿NXoDՖ粰kY y"J@iC$ %٬'ޭYPOG)EGhIC@lAZVH6rl`WbpAnpI R.%:y>3Yǐ5hBڤd!^\E_z7 ?n`%Jb*9H7endstream endobj 229 0 obj 3013 endobj 230 0 obj <> endobj 231 0 obj <> endobj 4 0 obj <> endobj 5 0 obj <> endobj 6 0 obj <> endobj 19 0 obj <> endobj 25 0 obj <> endobj 177 0 obj <>/Length 119>>stream x334R0A#9 İ36620E y\ XKM&"80N\ %E\ \ )YE ,x endstream endobj 233 0 obj <> endobj 234 0 obj <> endobj 235 0 obj <> endobj 236 0 obj <>endobj 237 0 obj << /Type /Pages /Kids [ 241 0 R 1 0 R 9 0 R 14 0 R 22 0 R 28 0 R 33 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R 63 0 R 68 0 R 73 0 R 78 0 R 83 0 R 88 0 R 93 0 R 98 0 R 103 0 R 108 0 R 113 0 R 118 0 R 124 0 R 129 0 R 134 0 R 139 0 R 144 0 R 149 0 R 154 0 R 159 0 R 164 0 R 169 0 R 174 0 R 183 0 R 191 0 R 199 0 R 207 0 R 212 0 R 217 0 R 222 0 R 227 0 R ] /Count 43 >> endobj 238 0 obj <>stream PDFCreator 2.3.2.6 2018-10-09T02:17:59+02:00 2018-10-09T02:17:59+02:00 PDFCreator 2.3.2.6 FluidPolyMono-0004Administrateur endstream endobj xref 0 239 0000000000 65535 f 0000005316 00000 n 0000005476 00000 n 0000013016 00000 n 0000245129 00000 n 0000245212 00000 n 0000245296 00000 n 0000013036 00000 n 0000013067 00000 n 0000013151 00000 n 0000013314 00000 n 0000019647 00000 n 0000019668 00000 n 0000019700 00000 n 0000019773 00000 n 0000019937 00000 n 0000024493 00000 n 0000024514 00000 n 0000024648 00000 n 0000245367 00000 n 0000024847 00000 n 0000024879 00000 n 0000027624 00000 n 0000027788 00000 n 0000032432 00000 n 0000245440 00000 n 0000032453 00000 n 0000032485 00000 n 0000032551 00000 n 0000032715 00000 n 0000037425 00000 n 0000037446 00000 n 0000037478 00000 n 0000037555 00000 n 0000037719 00000 n 0000041033 00000 n 0000041054 00000 n 0000041086 00000 n 0000041152 00000 n 0000041316 00000 n 0000045012 00000 n 0000045033 00000 n 0000045065 00000 n 0000045131 00000 n 0000045295 00000 n 0000049975 00000 n 0000049996 00000 n 0000050028 00000 n 0000050094 00000 n 0000050258 00000 n 0000054893 00000 n 0000054914 00000 n 0000054946 00000 n 0000055012 00000 n 0000055176 00000 n 0000060345 00000 n 0000060366 00000 n 0000060398 00000 n 0000060453 00000 n 0000060617 00000 n 0000064761 00000 n 0000064782 00000 n 0000064814 00000 n 0000064880 00000 n 0000065044 00000 n 0000071524 00000 n 0000071545 00000 n 0000071577 00000 n 0000071654 00000 n 0000071818 00000 n 0000075835 00000 n 0000075856 00000 n 0000075888 00000 n 0000075965 00000 n 0000076129 00000 n 0000080295 00000 n 0000080316 00000 n 0000080348 00000 n 0000080425 00000 n 0000080589 00000 n 0000085243 00000 n 0000085264 00000 n 0000085296 00000 n 0000085373 00000 n 0000085537 00000 n 0000090704 00000 n 0000090725 00000 n 0000090757 00000 n 0000090812 00000 n 0000090976 00000 n 0000096679 00000 n 0000096700 00000 n 0000096732 00000 n 0000096809 00000 n 0000096973 00000 n 0000101611 00000 n 0000101632 00000 n 0000101664 00000 n 0000101719 00000 n 0000101885 00000 n 0000105950 00000 n 0000105972 00000 n 0000106005 00000 n 0000106050 00000 n 0000106218 00000 n 0000111640 00000 n 0000111662 00000 n 0000111695 00000 n 0000111751 00000 n 0000111919 00000 n 0000116314 00000 n 0000116336 00000 n 0000116369 00000 n 0000116414 00000 n 0000116582 00000 n 0000122142 00000 n 0000122164 00000 n 0000122197 00000 n 0000122264 00000 n 0000122432 00000 n 0000129039 00000 n 0000129061 00000 n 0000129128 00000 n 0000129161 00000 n 0000129260 00000 n 0000129428 00000 n 0000137365 00000 n 0000137387 00000 n 0000137420 00000 n 0000137475 00000 n 0000137643 00000 n 0000143800 00000 n 0000143822 00000 n 0000143855 00000 n 0000143911 00000 n 0000144079 00000 n 0000148223 00000 n 0000148245 00000 n 0000148278 00000 n 0000148345 00000 n 0000148513 00000 n 0000168895 00000 n 0000168918 00000 n 0000168951 00000 n 0000169007 00000 n 0000169175 00000 n 0000172331 00000 n 0000172353 00000 n 0000172386 00000 n 0000172453 00000 n 0000172621 00000 n 0000175983 00000 n 0000176005 00000 n 0000176038 00000 n 0000176105 00000 n 0000176273 00000 n 0000180205 00000 n 0000180227 00000 n 0000180260 00000 n 0000180338 00000 n 0000180506 00000 n 0000184779 00000 n 0000184801 00000 n 0000184834 00000 n 0000184912 00000 n 0000185080 00000 n 0000188690 00000 n 0000188712 00000 n 0000188745 00000 n 0000188812 00000 n 0000188980 00000 n 0000191021 00000 n 0000191043 00000 n 0000191076 00000 n 0000191132 00000 n 0000191337 00000 n 0000199858 00000 n 0000245534 00000 n 0000199880 00000 n 0000199908 00000 n 0000199943 00000 n 0000199976 00000 n 0000200011 00000 n 0000200078 00000 n 0000200283 00000 n 0000205541 00000 n 0000205563 00000 n 0000205591 00000 n 0000205626 00000 n 0000205659 00000 n 0000205694 00000 n 0000205749 00000 n 0000205954 00000 n 0000212402 00000 n 0000212424 00000 n 0000212452 00000 n 0000212487 00000 n 0000212520 00000 n 0000212555 00000 n 0000212622 00000 n 0000212827 00000 n 0000219155 00000 n 0000219177 00000 n 0000219205 00000 n 0000219240 00000 n 0000219273 00000 n 0000219308 00000 n 0000219375 00000 n 0000219543 00000 n 0000224822 00000 n 0000224844 00000 n 0000224877 00000 n 0000224944 00000 n 0000225112 00000 n 0000231665 00000 n 0000231687 00000 n 0000231720 00000 n 0000231776 00000 n 0000231944 00000 n 0000235735 00000 n 0000235757 00000 n 0000235790 00000 n 0000235846 00000 n 0000236014 00000 n 0000241642 00000 n 0000241664 00000 n 0000241697 00000 n 0000241774 00000 n 0000241942 00000 n 0000245029 00000 n 0000245051 00000 n 0000245084 00000 n 0000024956 00000 n 0000245882 00000 n 0000245943 00000 n 0000246019 00000 n 0000246095 00000 n 0000246625 00000 n 0000247004 00000 n trailer <> startxref 148 %%EOF fluidsynth-2.2.5/doc/polymono/leg_00.txt000066400000000000000000000054571417326347500201700ustar00rootroot00000000000000echo "legato mode 0 (retrigger), egal velocity on n1,n2,n3..." echo "---------------------------------------------------------" # Sounfont: GeneralUser GS 1.471 S. Christian Collins # Some presets # 0: Piano 0 ; short release # 16:organ 24:guitar # 52: Choir Aahs 53: Voice Oohs # # 56:trumpet 57:trombone 58:tuba 59:muted trumpet # 60:French horn 61:Brass section # 62:Synth brass 1 63:Synth Brass 2 # 64:Soprano sax 65:Alto sax 66:Tenor sax 67:Baritone sax. # 68:Oboe 69:English horn 70:Bassoon 71:Clarinet # 72:piccolo 73:Flute 74:Recorder 75:Pan flute # # 99:atmosphere; attack longer than organ, release longer than piano # 100:brillance , 101:gobelin echo "preset 73:flute" prog 0 73 echo "legato mode:0" setlegatomode 0 0 echo "legato On" cc 0 68 127 echo "noteon C 60 vel=127, during 1000 ms" noteon 0 60 127 sleep 1000 echo "noteon D,legato up C->D (60->62, vel=127), during 1000 ms" echo "noteoff C" noteon 0 62 127 noteoff 0 60 sleep 1000 echo "noteon E,legato up D->E (62->64, vel=127), during 1000 ms" echo "noteoff D" noteon 0 64 127 noteoff 0 62 sleep 1000 echo "noteon F,legato up E->F (64->65, vel=127), during 1000 ms" echo "noteoff E" noteon 0 65 127 noteoff 0 64 sleep 1000 echo "noteon G,legato up F->G (65->67, vel=127), during 1000 ms" echo "noteoff F" noteon 0 67 127 noteoff 0 65 sleep 1000 echo "noteon A,legato up G->A (67->69, vel=127), during 1000 ms" echo "noteoff G" noteon 0 69 127 noteoff 0 67 sleep 1000 echo "noteon B,legato up A->B (69->71, vel=127), during 1000 ms" echo "noteoff A" noteon 0 71 127 noteoff 0 69 sleep 1000 echo "noteon C,legato up B->C (71->72, vel=127), during 1000 ms" echo "noteoff B" noteon 0 72 127 noteoff 0 71 sleep 1000 echo "noteon B,legato down C->B (72->71, vel=127), during 1000 ms" echo "noteoff C" noteon 0 71 127 noteoff 0 72 sleep 1000 echo "noteon A,legato down B->A (71->69, vel=127), during 1000 ms" echo "noteoff B" noteon 0 69 127 noteoff 0 71 sleep 1000 echo "noteon G,legato down A->G (69->67, vel=127), during 1000 ms" echo "noteoff A" noteon 0 67 127 noteoff 0 69 sleep 1000 echo "noteon F,legato down G->F (67->65, vel=127), during 1000 ms" echo "noteoff G" noteon 0 65 127 noteoff 0 67 sleep 1000 echo "noteon E,legato down F->E (65->64, vel=127), during 1000 ms" echo "noteoff F" noteon 0 64 127 noteoff 0 65 sleep 1000 echo "noteon D,legato down E->D (64->62, vel=127), during 1000 ms" echo "noteoff E" noteon 0 62 127 noteoff 0 64 sleep 1000 echo "noteon C,legato down D->C (62->60, vel=127), during 1000 ms" echo "noteoff D" noteon 0 60 127 noteoff 0 62 sleep 1000 echo "noteoff C" noteoff 0 60 sleep 1000 echo "legato Off" cc 0 68 0 echo "End legato mode 1 (retrigger_1)" fluidsynth-2.2.5/doc/polymono/leg_01.txt000066400000000000000000000054751417326347500201710ustar00rootroot00000000000000echo "legato mode 1 (multi-retrigger), egal velocity on n1,n2,n3..." echo "-------------------------------------------------------------" # Sounfont: GeneralUser GS 1.471 S. Christian Collins # Some presets # 0: Piano 0 ; short release # 16:organ 24:guitar # 52: Choir Aahs 53: Voice Oohs # # 56:trumpet 57:trombone 58:tuba 59:muted trumpet # 60:French horn 61:Brass section # 62:Synth brass 1 63:Synth Brass 2 # 64:Soprano sax 65:Alto sax 66:Tenor sax 67:Baritone sax. # 68:Oboe 69:English horn 70:Bassoon 71:Clarinet # 72:piccolo 73:Flute 74:Recorder 75:Pan flute # # 99:atmosphere; attack longer than organ, release longer than piano # 100:brillance , 101:gobelin echo "preset 73:flute" prog 0 73 echo "legato mode:1" setlegatomode 0 1 echo "legato On" cc 0 68 127 echo "noteon C 60 vel=127, during 1000 ms" noteon 0 60 127 sleep 1000 echo "noteon D,legato up C->D (60->62, vel=127), during 1000 ms" echo "noteoff C" noteon 0 62 127 noteoff 0 60 sleep 1000 echo "noteon E,legato up D->E (62->64, vel=127), during 1000 ms" echo "noteoff D" noteon 0 64 127 noteoff 0 62 sleep 1000 echo "noteon F,legato up E->F (64->65, vel=127), during 1000 ms" echo "noteoff E" noteon 0 65 127 noteoff 0 64 sleep 1000 echo "noteon G,legato up F->G (65->67, vel=127), during 1000 ms" echo "noteoff F" noteon 0 67 127 noteoff 0 65 sleep 1000 echo "noteon A,legato up G->A (67->69, vel=127), during 1000 ms" echo "noteoff G" noteon 0 69 127 noteoff 0 67 sleep 1000 echo "noteon B,legato up A->B (69->71, vel=127), during 1000 ms" echo "noteoff A" noteon 0 71 127 noteoff 0 69 sleep 1000 echo "noteon C,legato up B->C (71->72, vel=127), during 1000 ms" echo "noteoff B" noteon 0 72 127 noteoff 0 71 sleep 1000 echo "noteon B,legato down C->B (72->71, vel=127), during 1000 ms" echo "noteoff C" noteon 0 71 127 noteoff 0 72 sleep 1000 echo "noteon A,legato down B->A (71->69, vel=127), during 1000 ms" echo "noteoff B" noteon 0 69 127 noteoff 0 71 sleep 1000 echo "noteon G,legato down A->G (69->67, vel=127), during 1000 ms" echo "noteoff A" noteon 0 67 127 noteoff 0 69 sleep 1000 echo "noteon F,legato down G->F (67->65, vel=127), during 1000 ms" echo "noteoff G" noteon 0 65 127 noteoff 0 67 sleep 1000 echo "noteon E,legato down F->E (65->64, vel=127), during 1000 ms" echo "noteoff F" noteon 0 64 127 noteoff 0 65 sleep 1000 echo "noteon D,legato down E->D (64->62, vel=127), during 1000 ms" echo "noteoff E" noteon 0 62 127 noteoff 0 64 sleep 1000 echo "noteon C,legato down D->C (62->60, vel=127), during 1000 ms" echo "noteoff D" noteon 0 60 127 noteoff 0 62 sleep 1000 echo "noteoff C" noteoff 0 60 sleep 1000 echo "legato Off" cc 0 68 0 echo "End legato mode 2 (multi-retrigger)" fluidsynth-2.2.5/doc/polymono/leg_por_00.txt000066400000000000000000000056311417326347500210420ustar00rootroot00000000000000echo "legato mode 0 (retrigger), egal velocity on n1,n2,n3..." echo "with portamento." echo "---------------------------------------------------------" # Sounfont: GeneralUser GS 1.471 S. Christian Collins # Some presets # 0: Piano 0 ; short release # 16:organ 24:guitar # 52: Choir Aahs 53: Voice Oohs # # 56:trumpet 57:trombone 58:tuba 59:muted trumpet # 60:French horn 61:Brass section # 62:Synth brass 1 63:Synth Brass 2 # 64:Soprano sax 65:Alto sax 66:Tenor sax 67:Baritone sax. # 68:Oboe 69:English horn 70:Bassoon 71:Clarinet # 72:piccolo 73:Flute 74:Recorder 75:Pan flute # # 99:atmosphere; attack longer than organ, release longer than piano # 100:brillance , 101:gobelin echo "preset 73:flute" prog 0 73 echo "legato mode:0" setlegatomode 0 0 echo "legato On, portamento On" cc 0 68 127 cc 0 5 2 cc 0 65 127 echo "noteon C 60 vel=127, during 1000 ms" noteon 0 60 127 sleep 1000 echo "noteon D,legato up C->D (60->62, vel=127), during 1000 ms" echo "noteoff C" noteon 0 62 127 noteoff 0 60 sleep 1000 echo "noteon E,legato up D->E (62->64, vel=127), during 1000 ms" echo "noteoff D" noteon 0 64 127 noteoff 0 62 sleep 1000 echo "noteon F,legato up E->F (64->65, vel=127), during 1000 ms" echo "noteoff E" noteon 0 65 127 noteoff 0 64 sleep 1000 echo "noteon G,legato up F->G (65->67, vel=127), during 1000 ms" echo "noteoff F" noteon 0 67 127 noteoff 0 65 sleep 1000 echo "noteon A,legato up G->A (67->69, vel=127), during 1000 ms" echo "noteoff G" noteon 0 69 127 noteoff 0 67 sleep 1000 echo "noteon B,legato up A->B (69->71, vel=127), during 1000 ms" echo "noteoff A" noteon 0 71 127 noteoff 0 69 sleep 1000 echo "noteon C,legato up B->C (71->72, vel=127), during 1000 ms" echo "noteoff B" noteon 0 72 127 noteoff 0 71 sleep 1000 echo "noteon B,legato down C->B (72->71, vel=127), during 1000 ms" echo "noteoff C" noteon 0 71 127 noteoff 0 72 sleep 1000 echo "noteon A,legato down B->A (71->69, vel=127), during 1000 ms" echo "noteoff B" noteon 0 69 127 noteoff 0 71 sleep 1000 echo "noteon G,legato down A->G (69->67, vel=127), during 1000 ms" echo "noteoff A" noteon 0 67 127 noteoff 0 69 sleep 1000 echo "noteon F,legato down G->F (67->65, vel=127), during 1000 ms" echo "noteoff G" noteon 0 65 127 noteoff 0 67 sleep 1000 echo "noteon E,legato down F->E (65->64, vel=127), during 1000 ms" echo "noteoff F" noteon 0 64 127 noteoff 0 65 sleep 1000 echo "noteon D,legato down E->D (64->62, vel=127), during 1000 ms" echo "noteoff E" noteon 0 62 127 noteoff 0 64 sleep 1000 echo "noteon C,legato down D->C (62->60, vel=127), during 1000 ms" echo "noteoff D" noteon 0 60 127 noteoff 0 62 sleep 1000 echo "noteoff C" noteoff 0 60 sleep 1000 echo "legato Off, portamento off" cc 0 68 0 cc 0 65 0 echo "End legato mode 1 (retrigger_1) with portamento" fluidsynth-2.2.5/doc/polymono/leg_por_01.txt000066400000000000000000000056471417326347500210520ustar00rootroot00000000000000echo "legato mode 1 (multi-retrigger), egal velocity on n1,n2,n3..." echo "with portamento." echo "-------------------------------------------------------------" # Sounfont: GeneralUser GS 1.471 S. Christian Collins # Some presets # 0: Piano 0 ; short release # 16:organ 24:guitar # 52: Choir Aahs 53: Voice Oohs # # 56:trumpet 57:trombone 58:tuba 59:muted trumpet # 60:French horn 61:Brass section # 62:Synth brass 1 63:Synth Brass 2 # 64:Soprano sax 65:Alto sax 66:Tenor sax 67:Baritone sax. # 68:Oboe 69:English horn 70:Bassoon 71:Clarinet # 72:piccolo 73:Flute 74:Recorder 75:Pan flute # # 99:atmosphere; attack longer than organ, release longer than piano # 100:brillance , 101:gobelin echo "preset 73:flute" prog 0 73 echo "legato mode:1" setlegatomode 0 1 echo "legato On, portamento On" cc 0 68 127 cc 0 5 2 cc 0 65 127 echo "noteon C 60 vel=127, during 1000 ms" noteon 0 60 127 sleep 1000 echo "noteon D,legato up C->D (60->62, vel=127), during 1000 ms" echo "noteoff C" noteon 0 62 127 noteoff 0 60 sleep 1000 echo "noteon E,legato up D->E (62->64, vel=127), during 1000 ms" echo "noteoff D" noteon 0 64 127 noteoff 0 62 sleep 1000 echo "noteon F,legato up E->F (64->65, vel=127), during 1000 ms" echo "noteoff E" noteon 0 65 127 noteoff 0 64 sleep 1000 echo "noteon G,legato up F->G (65->67, vel=127), during 1000 ms" echo "noteoff F" noteon 0 67 127 noteoff 0 65 sleep 1000 echo "noteon A,legato up G->A (67->69, vel=127), during 1000 ms" echo "noteoff G" noteon 0 69 127 noteoff 0 67 sleep 1000 echo "noteon B,legato up A->B (69->71, vel=127), during 1000 ms" echo "noteoff A" noteon 0 71 127 noteoff 0 69 sleep 1000 echo "noteon C,legato up B->C (71->72, vel=127), during 1000 ms" echo "noteoff B" noteon 0 72 127 noteoff 0 71 sleep 1000 echo "noteon B,legato down C->B (72->71, vel=127), during 1000 ms" echo "noteoff C" noteon 0 71 127 noteoff 0 72 sleep 1000 echo "noteon A,legato down B->A (71->69, vel=127), during 1000 ms" echo "noteoff B" noteon 0 69 127 noteoff 0 71 sleep 1000 echo "noteon G,legato down A->G (69->67, vel=127), during 1000 ms" echo "noteoff A" noteon 0 67 127 noteoff 0 69 sleep 1000 echo "noteon F,legato down G->F (67->65, vel=127), during 1000 ms" echo "noteoff G" noteon 0 65 127 noteoff 0 67 sleep 1000 echo "noteon E,legato down F->E (65->64, vel=127), during 1000 ms" echo "noteoff F" noteon 0 64 127 noteoff 0 65 sleep 1000 echo "noteon D,legato down E->D (64->62, vel=127), during 1000 ms" echo "noteoff E" noteon 0 62 127 noteoff 0 64 sleep 1000 echo "noteon C,legato down D->C (62->60, vel=127), during 1000 ms" echo "noteoff D" noteon 0 60 127 noteoff 0 62 sleep 1000 echo "noteoff C" noteoff 0 60 sleep 1000 echo "legato Off, portamento off" cc 0 68 0 cc 0 65 0 echo "End legato mode 2 (multi-retrigger) with portamento" fluidsynth-2.2.5/doc/polymono/poly_mono_0.txt000066400000000000000000000004261417326347500213430ustar00rootroot00000000000000# What are default 'basic channels' in FluidSynth ? #-------------------------------------------------- # At initialization the default basic channel is poly omnion (see pdf 2.2). # type the command basicchannels to display actual basic channels. basicchannels # end fluidsynth-2.2.5/doc/polymono/poly_mono_1.txt000066400000000000000000000015541417326347500213470ustar00rootroot00000000000000# How to change the whole set of actual basic channels ? # ------------------------------------------------------------- # Assuming you want to set 2 groups of channels: # Group 1: first channel 5 in poly mode, only one channel # Group 2: first channel 10 in mono mode , only one channel # Group 1 should have following settings: # basic channel 5, mode poly, omni off, (mode 2). # Group 2 should have the following settings: # basic channel 10, mode mono, omni off, (mode 3), composed of one channel. # First use the command resetbasicchannels to reset all basic channels resetbasicchannels # Then use command setbasicchannels using numbered mode 2 and 3: setbasicchannels 5 2 0 10 3 1 # or using named mode: # setbasicchannels 5 poly_omnioff 0 10 mono_omnioff 1 # Use basicchannels command to verify your settings basicchannels # end fluidsynth-2.2.5/doc/polymono/poly_mono_2.txt000066400000000000000000000014401417326347500213420ustar00rootroot00000000000000# How to add a new basic channel among others actual basic channels ? # ------------------------------------------------------------------- # Perhaps you have already set several groups of basics channels and # you want add a new one without modifying actual groups. # Assuming following actual groups: # Basic channel: 5, poly omni off(mode 2), nbr: 1 # Basic channel: 10, mono omni off(mode 3), nbr: 1 # Now we want to add a new group 3: # Group 3 should have the following settings: # basic channel 13, mode mono, omni off, (mode 3) composed of 2 channels. # Use command setbasicchannels using numbered mode 3: setbasicchannels 13 3 2 # or using named mode: # setbasicchannels 13 mono_omnioff 2 Use basicchannels command to verify your settings basicchannels # end fluidsynth-2.2.5/doc/polymono/poly_mono_3.txt000066400000000000000000000016471417326347500213540ustar00rootroot00000000000000# How to change an actual basic channel ? # --------------------------------------- # Perhaps you have already set several groups of basics channels # and you want change the settings of one. # Assuming following actual groups: # -Group 1:Basic channel: 5, poly omni off(mode 2), nbr: 1 # -Group 2:Basic channel: 10, mono omni off(mode 3), nbr: 1 # -Group 3:Basic channel: 13, mono omni off(mode 3), nbr: 2 # Now we want to change group 1: # Group 1 should have the following settings: # -basic channel 5, mode poly, omni on, (mode 0) composed of 4 channels in this group. #First use the command resetbasicchannels to clear the group intended to be changed. resetbasicchannels 5 # Then use command setbasicchannels using numbered mode 0: setbasicchannels 5 0 4 # or Using named mode: # setbasicchannels 5 poly_omnion 4 # Use basicchannels command to verify your settings basicchannels # end fluidsynth-2.2.5/doc/polymono/poly_mono_4.txt000066400000000000000000000017211417326347500213460ustar00rootroot00000000000000# How to change an actual basic channel and add a new one ? # --------------------------------------------------------- # Note that command setbasicchannels allows to add or change # groups of basics channels. # # Note also that the commands allows this for more than one # groups executing only one command: # The following command restores Group 1 to the state: # -Group 1:Basic channel: 5, poly omni off(mode 2), nbr: 1 # Then adds a new group: # -Group 0:Basic channel: 2, mono omni on(mode 1), composed of 3 possible channels in this group #First use the command resetbasicchannels to clear the group intended to be changed. resetbasicchannels 5 # Use command setbasicchannels to change a group and add a new one, using numbered mode 2 and 1: setbasicchannels 5 2 0 2 1 3 # or using named mode: # setbasicchannels 5 poly_omnioff 0 2 mono_omnion 3 # Use basicchannels command to verify your settings basicchannels # end fluidsynth-2.2.5/doc/polymono/poly_mono_5.txt000066400000000000000000000004161417326347500213470ustar00rootroot00000000000000# How to know the state of one or more MIDI channels ? # ----------------------------------------------------- # Use the command channelsmode [chan1 chan2 .] channelsmode # To display the state of MIDI channels 2,5, 10, 13 only channelsmode 2 5 10 13 # end fluidsynth-2.2.5/doc/polymono/readme.txt000066400000000000000000000010531417326347500203430ustar00rootroot00000000000000/fluidSynth/doc/polymono directory contains: 1) FluidPolyMono-0004.pdf, the documentation of poly/mono functionalities. 2) tutorials examples files: 2.1) tutorials chapter 2.1 poly_mono_0.txt poly_mono_1.txt poly_mono_2.txt poly_mono_3.txt poly_mono_4.txt poly_mono_5.txt 2.2) tutorials chapter 3.1 leg_00.txt leg_01.txt leg_por_00.txt leg_por_01.txt These tutorials files are usable directly on the FluidSynth console application. See the pdf file (chapters 2.1 and 3.1). jean-jacques ceresa fluidsynth-2.2.5/doc/recent_changes.txt000066400000000000000000000252351417326347500202120ustar00rootroot00000000000000/*! \page RecentChanges Recent Changes \section NewIn2_2_0 What's new in 2.2.0? - #fluid_file_callbacks_t now uses long long as file-offset type (see #fluid_long_long_t).This is a breaking change, which allows to load SoundFonts bigger than 2GiB on Windows. This change required to bump fluidsynth's SOVERSION. - various fluid_event_*() functions that received a "value" argument of type short now receive an int argument in preparation for MIDI 2.0 support - fluid_event_any_control_change() has been removed - the sequencer has received a major revisal. For you that means: - the sequencer's queue no longer blocks the synthesizer thread, due to being busy arranging its events internally. - events that share the same tick was given a new, documented order, see fluid_sequencer_send_at(). - the sequencer's scale can now be used for arbitrary tempo changes. Previously, the scale of the sequencer was limited to 1000. The only limitation now is >0. - there is now a dedicated event type for changing the sequencer's time scale, see fluid_event_scale(). - the dynamic-sample-loader has learned support to pin samples, see fluid_synth_pin_preset() and fluid_synth_unpin_preset() - added getter and setter functions for individual effect groups - support for UTF-8 filenames under Windows, see fluid_synth_sfload() - MIDI Tempo of the fluid_player can now be overridden, see fluid_player_set_tempo() - a per-tick callback has been added to the MIDI player, see fluid_player_set_tick_callback() - WASAPI audio driver is now available on Windows - the following drivers have gained support for new_fluid_audio_driver2(): - DSound - WaveOut - WASAPI \section NewIn2_1_7 What's new in 2.1.7? - a regression introduced in 2.1.0 prevented chorus from being audible when fluid_synth_process() was used \section NewIn2_1_4 What's new in 2.1.4? - a regression introduced in 2.1.3 broke fluid_synth_start() for DLS presets \section NewIn2_1_1 What's new in 2.1.1? - requirements for explicit sequencer client unregistering have been relaxed: delete_fluid_sequencer() now correctly frees any registered sequencer clients (clients can still be explicitly unregistered) - using the sequencer with the system timer as timing source has been deprecated \section NewIn2_1_0 What's new in 2.1.0? - refrain from using fluid_synth_set_sample_rate() - \setting{synth_sample-rate} is no real-time setting anymore, see note about fluid_synth_set_sample_rate() - new reverb engine - chorus is now stereophonic - smallest allowed chorus speed is now 0.1 Hz (previously 0.29 Hz) - the following audio drivers were added: - opensles - oboe - sdl2 - waveout \section NewIn2_0_8 What's new in 2.0.8? - fluid_sample_set_sound_data() caused broken sound when copying sample data \section NewIn2_0_7 What's new in 2.0.7? - fluid_free() has been added to allow proper deallocation by programming languages other than C/C++ \section NewIn2_0_6 What's new in 2.0.6? - the MIDI player did not emit any audio when calling fluid_player_play() after fluid_player_stop() \section NewIn2_0_5 What's new in 2.0.5? - fluid_synth_process() omitted audio samples when called with arbitrary sample counts that were not a multiple of fluid_synth_get_internal_bufsize() - fluid_synth_sfunload() was not releasing sample buffers of SoundFont3 files if \setting{synth_dynamic-sample-loading} was set to FALSE \section NewIn2_0_3 What's new in 2.0.3? - fix incorrect behaviour of fluid_sample_set_sound_data() - add missing getters for midi events: - fluid_midi_event_get_text() - fluid_midi_event_get_lyrics() \section NewIn2_0_2 What's new in 2.0.2? - fluid_synth_error() has been deprecated, use fluid_set_log_function() to interfere log messages \section NewIn2_0_0 What's new in 2.0.0? FluidSynths major version was bumped. The API was reworked, deprecated functions were removed. Important changes that may not result in a compilation error but may cause your app to misbehave: - all public \c fluid_settings_* functions that return an integer which is not meant to be interpreted as bool consistently return either FLUID_OK or FLUID_FAILED - fluid_settings_setstr() cannot be used to set integer (toggle) settings with "yes" or "no" values anymore. Use fluid_settings_setint() instead, for example:
fluid_settings_setint(settings, "synth.reverb.active", 0) instead of fluid_settings_setstr(settings, "synth.reverb.active", "no") - explicit client unregistering is required for fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth() (since fluidsynth 2.1.1 not required anymore, but still recommend) - all public functions consistently receive signed integers for soundfont ids, bank and program numbers - use unique device names for the \setting{audio_portaudio_device} setting - fluid_synth_process() received a new more flexible implementation, but now requires zeroed-out sample buffers Other changes in FluidSynth 2.0.0 concerning developers: - all public \c delete_* functions return void and are safe when called with NULL - the shell command handler was decoupled internally, as a consequence the param list of new_fluid_server() and new_fluid_cmd_handler() was adapted - \c fluid_settings_set* functions no longer silently register unknown settings but return an error instead - reverb: roomsize is now limited to an upper threshold of 1.0 to avoid exponential volume increase - rename \c fluid_mod_new() and \c fluid_mod_delete() to match naming conventions: new_fluid_mod() and delete_fluid_mod() - rename chorus getters to match naming conventions: fluid_synth_get_chorus_speed() and fluid_synth_get_chorus_depth() - fluid_synth_remove_sfont() returns FLUID_OK or FLUID_FAILED - introduce a separate data type for sequencer client IDs: #fluid_seq_id_t - fluid_get_userconf() has been implemented for Windows New Features and API additions: - add \setting{midi_autoconnect} a setting for automatically connecting fluidsynth to available MIDI input ports - add \setting{synth_overflow_important} and \setting{synth_overflow_important-channels} settings to take midi channels during overflow calculation into account that are considered to be "important" - add \setting{synth_dynamic-sample-loading} a setting for enabling on demand sample loading - add support for polyphonic key pressure events, see fluid_event_key_pressure() and fluid_synth_key_pressure() - add fluid_synth_add_default_mod() and fluid_synth_remove_default_mod() for manipulating default modulators - add individual reverb setters: fluid_synth_set_reverb_roomsize(), fluid_synth_set_reverb_damp(), fluid_synth_set_reverb_width(), fluid_synth_set_reverb_level() - add individual chorus setters: fluid_synth_set_chorus_nr(), fluid_synth_set_chorus_level(), fluid_synth_set_chorus_speed(), fluid_synth_set_chorus_depth(), fluid_synth_set_chorus_type() - add realtime settings for \setting{synth_reverb_damp} and \setting{synth_chorus_depth} parameters - add seek support to midi-player, see fluid_player_seek() - expose functions to manipulate the ladspa effects unit (see ladspa.h) - add support for text and lyrics midi events, see fluid_midi_event_set_lyrics() and fluid_midi_event_set_text() - complete rewrite of the soundfont loader API, see sfont.h - support for 24 bit audio samples, see fluid_sample_set_sound_data() - expose new_fluid_defsfloader() to support loading soundfonts from memory, see fluid_sfloader_set_callbacks() and
fluidsynth_sfload_mem.c - remove these structs from the public API and provide proper getter and setter functions instead: - struct _fluid_sfloader_t - struct _fluid_sample_t - struct _fluid_sfont_t - struct _fluid_preset_t - add an additional general-purpose IIR filter, see fluid_synth_set_custom_filter() - add a custom sinusoidal modulator mapping function, see #FLUID_MOD_SIN - implement polymono support according to MIDI specs: - add basic channel support, see fluid_synth_reset_basic_channel(), fluid_synth_set_basic_channel(), fluid_synth_get_basic_channel() - implement MIDI modes Omni On, Omni Off, Poly, Mono, see #fluid_basic_channel_modes - implement portamento control, see fluid_synth_set_portamento_mode(), fluid_synth_get_portamento_mode() - implement legato control, see fluid_synth_set_legato_mode(), fluid_synth_get_legato_mode() - implement breath control, see fluid_synth_set_breath_mode(), fluid_synth_get_breath_mode() API cleanups: - the ramsfont has been removed, because it is unmaintained and believed to be unused; please get in touch with the mailing list if you still need it - remove deprecated fluid_synth_get_channel_info() in favour of fluid_synth_get_program() and fluid_synth_get_channel_preset() - remove deprecated fluid_settings_getstr() - remove deprecated fluid_synth_set_midi_router(), instead supply the midi-router instance when creating a command handler with new_fluid_cmd_handler() - remove deprecated fluid_get_hinstance() and fluid_set_hinstance() (dsound driver now uses the desktop window) - remove deprecated fluid_synth_create_key_tuning(), use fluid_synth_activate_key_tuning(synth, bank, prog, name, pitch, FALSE) instead - remove deprecated fluid_synth_create_octave_tuning(), use fluid_synth_activate_octave_tuning(synth, bank, prog, name, pitch, FALSE) instead - remove deprecated fluid_synth_select_tuning(), use fluid_synth_activate_tuning(synth, chan, bank, prog, FALSE) instead - remove deprecated fluid_synth_reset_tuning(), use fluid_synth_deactivate_tuning(synth, chan, FALSE) instead - remove deprecated FLUID_HINT_INTEGER - remove deprecated fluid_synth_set_gen2() as there doesn't seem to be a use case for absolute generator values - remove deprecated "synth.parallel-render" setting - remove obsolete "audio.[out|in]put-channels" settings - remove unimplemented "synth.dump" setting - remove fluid_cmd_handler_register() and fluid_cmd_handler_unregister() from public API, as they seem to be unused downstream - remove misspelled FLUID_SEQ_PITCHWHHELSENS macro - remove struct _fluid_mod_t from public API, use the getters and setters of mod.h instead - remove struct _fluid_gen_t, fluid_gen_set_default_values() and enum fluid_gen_flags from public API - remove macros fluid_sfont_get_id() and fluid_sample_refcount() from public API - remove FLUID_NUM_MOD macro from public API - remove the following deprecated enum values from public API: - GEN_LAST - LAST_LOG_LEVEL - FLUID_SEQ_LASTEVENT - FLUID_MIDI_ROUTER_RULE_COUNT */ fluidsynth-2.2.5/doc/usage/000077500000000000000000000000001417326347500155765ustar00rootroot00000000000000fluidsynth-2.2.5/doc/usage/_overview.txt000066400000000000000000000006651417326347500203530ustar00rootroot00000000000000/*! \page UsageGuide Usage Guide - \subpage CreatingSettings - \subpage CreatingSynth - \subpage LoadingSoundfonts - \subpage CreatingAudioDriver - \subpage UsingSynth - \subpage SendingMIDI - \subpage RealtimeMIDI - \subpage MIDIPlayer - \subpage FileRenderer - \subpage MIDIPlayerMem - \subpage MIDIRouter - \subpage Sequencer - \subpage Shell - \subpage Multi-channel - \subpage synth-context - \subpage Advanced */ fluidsynth-2.2.5/doc/usage/advanced.txt000066400000000000000000000005501417326347500201040ustar00rootroot00000000000000/*! \page Advanced Advanced features The following features are not yet fully documented. Some information can be found in the API reference and in the GitHub Wiki. - Accessing low-level voice parameters - Reverb settings - Chorus settings - Interpolation settings (set_gen, get_gen, NRPN) - Voice overflow settings - LADSPA effects unit - MIDI tunings */ fluidsynth-2.2.5/doc/usage/audio_driver.txt000066400000000000000000000066271417326347500210260ustar00rootroot00000000000000/*! \page 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 audio.driver 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) - opensles: OpenSL ES (Android) - oboe: Oboe (Android) - waveout: Microsoft WaveOut, alternative to DirectSound (Windows CE x86, Windows Mobile 2003 for ARMv5, Windows 98 SE, Windows NT 4.0, Windows XP and later) - file: Driver to output audio to a file - sdl2*: Simple DirectMedia Layer (Linux, Windows, Mac OS X, iOS, Android, FreeBSD, Haiku, etc.) The default audio driver depends on the settings with which FluidSynth was compiled. You can get the default driver with fluid_settings_getstr_default(). To get the list of available drivers use the fluid_settings_foreach_option() function. Finally, you can set the driver with fluid_settings_setstr(). In most cases, the default driver should work out of the box. Additional options that define the audio quality and latency are \setting{audio_sample-format}, \setting{audio_period-size}, and \setting{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 \setting{audio_periods} and \setting{audio_period-size} settings define the latency and robustness against scheduling delays. There are additional settings for the audio subsystems used. For a full list of available audio driver settings, please refer to the \setting{audio} documentation. *Note: In order to use sdl2 as audio driver, the application is responsible for initializing SDL (e.g. with SDL_Init()). This must be done before the first call to new_fluid_settings()! Also make sure to call SDL_Quit() after all fluidsynth instances have been destroyed. */ fluidsynth-2.2.5/doc/usage/creating_settings.txt000066400000000000000000000030101417326347500220450ustar00rootroot00000000000000/*! \page 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, \setting{synth_polyphony} refers to the number of voices (polyphony) allocated by the synthesizer. The settings also have a type. There are currently three types: strings, numbers (double floats), and integers. You can change the values of a setting using the fluid_settings_setstr(), fluid_settings_setnum(), and fluid_settings_setint() functions. For example: \code #include 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 a logarithmic scale. Check the settings.h API documentation for a description of all functions. */ fluidsynth-2.2.5/doc/usage/creating_synth.txt000066400000000000000000000011211417326347500213530ustar00rootroot00000000000000/*! \page 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 For a full list of available synthesizer settings, please refer to the \setting{synth} documentation. */ fluidsynth-2.2.5/doc/usage/file_renderer.txt000066400000000000000000000034301417326347500211440ustar00rootroot00000000000000/*! \page FileRenderer Fast file renderer for non-realtime MIDI file rendering Instead of creating an audio driver as described in section \ref MIDIPlayer one may chose to use the file renderer, which is the fastest way to synthesize MIDI files. \code fluid_settings_t* settings; fluid_synth_t* synth; fluid_player_t* player; fluid_file_renderer_t* renderer; settings = new_fluid_settings(); // specify the file to store the audio to // make sure you compiled fluidsynth with libsndfile to get a real wave file // otherwise this file will only contain raw s16 stereo PCM fluid_settings_setstr(settings, "audio.file.name", "/path/to/output.wav"); // use number of samples processed as timing source, rather than the system timer fluid_settings_setstr(settings, "player.timing-source", "sample"); // since this is a non-realtime scenario, there is no need to pin the sample data fluid_settings_setint(settings, "synth.lock-memory", 0); synth = new_fluid_synth(settings); // *** loading of a soundfont omitted *** player = new_fluid_player(synth); fluid_player_add(player, "/path/to/midifile.mid"); fluid_player_play(player); renderer = new_fluid_file_renderer (synth); while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) { if (fluid_file_renderer_process_block(renderer) != FLUID_OK) { break; } } // just for sure: stop the playback explicitly and wait until finished fluid_player_stop(player); fluid_player_join(player); delete_fluid_file_renderer(renderer); delete_fluid_player(player); delete_fluid_synth(synth); delete_fluid_settings(settings); \endcode Various output files types are supported, if compiled with libsndfile. Those can be specified via the \c settings object as well. Refer to the \setting{audio} documentation for more \c audio.file\.\* options. */ fluidsynth-2.2.5/doc/usage/loading_soundfonts.txt000066400000000000000000000032261417326347500222410ustar00rootroot00000000000000/*! \page 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. */ fluidsynth-2.2.5/doc/usage/manual_rendering.txt000066400000000000000000000013041417326347500216470ustar00rootroot00000000000000/*! \page 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. The function fluid_synth_process() is the generic interface for synthesizing audio, which is also capable of multi channel audio output. */ fluidsynth-2.2.5/doc/usage/midi_player.txt000066400000000000000000000033141417326347500206360ustar00rootroot00000000000000/*! \page 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); /* 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]); } } /* start the synthesizer thread */ adriver = new_fluid_audio_driver(settings, synth); /* 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 A list of available MIDI player settings can be found in the \setting{player} documentation. */ fluidsynth-2.2.5/doc/usage/midi_player_mem.txt000066400000000000000000000040511417326347500214730ustar00rootroot00000000000000/*! \page 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 */ fluidsynth-2.2.5/doc/usage/midi_router.txt000066400000000000000000000056311417326347500206660ustar00rootroot00000000000000/*! \page 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 */ fluidsynth-2.2.5/doc/usage/multi_channel.txt000066400000000000000000000010221417326347500211540ustar00rootroot00000000000000/*! \page Multi-channel Multi-channel audio rendering FluidSynth is capable of rendering all audio and all effects from all MIDI channels to separate stereo buffers. Refer to the documentation of fluid_synth_process() and review the different use-cases in the example file for information on how to do that: \ref fluidsynth_process.c The following chart illustrates how the voices (produced by MIDI NoteOns) are dispatched or mapped to their dry/effects audio buffers. \image html fluid_mixer.svg "FluidSynth Mixer Chart" */ fluidsynth-2.2.5/doc/usage/realtime_midi.txt000066400000000000000000000031241417326347500211430ustar00rootroot00000000000000/*! \page 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 \setting{midi_driver} setting defines the MIDI subsystem that will be used. There are additional settings for the MIDI subsystems used. For a full list of available midi driver settings, please refer to the \setting{midi} documentation. */ fluidsynth-2.2.5/doc/usage/sending_midi.txt000066400000000000000000000026351417326347500207760ustar00rootroot00000000000000/*! \page 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 */ fluidsynth-2.2.5/doc/usage/sequencer.txt000066400000000000000000000110351417326347500203310ustar00rootroot00000000000000/*! \page Sequencer Using the MIDI 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_setint(settings, "synth.reverb.active", 0); fluid_settings_setint(settings, "synth.chorus.active", 0); synth = new_fluid_synth(settings); adriver = new_fluid_audio_driver(settings, synth); sequencer = new_fluid_sequencer2(0); // 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 */ fluidsynth-2.2.5/doc/usage/shell.txt000066400000000000000000000006561417326347500174550ustar00rootroot00000000000000/*! \page 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, type @c help in the fluidsynth command line shell. For a full list of available command line settings, please refer to the \ref settings_shell documentation. */ fluidsynth-2.2.5/doc/usage/synth_context.txt000066400000000000000000000021461417326347500212530ustar00rootroot00000000000000/*! \page synth-context Understanding the "synthesis context" When reading through the functions exposed via our API, you will often read the note: "May or may not be called from synthesis context." The reason for this is that some functions are intentionally not thread-safe. Or they require to be called from this context to behave correctly. FluidSynth's rendering engine is implemented by using the "Dispatcher Thread Pattern". This means that a certain thread @c A which calls one of FluidSynth's rendering functions, namely - fluid_synth_process() - fluid_synth_nwrite_float() - fluid_synth_write_float() - fluid_synth_write_s16() automatically becomes the "synthesis thread". The terms "synthesis context" and "synthesis thread" are equivalent. A few locations in our API provide hooks that allow you to interfere this "synthesis context". At those locations you can register your own custom functions that will always be called by thread @c A. For this use-case, the following functions are of interest: - new_fluid_audio_driver2() - fluid_player_set_playback_callback() - fluid_sequencer_register_client() */ fluidsynth-2.2.5/fluidsynth.conf.in000066400000000000000000000003201417326347500173650ustar00rootroot00000000000000# Mandatory parameters (uncomment and edit) #SOUND_FONT=@DEFAULT_SOUNDFONT@ # Additional optional parameters (may be useful, see 'man fluidsynth' for further info) #OTHER_OPTS='-a alsa -m alsa_seq -r 48000' fluidsynth-2.2.5/fluidsynth.pc.in000066400000000000000000000005041417326347500170460ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: FluidSynth Description: Software SoundFont synth Version: @VERSION@ Requires.private: @PC_REQUIRES_PRIV_JOINED@ Libs: -L${libdir} -lfluidsynth Libs.private: @LIBS_PRIVATE_JOINED@ @LIBS_PRIVATE_WITH_PATH_JOINED@ Cflags: -I${includedir} fluidsynth-2.2.5/fluidsynth.service.in000066400000000000000000000005161417326347500201070ustar00rootroot00000000000000[Unit] Description=FluidSynth Daemon Documentation=man:fluidsynth(1) After=sound.target [Service] Type=notify NotifyAccess=main EnvironmentFile=@FLUID_DAEMON_ENV_FILE@ EnvironmentFile=-%h/.config/fluidsynth ExecStart=@CMAKE_INSTALL_PREFIX@/@BIN_INSTALL_DIR@/fluidsynth -is $OTHER_OPTS $SOUND_FONT [Install] WantedBy=default.target fluidsynth-2.2.5/include/000077500000000000000000000000001417326347500153505ustar00rootroot00000000000000fluidsynth-2.2.5/include/fluidsynth.cmake000066400000000000000000000067631417326347500205570ustar00rootroot00000000000000/* 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 #cmakedefine01 BUILD_SHARED_LIBS #if (BUILD_SHARED_LIBS == 0) #define FLUIDSYNTH_API // building static lib? no visibility control then #elif defined(WIN32) #if defined(FLUIDSYNTH_NOT_A_DLL) #define FLUIDSYNTH_API #elif defined(FLUIDSYNTH_DLL_EXPORTS) #define FLUIDSYNTH_API __declspec(dllexport) #else #define FLUIDSYNTH_API __declspec(dllimport) #endif #elif defined(MACOS9) #define FLUIDSYNTH_API __declspec(export) #elif defined(__OS2__) #define FLUIDSYNTH_API __declspec(dllexport) #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/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" #include "fluidsynth/ladspa.h" #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_H */ fluidsynth-2.2.5/include/fluidsynth/000077500000000000000000000000001417326347500175415ustar00rootroot00000000000000fluidsynth-2.2.5/include/fluidsynth/audio.h000066400000000000000000000127521417326347500210220ustar00rootroot00000000000000/* 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 /** * @defgroup audio_output Audio Output * * Functions for managing audio drivers and file renderers. * * The file renderer is used for fast rendering of MIDI files to * audio files. The audio drivers are a high-level interface to * connect the synthesizer with external audio sinks or to render * real-time audio to files. */ /** * @defgroup audio_driver Audio Driver * @ingroup audio_output * * Functions for managing audio drivers. * * 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. * * @param data The user data parameter as passed to new_fluid_audio_driver2(). * @param len Count of audio frames to synthesize. * @param nfx Count of arrays in \c fx. * @param fx Array of buffers to store effects audio to. Buffers may alias with buffers of \c out. * @param nout Count of arrays in \c out. * @param out Array of buffers to store (dry) audio to. Buffers may alias with buffers of \c fx. * @return Should return #FLUID_OK on success, #FLUID_FAILED if an error occurred. * * This function is responsible for rendering audio to the buffers. * The buffers passed to this function are allocated and owned by the respective * audio driver and are only valid during that specific call (do not cache them). * The buffers have already been zeroed-out. * For further details please refer to fluid_synth_process(). * * @parblock * @note Whereas fluid_synth_process() allows aliasing buffers, there is the guarantee that @p out * and @p fx buffers provided by fluidsynth's audio drivers never alias. This prevents downstream * applications from e.g. applying a custom effect accidentally to the same buffer multiple times. * @endparblock * * @parblock * @note Also note that the Jack driver is currently the only driver that has dedicated @p fx buffers * (but only if \setting{audio_jack_multi} is true). All other drivers do not provide @p fx buffers. * In this case, users are encouraged to mix the effects into the provided dry buffers when calling * fluid_synth_process(). * @code{.cpp} int myCallback(void *, int len, int nfx, float *fx[], int nout, float *out[]) { int ret; if(nfx == 0) { float *fxb[4] = {out[0], out[1], out[0], out[1]}; ret = fluid_synth_process(synth, len, sizeof(fxb) / sizeof(fxb[0]), fxb, nout, out); } else { ret = fluid_synth_process(synth, len, nfx, fx, nout, out); } // ... client-code ... return ret; } * @endcode * For other possible use-cases refer to \ref fluidsynth_process.c . * @endparblock */ typedef int (*fluid_audio_func_t)(void *data, int len, int nfx, float *fx[], int nout, float *out[]); /** @startlifecycle{Audio Driver} */ 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); /** @endlifecycle */ FLUIDSYNTH_API int fluid_audio_driver_register(const char **adrivers); /* @} */ /** * @defgroup file_renderer File Renderer * @ingroup audio_output * * Functions for managing file renderers and triggering the rendering. * * The file renderer is only used to render a MIDI file to audio as fast * as possible. Please see \ref FileRenderer for a full example. * * If you are looking for a way to write audio generated * from real-time events (for example from an external sequencer or a MIDI controller) to a file, * please have a look at the \c file \ref audio_driver instead. * * * @{ */ /** @startlifecycle{File Renderer} */ FLUIDSYNTH_API fluid_file_renderer_t *new_fluid_file_renderer(fluid_synth_t *synth); FLUIDSYNTH_API void delete_fluid_file_renderer(fluid_file_renderer_t *dev); /** @endlifecycle */ FLUIDSYNTH_API int fluid_file_renderer_process_block(fluid_file_renderer_t *dev); FLUIDSYNTH_API int fluid_file_set_encoding_quality(fluid_file_renderer_t *dev, double q); /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_AUDIO_H */ fluidsynth-2.2.5/include/fluidsynth/event.h000066400000000000000000000151301417326347500210330ustar00rootroot00000000000000/* 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 /** * @defgroup sequencer_events Sequencer Events * @ingroup sequencer * * Create, modify, query and destroy 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 */ FLUID_SEQ_PITCHBEND, /**< Pitch bend message */ FLUID_SEQ_PITCHWHEELSENS, /**< Pitch wheel sensitivity set message @since 1.1.0 was misspelled 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_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.0 */ FLUID_SEQ_KEYPRESSURE, /**< Polyphonic aftertouch event @since 2.0.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_SCALE, /**< Sets a new time scale for the sequencer @since 2.2.0 */ FLUID_SEQ_LASTEVENT /**< @internal Defines the count of events enums @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ }; /* Event alloc/free */ /** @startlifecycle{Sequencer Event} */ FLUIDSYNTH_API fluid_event_t *new_fluid_event(void); FLUIDSYNTH_API void delete_fluid_event(fluid_event_t *evt); /** @endlifecycle */ /* 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, int 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, int 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, int val); FLUIDSYNTH_API void fluid_event_modulation(fluid_event_t *evt, int channel, int val); FLUIDSYNTH_API void fluid_event_sustain(fluid_event_t *evt, int channel, int val); FLUIDSYNTH_API void fluid_event_pan(fluid_event_t *evt, int channel, int val); FLUIDSYNTH_API void fluid_event_volume(fluid_event_t *evt, int channel, int val); FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t *evt, int channel, int val); FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t *evt, int channel, int val); FLUIDSYNTH_API void fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, int val); FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t *evt, int channel, int val); FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t *evt); /* Only when unregistering clients */ FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t *evt); FLUIDSYNTH_API void fluid_event_scale(fluid_event_t *evt, double new_scale); /* 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 int fluid_event_get_value(fluid_event_t *evt); FLUIDSYNTH_API int 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 double fluid_event_get_scale(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-2.2.5/include/fluidsynth/gen.h000066400000000000000000000135371417326347500204740ustar00rootroot00000000000000/* 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 /** * @defgroup generators SoundFont Generators * @ingroup soundfonts * * 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 */ /** * Initial Pitch * * @note This is not "standard" SoundFont generator, because it is not * mentioned in the list of generators in the SF2 specifications. * It is used by FluidSynth internally to compute the nominal pitch of * a note on note-on event. By nature it shouldn't be allowed to be modulated, * however the specification defines a default modulator having "Initial Pitch" * as destination (cf. SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch). * Thus it is impossible to cancel this default modulator, which would be required * to let the MIDI Pitch Wheel controller modulate a different generator. * In order to provide this flexibility, FluidSynth >= 2.1.0 uses a default modulator * "Pitch Wheel to Fine Tune", rather than Initial Pitch. The same "compromise" can * be found on the Audigy 2 ZS for instance. */ GEN_PITCH, GEN_CUSTOM_BALANCE, /**< Balance @note Not a real SoundFont generator */ /* non-standard generator for an additional custom high- or low-pass filter */ GEN_CUSTOM_FILTERFC, /**< Custom filter cutoff frequency */ GEN_CUSTOM_FILTERQ, /**< Custom filter Q */ GEN_LAST /**< @internal Value defines the count of generators (#fluid_gen_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ }; /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_GEN_H */ fluidsynth-2.2.5/include/fluidsynth/ladspa.h000066400000000000000000000054101417326347500211560ustar00rootroot00000000000000/* 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_LADSPA_H #define _FLUIDSYNTH_LADSPA_H #ifdef __cplusplus extern "C" { #endif /** * @defgroup ladspa Effect - LADSPA * @ingroup synth * * Functions for configuring the LADSPA effects unit * * This header defines useful functions for programmatically manipulating the ladspa * effects unit of the synth that can be retrieved via fluid_synth_get_ladspa_fx(). * * Using any of those functions requires fluidsynth to be compiled with LADSPA support. * Else all of those functions are useless dummies. * * @{ */ FLUIDSYNTH_API int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx); FLUIDSYNTH_API int fluid_ladspa_activate(fluid_ladspa_fx_t *fx); FLUIDSYNTH_API int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx); FLUIDSYNTH_API int fluid_ladspa_reset(fluid_ladspa_fx_t *fx); FLUIDSYNTH_API int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size); FLUIDSYNTH_API int fluid_ladspa_host_port_exists(fluid_ladspa_fx_t *fx, const char *name); FLUIDSYNTH_API int fluid_ladspa_add_buffer(fluid_ladspa_fx_t *fx, const char *name); FLUIDSYNTH_API int fluid_ladspa_buffer_exists(fluid_ladspa_fx_t *fx, const char *name); FLUIDSYNTH_API int fluid_ladspa_add_effect(fluid_ladspa_fx_t *fx, const char *effect_name, const char *lib_name, const char *plugin_name); FLUIDSYNTH_API int fluid_ladspa_effect_can_mix(fluid_ladspa_fx_t *fx, const char *name); FLUIDSYNTH_API int fluid_ladspa_effect_set_mix(fluid_ladspa_fx_t *fx, const char *name, int mix, float gain); FLUIDSYNTH_API int fluid_ladspa_effect_port_exists(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name); FLUIDSYNTH_API int fluid_ladspa_effect_set_control(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name, float val); FLUIDSYNTH_API int fluid_ladspa_effect_link(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name, const char *name); /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_LADSPA_H */ fluidsynth-2.2.5/include/fluidsynth/log.h000066400000000000000000000062021417326347500204730ustar00rootroot00000000000000/* 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 /** * @defgroup logging Logging * * 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 * * @note The logging configuration is global and not tied to a specific * synthesizer instance. That means that all synthesizer instances created in * the same process share the same logging configuration. * * @{ */ /** * 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 /**< @internal This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ }; /** * 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, const 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, const char *message, void *data); FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...) #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) __attribute__ ((format (printf, 2, 3))) #endif ; /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_LOG_H */ fluidsynth-2.2.5/include/fluidsynth/midi.h000066400000000000000000000273761417326347500206530ustar00rootroot00000000000000/* 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 /** * @defgroup midi_input MIDI Input * * MIDI Input Subsystem * * There are multiple ways to send MIDI events to the synthesizer. They can come * from MIDI files, from external MIDI sequencers or raw MIDI event sources, * can be modified via MIDI routers and also generated manually. * * The interface connecting all sources and sinks of MIDI events in libfluidsynth * is \ref handle_midi_event_func_t. * * @{ */ /** * Generic callback function for MIDI event handler. * * @param data User defined data pointer * @param event The MIDI event * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise * * This callback is used to pass MIDI events * - from \ref midi_player, \ref midi_router or \ref midi_driver * - to \ref midi_router via fluid_midi_router_handle_midi_event() * - or to \ref synth via fluid_synth_handle_midi_event(). * * Additionally, there is a translation layer to pass MIDI events to * a \ref sequencer via fluid_sequencer_add_midi_event_to_buffer(). */ typedef int (*handle_midi_event_func_t)(void *data, fluid_midi_event_t *event); /** * Generic callback function fired once by MIDI tick change. * * @param data User defined data pointer * @param tick The current (zero-based) tick, which triggered the callback * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise * * This callback is fired at a constant rate depending on the current BPM and PPQ. * e.g. for PPQ = 192 and BPM = 140 the callback is fired 192 * 140 times per minute (448/sec). * * It can be used to sync external elements with the beat, * or stop / loop the song on a given tick. * Ticks being BPM-dependent, you can manipulate values such as bars or beats, * without having to care about BPM. * * For example, this callback loops the song whenever it reaches the 5th bar : * * @code{.cpp} int handle_tick(void *data, int tick) { fluid_player_t *player = (fluid_player_t *)data; int ppq = 192; // From MIDI header int beatsPerBar = 4; // From the song's time signature int loopBar = 5; int loopTick = (loopBar - 1) * ppq * beatsPerBar; if (tick == loopTick) { return fluid_player_seek(player, 0); } return FLUID_OK; } * @endcode */ typedef int (*handle_midi_tick_func_t)(void *data, int tick); /* @} */ /** * @defgroup midi_events MIDI Events * @ingroup midi_input * * Functions to create, modify, query and delete MIDI events. * * These functions are intended to be used in MIDI routers and other filtering * and processing functions in the MIDI event path. If you want to simply * send MIDI messages to the synthesizer, you can use the more convenient * \ref midi_messages interface. * * @{ */ /** @startlifecycle{MIDI Event} */ FLUIDSYNTH_API fluid_midi_event_t *new_fluid_midi_event(void); FLUIDSYNTH_API void delete_fluid_midi_event(fluid_midi_event_t *event); /** @endlifecycle */ 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); FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic); FLUIDSYNTH_API int fluid_midi_event_get_text(fluid_midi_event_t *evt, void **data, int *size); FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic); FLUIDSYNTH_API int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, void **data, int *size); /* @} */ /** * @defgroup midi_router MIDI Router * @ingroup midi_input * * Rule based transformation and filtering of MIDI events. * * @{ */ /** * 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 /**< @internal Total count of rule types. This symbol is not part of the public API and ABI stability guarantee and may change at any time!*/ } fluid_midi_router_rule_type; /** @startlifecycle{MIDI Router} */ 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 void delete_fluid_midi_router(fluid_midi_router_t *handler); /** @endlifecycle */ 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); /** @startlifecycle{MIDI Router Rule} */ 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); /** @endlifecycle */ 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); /* @} */ /** * @defgroup midi_driver MIDI Driver * @ingroup midi_input * * Functions for managing MIDI drivers. * * The available MIDI drivers depend on your platform. See \ref settings_midi for all * available configuration options. * * To create a MIDI driver, you need to specify a source for the MIDI events to be * forwarded to via the \ref fluid_midi_event_t callback. Normally this will be * either a \ref midi_router via fluid_midi_router_handle_midi_event() or the synthesizer * via fluid_synth_handle_midi_event(). * * But you can also write your own handler function that preprocesses the events and * forwards them on to the router or synthesizer instead. * * @{ */ /** @startlifecycle{MIDI Driver} */ 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); /** @endlifecycle */ /* @} */ /** * @defgroup midi_player MIDI File Player * @ingroup midi_input * * Parse standard MIDI files and emit MIDI events. * * @{ */ /** * MIDI File 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_STOPPING, /**< Player is stopping, but hasn't finished yet (currently unused) */ FLUID_PLAYER_DONE /**< Player is finished playing */ }; /** * MIDI File Player tempo enum. * @since 2.2.0 */ enum fluid_player_set_tempo_type { FLUID_PLAYER_TEMPO_INTERNAL, /**< Use midi file tempo set in midi file (120 bpm by default). Multiplied by a factor */ FLUID_PLAYER_TEMPO_EXTERNAL_BPM, /**< Set player tempo in bpm, supersede midi file tempo */ FLUID_PLAYER_TEMPO_EXTERNAL_MIDI, /**< Set player tempo in us per quarter note, supersede midi file tempo */ FLUID_PLAYER_TEMPO_NBR /**< @internal Value defines the count of player tempo type (#fluid_player_set_tempo_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ }; /** @startlifecycle{MIDI File Player} */ FLUIDSYNTH_API fluid_player_t *new_fluid_player(fluid_synth_t *synth); FLUIDSYNTH_API void delete_fluid_player(fluid_player_t *player); /** @endlifecycle */ 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_tempo(fluid_player_t *player, int tempo_type, double tempo); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo); FLUID_DEPRECATED 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_set_tick_callback(fluid_player_t *player, handle_midi_tick_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); FLUIDSYNTH_API int fluid_player_seek(fluid_player_t *player, int ticks); /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_MIDI_H */ fluidsynth-2.2.5/include/fluidsynth/misc.h000066400000000000000000000035771417326347500206610ustar00rootroot00000000000000/* 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 /** * @defgroup misc Miscellaneous * * Miscellaneous utility functions and defines * * @{ */ /** * Value that indicates success, used by most libfluidsynth functions. * * @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 * * @since 1.1.0 */ #define FLUID_OK (0) /** * Value that indicates failure, used by most libfluidsynth functions. * * @note See #FLUID_OK for more details. * * @since 1.1.0 */ #define FLUID_FAILED (-1) FLUIDSYNTH_API int fluid_is_soundfont(const char *filename); FLUIDSYNTH_API int fluid_is_midifile(const char *filename); FLUIDSYNTH_API void fluid_free(void* ptr); /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_MISC_H */ fluidsynth-2.2.5/include/fluidsynth/mod.h000066400000000000000000000101201417326347500204630ustar00rootroot00000000000000/* 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 /** * @defgroup modulators SoundFont Modulators * @ingroup soundfonts * * SoundFont modulator functions and constants. * * @{ */ /** * 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) */ FLUID_MOD_SIN = 0x80, /**< Custom non-standard sinus mapping function */ }; /** * 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 */ }; /** @startlifecycle{Modulator} */ FLUIDSYNTH_API fluid_mod_t *new_fluid_mod(void); FLUIDSYNTH_API void delete_fluid_mod(fluid_mod_t *mod); /** @endlifecycle */ FLUIDSYNTH_API size_t fluid_mod_sizeof(void); 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(const fluid_mod_t *mod); FLUIDSYNTH_API int fluid_mod_get_flags1(const fluid_mod_t *mod); FLUIDSYNTH_API int fluid_mod_get_source2(const fluid_mod_t *mod); FLUIDSYNTH_API int fluid_mod_get_flags2(const fluid_mod_t *mod); FLUIDSYNTH_API int fluid_mod_get_dest(const fluid_mod_t *mod); FLUIDSYNTH_API double fluid_mod_get_amount(const fluid_mod_t *mod); FLUIDSYNTH_API int fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2); FLUIDSYNTH_API int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl); FLUIDSYNTH_API int fluid_mod_has_dest(const fluid_mod_t *mod, int gen); FLUIDSYNTH_API void fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src); /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_MOD_H */ fluidsynth-2.2.5/include/fluidsynth/seq.h000066400000000000000000000100361417326347500205020ustar00rootroot00000000000000/* 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 /** * @defgroup sequencer MIDI Sequencer * * MIDI event sequencer. * * The MIDI 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. * * @{ */ /** * 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 * * @note @p time may not be of the same tick value as the scheduled event! In fact, depending on * the sequencer's scale and the synth's sample-rate, @p time may be a few ticks too late. Although this * itself is inaudible, it is important to consider, * when you use this callback for enqueuing additional events over and over again with * fluid_sequencer_send_at(): If you enqueue new events with a relative tick value you might introduce * a timing error, which causes your sequence to sound e.g. slower than it's supposed to be. If this is * your use-case, make sure to enqueue events with an absolute tick value. */ typedef void (*fluid_event_callback_t)(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data); /** @startlifecycle{MIDI Sequencer} */ FLUID_DEPRECATED 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); /** @endlifecycle */ 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, fluid_seq_id_t id); FLUIDSYNTH_API int fluid_sequencer_client_is_dest(fluid_sequencer_t *seq, fluid_seq_id_t 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); /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SEQ_H */ fluidsynth-2.2.5/include/fluidsynth/seqbind.h000066400000000000000000000024121417326347500213360ustar00rootroot00000000000000/* 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 /** * @addtogroup sequencer * * @{ */ 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-2.2.5/include/fluidsynth/settings.h000066400000000000000000000146201417326347500215550ustar00rootroot00000000000000/* 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 /** * @defgroup settings Settings * * 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 #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 */ }; /** @startlifecycle{Settings} */ FLUIDSYNTH_API fluid_settings_t *new_fluid_settings(void); FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t *settings); /** @endlifecycle */ 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, int *val); 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 int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def); 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 int fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val); FLUIDSYNTH_API int 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, int *val); FLUIDSYNTH_API int 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, const char *name, const 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, const 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-2.2.5/include/fluidsynth/sfont.h000066400000000000000000000337511417326347500210540ustar00rootroot00000000000000/* 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 /** * @defgroup soundfonts SoundFonts * * SoundFont related functions * * This part of the API contains functions, defines and types that are mostly * only used by internal or custom SoundFont loaders or client code that * modifies loaded presets, SoundFonts or voices directly. */ /** * @defgroup soundfont_loader SoundFont Loader * @ingroup soundfonts * * Create custom SoundFont loaders * * It is possible to add new SoundFont loaders to the * synthesizer. 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 instance created by new_fluid_sfloader(). * On creation, you must specify a callback function \p load * that will be called for every file attempting to load it and * if successful returns a #fluid_sfont_t instance, 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 instance. * * The #fluid_preset_t instance contains some functions to obtain * information from the preset (name, bank, number). The most * important callback is the noteon function. The noteon function * is called by fluidsynth internally and * 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 instance 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 */ FLUID_PRESET_PIN, /**< Request to pin preset samples to cache */ FLUID_PRESET_UNPIN /**< Request to unpin preset samples from cache */ }; /** * Indicates the type of a sample used by the _fluid_sample_t::sampletype field. * * This enum corresponds to the \c SFSampleLink enum in the SoundFont spec. * One \c flag may be bit-wise OR-ed with one \c value. */ enum fluid_sample_type { FLUID_SAMPLETYPE_MONO = 0x1, /**< Value used for mono samples */ FLUID_SAMPLETYPE_RIGHT = 0x2, /**< Value used for right samples of a stereo pair */ FLUID_SAMPLETYPE_LEFT = 0x4, /**< Value used for left samples of a stereo pair */ FLUID_SAMPLETYPE_LINKED = 0x8, /**< Value used for linked sample, which is currently not supported */ FLUID_SAMPLETYPE_OGG_VORBIS = 0x10, /**< Flag used for Ogg Vorbis compressed samples (non-standard compliant extension) as found in the program "sftools" developed by Werner Schweer from MuseScore @since 1.1.7 */ FLUID_SAMPLETYPE_ROM = 0x8000 /**< Flag that indicates ROM samples, causing the sample to be ignored */ }; /** * 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 occurred. */ typedef fluid_sfont_t *(*fluid_sfloader_load_t)(fluid_sfloader_t *loader, const char *filename); /** * The free method should free the memory allocated for a fluid_sfloader_t instance in * addition to any private data. * * @param loader SoundFont loader * * Any custom user provided cleanup function must ultimately call * delete_fluid_sfloader() to ensure proper cleanup of the #fluid_sfloader_t struct. If no private data * needs to be freed, setting this to delete_fluid_sfloader() is sufficient. * */ typedef void (*fluid_sfloader_free_t)(fluid_sfloader_t *loader); /** @startlifecycle{SoundFont Loader} */ FLUIDSYNTH_API fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free); FLUIDSYNTH_API void delete_fluid_sfloader(fluid_sfloader_t *loader); FLUIDSYNTH_API fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings); /** @endlifecycle */ /** * Opens the file or memory indicated by \c filename in binary read mode. * * @return returns a file handle on success, NULL otherwise * * \c filename matches the string provided during the fluid_synth_sfload() call. */ typedef void *(* fluid_sfloader_callback_open_t)(const char *filename); /** * Reads \c count bytes to the specified buffer \c buf. * * @return returns #FLUID_OK if exactly \c count bytes were successfully read, else returns #FLUID_FAILED and leaves \a buf unmodified. */ typedef int (* fluid_sfloader_callback_read_t)(void *buf, fluid_long_long_t count, void *handle); /** * Same purpose and behaviour as fseek. * * @param origin either \c SEEK_SET, \c SEEK_CUR or \c SEEK_END * @return returns #FLUID_OK if the seek was successfully performed while not seeking beyond a buffer or file, #FLUID_FAILED otherwise */ typedef int (* fluid_sfloader_callback_seek_t)(void *handle, fluid_long_long_t offset, int origin); /** * Closes the handle returned by #fluid_sfloader_callback_open_t and frees used resources. * * @return returns #FLUID_OK on success, #FLUID_FAILED on error */ typedef int (* fluid_sfloader_callback_close_t)(void *handle); /** @return returns current file offset or #FLUID_FAILED on error */ typedef fluid_long_long_t (* fluid_sfloader_callback_tell_t)(void *handle); FLUIDSYNTH_API int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, fluid_sfloader_callback_open_t open, fluid_sfloader_callback_read_t read, fluid_sfloader_callback_seek_t seek, fluid_sfloader_callback_tell_t tell, fluid_sfloader_callback_close_t close); FLUIDSYNTH_API int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data); FLUIDSYNTH_API void *fluid_sfloader_get_data(fluid_sfloader_t *loader); /** * Method to return the name of a virtual SoundFont. * * @param sfont Virtual SoundFont * @return The name of the virtual SoundFont. */ typedef const char *(*fluid_sfont_get_name_t)(fluid_sfont_t *sfont); /** * Get a virtual SoundFont preset by bank and program numbers. * * @param sfont Virtual SoundFont * @param bank MIDI bank number (0-16383) * @param prenum MIDI preset number (0-127) * @return Should return an allocated virtual preset or NULL if it could not * be found. */ typedef fluid_preset_t *(*fluid_sfont_get_preset_t)(fluid_sfont_t *sfont, int bank, int prenum); /** * Start virtual SoundFont preset iteration method. * * @param sfont Virtual SoundFont * * Starts/re-starts virtual preset iteration in a SoundFont. */ typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont); /** * Virtual SoundFont preset iteration function. * * @param sfont Virtual SoundFont * @return NULL when no more presets are available, otherwise the a pointer to the current preset * * Returns preset information to the caller. The returned buffer is only valid until a subsequent * call to this function. */ typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont); /** * 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. * * Any custom user provided cleanup function must ultimately call * delete_fluid_sfont() to ensure proper cleanup of the #fluid_sfont_t struct. If no private data * needs to be freed, setting this to delete_fluid_sfont() is sufficient. */ typedef int (*fluid_sfont_free_t)(fluid_sfont_t *sfont); /** @startlifecycle{SoundFont} */ FLUIDSYNTH_API fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, fluid_sfont_get_preset_t get_preset, fluid_sfont_iteration_start_t iter_start, fluid_sfont_iteration_next_t iter_next, fluid_sfont_free_t free); FLUIDSYNTH_API int delete_fluid_sfont(fluid_sfont_t *sfont); /** @endlifecycle */ FLUIDSYNTH_API int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data); FLUIDSYNTH_API void *fluid_sfont_get_data(fluid_sfont_t *sfont); FLUIDSYNTH_API int fluid_sfont_get_id(fluid_sfont_t *sfont); FLUIDSYNTH_API const char *fluid_sfont_get_name(fluid_sfont_t *sfont); FLUIDSYNTH_API fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); FLUIDSYNTH_API void fluid_sfont_iteration_start(fluid_sfont_t *sfont); FLUIDSYNTH_API fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont); /** * 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). */ typedef const char *(*fluid_preset_get_name_t)(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 */ typedef int (*fluid_preset_get_banknum_t)(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 */ typedef int (*fluid_preset_get_num_t)(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. */ typedef int (*fluid_preset_noteon_t)(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); /** * Method to free a virtual SoundFont preset. * * @param preset Virtual SoundFont preset * @return Should return 0 * * Any custom user provided cleanup function must ultimately call * delete_fluid_preset() to ensure proper cleanup of the #fluid_preset_t struct. If no private data * needs to be freed, setting this to delete_fluid_preset() is sufficient. */ typedef void (*fluid_preset_free_t)(fluid_preset_t *preset); /** @startlifecycle{Preset} */ FLUIDSYNTH_API fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont, fluid_preset_get_name_t get_name, fluid_preset_get_banknum_t get_bank, fluid_preset_get_num_t get_num, fluid_preset_noteon_t noteon, fluid_preset_free_t free); FLUIDSYNTH_API void delete_fluid_preset(fluid_preset_t *preset); /** @endlifecycle */ FLUIDSYNTH_API int fluid_preset_set_data(fluid_preset_t *preset, void *data); FLUIDSYNTH_API void *fluid_preset_get_data(fluid_preset_t *preset); FLUIDSYNTH_API const char *fluid_preset_get_name(fluid_preset_t *preset); FLUIDSYNTH_API int fluid_preset_get_banknum(fluid_preset_t *preset); FLUIDSYNTH_API int fluid_preset_get_num(fluid_preset_t *preset); FLUIDSYNTH_API fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset); /** @startlifecycle{Sample} */ FLUIDSYNTH_API fluid_sample_t *new_fluid_sample(void); FLUIDSYNTH_API void delete_fluid_sample(fluid_sample_t *sample); /** @endlifecycle */ FLUIDSYNTH_API size_t fluid_sample_sizeof(void); 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, char *data24, unsigned int nbframes, unsigned int sample_rate, short copy_data); FLUIDSYNTH_API int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end); FLUIDSYNTH_API int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune); /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SFONT_H */ fluidsynth-2.2.5/include/fluidsynth/shell.h000066400000000000000000000110021417326347500210130ustar00rootroot00000000000000/* 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 /** * @defgroup command_interface Command Interface * * Control and configuration interface * * The command interface allows you to send textual commands to * the synthesizer, to parse a command file, or to read commands * from the stdin or other input streams (like a TCP socket). * * For a full list of available commands, type \c help in the * \ref command_shell or send the same command via a command handler. * Further documentation can be found at * https://github.com/FluidSynth/fluidsynth/wiki/UserManual#shell-commands * * @{ */ 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); /* @} */ /** * @defgroup command_handler Command Handler * @ingroup command_interface * @brief Handles text commands and reading of configuration files * * @{ */ /** @startlifecycle{Command Handler} */ FLUIDSYNTH_API fluid_cmd_handler_t *new_fluid_cmd_handler(fluid_synth_t *synth, fluid_midi_router_t *router); FLUIDSYNTH_API fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t *settings, fluid_synth_t *synth, fluid_midi_router_t *router, fluid_player_t *player); FLUIDSYNTH_API void delete_fluid_cmd_handler(fluid_cmd_handler_t *handler); /** @endlifecycle */ FLUIDSYNTH_API void fluid_cmd_handler_set_synth(fluid_cmd_handler_t *handler, fluid_synth_t *synth); 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); /* @} */ /** * @defgroup command_shell Command Shell * @ingroup command_interface * * Interactive shell to control and configure a synthesizer instance. * * If you need a platform independent way to get the standard input * and output streams, use fluid_get_stdin() and fluid_get_stdout(). * * For a full list of available commands, type \c help in the shell. * * @{ */ /** @startlifecycle{Command 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 fluid_usershell(fluid_settings_t *settings, fluid_cmd_handler_t *handler); FLUIDSYNTH_API void delete_fluid_shell(fluid_shell_t *shell); /** @endlifecycle */ /* @} */ /** * @defgroup command_server Command Server * @ingroup command_interface * * TCP socket server for a command handler. * * The socket server will open the TCP port set by \ref settings_shell_port * (default 9800) and starts a new thread and \ref command_handler for each * incoming connection. * * @note The server is only available if libfluidsynth has been compiled * with network support (enable-network). Without network support, all related * functions will return FLUID_FAILED or NULL. * * @{ */ /** @startlifecycle{Command Server} */ FLUIDSYNTH_API fluid_server_t *new_fluid_server(fluid_settings_t *settings, fluid_synth_t *synth, fluid_midi_router_t *router); FLUIDSYNTH_API fluid_server_t *new_fluid_server2(fluid_settings_t *settings, fluid_synth_t *synth, fluid_midi_router_t *router, fluid_player_t *player); FLUIDSYNTH_API void delete_fluid_server(fluid_server_t *server); FLUIDSYNTH_API int fluid_server_join(fluid_server_t *server); /** @endlifecycle */ /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SHELL_H */ fluidsynth-2.2.5/include/fluidsynth/synth.h000066400000000000000000000573131417326347500210700ustar00rootroot00000000000000/* 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 /** * @defgroup synth Synthesizer * * SoundFont synthesizer * * You create a new synthesizer with new_fluid_synth() and you destroy * it with delete_fluid_synth(). Use the fluid_settings_t 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 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(), ... * * @{ */ /** @startlifecycle{Synthesizer} */ FLUIDSYNTH_API fluid_synth_t *new_fluid_synth(fluid_settings_t *settings); FLUIDSYNTH_API void delete_fluid_synth(fluid_synth_t *synth); /** @endlifecycle */ FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth); FLUID_DEPRECATED FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth); /* @} */ /** * @defgroup midi_messages MIDI Channel Messages * @ingroup synth * * The MIDI channel message functions are mostly directly named after their * counterpart MIDI messages. They are a high-level interface to controlling * the synthesizer, playing notes and changing note and channel parameters. * * @{ */ 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_key_pressure(fluid_synth_t *synth, int chan, int key, int val); FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank); FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id); FLUIDSYNTH_API int fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id, int bank_num, int preset_num); FLUIDSYNTH_API int fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan, const char *sfont_name, int bank_num, int preset_num); FLUIDSYNTH_API int fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id, int *bank_num, int *preset_num); FLUIDSYNTH_API int fluid_synth_unset_program(fluid_synth_t *synth, int chan); 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); FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t *synth, int chan, int param, float value); FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param); /* @} MIDI Channel Messages */ /** * @defgroup voice_control Synthesis Voice Control * @ingroup synth * * Low-level access to synthesis voices. * * @{ */ 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); 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); /* @} Voice Control */ /** * @defgroup soundfont_management SoundFont Management * @ingroup synth * * Functions to load and unload SoundFonts. * * @{ */ 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, int id); FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets); FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont); FLUIDSYNTH_API int 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, 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); /* @} Soundfont Management */ /** * @defgroup reverb_effect Effect - Reverb * @ingroup synth * * Functions for configuring the built-in reverb effect * * @{ */ FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t *synth, int on); FLUIDSYNTH_API int fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, double damping, double width, double level); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level); FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t *synth); FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t *synth); FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t *synth); FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t *synth); FLUIDSYNTH_API int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double roomsize); FLUIDSYNTH_API int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group, double damping); FLUIDSYNTH_API int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group, double width); FLUIDSYNTH_API int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group, double level); FLUIDSYNTH_API int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double *roomsize); FLUIDSYNTH_API int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group, double *damping); FLUIDSYNTH_API int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group, double *width); FLUIDSYNTH_API int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group, double *level); /* @} Reverb */ /** * @defgroup chorus_effect Effect - Chorus * @ingroup synth * * Functions for configuring the built-in chorus effect * * @{ */ /** * 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 */ }; FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t *synth, int on); FLUIDSYNTH_API int fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, double speed, double depth_ms, int type); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t *synth); FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t *synth); FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_speed(fluid_synth_t *synth); FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_depth(fluid_synth_t *synth); FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t *synth); /* see fluid_chorus_mod */ FLUIDSYNTH_API int fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr); FLUIDSYNTH_API int fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level); FLUIDSYNTH_API int fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed); FLUIDSYNTH_API int fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms); FLUIDSYNTH_API int fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type); FLUIDSYNTH_API int fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr); FLUIDSYNTH_API int fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level); FLUIDSYNTH_API int fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed); FLUIDSYNTH_API int fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms); FLUIDSYNTH_API int fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type); /* @} Chorus */ /** * @defgroup synthesis_params Synthesis Parameters * @ingroup synth * * Functions to control and query synthesis parameters like gain and * polyphony count. * * @{ */ 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); FLUIDSYNTH_API int fluid_synth_count_effects_groups(fluid_synth_t *synth); FLUID_DEPRECATED 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 */ FLUID_INTERP_DEFAULT = FLUID_INTERP_4THORDER, /**< Default interpolation method */ FLUID_INTERP_HIGHEST = FLUID_INTERP_7THORDER, /**< Highest interpolation method */ }; /** * Enum used with fluid_synth_add_default_mod() to specify how to handle duplicate modulators. */ enum fluid_synth_add_mod { FLUID_SYNTH_OVERWRITE, /**< Overwrite any existing matching modulator */ FLUID_SYNTH_ADD, /**< Sum up modulator amounts */ }; FLUIDSYNTH_API int fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode); FLUIDSYNTH_API int fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod); /* @} Synthesis Parameters */ /** * @defgroup tuning MIDI Tuning * @ingroup synth * * The functions in this section implement the MIDI Tuning Standard interface. * * @{ */ 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 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 int fluid_synth_activate_tuning(fluid_synth_t *synth, int chan, int bank, int prog, int apply); 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); /* @} MIDI Tuning */ /** * @defgroup audio_rendering Audio Rendering * @ingroup synth * * The functions in this section can be used to render audio directly to * memory buffers. They are used internally by the \ref audio_driver and \ref file_renderer, * but can also be used manually for custom processing of the rendered audio. * * @note Please note that all following functions block during rendering. If your goal is to * render real-time audio, ensure that you call these functions from a high-priority * thread with little to no other duties other than calling the rendering functions. * * @{ */ 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); FLUID_DEPRECATED 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 nfx, float *fx[], int nout, float *out[]); /* @} Audio Rendering */ /** * @defgroup iir_filter Effect - IIR Filter * @ingroup synth * * Functions for configuring the built-in IIR filter effect * * @{ */ /** * Specifies the type of filter to use for the custom IIR filter */ enum fluid_iir_filter_type { FLUID_IIR_DISABLED = 0, /**< Custom IIR filter is not operating */ FLUID_IIR_LOWPASS, /**< Custom IIR filter is operating as low-pass filter */ FLUID_IIR_HIGHPASS, /**< Custom IIR filter is operating as high-pass filter */ FLUID_IIR_LAST /**< @internal Value defines the count of filter types (#fluid_iir_filter_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ }; /** * Specifies optional settings to use for the custom IIR filter. Can be bitwise ORed. */ enum fluid_iir_filter_flags { FLUID_IIR_Q_LINEAR = 1 << 0, /**< The Soundfont spec requires the filter Q to be interpreted in dB. If this flag is set the filter Q is instead assumed to be in a linear range */ FLUID_IIR_Q_ZERO_OFF = 1 << 1, /**< If this flag the filter is switched off if Q == 0 (prior to any transformation) */ FLUID_IIR_NO_GAIN_AMP = 1 << 2 /**< The Soundfont spec requires to correct the gain of the filter depending on the filter's Q. If this flag is set the filter gain will not be corrected. */ }; FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t *, int type, int flags); /* @} IIR Filter */ /** * @defgroup channel_setup MIDI Channel Setup * @ingroup synth * * The functions in this section provide interfaces to change the channel type * and to configure basic channels, legato and portamento setups. * * @{ */ /** @name Channel Type * @{ */ /** * 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); /** @} Channel Type */ /** @name Basic Channel Mode * @{ */ /** * Channel mode bits OR-ed together so that it matches with the midi spec: poly omnion (0), mono omnion (1), poly omnioff (2), mono omnioff (3) */ enum fluid_channel_mode_flags { FLUID_CHANNEL_POLY_OFF = 0x01, /**< if flag is set, the basic channel is in mono on state, if not set poly is on */ FLUID_CHANNEL_OMNI_OFF = 0x02, /**< if flag is set, the basic channel is in omni off state, if not set omni is on */ }; /** * Indicates the mode a basic channel is set to */ enum fluid_basic_channel_modes { FLUID_CHANNEL_MODE_MASK = (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< Mask Poly and Omni bits of #fluid_channel_mode_flags, usually only used internally */ FLUID_CHANNEL_MODE_OMNION_POLY = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 0 */ FLUID_CHANNEL_MODE_OMNION_MONO = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 1 */ FLUID_CHANNEL_MODE_OMNIOFF_POLY = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 2 */ FLUID_CHANNEL_MODE_OMNIOFF_MONO = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 3 */ FLUID_CHANNEL_MODE_LAST /**< @internal Value defines the count of basic channel modes (#fluid_basic_channel_modes) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ }; FLUIDSYNTH_API int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan); FLUIDSYNTH_API int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan, int *basic_chan_out, int *mode_chan_out, int *basic_val_out); FLUIDSYNTH_API int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val); /** @} Basic Channel Mode */ /** @name Legato Mode * @{ */ /** * Indicates the legato mode a channel is set to * n1,n2,n3,.. is a legato passage. n1 is the first note, and n2,n3,n4 are played legato with previous note. */ enum fluid_channel_legato_mode { FLUID_CHANNEL_LEGATO_MODE_RETRIGGER, /**< Mode 0 - Release previous note, start a new note */ FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER, /**< Mode 1 - On contiguous notes retrigger in attack section using current value, shape attack using current dynamic and make use of previous voices if any */ FLUID_CHANNEL_LEGATO_MODE_LAST /**< @internal Value defines the count of legato modes (#fluid_channel_legato_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ }; FLUIDSYNTH_API int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode); FLUIDSYNTH_API int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode); /** @} Legato Mode */ /** @name Portamento Mode * @{ */ /** * Indicates the portamento mode a channel is set to */ enum fluid_channel_portamento_mode { FLUID_CHANNEL_PORTAMENTO_MODE_EACH_NOTE, /**< Mode 0 - Portamento on each note (staccato or legato) */ FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY, /**< Mode 1 - Portamento only on legato note */ FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY, /**< Mode 2 - Portamento only on staccato note */ FLUID_CHANNEL_PORTAMENTO_MODE_LAST /**< @internal Value defines the count of portamento modes @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ }; FLUIDSYNTH_API int fluid_synth_set_portamento_mode(fluid_synth_t *synth, int chan, int portamentomode); FLUIDSYNTH_API int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan, int *portamentomode); /** @} Portamento Mode */ /**@name Breath Mode * @{ */ /** * Indicates the breath mode a channel is set to */ enum fluid_channel_breath_flags { FLUID_CHANNEL_BREATH_POLY = 0x10, /**< when channel is poly, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath to initial attenuation modulator */ FLUID_CHANNEL_BREATH_MONO = 0x20, /**< when channel is mono, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath modulator */ FLUID_CHANNEL_BREATH_SYNC = 0x40, /**< when channel is mono, this flag indicates that the breath controller(MSB)triggers noteon/noteoff on the running note */ }; FLUIDSYNTH_API int fluid_synth_set_breath_mode(fluid_synth_t *synth, int chan, int breathmode); FLUIDSYNTH_API int fluid_synth_get_breath_mode(fluid_synth_t *synth, int chan, int *breathmode); /** @} Breath Mode */ /* @} MIDI Channel Setup */ /** @ingroup settings */ FLUIDSYNTH_API fluid_settings_t *fluid_synth_get_settings(fluid_synth_t *synth); /** @ingroup soundfont_loader */ FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader); /** @ingroup soundfont_loader */ FLUIDSYNTH_API fluid_preset_t *fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan); /** @ingroup midi_input */ FLUIDSYNTH_API int fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event); /** @ingroup soundfonts */ FLUIDSYNTH_API int fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num); /** @ingroup soundfonts */ FLUIDSYNTH_API int fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num); /** @ingroup ladspa */ FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SYNTH_H */ fluidsynth-2.2.5/include/fluidsynth/types.h000066400000000000000000000100031417326347500210500ustar00rootroot00000000000000/* 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 /** * @defgroup Types Types * @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_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_hash_t; /**< Command handler hash table */ 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 struct _fluid_cmd_handler_t fluid_cmd_handler_t; /**< Shell Command Handler */ typedef struct _fluid_ladspa_fx_t fluid_ladspa_fx_t; /**< LADSPA effects instance */ typedef struct _fluid_file_callbacks_t fluid_file_callbacks_t; /**< Callback struct to perform custom file loading of soundfonts */ 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() */ #if defined(_MSC_VER) && (_MSC_VER < 1800) typedef __int64 fluid_long_long_t; // even on 32bit windows #else /** * A typedef for C99's type long long, which is at least 64-bit wide, as guaranteed by the C99. * @p __int64 will be used as replacement for VisualStudio 2010 and older. */ typedef long long fluid_long_long_t; #endif /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_TYPES_H */ fluidsynth-2.2.5/include/fluidsynth/version.h.in000066400000000000000000000031531417326347500220060ustar00rootroot00000000000000/* 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 /** * @addtogroup misc * * @{ */ #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-2.2.5/include/fluidsynth/voice.h000066400000000000000000000057111417326347500210230ustar00rootroot00000000000000/* 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 /** * @defgroup voices Voice Manipulation * @ingroup soundfonts * * Synthesis voice manipulation functions. * * The interface to the synthesizer's voices. * Examples on using them can be found in the source code of the default SoundFont * loader (fluid_defsfont.c). * * Most of these functions should only be called from within synthesis context, * such as the SoundFont loader's noteon method. * * @{ */ /** * 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 float fluid_voice_gen_get(fluid_voice_t *voice, int gen); FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t *voice, int gen, float val); 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_get_channel(const fluid_voice_t *voice); FLUIDSYNTH_API int fluid_voice_get_key(const fluid_voice_t *voice); FLUIDSYNTH_API int fluid_voice_get_actual_key(const fluid_voice_t *voice); FLUIDSYNTH_API int fluid_voice_get_velocity(const fluid_voice_t *voice); FLUIDSYNTH_API int fluid_voice_get_actual_velocity(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_optimize_sample(fluid_sample_t *s); FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t *voice, int gen); /* @} */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_VOICE_H */ fluidsynth-2.2.5/sf2/000077500000000000000000000000001417326347500144175ustar00rootroot00000000000000fluidsynth-2.2.5/sf2/COPYRIGHT.txt000066400000000000000000000045771417326347500165450ustar00rootroot00000000000000 Vintage Dreams Waves v 2.0. for Creative Labs' AWE Soundcards (EMU Soundfont 2 Format) Copyright (c) Ian Wilson, 1996 (Updated January 1998) This soundfont is freeware. You may freely use and/or redistribute it subject to the following terms: 1. It is not altered, edited, modified, ripped, or converted to other formats, except for private use only. 2. It is distributed with this copyright notice. This soundfont is distributed WITHOUT WARRANTY, and without the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. No liability or damages can be inferred upon the said copyright owner, Ian Wilson. Any feedback, contact Ian Wilson. vintagedreamworks@hotmail.com http://www.geocities.com/SiliconValley/Campus/8645/index.html http://members.nbci.com/silicon39/ http://www.mp3.com/silicon39 https://analoguesque.x10host.com/ Update 2020-10-31: Ian Wilson has granted FluidSynth explicit permission to convert Vintage Dreams Waves to SF3: On Sat, Oct 31, 2020 Tom M. wrote: Hello Ian, I'm the current maintainer of the open-source SoundFont synthesizer FluidSynth. We are currently using your VintageDreamsWaves-v2.sf2 for unit testing, as we found it small and useful. We would like to convert this SoundFont to SoundFont3 (an unofficial extension to SF2 which stores the individual samples as OGG/Vorbis compressed stream, rather than PCM). However, your copyright notice doesn't allow your SoundFont to be converted to other formats: https://github.com/FluidSynth/fluidsynth/blob/master/sf2/COPYRIGHT.txt May I ask for your explicit permission to convert your font to SF3 and place it into our public Git repository? Apart from the lossy Vorbis compression, your SoundFont would not be modified further. No matter how you decide, thanks for your work! Regards Tom https://www.fluidsynth.org/ On Sat, Oct 31, 2020 Ian Wilson replied: Hi Tom, Great to hear from you, and thanks for reaching out regarding the SoundFont license. Your unofficial SoundFont 3 project sounds very interesting, and glad you could get use out of the Vintage Dreams Waves. You are welcome to convert it to the SoundFont 3 format. Please let me know how it all works out. Regards Ian fluidsynth-2.2.5/sf2/VintageDreamsWaves-v2.sf2000066400000000000000000011464201417326347500211270ustar00rootroot00000000000000RIFFsfbkLISTINFOifilINAMVintage Dreams Waves v 2.0isngEMU8000irom1MGMiverIPRDSBAWE32IENG Ian WilsonISFTSFEDT v1.00:SFEDT v1.10ICRDOct 24, 1996ICMTCreated by Ian Wilson Last Update - January 1998 eMail : aztec1@bellatlantic.net iwilson@butlerintl.com Downloaded from HammerSound http://www.pvv.org/~thammer/HammerSound/ICOP(Copyright (c) Ian Wilson - October 1996LISTsdtasmplv W\A.'@(Ѐƀ:~50*% w: - Q;c ǁJEYN"/}CHMLS|X]Kbfknruxz|X~[QA|-ٽlʇ>z94.a)L$mpcY i#< (3#=*H1R8\?fFoM{T[bg]SIąΌؓ WOHA:3,% ոήǤ{qipw~4* ك5>HT]gq{šɤЮ׸ )3#=*G1R8\?fFpMzT[bipw~ s$c*07s #&_-2.+-)&w$@" oխg΋F -kǿ-ƴW"Tm󁍂Յjͤ@PΖ{<ɿzT5 !,Y7-BLW\(3=)HR]zgq|}(~~u;::9:9%9v876543420N/-,>*]dUӮj=-=\v }vi\N:'!->O`/cba&aZB) P+:L ;ZzmXN.-ԣFג/͍}wun'kFeeh{@{DsehXI&5%Q_!X2SCNTcbSba`So;X# 4u !^] KDiK]UXppZHp9R9?hgpDߝ>ǎqjIA!~U4*:_sZV_qTjW7R99v*$j]$#ߘ?V!!)H[`VUhq`ye`98g9394LGfƔǺڎǤڢn>4 'BL] VZqo|YG9v9EB%[*`?>ڠJIe$tXErf3B;,#H==!M wnƶm ~m*((2DF'B=U@IKHj9)#5%_.u}UMѓз W+.:7EV95$ b06sPaPͿ˂ m%+.:Y.V(-A(I vZLgwKז4./:DA') ~ W% Q)(c@ #W8>.lϰ+5"l IZߩULO%3 (L8q;s3( %,G4 $8 "%ߺֵ {gJ?k*).x oaH(QC: c_ 01! ҈\-!!"$"$; I 4q$a6+Gε5/$ ! +3h6R1,[6ͭ)s:)P>0 0 #I9׽X3!G r29y߲p `C2nffc', D>@ ;9ca ABq C8| {գ +c *b+\2qN>U:3(uCsBL͡+|g rq.eF! $VrF"#!7?\30IV]>< L #$S t#uw;3) B C|^&56*Is8I  K t p$46,#"-@ =#  -e \Q3v-10h x%&tz &1AǐР+#@ 3!a");,1/%" r(/ J x{ NZV9 y|S  */^Q$:_ p+,QF ½׫VV4%`)iQg9\1C Td蒿 hYy \5THe,$Ci* FrIYm0/0< &@E BA֙ 0 1o{&s'2<`G-T n  ] i# "K6D>1jbqK.i u"B! f r ?ѹYU JZz1$=-W EY 0c r A#q&x63+gÔݘTP 53[ND 5|2+q\/61 *1$';O`1/jYʇ-Ōߕ  Ӷ=¥~&',xi*3 YάlӌN!U0-&'9G!-k?s?亚l]R -<JLG7 (Z9F>"s mEgJy6^m=8;)'y5'").  A'*/98$HL mѐ7,D7~c" 137WK|7ſ#l \!;G86{09 i [ ߊ,~(B3)q >1<$F (vZN /НV1Cb߲b,31) @9 FM%{ XR&_*VAb;ľ/ :BA5HR/w",$_):! A/N  S3 ظsH|ےƒe?ap*E$#NBvdX *D+z"4EB}>6!'<CbJa6=•5ϸ#= 2#,P! M&dAĝL=[>$T a) j Z,5!SV(HNVY$ OKA(kT <͓|x_Jz%1%][-)28,3wTըM[ӴGϭdzs &6c7 (&. 3*N  IҿNj4] C6w Pj?GYQ?4,#7KDzݨGrz &+/7<61g8:(\%"Y8Վ V0.N/ e%D{շU)n'M%zX25aS)ڷ)3ڢA(8 $"˱C 0r'~+0 % \&!$-$#l qٸ;)$MAkSDBT\X)N 2O_P Ml fk>Aޠ 'JWjw˞&{ uZ*}*j K U (՝)%d-J  >UA! T/Ds(g߅y[JJ;2hNRC5 '61L,4+zmޗjrQ ^< o3HJ6}+&%1v\ $"7t;0 yb/ Fj}~/_7B).AmHk2h  ?~x!1 }"P7, \  21MZ84bߋq@ -C~L1 :. x 9Q`0R=qFG*7+" sNK>{׹9dTdmZ1NPK:-k 4?U“ܞ#5d*SG) ].~.^id ޙE׉ݩ#a #ɤ֨n:"&&1@E<7 RV!B"K:#ܫτxuZ|\& (->hMP"< &U,l F.S4"Uߪ;C^CAI}N&.z"# ^8;+ "8~- )24#pu) # UD_=>U ([lwըDo5ܾ"M r T)V!'2.?#7y 0qݸ[/":H. ֙E P%9Վ'<ϢM%/>+) X[fhKϯԽil(G?"5&' :b'$JD^Iz8ҡT 6:#F-i3[H1L3,1DD*.'H~8xTVA "k LQ*2.$F;FWN7!n乨mcsε+0 'n &BW9] /R@83q "~<Ԯ ?fI&[1!A;N<2d|YԺn1*UжgՒi. z y5O9c& sC"-()$--A!)XP8 өWE-4-(+^>kT _]+ZPEIL/tЦɨD9EϻRѠ" a p  "n'IZ!E9@~U *F.δ % 1C!v?e9G #p$.C qkA"v@0C' ';=BAM[Jg3(  ɸПPϨp޸2"/P83#o =2D4E>/?K )_mWOIϿҖל;&:E,ֽ(,3G-,p5V]>, AbW{Ed{S[%wUNύ _j!i}K0^ ʑ,eCF؜Q4&O&/21,/D=7 hS } rj*T x#[. J  " H4 5~8 /51X5Y  \4:C"2(!:T;I7z S! >0=B  1l$J 5%T5My5?0 y eO(-# U`wп"ӝ6wJ)#4E/9 0++σ7L (f*(0aA6C=J9/&_a'5HJ tP v;<,ЀG O , i,G8=e:# 6Plʹ#%' _-@?UKV\7L 1_UT֞#!a~$|e>+_@s]3u%K02+KnPݼք–x xH]ѾQL:M};/Y"R$1 d7=t~f,fW ہ/?!n2Ė&8qZ6OGMM-=4!p45+JY:||!q9|3 q i) ]Q=$wڵ.?C7 Y/`$ 1 Q"C#<{]լ?!W,#Zv )ۓ:a5]NZa* ;u#(?۔y xL7?/ݑ, xY /*d uQ / Q TB]T}^F5%L`DW l(N!Nt.M0.)_b K..J F]-P#tLdԽK3"' 0*gi3' ,+ͩF-l0-K4 $)`!~!-;M;sEY#X1q 8Z ] E0F2 Pk;ݧM60%f {^M-kM6[S "OJE]WJ'˥tSU} IB踩! #y; =C'6=3"aA*ͭ"#P.]58s;FDiVC8Wf@5 L (G ('Z(s(=.^8~5xwl1E1 V #!*.,7  fo"`f &YqG];H_;g kTъ1=T6/B Li!S|!{?=l Ix^ 5F2N_.ZiH?!Ֆ:j%h 'g^cbo= h(+/iB?;JB^3O2?m˝_+Fh>*7v˫(/}ۥ"fy$8ׂ+!w4ԏv")v30%'/H {E)n8:\,̙PCDc */E +ʣ/9 NG36R  ':ȓ (ń26 I2J e&Nf";+9 (Z@(^"(`\$k8*|r?ئR3shӼ<t;1 q=-x$Y(kԣ!j_2"{'8n2 * @mR2Z7gi~&$WFP#S 4BX݋6˽-t_(k &00?_B  q (פ:UCyp2LU(;207Jv!~zo7ͯGe}x7S Ak2}r~q9&ƧiXW+ U ח" N%[h,4X3fY$" (*L8~$XG՟$L!.wg xP}#B27n" }ۀF%='h !,j% Ƀ7_4, z !| 3$;3&,7^Lk>&MU(7e86D=jF !KԀoB& 'x| )]$P)$p +c^L]7%›@5R)^g47.ϑ"o>L0zILd+:ח9c݉G W>$! * u&\& T*4&*)}(miA,]t $ 1TV'-aL. { .yT,Vߧl 1R!^) 68 N8%*-G3 hy߲a4 "U&)8=d-0%HdԡLR-': ! j qm1EZB%! _+' 0#L-)Mwe1GF{Q *o2 fD$}A8е$mׯi#V"@ZI:Vu&$z"1ſFwʬROR bu% /قB=$9҆׷672  9/ #F74Ĥ)>Ca<Juc@97G@ڬrBی e˽4e!F7#   E(q)ؘB81ޡ$@6T٢![9 d=E.Ft@!%1 i0C,X'NG-%T ߪ%YUp?r2#@?!(@ ٯ0)1F Z0{&,2e6D3bz' |c!\w>G$:&bX"[& eȎ]2t.0sJ)%_P2 6ל8.7w՛IAZy-EP,؞"yڽ@6QuT5?D+*F65 r&~} d& %d$ܕ4r՚$ܔ*@+/ԋ bu$N*hZ-U N'zX4]Z)K &CSiK;vBcC2S m*ѿBQ/8G&{=@.ԇµa+ .,:*MkJE'IYa"\f a/,EL BMɗ)߄N +xq! =Bf3Oۖ3fN*q/cڑq/Dy.9'9b]5Ln Ҵ,f: K P%S$`H/ Q-@"n,1d,9)ʴ[V:kϠ J߇2*Ԓ z z`( 1 T tO)#[O0G ݴ6+8x,M:ؒl ۉ?CȜYyf `cCF^`.4 F> Zڀ'֩&f x"G"Kt*3`'8h2Ȳ & 3 \XZܜ.%=+K \)7F&Ը-: i ,$ #]E (213 V6v_ڽP֌:B>NjRRG 7 og,+,ahVRYGqf!WrYM 4 :[6Cb6y49*+y 3#'X4NS,&9d*7~ȘY"e gXZ'P!?I Ɇ&B/R)o -,ac04b!G++W'!>)Ԛ b  .Ѷ7ȝC9!* `0ڟ` J6D+r=yj % fDř&c W6vΆ+Ά {6(YIƘbLGN2- 3-G[5ŝ/ը C"@$?~ Zb rp1̈́#QH($hաgC ' 36?7Z>W#}(R6VPQ'hMց@OvHν50\l",dw>| Bt7x ,|Rk"ز3BJ'y0  /1'n  we'q  P$8$%4\m!d,>9jO))9A }6T9EbY3%nke9,KVa Ys8Rϙ*T K&ݭ f߲+6 E'x!L~)%%HL1xR.V# D_'dRwh&U~w1=q,SE$;ۺ$)ASd o:^@ B.T X41^Ԝ$teZ}P\~@'w@@|# Z@Sh/η" `z"E :@N4+Uiik+̐w. SB(q zn.`!̨,>7Y0*Iڦ@ 92$j5Ah|)в2d%$ Ed` Oc24'u'V ,By)X{!>`t%h M0 S ;iGgĴ#6mmfdLJ߹ 'd@-;z'֥"suDEۿ2/rK%R09ZppD62P/^Y&.6PO(Bؿ*U>х[B> ԓ!Msc%?(lR15 U~t&а;ؿf?ZY!@;F@ *N|YE<&_`4'\ )mٰekX7%)H-98Đ,ד%~i8$CJ?9`?d_ Dvڽ940J( 5$ן,h |N I'@aù0U?`|np*ޙA Z2O%9:U,`F'Q,Gc*J>ھ:-I$YZw uKJ% %x00]*c(93_}#by=T=:/mn'b$c#<F:ЂP E$^| j X(F /0} kL)%Rty;Ý 'L.I&Hǻ5Cp K|q0/(FsWOh`b^xz̵%|l~R>y 5HtzE <C,ziu7〙UP ~mW("ϱكb \dziD؈`dgY6ӂ͏WӢ,0p/}KMQo;%wx?_#AI%|Rr0"bVj]!{ș҅{a_J1 >?M e F[  v #Сcq7TV!: :fwlIayrͦQ38@zUC'zm\3ƚ9W(6Iއ{) 0 O zAz7 _ݚ-$ˀ2 %f ".1LGF{'1qʶPAYDXP@G#/:5TڣJ#-I$sKLN;.T F7@2-ojtr2 |[tם˕I6@C>+9()N(-DnQ$x # jG 'GXBd/NA7mӞl t 1Mc/Y0q@/F$8+جPDe QXةa -3bDx>(&ug Nq.-!46> =f’Jt]D( -lIBGMt7(#F߷fƎ?b8\wr> 2γT16i)  [Q  31,$&7- 9=(? BUĎo.q` %GCy8eY %C" !p=s C֔<VG t@Gj/SZ-M.LuFr4D6j8$' L!1XԐۗSOLk2 :=,UILT*bi-a dơ"?yR*WkU+7WrCʷO 8@7?1<] 3QѴڣK/`pEc#j003v>,29ɋ}. /"CE[k2y̜YoB?jb^S7nHvq '(hIֺԒkP7vm܂ L& %qIެ+9u/!V) lj*3kr&GƼ!376@LROH8OۚIG߇ x n(~42a[a>4<iˈícNUo|Pt˼t!,RR7?3IEwP 9b<Ծo,NjLw T(4A>.;e}ةiP]g_[L)U%²l3XJ.. J}q22x1ƗԦ !# jK0BfBYpaZIP+ i[·Âs[3i%}dڗؾ̯(P0 ^jA *ISQI5"?x AL/7ۘD3y| ["0V>s; ;ZO8SЄ~1ME+[ @Z z+=;Nx'qנ2 #&2e2BrYW K7_@࡚*B6fr[*3Vйg=T 3X!R>}" ֘·2 /6?y v"Tљ"10 ?&7v-LJV3*#^vp%21&)ԃ4p f32$6f E<@F7+Qd:v3,uR:jOsp }w-`@#'*>B'iVli?!#L--?.? ExƩtӑ?n ]|U3 , .i bX$ &9%mQɷͺVGpx4(˸h54UfWY/WܤE:Oc_MN6c#ҋ Ѳ ~8D- oiWE>ئ%9t8B s+w1ՙ""l,3o8BK;<&*tʜ*r0}'01?$N_O-: uWn^ +"" f5HEHB7.?WﯽƴƧqlALq- DbfN# F۫yֽr0BK*6m5 v@pOF%ΐNf244m%g؈.-u j*'DӺZ>t D  x*D=-Y  Z9;')! w)WB3^5A/m32<2rZmP\Z}<hh+%t#D-iė0;>GT?p:KL$&K"  2C RhMҲO!W`R5[==ȀA|70(9e/gXߤ\)%W'Is1.H4Lt4MZ'x5}ȍ 0'(r%5}_$8@o>F)u_ZxH 3+h1QBHtJ&( - )}1(N} aFѶ 5q.`$ g7#7-fzxdi 'h {3_CAr8R;FL"YpAԭ : UvW וD^w!/1& I J.vDG/B GA3m0M)BgCPvr$1mJGf*~LQ2<ׅen%z4!!11HxŦa 5>&*Ց7= 3PSH-! *%oLExj Ӂ%'m4WGN#6Fe:V$ϔϮ*3oϔ 6 yۧR B~7k2n2  !!\򛘗W4 *APR;:B7ZR?ٰf+1C/"aJ@ '!iٱ%Ci.Vx.G*g?5 f& 7˭ZćRlAY3X!$?Mҕ NVcyiN+80dza>E?3B2/cU}7)r 9I>2L}/|)|@cY>ԅ]^ %fֹPH!5-0(& jnBFATT3̈́ƿˈTQTvzkID^U>4K7 V.B&"C^Co/QgL; bf, 5e\kw9Е F(0/4+ SJʸF2<9?CB%Qׅ?ߠ*QW D e2"O1bӛN4?^;z!r=l6$ư*$=qr=$٢a}+GfI,7HDFޠRU:$@-C"ѭ֯+C?A,X*=z;+.xS,Ӝ֢w ;fp,Ͼ{7 ^T$6:QZѸrG;$:#@=9>*k שղZ] =PS[F5E/d}jh+E:5gù]zV/ZH=;)-dD}j/:Af+ U7 Dz R A*f<eyhI @٪\o6C*1 .<8N9tJ_8d -5;`ȱ"DK:R/^̐ *OI[`VdEt^s 5>#$ኾǏ m06 VNA-m7!1.2q0d #>!33 swn8N.z-YCgl}<&ˏΈr+C#' F 3j*'ޖ|޲2_A^]JJ)}:17  $ 5$n$k ھ2rdVIZ7Д|s y BE5-x N. KFa.ƶŮҊ 2FLL1'P)< 7"^1K8(#כ'zb /C*8*؁ <.P;)/l %֠ 4QT`px]OÎBP1j46JM}F ;^6߃t["} <)x,=2ؚ*1ڒm~P.NIe4 餵^L)Jfuem;u OUѪΐ"HМd 'Rxdw_-,jj&07VReF4H1F'4J9(<ۧ);[r..6K7-I<%[6+*aE6; < Xըҝ1ٕ1HT F@ k 1dEr(.*/C<>&^cG[D 'NwOjӚ#KkZiD{k֥É~3:dM@ ^o)!$n(&^EK2Fi{ e7*!=FP2 ~aŒF)vabn1Jt"`/1,m c7TH` G#rӥ , E|ܕV/`rGP'  N⯀ݵH!r9 KKQK?F~7 \Y\2VpVX2uj`vQO! 7>.Iٳz0*6Ddvyx!'L4F@M tyo̸7 "* %Ӑ}"hi6@d'  䎾ݹhKK <$%!B&I!R /)19)w52 к}њ1zE9 EC-*yd{ ;48 ȖIW/v$&طFD@laWºZ2ZhG7 ݾ͵l ~/H>,յ   [".3N   3A#3P-"˹ :i^F  T zn T.&-#=3\S#?- 3Q w ) Y g 7!JDɑ+ijR:eP W!7 F"*1 0e67 x 0am ';9EI"฼Kmt߀K1S13@ǥ\ΖQZ"U1·kA΄{Uc =G@33'+ҏ"^ - >"qcӚ% +*\;a$6&(38-F47ƨ´ D88;5;<'ƽ@y3OJ!|qsKI M|T 3X 8^o)|  ! /9eͼ5V0kg?N خ+;GaK[;IU68~F=5KzӽXOfswN$XD-9E)4%GwLvp-2[f?%e($r%g R~~TH۵&TV9+}!/!+IɽB _CVGVAV% ށ'Rl\[rkƿ*. I ^(-@cމ_ k4D&4UřtQT )*'eIѫx! 1( {t"qV? ?R %V[ۊ| r!0/)$# ߎ5 BDL#.V ; #K<5> 칡ʿ?jBb9yօ~ '-% "% <6u$Ǜ~e<; 'eCmْ` CM@n;$|L:-y zh,M9Og=mϷp.X9+5: bp#*R(&Ni6( 牾lW\a'  - XemN'hUʱ:IFfF4 1U1.,/00a# fk/9F;'5 㹗+ bb- GN2KַB 104.۠^hn#!53t næ)#} z(+ic7u*-?YJ;.5& ĕF  q)+rѢ^ι)m+b/" ;ݭ vT,%>&x ކF[~8mke?ЮH3ӝ-j2Vc^B"Q:˰ <c_@;"tALb:LHo'|IW6E/c }şjےH0G'|f}у8%*} AObBAQO5VҚUq:3XjJH*#UcT -[2J"Y,%h{I³[G^xjΦEb&3t;T\Y'mw}>ۈ a&RyiX!G2_Z=~fk 3>tf2YwנrR }?:mVG/kF:23d+\VQV4괞ӓ L}pJ^ -d-rHvL,=1&#D-˜GkIJIq;P(:&ȶ } 3vH+),./ >_;QUU #32 ғɼ8F&OBG4DJnD? !%?0 6/=6/ O-<2:&(]%8$[?pmT pa -3+!V #x0{܂%+rATJrS:2:IEbю98 E> { &@U-nG:hyΟ˺ &+>Kfa4b ٻ#.1{mP'45_}iq:JiJF"剿 v[ -;W =,HO3 K 9ǺX U&2)/ yg;Z_6-[+u)'%#!    ,:K^s؋֥+TˁɱW–پ kgŴ'j[ܩbUaݚsH$ޒՑҐ׏0YEEF違HǀgB&&BgǀHFE膓EY0׏ҐՑޒ$HsݚaUbܩ[j'Ŵgk پWűǁT+ҥԋs^K:,    !#%'u)[+>-/0246O8:;=j?'ABDEFGI;KLsNPQ ST$VWYZ[N]^`Tabc#e[fghijl"m+n.o)pq rrstxu@vwwmxyyVzzu{{u||V}}~k~~~9mm9~~k~~}V}|u|{u{zVzyymxww@vxutsr rq)p.o+n"mljihg[f#ecbTa`^N][ZYW$VT SQPsNL;KIGEFDB'Aj?=;:O86420/>-[+u)'%#!   ~JG*.F078f55E1,$k}ӢŨ1`B$d7~4 Ic +3ƍ$c2! + L(˿7m-x^c27j\<0CW{>W&b4.U]%&..qk=һ@Ȕ?Ip)SoxscH'f5Ɖfd") .=JgT[T_D`_}[UO?G>6-$=L FD#<9.kT?b{48 a!$')*L,.0723a57,99:;<<<! A>'/ 2&jDQ$pkY ޸D%!я΀BIŨݿýPBK%6*,] xC5N֪K֭i'^::Ihη7i|Կc?ȍzͳϕԪG=2]TE#C*S  $(+0') /"($!&(*+-/135[709;<>t@4BCEfGIJiL NO>QRTTU\WXRZ[&]^_=abceUfghikl0m;nAo`<:8715T3~1/-+)(*&E$c"j z 8He~۔٬?_Ύ%aŨ/sԼ#nɷ3mܯP׬Lr2Ϣ.垡Z(ɘr_=8>Ki}.b=T˅:EȃZ󂒂=ꁬ_"΀zr܀ 9nPtރXӄWᅃeʉL،ōwe\fiyȘWM?tܪWƭSаAٳ\~_h!|4̅`.ӭՀe7 u_7}gG*f ] E(uQ/ "z$[&*(*+-L/124@679V;<>9@AwCEFBHIOKLiNOTQR5TUWdXY[W\]^)`Tabcef,g[haitj~kolrm^nFo/pqqrsGtuuv:wwgxyy&zz>{{/|||L}}}.~o~~~.F^l>(~~~6~}}O}|y|#|{.{z3zy#yxw3wvu-udtsrr8qDpWoznmlkjihxgFf2e0dca`_F^][ZBYWV@US|RQO8NLJKILHFDECAB@?~=;5:86M531U0.,J+)'!&c$"!0i'e'a :%;-_B:FݬC؎Hӗ@ΨPɧ ƍ_ֿMJùDֶX᳈_Ou7i=/ ښ˖ǔ-2]ӍX؊dوC+#ałZT'΀Āyv̀'dہ^Zu0Ȉ4щ{ԋh&юd< ʔ•ƛڜӝ =oϤDuWBfȵ%U5+ę7ǻHa΀҄պ`و'|OS::Ud^b t PP+S!"/$%p'(*9,-T/0123G5689:<=?@WBCEkFG9IJK9MNO.QcRSTVTWcXYZ["]1^._d`iabcRdgeRfbgWhTi)jkklmpn_oppq5rrsDtuu=vvw2xxyyzz{h{{+||| }h}}}~O~~~~~+OWf[N@)~~~R~ ~~}}T}|}|T|{{{z]zyoyxrxwWwv#vutatsr@rOqpp;oznmlk kDjoihgff+e)d=c5b!a(`!_#^][ZYXoWLV)UTRQAP OMLzK.=;@: 9764j31V0.-,*A)'q&$g#!l ro wPb I O4szqWr-*/L޿9نײHiϫ;ͻVȪ5ħG,ۿqA|E⵰* 󮑭haE Τ٣Ƣ̞Ɲ>u˖/b^PP#.A30փp3OсE.!׀nni~р|?@AB"DBEgFoGpHIJKLMNOPQRSTUVsWgXYYiZT[5\!]]^_`>abccdoe&ffghSijjGkklomm}n6oop7qqNrrjsst$uuvyvw~ww\xxxbyyUz}zzx{{{[||| }X}}}}9~u~~~~ 60a{rM/~~~~q~0~~}}R}|}||N|{{`{zzIzyy2yxOxwwvzv'vuuttsrgrqqpYpoHon&nxml:lk!kjJihhhgfeegdcbPb~a`__$^f]O\[[@ZBYeXWVUT T"SmR]QuPzONMLKJIHGFEE D CBA@>><;:98^76m5u4E3#21A0I/.,+*)('8&M%$#" +51#n^De W J  U8Y\0D+~S"kn\܌ۏmI"1OCϽδ͟uȕƱΚH?j*NxlԮ֭$QSK٣ӠSžН11wߖ1BΓ6&5p ߋa슌g}RLJ{ цlɅ]-h=փ~ołZ%AC뀭wxogGf]G@   5Uq-.>ׁ ?G!Q+R˅Ԇ'lׇe=Պ c'8ɎhMZSˑ;ڒmߓbqFۙl:ŞE埝Bע'wPQ!g򪲫fۮk5˳qb:˶H{\."ID•m94ƤǹȊɑl̮`9 ӓu;4 ب٣ڣ`ߙskP_5 bH eP4 s}iD&vT | ufNjK&q! z n!n"T##$%&''(})*)+ ,,-.>/ 0012[3%445O667o8;99:;i<==>Y??<@&AABgCCDEaF$GG6HIIJKKgLLMNNO^PQQdRRVSITTqU"VVFW;XXUYYZZ['\\e]]^ __``Gaa.bbSccVdee~e.fffJgg[hh2iiGjjkkk`llXmm n_nnoop1ppp8qqq?rrrTss2tbtt u^uuu$vivv,wWwwwUxLxxxMyvyyyyzzz<{~{{{|8|e||||||>}e}}}}~P~~~~~~~~~:_I[im9~~~~~~~~~~~m~d~4~}}}}}}}R} }|||9|2||{{{i{C{ {{zz#zyyyyyxxwwwkwvw!wvvTvuhu-utttQtssjs,srMr rqq&qpppopo oionnmnmjmmlllk^kjzjiihXh+hg,gf`feeleddcic cbbaa,ao`__1_^p^ ^c])]\4\[[Z%ZY_YX0XWWVVUUTSSRZRQtQQPOwON0NMMLKzKJPJJ@IH8HGFFEzEDXDCCBBA@@?o? ?P>=-=< <962.j*%!Hw" *[lN{H[1vL9[WRPRM4/( ώȍŒhۆZ΅Jń82!(0AÅOӆ_uՏې";At1՝w6٢:ݧ?⬡x ̵_ FÚ/-Ԥ6H {!%$*T.26\9;>EACF*IKNPxRTUzWYZ\!^_a#cdUf\grhzijklmnopqrsu v!wwjxx{yyz{{||.}}5~~F"~~}}|{o{zfzyVyxGxwvutsrqp|otn]mYlQk:j3ihgfWdbaU_]\PZX WLUSQJPM K|HE(C@=?;85d14-($_ #K [Otp0h!_~QlJFENP=AFsr(/=DG )(C%A8R^MeQ޿ =轖gf E+co(`U;i3[/ݳf@ )#8oV/ $WŎX0D14cuK'! : 7˗ݶ%~H>.f:7k\j7==(̝&/; q0f] $K^. l s!&Ol C| &y w,')yL,_M)tIiS?\$X]O  {z;R "nZ  C+Y*\GCSpE ufz]SFLaY~LW&5s;H.$f0E7-,b2kSnLH J6F :^vHq\ >00smx$?9F!-owQ&P8x+|cG(3ilG_,H7x9| WrR/>HoM(1 2S20kȍAKAl Q԰RpT#2!^o ݴi 3UՆס*-N+_.Z~,,%j\ Y< IhJ(2FSfTbLDT,A97gIh=2]n9 il34#|6 U,?C>.4+H1,dGw1̯p  2)GQ.'jom W S)( sM2kFh ( ( ]J8w {s KU\2e < N7 N.: >V<}O h _j(m%TO.`MOM'(= FUv]TO0^;6U(O%65% xc%NI{gc^~w!m1N6^QW!M<+ _LÎ|.+:#<>(mpù"Roஂ/}/@ %_}bPH7yUW` SyIh!g{nmOS8l49CL('3^&Hyj G6rQ⢰,P>'bD$J`TFv#P8r;(?L p,3j*S/zºɀ )S CkU/9# {Vȃ-T_rN0R>7Ȑ@NBG޿qb1m `sEY- H]O 7'kp(ȣSJ0k*׎<]Sn#zp*=(U%xGa2%зb(z߰'ixo>7C~p˽P@ : #X̶(r"b2d*˿k7Q6PTƆ  $ߩ4:ILjo>qܵE`߄][1۽ R ?D*vĵ)Q)ۭwhL( U#5pe:5QhF**h)`6/n^ ̆Whз?؂zb˵WJC<7> תNQg.T~ۭ,$ :3ߛ1M/F'^#Gm :4; !ˢ c~ ((^Z|ֳ,H Ir9ڏKß"8ķ.kV s7:1 7E?>HY/7#-@ 6ӀM]. C͎)>uo:oVn &yU M[R>B kµKȬ6GkB:<$8 +θ24!]o(Mg% x 7'veE-K-,TGGLw7ְ_odi4,_E_#ml4훸Ƥ͋\ 5RU&#T5M\M 僜+$Ϙ+&.šiF, ^t#p ߻Nɇ-٤#6&Mn'\y%bЛֺg+6rn`c.3*v)8] ;ĻǴn驒 p8FE../-^A5`ӻTs4 SSY.օA?mS?mHu#Q?!Dg /D.H c=#;X 6ώUb|\@ *jzB Nkf^9ݞQEY 8 7N$~/dH@ 4_8K,{  ږQ1#\ \}yli#?z CX:q^P=O"IS'.`</ 4{܄S2f xǯ9kz 2=[f%$7@RK ?.d3 3."+iH M$Dz*9|" yօJ Rf%@*6,wbd0X9 Q\7޾b!!*!}ŤL׃s)LP/i)jٱdQL%Cl$M%! _/Bbɇ7Yy H E9"1l $1 ) %/PD!ڭ!޼矒6h*|$<?2<DO."{ j`"sBU^(͉ p )]7d`G8J3~/.p=0-2q, P5̒ 3~sJr )Lu<" . *K$a'q;? .':uM;º#$[e MQ$VN n\  P /½GSWF- D4-+(d8 *Aa  D ;1_.9T]:7PY%%6d * SLqu pEռ {J{9uf%@U0{ 4+~Q5:Z c3!;AZa gQ ]>DD&in Sދ8!c[> 2:3B  8X #Kd ޺jAU3P+;gވ#e+M(&6Ld6H[-.E^ 2ٗ # ЁQ"" [)9 nKɔ \ѤO3$Q:3'DΣ:$K B 0I3`oYZ H ܮ6 QpY6 0q-%& u@nL"=q/o%@.@/Bwq$ a-DO? H uc n= "!l"{` , & >L`%f'zL=۾h"*.ζ *UO$ rf  I<`H _?>5M%6+B%!4#Ѳ,Qޕ lM ew܁sxp k.JE (I";G$}1 iZ+^/7*$F,"Glܛ 4e b  lQFG \'&$WҚD~+-hD& )WQ,<׻כrG AQJDI1~'Q5 #f&XP"J5 2'>! i7E#X^'R6[Wluْ[Q{ER#h GM*r4 k\v3)q6 .*X "nV?kLC`DF v " ? y7:F]H26/d RtU3 # $ _^#sk g v(!qcޠ_pLl dN# D#e Q20ID7\5"%OzF &R1>a'ae #".-l: !#2dk"='zF)mRF?r W*mCYyp t!C+<}aݧB f"@yd\)"%8 RK! 5 * PX i FmI`{(e9N~ j:q0 [a^] 1ؐXzG2F#yLPO(a  g 03;3Z /(' p7 *vu=|`U ?H\ (4 hc B 3aWEo H zKCw 01Xs5S5lT&r O :U uf?#!B #z|l Uy3D3fC'7a1+ =&v.[KdP o Es}3/ Z m H + ~IKS :, ~g = !/ I`s :ln  B. w@^ 0 _ 77*&^RE` }Ao {ITxi#OZJ0p  /w@{i" ,u+md= a P IVv   hA X JuZ\&a@ ]-klFP 2C :)MkZ&=i4m'[h  {}Y{U>A? D g" }- "sqE ]RFb MRs u& < ECp t JA\PvYcY[= Dqy gd6  \q'"M ['>7N]@b%*)kM=` L ?s^h Pw^ZL}iq_/_n uv p@f=x&3S?\ sSzFSCvVs&qfkK,lKRT[TG5Q<5\YXrD+m V^3r1SBw o$zB{aO?0u=2xsXV\,KzwX9C=4|,Vl2v17EQbGebPVi|WG&#'(0k S$*"r{V 0#z >a|*wB{p)  HoBS]WOd-b'|gr nfbP~-$S .w*)q;)@1(nJ6:0# (_ #߹,6.KGA#Yw Ew-&7) W1jA-ն9\N(ݝ dldؑW:jjdRr-FX7a (+/#ޢE]M5$ ޫ }HBC7^ Dр [o"9t\33+(8/sC;rӿ3OYP] <3F B9,!!LrCR(z '#v , ( +VLt%X } Xa l2.0Gʾ%ܓ0 ˇ s 0@ ba&36 ~/ Fy:X9B.!&\"x;&t y& ,.'@x'Qe*49f #/s |_%^%. '=TR+ mh ޻)'%k֥:51t*w}O{ _RZ6-N̮߬A=v/+)~JyA8F 7,|&q 'NbW!,qM$% )Vs\+>qp5ZrW.0۫ P.HMhB'զA.'%5Q0&)>M)΂<;siz; Ao !q$fqDl[Jܓn/Yv xU+5!9? w1hlx% Gg#=a7`!7($[F>G B 1\;υ> q%`< $2% 9ۍ އ;!ĸ'+&O^mY]YGaZ\ w0z}%<;q M (%%'!,X,t&N*!%` Q6,u"66{{=!(yhW&k2{%ׯ'I:Z٢)F"h nѬOx%_-[h *ц*3?  8 gu8(T4s+d|è* (~8m3IPH&K6O. [$^E/6:-Z2>n=]!W%{E:.Nv"v7(%z|OJ)jΗCg30-46 )(Ca  CL?=74 ^ h w32PcM {dW4)1)ϗ%fpUHU} t "p-7wAiƝ9w^,m,J)EoVr% v>07A$~F im!ՄKTHx|,-loJE* #;ޒ7 ְ2^7ߋ ').e ;V@!ڽ( a h+B7I!%c0 &~*0ͤ. [2QH)EAЭx- ]؅9GUCCۗkuw6@ ,xBV_:2$ [1<)JY1P B͜ '{ C7zͶ]qx Sq%V}e'8,::~GYq::L@JRcx<>Aڄ.֯kZy "o'n5` Aߞ:2r;M$$ ѼPD%j= R0 +D2x Cص"g 6/г >(&Ž F@%iO!߭4hz)9du3v [&&0 $NZ7# X 8ʊG;19om&޴>] S~- se  \94D579k  * /,95-|*-Y/w`;`ss1>!Zz/G&=ip=h2K*&+X'p"U ]Cľ(~ aڊ.>|r!! J ?p0 x: 5ܾ05%H*S0_%*2$"EY~CWY 2=.(,)8.(}pV3j8|F  +S21y <".T '${ ݣOk 9 X V9P܏<˒K# C"QU0a?72s)s+ 9-)#J-7 Kb\8Bg  Y 7:,! )5zH϶ 8؃ adOXHA ,p%E"(-纐.%^kܔgKN,|KB]-3;|AI<,  D_!!604CHmj,nz՗^Fh|6ic;X$>H:J ZvV(:+) 8%$+ݔ7'd,1}%YF~i7oN" 3Jo)ZXT n$y5m6I[ӽzq ;9 /,qLBl<ۃ^Fh|8Z;bW$ݦ" xw '/''*r qiCN!aA   ' 14 cIUퟯ K]r zi GtAj$3պ4=3۬+s> 1/!HH,E =7\ 6Y2Ll N5N!9/(LR l YC&Ѿ{&% ֪ES*#ݖ#? "/eB$  Z+=Ʒ d!3 /I,} 3$cG* 6Y ^yG C$ R |,r("5@1;4`!s+s"2Oo=M0o97 I%N=܎ #.(^7.k + +'/` SYvC":M-Q&jtGs0 j$"K  0 3p#U A/:M#(ִ;  $0P5u͜L\ٌ 3] !2, "hhL3N \kU84TrĚ (N-C #H NK /{%>&"]Cc:ą nY9 2o#ZόH65#8K17- `c%*(Jķ/'lU1t3єEA!%fO$Y\'P8`~,N ^dѺ|7#o I }L]($ְ< Aٳv@7 u" GՌ9 c)CGŢ 0,9 3wZݾ&L' -.ݱ} ]xTGQv+yon +xYDw`6UuH\š*l7&k#Z2T`84_E~-8"HZ(_/I&y[>r`e' * f΋ V&@ݗSrޕ)r52 3B  0X #ޮM.~w o C56iW'U*9#ߪ)~ 8 ?&'tBu2d+uD_M09R> /,+94  ^@ cf yThP #e&ف:c#15 4& ݗ -!zav'V AɪHZ*;rkCz#l8)!%$ p J$P c>Q+( RNBq>L7> ! UH7d=ݜ/ 1 ߿qBkN:$8*<cZ P%'i' eܢ%-sp2"6  0. j0*e+L "oh 0M ' 9$ !TJ_oޝ~,8N>fhL-D< jFf=:W%-7/~x= ƾ 7*? "Hhё243q .Q" |Ԡ>& '>,R.*,r-!-!!FyՖ_8{Xnm *א)bU)N$ ./;kE3 Z45Y:݃ ;9T0,s<= <@8^ " {'8 El'w S2KAڑYm#ʀ" "b/g.1g <90A,?B*衫&LJ 3[+gY0}{TH^؀  j0RFN wӸ4VoO&-/‘N~.>u)1\ !iT#.+*\ U "[ʫ2*r 1M "37L$ " Y%i߹Ij̮1\"}&!)/ Z5 [ELv.)[oYI' ׾Ng g1n8'oePʽ.!U.!٧'E2{FO&'4/͒S+'p?"НI9կ }PeЧMxs :U-{* ӄ7pN]2(t+$6تfr  G!# /@jv CHU Gߍ+K "}8ۇOuRк1b\04`aۻ6&fbR | # # &7c7 > %^n>cw;%Ӱ"<b| R k $d87K  H#*{7* 0O;H߲ "+nUJk?ܾU1?TV$io4g- yNMU;*ʰ4"_Bh F  '?[ռ.''mYMP{]Q۔CK >cE09@U% > ol4z++ߝ .!j(j t x'{Ml0 )U u JW]{12 hNٍ {WQ0`? ]f,؆߆ 2QQ aз+g # @]N)~ÆߐK*l \P5z%zl) !{ i$W2gғN9 \J 2b,pAJ2H"= qINE00I&ݵ&Cn֠=bAM'3YCɒ4[/&95x9 >, '5S )vta& z6otv0@ν2eIL" 83,˿,| K[%IXpTWUlXTڛ#pB#C7&ĪAߌ *%EUq8/t $Z;3"~q(߁; O!~_ ?V,u/ 9^Õ%p$ ty $ J@iI5[ C sUS7%81HCD0c [H0"%RO0K T"J 2gۮ"a͆!00b$[%$iS,o'%.*u5 ;N ]"+?&<)% =@56+u.q(ZN^j ; %^šKƩhHݽ'1j 1Bt0oqڡ#I rJs E F:8VGCDLVݏI:L!E f z1<68 يB" k a *сY*GN(zav  6Tb@-ޭ h^İUK;T 8ӉM)V"R'F8t 7>݂֟װ="C-\ OLʀ X#C[Ǻ1#L$Vt4[P-#}.I"@(߽N(f#s  -Du' {(EGZݵB)"XRkӳ`! o=Ӷ2*w%4N%->*|ý U3?qeDP*0"+3U)P4.݆#P #b2!+../< 9/ʝlSb %i̡ @ ''((l) :²<~a?CZݚ Pn+H 2g+F Ȳ Z)>U. .l5)"ydlm/[>[ڙO )!І#-:EsEJ(2ul-(=OO!-O2 U*ޣskЩ(hI g<zɱRaۘ |>8'#(+]Q2e>ы1P (o0#uJnsԎv2DЁcLF](Wc-'=յ*(F D )-* 'I,xH=9QYrpH 1@ߧ *I[ˀ$m x|X)L@+ * 3r8 "2TT;0F Y%8d4dՊ: 15%l3 1x ٟ,qzf pKAezPJvDE^ /0+}#%-?h@9<&p>3|2E(Ձ6բ<<ϥ1y㘶@8nWDB,}bE O(4΄ "BU͆˾*t>ɿtzMO6cgP]n1G0k#>=D 0}\p|:Ǫ6H\KGE>1Qšز}b%6,G4rзB,h5aVNnWVy >i6 +t5'Z^{ !)r#;.x8B;Tq} ${Wqg0KStW&o4HzC"6ZL6yxqE0WP JGGp;q;gV \j7=6V9{\MH!&sMGmR[TTxE0E5;#,;';԰@0DۋTh97Qh.Aܮ+y.56f {]E@f[dۺ?vpz U3L+BIL16`@>NaC7@M h6a($ 2z"64+ZDe+lS,`h 3 5F|J? P#G . p 9kV9,W us[[f S9S Csv5v%Xt>Bi_ HxC"a$Ueب7Ga,5.B` RLT~ E ަV;k>G x؜P )OEBm=cy`Fq<V֯[ &2"r[Tams6+F ?"[q*wh) ?c2}XD,%NW =cJ2f/:cեYj'&+Yd=];(w?j0aɡDX }u a?@ afRKl$\ F6Yٹw$O8/YwgER0[,R !fE${u!Y֎WF14gB ¾PE'wTN4̩Ռ"LH^U l2'*?^.l|C)G#Q^/SSClm: .!i;Gk$6U/e-guAXґ%b"8JHL :< km)RLO^ʧYj^AJb&7)LBED|5%d)k ˘ BTN޵IV 9/ f^Lc!<9иiol6$C[l5!!jE73ƶ!<&9!b=Ӄ¼ŘAN.S%IgB#h)+ՃC+F Aĥ|D1-#)'I#ɻՈ`/i$ :\G#Q< Kc.%.&0^CG/䬸\u.RG\2 "_ 0d Us٤4 V@LAs-":p# |^¶'<"aBɽ-o }g񻒏Nݖ,Y*Nť*R@a^: P0jXN :4: lԠ8#DTM8T5 ϛ x=<ߵ _}[^+ >^]w@ Z,0@k f8kiY?'6]dT7 `^"IǾe\޶ ]BsIEMEz4!pM5 : <®TjE )ERqx< ZN9ۣ(;maÂJ٫*rˋ5l/|Q w: CP`!41p f9k8Vƫ#B3<_K $~X!Vj1U{O,St@&چ^ R3>c:BuU$%nS᫏#v~.S5>Y9pܰ[8+ .joP H']DWCR <4E0TѦ|*OЛ4aE[,VrQ-JOQQ Q,:g}\k*Gբi+CM Ba['vi*/E8-.j%W1Z!:EA0 u@E)ko Zz{cXW 7V+3RA &K)}50G 0= f%P@eD'O(53)o%4"{=*H$. G#_JC(Uj58ԉDI"%x;ĖPӳ: $.$4i=D|핥*,5 f^(i~>4)&^Z54'{^ ) 9r!G(W p<+PZ#hdndž51Ϊ> _k !9Z=u9'* iGȂ͔Sm2',=*, N O" 0S]%4( +d0 31"ܙL1΋7I&(63Ј652 SqX*/APv('!N-BYW{W`?9U5*7 - $[, . 2b+M u =̇nـE0Vl[- >)bT&1`s HqUҖ 3 !Ԕ O ڴ$n\\#"&X z!*L &!wqp3_?'{$@XjP4> EB _ Aj M Q܁4#M "W U &!\=0ge9V&3 ++:F lKF&#qO+~BPc-  jGϋ,_" w (ش(]' k:Y!4TG+js b3I / ~]GP+" JSR3'.j 1 u\:s>%!{$b>}?TAQ&Q",%p9y~* &ci5 i^~CLd NeP nL B e  3Q h 3D8*yL- E I i55BFPdR Zc3 mL! fc^Crm s[ Q -G4MX#AAg >a ] i._~bTLG oy5 y? (0P },> DAne7I?*>- 5 %+ L c "(m +=-t? +G l9qdzz[}E*i Zk : p*zu)# D q;-Gm:z wMC7 Aw 5d k<'  DDJxI`! <[3 MIp(J*t`RZmnfcN KF" $T |W{0="1E MY_?*c U  4b B rK%"N 8/V^Y=) w  ^u N,>ykvS{ 2pW #HF HQ bi~w+ p h W5 Oq,E \w zcSm5=#t/ l9vRn (r k( . e49 . 6O zYu%% Q  V  ?Q {UD-e+  <&Xn(o(E 7z ZJ  /#J, \p!a" !gud&5 Ob??\;0i )w{C]8 " m#r+!k 8f Lx*cG|j{ +)! x`_UlC bN E[ >=@ |f0V kdWf IP6 8WO 5#}e6a[W^:b Y >"[$}I ;3e)Fj; XOI\!#.pF$]Ln3M!Wq2Y9:Jn\{XG]%ZYBGlM/dO{=GO Dx\7 >cDtGXk hz`qHg--:2 W|NQT]&%J6n#=DL$-dWK>GF:%C  9|:LBv259xT6Y SHx9@%~ KsJq GAG?f\7Or3 2B^w7,,-d@W}b>KJ<#+h;.ᑾAF5E߹j3ںǸ~.0C\حI]$6j~ & .-΋y~2a7h<_1US*?_ _#;G$BtѽQ 5PͮJݶ{9?tYa=&Cb_;jg fbN oG;vƇV g㖿=!p_W"jE-+H^l^DҭK$9G<04â n8+Ec] 19"<1] /2eԶ 澢y!C6) ɾ ` #e/Q k!ܲD,oŃV +6=[4 M~5u'̽^)uNB6|a .y /o 6Z p ?ܯ܇ d!^0<3``O D)g ~cj _DlSɣ)~#k.>N9 "B?V/so;3{/8,v@D\TWwMߣ]D6LVzP,=ύɶS} \bM/'e͂A|:*%큿?s$#4UE9x8E$94'<4gܜvxZ I>[5ZaJ$@LRKզ-a d%)%ƺ q 'vФºϳI eۖγ~&UKl<Z&|OE`abdx4.@ L!]MRߣ'b4=!(?s0!"Mk,Z2ڭ(SDD+,sn =f.$E(fAT-EHR_>2/+hPX:*I0 %E Fa oASB6O0& _F\U'թ)0;VL3D[[T͛ 8 ' &gO˱L*7N.^9Ϯ)o]03iJЉ"?OfNgS_5Mr[-F?%t8p c67a%y.EQ)l؜+U`M.ִm^ht%Kfadi]H&9/k  y R仹a뻶d5ݗ#-' kq T`(܈ ћ#, \(/٘P ]26nR@(E l+c̽ԉ9k^. F6OEz|-&P)ـ:o8(F(풻q;%E>a&: =);]ߎ̦0Ec7VL g-?zHR(!*]Hv<%9j.)ɟ ڟ,k+2 Y޶Xzڏ+n"Q1uם5-!O:VGZ1 >J&H} dEY$Gr{GA7Z;>MV(m[z,f2 X,=}P&`ms7lx 5BSOa\H]ɚ&/17BV2)[֘ #v/Vp!LGH2[(W g% 7XN0/RЧHڷ̍Q2;ӧIŏ| *9"uu0QExD3FT[8=59`6@NA'f*5;$(E,O*.x'&6J' ᤎL-c ԛD.dZfC\K\3 } 'BA\\Lr%TW42 ocЬM/T`g^A7!u`n8qX%WO-TI%#Q8)8 %sˇ.*k1)5!_;R*/+w$cřa*[Q -޹Є޽ZʘÓ++VE(S0-b׾̬¾6!zA=?b1*ıB[+= f/!{3!lI! & *[C.., ! аЇ'(#`p# Pw5^+5]ռԳ"Ӛb|(+`""~q>qPOCj) ߳Һ$ԄBJ[rnTxqp )5t/ >;4I<j񮺽o%U .T-7˶ͥkΒ*"ߑ%-6@Waq !q} Q7>Pûнzʹ(-,L  彼8jރ(vʢ񤶖˜VȺƹ՛d2h(-  li޸*"i69@Mқo I9I׋ t*ݯO&O%&0/'o ?xS;ŶZ 2?PH !B+<5@]a}#hM)mֲ˼#3\ (o%=Q2|#'#.w&8֥e F&D7%zwreB:7)1)x8WJQaadRT!d mVU +:SJ?'Ubǝ>(ɹ٥ 8(Ji0!0E% eB;\ P?zѬ!P෿lˌ&X! X}u08'8ѵJ2n253a,VbJ30:B3E:67E_tOy'0fZ.a:59'3:j7#^F6Y2+/@ %i'-x /'0Ye0<ݢk\^ a5VJO!Pa!|> 6j :<ԉv붑(HS]^2SDs)ň9w3c[F=   ςj2)[ (0w@ܪ|eީʐHk4-鬞 (sneԲ ; ֱ̡ӑZ˴'UƺI|o/*񗵱ӓjbTRB}|;]L 2uY,762C7; W Kջz"|C #@]G=DJ&v48XiavovvaA`p+ $6JY ~1#k 2bW9]LR&.) 4;.r܃4E*Z r?UKߨP^k9Fyn$6;+oD L<'5@H#in)9 pm+V;YG`6=MDR0c.6AWF`kp:G .֎MĔ^1Pd8aߒͪH,: aD؄޷#\X#.)$\$.:§ҧ'{ࢻ*CR`J". @ῙaѽOܭF Dƛ'jОG-z-T`Gc%1dm017 QZ"*7fK],"ۭedM%m<ӣ+ȪؔD. J"L;G1}!q!J% -6!ܨ ޹9 Q 0 .Y/XPZ*rԕ`\7ʛ/0d?b3f~5*.%" 4G Y 5#~(+P1*A ť+YY73ow *r{%ۼQ$|($,+y3,)>QmdgdeH2%v=s"DCi+* pQAV оjW ů H*# 9BP&f8f4Y x;)1-791E'g%Qý]L:! W l/ |]6 tWVͅ10$))OSiL*ZӚt0ݾY?٥9T>:`E|( "rچǃbA{KɁ \##-( 3Py2-JG,V]-" l.&)C'ٷ5$d=6$/|JHF"v ْ) b j֓𜓗GXjmɔT*&]Ӓϓ by;SD3m 8pmѸDL"60~O4n܃6Iu=' '>RN6HpP  ?%   %O^р lsI=5R@7Q*yy#B:gPN>1VPϋnÔ9-.?9;) 98HHQ2n'mt+yJCHI=K#2u@㿤2(RY#%Ԩ)D]Z>lE32z+ q̧HրTըo-(T?' "ZDACoԌp` ʭ/c TAŷ;=q\g]N$!G4" 6JJ:7WOWdZ d /X4"EDNo =nJ$C 6u(zR O+q2r8 *i9hˎ̴҇OC>vL3 850@EΓf/н\ZY  46TbX WP8zjhߵUڤ,Zī9:7ZNaI%`@\&4ZծC# k5ϋ3DK?Dꆺ]#W)9s-Sq^SCNPS&'k;دҺF- 0"c#Gq~/Ԟ?eR Zx$8VoѰHi,(ٹ(,҅f.dM/9|}"G}x 1G9V%/mqq.;J0gd$GA c3@q@!k% /a )0֬Ǜւ|:\9J&f#^Š8K:{,y]!ZѡA)+OZ/!錄4590+j{-Qӽ# 3"%ʊ|bL* uB6$O+IJVq̫˙=uAƭ?$K@6I閶DO#{Ni1r_TG,SN+%# _)UT\F.~UL:+hGN28 !HnpU Y9@E*J%e19&"H(7 i;1($ռʳ]jнpٵ 1-Zim`:1QkOB?Ç!/ú3:r/!Ko-oTxJT(=<(4Do3u[6HFT_.ݐQ̵!s l:SL8p&{e'/i4 E$ɸqqɑ̞dv?[GQ+ܐ' 뾗kk EԋS!z G1 -˥rL"MCeo.z7="a !+30O@xd^3@4G!$0Wr@()B,;޹a.5;h 14=ֿ'[ 8ЄٌOi>FMUCMW"9AH̱X5Iw ++XO.%&=OGلr/^\9 ,3dN!9J @ ޛ= ,F0k;I?I{' i-#+4UOۜ-t!4 0#ڎ@֥cОlֳ֑=ݔ| ]2u6U\mQG&&E6U:j0.u'U-D.9ZouQ 4p8O_hD|r9_ H-<(xѻF ܍E}@ a @GY8A.4-V2Jx ̻! ,$N@0FA0ZO#h}tF E1d,(?[u_N9$!Dg&7A2K G&5[܂ZG6x:>9>C=s13eո۹]6s"ܵo\ f`࿶ޠN"[73X2?F.)r(- !>P(=e-K = (s([d`#%;3Ҝ 0<;VT ,ӹXFڽ@ͺ~,[5bޟvzq02yBE;(#'<&܊m/<'12)P.T7ʓp1 Cʩ6X̿ #eKDz` 25g&jJzW|W5ɿѨ^ݼf[9 !-+%1)pܬ*C*Cp0, k%*!`WC={ 0GB3a .mXӧCR U2ܹ`wiГ˔љ.?H5.Aц[ 2 RӄR :GF0W+9*h4*K`V)&9X1oPtV%.96&<&8/5@bN]9WOP>NVG%#;>0*o0#*26y| ɂQ"!+{ya,MH%X'H(51 B !gьo4z?5Y puؔ8r4TӾ4'ݴ*5%4Be<7˾ԯRL:&:HK 607|ǰ#0 ~S*ͭ^A&m<)ڇuiSRXٵp<(z+$*rh!F . 3݋--1`[6"i*9g:Zp+d =)_%;-pI4(0U( dB{_ l,>E*2gIr/D%Y dEv=!Wլ e87;TYjz78 Q R4d*+05Uϯ3!ϼ9Y_i 㩼$^ $3]ܚ(9DT*f]r%`jŅ -3}Ծ ğYөzȃ`'eS^)@Hּaȝ& (_S%-PΑIo;7<6U"#{A) #냸 išq^} Q 11hT~|T]ӥp{ > ]&|"_{yPۧˉ Gpm8sMLG&eЧp|$y,7]˾G#/#0-r0lw}mH M M!G( %g9ߨFAQ<'%TI;NQU*kDu{tc%JE%HG=uhE$ RDZ!Wr%Jɿܺ2ٺ,r mO^:b-'6%hG<> Ǻ -N'7 ^ ?)W!$ qa}.Bhq2"Y:HP)uD>`RL %o ou##*5fB;a.+9Y)L&&mz`شլ(Ƚ=0;9䬛7JVJ 5J7}EŘJc6"h7ze>ZhQ^C::Blm @/1Pa :ұFwʛ بs"+"Է̻ 7/S97 =_"G8[;+/~ MEh7_b?~FO]L@LU `q6OHؑAXd\32 ƚ<~$ İӍ఼8 &H X!-Oa0A.87.5 PET0#*.5eC]7Ѝ ]8v";OXM`q.q}Ov D0oz25NV*U<9r-xДV7 cՆ;{[;3)8Oyte1-> Ш=G< (We"&x$/:-mI{g h &?)՝QjkǼ!2%2($QL. EMK#CiL:Z5?/[uC 0QAeŧ!Gwů@!B586G@r%AD=WQ/e = nL#7 s0U96.0; uC% D'W2+UE" V-saYy)R5q !;[DDWҗ˧f–HA;D^ol=39ؼ"ӻ.SB:; 'LEY>_2\%An/IRJg4;M1}VA~0~-kϵH![=ADR,[4(OZ{ʥ7PިZ"I S0om3$'i ")?A>9A+u_ Y܎6%+*AYIjf]I 'JcVkM J&?J,GRL+@}x/kRb@dt_N5D "ի ӽ:>ҭ*۳?rL 1"EARέ- _y ع9 %\K2(4O;Ew/L.<3kM9T?<]d~+çr *oP%U+4{> PkTO7>+%dz%([-,uNGJ''P,ts<.PB# "R.2CNG0_$?RS9[׮LlÅyƺ!cɇ˽/Ly=!J6(+< PQ+29<=M/B'|8ɷǫ, ӫr<$b; s!63v PoO×)2+0e:F}Ɗ°%(7 Bo94)-S\ :sA3$G&"{DR]A\:?;X -CRX]Zb4U @w'[,8F!Œ\jˍWY c;K&OFeBnp%P;14LtHU<819'7#$%( >+Qڠ6T Ãz).ǯR,3s: "UQy%.*籵ȜS=|]/=`;~Mļ% ;=&5p%1:>@ N}UA#V/ٞ -?C/(8u  νtI L-#[Ofy8vGd";qΐj@U;ٞv4*6ݫڎצ) T:5.wB$b)j2z6?^&%B30?^#8X>*HY$.RAN50װիI <מ[]'!S*W]Te\R7NZH* !!dUU| @ZUQ07*3Bh";۲ܵa6%DRIJ>u`{PQˌIG{ק^2ì΄ 01Ґۋh/*7:0 W ! `efUh#4gL3ot쪾?DZu;! $'^I`׺ d$Ԡ*9ږձˡB/+3N JpT)b#*1B@( 0aqG݂($fKDiD' -AjMK=2D$~ ' i: bͰ C&8PTA2 SI .'믷C]{!DiH R>]f$E(tZ62Q & g}l s#@,JxJ`ՕG%4+]+ X]*,U~Y3/4,l ̽ t%H76ɤ2ɼ!פ ߫ . +g~ h\UEAG Hn 8%lKU S+K61Z@=݌BNEH4!8:$d@< ؘ<-G6GBR8r%'sys[ɂ[:Z鶷9ޖ<5ԯ#ܥUȕw *|٤؃8GHH4+lB>;#>!Y )^AE C_FMM&x-(?LUA<8+dȤ5*~!r/K0*#C/[L58!LLbR[5qկ0[ r)Dz8q 9 D>+0= JzDs噱_*ҹw3pC7,lȝt/_n+9.H\_6.)+N]?gg?VEZDY5 GB9$(2Ooбģܭ8 ]:R;331WmfHn"aDD:)<1.יe^"Z !!-0/H4_Q{FίRp; ĉigȳ63̥F`A37U&*H:ؼ&H46k*z;֎ӳO{KKD%Қ l1:'D1 SP3kn I?<xљ`j֕D( 0&Tt9/Kg|xFdzƬ#jA2& P0c#TZSS7je@1 R4-NMBG;r1.ie ݀K|IFX!K;0 z @97 (Z2U٬;X޴75o,l,"+,>?SQ_QO0-c!`+덹aԧ/V%{,@)_emA676ZQ8X;ERHISV 7_ 94/U 73`L06\wG/&6̿Ơ:;'8|ϽĬF; Q9 )I ePB"B( 8?E3U,ۘIP?~ƲFs/557"z9!WӪ޸ nj(5 &V%Y50=6Fi ̪B,)%&3($:8'6N<< 2~B897Gϡd #H]b_\$ԉo >OӐ+v 9EITP;$o#j6x x*FLڪ;.w8nİ3  ?N_9@0'zO/YP MD02FBlz&צʸ&py$&2W u29HWR1N,=8ʢи= 1bB-t &7j ?IUpSyTUa9 5<,6ONZ%z!5)]C; '# &%8!9B%L/Eupү+4# 7d`qqvB38L"/&bj0FOGZB>H^'d۔h.7QL<ϕD,C%K42 Ȁ40&3Y0.2s/ABG9BG%8j0&&>Eg1c+4Ȫ Qȏ8 " "h⇼z"k.gll/*N 2y93Do4GJU! γS KNW*!BVG Q j4a c =$-9O]DIW: ^%3-%,24nϮ v/D 7C"$@> {Hx]9LG5 "TQ-^1 &=3- ,%J~?B5n!>H_?8$]#<+69Gji}-7_8 :MJEOE=S 9?=`C 99dÏĺK7xQ/A>!C'&$F<8>ݭ :;_`ZS>3<ՆNph+JRCfD]& !db:8Y-Q8M*]ȧl -֌hpWO@ԭYBuy  'ծ .62x80da fx!\34E0 ?0\< %,ݦA I#>5FJ[&i1~'2`;h$"d/%EYm]DQ0+Ra< E~բ*?ؑKH\;%GO-|u7Bz-!*T{L{PSl([0C99+25 U R>[XpR-(m<|PpMu\ 6b90j (WPaE,X2ʕש*0sx:I[Vj=3jGjV??2<f ٣ ԕznEH^.q;,@E $J`}K$տ*o?jEX= 3GM= FŠ׮˦a$A4x'9W$Ym}I?wP.gֶd ' 0+(f6UeUťPs(X׳ɵ{K7^! qں!JCiQ՝' <7@B`A¼ѭΊ]"f2T #5$LAP338lh0 ,5@2h{ͤ;0u  iȪ} 0@TÍ ̑0n(0)?2N]4g)GE!i/?5z--[970 +ѾOq #Mf4j0-[΀16 ;NVЬh1$/B(} S0g19rC{/&Q#|2G|V +:>&-z -4$"ȉ2p0`b1V=֯1+BI= +2~H(wV.94PQ4"NA5=7ۭ <~ "0gAK úv#C{E5+ʔ:F2_d.d٬~Vw1ۺߖY ZD(!Y}S4P8Y er3.TK#B'* L7Q4MTp7~yДV n:2,nU'{D aj-1v*`T2XQ&KI*_;GhQ:C6D+/!('L$ x}2X6N?7!)/ͮ lȠ-( 6w@AUA>I?(FU+!ZP%ShΧ&=P2,;^Sla&=2ڡ 42*wԞ+ywxʷ? 7/)9V չې):-܄!C%0A^=Fq5 .B..ډ;黶[t%B۱ʮ;6: () @- LǺ=ƌj^3.7&K6J>NoB41AhNTH,I"~ ɘ7$.ɂF >W?4k,hÇ 쐺pwڷ gi #ΏvqS%:, - P n _/7.'/]<ڦИ4!EEմ`!}&p)h ws%ѿ|-6j%4Xe2~?IW[ $rqI0L$-M L vA tb>v!zҧ ($RW 6@lj1i5f ȴ')@(;;I;(;c){ HGpcj] xA tD -UWbƎѦ$A=(R9/5KF9 '%ӛ@ϡkB)`/_[V[p)R 3\. AxYio?4Wŧ,X-^"'6ґ b&4HDy@'){q>E9, VL;G)t$:m&5dz0_PEo9Y5(&W2U= KY@L4rػ u笳r|t @I9Abl#cMUaQ&U,T6pH%)Ђ(3#xvb?4`*_~/51z[sCS_7|&a13:;[C0' $U/N͒4' w /_vϖ]b׼)C&o.WH)M2N&]V#Rٟ2Y)/4$Fn 4oRl ߻׫al-%%3 ^lV*&!E p ۶a#aE9/"D"|`!&X2^*" 0PaU/S >" {?UCc 9d - V2re ´VJ%2gK (#xٕ܎o`$w =~r9x_DI׭ӆ % &˘لo'C.Z>$&53&{j&%}%ôAB_U`:V.0<ӜYɋȷA&"Uui0Yld#+^s{廲2Ϙ F p7Z/IcX@4LUݳ2>^0\!Is[Jq$v Mexޭ+$VsD> !QJfHBL@5f`.AB@B2{06V+; R:Lz4۴KOl^H7A<+0-U\ UWF2@D. K7O7J JCn v+9itGe`"# \GOm- ޚ!] }*"#2]\A sň鱱kۺ+C4-v 7P>R8ڲ( ݟ 03 G ͟rw\Nݜ-`.6@W8['Q/aM#eXS¾iV߰.QG '+L,5B& A?&.bƜ/H/ޒ^ѫ+%pL//h ܆C 9;sd[Dh ؃Sؖ(HPd9\ RhH 4X9R2 n 6 c8 g'Ɯ$H ʟ#/P.*N Z)5;sOY=EZ#'6 -Je3+.'I>ХZs.#F?z 3QZfOG39ȈIH->#=.D}3/NIM-.Zëc $t0 rur>lINbPv1tYH <@FJѓ]f !')/'OgC'4U-ǧ-T3DE8nE(M 2n,} "14uk̀s6ֻ|-ݚڴ^ T<WMX96)!Ge  /924gc!' &,*ȱFX;2K)ф\֛&;}K@ RU5ʷ1.6́;96ni)x3ഷmʚȾ2:~Z#ӑ ǎɸ&.LUG/)n-Ap[KSb+E9?Oy?[XZDw&zxӭ@~OGpČ >;c d~0!0QX4 e¼y*˩%JU\AXĬunp9?)/\HC37PTHCɋ`F" I ]|))Pw7%٤// e޿?1_ P L:UcFv&?'F_5Y*Qa9ڃ++-NI1N ~-&t),i(5!u9A5:UUdmLi2W,*՞^* c0Wd a(Vߓ&#;y4$7P] }5~NZ2џ 7:Vt|L arm{mGK&]'$JaVE+ 6\#ol_*mӮ Wf:\ m1J"*,+n-S+57gީrLQRH Hݴ(&uT9 !)h4(7҄n!a.d0 $+`&9O*8_@v9 \1G 252ә'i4N[ t)yj;Q8L8Ɏ6OM z} Sݡ1%2P5*9II 'iN<H]cߪH1G sxfM** xW,' 4?&[#%<2<&J&+7SS~4JU 0rDX+:z1$^ &Mj !7ݔ֑7ݷ#\2#OA(bH^.OG'zΌmIH!wˇ1h05 C$Ż2gS]~=)ʬ2@4, qO<H @!w:d;/k'Hޭ*iC,!7F'Ζ]5.ʲ3SU=.{zB0ž5L!!D*+˨MÏ&6 3N:LϘ9"Z88 2 Z݆&~dP h˜+ i8t}D\Y=,W+/2!/U& 9HBр-+B(ojM,; =3ϖ\4"4\'ru׬dH(d! |QS5|$*L@9#H3)ilﵼn˯GM:0mCEݡ;3c'CjC6޵*$X-я+o}=2Ц5Կ;HټDS՛C+b;# C~,u, +Xh&8Z L(A+#}*f'ÇZmI>KrA@(6 {LL2b&"G~,5,#! oibP5GB1$vC}C?rŁV?oA7lU-1)Wĥ/w._%;H v/k3NثFtHbӄ8we$H\;iD25$C<)tOݼz"1ڥJQC-L̪"<%Ce _Aq-g(=eAGO6 e2ѡ@1օT>E̡FP8jۖ5O[erJGĚX?zνM M ?=Sl%_N -//Auƈ,JP PoC…cz 7^$9~u'( 43i\})̷ 2 D7RMbS 1dHmj(7 &e/R{PktzcmsA:x],! %0#l]wPI53^Rh k(hH#& x{ڎ N@X|[U oRe|+};R SE4 [^vq!M pBmzFK3ZQ~9tH{ӯE'5Eϑ'YQG՝M÷'N. &-"3 ˴"$)%Ǩ,x>^ * ez~" zVRE1׾%B(c9S"K< fjB0Ě#'ȉ!:6z Ѣ5E5I! 9aqhvE1GZ4gqMyv`=,f) 2$}V\+/e?ƶ7gw~o"98LW +`>ʔ8ԯgVC WiFP$Ca'4żWW %P j4ǨzXD NW**^3#i+`[?:]c-Ǩ^ &i >$Qjz0+08))A6l.M"DE$];GuDkRBjәRAޗut{-(4&N;- u/#!տD.-IMܘ)Y1V5f!ɛDCHvz 8͢Wx(@-/ I)l:+S0PxL| fmLjA2D ɾz1Q5^˭49$4)K JIcc > %uʺ e.7uI9Pr7&7l2}m%S Y]ڂ u1í(:"&'PCm,ʹ/r(17Eٔ%ɊD۲R e7~ϐ1$ *&l%'  aPzؤ 50  C< >O iX w?)(7hjnWfA30+f&E!7v kō#VQL(7kmE: 8%%WO.*UCc$dJ$"D(m j3r7#^D윃sS;V#&Ys,+8*H#dAu~ťx)ܼN T9ܜ= H 12 3`&3 o,kQ8~F(i.fxz:d Q%]4>p޵(^ِ1CV%WYErb ?'lE\A>|N1aŚ.WNNu s"090l*H`YK1?a6aB=.SM4T1 :i(;P/l,{_ On&rW7>'*$0qs3) ,{Y!<9*Y̕gNXQ40DB#a޻C@" ǴiH1:! U'8);l+ѹ H(vz-ܹ׍R‰&u-3d ޫ0 @ ?۱97@يn#PI(`)p*ft8t"WRd_q)!]KZǐ z[X9(0}RK4)O3)R }v8 E ! 3'<r3 S2K9Ա#C IT Q,q a2 # :Ф ?"9)V//Fn.Dm49JUOm4J"4h֋MjU*&)JZPS ?4Ļ1!|P0)%%&٦[bdF(ӷWv0#] V >85R5J"/XVxni.\'@t*` 'd+5L$A)4`$h g$ (GUp= H%h Qu76pF+ڻ7&6ޞ_N!QV֧]n7ʔR;Q+lNܣKdİ.k-X+݇ Oy8O0^Դ#XʏPX`T #Ine%"|#=k5Wh!'mO3V9*+1䴪BؽrZx4֯Eܕv&%4 @8,uNcw9kF|2Os&4χST9(x &'p(ɪH7=J/#JRϥIm@@-Wl@EF#?"u29j- PS*$%Aݸ(ݞv5d"]얆 ҔzUL* B@)^v\*310Qj3K> Zy!3&]2ѕi%l y Sܘ6X,ӄ ~_VS RGALp7@mՉ+I#݈(!T*^| T =$ Vn& 5_#p]((H:>'*6ߣ&8ÿݯNҪ'#>ݽ݉fTS5J $vc/-<' M>7;hIx l lW, 2!JۂR$ . J#<|-  &YT Q;u^ BQ aFb-mH2x}EF#4iqO99Y=|WQwJ$|Uo#rlw'-vv͉Ԥ%s y.6]oIX-zBe/y2mG=Ey?y=z1ܱ I%"-nsҸ(ˢF*نS*+FϾ6p1fR7g+NDaX=o-i7ws,yx%uvTjo[cHS2>2b'#uhܳd w2B{ۈZcŌޢ.Tj\ ?!P6+)yI=YOg_pj[vsx_wuwqotelXaHS7C~#+1d1' ̥ܿ*ϡK :B#룎Gmv*3&7*Fn:kT\I`V^iaVpjt3qv@uvvruHm=ruebl[SdOEZBsN-4 A$2##@yQԄ/P@{:Б*<P=Z]Eo\K V4j%¬eku^$ 1#=0[I@=SH\Sd>\ kdojTso)uspu u/t{upqdt@mqgm`phXaOYEQ;FG/<#Y1P% P0 U^mْ͝/ţ6}ş_J,Ő|pȊ/s{yO ѵQW1Ik-ٺBX t&0=#9+-B6(K?R/HY P_Er|,3_g]k ;Jk%2 hN[-ɬ Lc5g% xZ(")90$7+8>2C8I>OE%TJXPK](UGaYd^gajwelhn&k9pWm,qoqhpqHq^qqpqUoGqmkpk%o6iumdf`k6ch_f[bWO_*So[jN@WgIR'DN>I9C/3R>1-8'2 ,&  K> [K^5*f@E~Ϩ%+ TO0бAѥϭZϩ)BhQ~Kޙ&{7J$}L֒[n%3ԕӗ؟ݢOE1s֨ l54'FƌV)Є/*Lzݱ@U?uo 4N ]!`&RD+2 /$V4)8M.<2 A.7Dp;H?uLCOgGHSKqVNmYR;\:U^CXHa [c]eN`pgbidjfkihlimMkqnslngm0o)nDon'oonAoXn;omolnknj=miHlg$keic+g`e|^c$\aY_ Wn]HT [fQXfNUJK SHPDMRAI=F6:XC6?2_<.8+5/'S17#-4))%! x e^ KC6)(!".Fk *2Xݗϔՙ;9ũ)˹ǜgė%ڲ w$~_<6-N٤؝ۢKݚ8`O^_6ܔ+J@ؓuȔQ;<͓F~nM<Hs:"Ζ—FӘܖBЗ PS%줵ڦ\ݨ#ܦcݨZvUѹz\@ʞTջ؞ۊ} 'Jn +3NWrz / E U`g"e%](MW+4".%0'|3*6w-8-06;2=x5@ 8zB:D = Gx?:IA|J\CLENGoPIKRKTMUOzWQYSZDU \Vo]X^Z_[-a\GbL^Pc_Fd`*eaebfchgdhehrfi%gdigiVhih!j=iEwԷ4ώ׳5ҭڹ@@jVٚs5l, "H!sW :*ka80kb5'cO  s $J k / ;>#7.' 0! #)z$%O' (!*l#x+$,A&..'/ )0l*%2+q3#-4-P54/60718 3-:O4[;5<6=7>29?a:A;B<.C=EE@JF.AKGBBHHQC@I]D4JeE$KhFLgGLbHMYINKJO9KcP#L3QMQMRNSOEToPT>QURbVR WSWLTTXUXUYeVZWZW3[UX[X8\Y\Z*]Z]1[ ^[q^ [][][X^h\^\^:]F_]_]_R^ `^C`^w`@_`_`_``a>`5ar`Ma`aa`pa`{aaa1aaJaa^azanaoaza`aaLaa4aaa|a`ra`ca`Pa|`9aK`a``_`_`]_`_V`^"`^_0^_]n_]*_&]^\^a\F^[][][\Y[YO[XZXZWZWYV3Y VXUCXTW]TEWSV.S;VRUQ#URQTPSPhS[ORN0RMQLMPLHPKO$KNfJGNIMHL H-LYGuKFJEIDBW=Bu<;A;c@:?9>8=7<7 <6`:r4~938271605/4. 4-3,.2+=1*L0)X/(d.'n-&x,%+$*#)"(!' &%$#"! {unzgp`fW\ OR GH ?? 75 .+&"     *7ESct+:J[m ހ%ݕAܪ^|ߜ޽ (D'bMՂuԢٟJdqؓҘ)_DԕsͣCр<оrɩÄ́YǗYʣZ=ğȌ,0uƄ 2[ČEP¤iTͻ4 hȽs)ḍQ÷Y7º,$w]ӶIŶ1BHBֱŴfIгX"ⲹoS+̭Hp߯ydPj9Ϊଂ92߫h=&樣Yk1˩§D[Ũ*MΦݧzRt-C ǥ|W5lYH٦8+x dRB3& #/=L] p&3˦AQc!v@aҦΧ (IHujѨק3f(Rѩ:r@q#aت D$|g-8jʮ,aoL@ѮChDRPHfŴH%SK h{ƴ%JJbxк߷?GgWھ»N/¿7 {$뽜\ξAõ)ßzčt}rprft_y[ƁYNjZȗ^ɦ.dʷ@lTwjф ΂ғϛӤ).зԸEBv҉׉ԥ؞4)ٴR@rZגt#ٴݐFޭi<ێ[ ܳ{F m.ߔP(tQz,Q8xc 3$[OzB;kg*($J:p`(N@se+PAtd*M9oZz! B)cGd 0  M(  jB  Z  p,    \.  kA  xRb q~%*.$0+1004-7(8"7 5 !1!"+}""#o## ^$ $!K%!%"7&z"&" 'j#'#(#'B$Q($(*%1)%)&*&~*&*e'Y+'+F(1,(,%)-)r-*-q*F.*.J+/+~/",/,L0,0b-1-}14.1.E2/2l/ 3/m390300414h141O50252 62j6W363#747y47458758585A9P6969 7H:e7:7:@7d:7:7 ;H8\;8;8;J9O<9<9;q>;>1< ?@R>A>KA>A4?A~?B?^B@BW@B@&C@gC+ACqACA'DAfD>BDBDBEC\EGCECEC FDGFFDFDFDFD)G;E`GwEGDFDFEGKEMGEGEGEG(FH]FCHFsHFHFH.GH`G+IGWIGIGI%HIUHJH-JHVJHJ IJ:IJfIJIKI@KIeKJK:JKbJKJKJLJ5LJVL%KvLJKLnKLKLKLKMK+MLGM=LcM^L~M~LMLMLMLMLMKLKLLM2LMML3MgLGML[MLoMLMLMLMLM MM!MM6MMJMM^MNqMNMNM,NM8NMDNMONMZNMdNMnNNwNNN NN-NN9NNENNPNNZNNeNNnNNwNNNNNNNNNNNNNNNNNNNNNNNNNNNNMwMMtMMpMMkMMfMMaMM[MMTM}MNM{MFMxM>MtM6MpM-MlM$MgMMaMM\MMUMLOMLGML@ML7ML/ML&MLMLMLMLLsLLcLLSLLBLL1LLLL LLKLKLKwLKgLKWLKFLK5LmK$LWKL@KL*KKKKJKJKJKJQJdI@>j@>?@X>@(>?=?=?=_?i=2?8=?=><>tB<><=;=;=y;\=F;,=;<:<:85 85757S5k75674746t46;4_64)63535X353N52524s2492o41741313O131V30302c02(0q2/72/1u/19/1.O1.1.0G.0 .d0-)0-/S-/-w/,;/,/],.,-#+-*G-* -k*,.*,)X,),t)+6)+(f+(*+{(*=(*'s*'6*')B')'})&?)&)E&(&(%G(% (G%'%'$N'$'H$&$&#S&#&G#%#%"V%"%D"$"$!X$!$@!# # X#} #< ""W"w"5!!+  B h (Ce$B`@[>U{;O x7Gs2@nz,8hq'/bh &[` w6Ft4Aq~1=  oz .9   lv +5   ir (1 f o % . c k " +  a i (  ^ f &  \ d $[c#8N{<RAXF]Kd &Rl.Yu7a~#@j-K t7V~Ac&Mp4Y4g,F{ @YVn3n4IL`&g-x?IYf.t;MXm6v>X!^&|:h2a+](ST|GMr= }Jk6|Ie1}Jb/Nb/U$e2_.j8%S#g8a 1}Nr Cg9W (Un(?ߵt߇HY,ޤjv>I߻ߐݖdj9=޸ܹލ܍ca85 ݹ۳ݏ1۳ݟۉv_N5% ڹܭڐ܅g]>5ۿٛۘrqJJ"#تگ؂ڈZb3< ׽ז٥oHZ"4֮ֈؠb|ԝzֶW֔3sR0ԩӇdխBՌ kK+ӻ ӚyWԫ6ԋlL-ҳғrRӱ2ӒtU7ѳѓtTҿ5ҡ҃fHк+Л}^@ѷ"ћ~[к?Н#ЀcG*ѶћЀeϹKϝ0ρeJ.Эϓz`ΦF΋-qVk.]O@2$ ȴȥȖȇyɸjɫ\ɞNɑ@Ʉ2x$k_RF:."ȸ ȫǞǒDžxl_ȻSȰGȦ;ț/ȐSɹHɯ<ɥ1ɛ&ɑɇ}tjaXNE<Ȼ3ȱ*Ȧ"ȜȓȉvlcYPG>5,ȸ$ȱȪȢ țȔǍdžǀyrle_ǻYdzSǬMǥGǝAǖ;Ǐ5lȼfȶaȯ\ȩWȢRȜMȖHȐCȊ?Ȅ:5y1s-n(i$c ^YTOJ E A<73/*&" !   " $ ' )MkOmPpRsTuUxW{Y~[ʁ]ʄ_ʇbʊdʎfʑhʕkʘnʜpʟsʣvʧxʫ{ʮ~ʲʁʶʄʺʇʿʋʎʑʕʘʜʟʣʧʫʮʲʶʺ˿ %*06'v,{1́6̇;̍@̓E̙J̟O̥T̫Y̱_̷d̾jouz̗̝̀̆̌̒ͣͪͰͶͼ#+29AHPW_gnv ~͎͆!͖)͞0ͦ7ͮ>ͶFͿMU͑ ϘϠϧ"ϯ+Ϸ3ϾҨIҲTҼ^itъєџҪ ҵ"-7BLWalw&ӂ2Ӎ=ӗIӢTӭ`Ӹlw҃ҏқҧҳӿ)4?KWb n,y8Ԣ\խiչuԁԍԙԦԲԾ ".:F Q^"j/v;ւH֎U֚c֦pֳ|ֿՉՖգհս "/; HU&c4oA|N׉\זiףwװք׽ֺ֭֒֟%1>K X.e;rIWٌdٙr٧؀ٴ؍؛ةض+ 9F&T4aBoP}^ڊlژzڥوڳٖ٥ٳ" 0>&L4ZChQv`ۄnے}۠ڌۮښ۸۞۬ۺ '5C.Q<_KmZ|h݊wݘ܅ݦܔݴܣܱ  '6(D7SFaUpd~rލ݁ޛݐުݠ޹ݯݾ .(=7LF[Ujeytod~s߂ߑߠ߯߾ .'=6KFZUidxs ++::IJYYhiwx#.3=BLR.-==LL[[jjyz#.2>BMQ\akp{ #+2;BJQZ`jpy ' #)29BHQW`gov~!&06@EOT_dns~ *,9;IKYZhjxy#.3=CLR\akpz$'36BERTadqs **9:IIXXhhww$,3;BJQY`how~ "+1;@JOY^hmw{  +/;>JMY\hkwzy} ',6;DJSXagpu~%-3;BJQX_gnu|$+2:AH ..<!J-V9bDnPYdp{)5@K%W0b<mGyR]it "-8DO'Z2e=pI|T_jy1mKжÔюbm H;av9wic>~ܲ.*|Ren{ymrZ5G|yr\H;)]rypYZ8墾@uÿ58sXnxvg%N,ٽ;ޘ$oDM:Wlwxo]C$w+ȦцPoУ.]J`Vp}xx q7b#M{3tۇ\[ÐUʡ^)5 LI_mevy wnagPr;$+  鉅̗̇жU "U7IZ;gpwyHxfs(93.)$YE' &EX1LщɄԹжmlΧšy]b="*M|]6;ljec܇o@ن{rkhggjmrxɆֆ)5@EP]bouƇƇƇ̇̇̇ԇԇԇԇԇԇԇ܇܇܇܇܇܇܇ !)1:BJ[cu}ƈو ;[zމ ._6l@֌3Zю;=Ð_둏8䓬yJ"(W˟{g iI$+^ŸˆU+7LіGۭF}  @D!$&*e/38=AFJNRWZ^Tbehkn.qfsIuvxxxyyKyxpwusPqQnjfpb]1XYR LgE0>6.~&UH [sf}85XaHsGW5 * a(|zLqU>?us);}$OJ}j=+vMo$hxxke93⼉Ba#t%΁!36oꞷ[:QrY1|)Dm9fV/~{x3p8gSѦF5qbз: ̂"եqIJk=x~xC@3V9j{djS\օ[}4"<9/1NjhEz߅: .:~Ckj臃̱;|/녖ioy {XqX/~`LȀ1~aPԱyc=ey$K[82|6`#MOe 6N/TDO΅8{j/cq_iP#` 3f~N-DTGg37IJyf]"&kf Wm~p@5{V:`^~q<-QgaY6kGmK:E&ˀ: Js/l>7iGnwY+ z<4IDg{KqT. S c1`ORw3Sk{zlV";stxmlSa,CA8SQbZn9w||xCqh_eTI9=C1V%B_/?O_oo_O?//?O_oo_O?/f ~n~~}}||{3{zyxx4wSvTuIt2srpo|n,mkjigfdcna_ ^K\ZXVU(SYQ]OXMLK8IFG#EB@>@BDGI#K0M5O3QSTVXZ(\]_Mabkdeghljkmcnopqs4t@u@v"w xxy|z%{{p|}}}f~~aa ~n~~}}||{3{|zyxx4w@vTuIt2srpo|n,mkljigfdbna_ ^K\ZXVU(S3Q]OXMLK8IG#EB@>V(xR q,V50~%ͅJ ?o'X| [ٔau{( "/,(E b O? 7Y'۪b  S\MuI o /&)DCg:1& tx& p+8;56EvM Xm5BD=*LA@3MՆʙZa`7 L&(#D%[; !!NB ^ߓ K V ;5 1uٙf!(_% ;# .!Dq$,T5X/\F m ;]w#$?{ss[#  ",ah8·h"p5$( 'ޡj҇ #>*j:9KF1p~|o^3 {ߴSmet*N)L疶Ƕ <+G?/ عW۲$' zyZ NH1~ 3;%"|0<"#hd 0# !0`B7#mFtdC~Nٓq˲ܠ"L#fq %GA;X8>'D J۱ˋIvQ. )$O2 {&O,(j%7#d!q,]0%W&:'-bCB,,*G] :.tD/#\B4p" 4H;t4'z#  & E Vt>y>-5{05$q8qg3&%, ,<v{ - #VW  Ф\@ |{ )?AE*:!')%tCu"H*KϺ7aZ$  H R=eJ)sbV}ӫʭtG!f4|(|l>{x͖y0o.3T?(0_ ('WāۃޒڬD"#,9- qc .yN>YB <9"q!.TCG7ޝƢ \o &'WN n&?fI۟9̮,HO34Y! ,u 2)WU!9MARn׻C]c.A0c,T|r}@Y]*'ng{1$YWK.:qAVa .3!3չݹjs/bi t`ς&J0DZ"u~e()I|xê F3 =T+;&}^dӰoOrbf'?̈́h-|$>(PKCmHs&+ҥ2 !:3$Kҡ{ . ?S'dCGAɕֿ&  KA6ɞ"-)|3 10XE4&8; |۾'[$!j90.%s$]۶ڻOv o&=9#68`34-#YsB>!=_" _AݽҫԿNAQ$8;| EZ O  nNCV .3&(+>o!gLۃۜ+5V@ E#!!$Y-%x? |TD` !9T W[, }nFEωs%^ 3FEO]6)PR gr:$G}Q0 W.0 !s,344`U^nomQ, (5CyP@}j go%~cIW/U*\R ;9I:54Kiݭв¢١ x82DQ:15vT9J&ڀ2) 5;H:$UH3*3 ܴG#XF``V("ԡj/#!ԅֳˑ i-%&!1, B#y&#B ci")K"'4m' Ѧ] ߰sI,88 VUP ".f,3>4M&Lcr 09DK8 -ҳ2;-v s'2l$'N6وO1 3B8V%VڦX=0  g,6z$=[Hv4A> y<l!o*-(&v G /Iov%`ЩlϦ5ʩ^ O00u+n()z3(*IDaJ: \ӯ iAH2zګַ~sRM+ Jn{3gTC e%.3B?2eX,,V4  -V|AQ٥dWޠ u:!3W*hn6x &M b/!Gѡ'N,A'vJ:F"jEKTMGcݰ(\4 5)Y' R5K1,d&+4"B4-(x~VŊ@e{%~:}.B%7$$&aDd::;)=.3.j/. E|  LW4@-5"\֍Հ $}u 'MY(T&f쥼 ȥ7M/|fΖ.o >: ';W*bNG]A"thjvZ׃νK%h*5F9"`+A )395y"jmxE~vt`j5<3:l ')Iu.}@1.iՑe! e14p%V] ( k?5cĿh7py eAj r?kVD"]* :#?(wҞp( ]/7(""#[PG%,' `B߉(#.5ZOw>۾ y1(_D 3;?/ɠ?5VR_J<޸"O?6]0C @RM @!/T:fIt2*:EbLh*cU O Aؙ[=C=gL3V"F<\Qawo0:'DR/b$. b-6"f ]ii7kEօ&-J!# `nfX"&^ >1'8-<$WH kh{/LpXII*FڷВAL+bc$(<93-z V2X:a ' R L l*3*]u-1xu3}"g1Z'CFvݑkA38Ea[]= 辍XB) ##z\3Q3B<\4'.T!dfc!ѭ,nE7EڤĎ͡T v 17Dk!##.& gձb0"3`u7LS!G*l%iK Y }A~h'+2,PfL "8y7U)[ʢ*%EFWK"1@!@Eۗ**#Y'48K6b!poެ˟dƊIr=7d#!$&P  I$'9{"R"*Ik^aZˡe2L0 /x ps *61>+yRLA45QK*7wݨj*q &)3&  ho+ L/80PB; %} ׵k-6(w7c<ڶ*k#N,fjUaԽ F0lu +7=8&y͉VRl<"BSUM:u#F 9 d (i7Pړ8FDS8D &nٹ^ &J(Y. !u/?u|-;79-;Ĥs &RFԂ$=1-5)} bszx%!6 1'! ..](C fl͡/1 $(v"˕4!`'7!h٨y{&i+73-" @vpYmw?Nf=ڿ*Wy*77E*e  ,598\ y pȖj2MMK0>񪜿>E@UCWm !f a:H;HE9'?Cd>(3'] $īќݕu E2 9ZV=1^/+-.Z 4 17zJq+0)vb ([>"'3(ȶNI7ٵ&eBA:r;b &zsO@-5P`c*SD85 =~ u) _%F(_A5iͶò!ѣt" ESF@7+&PK 6m7 Im qvݹ_r-WEL:  ө-"&UY_xK(%;Z u, M`ܵ P5 Uz^ +T6*mm 4 #܁. J83(7 V>~+.292&s=ц_)D&GX0 7)dho+?/ _ n!"* *Zޛ =f/ 5 8xM.)wt j o} *K2lGU9ޛ?-(& g::;6h"BU}ħ 4;B$@4, ָB2]!!#xsO %" 1* j2F Y cw#>>0D+D &*&*ԪȂx(ɘ.,$e'-n(c<ZVu s )Y,]"+>VK(/W? cxˤ jI}  (P. CJ5p8( . j(!5 n 0$P22#} (B-?.3 `ږ4ʳ5 őݺ4l3+ 0 |(IbLz(5.WNW9/ľ^Ͱߪ6_+G; g9l*Mzaz_k0&e'kAyqli(I'LȚزZة [2J!WѾ2~K H P  >M X N,33jp!<وt޻b (0÷qj L\Cq$P%<++264|#Xyj̵%Pű_Y!X fئ t:[lJ+fT .,(3IeH$qPlhAc W]Il'/VWFP)f鵫 APMM= ;\/@Bx0+7Z-\|Ӷ'+ *Ɏ5JB =4R DJ6MZzk_4 2'42 &- Hh*& - UhfA0nc_{"56-*I+^I *D4 S$?P Km%%"7  ho~ ;>4ζٍ U k 5!Kɷłੂkׄp?E2%! 2ߎy΍فoDH}LoM,7K29+$+1da:zp~!S:!0Z_J f|#txw3y I XG_Xp J+s,GnT Q=" JΣݸ#68203L:5!-}㈵ᢛk}zq_i`X?PG?7._&? !aۡA!aaa!Aa!݁A_ (0?9AI_RZcks?|_{rj?bYQI@_8/'??~l+}B}||M|{{3{z^zyyyyx x}wvgvuTutttsr&rqp"pvonm,mslkjj5ikhgfed dc0b-aF`]_O^_]K\V[:Z?YBXWVTSRQP]OHNMKJIiHGEDzC#B@?g>3=;:2976M5 42X1/.%-+*)'G&$u##" Jm3Mdy - =L Yi{1Ha~&HoܙI{ղ9Ѩ4~=ɑ(ŵR¾3ۻb򶩵q0߯ͮVR,- 1OQv!?@#BzCDEG?HIJKMHN]OpPQRSTVWX?Y:ZV[K\_]O^;_F`-a0bc ddefgkh5ijjksl,mmnvo"ppq&rrtsttTuugvv}w xxyyyy^zz3{{{M|||B}}}~V~~~~&Lt}W1~~f~-~}}W}}|e||{N{z|zzyyx.xw"wvvhutItssTrqpRpon0namlkkPjnihgfe%eLdPcqbna`~_^]\[ZYfXfWdV:U2TSQPONXM?LJIHoGJFDCyBLA?>=)<:9Q865:421A0.-:,*s)&(&W%#"!Vx Uk 2 BQ_n/ltK(<#yF(3I0,ߖ'8.CT5s>{#TQ;3%fC*A/4B"(R5Ha , tc\Tص#SS ޵ 9A)_π5}ZzM3Ơ #275~6(e9P[ߌ\/g K4ݯ0~ QGĚ0H,9o;#*7m=w;(Ή T)FSf$T#lRƇ I _Cye0ُ*X;[VDj ' ^re@ , wb \S A t"G71 I\ޣNb E5Z7N0N0i&׸5 p *`a P'h /ͦn{T)ݼ(88(JX/$K7ސL)fҪ$I,?֔.<&31ɬan/?dIR۫$q9W!fStҿ&$B ي?3&0 Q*'#岫 _H)lߔ%`w~-\ѲL5 +*=&h z$8;!B"mR!HN!m/g*M?e '@?_`4 b o`28GMڰ +nb320.=,q!Sɖ46#<"-͡5[,2=%yǫM*`}-@;{E T;651̈@&jR'\ZFo l n"2 2MJ G_&l/к327  T!L&,?r 'z?SZ" WS?&4:4YN -Ï79*Ze*4 /\1CEz >}h= 7lq !hϚ!=I܊.  b)?3(i$Qeށ4"/"Զ<}֐ю=#4=*p~2!=d0u9"%+9J )^ ow RJ"ӯ$!,֘ yTV[,tD1ӝu>y>1;F(~5o3R\z-- 7' :!MEAlH/Dܼ3NGŜ`"  *KyAAV e'[%a w *۟-ϮMX&T t3SzQL/ 9XNpHXL}2"̞ (+:Z6QC @USV.z(v5SG? ;JY&96T5V/9 ZH)Cc%&Q jY]sm YC<0LɲH.׻ ;Ru7)HzGͳK9c$:x҂\vG|B'S# ?A\!5EY2~i0өXc a{ƀo1ϰl"R0#0r?)I+V$X &r387nV)fIw$8׸%'() C(6#a7 Io33|KߠK$nO5yb_T) h;T6+&~W2FY9^$d73 ?bk,Y,)&f " k CDQv&ѕ+$>~#! K11}!<Lљm,"O V8 $$nL宴Hź*| 4-饩-+7^!],| 0D &*:{F8 {=ۆ:+$8 o 0#0s#Կ6G !#g"ٮiv6k6ܬ{>F< $NC!7|v)ܺGj @ɼZCXGΥp IWw/4' tGr=FZ3  2@2.0 xq;eQ),!&'B5{=! ɝ7(F/* ӆ?#^ֶbf$ޣ:I&>W !3Ht2#R |1 @p7-Z\'L$ k%:2 سA3B ũ .b0`׀:-`1)c0t)7S,v/6fƿMSЉKB{|K:RE6׃H D1cH hn$.2,Z,/p -ۥ!nW.$Uq?  8G -ɻDy~ ]<*ΧCg^#o m+T<@h}a\蚚+k+ʝa y -k* 2:+!By9!9[9,58WG4EJ;9׆BzԽ( w,!9?t4:P"_9?D6+(#N BIe(y"Ah( ?ҧ48FiǼM-7;٬514QC$Fn_G&"=##e/v/CW6~Sx ̽7L$>{!4T w<< h S*Q"*!r36^V   Xc'4Ž YF2L6 ~Ly+R۴**=s"SVF18 C qO|H$3#z ]*] s }@is*~"!E"5GY*E3$'1 SښUl? 9͹N)M:S'<m! L43̿ ~9#t^ ܩ%4ZC ZF0'C F=[ +Q%/" ۓq0e n,4LFh\q=?.Yb 89No"O%*Ӹ>NJ%V< &= %N&&t܃3l.B_dF6r_;8'z>eK(aY"6 #r (F٪h*O 7'ZU "߾$ ߯oGR`՘4ߪ2T⟗SH:T]g 6 f$ -vt!$cH(N2N"3Y M-b) Q,!W/&ݑ8J;!MG!A!]׳3G)#1l)'5 3 &ТE+ބL+J0U&E&MIr-4 G+M& o[Tv%6="=\ ce.b^ ދ #rBČ-h:uG+(Ԡ ih'^,]!#c7lg {gLM*# %wK _u.cUu8Z M+"&yҧj k' r6c &',Z TؿތG4fدҭKO9,5q:ǭ۝$ e)x~4ńYār ϱJ )A#~9T8h.)<3ǢA*m#Ƴ,;QL:a.>  ׻3+0(^'j 9pY0o\-/z4&?&#aO&_}ZI c l?T(tE2‘Z vp~d !bFD(W1 >49RpQE>( "&r{/ ߌ#mAҾEZ:NjSh"3ש,-ݥ + -R%v26 8&Ѻ,'Sk)2jzܼRQ6|$K_JE0XO+jԡ-a\S9rLgغdu(- #ƍ [aDʩ{ W)ҽ4i5E D{3d""1r-Mj)H<0IBH$†7$!7QT,zڨ.ڸAB (0Az :6 . ,#/ &I!f.2u˧'R҅7p03mo"10K*F+R%-ɐzVn/UW!x>6jOH+5Vz"c|  S- +@t8 d>6b9:;pz1 5 ti͜ tYWJWWTE1h!.( R,#wׅ$o"&QKz<' )D!ƕ slL%IY,#+%̢ V'B5؏MR14,T)i%'Y,w/Vf7&|t P!Zٯ-E!j1&U&O?MFG"x;$)`B 0 pg0&<Aq"?  j1@+bI: qqoO.76X7N%6 hN2$ B=ēW/* ۦM]#iT,*iV#O"2 a)R P |Ue^<'egaQԙb b0; 2,td"F&;!Yyړ׋ u ٧F)/1- @(<@S p'w z!_6Fd& 9]ة/ߵ{3O ({ }#^1ʘ!d>0=?M:=a9̻*KaM>.H<##+R(!1d#M"I:<~.z__L"Ei0 In7^X ۱i^ (^;P "$e$$a;5.ѿ"sh$|~MKc%n\Z# 4QD | ,˹$ D7h oռ,t} N,r۸h/Ykv&Qo*cuS;a12t5#g#~ <: G~غ-%b (jKE.HɥFXAfe,3'V+^QߌؑDI -} /} ;n/47"~<t7kcGJ&}* U-Tv:(yfz!o\2j۰~.?Pˊy$\fԯpJ*1 h&Uܠ9$4f4(ԧAS'C{IHR },\+b2=(TG<\N.ѓFxB;R! &)~R  VN[ < ;C3ș)yW fF}C*j3%!˹=ҁALܳY^x/sBA /hIT_<{w8RG18gGss*oTZ.pb.gu+/X_*ݫ(TG+C˛( @yfoBʨ l7'٭ 5R8 2R11/_l60 F Q2'QAHN|J +tx- \2X BYXʷ$i*@>Gvn4 ׯS!S˴XsmU` '0 .-<^Amd$<.$T,B",.6DԄMv& Q$Oʶ>e)R*Ȉ'߹2p0 A ("U5J!<$SҪج%UQܿAAŘ`CA̳+l!{%-|99@5&zA |=l t <bw0 [ 8} pH  H9 N4 +q2X=+ P1N kuѭ8, qN·.+?A=#$7@Ҿ(v(Ҷ7S1!7=a@7_ ! <Mg˲K`M+ل{$et<  yF 2kBd y+F Fk ]G>}HU'c "d˺-0ŒL<(ޜ9H^ ց1#bji=eZ6M`" "PG̳9!&3W5`|7ڳ5 4C%3ޓ ~<82  4Y[Nۭt]:zv#ؔ-HU۹'-^,*XE$mȘ?9 t+(5< ([,ܾ1 Ձ=]7#!f8)P%ݰ@Il7C2+T.! o۴)f$5p։Ot?C (ph+JȖ% Of88In[Q_^HG1t=,Cb 90`_*NtV|_ԩLR;"M>gy"A_02(Emk"RL5soEɺ/DT<5!0PdJ)G= S/NATR+,8-E"γs e]K " zkc $Nr̻6!!'ȉB}QRP;P:8>נ!vLb 麰IX$":+7wp# :0"P".":%%?VQ4:Z#ڨg3}޸33ԇ"?>0MCD8ګ! ۰4ܛ'U٠*)I b0@?$# *R:I ,w t*8˾- V JUsьCR\ i^`8E#cں a77_!,4-"0&~~x;0[U{#"[FQ4&7ײ)\3+!(D&eԗ? -O'wKyJ-$ctásy)FO3CпO4\ɨC&FLDC,J}.h81RBl9#R DEWhM7T.Հ 8w @LR-mY"c &*ڶ&׺ZE@.(ՙ(#Juyک8_԰M&)X~E PU} Se(a"Ԍ3(/0TWb%?] @VA )<X 9oRnNaR=*Zė @0_ܞ#$s t@"U´0>O?c{W 7$m RHtO?]. t&2-:0L ="@7.Fd@k$Γ 8/+Gu$rђ,@2cB%y-J2}I( `5ͅXҲ73D,p4v O$ %$ RJh+c(@hȷJ('K?y?_B׏ $ aA_-9X)ӱi $<PX} 7)+k^<0B#-gLә= :1 EUoE?.b-!N,"$L/= &Y0&<=7" u-WEyh _ m:<^54xh D!Nl({* F W<37Mx'K/ X]{@z'JE/-f Cf- QSH#S<('Sl&94ug֝*e&5$O0v!xn,` i2'l,B+ҼS21Y8ɸ..'ssM~% N"v'!gYm| Խ(=9 L d ;$RG>X*y@#? B$߶/N <m; z LAHB=PѧCv%$ ]D`{ >ї=K5s1AN-7l 3݈8bIЃ rޭ(ԩ q ֲ8]-q@xє$PGst5a+ɩH++.W "T!.TЬ)E'A6"@܎,97m@H+#73.*jYUPb `R_$9 e.}Uڵd( "Զ- ק(O4$(f'#(+7%e Q*QU|ݭd< &g&;l7,06~=F'D'5#8jSi6wfon'XhƘWdĩSBU.;+ O 6v d<߉i@7C#PtTCD^#q+( cQ #A KR8ϑ3*w" J4? WF>.>$}&։!rA_(7 N,S$[.S_8512+ p!hS>0Ym,,(FW!{ Ւ+:\[ <~2'NR!-g&lc:| .&T!0ʂN!IpK:Q S 4H('mف ׾^b^pѲ.5e-+HWb(`&F*ٌ. }?W*/b5. 9C! b O OysM1_pi9b#Rv~+nG8G2b#Ҵ -* e:ؘ r+B>$-F $~! H+j&T,Y/2Gd%G9R9FD* Ě0Vob@:<1,lPt~+|+)F$kk  kn4>r.Q2=%+e 31C7;;S RBL~ZsA4͌*P #[ hT;\(fG.&="[`7XNS )9AW"Fl%m/_v"e2[־ n;'[v(*)5d:.[  ?%8,R aE)pٚ   `+!W %Ɇ "(͇#{ N3( 'G/Ѻ=L,o5 p&P$ѷGa ;ُb%t@'2V(_ǖK07\ -aqߌX Z*O5!kT2 >"v2%/89%K  L ,?S n) 'W,[Ӝ5aDP@R I#"g&* " ճ@|So4H"] &oh((&Aˮ0Bc:%iK6~'HAGo1&* 1!!uQ7H,(^Զ2%( 9?-+t:;=&9 * T  %wB2 (F#ѻSxIѶ 2M8"γ<*ޚ$F*B e0[ ֮,^Җ?$̽9L+H/-E0 yk #rOEE+n0V*!G4?YLF/$hDn0ܾl#*}M)"  a (d5 k=bml*= ڥ*-,?2+]XF3jC UJH*Nα  4D4'#.h1+OJǷc3ti1 IcE0=5"O ϟDI d TB]"m3}a * 0Z;9}M _C(}n; , v28[V"UHa&HLE p 8+w$Ղ !9'k('3L\$5"@H)IuS()I. " ? ,q 9|&ԡXk,6(Kr,C]G)-"j ) A' Nvۻ!C'80&O>-̾e 4%jK' \y2AbA-`wX l2@f$dC r (*RJ/,H$?\pt {ӬG'# >/)qEQR-7 alJ׏.*(%Jd(Lӆ K~+%] }0X&bIZT*/e`(b[·>3F@b4M_N'$3 _g|i+#33q:/9B3;XUDS (q T Ng *vu&OYN3&#O&|-8= H$ 7U4MP_%yڀ~9lV~~}}B}||M|{{3{z^zyyyyx x}wvgvuTutttsr&rqp"pvonm,mslkjj5ikhgfed dc0b-aF`]_O^_]K\V[:Z?YBXWVTSRQP]OHNMKJIiHGEDzC#B@?g>3=;:2976M5 42X1/.%-+*)'G&$u##" Jm3Mdy - =L Yi{1Ha~&HoܙI{ղ9Ѩ4~=ɑ(ŵR¾3ۻb򶩵q0߯ͮVR,- 1OQv!?@#BzCDEG?HIJKMHN]OpPQRSTVWX?Y:ZV[K\_]O^;_F`-a0bc ddefgkh5ijjksl,mmnvo"ppq&rrtsttTuugvv}w xxyyyy^zz3{{{M|||B}}}~V~~~~&Lt}W1~~f~-~}}W}}|e||{N{z|zzyyx.xw"wvvhutItssTrqpRpon0namlkkPjnihgfe%eLdPcqbna`~_^]\[ZYfXfWdV:U2TSQPONXM?LJIHoGJFDCyBLA?>=)<:9Q865:421A0.-:,*s)&(&W%#"!Vx Uk 2 BQ_}|~~d[~o~}}N|f{ezKyxvmusbrpn'maעOʐfI73/;'851.+s(8%!`_ B~\IYۘf<Au_ʬÇd ƠkWVh’ kݍboG58QĂd-  /g%˂Y@*d'$!  $1AVnݭ5nϰHǠhۼWܷl[ݩ|v|iĔ,,Îj 犽g‚/<܀P$  #M؀6&Z ф8_ߒn n4נѢפ1gD zwÓ.w&ҔUݱX0 pT8 Z3o"3%'*`-02W57:=? B}DFFIKM)P_RTVXZ\^`bOdfghiklnuop(rjstuvwxyze{||c}}g~~,v>~~ ~}|J|{zyy*x)wvtsrKqonmkjvhfe\ca_][YWUSQfO.MJHOFCA#?<4:7*520m-*((%" j6t & _?w)g2|o)֍fXe/غCұpb`i|ğ;E6Xq>3cI+H퀡c5*Uր.h#ņˋ+p~ LQ-&Fn׸_c2ɢΓә$زDsRCBCA 3tA!b$&r)+p.0X35)8:<1?|ACE.HZJ}LNPRTVXzZS\"^_aPcdfhiklm3ozpqrtu$v!wxxyzJ{{|+}}(~~~>h!~l~}}|a|{ {Nzyxwvutsrlq3pnmAljgigbfd2ca_^Z\ZXVTRPNLJHFvDFB@=;F964I2/-+(B&#V!_[O= % ~fO=1/7Lo֤DΕFǷyBy{Ө3q dǜ6/SYȇ'iwh󁊁.ހc8:f3n|k'Çǎ*lnחKȚOߝxƢz6ȩy]I;56>L`{SʍXӤEڜRuC[5{R $So"p$& )Q+-/ 2D4t68:<?A&C/E1G.I#KMNPRTKV XYv[]^V`akcd\fg(ijkmLn~opqrstuvwhx1yyzK{{{|}}}Y~~Ia ~~~}9}|+|{zDzyxx)wHv^uitkscrQq5pomlhkjhog fd(ca$`^]b[YX[VTRQEOmMKIGECA?=;9753a1=/-*(&R$"c!R  w , IgAr1y@ ۪~U0ʴȩƣġ¥к.ZůIEy)ʙr#ܕi<=k0J8L偈5쀭wK)2UHdՂQփeIP) &Lz3֚3uoU^׳e5 wg[SOORXbn~ؐڥܼ.Oq.V}Fm "Cb~ "$ ')+-/13568:<>c@9B DEG^IKLN-PQrS UV,XY3[\ ^_`PbcdAfghikAl^mrnopqwrdsIt'uuvwHxxyJzzv{|||h}}.~~~Or=~j~~}J}|`|{U{z)zyx*xowvuu8tWsmr~qponrm]lAkjhgfOe dbma`^T][{ZYWVTReQO4NLJEIGE-DrB@>'=[;975 4/2O0m.,*(&$"   !!!    %-8DScw݋ۣٽAjΖ/iŧ.xǼp˷*fٯQϬQ٩f.Ң{*ߞ[#Ę~fSGBCKYpً DʈmɆ, PX؀uO1  -Jǹ J<n_k2և6vDӎЗ9gӞU?U&};Ӽn SħUʺr+Хe(׳|FR%|S+iC~V -  Q"V "o$/&')f+-.04235278x:<=H?@kBCEGHJ}KLdNO9QRSZUVXQYZ[]Y^_`ac3dPeffyghijklpm\nAo!ppqrhs,ttuRvvwAxxnyyz{~{{a||+}}},~u~~~,\oB~~Q~~}]}}|3|{N{zSzy@yxxxwv.vuttUsrqp&pMoonmlkjihgfedcbnaN`)_^\[kZ2YWVmU%TRQ3PNM"LJ[IGFEC3B@C?=G<:A9716431/c.,6+)(f&$(#!BQXY  U OIEDHU j*S߹!܊cCԶ,ѣΘ˘Ȣ*ŵCf3ҹuĵpұBM觼nM/ۜכ֚ڙ0Orő*e-zˋ!|܉@:ԃsp#܁\#sT:$ .EbԀ:s>݂4S+ @׉r^ p(揧m6ԓ`A%ܛӜ͝˞͟Ѡ١2Nm٬0`Ȳ:w<̼dYî_Ǻw9Νh:ե~\>߱$ qaSF;1&}o _ L8tS,!k"#8%&'a)*,|-.00121456"8n9:? AGBCDE#GTHIJKL"NCObP~QRSTUVWXYZ[\]^_`abcddCeffghni:jkklDmmnhopplqrrOsstuu-vv8ww5xx"yyzkzz4{{{D|||2}y}}};~s~~~0VwvU/~~u~>~~}}<}||V||{O{zz*zyUyxrxwwwvv}utftsBsrrvqp4pon9nml%lmkji6ishgffNe~dcba#aF`f_^]\[ZYYX!W*V1U6T9S:R9Q6P0O*N!ML KIHGFED~CcBHA*@ ?=<;:Z928 7543[2,1/.-h,3+)('\&$%#"x!= LST Q  M IHJ Sc)~Eo; ߧwGؗmEѱЎmO0ǰƛņtcTG:0(!")2>JYj}ܪ8\Ҥ+[ 1l*oI9=W _Ώ@*':ljW~L솎2؅/݄Bm+산u> ցyN&ހlVC2# +;Mbỳ8`L5tBք$tƅqʆ%C t߉J)yuw !?Гd)Ėa@䙈/כ-ڝ:쟟T â}7r2}C ӫi5үtGƴuN(๽{\=—~fP9$̱͠ΏπpbUF:." ~xrmhb]XSMHC>94.(#  ynbUH;,p[ F!/"#$$%&'(g)J*,+,,-./m0J1&23345e6=7889:a;2<==>n?:@AABaC(DDEwF9GGH|I9JJKoL(MMNOOPPjQRR|S*TTU-VVW%XXnYZZT[[\.]]c^^_*``Raatbccdd5eeEffQggWhhWiiRjjGkk7ll!mmnvnnSoo*pppbqq+rrrMss tdttunuuvkvv w\www@xxxy]yyy"zbzzz{Q{{{{,|^||||}G}r}}}}~3~V~v~~~~~ 8McvxfQ<%~~~~~g~F~%~~}}}n}F}}|||l|>||{{w{C{{zzgz.zyyyy]\F\[M[ZQZYSYXSXWPWVLVUEUT=TS3SR'RQQP PONoNMZMLFLK.KJJIHnHGRGF4FEEDCeCBDBA!A@?k?>E>==<;b;:9:99z87O76$654`433322n10@0//x.-H-,,+*N*))('S'&!&%$U$###"!W! % Y'[)_-c2i9 qB  | N ! ^3qGa9?|nqJw oвʫRŰB%ʼnPq됗ɟ:BįН)f[\p7`h ] = z -;d> 20nciÌWgoYЗ#OHWm0 m!"#q$$$$8$s#Z" S\+o hBOTC ddBOI8tI9 M"(-15 96<?ACEGI&JJsKKKKKJIrHFEA:!5/[*m% eW aXh%0%D e!&+16~|zcs@kjc[TMF@n:4/)$Z 7 *6^$45)fE Ec"!'3,1*7=[CE|GHJJgKKKLKJIHGAE&C@> ;7H4l0,%L% ~ #MmwP| j!"#i$$$$7$k#[" NY&m o!o3݉hEnɣǮ߄D %60xAS%עg*  e 2 ] |za;ڔԦj5hߌȊˉьƤ(Imܕ:=r Q5"IiB"oϟɄ$xz:Иt(v7`=&> Zu?{pR8#C jJ'<WvzitT9!#h^ k}A ,@@'E^E@7)uHAFN' }@6JϏÁ$xyܛQ*Pɺ(cvQӀ܃51^hkg7Wc&~Vx MADW y$h4>DEB2b A[ 2LGkgy*|_C,! _PHM s2Jeg{wbHE, A~l Ya#~9CECx<0@f@`L R`J #$$"$&"8.!p| X@)29?^DGTJKKJ9HD9.$^ +JH $",7C[Q_o{ uYfWJ|>3)5!{ N *P'$/-\7BDO\k{}HziZkLm?3)O4F THf( (2@>#F*I KKAKIFB=?7/$\ 2+19n KdF "$$!$&"1!6ռ|Zn¬f^e.BH D ~ n #  9>;ًͶzSQrӾ1*a=hm@ݦ^͉}# FbP   S eN"*{{D:PǷˋуif?_  1#$$#!< 淾̃I,oc'Nf+)" >E5% 0Bh|E&r0d]xGpFY7!CC1  LSκ w35݃{,0Di 3,BD(i*0>up8]E9d4/*,&!dS G-_ /Hf|Gwisokgc`>\tXTQdMICFBU?;8E51.+`(9%" t`I+ s0ڎ,ԾDͿ*Ɔ־Gi|{jP3 AMȁ#%^}}xt9pYlthd`\YPUQM`JFTC?z<952E/,(%"~hVH=5- ' " _€+2? Pܨ$KjY{}"~|){yw v0tNrbpqn|ljhfdb`^\ZXVU0SPQrOMKI HSFDBAB?=;:b865Y31 0g.,%+)'U&$)#! tW@.  xqldYL<& ~\3h+܋@ח>ҍ"h˫(aė$Lpdzݱ   HΆ7ƒwcx݆c 9?mbwI~'}{zaxvtrqom&k.i5g;eCcKaV_a]p[YWUSQ P0NXLJHFEUCA?>Y<:8=753820.J-+ *r(&A%#" dI5#   ~ xqjd[PВDNa u:g QT{s|_|p_^4:li DgDŽ!@gat|`t:/%n)<3z 6%t7nŠFxpvz|{FtgR9%,{)6H+D[m0y}dvr|]HqZܢ/,w 9(ZƅʊXتܺͽxWTr1/*!ւ"*A쎢7T׺Va|rns=uxz|| {voeX7I7$$~&WoeEϐ h-%e6GbVcnv{|xDXM҅M|їٲ aY*Cb+N1j-C&:!= N*]|_?dԧ+G7>3*l'08@IPX^Hejo,tw=z|||yt_΋Ac^ ֊bJ|aȋ0´0"ƈOi0umh(-m0C10/U,(w$l {2 Ԁں_DbǍ솾n`)|zvtmsrsstu9wxz6{+||}|{z|xu^r@niic]VOF?>5t+!G _+T_|]"7ԓ @f\pij=' !"YORB2)^wE'o 98KVˍ˥  Iqrmqޟ$gAO=χu'049P>Mo8AAEMtylV`\Z!@9[+CF,-:Wo44/+B8# )'^ns᭤xD(=#& `x3!/' !"YORB2)^wE'o 98KVˍ˥  Iq>#01 H'3Q /.G-Vڐ!92.IK8Jev]#V66^&!S>J%8zR@+B(3t! b?ʨ$H)=)c^W!3mߩPNSӎj7<5#%"2,*Q[{B'Y E-MH2\vi3f^3T'8 )8l60*z!|mΏ֤`neg+`:FNXP:G1{#CB.һ•ί#ʦF@KފW<0  q37=?8>-+28;48.r(muRtԱ{TԳlCHFMRoV8ZVY3OA8< K^Wn"tt.trozh[C67%) d%i13//T+*&[$7"bnP ^JPW׫х\ Yoyj̥1 kM"'r' @&B8VA DXDXFBFG*GF~FGA3 *%&\3630l-*&"$7teտ&nΔsz>?/?8(Y \.89753`1/h,)%VPH L)ID;ΥJFC~EtŦѱŠo$R\f<%@2E8a=oBGaLQUY7ZQC{9:GL[?lst\tsQpj^H#7;8)r 3^#/30/+l*'$"Yl,H  ٷ֢fաU` `ր͡#([ˬ!\[e-.5Q|"y n  M9 C#-C599<>/ACEcHJ.MOQSV.X9Z8\)^__6[TRME9?;:z<&BlJQT^huosSu^uIuudt_sq=o;kcWB`6;:^9N;^856.4& Di\ |").1U0S/.,+m*M)'&Z%#"=! ) ATjh݊w2sq46$s V8ݭ#ХͰ9ӑB6  %  * <Fgn ! :S*9pp'  $X5QX *"B$%&$E o e *3:=/??\@@4AAAABB B BAAJA@<@%>80)'  bh j[&,0x01/-+6*(&%%\#!'4@Y 0x۟^.fq*մ汙lbTүdk")2S܉{ĜbXw[bǜ9ͺѻӹչٛڤd{'8?%tf6 Fn L"$%w%"' bNJ!+W3H8%:L:5:9X9n8641*& +'ZV?H@8ABBsCDD E~EEF@FFFEA:/1')#gP!)r29">?>=<;:o9E8 75j431d0.-,h*'  BRGfHLՏ>Յՠ8ւS֖uYG#ﬤ6ǮѿPixeJ G c9Wi*2J8B;=9@BEGIOLNP!SCU^WY[o]=__\zWIPHbAM<:);? GvPZdmruuguutsrrNpm0gs]zKo8J9Z<87;97c2F) R5!6$@'&-00/.Y-G,*)m('%x$2#!a U8u;WNU-G~ cV vc ipiòC+)vC;tvW F !gENmHq/%--/)=llgeT uPfZY1!3'6b>=YSSn7WKaT^`R 23g7{HR];0%M3v_曥h{kGhϳP+D&)`|d4a0r;Ծ3c8ޗǖ'1K櫑Ҝن*D _8ìLq(7fo ՖǼORi6_Lp2&9VONc>ضfvJWta@E?Y]oo:K{Sy: Ypl^ E Jef`[@̦ (TgWE?=dccT^dc5CT%#%"@Sk*cd;^eAMoVH\1KnLUO[OBؠ7fifd8a,TR"j`Hheb9ʔr۷6]W_eZC@Ʋk2 ,Ѷߤ&4.+णȵ޶(#tg"ފ׬e|t #V$SXPoUXjE$ُ,>xVZXWAƽ_ Ց(Hs+9X[NP& ܷړy͒Wyt!pFO[= $ŤXiѶ 32 ՜EݹhCڨ)B.K"F c٠Zh>%X 3΂5ѕ D-Һ՛ۨ&(15W62$ܳZÝ4NKV?ViT#TVZ\[XD ب1.L[`Ca2]QS6yҊoAνRkfon/m7jhfhjilblkhnS׮v!#IM]efld[Eؽ֩@:QSh0a\4afhxgf?gme^FS?NtV`aMu9lHƿ<@X/bdc`S8O׍>P+XA7PN3JkY__@^W#Gf+ X B1TKNx&+ 9 i$BV\[[[}YJN8 F0NHwGT]%BPQUTN>f5ϿV( QN=:EQrVEUeSS(URJBDNB 3־|/DK"MHv<!&A'M~TSSQLgH\GLJNQPLR4:scW %374)m|뉹͇  YQ1;z:3 -)Y+/2++[Sɡ.“߸j[7aG'ध3ɕ=  Ud̪uǭYvzPU9tws -Ze< 6%Cx ^ t#8:HaI?~0!{w|`!*28g< ?Z@@?=1:g5q/(O"  %0$?@@@@@:@??> = <;%<7==:1CY ܻВcȊ "\; |,49i<==>=J=r<:84>.# @=pאɝjR$J#18U<=@>=L=y<~;:%:99999873M-"T[i ^lgfv~ #$*-0000/!/- ,>)$8Sɼ4' ؞}˺.Pe ~j tnodJϐɰdCK5(Uϕ;UT<2lHh– w‡ت/vB1ԝ^_[d^VДԹDp[Ů3°ް(ɵƷb .cIZ\b5eeab[PAl-_}њxˈ˟͚طRh*?QG^dffdfebWA"jX{дͲdѽ>dQ1%7Ŧ߽6 .L=ahg`a\[!]Fa&efd1_%WN1E=Q85t58@=QDLU\*akaQ]UMiGE.IHQGZC^}WzC'&Y `:2 /r<FhLPQQiP>MG?V4C&(޳ݎA? 2JXYN=.$,!V#*3=GZNNSVoWWW]WVUWRMYF]^3,r+x1/;DIG >l.;, H7k {~dv!2Q@H_IE,@i<{HKOZOwNNUN/J>P+-ڰ=I|]L mէήo+:?JbP RQOJ #I/O5A1.+Xu N#4?Z.$)Z%o4{".'nAX]cD EFE&hW* 6'0O38pWe?c ;hπt0_2ilQ 擿>Ac6dE> F+ "Ziq[ݧ4]\@+I»/Y`b/.7 i/+K; r<;,1$-3?8 Y(U?IT>q,`>t!smXe%R&+ +CR-!>   H$1 ,)  i %XUs\[A=WMQ1uQx$x:Q LBE +* b|T*F"PR#"D`b+ /at{oL= !7Kׅ8@D{C$$"4823V ۿ'U\W+?+uٵ 260e7?,1( p-| e4o!Nye&q%K9!GgY-c9:9faUdFF('1I<3$WR%(в,,2U>b=Ү_;TzLmdQa-5vmO2<5G96sU}%vã㉵bݩgOHk3|{ +)>89 &X^vA1\+yOj:#_+#ҎÂ߄ `}VwTм =L|%!u. 2:(o2,\:Deؘf@4qÑ5uWݤEx^=+re,J- h0<0[$cx k rxt÷˘Շ:͒🸯bςdj  %Y:p ^1>`Qm`2py|o W?5) (&A)`|Ar_??,c/qu=4qʋے-P{C]uTP>wn,fXN 'P.^ 7MUi2Ku#{rV['Ic{)0PYhU4̀߃K*zrCޓ_{~|wlV+-@ϣ}/*6AHF;% 5>qϴ,Yz {p BEkz}~gh~+}zv}nauL* R;R(  $(*)$ 1K(q p Ҝ:fPy}~V|~"CO]mtx{o|p}+~~]w/~@~}|7{tywso4jbXK9!EV'ה싞FÃawP6"    /OĀ#ZJ;xusɔ򛓥Oگ9V iswmz|'}}~~HI~ 1BZR`kt:{~oy rh[M=2, %L3@L Wai5q2w{~}zzuoFg(^SHCFzNU\bhmr{vy`|I~~:}zw2toj]e@_XxQIA90'P K> ?2$(@-16Z:>BFJzN3RUJY\_behokmSprt{v6xy({`|k}I~~h~!~:}%|zxywv2troxmj6h]e`b@_[XUxQMIEA=9250],'H#P K!wsm5ʹF:7+(ui~m >0 .#CBȁȃQiZHϝ&%;1o6? GMcWMZj8*2(!OYarjc^%TO;DA2I8lԅ̈́; ]  C#. 2q<īԞؤFWNyjzze|tJtxii\[`tiัܣ ܮ OV1 ơRɱ3&Txoyzb :u@2SfQ8 5wL0`6OgM+*V(`-6/,W#RIؼ/4WY e>=jۣY!f?|<a{Zng""};60vXWN 3K&@a#QŋN5޽Rʨ뤳ޟ}ӓ K%óΕҨҹ0'+ɉ!K uIu(>=!QD* P" ؎2Q[lGԺ~$ʶҾj]і-U23 \7=3OE!d2f/yb*\Q U+Zh-TB(:,. T  +/,Î #%)*eFbG@DCDk<3 $/("6pmh+_4(% &(.+ %=89s~hgfgd_a)YOL H:&6\:X0\KDgU4ѽj'f̂룛H vq>["ݟzCͽ hR-O`8g.}F:k ~, Kd,UEH\'I\Kp$Va[\*yəጦ  ܧ[[@xͥ# a24H <I7V? PbxIZ8e@UPFE5\hb{FF5z-e35|Fsj^ k1JQI&U ' > WJ z.3=#A2r/8(T Y&0E,#F; k0.6e*172- IE9-.Y ^?@zL3# 25ۢ׹{.<598 M_:wE)k-4eqW!;\; ;,,:98,2v j&Hb_0 R-ݬd " u a5u+5&>9 ޡ 0O?1u`&.1 &*-7FZnG_)T9< !_y N9!)s l_" 0QLl(" !### 7  &*-x+R   "5r='J D/ < 2-9$O;#=b+"CBr#A4I 6$#IW@ ,># ڊ3-4#E1' .Oރ"D?lm"'6_4 06('U q}:;'!)a<+/<]30#5ux1TO+,0 Ja=I, 0sb$u  V!?-i8*/ < ~~ -"('>I' н{7GEEOFFX=wh@TK2YV,9;"y3L_iKQY2'7(4=)`1yT]}A @l:&5V,7ԧϕfPi1>T"114JcE6RarWa=8/4G={*1{ 9aOgF8Z)=NljkU+Z; 3>,8(W*,%_"7xFM-h k')*r), qKN #V. V |!(0I \7=u-F+&1$L{F!2v. _&F' 07y2csM 2r"5) !%C]kU C)0#/6,oR ?1-I!+hr* 9H.in+06((( 6-- K@"+8&_"7H^o!5 D? X:g9L~)#S6M#-0/ k o+u#-!f?N2Y!vlxr  4sD%'%!u GpFG1@ز | ],A9C }(8T44  Un9"\ p-I(WS`"c'|'479! tCro% 0ENX1/ S3ߤ*28\FC96/ 1FkJ($0B)!BJ ==7402wA EOMYH,+^#2Y t$LCEX"R  0!30=)+ ;]$41R3)/! 0 -B@w s$0+! wd&2=' >Zs0[24G.#<4v 1!&w'-%d '+/#* i"} 'C=i r#64"mX "@GHP9)' _)e<=e$;I6-| 421!]6AOC B4-J@s@ <4lLF#rO q+3M4>C?- NaM,&e*^h}*: O9Ebo0n)g9!\7*XC F)< B)XݲHV&7oBEwD-{ӡ},@/-[fk}LV&-WgEI'm?ؓ0%1CQAZ> #%N=K6 Tz+p>(=A02j- "z@+X_J( Au=F:6=S8}/ϳ t7ka7"xebg(_+QA_r8= )3A<3+@qJ T1 ޯC -6QiCr4r#N(SRJ&MobO(`Q,_?=;Y}U0*38U[DN΋/YQ?]@L9N;r[+qwl{T?+&  #&IYV =D- 9"U-&6-W   ~ &' 6C7/<#,w% k ?T VdOZ&ѽ ;]b\!C(H ڭ¿8!V/RE77ݯ4lY|68,ȧ̂ (@P[B*/NWt# "+&H2F} 4"54R^'$3F}=~QI-x#,1[D!?0 GM#1""1;'G +:7)"(0FU '.9,%~)T~4Cp.. QG$ F3;=G 8i=h6)?)!#iZ1,!!. O!*)nT 1p"'R m%<08K;-" 1:(EC7_&)#'6s%-*+/ uF{%8@#iF *Z >nl4) 4M)v B7 +4"  89%\l#2R+r-2 $p0oA51'R mJl0'M`C5s0EN$5D JO:-83/+)-/$T#rJf D&V{g585.#*!]QI֥ c!1LU1fe3,3$&@J   t-}1LXYP!/2$19b-:f2N"25l#.#*.q<=3xR,TABe<) |c B@ 7? F)&&J!R)/">#"v Uj57O2 J\%1B+B"!9m   #65(,+o/1_y %:-:q6{F:Y n38B&j l/^F#k \0)`<%09# w++" 4jQEt:P KQ+,":)q̎Z cMhOD-):*&G |o-A(d1az%0+ -7)9 /1'O&~x/2Y j5s;PYb-$'$<B `$G'y -r 4$S F*x! D B1(]&!,I% 5,{ ~*#m!12:Hx\,!n X2S>)172*kJWC-B,"7*T?'7Y<8t*Ns c%mK(*(o  k %,28>+%]0EKk; )/V:='|=^_H"F" .  $0Dj8~u>68a'c "7)xA;1_d*(  \J5|68D$ Z8 m["eX( o+~!3 `Pn!C"!. W"  |(/9l5uRy7$Q#rF"#3' D ;p9;G/{lH+ (->Q 8^6<X&G2 !,p k (Bd2j; ܅'z2`&.>%lЄ} 2MM `/My ?L=4&{O ;Og_2!3"%'a!e)45Jssn( 3@,PS."= 92 0TF#0B6IT(5I4 ML@#@Yqf*0Kb53Sf9,)%W1E-jm,A;)_f. C";4+a 6(7/)=6];)0#$2DI\kXQ9.{3 e* <%ME E!1<>K N3 |&"4K;@h69Z&6 RA*& uG-zHmC2B$ 9e*4LO( e0תZ-4J 6 a [ c/DB`FB$3<@i,C#!I E~4;3O)U) ;;6[4Uz=T33H!(1+];8ރq6) 3/+%   d d5D3c8,#4հ 4zSI_beҞ-A B&K 3L +~.Co%*|%Tt# ~ )-*F28' "=<T(.9%%0+J>{.Y| ;';%  L$*\!$3j6 3r)) z#P$4R &]b8@:}02Kv[%67+*~)l+$ & Ue!~ Af -5{&)2;+$!c2F$9l+ E%e-4/(W ?3B.)SvF"#1Fg36/޲(/N",,>IZ@;) c'bHەc$>B". 89dN;+q#$7%IC=:"3C^>$gp quIJ  (9 m\ 7L1r+ O 11' },T5$S {B b<D5&Yuh] /C?>!&a3)*&17- Gm  .nED~6f*&6$+ {m >Z#%+a$OkQ.-2'Q 6N #&%\A(  ''4 )&+5  $ v] F>"g9rw A (5a`B" y$4-1-n49a4g7\0MQra32,B(6zw 1)'%4 M4C.s w#57*YI x޴I3,@HEUBnA )S9S0(F 9[E}7M;!^1P\  (B*>-9x41,}gr0Jg-J}  / SG?K ל!9P6&l39/jW G=UyB* nqI'!,HX:mLp &W(8- m I: $a)'" 09/7l+ G2 Z51H) S4f=+s"P";GM 2I<8%)G^ #-b*33[U  4`*Wv4 W($P @ xETO:XGH6הwրTW3}^OL9WF<=GWkZB #q Y  %9O@0Xs3&HK;4"3NՈ@*?NN!? }7ia\E5!G1?5j(`"?#>3z8;:a-.  ,SC ߐ+ 8$UQa4b(xdjFI-7Nd0Q8 CCS0# /*zqY<+GLA$gW-K#<+)6@6*?3!SE]k='*Ё0$>WUN.I5@;oR/BxCO # D{Y23&K.= ,/ - R0s*c"Ni(*3XG7kQM9,m4069]n1 ) Med6yrAa6LF?pX"E~O`m|-p:UJJ=$p9p! AJ9,1/EZ$'WC$S:<![~v+/ZL[4 p ބ0'X?=M9EN6: |i%:PON:&c (',"<'7"1:.- +&! 6%4#z-Z)'m /@'sc D, 3n!!" Oj"><i /$9d "R(olee 7'.Ex$n -%o] e+y5)9 /=^Z+U>$]*I*0$cw) :.3DwR+4-1 $]2?"4+U' - )-g(-  tO[ +*.?.j(W0%9GK5^݇|"5HZ< 0 v*0/(C- _~*'gXH1<'_E߼L&E@epXE3B"{&xBS=EW9 E~22z&2C/jA#9*0 |&&2 %JK3KM(1G+څ'jFJF~G-/o%1t !(BE<1KDK! 1G6&+.vQt B 57=wG T<7+6F5+M-=;?2+'7*sQtE),,s 8+K<.+| {u(-&"$P {(!5NV@C С/f/ Ecf?NwqO > ?B_Z[$kݨB+W%D01?6 ɳQ^KoG 1 ^+H)__/:1$aH,e\'2[2OLqjRH @Mߦ^.>6GX3ݑE|&MC:SCWƀƞGpPEc\J?+~!/eت$&DU99z<62=44$5[@6gXAVsE7v!?_"l?CxjC/ni4J )a#k5Y 5p~U(i#m v,# $ | @%0@GL!r6lOd?gSv5'2--/GPP\'@ n,.%sC m$~g;6'"V! :Lr $J}L+9 k_CGLD'+ #;md!2[+^W'  +%td'*?.F 8 2E&N,E9w8$&8.3% w6'I0K9,2~%< (:t?y9&.`(i..@/_R&&44GGI00;ad0 .6GnJ&L0+#8=&%4C?l#D]4y ZB/"&GN$TYzBB%:^CYʄD4 KMCC$03H.-y ks.L12  g/( $ &$qs%8% +S1 =IzV G?8A(1&9dK &NVDwߣ'le AN12 !}+& "2 #s&|*7 ])mp=3E?b.>[X D'"a$BOF.{ DLb")9k2|po +ABF;Y.5)[.?OL68bIkbQr-3O?*$T0B 9'FJYF.PMw<(RjO:2n 8,se3GM>n ;m-41AJKWd`+dK F9 e);!EU-l(' `'$+9 gr>B [#*}a\F8Wk2@ 33 /;{ /Lw: & /$ K(%?'1?#q jiM  ` '-#;_&]"}&065p^]"#$e8%SQ^ }7a$~$ ^#4r :'S#S2t!u!%%%Mz[[c{_1!)5>,fO4szN:[" X!X/pWP[-;C!X K8*a.'.n5! M3c?5k((?;3dw=\O% !kIzA&=X  : $Z %09"#[9 0 / " #!G30G+04fܫjM` ]T)+2iv~QE31WY5NU-WN;F Q~(HD%  5@27ND1%;2J;r:H>0>)'1KRDY<<;D6$)2 #J1@  Bj Un y"3 NP# %&.0uH DD3(7$4q9C9'8,! 6S3(0-"  B!06'#*E):[9r !S_(ጰ7u {5Zj^i=9OiE?EDZx 19!jR*;T5 '!he(E? t#^o1;: W}` # 'Yj,'$ t ?:sM<DW.Sۻ='JJGz' }:v> m"SAI!Znmfw$%&) |` #)Qgc, kG)^/ |; 6%S2>1.W-4x@C) wq'*"5 4C) g |*VI#OCZA-/ %($ $'   <.&T ,15=-JAL!5+' \^!UV I V& &,H$"c3 `5*t8W255had3['`_k&@  rl P >")N#>-r@K'4!3K =+]P ! ;B> ?(|-/$53 B*> Z;f1 } ~%!$ o  0*o" l !!R86; <\%2]0-H3,-ߡP N4K5"B"=bd $:O>l K ؉718E!8b2i!J>rN; Z4H_%$!<M0j+Q*a'(( )W9M2 ,F(n" ( b m 9Z72* \ m#" Ye:-#+#w &w+ U*hoxOHSI $ "$ ws% R *^[&@&3Iam[D!&s0:0W`@u/ 0 G o AHK=r>#'+/9n > 7!%"v  {Bjq 3 Aa T +v$D\GU-1 I D %%Ze!52`&%4 '1$U`: k9~*]77*o>*, 6 r91=, $,$o& 4Bxb, "#x'($4rG6'' i!+iA*! $['*71+:dEC9y,&uA9L0@$1!+ &-5|$ o iM/6U>5! k};'U"` Sdy J$=2eM%L!P b(BT0 E2.%!Oc Q*,*#EwI Z)'l gE>pwN E2,2 "R] :26 B4oG&E-o2 $ !'t9<(3l|B=P,A~+D{+Ts%47"Jk(Q Au$3 ]x&%>+/Es  ",2+%;#8>+2JlD 8I "a3&F87!/u "/5"9R w6? "KkN Dm713$gO!b.L* <{ >=w*:*&!\mJ9b?0 '&;(A'. =mC2R*#Fgm7 7o 5#@G$.VC&7M16tqP0B2!Kee BM c##s9@ .TS7$]lҍ 46^l0bCz(82'4=Z4P3?1&& Y3K11Ir;'<)S%0o"61 b~ V /+*r)<%%=D%"u/!1HsV$"7,b C*}%m5$2, 4 %5wT)(q)8#dp }^V/c#$>"?*Z?9>qZO^1137b'F= 8 (> $~hd^B'R.9"  ip'kD2B ?ըv3IKG,[ u- hb16F? o[rN 6@ l+ %T N p-p'sE!U[jS;'Y5(-88?"]WSJ%9S "Ӑp75D19SO 5<U .E:i,1&m1?3'Z ` 4# 3~%r9=7& $GQO#!+[;72!d S<6+rRUݤM/"$6f1 1u b<<+f[sg +/ '.j =+ 40  #)1&),Wl?{J 6X.jo  v/EB6!RR4E6#n,3jj@c#$/!U 'GL%45%`B ##>&/V=U%@2 u d o3*t45=`H .t))0C4,Rr:rA ")0a)vu!QVH!X4984+$&u'1E=.0!SH K5,&G ;j-R!% z#7c.)1 +*^+k/ &! {^ F % j v uK#| /  U$ ,<6'?9o'7#C/ZQH"|.' $$u s$/ , ^ 6):&  3%= t 5uD(. &( i#z 40{ ` N00  :1 '6:#,/g#AG)&33 a"$/>$.h,E.3&!9$DH+! } AJ3oa *"H)4o`^  C?YE!Y&sK c]^ P>=,$SS { C y%7r(R%&%0> Yn9$"I;0 y;p3??%'*|<gV9E/SLB/+*z<m#/m (+ll"&VN@"0&0-)w?Fv?9*?-I'Nbݥ5G5u2\-nE2 #%HAlE- q%%+?!:>*vQ׷6m)'2%i78Z Qlpү̷̤4юR $+9DyP\f`t|<.yvUwtpMiZJ@2# VQr~,*jpF (- ]T+rϯqvIۣI%8c{0qV4kp݁edɵZ(ނo!0;CH7JG@4" 7rHٜǨ41`uzrX7%߶*Ƹٵ۶}%.)Rl|x$iS:h!I p?'Бc4Iy&09@EHIHE?6*yvٍ0v1Յa³uؙ*N^j{z&k V=$ :OȽ [_\yocTC2!tK1ܦҤ߾(yεWxxT{ʵН!'=  v"*06,<@RDGI J J9IJG?D@:3:+R!h.+E/3/GI7NKbZqM{zqDeVuF5$r9ŝzڵﻧ4čɩw2n"LCr;2u* " T>܍bĞ- 9ruGX׶ qvŻǎʏͼԜH9t{B =op"1&)C-036V9;:>^@NBDEFGHcIIIIII.HGED%B?R=l:,73/C+&j! -ҭ2ӸǨKMAesgʖ sTqs.)h6BMOXaipdvz}9K}HzDvXqk.e'^VNF(>5,-$gC[ dg/e$̑YyȻ&ϸf =M+Cֽ˿PƠɓ̳yݿ_v17!9%}44"V;qg.Գ{փ`- ͑^5º̔ 2_4}$m'' 6%0/;o>$bcA ۏo2۝W J ӵY+  a*,A1 2)].*;w E(L#%Cyu 2ԨaL^ TߺL3 f1$7!59'(%=D}44"V2>;6qgT.iٳ{䐃``x|- ͑R^5ºժ̬F ]2{_ 4{B}$I  m'RG' ~"6h9%"!0 /(51;{,o=>5$Xb7cA|  v_׏o2SŞWwo J ӵY׷+ ŵDl b a2ff)1-$7!*5;92'uR (q %83=DE}44""$Vt.2>D;(6qgT .G i+Uf س{iQ``x. |H7 - t7͑R^5ipºªժ#ܬvF% 3 ]f2{/_ 4B{B]6}$mI  Im%$T a'%@RGL<' ~"x-6;h990%"!)0q. _7i"/h0( %519;(6{,t! :o )=wG>$55$Xa b "&7 CcAQ2|8 _ (v'ؼ_/! Goݝ2ۓSxŞ W/wX 8o0_!Z).10-?)$ .!u%*059;;9u62,y'!m+ P &n (*3=)DUGEG?n4T&<n9\A P"$" U  D%g. 7 >BDA;(3{(0grtWg4R X * D j0ߔ+bΨ:ʱֻe(-h  ,ݫټڼ7ŀnurdmGx-@  zM#)H94 * rG=ޒ/چǀ<4wUe=ߊ8;w"Kg o#6ֺڪʨ7̍}I$  ,/ [ e2u{0`T |)4<BDgB=M6-s$gF vf @Cd"$~"P g'5@F=GC;<32'4  St"'k-x26:;;X9c5+0h*% ! %)-0 1c.( Z7=_")/1Z0@-(2$| !%l+'1669;;961n,&j!i  48 i)$4=DcG`Ex>A3$/W71#$a"Rh^ [aC zP&8/7>BCPA1;C2w'FN`|]81 } $ @,y,Ob;ѝm7;/`g>+BьIvҤ/9b#d?ա ?V+k0Y){-^ g ՟L6+\jlBVC %}Fr @{,/y,&Ob6;ѝƯSm7d';N/`qgWR>*+tHIBߌIhviҪؤH/&9Mbt#dPR?+աÐ Lx?1$V+n/k0e"C Y )r0{-(^ mg p_ۤȼ՟;♖L1"6I+\]mjlWB)/V V-C %s}PFr @{%,0/.y,)&"O\ bk&;6;G,FѝƯmSXm 7Yd'R1;DNHW/`hqVrg\WRH>V4*!>+t@HIJBsiGIhvipۤ ҋժ^ܤ{H /&/9pCMWb[mtl#d][PR I?5+! Isͪա'Ð& ƠL(mx? 1 $g(V+-n/0k0&)e"8C } Y ")0r0A/{-+($^ mg p7_+ҤѾd՟;{♚ԴL1g|[v.*ip;Xސ,ؤ b\0ԋ תt^mޤ{wH  /o0"&L+/49y>pCHMRW;]bg[mrtplmh#d_][VPRM ILD?:50+&! aI3Ds]ͪDաp'Ð& qƠLBҚ(ܚ=m|xS? '1! "$&g()V+,-.n/ 000k0,&)%e"<8XaC K } Y9KXU6 |"A&)W-00r0/A/t.{-_,+)(a&$"^ m g p~7_+uͤѾԹd'՟;m{72ԴL1Ig~ "',16;[@&EIN!SW+\`d2i]msqzuqjlgaX\WQLGB=83)/{*%i!zXV iVF-2U<UػC3ЪE ϜAUj[%HniysJx}Y=Q=!tBXԦ!gi% Sԫ)ؚڌܣ6T#iEt a p("'+d0+5:>CHN6StX]cmhmLsBӪIo"o=Χ^ (a^d]S3^pc3ȀR/"sΊtm=v6VhgúbWѾ?[߭Afbw9~)&TM[jwV% 4хWlcRP}|Mjd׏#2DmG} K=k BC?˔jN\Biv&̩#:ɿ{BnH·dZaӪوmIYꟿo0o`Ebg:v,"DkoKegt/ϟHz=*A"`Χ`O^G% 컟n,2(I6a^*x}6rdgbzrԀ]`qbbS#/|P=35Ŷ^p;(#<c3@{R$Д0Ȕ߾l)܍tR{{ُBюn_H#ṙd×ZaӪ2rو]|m\I3Yӟo0os`DEb3g:cFv, "yDߒhk yohKedgPltƁw/"Hz^=G$*(A"`rΧ`2OM^?G%5ތ \컺n6䀘,װ2M(I?"6Ka^m*x<}}x6r kdoagbizrF]`qe dV޹ۥWDө?Ѵяsf^^VK;Уj"mΆs$g˚ʾƫu)L=L68F ǟOlχ${thl"d?[QG=11& 1qT߭=}$1x숍}yDrwi^QA-ͤS?d}ӮCHI$fb ڱn;/@ƖǥJ֎P~wranj9hRfeddebgilpu{s*$d'P-bB_~99hSqexs|TӎD%w)* * ((%! &Ɠ@Ǖ<q\E.Q.ںp~l$[mK=/#( pcס\]ųaaR/RQ-J)/fb( {B_UA$o.8ZCTMV_gntx{b}}|{^x'uqm[jgbdsb\ahabejDr|&9`[ wVȆ)JcSPɵ'(Kf!|%К=g!ͪбDl>ھݖO[/5eX ۀٞaռҡz¬وm&B1rՇYVlxZMB:2,2'="ly Z [ P;t݂u٭/=ӥ5ѩцqkc]UE4ТdadR˅ʨDzƎVã.ôWʯ9lP<)zVmXA*.䶩zh(XHz:-- L ֱ̥ȯҽAϳp#D̬sO{Һ- T?8J%S0:E+OXa7ioguy9|}}|zwytpLmifc!b;aa9cfls~HݽSg⮿j`UIXʴДf$0>Qk TD5Դ؈" ޲!TUߴ;#+9=/˘JDZݦ>g]7H-U>_fShWK@81+M&m! k  ikg[u*r0i=Z`nQӚUb%\>0*()B*/d$:630.1-+*)R)(()(()B*E+,./H2 5c8d<>A3GNWbcr8(I2u((()Z))*u**+',,-z.m/r012845h7;95;i=?BrEH`LsPTZ_zfnv-`܏S,l8jUsd`?8Oaok|̗۩n˹ӼER0jCXP*ұU_7| ׆SZ*֨i֜#աQӖҿϯs̚6?ÿ!<"{U}uBhWCr*H beOMtk9B% Fͦ;=N{qib\DW~R3N^JFC A><<$:;8643,20/..1-|,+9+*J*))R))((()-((((;)))B**E++,B-../1H23 56c8G:d<>>AD3GJNRW3]bcjr-|((()-)L)n))))*O***+D+++0,,,L--..//00!11Q223_45567s8d9b:j;<=>@`AB7DE\GIJLNPSpUWZo]x`c%gjnsw|‡ Zyb@D1}Ois؟{B}zHq½t=vV"Z+˙-:FP!Zb?j#qwp}܂ԇwŐƔq1:ʧS Ķzvɿ ?dt~|nV3ʈ@̖8bk`Cҫ cӻ]ԭ4iբ?h֏ֱ*9GؚA3# ֯~W+՚_ԍ@ӡF~ҥ0Ѽ;д#ϏPͬ>zʫº6xe$0*;ۣҝwߖy}zt0nf^&VaLA5'XrľouSC|I?皯4˓\o"=<:99837\6544[322x10S0/K/.b.-- -,f,,+{+4+**o*8**)))^)>)!)))(((((G((((((()0)@)d))))*U***+K+++>,,,N--#..//00)11c2 33q4*55678}9{:;<=>/@AB\DE}G2IKLN QISU)XZ]`dg)k)ovsx}y 9Y4!D)Oo$4!,Ѕ.ބAZC9Aϱ @/ǝK]ծ8Vq-?ȠKlG6 %؈ ,7 H kϨ?-b-3μ.NL +Z% Yh. &]6c5ǘ')E+>b%F•Z*/F!gaڄGE͈5\;5#$u1$Uj 9g& [/.ק0b|A?{4{Iz'%@%ܞ! "k͂~8enu2j!|=F VS> Q7 B+{ / X," *$ئ1(!H`Bi ]>SH.'V!:ZwD̻ 4 (;M\m(߳A9481;r %[8nӦ3b>cJ۪h zI0PfK6$'r`iZH<pHX> {Kc.zO:Я3  yM1zlwwS%#Qt ;/!¡b,S;62IGeV  H/}"ܰZ+$qQ }Q%_ *\!"1v'eI]. IdsN/ȊO=@. T?., *2!4zֲA^Q"tM fƸ!uvԄ\Gگm j,By9GʘtM>`n*%0A6qps]: ##'):b\-} )u76vR/ B?DWD^'+ƅ,QX wB[M:ҩQ,8;':_Yd'\{R+3UM!xL%]%("[|X+"5=YDZ4G`ZIuB #+^$zТ#7 -C(V*umߤ Rhx=V.vq\;R@ʯ!A[)C =%ޕ(-0 Uj- / s޶'f/z 2 j5~;"bf)~ PL mx =؍#V c@& 7X>y!mF]Bs:] " c"%Ȟ=zL J7 m_>3&"6<'u\l6M˱&0K'r_3O $=!6=_ CI )Gb*lC@K*5c;*,\U8;Nw#Yrxމ{ ) 03Ï+ .H '*fN!<ߤ F  _JMW b".,3D IIB,D/" wj*:`r R 8@i4'ԑ3ʲ :e s|E빽bP)DTa<Oq sv`Ю'}+#ƪ<;U(g%Mr0rٖ'~w" JΐA%j=.X 09)1q&L7:Q3U%}8Xܱ: !敭W !S!9 4+E1~!: gͰ"/M30^Đ/ $^y>;y+ o%1|ۻ e#;lپH&LZ= ) eK%f_rw\UO1Ci#nN)I7.; uBܭ54~5%(v  (tg$ ,љ 9VO WzL[i - 1̞+ A)a"4 $#[4G6K_F$  ݺ5B2%/BNkjӭ#@&RP 2ܬ0FJ#,޺l>ށ } !"@% Z)(MHVjŔ #&h#$fO1&&( M1R-`&j2ݹ!['?>,d9}.Fj 5] @)l!eTbm F& 9"ڝ,^3& "S F$UM [ 70(5:K1/vّ(տE ,~ˍ7:;J*hʷZ+gT ]#qEɏ <-84ZȴX mq1}x& .b,&+f,B1%* PzB~'ǘ#z&?  !+!4512cbefx wYB(L{_PT'ׁ)"24C˶pL\O\"^B!R&?_ [%)c#gI 2c+JNpޭ/' tD.!ȱP5vg6#r$LĺUXe;!8k' Q6+m1`ά0TlC*%A\2&>Uj/$*)u76*F-m@ʲ2:"ϨLA36yɨwg~Tҵn~,$NED Ip{1f)(U7& '%L&87d#D UA3<_ Ul( s6߹| L/(`Bn%2$ 4C.yl* G ݪ"Jf_T6F8(`κ9<'W9W',%x$\l0#0`nZ&83)t V&5 .ثd?L .8d߱C8έ+s-{ #-KEk1R/D`F<#%\S 5h ŗF?[ )v/gbś#?D$D. &,SQ ś$q$.'-(N7N)'*/$]lG.%PKе*N!K9(;U :Z$]s# iTA*@Yڥ6!_^)+q韜A1×j!mDpp8E+2>FPC@ܭ + a}4)>rW ǭL ӯ)lԹ&@T(5eBq ..)mG=3 ށq%8I;5- 8By^.u&Jm&ʁ #M ]S1< r R!W%L֢=.& eh1/ T52(5ښ5X<&!1B]Z@ *5p8vbZ]/ 9lGe{ >ó  \9 @^'13үRF1"\I)F&*;+ش+o3Ġ=JGf/  d?hӟD_:yD v Ƕ!k;3caaw c3y6„8!2,1WhA{=qJc#1-A߬(\)lkRT/:3> 0 6=O1#edu h<= Ě!A!e=ӳE raE`u"7 I i?n B5,)>IOO3̶>i9A+lFO }ccd( ~d!*u 3=TA~ aP!, :m G"C{NޥI1,ѼSS,c,1o͞'4v8HB!! Q?hFd$k MFIb !轵2/ 7 k[Ͽeպ(!-!Uj [a NXTy Nx).ifn 2'?kOS3hD 1F(G "C.m#Qi 1I8K7 R(g6 ,<´!w?Ҏ"53@NРg+JuLooYan< 9(vEDgP,k&ּJ+rUk:T=}4 zH9MI/7&g L'7"˯/D9ڈ5h3NF#}]i! MZj $AT V ::K(c .JG"q &2"-.5PҸ7#8 EhL ЏV &'BP}6(s"-ҤP+0ټ|3r2YW^5a(;l2INK6ň1?B_&] m~?<LOB*C\ї-y 5>β $] 25"O@e#z( ^z &EJ^Φ> yt!̘$PL+JkA]?`PSEҠA 9]rfv"/tS14 la0pPb& w9&q9;p?ƭVK{97.8E0ȊIZ4}H CZCBvD 3MA.ͪ-{WdM<g 2dZ mϙTd/[-*#6 ~:8@H/'s CC)}+%Q -7V)e .cEBĻLUN;I&4I_zXGP*/o1>J)N&e Ҵ;m)8߀$B_&ge _3r ) +%+5J kFQ䤌'DV`8ͳ&* R,3MJ# 0:?&1&8yS?>L8 m Vİ<s#! ~G@X/ҩ-\Q0A̸ Q I .+h_W0v'ؤ1d%ͷ@Ρ+Η#2=Ii;r*:ܽ' @%5t9?PO͝Xx$5[IS%ΉKw_3%u" 61i^:5g͂FD۱6L=o^ |rE=Į=Aw4w^#y }A&?02ZjH O.!_f - @lNg]22?t"d),Tٓ aPZ a<7M69|)ޠ0' Yu0c9N/^3I,&=04$ <)[ڶ?цY4D *E _(2?=GQ[f_pz?{pf\_RH=3)? aAׁa!A!aA!a?_'1AC?FH_KMPSUXZ?]__bdgiloq?tv_y{~~_|y?wt?romjg_eb?`]?[XVSP_NK_IF?DA?<974_2/?-*(%" _?  _A?_ _ ?_??!_"#$?&'(*_+,-?/012?4568_9:;?=>?A_BCDFGHI?KLMO_PQR?TUVW_YZ[]^_`?bcdf_ghi?klmn_pqrt_uvw?yz{}_~}|?{yx_wvts?rpon?mkj_ihfe?dba_`_]\_[YXW?VTS_RQON?MKJIHFE_DBA@??=<_;:87?64321/._-,*)?(&%_$#! ?_?_  ??a!Aa!aAa!A!ܡaׁAԡa!΁AɁ!ša!Aa!ᷡA!ᮡa!Aa!ᠡAAᗡa!ᒁAa!ቡaAa!A!aAa!A!aAa!aA!aAAš!aA΁!aӡ!ׁAܡ!aA!a!A!aA_?  _?__?!"#%_&'(?*+,-?/013_456?89:<_=>?ABCD?FGHJ_KLM?OPQS_TUVXYZ[?]^_a_bcd?fghi_klmopqr?tuvx_yz{?}~~}_|{yx?wuts?rpo_nmkj?igf_edba?`^]\?[YX_WVTS?RPO_NMKJ_IGFE?DBA_@?=/ HRéR1ΌZ+΅Q-q4o %-;IV-ajryB}-~|ztamLdYXM?1"DWJ肃K86͋@FY }+?:HT`irax|jv~zucnbeZNAT36$g'.^6_=ODJYQfW]qbcgkosvy{}~}|yGwtpslgc]BXARKNEg>A7/V(  'HцR[iR12qΌb2Z+Ԁ΅qxQ-%q๾4ϼoC %%-4;BIOV[-a8fjor-vy\{B}~B-~||zwtWqamhLd_YSXMF?911*" 9ۗ"D˰ΤWBk4Jۄs ?K5u8`ꥨg6GƓՋW@=FQ u#g'+.2^69_=@ODGJHNYQXTfW:Z]_qbdcgikmoqsTuv?xyz{|}V~~a}~}}|{yxGwutTrp|nslPjgec`][BX:UAROKHNEAg>:A73/:,V($   'HLٟ݉LΆR[i@RUŠ12疛qΌvbmӁ2Z +rԀ]Ȃ΅q才<@xQA- %-q\\ǘ4Ҽyo=C (2 !%s)%-04Q8;n?B FILOSVX[^-ac8fhjmoprt-vwyAz\{e|B} ~~1B~-~k}|{|z9ywgvt2sWqvoamJkhfLda_K\YVSPXM.JFzC?<9M51-1*G&"  !u#%g'D)+,.02h4^6$89;_=B?@BOD FGaIJLHNOYQRXTUfWX:Z[]q^_ aqbcdfcghijklmnopqrsstTuvvw?xxy2zzN{{Y||8}}~V~~~,a}G~~-~}u}}||{{zyIyxwGwzvuttHsTrnqpo|n{mslekPjigfekdca`;_]m\[YBXV:USARPOMKWJHFNECA@g><:9A7z531/.:,a*V(w&$"    '1H1Liۉ٪ןLP̆ʿRÙ3[ni@ƪRU񤓣:Šx1`2 疰q~ΌH[vbm򂋂+Ӂ{2򀹀Z9 +LrԀ]ZȂ4"<΅hq(扬a<.@YxQ?Aƥ- %-q=๱\ u\Ǣɘd4Լ֙yڋoU=&C0$ ' ( 2 ' #!#%'s)N+%-)/024^6Q8:;=n? ABzD FGI#KLoNOQS}TVWX^Z[=]^_-abce8fcghijkmno ppqrstTu-vvwPxyyAzz\{{e||B}} ~^~~~1fxB~~~-~}k}}||{ {|zy9yxw4wgvut t2s=rWqjpvo|namXlJk4jhgfeLdbag`_]K\ZYXVUSRPNXMK.JiHF#EzCA?;><:97M531/- ,1*V(G&g$"  / O_#(-3/8?=_BGLQV[`f?k_pouzф!AQq1Qqс֡1Aa #?(_-27?A_BCD/FGHJOKLMOoPQR?TUVX_YZ[/]^_`Obcdfoghi?klmo_pqr/touvwOyz{}_~AфQ1qAђ!aAQ1qAѩ!a1Q!qAaá1ǁQ̑!qѱAՁaڡ1qQ!aAQ1qA!a?O / o ?_/O !#o$%&?()*,_-.//1234O678:o;<=??@AC_DEF/HoIJKOMNOQ_RST?VWXZO[\]/_o`ab?defh_ijk/mnoqOrst/vowxy?{|}a?/o_? / o  _ O/_O?oO? /!!o"##_$$%O&&'/(()**_++,O--.?//011o233O445?667/88o9::_;;/??o@AA_BBCODDE/FFGHH_IJJOKKL?MMNOOoPQQORRS?TTU/VVoWXX_YYZ?[[\/]]^___``aObbc?ddeffoghhOiij?kkl/mmonoo_ppq?rrs/ttouvv_wwxOyyz/{{|}}_~Q񀡁Aႁ!фqQAቑ1ыqaA񐑑1ђ!aQ񗑘Aᙁ!qQ񞡟A᠁1ѢqaA񧑨1ѩq!aQ񮑯1ᰁ!aQ񵑶A᷁!ѹqQAᾑ1qaġAő1ǁ!a˱Q̑A΁!qұQӡAՁ1qٱaۡAܑ1q!aQ1!aQA!qQA1qaA1!aO?oO ?  / o _?/o_O/_O ?!!"##o$%%O&&'?(()/**o+,,_--.?//0/11233_445O667?889::o;<???@/AAoBCC_DDE?FFG/HHoIJJ_KKLOMMN/OOPQQ_RSSOTTU?VVWXXoYZZO[[\?]]^/__o`aa_bbc?dde/ffghh_iijOkkl/mmnooopqqOrrs?ttu/vvowxx_yyz?{{|/}}o~Db~T!yE`Pr-A%a@OR..nSasgxh"/ =Ȥώ5Ii#ج، εN &Ya[%@_T0$r-#A%T0a@"NOR H. .wVnqSa>Bsgx_h"&/ 2=Ȥ2ώl5ǩ}>II?i#|֯8ض܌ ε:ݨN Ǖہ.&*Ywyta[ >%x+@LT__TB0?%$%-H5h-v'#4#9%)I08S@GNQ=ROG=. ]'%Ƚ.DdVdmnrqk=aS0B0 xkht`7 cWn,8&'+#1B^lܤ~J4+Bǃݬ: B1o>FIF;?3a#?ɋ 'Dz7ܽvڕ~pŪ-y,޵kFkU:ξgޕ d1 =)N*CY kzw`~P|_thM[L=0%k"n+5@ K9TX[_Ga_O[TLBU90)7%2##h'-q47N1t+J&!^4ZcJa"i&*/,5:@aEJdOSW[]_`;a`_][sXTPqLGC]>9B51c-#*r'Z%#6#/##4%.'),C0480DG@KN9PQbR3RQOKGB=O6.&| 7ۻU[ e/,rS v#.9CMZV^d/j`n?qrrqfokg2axZRJ(B+9/& M(zX'm5iGV r%]Od4eD }eZq/#+;)' * > 0Eyc0vXBߓX NA̛^4H9҇9 )<1j8h>.CFHzIHFC3?93+]#=8{؎e“ðȮŭe3 b.KJӅ;{[ؚN҄Y8x&ӮϪ?8ϡ ƞA7rLˇpIX>#ΰ%rtɊ/_ r4>Eʷ;*>L(*7C7OYbkqkw{Q~~A|xQtnhJbB[SL!E=70*%!,h~B"&i+_05;@EKO/T XM[]_`;a`_]D[#XTrP LjGB=O940-)9'3%#.#9##Z%d')-0k4u8<@D?HKFNdPQfR%RPNKGxB|<5.% S%M kIIm Gܕ5#[#=;\r*Ҝ8;3p }zX$2K8eq!Ѽ#SU.w#^{ h<1!p> V[L,D~)A$C=7Eg8~ h -6sb&Z!*.'0T,iow?Ū"<\@4,+T(wTԕAlt a,f> : A  l"+xR2_w#;E.#cv1b/66ZmS/r߅%;_֤ |*GUq pdiv 6  54s؇Q%lus/G 0 }+ *\8un޸,o$/ Ԡ$ &Z6׾'LO1_Tc7 6;N= -fC~ _'lf9E-!t0c/A) pn S<`|BO2[J >c!GõtL/3G:- [Ǵ8&H,cۍ "X$ lmU!P00. ӱIl(-(Ij v \$^,n1;@+Ϝz:x_Я1!2+b43DT$mJ=Zƞ ,j &ڣM/ϺډM8;-v6}* +oH[g@c;Ox8#>)5 /Fs";pwC`l?;_) |Qś/q E`Lo|<ԯ(Aq9v BpN +rN'8 "Y!D42x( lv/vH@&;_o@A t4r"]("'e!шH׏=ӺZ6=Dخ"=$ ^.6gP gr.ac&8]ݴ  ,]Nh֒*c1@|e5J4UUF }-''@.jjT v :* ,VNc' *ڀ(uAr ^&! :)!c@] %ф$K:]m,+wӈ$0}*!_U@?}-e 3d7hb5SH9Q#c>YݑY)$U\1-jP"iA+#s3} ZF86j׻C>&GG %-62H$nX 0 y:q%1 b5M3i$$_ .=8bGD .O9`{A _-&r#xX, ^.?D0Z(ܼ# j;$/S/(R00xе 17L1ӕGk4P ا'OOpm#8oW;d+ AK`]ɶ. j 3H~wz|a1C7Э6g&R!,@h:c dmZ([?a1;8$*i ]ЃK܅%П#ňH"5H'}ݔ`-u_zg O')Z2ׁt90m/a *W WԐ< R*i ϗ#|0  1g=!E9$L /'U֟E 43~"1K0gT% #\M 2%5ť-ԤL̯  5Ƨ;ά$;''7HST uUђ"y1c:NRqH *9  h$Jn>x8> 'Z?Q" q +T J.1:oqA XĠ@p"OQ'0!@4F^&}>? Z[G&|$ A "?]5^x*&0$FVZ*ly-r]Z=1PQ!JdU% Dȝ9x L$aABS#/X O6Cd we/ !#%pr`LK.{R6DUVA7"+ /  919?/$Jlz?$!6B}=*@8Xq%FQ|,?,9JGq >* 5Z 1Z*EsgX033 Teݏ$XW!>q"9" 2&a]>6~:2v=F[3vΑ&\ܼ&P԰HB N6!&m&\ v B:o=%:4(QO C &E.;&&{4F_0"'k 4 fmQ= % h{zu)󧷟G%P:.FˀDt J F-+eDb"(gx<5?}+R07 -* / 0qq?"EAa60NeKjNJze$ڭTB6b] * W&?rh@L !J"HL7/E'ϻI1 "6+)" _ҩ3w? Y1\LYP,#߬(?X<eC Tڀ P;2|.Aq5ض!4->;k @-,'[.*3 'LMY$Ԙ, sT+0C"-=(-a7I`@m.( }*D):u-%6Hj&w?4Įn2b14 52%]n49_W-Z6"J\li!D#ت  A7Kl bFκ* .k\j2I f;#* -(4 `* ;̇#jFIy18w~?~.E%E*3'Y 'TY8,ۄ$H$m//uQ)RcGW"! ' }9\ Q,wb.3`) | .W~@z Uͭ FujASN&e}AGN+<-y \Z6Ra7 ^*R |fR"_ j27J*mѸ<r-VпIX,I0,g:VNE)(AKcxYf"ˉU$ )o7NYft7'P6I,;;&4\?FկC;En!!Jw){+A ׎0Zݞ&tXͪ"< y-G1QR6ڞj]'e( z={ * 4Ņ/,B@ E iN'i"oo!K+<:%T ")SGϋ#-*W.5j޵qYR4šNnC+ ޛf:<٥4I|qRz+@a {>k95V*_)Ͱ$ ,Hv&iX."KgW&O8JϾ": 0I9۴Ag"CpL a ?LO9ėMg̩IPڷC)&2 ,ۃ+ y" ,ԛ/ O"{҈4`D4`5@p.-+D";-;`;+cV)%8W}uD5)ZI b[DproaƠ\ a L 0G;Xȩ1;z wk&1 n W ,Kt !< O+^*ֵC,1? C5c?qޜ %WqAQx4Y # Q:/#4Eomk ԿW@&+4Xg v.1!Kv"T1C#R8MP.u)zˆ`x([MC+5TV,(`,$9!]"7{'z/TV &eR"Ʋ2eމ) c/3G>ƥ~VH1[Oa.[>%Rԯ?0?UjB("9 -CN!WCv~5VR:(9Bҵ.e  sxP"C#a`! %Xbߩ*w)*6 ISֱ9 vE j4>^&VFuA)'2> W P| sN0G+~Ij(b(A0Iݓ G#[J !U< f y)Gq@ <C߽A Rj6Զ"cJcgH8/8 m=V΁Uih7T42=0f1  ]]~> ǁ-: 9;˕`8]?^AХlIVCr"*1)A i fBR:m{ )%1=v2EwUQ 7I[~2wJ:͸%@C//(>2 k j>e3ArNܻ*7)Oå5`c  ]-C8Ԍ@m#}9v" &L8T[;ߖ2 F*K@$M)R:⡑][0,/ V2$L?HAJ28#,& $125zq1d|5]ņ!Dӌ@Xx?˒O<0tG ,7#( MM%[ߡ)k!ER^x0m% IqIJUH4`> TѺ)1'6X@$!b6T7{- ݀݊.*Uoh{)e +N&+ "( r!| @M# /)>ū!X :Ry"@]n^;@\Dؿ%l1F;ڭ!t +t _a"w#;ʌ O 1 +?qn}L-n:w#_r>KӶ0I~lVN&Ên363U51 h@7П;XO !a!<%=0YEtDt(Zw#d+(M3c8'@W|\ +p$lBb !Ugtk-WHrX.$;A4<0i)z*ڛC9E<(Qݽ19G!8T^NJ`i]!R"JΣGQ5%ϳ32$7j!A9-w)nt'?֛Czq5,۝ZqO Jޱ6w H.~A5Ef`$yW05nd$/;W+E9lr$* 9(W Z֒* lR zC?C U#?DA1$ֶ.4̿"֠TyM mK#qn7W#nJ"1w 9#! oE:2E<%0 ϦQV^ [|{[)2Kѝ2̃]Ϊ}5#Vuym~ a(892^70-W<4X F}01<2耄GԵUz}`/h5޼Cz#@Qve|/D O*;6"\ u N.;5e[×̽Oxpe5 ׂęJܨF/L(u29;8/ 0YK5vy1 ?"[o|>{n~[Cj)@EѦy^֭PU ٢P4 #=%Vjx}1s`F'aóN߃h(-,7<:4*J CA7"%:0(8;*:2$ioaĀ( 9_Uky} rG`MI/`Qߝ'/ {"L(-u2d69^;;A;8F5/) G 0BYKы5v[yۡ.1 j0?CN"[8fov|~>{une~[PC6j)@6E،˦&Œy^ۭBP'UH {Ӣ΃xP74| s#g0=MJ%V`jrx;}}y1sj`eTF7' a򴳧NU܀߃Ј!^hUƛ(-Tt%,27w:<;: 84,0*q%J KC%'Apq 7d"%=+:04(8:;;*:72K,$*5߁iŇoA"aĀD(Ą -P[/; '{"%K(*-/u24c689:];;;;@;E:8O7E52/,)% F  0B)Z$Lšݗҋ̃܀7zԊx] .Mz÷˺R;.1 ~(i0K8?AGBNT [`6f%ko}svy|}~)}<{xuernZje`}[UPIC4=6)0j)"ba&@6FܱԌx˔ɧ^&ǓPz̨ш^ޭ_B P\'!UHu4H~ P|Тx̃ʗ@yHQ(7 5ݨ} sE{#)f07= DLJoP#V[`ejnruxL{9}~}{yv0s5oje`ZdTMF]?70'@6U "a%PAقVs3ހ҈#`:iſV̜P)0- S#!t%l),.02q57!9v:l;<1<;r;:98g64r2+0-*<(p%g"k J #KC %'A0p`q 7d%!"%(<+-90246'8}9:r;;;;,;):87 52/J,($t  *%54قiネ`qBC$}cƀ/ 3F)ێؗ 1|̅ԍw==> ?JovQu ;uw?" ;NoN W' ?'JAeohvQ3u n;w\uw`? " g'0;N0fojgN<, "$WK4' 1?L'9JYAelonhI^vQB3%u  .n+;hLw\ju}*~wm`IP?G/ " g, !'0O?;N[0f3momjg\N><,Y "$1W95<,+#Y o"VIp ${+1r7W6.&{ W y |9"u)0_8@GNU\af~jgm:oo`omjf b(\yUMEI=p4q+w"' 'roiWBk" Dmp% ,E27<@CEFFEB>3:4-e&T t3 '߬xʊżzSĒ^ϽՈ=' \%-56 D{3WBI5UőoC@Lz u§.n?}h,e"SnN^FMMɗzq샀7([S{^٤̬d͝ܗVee} ˀnP'ՓKLISTpdtaphdrVSpace FlutePX000 wWonderland Xylo0Y~DxGated Screamer-0[}xNew Age Organ |y STR-808 Drumset 2o F |{Electronic Drumset Kraftwerk DrumsetT)Analog Drumseta( S:v := TR-909 Drumset*RkN0TR-808 Drumset c~CR-78 Drumset* OU} 0TR-101 DrumsetebGpy0 TX81Z Sqncr Bass 2{Filtered StackaserzWoody Bassa000yDelicate MarimbaxDelicate BellsX awWind Down0- a000vWarbling Bird 2auU* UWarbling Bird 18atejiWjLaser Pops]000sYWind Blast]000rr2Warble a000000 q8uSinging Strings0 apuDistorted Lead-aouDHeavy Squarebbnu(Cheap Synth000mu$Polysynth 2tVclPing-PongOrgan ckVenus Violin Hitj Polysynth HitXiMono Analog BassilthCheesy Pad2TfNew Life 2a gMonster Strings beStab Bass StringsddKiller BassxPer Qc ThunderHcx **b#Zippy Bassa000a'Yazoo Bass Hit `+Yazoo Zips`_/Oingo-Boingo ( b^3Sawtooth Hit*** _]7Warehouse Percussion\;Breath Padingsa[>Dreamy Pad JZATechno Bells\ \YDRough StringssXIAwesome StringsWLSustained Harp VPBonky Organne ^USHouse Organ TWNoisy PopsPopsp oS\Ethnic Bowkm^R_Panned StacksX_Q`Monster Stack LeadfPcAcid Bass 2 c 4OhEcho Pop Bass0 plNkAcid Sub Bass* 8cMoTX81Z Sqncr Bass **LsYazoo Blipsa000-KvWarble PadlsTu _JySpudge BassPe000I|Undulating Pad dHBanshee Pader QGLong Bassc Bass FNew Lifeute ] ]EXylophoneZ000DClick PopsxexCBlistering Bells BWahodxATwips Ring ]@Melodic Vibratoh ?Screaming Wavepadi>Stacked Mega-Phaser=Pop Bass 2 hL<Pop Bass 1 ULWavepadavepad(b;Polysynth Warp i:Rubber BassTL9China Voicess d8FM Clang L7Phasing Choir* P X6Singing Bells FT5Harsh FM Bassyn d4Gated FM BasskHk3Dream Hit @ mL2FM Bass Hit mL1Faerie Chorale (k0Metallic ClinkexV/Oink Grind*hVL.Wailing HitWL-Casio VL-1 Popsj,Square Pop FluteX"Lead Synth 3ii+Lead Synth 2* j*Lead Synth 1 @j)Aluminium PlateHT(Space Warpi* t'Sine Bongos* X&Sine WhistleiXi%Vatican BelltteW$Phasing Strings`#Square FluteS iWater Triangleer!Cosmic Vibraphonei Bass Dragon ChoirVatican PipesTTFantasyPULlElectric Slap BassBingo BellsUL D50-ish Bellseq i Breezy Calliope hChoraleHSpULDistorted Sweep BassTX81Z Lately BassFFM Electric Bass** Smooth Strings 1tch Smooth Flute W#Triangle Simple V&Triangle Dream Flute)Thin Sawtootheh,Classic FM Bassh .Church Organ 1Meat Grinder X 4FM Christmas Bells 7Hard Grunge Bass j :Resonating Padg Pad=Sheet Bass XL@Dragon SweepXVCEl Cheapo OrganhkFDetuned Sawsum WIGrungy Ramp BassuatMSimple Square FlootPFM Carillionx iRFM Bells 1e me UEOPXpbagd !$&:;GKPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     !')3=GQ]hjlz|~ %')+:<ACKMPS[\lnq$(,/ADHKWZ\_ruvy#'035ADGHRUWbglq| -03<DJPZ[_imt#&).89CP]gjlnq  !#./=>@FUYhios{}  %(*47<DFHPRT_aqtv}    & 0 5 8 : A D F P R U _ b d m p r w x      & ( * 2 3 @ C E R U W pmod W pgen`)<3&n$%% `,%#"r!)3 n%$ &  x#n"n!n8)) 3" L#!<P ,(&)"n3n$ &2 #n!n  ) 4))))))))))))) ) ) ) ) ))))))#)")!) )))))))4)3)2)1)0)/).)-),)+)*)))()')&)%)$)e)d)c)b)a)`)_)^)])\)[)Z)Y)X)W)V)U)T)S)R)Q)P)O)N)M)L)K)J)I)H)G)F)E)D)C)B)A)@)?)>)=)<);):)9)8)7)6)5)x)w)v)u)t)s)r)q)p)o)n)m)l)k)j)i)h)g)f)))))))))))))))))))))))~)})|){)z)y)))))))))))))))))))))))))))))))))))))))))))))))))))))))))!%$` # "& L#"!) )#  &` $` "!,d < d 4) (6n 4) 6  4) P 6-U4)3`  L#!"& $%,4)4)3$&%% `,%#"!))3,&$%#"!4)4)&$n%  #"!3<X%4)%4)  &$n%#"!3<X< P g%)&$%) L&$n% #"!3<X P )%)&$n%  #"!3<X))&$) #$%&% "!3<,)) n&2$n"!3, P )%) 6S&4 $ )=%")" #&` $` !, )4)4) n n&2$n"!3,4)S&3$G)4)#n"n !n&4)4&n)4&n) &#"!)  0&n%$` #n L "n!n4),4)& L x#"!( 4)!S4)  L&P$#n"!nd4)4)&%$` #n L9 0"n!n4)4)$#n L9 `"n!n34)4)#  &` "!n,)4)&$% L#"!&< )) L&n#n!n"n3 4 )4) L&n#n!n"n 3 %4 )4)&n#n!n"n 3 %  4 )4)3, L j#n!"& (4) 4) ()f L&$%#  "!(4)$~&~3)4) L&$%#  "!(4)3)4) d&$ x%#  "!(4))4)&$` %4 #"!(P %$P&PX!"#)3)4%$P&PX!"#) d%# "n!nd4 )4))$n L!#%"   4))$% (n &n"X4)$% ` &n"X4)% Ln &` $B!"#(X) $` 4#"!P &`  &  (4) nP( :4)$` %4 #"!(P &20,"4)0,4")%4$P&PX!"#)03)% L!# (,"P&n4)) L!#2 2(,"&n4)4))!#2 2(,"3&4)) L#"!<~ &0d"4)0d"4)(0d3")4 #"!( &" 04)" 04)%4$P&PX!"#)" 3)$&%#!"(P )d L3&$)P)  ) #!"n,  H ))  ) d#  &n$n"!, 4)4)3 L)$` &`  ) L X%$ #%!"34)4)$ L&. n#%!n" 4)4) $&%#n!n"n) ($& n#%!n" 4)4) $&%#n!n"n)!%$` # "& L#"!) 0)0<(&$!% #") &J$) L ~d"P# 2P!&n&X$ %)$) %"&n 4)&~$`n#%!"3  4)4) d "%# P!&n4)4) L~"P# P!&n4)4)%$` # !"&))$~&d% L#"!4))% L $&.% #n"n!n4)4)!$% L&#"),(&,$,3) ($2%&#"!: :4)4) dP #%$n&  "!)) #%$n&   n"n!n,4)4)X"&$) L&#"!&< ) ,!n"n  (& $ 4% T L#"!)4% T L#"!)#  &` $` "!, 4)4)4) 4)n L#$%&P!n"3  4)4) $&%#n!n"n) L#$~%&!n"3 ,n 4)4),!n"n  (&2$2&` $` #"` )  `  H#)% l L#"!& $ )~ %&$` n,n | n"n!n3,4)4)&!nn"n3( n,n 4)4)3!nnn$ &n#n"n)4)$ %  &#n"n!n)  & #n!` "n)4)%(&$` #n"n!n ( X) nnn&$n#n"n!n%  ,)4) L#"!)() % n,n&$` #n"n!n  X#"!)(% nN &$` #n"n!n ( X)<&~  ,%#n"n!n)  &$n"!n)4) (&$%#n"n!n4)) #n"n!n), n  & $nP#n"n!n4)) ($&%#n!n"n(P 4))n&P ( n T(#n"n!n)!n"n  &2$2,34)4)  &2$2 n"n!n,4)4)n&2$nnnn | n"n!n3,4)4) 3&"PP )) &<"n4)4)!&#$%" LP )4)< "&P #4))$ !#%&", P  4) $n&n4) "#n P!n&4)4)"#n  ~!n&4)4)#"!,P (P ~&4) n%$ &  x#n"n!n4))&< "n  4)) "&P 4)4) 2!#%$ P,"&` 4))% $&#n!n"n 3<    0P4)$&)#n$n% &"n  ,4"n)"n) &n"P  &n"n)") P"P  ))4)4 "y&2 P 4)4) n$#n!n"n&. 3   4)34)4 $.&.%#n!n"n)4 ,#n!n"n%&$3 ) ,#n!n"n%&$3 ) $.&.%#n!n"n) ,!n"n&P4))n  "&4)) n!n(P ,&2")4)#n !n"n(P &4)) !n(P ,& "4)) "&)$#n% 9 "n!n34)4) &d4)4)#n!n"n 3P    4)43) !n<P ,(&` "n4)) "n!n3(n nP 4)43)#r n, !n& "))#n!n"n 3    4 )4)#n!n"n x n3    4 )4) #"!( &4)4) "n!n3,4 )4))"n!n3n nP 4)4)#n"n !n&)$#% !n(P ,(&"n4))$` #% !n(P ,(&"n4))instInstrument8pInstrument15pInstrument16pInstrument9pInstrument3p808 Maracas  808 Clave1zX 808 Cymbal A 808 Conga Hi1nHC808 Conga Med( 808 Conga LoC808 Tom Hi 2 FC808 Tom Med 2P A808 Tom Lo 2* D808 CowbellXD808 Tom Med 1hX A808 Tom Hi 1CC808 Tom Lo 1 P A808 Hat Closed* C!808 RimD$808 Kick 1*AHz &808 Clap CG'808 Snare 2X C* )808 Hat Open** B,808 Snare 1 CG/808 Kick 2 BG2ELECTRONIC KICK 2ff3ELECTRONIC SD 2(i6ELECTRONIC SD 1j9ELECTRONIC KICK 1<ELECTRONIC TOM LO 1@ELECTRONIC TOM LO 2BELECTRONIC TOM MID 1DELECTRONIC TOM MID 2FELECTRONIC TOM HI 1HELECTRONIC TOM HI 2JKRAFTWERK BD 5lLKRAFTWERK SD 7OKRAFTWERK SD 6SKRAFTWERK KLAPScWKRAFTWERK SD 5hZKRAFTWERK BD 4g]KRAFTWERK SD 4_KRAFTWERK BD 3fbKRAFTWERK SD 3hdKRAFTWERK RIMSHOTncfKRAFTWERK CYMBAL** gKRAFTWERK HAT OPreqhKRAFTWERK HAT CLtchjKRAFTWERK SD 2alKRAFTWERK SD 1XgoKRAFTWERK BD 2sKRAFTWERK BD 1 auANALOG SNARE 9kwANALOG SNARE 8jyANALOG SNARE 7j|ANALOG SNAPPY BD kANALOG HAT OPEN 3s ANALOG HAT CLOSED 3ANALOG HAT CLOSED 2ANALOG HAT OPEN 2ANALOG SNAP SNAREhANALOG BOOM KICK ANALOG CLAPS 3aANALOG CLAPS 2^ANALOG ZAP OPENHfANALOG ZAP CLOSED**ANALOG CLAVEL0 fANALOG SNARE 6@ ANALOG SNARE 50fANALOG THROB KICKdFANALOG POPS OP 2astANALOG POPS OP 1se ANALOG POPS CL 2ANALOG POPS CL 1lteANALOG PHASERPANALOG TAMBOURINE ANALOG KICK 3o cANALOG RIM SHOTdANALOG CRASH CYMBAL1ANALOG COWBELL(bANALOG BONGO HIdANALOG BONGO MEDNALANALOG BONGO LOPdANALOG SNARE 4 YANALOG SNARE 3qbANALOG MARACASt_ANALOG CLAPS 1 [ANALOG HI TOM 2ANALOG HI TOM 1HfANALOG MID TOM 2* ANALOG MID TOM 1 *ANALOG LO TOM 2hANALOG LO TOM 1eANALOG CRASH CYMBAL2ANALOG HAT CLOSEDcANALOG HAT OPENdANALOG LASER ANALOG SNARE 2ANALOG KICK 20oANALOG KICK 10rANALOG SNARE 1Xp909 BASS BOOM1(j909 TOM HI 2 909 TOM HI 1 909 TOM MID 2 909 HAT OPEN 909 TOM MID 1 909 TOM LO 2 909 HAT CLOSED 909 TOM LO 1 909 SNARE HI 909 CLAP 909 SNARE LO 909 RIM 909 BASS DRUM HI 909 BASS DRUM LO 909 SNARE 2 909 SNARE 1 909 BASS DRUM 2 909 BASS DRUM 1 808 CLAVES 808 MARACAS 808 BONGO LO 808 BONGO MID 808 BONGO HI 808 COWBELL 808 HI TOM 2 808 CYMBAL 808 HI TOM 1 808 MID TOM 2 808 HAT OPEN 808 MID TOM 1 "808 LO TOM 2 #808 HAT CLOSED $808 LO TOM 1 &808 SNARE DRUM HI '808 CLAP *808 SNARE DRUM LO +808 RIM SHOT .808 BASS DRUM HI 0808 BASS DRUM LO 1808 SNARE HIGH 2808 SNARE SHARP 5808 SNARE FILTERED 8808 ULTRA SNAPPY ;808 BASS DRUM MUTE >808 BASS DRUM BOOM2?808 BASS DRUM SHORT@808 BASS DRUM BOOM1ACR78 HI TOM 21PBCR78 HI TOM 11SCCR78 MID TOM 2 RDCR78 MID TOM 1PECR78 LO TOM 21OFCR78 LO TOM 11GCR78 BONGO LO #03HCR78 BONGO MID #02ICR78 BONGO HI JCR78 COWBELL KCR78 TAMBOURINE MCR78 HAT OPEN OCR78 HAT CLOSED QCR78 SNARE 2 SCR78 METAL WCR78 SNARE 1 YCR78 RIM SHOT ]CR78 KICK 2 ^CR78 KICK 1 _CR78 CLAVE `CR78 MARACAS aCR78 GUIRO b101 LASER 3 c101 LASER 2 d101 LASER 1 e101 MARACAS f101 SHAKER g101 BONGO LO h101 BONGO MID i101 BONGO HI j101 COWBELL k101 TAMBOURINE m101 HI TOM 2 o101 CYMBAL p101 HI TOM 1 r101 MID TOM 2 s101 HAT OPEN t101 MID TOM 1 u101 LO TOM 2 v101 HAT CLOSED w101 LO TOM 1 x101 SNARE 2 y101 CLAPS }101 SNARE 1 101 RIM SHOT 101 KICK 2 101 KICK 1 101 SNARE EXTRA #02101 HAT OP EXTRA 101 HAT CL EXTRA 101 SNARE EXTRA 3 101 KICK EXTRA 3 101 SNARE EXTRA 2 101 SNARE EXTRA 1 101 KICK EXTRA 2 101 KICK EXTRA 1 ADDITIVE BELLch ]SMOOTH SAW S MELODIC VIBRATO FM CLANGt1 FM3rument1|FM BELL 21THARSH FM BASSs FAERIE CHORAL AAArument1pxOINKument1`oCLINKment1pSMOOTH SQUARErHALF RECTIFICATIONVIBRAPHONE]xGRUNGECHOIRe TXPIPESment1\xBELLSH BELLS BREEZECALLIOPE AAHSAAHS ELECTRIC BASS DM 3ument1x V DM 2ument1Z DM 1ument1V WHITENOISE 3 Xh XTRIANGLE WAVE1 USQUARE WAVE0UXSINE WAVE1VXSAWTOOTHt1XUXADDITIVE 3 T,WHITENOISE 0** XFM 2ument1V` FM 1ument1U`EOIibagP &29ELX_gs$+6=IMWamz!+;IZn{!)48JN[hx|"/:>LYau,29K^k{'';OP[ep!.<JXkx-=MRZ`emsx   " - 5 ; F N T _ g m x    ( 6 C W e x  # 6 K ` x   0 A V g w   & < N ` v ,CZq %:Og  1BTf|*<Og%7DR`v 2H^u,=Shy$;Qi 6J^u  4I[o !5Kav $,5>GS\enw&.;CPXckw'1>FR[dmv&/8AJV_hqz )2;DMXajs}imod }igenu" d.#&$:962-5" d.#&$:962-5" d.#&$:962-5" d.#&$:962-5" d.#&$:962-5+FF4:,"&$%5 +KK&$%4:W62-5 +11  $&%:=62-5+110&%$4:+62-5 +110$&%4:,62-5 +>>&$%:e62-5 +??:'&%$62-5 +@@4(:/&J%$J62-5 ,IIIII+22:&%$62-5 ,IIIII+//:!&%$62-5 ,IIIII+++:$&%$62-5 .4 +880@&$%62-:85+884&$%62-:85,IIIII+--:"&%$62-5 ,IIIII+00:&%$62-5 ,IIIII+))4:&&%$62-5 %$&+**:203 62-5 +**} 62-:55+%%3: &$%62-5+%%4&f$f:C%3 62-5 +##(&%$362-5 e _&$%+''062-:'5+(( W&f%$f3x62-5 +((3  &$%:&62-5 +((3 &$%:&62-5 & #%$+..$:603 62-5 +..} 62-:95+&&0d&%$3.62-5 +&&3 ( &$%:$62-5 +&& (3 &$%:$62-5 +$$(&<%$<362-5 "g3&P$P%+$$ 2-:.6K5+$$42-:.6K5+((("r4&P$P%:(62-5+(("r2-%:6w$&5+((("r 4&P$P%:(62-5+&&4 P '&$%:(62-5+&&2-%:6w$&5+&& ' P 4&$%:(62-5pe"g3&P$P%+## 2-:.6K5+##42-:/6K5+## ,"I&P$2-:6K5"r1%3%&$+))2-:(6K5"r1%3%&$+++2-:(6K5"r1%3%&$+--2-:(6K5"r1%3%&$+//2-:(6K5"r1%3%&$+002-:(6K5"r1%3%&$+222-:(6K5+$2-%:76L&5+:%5+0,:F%&~$~qq62-59,$J&J%+ 62- U":!5+ 62-"  :!5+ 2-%# :6K" $&0N5,$&%+//4 2-:#65+//2-:#65+//:?qq$&&&62-59(%&P$P (3+'' 62-:+5+''62-:*5+-- x4 ]&$%:+62-5+--2-%:6w$&5+-- x ] 4&$%:+62-5+!!@2-%:16L&$5+!!:%5+++62- 03:!$J&J%5+++62-0 3 :!$J&J%5+++&f$f62-3:0%5+""2-%:56L&$5+"":%5+))4 &q$q2-%:*65+))&q$q2-%:*65+%%&$(:>%5+11  @2-:46$` &` %5+..) d2-# +:46 " $&1%5+.. 32-# +6:! " $&1%5+**) d2-%# +:06 " $n&5+** 32-%# +6: " $n&5+((, # 3:!$&%5+((:#&$%5+((,#  3 :!$&%5+&&)2-%:$6L&,$,5+&&3:!$n&n%5+&&&&$&%:6 62-5+&&3&&$&%:662-5+$$2-%:76L&P$P5+$$:6&n$n%5+##1%:6&$%5+##III2-%:56L&$5+HH2-%# +4-:66L" $&Z 5+HH:M%# 6( " $&5+GG:B P 6 (2-&$%5+GG:D P 6(2-&$%5+GG32-%# +:.6z " $&5!+FF:B P'6 (2-&$%5+FF:D P'6(2-&$%5+FF32-%# +:.6z " $&5!+DDI:O2-%# 6LI" &$ (5+BB :D#d&$%62-5+AA :Cd&$%62-5+<<:B) d2-# +6 " $&%5+<< 32-# +6:+ " $&%5+==:C) d2-# +6 " $&1%5+== 32-# +6:, " $&1%53+;;:0%4-6$& 5+;;:2%4-6$&5+;;:2%4-6$&5+;;32-%4-:6L$&Z 5+:: d$&%62-:85 &$%( P3+77 62-:+5+7762-:)5 P3+55( %&$62-:+5+5562-:& $P%5+55(%&$62-:)5+44:= , & $%IIN 62-5+33 , &$%IIN 62-5+CC9&n$n%:*62-5`+32-%# +:6z " $&5!+( %&$62-:+5+62-:& $P%5+(%&$62-:)5+6 (2-:&P$P%5+6(2-:&P$P%5+32-%# +:60z " $&5!+ x&$%62-:5 +9d&$%:62-5`+9d&$%:62-5`+9d&&$&%:62-5`+9d&&$&%:62-5`+,,, &~$~%nn?! 62-5 x%(&$+66 n2-:465+660 n2-6:95+%6d$<&<:*5"+%2-:65+%%&f$f%# 460" dZ: 5"+%%2-%:605+99::%2-65+99d%&n$` 62-5 +88I n:F$%2-+4-6&%%5+88P$%:F2-6&5+88VPd$%:R2-6&53%&$+>>:2-6K5+>>0d$&%53%& $ +??:'2-6K5+??$&0d%53%&P$P+@@:32-6K5+@@$&0d%53+!!d32-%# +:60z " $P&P5!+!!62- n :(&P$P%5+!!62- n%&P$P:(5+""( &$%:062-5+""(&$%:/62-5+""d2-%:6w$&5+EE n2-%6"&$:Q5 )X(&P$P% Z3+''62-:+5+''362-:+53%I)&$+222-:(6K5+22&/$&0d%53%I)&$+002-:(6K5+00&/$&0d%53%I)&$+//2-:(6K5+//&/$/0d%53%I)&$+--2-:(6K5+--&&$/0d%53%I)&$+++2-:(6K5+++&/$/0d%53%)&$+))2-:(6K5+))$/&/0d%5+113 n$n2-04-:46 &` %5+113,$n062-6: &` %5+** n$2-0# +4-:06 &%5+**$062-6:&%5+.. n2-0+4-:46 &%5+..062-6:!&%5+  $& : 53+((d32-%# +:60z " $&5!+((62- :-(&$%5+((62-%&$:)(5+((62-$&%:*(5+$$%# 460" dZ$&:#Z 5"+##%6( dZ$&:,5"+&&32-%# +:60z " $&5!+&&( 2-%# +:6 " $&5+&&6 2-:!&$%5+&&62-: &$%5+""3d<%# +:0" Z $&5"+""4$"g32-%# :46K&5+222-%# +4-604;:"" Z$1&1 F5 +002-%# +4-604;:#" Z$& F5 +//2-%# +4-604;:%" Z$]&] F5 +..%# +4-:40 " $0&6 5+..%# +4-::6 ss" $o&o0 i5+..2-%# +4-:$60`  " $0& 5+..2-%# +4-:%6  " $0&0` 5+--2-%# +4-604;:&" Z$& F5 +++2-%# +4-604;:(" Z$`&`  5 +**%# +4-:00 " $s&s6 5+**2-%# +4-:!6  " $s&s0` 5+**2-%# +4-: 60`  " $s&s 5+**%# +4-:66 ss" $&0 i5+))2-%# +4-604;:)" Z$& F5 +((%# +4-:,6 " $1&1 0 K5+((2-%# +4-:60L" $&Z 5+((%# +4-:-6 " $1&10 K5+((%# +:,6 " $&4t K0Z5+''2-# +4-%:.60 " $&95+&&%# +4-:,60 " $& 5+&&0%# +:,6 " $&4t5+&&%# +4-:,60 " $]&]5+&&%# +4-:-60 " $&5+&&2-%# +4-:60L" $&Z 5+%%2-%# +4-:&6o0 " $&5+%%2-%# +4-:@6 " $&05+$$%# +4:0" Z N$&5"+$$32-%# +:46K "$&0N5+##%# +4: 0$W&W" Z '5"+%# +4-: 6 " $& 0 K5+2-%# +4-: 60L" $&Z 5+%# +4-:!6 " $&0 K5+%# +4-: 60 " $`&` i5+2-%# +4-: 60L" $&Z 5+%# +4-:!60 " $`&` i5+%# +4-: 60 " $& K5+2-%# +4:0$W&W" Z 5"+2-%# +4-:+6K0 "$0&05+2-%# +4:0$W&W" Z d5"+32-%#+:(6K0 "g$<&<,5+KK3 2-%# +4-:6 " $o&o5+GG2-%# 6" $&:E34d5+@@2-%# +:<6K " $&04_'5+??2-%# +:56K " $`&`04_'5+>>2-%# +:,6K " $&04_'5+882-%# +4:26$&$ " 0 U(5+882-%# 4-:860; ' " $& !5+222-%# +:6K" $&0(Z 4N5+112-%# +4-:+6 " $& 0s5+112-%# +:(60 " $&4b5+112-%# +4-:76 " 0$& 5+112-%# 4-60:# " $& =+5+002-%# +:6K" $&0(Z 4N5+//2-%# +4-:"6K" $1&10(Z N5+..2-0# +4-:46 " $&1% 5+..2-# +4-60;:! " $&1% 5+--2-%# +:$6K" $&0(Z 4N5+++2-%# +:&6K" $]&]0(Z N45+**2-0%# +4-:06  " $s&s5+**2-%# +4-60;: " $s&s 5+))2-%# +4-:(6K" $&(Z N5+((2-%# +4-:6L " &$m5+((%# +4-:,6/ " $&, y5+((%# +4-:06 " $&/ | 5+''0(32-# %:-6 " $&5+&&2-%# +4-:6L " &$m+5+&&%# +4-:%6 " $&6S5+&&%# +4-:&66 " $& <5+%%2-%$ # +4-:6  " 5+%%2-%$ # +4-:&6 " 5+$$2-%# +4-:(6L" &~$~Z '5+##2-%# +4-:46L " &$N5+2-%# +4-:6L " &s$s5+%# +4-:6 " $&6 5+%# +4-:66 " $&  5+2-%# +4-:6L " &$N5+%# +4-:!6' " $&  5+%# +4-: 6 " $&'  5+2-%# +4-:6L " &s$s5+2-%# +4-:6N " $&  s5+%# +4-:6 " $&N 5+2-%# +4-:6L " &$sm5+2-%# +4-: 6 " $&'  K5+2-%# +4-:!6' " $&  K5+2-%# +4-:(6L " &$ Q95+2-%# +4-:*6L" $ &5+2-%# +4-:(6L" &$ 5+2-%# +4-:/6LI" &$5+222-%# :6K" $&0'45+002-%# :6K" $&0'45+//2-%# 4-:"6K" $&0'5+--'2-%# :$6K" $&045+++'2-%# :&6K" $&045+))2-%# +4-:(6K" $&0'5+@@4)&$%:T62-5+??4(3)&a$a%:J62-5+>>43)&$%:=62-5+882-0%# :A6  " & $45+882-0%# :A6  " $&4_ r5+6662-%# 4 " &0r ]:A$5 +66 P2-%+4-60P:' " $& 5+..2-%# +4:/60 " &$Q 95 +..2-%# +4:460 " $o&o5 +**2-%# +4:+60 " $s&s 95 +**2-%$ # +4:/6 " 05 +((2-%# +4:60{ " $&5!+((2-%# +4:%6 " 0$&L5+((2-%# +4:06 " 04$&5 +((2-%# +:0604 " &$ 45 +''2-%4:'60T+  " $&#S5+''2-%#S+4-60: " $& 5+&&2-%# +4:60{ " &o$o5!+&&2-%# +:26 " &$ 4045 +&&2-%# +4:26 " $&045 +&&2-%# +4:%6 " 0Z&L$5+%%2-%$ # +4-:0  " 5+$$32-%# 60" &:+Z $5"+##Z2-%# 6" &:. $5"+""2-%# +: 6 " 4T0$o&o5+!!%# +4:604 " $&5+ 2-%4-: 60 " #$& x 5+55<2-# +4%604  " $J&J:45+44::$ # +4-%0&h " 95+332-:3%+4$h&O $5+GG2-%# 46 " &$ v :J5 +EE2-%# 46 "&$v:Q5 +@@2-%# +:<6K " $&04_N5+??2-%# +:56K " $&04_N5+>>%2-# +:,6K " &,04_N$,5+882-%# :?6  " &s V04$s4au5+882-%# +4a:?6  " $s&s04u5+662-%# 460 " &$v := o5 +662-%# 4-:"6  " $&0 <u5+222-%# +:6K" $&0(Z 4N5+112-0%# 4:76 " &$  !} p5 +112-%# +4:.6 " &0$5+002-%# +:6K" $&0(Z 4N5+//2-%# +4-:"6K" $&0(Z N5+..2-%# +4:/60 " &$Q5 +--2-%# +:$6K" $&0(Z 4N5+++2-%# +:&6K" $&0(Z N45+**2-%# +4:+60 " $s&s5 +))2-%# +4-:(6K" $&0(Z N5+((2-%# +4:60z " $o&o5!+((2-%# +4:$6 " 0$&5+((2-%# +4:16 " 0$& X'5 +((2-%# +4:060' " &$  Z5 +''2-0%# 4:'6' " $& ].5+''2-0%# 4:16' " $&r ]5+&&2-%# +4:60z " $&5!+&&2-%# +4:%6 " 0$s&sL5+&&2-%# +4:06 " '0$&5 +&&2-%# +4:/60' " &$ 5 +%%2-%$ # +4-:60 " 5+%%2-%# +4:%60 " $&5+$$2-%# +4:)60w " $&N5+##%# 460 " FZ$a&a:15"+ 2-%# +4:60z " $&5!+ 2-%# +4:+6 " '0$&5 + 2-%# +4:(60' " &$ 5 +2-%# +4:60 " &$ 5 +2-%# +4:60 " $s&s 5 +2-%# +4: 60z " $&5!+2-%# +4:&6 " 0$& K5 +2-%# +:&60 " &$  K4=5 +2-%# 460" Z$&:!Z '5"+2-%# +4:60z " $&5!+2-%# +4:&60' " &$ 5 +2-%# +4:)6 " '0$&`5 +2-%# +4:6 " 0$&L5+2-%# +4:60z " $&5!+2-%# +4:+60' " &$ 5 +2-%# +4:,6 " '0$&5 +2-%# +4:6 " 0$s&sL5+%# 460" FZ$&:Z 5"+%# 460 " FZ$&:"(5"&n$n <"g3 +R:T62-5&+:E62-5%+FQ:H62-5$+9:062-5#" d.#&$+E462-5*+FQ4:H62-5)+R]4:O62-5(+^4:T62-5'3 " d.#&$+E4:H62-5.+FQ4:T62-5-+R]4:[62-5,+^i4:_62-5+" d.#&$4:M62-5/3" d.#f&$+E4:862-5Q+FQ4:D62-5P+R]4 :P62-5O+^4:\62-5Nn" d.#f&$+E462-53+FQ4:H62-52+R]4:T62-51+^4:`62-50" d.#&$+E462-57+FQ4:G62-56+R]4A:T62-55+^4:`62-54" d.#&$64R2-:O583" d.#f&$4:P62-5:3" d.#&$4):M62-5;" d#&$4^:O62-59$4"r d#Y&+E462-5?+FQ4:H62-5>+R]4:O62-5=+^i4:T62-5<4" d.#&$+E462-5C+FQ4 :I62-5B+R]4:P62-5A+^i4:T62-5@" d.#&$4/:Q62-5D4" d.#&$4S:N62-5F" d.#&$4-:N62-5E" d.#&$4):N62-5G" d.#&$.493:R62-5H4" d.#&$4:L62-5I" d.#&$+E4:H62-5M+FQ4:T62-5L+R]4:[62-5K+^i4:`62-5J3" d.#&$+E4:-62-5U+FQ4:962-5T+R]4:E62-5S+^i4:Q62-5R" d.#&$+E4:-62-5Y+FQ4:962-5X+R]4:E62-5W+^i4:Q62-5V" d.#&$+E4:-62-5]+FQ4:962-5\+R]4:E62-5[+^i4:Q62-5Z" d.#&$:K62-5^" d.#&$+E4:-62-5b+FQ4:962-5a+R]4:E62-5`+^i4:Q62-5_"g d.#&$+E4:-62-5f+FQ4:962-5e+R]4:E62-5d+^i4:Q62-5c" d.#&$+E4:-62-5j+FQ4:962-5i+R]4:E62-5h+^i4:Q62-5g" d.#&$+E4:-62-5n+FQ4:962-5m+R]4:E62-5l+^i4:Q62-5k" d.#&$+E4:-62-5r+FQ4:962-5q+R]4:E62-5p+^i4:Q62-5o" d.#&$:K62-5s" d.#&$+E4:-62-5w+FQ4:962-5v+R]4:E62-5u+^i4:Q62-5t" d.#&$+E4:-62-5{+FQ4:962-5z+R]462-:E5y+^i4:Q62-5xshdrvSineWaveTriangleJFD<FM4-04400 lf}<Tri2q AltP}<Tri-Sq AFilter Q9}<FM5-04400 (}<Bell2-0440 WQ}<cowbell w}<claploopAAuenc.h6`D<white5  &&D<sinehi  &]'&V'D<white808-2B}'@'j@K}<bdloop @B@@B}<THROB DRUMb Drum* BsP L$PD<909BD11PQPQD<CLAP21QdRdD<SQUARELOdfdfD<NOISE6f{f{D<SQUARE2;{{<{{D<CLAP2{{D<NOISE3D<NOISE5D<SINEHID<1$7%7D<LASER1WXD<RIM''D<CLICKGHD<TRIHI >>D<SINE32^_D<WHITE4IID<TRIANGLEijD<808BD~ ~ D<WHITE2  D<WHITE32,2,D<BD1R,.S,.D<TR909BD/>/>D<wav1_1>A>A}<wav1_3ABAIB}<wav1_2BVD C-D}<wav1_4vD+EDE}<SAW4KEEeEED<SAW3EFEFD<SAW24FF[FFD<SAW1FG/GGD<W12_4H;HH;HD<W13_3[HH\HHD<W12_2H-IHID<W12_1MIJeIJD<W10000r>M 1JJ;JJD<W7_4JaKKZKD<W7_3KdLKPLD<W7_2L-NL#ND<W7_1MNQNQD<W6_4Q,RQRD<W6_3LR/SiRSD<W6_2OS/USUD<W6_1OU1YRUWD<MOD2_C3QYYoYYD<FM90000r>MY[yZ[D<191r>M[\\\D<WAV5r>M\']\\D<SQ4G]]Z]]D<SQ3]]]]D<SQ2^^;^^D<SQ1^_^_D<HR4____D<HR3`f`,`b`D<HR2````D<HR1aa+aaD<HOLLOWTH]ld 1 a\cybGcD<BELL4r>Mh|c ec eD<GRUNGE[Coarse T-e5g8f2gD<BELL10000Ug"ihhD<HISSBiɏBiɏD<AAHHAD<W8_4hwD<W8_3ȐސD<W8_2>RD<W8_1֑D<dm4_a4 Ytoff *ےgMD<dm4_a39D<dm4_a2YY!D<dm4_a19D<dm3_a4ΗΗD<dm3_a3;՘;D<dm3_a2D<dm3_a1,D<dm2_a4uD<dm2_a3D<dm2_a2?-KD<dm2_a1MdD<dm1_a4 D<dm1_a3-D<dm1_a2M;UD<dm1_a1[3`D<Noise38XYS[D<Tri_a4TUeqXX,D<Tri_a3TUeqXXLD<Tri_a2TUeqXX6dh1D<Tri_a1TUFrequenc޻zD<Sq_a4UHU U}HzD<Sq_a3UHU Ugм4D<Sq_a2UHU UD<Sq_a1UHU* Uվ/*D<Sine_a4 U* UOD<Sine_a3 U* U;D<Sine_a2 U* U D<Sine_a1 U* U&&D<Saw_a4cU* pYD<Saw_a3cU* pY? bD<Saw_a2cU* pY)W1D<Saw_a1cU*** **wD<add3_a4x Vyn TWD<add3_a3x Vyn TlWD<add3_a2x Vyn T{D<add3_a1x VKeynum T)D<Noise4Txcay **!^D<Fm2_a4VWAVD<Fm2_a3VWxLD<Fm2_a2VWpD<Fm2_a1VD<Fm3_a4  }7iD<Fm3_a3  aRD<Fm3_a2  #D<Fm3_a1 !eD<EOSfluidsynth-2.2.5/sf2/VintageDreamsWaves-v2.sf2.yml000066400000000000000000015220301417326347500217220ustar00rootroot00000000000000samplepos: 464 samplesize: 252534 sample24pos: 0 sample24size: 0 presets: - preset: 0 name: FM Bells 1 bank: 0 num: 0 global_zone: name: pz:FM Bells 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -884.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:FM Bells 1/2 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:FM Bells 1/1 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 1 name: FM Carillion bank: 0 num: 1 global_zone: name: pz:FM Carillion/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -884.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:FM Carillion/2 instrument: FM 2 (index 236) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:FM Carillion/1 instrument: FM 2 (index 236) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 2 name: Simple Square Floot bank: 0 num: 2 global_zone: name: pz:Simple Square Floot/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Simple Square Floot/1 instrument: SQUARE WAVE (index 231) key_range: 0 - 128 vel_range: 0 - 128 - preset: 3 name: Grungy Ramp Bass bank: 0 num: 3 global_zone: name: pz:Grungy Ramp Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -6500.00 MODLFOTOVOL: 20.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -1200.00 MODENVHOLD: -3986.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Grungy Ramp Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -5.00 - zone: 1 name: pz:Grungy Ramp Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 5.00 - preset: 4 name: Detuned Saws bank: 0 num: 4 global_zone: name: pz:Detuned Saws/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Detuned Saws/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Detuned Saws/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 2 name: pz:Detuned Saws/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 5 name: El Cheapo Organ bank: 0 num: 5 global_zone: name: pz:El Cheapo Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 10.00 CHORUSSEND: 400.00 MODLFOFREQ: -2084.00 VIBLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:El Cheapo Organ/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:El Cheapo Organ/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 6 name: Dragon Sweep bank: 0 num: 6 global_zone: name: pz:Dragon Sweep/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: 110.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Dragon Sweep/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Dragon Sweep/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 7 name: Sheet Bass bank: 0 num: 7 global_zone: name: pz:Sheet Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Sheet Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Sheet Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 8 name: Resonating Pad bank: 0 num: 8 global_zone: name: pz:Resonating Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -1166.00 FILTERQ: 110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODLFOFREQ: -884.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 6386.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 2786.00 zones: - zone: 0 name: pz:Resonating Pad/2 instrument: DM 1 (index 228) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Resonating Pad/1 instrument: DM 1 (index 228) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 9 name: Hard Grunge Bass bank: 0 num: 9 global_zone: name: pz:Hard Grunge Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -7200.00 MODLFOTOVOL: 20.00 CHORUSSEND: 500.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -1200.00 MODENVHOLD: -3986.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Hard Grunge Bass/2 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: 0.00 FINETUNE: -3.00 - zone: 1 name: pz:Hard Grunge Bass/1 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 10 name: FM Christmas Bells bank: 0 num: 10 global_zone: name: pz:FM Christmas Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 60.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:FM Christmas Bells/2 instrument: DM 3 (index 226) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:FM Christmas Bells/1 instrument: DM 3 (index 226) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 11 name: Meat Grinder bank: 0 num: 11 global_zone: name: pz:Meat Grinder/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 80.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Meat Grinder/2 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: 0.00 FINETUNE: -3.00 - zone: 1 name: pz:Meat Grinder/1 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 12 name: Church Organ bank: 0 num: 12 global_zone: name: pz:Church Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 200.00 REVERBSEND: 100.00 VOLENVRELEASE: 454.00 zones: - zone: 0 name: pz:Church Organ/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Church Organ/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 13 name: Classic FM Bass bank: 0 num: 13 global_zone: name: pz:Classic FM Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -50.00 MODENVTOFILTERFC: -7200.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 MODENVATTACK: -617.00 MODENVRELEASE: 3641.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 VOLENVSUSTAIN: 960.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Classic FM Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Classic FM Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 14 name: Thin Sawtooth bank: 0 num: 14 global_zone: name: pz:Thin Sawtooth/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 VOLENVATTACK: -884.00 VOLENVRELEASE: 748.00 zones: - zone: 0 name: pz:Thin Sawtooth/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 - preset: 15 name: Triangle Dream Flute bank: 0 num: 15 global_zone: name: pz:Triangle Dream Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODLFOFREQ: -1817.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3671.00 VOLENVRELEASE: 2786.00 zones: - zone: 0 name: pz:Triangle Dream Flute/2 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Triangle Dream Flute/1 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 16 name: Triangle Simple bank: 0 num: 16 global_zone: name: pz:Triangle Simple/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 17.00 CHORUSSEND: 400.00 MODLFOFREQ: 0.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 454.00 zones: - zone: 0 name: pz:Triangle Simple/2 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Triangle Simple/1 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 17 name: Smooth Flute bank: 0 num: 17 global_zone: name: pz:Smooth Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERFC: -3986.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODLFOFREQ: -1817.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1200.00 VOLENVRELEASE: 1586.00 zones: - zone: 0 name: pz:Smooth Flute/2 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Smooth Flute/1 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 18 name: Smooth Strings 1 bank: 0 num: 18 global_zone: name: pz:Smooth Strings 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -3986.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 400.00 MODLFOFREQ: -884.00 VOLENVATTACK: 919.00 VOLENVRELEASE: 1745.00 zones: - zone: 0 name: pz:Smooth Strings 1/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Smooth Strings 1/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 19 name: FM Electric Bass bank: 0 num: 19 global_zone: name: pz:FM Electric Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 300.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: -1200.00 zones: - zone: 0 name: pz:FM Electric Bass/2 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:FM Electric Bass/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 20 name: TX81Z Lately Bass bank: 0 num: 20 zones: - zone: 0 name: pz:TX81Z Lately Bass/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 PAN: 1000.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 814.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 814.00 - zone: 1 name: pz:TX81Z Lately Bass/2 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 300.00 PAN: 1000.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 COARSETUNE: 12.00 - zone: 2 name: pz:TX81Z Lately Bass/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 300.00 PAN: -1000.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 COARSETUNE: 12.00 FINETUNE: -1.00 - zone: 3 name: pz:TX81Z Lately Bass/0 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 PAN: -1000.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 814.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 814.00 FINETUNE: -1.00 - preset: 21 name: Pop Bass 1 bank: 0 num: 21 global_zone: name: pz:Pop Bass 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Pop Bass 1/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Pop Bass 1/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 5.00 - preset: 22 name: Distorted Sweep Bass bank: 0 num: 22 global_zone: name: pz:Distorted Sweep Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 1516.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 VOLENVRELEASE: 814.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Distorted Sweep Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: -12.00 FINETUNE: -5.00 - zone: 1 name: pz:Distorted Sweep Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 5.00 - preset: 23 name: Square Flute bank: 0 num: 23 global_zone: name: pz:Square Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 10.00 CHORUSSEND: 200.00 MODLFOFREQ: -386.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 3802.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Square Flute/2 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - zone: 1 name: pz:Square Flute/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - preset: 24 name: Chorale bank: 0 num: 24 global_zone: name: pz:Chorale/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 17.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VOLENVATTACK: 2169.00 VOLENVRELEASE: 1586.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:Chorale/2 instrument: AAHS (index 224) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -2.00 - zone: 1 name: pz:Chorale/1 instrument: AAHS (index 224) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 2.00 - preset: 25 name: Breezy Calliope bank: 0 num: 25 global_zone: name: pz:Breezy Calliope/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 500.00 REVERBSEND: 80.00 MODLFOFREQ: -1586.00 VOLENVATTACK: -1200.00 zones: - zone: 0 name: pz:Breezy Calliope/3 instrument: AAHS (index 224) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 1 name: pz:Breezy Calliope/2 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Breezy Calliope/1 instrument: AAHS (index 224) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 26 name: D50-ish Bells bank: 0 num: 26 global_zone: name: pz:D50-ish Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 500.00 MODLFOFREQ: -1586.00 VOLENVATTACK: -1200.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:D50-ish Bells/2 instrument: BELLS (index 222) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: -617.00 - zone: 1 name: pz:D50-ish Bells/1 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: -3986.00 VOLENVRELEASE: 1902.00 - preset: 27 name: Bingo Bells bank: 0 num: 27 global_zone: name: pz:Bingo Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 25.00 CHORUSSEND: 300.00 MODLFOFREQ: 0.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Bingo Bells/2 instrument: BELLS (index 222) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 VOLENVATTACK: -3986.00 - zone: 1 name: pz:Bingo Bells/1 instrument: BELLS (index 222) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 VOLENVATTACK: -3986.00 FINETUNE: 1.00 - preset: 28 name: Electric Slap Bass bank: 0 num: 28 global_zone: name: pz:Electric Slap Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 200.00 REVERBSEND: 60.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Electric Slap Bass/2 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 VOLENVDECAY: 17.00 VOLENVRELEASE: 17.00 - zone: 1 name: pz:Electric Slap Bass/1 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -1200.00 FILTERQ: -110.00 ATTENUATION: 80.00 FINETUNE: -3.00 - preset: 29 name: Fantasy bank: 0 num: 29 global_zone: name: pz:Fantasy/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 50.00 MODLFOTOVOL: 80.00 CHORUSSEND: 300.00 MODLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Fantasy/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Fantasy/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 30 name: Vatican Pipes bank: 0 num: 30 global_zone: name: pz:Vatican Pipes/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 200.00 REVERBSEND: 400.00 MODLFOFREQ: -1200.00 VOLENVATTACK: -884.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Vatican Pipes/2 instrument: PIPES (index 221) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:Vatican Pipes/1 instrument: PIPES (index 221) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - preset: 31 name: Bass Dragon Choir bank: 0 num: 31 global_zone: name: pz:Bass Dragon Choir/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 16.00 CHORUSSEND: 500.00 MODLFOFREQ: -1586.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: 316.00 zones: - zone: 0 name: pz:Bass Dragon Choir/2 instrument: GRUNGE (index 220) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Bass Dragon Choir/1 instrument: GRUNGE (index 220) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 32 name: Cosmic Vibraphone bank: 0 num: 32 global_zone: name: pz:Cosmic Vibraphone/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 30.00 FILTERQ: -110.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 MODENVDELAY: -3986.00 MODENVATTACK: 0.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Cosmic Vibraphone/2 instrument: VIBRAPHONE (index 219) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Cosmic Vibraphone/1 instrument: VIBRAPHONE (index 219) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 33 name: Water Triangle bank: 0 num: 33 global_zone: name: pz:Water Triangle/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 80.00 VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 17.00 CHORUSSEND: 300.00 MODLFOFREQ: -386.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 919.00 zones: - zone: 0 name: pz:Water Triangle/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - preset: 34 name: Square Pop Flute bank: 0 num: 34 global_zone: name: pz:Square Pop Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -3500.00 MODLFOTOVOL: 40.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 MODLFOFREQ: -1200.00 MODENVDELAY: -2875.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVRELEASE: 2837.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: -884.00 zones: - zone: 0 name: pz:Square Pop Flute/1 instrument: SQUARE WAVE (index 231) key_range: 0 - 128 vel_range: 0 - 128 - preset: 35 name: Phasing Strings bank: 0 num: 35 global_zone: name: pz:Phasing Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1200.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Phasing Strings/2 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:Phasing Strings/1 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - preset: 36 name: Vatican Bell bank: 0 num: 36 global_zone: name: pz:Vatican Bell/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 300.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -884.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Vatican Bell/2 instrument: BELLS (index 222) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1902.00 FINETUNE: -2.00 - zone: 1 name: pz:Vatican Bell/1 instrument: PIPES (index 221) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 FINETUNE: 2.00 - preset: 37 name: Sine Whistle bank: 0 num: 37 global_zone: name: pz:Sine Whistle/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 60.00 FILTERQ: -110.00 MODLFOTOVOL: 35.00 CHORUSSEND: 200.00 MODLFOFREQ: -1200.00 VOLENVATTACK: 3986.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Sine Whistle/2 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Sine Whistle/1 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 38 name: Sine Bongos bank: 0 num: 38 global_zone: name: pz:Sine Bongos/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODLFOTOVOL: 15.00 CHORUSSEND: 150.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 zones: - zone: 0 name: pz:Sine Bongos/2 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 1 name: pz:Sine Bongos/1 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 39 name: Space Warp bank: 0 num: 39 global_zone: name: pz:Space Warp/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: 316.00 zones: - zone: 0 name: pz:Space Warp/2 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 FINETUNE: -1.00 - zone: 1 name: pz:Space Warp/1 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 FINETUNE: 1.00 - preset: 40 name: Aluminium Plate bank: 0 num: 40 global_zone: name: pz:Aluminium Plate/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VOLENVATTACK: -1200.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Aluminium Plate/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Aluminium Plate/1 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 41 name: Lead Synth 1 bank: 0 num: 41 global_zone: name: pz:Lead Synth 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 MODENVTOFILTERFC: -3716.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVDECAY: 1902.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1586.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Lead Synth 1/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:Lead Synth 1/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 42 name: Lead Synth 2 bank: 0 num: 42 global_zone: name: pz:Lead Synth 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: 110.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 1586.00 VOLENVRELEASE: 1586.00 zones: - zone: 0 name: pz:Lead Synth 2/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:Lead Synth 2/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 43 name: Lead Synth 3 bank: 0 num: 43 global_zone: name: pz:Lead Synth 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 1586.00 VOLENVRELEASE: 1586.00 zones: - zone: 0 name: pz:Lead Synth 3/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:Lead Synth 3/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: -12.00 FINETUNE: 3.00 - preset: 44 name: Casio VL-1 Pops bank: 0 num: 44 global_zone: name: pz:Casio VL-1 Pops/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 17.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 MODLFOFREQ: 0.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Casio VL-1 Pops/2 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Casio VL-1 Pops/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 45 name: Wailing Hit bank: 0 num: 45 global_zone: name: pz:Wailing Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 19.00 FILTERQ: -110.00 MODENVTOFILTERFC: -5100.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODLFOFREQ: -1067.00 VIBLFOFREQ: -261.00 MODENVDELAY: -3986.00 MODENVATTACK: -1200.00 MODENVHOLD: -3986.00 MODENVDECAY: -617.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 VOLENVRELEASE: 2786.00 zones: - zone: 0 name: pz:Wailing Hit/2 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Wailing Hit/1 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 46 name: Oink Grind bank: 0 num: 46 global_zone: name: pz:Oink Grind/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 zones: - zone: 0 name: pz:Oink Grind/1 instrument: OINK (index 215) key_range: 0 - 128 vel_range: 0 - 128 - preset: 47 name: Metallic Clink bank: 0 num: 47 global_zone: name: pz:Metallic Clink/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 zones: - zone: 0 name: pz:Metallic Clink/2 instrument: CLINK (index 216) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Metallic Clink/1 instrument: CLINK (index 216) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 48 name: Faerie Chorale bank: 0 num: 48 global_zone: name: pz:Faerie Chorale/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -746.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 3986.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Faerie Chorale/2 instrument: FAERIE CHORAL (index 213) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:Faerie Chorale/1 instrument: FAERIE CHORAL (index 213) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 - preset: 49 name: FM Bass Hit bank: 0 num: 49 global_zone: name: pz:FM Bass Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 60.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -386.00 zones: - zone: 0 name: pz:FM Bass Hit/1 instrument: HARSH FM BASS (index 212) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 - preset: 50 name: Dream Hit bank: 0 num: 50 global_zone: name: pz:Dream Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -7200.00 MODLFOTOVOL: 40.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 MODENVDELAY: -2786.00 MODENVATTACK: 2894.00 MODENVHOLD: -3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Dream Hit/1 instrument: FM BELL 2 (index 211) key_range: 0 - 128 vel_range: 0 - 128 - preset: 51 name: Gated FM Bass bank: 0 num: 51 global_zone: name: pz:Gated FM Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 MODENVDELAY: -3986.00 MODENVATTACK: -3284.00 MODENVHOLD: -3986.00 MODENVRELEASE: 3369.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Gated FM Bass/1 instrument: HARSH FM BASS (index 212) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 - preset: 52 name: Harsh FM Bass bank: 0 num: 52 global_zone: name: pz:Harsh FM Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODENVDELAY: -3986.00 MODENVATTACK: 1902.00 MODENVHOLD: -3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 260.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Harsh FM Bass/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 700.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 - zone: 1 name: pz:Harsh FM Bass/2 instrument: HARSH FM BASS (index 212) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - zone: 2 name: pz:Harsh FM Bass/1 instrument: HARSH FM BASS (index 212) key_range: 0 - 128 vel_range: 0 - 128 - preset: 53 name: Singing Bells bank: 0 num: 53 global_zone: name: pz:Singing Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 20.00 FILTERQ: -110.00 MODLFOTOVOL: 40.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Singing Bells/1 instrument: FM BELL 2 (index 211) key_range: 0 - 128 vel_range: 0 - 128 - preset: 54 name: Phasing Choir bank: 0 num: 54 global_zone: name: pz:Phasing Choir/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -40.00 FILTERQ: -110.00 MODLFOTOFILTERFC: 747.00 MODLFOTOVOL: 20.00 CHORUSSEND: 220.00 MODLFOFREQ: -884.00 VIBLFOFREQ: -884.00 VOLENVDELAY: 2400.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Phasing Choir/2 instrument: FM3 (index 210) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:Phasing Choir/1 instrument: FM3 (index 210) key_range: 0 - 128 vel_range: 0 - 128 - preset: 55 name: FM Clang bank: 0 num: 55 global_zone: name: pz:FM Clang/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 25.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:FM Clang/1 instrument: FM CLANG (index 209) key_range: 0 - 128 vel_range: 0 - 128 - preset: 56 name: China Voices bank: 0 num: 56 global_zone: name: pz:China Voices/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -600.00 MODLFOTOVOL: 25.00 CHORUSSEND: 200.00 MODLFOFREQ: 0.00 MODENVDELAY: -2084.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVRELEASE: 1902.00 COARSETUNE: 6.00 zones: - zone: 0 name: pz:China Voices/2 instrument: FM CLANG (index 209) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:China Voices/1 instrument: FM CLANG (index 209) key_range: 0 - 128 vel_range: 0 - 128 - preset: 57 name: Rubber Bass bank: 0 num: 57 global_zone: name: pz:Rubber Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: -2084.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Rubber Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -5.00 - zone: 1 name: pz:Rubber Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 5.00 - preset: 58 name: Polysynth Warp bank: 0 num: 58 global_zone: name: pz:Polysynth Warp/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 MODENVTOFILTERFC: -3716.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -386.00 MODENVDELAY: -1586.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVDECAY: -3284.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2786.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Polysynth Warp/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:Polysynth Warp/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 59 name: Wavepad bank: 0 num: 59 global_zone: name: pz:Wavepad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 40.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 1586.00 VOLENVRELEASE: 1586.00 zones: - zone: 0 name: pz:Wavepad/3 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -4500.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 0.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6602.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 3102.00 VOLENVRELEASE: 3102.00 - zone: 1 name: pz:Wavepad/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -884.00 FILTERQ: 180.00 MODENVTOFILTERFC: -3000.00 MODENVDELAY: -7973.00 MODENVATTACK: 2400.00 MODENVHOLD: -7973.00 MODENVDECAY: 2786.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6663.00 VOLENVHOLD: -7973.00 - zone: 2 name: pz:Wavepad/1 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: 2400.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 - preset: 60 name: Pop Bass 2 bank: 0 num: 60 global_zone: name: pz:Pop Bass 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 20.00 CHORUSSEND: 400.00 REVERBSEND: 0.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 702.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Pop Bass 2/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 - zone: 1 name: pz:Pop Bass 2/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 2 name: pz:Pop Bass 2/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 61 name: Stacked Mega-Phaser bank: 0 num: 61 global_zone: name: pz:Stacked Mega-Phaser/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Stacked Mega-Phaser/4 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: 1046.00 FINETUNE: -4.00 - zone: 1 name: pz:Stacked Mega-Phaser/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -2.00 - zone: 2 name: pz:Stacked Mega-Phaser/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 2.00 - zone: 3 name: pz:Stacked Mega-Phaser/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: -1344.00 FINETUNE: 3.00 - preset: 62 name: Screaming Wavepad bank: 0 num: 62 global_zone: name: pz:Screaming Wavepad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 VIBLFOTOPITCH: 10.00 MODLFOTOVOL: 40.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 2786.00 VOLENVRELEASE: 2786.00 zones: - zone: 0 name: pz:Screaming Wavepad/2 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -3500.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 0.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6602.00 VOLENVHOLD: -7973.00 FINETUNE: -1.00 - zone: 1 name: pz:Screaming Wavepad/1 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -3500.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 0.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6602.00 VOLENVHOLD: -7973.00 FINETUNE: 0.00 - preset: 63 name: Melodic Vibrato bank: 0 num: 63 global_zone: name: pz:Melodic Vibrato/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 38.00 FILTERQ: -180.00 MODLFOTOVOL: 18.00 CHORUSSEND: 500.00 MODLFOFREQ: 316.00 VIBLFOFREQ: -365.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 643.00 zones: - zone: 0 name: pz:Melodic Vibrato/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 - preset: 64 name: Twips Ring bank: 0 num: 64 global_zone: name: pz:Twips Ring/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERFC: -2786.00 FILTERQ: 110.00 MODENVTOFILTERFC: 7200.00 MODLFOTOVOL: 30.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVATTACK: -884.00 MODENVHOLD: -7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 zones: - zone: 0 name: pz:Twips Ring/3 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 VOLENVATTACK: 2067.00 VOLENVDECAY: 3600.00 VOLENVRELEASE: 1200.00 - zone: 1 name: pz:Twips Ring/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -3.00 - zone: 2 name: pz:Twips Ring/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 3.00 - preset: 65 name: Wah bank: 0 num: 65 global_zone: name: pz:Wah/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERFC: -3671.00 FILTERQ: 100.00 MODENVTOFILTERFC: 7200.00 MODLFOTOVOL: 30.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: -884.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 0.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Wah/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Wah/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 66 name: Blistering Bells bank: 0 num: 66 global_zone: name: pz:Blistering Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 58.00 FILTERQ: 180.00 MODLFOTOVOL: 58.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 MODLFOFREQ: -884.00 VIBLFOFREQ: -365.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -365.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Blistering Bells/2 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -2.00 - zone: 1 name: pz:Blistering Bells/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 2.00 - preset: 67 name: Click Pops bank: 0 num: 67 global_zone: name: pz:Click Pops/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 REVERBSEND: 20.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -5186.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5186.00 zones: - zone: 0 name: pz:Click Pops/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 300.00 REVERBSEND: 40.00 VOLENVDECAY: -3284.00 VOLENVRELEASE: -3284.00 COARSETUNE: -20.00 - zone: 1 name: pz:Click Pops/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 - preset: 68 name: Xylophone bank: 0 num: 68 global_zone: name: pz:Xylophone/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 30.00 FILTERQ: -180.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: 0.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 814.00 zones: - zone: 0 name: pz:Xylophone/2 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -2.00 - zone: 1 name: pz:Xylophone/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 2.00 - preset: 69 name: New Life bank: 0 num: 69 global_zone: name: pz:New Life/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 500.00 REVERBSEND: 100.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -746.00 zones: - zone: 0 name: pz:New Life/2 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:New Life/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 70 name: Long Bass bank: 0 num: 70 global_zone: name: pz:Long Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 500.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: pz:Long Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -4604.00 - zone: 1 name: pz:Long Bass/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 - preset: 71 name: Banshee Pad bank: 0 num: 71 global_zone: name: pz:Banshee Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 0.00 VIBLFOTOPITCH: 140.00 FILTERQ: -180.00 MODLFOTOVOL: 30.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VIBLFOFREQ: -386.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 4688.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Banshee Pad/2 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 1 name: pz:Banshee Pad/1 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 72 name: Undulating Pad bank: 0 num: 72 global_zone: name: pz:Undulating Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 0.00 FILTERQ: 100.00 MODLFOTOFILTERFC: 1675.00 MODLFOTOVOL: 30.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Undulating Pad/2 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - zone: 1 name: pz:Undulating Pad/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - preset: 73 name: Spudge Bass bank: 0 num: 73 global_zone: name: pz:Spudge Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -6000.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3102.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3102.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 5728.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -386.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Spudge Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Spudge Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 5.00 - preset: 74 name: Warble Pad bank: 0 num: 74 global_zone: name: pz:Warble Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 100.00 MODLFOTOFILTERFC: 2380.00 MODLFOTOVOL: 50.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VIBLFOFREQ: -386.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 4688.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Warble Pad/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: -1344.00 VOLENVATTACK: -365.00 VOLENVDECAY: 3369.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 FINETUNE: 3.00 - zone: 1 name: pz:Warble Pad/1 instrument: FM BELL 2 (index 211) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 856.00 - preset: 75 name: Yazoo Blips bank: 0 num: 75 global_zone: name: pz:Yazoo Blips/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1544.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1544.00 ATTENUATION: 24.00 zones: - zone: 0 name: pz:Yazoo Blips/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: 4379.00 VOLENVDECAY: -856.00 VOLENVRELEASE: 330.00 - zone: 1 name: pz:Yazoo Blips/1 instrument: OINK (index 215) key_range: 0 - 128 vel_range: 0 - 128 - preset: 76 name: TX81Z Sqncr Bass bank: 0 num: 76 global_zone: name: pz:TX81Z Sqncr Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 500.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: pz:TX81Z Sqncr Bass/2 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -7200.00 CHORUSSEND: 560.00 MODENVDELAY: -7973.00 MODENVATTACK: -1544.00 MODENVHOLD: -7973.00 - zone: 1 name: pz:TX81Z Sqncr Bass/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 700.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 - preset: 77 name: Acid Sub Bass bank: 0 num: 77 global_zone: name: pz:Acid Sub Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 40.00 MODLFOTOFILTERFC: 7200.00 MODENVTOFILTERFC: -5500.00 CHORUSSEND: 400.00 REVERBSEND: 0.00 MODLFOFREQ: -746.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 454.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -498.00 zones: - zone: 0 name: pz:Acid Sub Bass/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 - zone: 1 name: pz:Acid Sub Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 2 name: pz:Acid Sub Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 78 name: Echo Pop Bass bank: 0 num: 78 global_zone: name: pz:Echo Pop Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODLFOTOFILTERFC: 7200.00 MODENVTOFILTERFC: -5500.00 CHORUSSEND: 400.00 REVERBSEND: 0.00 MODLFOFREQ: -884.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 814.00 zones: - zone: 0 name: pz:Echo Pop Bass/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 - zone: 1 name: pz:Echo Pop Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 2 name: pz:Echo Pop Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 79 name: Acid Bass 2 bank: 0 num: 79 global_zone: name: pz:Acid Bass 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODLFOTOFILTERFC: -7200.00 MODENVTOFILTERFC: -5800.00 CHORUSSEND: 400.00 REVERBSEND: 0.00 MODLFODELAY: 7973.00 MODLFOFREQ: -1035.00 MODENVDELAY: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Acid Bass 2/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Acid Bass 2/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 80 name: Monster Stack Lead bank: 0 num: 80 global_zone: name: pz:Monster Stack Lead/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: 100.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Monster Stack Lead/4 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -4871.00 MODENVTOFILTERFC: 7200.00 MODENVDELAY: -7973.00 MODENVATTACK: 17.00 MODENVHOLD: -7973.00 MODENVDECAY: -7973.00 MODENVRELEASE: 702.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 - zone: 1 name: pz:Monster Stack Lead/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 500.00 COARSETUNE: -12.00 - zone: 2 name: pz:Monster Stack Lead/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - zone: 3 name: pz:Monster Stack Lead/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: -793.00 FINETUNE: 1.00 - preset: 81 name: Panned Stack bank: 0 num: 81 global_zone: name: pz:Panned Stack/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 zones: - zone: 0 name: pz:Panned Stack/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -2786.00 MODENVTOFILTERFC: 3000.00 MODLFOTOVOL: 240.00 PAN: -1000.00 MODLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: 3369.00 MODENVHOLD: -7973.00 - zone: 1 name: pz:Panned Stack/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -3000.00 MODLFOTOVOL: -240.00 PAN: 1000.00 MODLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: 3102.00 MODENVHOLD: -7973.00 - preset: 82 name: Ethnic Bow bank: 0 num: 82 zones: - zone: 0 name: pz:Ethnic Bow/0 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -2786.00 MODENVTOFILTERFC: 3000.00 MODLFOTOVOL: 240.00 CHORUSSEND: 400.00 REVERBSEND: 80.00 MODLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: 3369.00 MODENVHOLD: -7973.00 - preset: 83 name: Noisy Pops bank: 0 num: 83 global_zone: name: pz:Noisy Pops/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 MODLFOTOVOL: 17.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 MODLFOFREQ: 0.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 zones: - zone: 0 name: pz:Noisy Pops/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 700.00 REVERBSEND: 100.00 VOLENVDECAY: -617.00 VOLENVRELEASE: -617.00 COARSETUNE: -12.00 - zone: 1 name: pz:Noisy Pops/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 - preset: 84 name: House Organ bank: 0 num: 84 global_zone: name: pz:House Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 10.00 CHORUSSEND: 400.00 MODLFOFREQ: -2084.00 VIBLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: -2084.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:House Organ/4 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 VOLENVATTACK: 2786.00 COARSETUNE: -12.00 - zone: 1 name: pz:House Organ/3 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 REVERBSEND: 140.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 FINETUNE: 3.00 - zone: 2 name: pz:House Organ/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: 2786.00 ATTENUATION: 84.00 FINETUNE: 0.00 - zone: 3 name: pz:House Organ/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: 2786.00 ATTENUATION: 84.00 FINETUNE: 1.00 - preset: 85 name: Bonky Organ bank: 0 num: 85 global_zone: name: pz:Bonky Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 0.00 VIBLFOTOPITCH: 60.00 FILTERQ: -180.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -2084.00 VIBLFOFREQ: -386.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Bonky Organ/3 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: REVERBSEND: 40.00 VOLENVATTACK: -7973.00 ATTENUATION: 40.00 COARSETUNE: -12.00 - zone: 1 name: pz:Bonky Organ/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 VOLENVATTACK: -7973.00 ATTENUATION: 40.00 FINETUNE: -2.00 - zone: 2 name: pz:Bonky Organ/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 VOLENVATTACK: -7973.00 ATTENUATION: 40.00 FINETUNE: 2.00 - preset: 86 name: Sustained Harp bank: 0 num: 86 global_zone: name: pz:Sustained Harp/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 19.00 MODLFOTOVOL: 9.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: 2098.00 VIBLFOFREQ: 1586.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 2067.00 zones: - zone: 0 name: pz:Sustained Harp/2 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Sustained Harp/1 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 87 name: Awesome Strings bank: 0 num: 87 global_zone: name: pz:Awesome Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 19.00 FILTERFC: -4373.00 FILTERQ: -180.00 MODLFOTOVOL: 9.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: 2098.00 VIBLFOFREQ: 1586.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6386.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Awesome Strings/3 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Awesome Strings/2 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 2 name: pz:Awesome Strings/1 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 88 name: Rough Strings bank: 0 num: 88 global_zone: name: pz:Rough Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 7973.00 FILTERQ: -180.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 4688.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Rough Strings/2 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Rough Strings/1 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 89 name: Techno Bells bank: 0 num: 89 global_zone: name: pz:Techno Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -1200.00 VIBLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1586.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:Techno Bells/4 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 ATTENUATION: 80.00 COARSETUNE: -12.00 - zone: 1 name: pz:Techno Bells/3 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 REVERBSEND: 140.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 FINETUNE: 3.00 - zone: 2 name: pz:Techno Bells/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 VOLENVATTACK: 3986.00 ATTENUATION: 120.00 FINETUNE: -1.00 - zone: 3 name: pz:Techno Bells/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 VOLENVATTACK: 3986.00 ATTENUATION: 120.00 FINETUNE: 1.00 - preset: 90 name: Dreamy Pad bank: 0 num: 90 global_zone: name: pz:Dreamy Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 5888.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:Dreamy Pad/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 MODENVTOFILTERFC: -3526.00 VIBLFOFREQ: -1200.00 MODENVATTACK: 2565.00 MODENVHOLD: -7973.00 MODENVDECAY: 2014.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 1902.00 FINETUNE: 0.00 - zone: 1 name: pz:Dreamy Pad/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 MODENVTOFILTERFC: -3426.00 VIBLFOFREQ: -1035.00 MODENVATTACK: 2854.00 MODENVHOLD: -7973.00 MODENVDECAY: 2484.00 MODENVSUSTAIN: 1000.00 FINETUNE: 1.00 - preset: 91 name: Breath Pad bank: 0 num: 91 zones: - zone: 0 name: pz:Breath Pad/2 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -7200.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 MODENVDELAY: 1902.00 MODENVATTACK: 1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6386.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 5186.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 - zone: 1 name: pz:Breath Pad/1 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 20.00 MODENVTOFILTERFC: -100.00 MODLFOTOVOL: 30.00 CHORUSSEND: 600.00 MODLFOFREQ: -1586.00 VIBLFOFREQ: -617.00 MODENVATTACK: 2400.00 VOLENVATTACK: 6429.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 FINETUNE: 1.00 - zone: 2 name: pz:Breath Pad/0 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 20.00 MODENVTOFILTERFC: -100.00 MODLFOTOVOL: 40.00 CHORUSSEND: 600.00 MODLFOFREQ: -1586.00 VIBLFOFREQ: -593.00 MODENVATTACK: 1902.00 VOLENVATTACK: 6386.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 FINETUNE: -1.00 - preset: 92 name: Warehouse Percussion bank: 0 num: 92 global_zone: name: pz:Warehouse Percussion/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -180.00 MODLFOTOVOL: 15.00 CHORUSSEND: 800.00 REVERBSEND: 160.00 MODLFOFREQ: -1586.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 zones: - zone: 0 name: pz:Warehouse Percussion/2 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Warehouse Percussion/1 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 93 name: Sawtooth Hit bank: 0 num: 93 global_zone: name: pz:Sawtooth Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 100.00 CHORUSSEND: 500.00 REVERBSEND: 100.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 VOLENVSUSTAIN: 960.00 zones: - zone: 0 name: pz:Sawtooth Hit/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Sawtooth Hit/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -10.00 - zone: 2 name: pz:Sawtooth Hit/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 10.00 - preset: 94 name: Oingo-Boingo bank: 0 num: 94 global_zone: name: pz:Oingo-Boingo/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -1200.00 VIBLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -365.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:Oingo-Boingo/3 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 REVERBSEND: 140.00 PAN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 FINETUNE: 1.00 - zone: 1 name: pz:Oingo-Boingo/2 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 COARSETUNE: -12.00 - zone: 2 name: pz:Oingo-Boingo/1 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 REVERBSEND: 140.00 PAN: -1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 - preset: 95 name: Yazoo Zips bank: 0 num: 95 global_zone: name: pz:Yazoo Zips/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: 100.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 MODENVDELAY: -856.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: -7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Yazoo Zips/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Yazoo Zips/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Yazoo Zips/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 96 name: Yazoo Bass Hit bank: 0 num: 96 global_zone: name: pz:Yazoo Bass Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -180.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Yazoo Bass Hit/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Yazoo Bass Hit/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: -12.00 - zone: 2 name: pz:Yazoo Bass Hit/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 97 name: Zippy Bass bank: 0 num: 97 global_zone: name: pz:Zippy Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -180.00 MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVATTACK: -1544.00 MODENVHOLD: -7973.00 MODENVDECAY: 5222.00 MODENVRELEASE: 6296.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Zippy Bass/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Zippy Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDECAY: -386.00 VOLENVRELEASE: -386.00 COARSETUNE: -12.00 - zone: 2 name: pz:Zippy Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 98 name: Thunder bank: 0 num: 98 global_zone: name: pz:Thunder/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODLFOTOFILTERFC: 4202.00 PAN: 300.00 MODLFOFREQ: -2084.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: 1902.00 VOLENVRELEASE: 2786.00 COARSETUNE: -34.00 zones: - zone: 0 name: pz:Thunder/3 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 40.00 MODLFOFREQ: -1586.00 - zone: 1 name: pz:Thunder/2 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: -40.00 PAN: 1000.00 FINETUNE: 1.00 - zone: 2 name: pz:Thunder/1 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 40.00 PAN: -1000.00 FINETUNE: -1.00 - preset: 99 name: Killer Bass bank: 0 num: 99 global_zone: name: pz:Killer Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -110.00 MODENVTOFILTERFC: -6500.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1902.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Killer Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Killer Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 100 name: Stab Bass Strings bank: 0 num: 100 global_zone: name: pz:Stab Bass Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -180.00 MODENVTOFILTERFC: -7200.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVATTACK: 702.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1902.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1902.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Stab Bass Strings/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Stab Bass Strings/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 101 name: Monster Strings bank: 0 num: 101 global_zone: name: pz:Monster Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -4169.00 FILTERQ: -180.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1902.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1902.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Monster Strings/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Monster Strings/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 102 name: Cheesy Pad bank: 0 num: 102 global_zone: name: pz:Cheesy Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: 180.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 3986.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Cheesy Pad/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 1 name: pz:Cheesy Pad/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 103 name: New Life 2 bank: 0 num: 103 global_zone: name: pz:New Life 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 38.00 FILTERQ: -180.00 MODLFOTOVOL: 18.00 CHORUSSEND: 500.00 REVERBSEND: 160.00 MODLFOFREQ: 316.00 VIBLFOFREQ: -365.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 zones: - zone: 0 name: pz:New Life 2/2 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:New Life 2/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 104 name: Mono Analog Bass bank: 0 num: 104 global_zone: name: pz:Mono Analog Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -4000.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -2084.00 MODENVHOLD: -7973.00 MODENVRELEASE: 3641.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Mono Analog Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Mono Analog Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 105 name: Polysynth Hit bank: 0 num: 105 global_zone: name: pz:Polysynth Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -2000.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -2084.00 MODENVHOLD: -7973.00 MODENVRELEASE: 3641.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Polysynth Hit/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Polysynth Hit/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 106 name: Venus Violin Hit bank: 0 num: 106 global_zone: name: pz:Venus Violin Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -180.00 MODLFOTOVOL: 25.00 CHORUSSEND: 400.00 REVERBSEND: 100.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -617.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 VOLENVRELEASE: -1200.00 zones: - zone: 0 name: pz:Venus Violin Hit/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -2.00 - zone: 1 name: pz:Venus Violin Hit/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 2.00 - preset: 107 name: Ping-Pong bank: 0 num: 107 global_zone: name: pz:Ping-Pong/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -180.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 400.00 MODLFOFREQ: -2084.00 VIBLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: -2786.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Ping-Pong/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 VOLENVDELAY: 595.00 FINETUNE: -1.00 - zone: 1 name: pz:Ping-Pong/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 108 name: Polysynth 2 bank: 0 num: 108 global_zone: name: pz:Polysynth 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 20.00 FILTERQ: -180.00 MODENVTOFILTERFC: -2000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -884.00 MODENVHOLD: -7973.00 MODENVRELEASE: 6922.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Polysynth 2/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 300.00 FINETUNE: -3.00 - zone: 1 name: pz:Polysynth 2/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 109 name: Cheap Synth bank: 0 num: 109 global_zone: name: pz:Cheap Synth/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 10.00 FILTERQ: 180.00 CHORUSSEND: 400.00 VIBLFOFREQ: -365.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -884.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1687.00 zones: - zone: 0 name: pz:Cheap Synth/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 - preset: 110 name: Heavy Square bank: 0 num: 110 global_zone: name: pz:Heavy Square/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Heavy Square/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVRELEASE: 1902.00 FINETUNE: -2.00 - zone: 1 name: pz:Heavy Square/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVRELEASE: 1902.00 FINETUNE: 2.00 - zone: 2 name: pz:Heavy Square/1 instrument: SQUARE WAVE (index 231) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - preset: 111 name: Distorted Lead bank: 0 num: 111 global_zone: name: pz:Distorted Lead/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 MODENVTOFILTERFC: -3426.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -365.00 MODENVHOLD: -7973.00 MODENVDECAY: 1200.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 1902.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1586.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Distorted Lead/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - zone: 1 name: pz:Distorted Lead/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 1000.00 MODENVDELAY: 595.00 VOLENVDECAY: 2119.00 VOLENVRELEASE: 1843.00 - zone: 2 name: pz:Distorted Lead/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - preset: 112 name: Singing Strings bank: 0 num: 112 global_zone: name: pz:Singing Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 1200.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Singing Strings/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -5.00 - zone: 1 name: pz:Singing Strings/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 5.00 - zone: 2 name: pz:Singing Strings/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 20.00 - preset: 113 name: Warble bank: 0 num: 113 global_zone: name: pz:Warble/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1586.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Warble/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 215.00 MODENVTOPITCH: 1341.00 CHORUSSEND: 1000.00 MODLFOFREQ: 7414.00 MODENVATTACK: 4263.00 MODENVRELEASE: 7973.00 VOLENVATTACK: 17.00 - zone: 1 name: pz:Warble/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: -1738.00 CHORUSSEND: 1000.00 MODENVDELAY: 595.00 VOLENVDECAY: 2565.00 VOLENVRELEASE: 2356.00 - zone: 2 name: pz:Warble/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -1639.00 MODLFOTOFILTERFC: 3625.00 MODLFOTOVOL: 80.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 - preset: 114 name: Wind Blast bank: 0 num: 114 global_zone: name: pz:Wind Blast/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: 180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 300.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -365.00 MODENVHOLD: -7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 7973.00 VOLENVRELEASE: 7973.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Wind Blast/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - zone: 1 name: pz:Wind Blast/1 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - preset: 115 name: Laser Pops bank: 0 num: 115 global_zone: name: pz:Laser Pops/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: 180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 600.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Laser Pops/3 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDECAY: -2786.00 VOLENVRELEASE: -1586.00 - zone: 1 name: pz:Laser Pops/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - zone: 2 name: pz:Laser Pops/1 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - preset: 116 name: Warbling Bird 1 bank: 0 num: 116 global_zone: name: pz:Warbling Bird 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: -180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 600.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Warbling Bird 1/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -1639.00 MODLFOTOFILTERFC: 3625.00 MODLFOTOVOL: 80.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 - preset: 117 name: Warbling Bird 2 bank: 0 num: 117 global_zone: name: pz:Warbling Bird 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: 180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 600.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Warbling Bird 2/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 7973.00 VOLENVDECAY: -1586.00 VOLENVRELEASE: -884.00 - zone: 1 name: pz:Warbling Bird 2/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 60.00 MODENVTOPITCH: 1639.00 MODLFOTOFILTERFC: 7200.00 MODLFOTOVOL: 80.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 - preset: 118 name: Wind Down bank: 0 num: 118 global_zone: name: pz:Wind Down/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: 180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 600.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Wind Down/2 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -1639.00 PAN: 1000.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 FINETUNE: -2.00 - zone: 1 name: pz:Wind Down/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -1639.00 PAN: -1000.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 FINETUNE: 2.00 - preset: 119 name: Delicate Bells bank: 0 num: 119 global_zone: name: pz:Delicate Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 5.00 CHORUSSEND: 300.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 7088.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 6386.00 COARSETUNE: 3.00 zones: - zone: 0 name: pz:Delicate Bells/2 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - zone: 1 name: pz:Delicate Bells/1 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - preset: 120 name: Delicate Marimba bank: 0 num: 120 global_zone: name: pz:Delicate Marimba/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -4000.00 CHORUSSEND: 300.00 MODENVDELAY: -7973.00 MODENVATTACK: 3804.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 5888.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 5888.00 COARSETUNE: 3.00 zones: - zone: 0 name: pz:Delicate Marimba/2 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Delicate Marimba/1 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 121 name: Woody Bass bank: 0 num: 121 global_zone: name: pz:Woody Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -5500.00 CHORUSSEND: 300.00 MODENVATTACK: 2400.00 MODENVRELEASE: 7312.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 7973.00 VOLENVRELEASE: 6155.00 COARSETUNE: 3.00 zones: - zone: 0 name: pz:Woody Bass/2 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -2.00 - zone: 1 name: pz:Woody Bass/1 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 2.00 - preset: 122 name: Filtered Stack bank: 0 num: 122 global_zone: name: pz:Filtered Stack/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 1209.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Filtered Stack/4 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: -10.00 FILTERQ: 180.00 MODLFOTOFILTERFC: 80.00 MODENVTOFILTERFC: -2334.00 MODENVDELAY: 2218.00 MODENVATTACK: 1365.00 MODENVHOLD: -7973.00 MODENVDECAY: 1837.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6198.00 FINETUNE: -2.00 - zone: 1 name: pz:Filtered Stack/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 MODENVTOFILTERFC: -6500.00 MODENVDELAY: 1783.00 MODENVATTACK: 1018.00 MODENVHOLD: -7973.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6198.00 FINETUNE: -2.00 - zone: 2 name: pz:Filtered Stack/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 40.00 MODENVTOFILTERFC: -6500.00 MODENVDELAY: 1200.00 MODENVATTACK: 1268.00 MODENVHOLD: -7973.00 MODENVDECAY: 1902.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6198.00 FINETUNE: 2.00 - zone: 3 name: pz:Filtered Stack/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 100.00 MODENVTOFILTERFC: -5214.00 MODENVDELAY: 2404.00 MODENVATTACK: 316.00 MODENVHOLD: -7973.00 MODENVDECAY: 2246.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6164.00 FINETUNE: 3.00 - preset: 123 name: TX81Z Sqncr Bass 2 bank: 0 num: 123 global_zone: name: pz:TX81Z Sqncr Bass 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 500.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: pz:TX81Z Sqncr Bass 2/2 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 - zone: 1 name: pz:TX81Z Sqncr Bass 2/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 700.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 - preset: 124 name: New Age Organ bank: 0 num: 124 global_zone: name: pz:New Age Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 200.00 MODENVDELAY: -3986.00 MODENVATTACK: 0.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1902.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVRELEASE: 1586.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:New Age Organ/2 instrument: Instrument3 (index 4) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: -9.00 MODLFOTOVOL: -17.00 MODLFOFREQ: -1004.00 FINETUNE: -3.00 - zone: 1 name: pz:New Age Organ/1 instrument: Instrument3 (index 4) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 20.00 MODLFOFREQ: -884.00 - preset: 125 name: Gated Screamer bank: 0 num: 125 global_zone: name: pz:Gated Screamer/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 60.00 FILTERFC: -4604.00 FILTERQ: -180.00 MODENVTOFILTERFC: 7200.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 VIBLFOFREQ: -1200.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: -7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Gated Screamer/1 instrument: Instrument9 (index 3) key_range: 0 - 128 vel_range: 0 - 128 - preset: 126 name: Wonderland Xylo bank: 0 num: 126 global_zone: name: pz:Wonderland Xylo/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 30.00 FILTERQ: -110.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 MODENVDELAY: -3986.00 MODENVATTACK: 0.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Wonderland Xylo/2 instrument: Instrument15 (index 1) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 200.00 - zone: 1 name: pz:Wonderland Xylo/1 instrument: Instrument16 (index 2) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 400.00 PAN: -200.00 - preset: 127 name: Space Flute bank: 0 num: 127 global_zone: name: pz:Space Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -4000.00 CHORUSSEND: 300.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: 3804.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -2702.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 5888.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Space Flute/1 instrument: Instrument8 (index 0) key_range: 0 - 128 vel_range: 0 - 128 - preset: 128 name: TR-101 Drumset bank: 128 num: 0 zones: - zone: 0 name: pz:TR-101 Drumset/33 instrument: 101 LASER 3 (index 172) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:TR-101 Drumset/32 instrument: 101 LASER 2 (index 173) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:TR-101 Drumset/31 instrument: 101 LASER 1 (index 174) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:TR-101 Drumset/30 instrument: 101 MARACAS (index 175) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:TR-101 Drumset/29 instrument: 101 SHAKER (index 176) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:TR-101 Drumset/28 instrument: 101 BONGO LO (index 177) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:TR-101 Drumset/27 instrument: 101 BONGO MID (index 178) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:TR-101 Drumset/26 instrument: 101 BONGO HI (index 179) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:TR-101 Drumset/25 instrument: 101 COWBELL (index 180) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:TR-101 Drumset/24 instrument: 101 TAMBOURINE (index 181) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:TR-101 Drumset/23 instrument: 101 HI TOM 2 (index 182) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:TR-101 Drumset/22 instrument: 101 CYMBAL (index 183) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:TR-101 Drumset/21 instrument: 101 HI TOM 1 (index 184) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:TR-101 Drumset/20 instrument: 101 MID TOM 2 (index 185) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:TR-101 Drumset/19 instrument: 101 HAT OPEN (index 186) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:TR-101 Drumset/18 instrument: 101 MID TOM 1 (index 187) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:TR-101 Drumset/17 instrument: 101 LO TOM 2 (index 188) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:TR-101 Drumset/16 instrument: 101 HAT CLOSED (index 189) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:TR-101 Drumset/15 instrument: 101 LO TOM 1 (index 190) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:TR-101 Drumset/14 instrument: 101 SNARE 2 (index 191) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:TR-101 Drumset/13 instrument: 101 CLAPS (index 192) key_range: 0 - 128 vel_range: 0 - 128 - zone: 21 name: pz:TR-101 Drumset/12 instrument: 101 SNARE 1 (index 193) key_range: 0 - 128 vel_range: 0 - 128 - zone: 22 name: pz:TR-101 Drumset/11 instrument: 101 RIM SHOT (index 194) key_range: 0 - 128 vel_range: 0 - 128 - zone: 23 name: pz:TR-101 Drumset/10 instrument: 101 KICK 2 (index 195) key_range: 0 - 128 vel_range: 0 - 128 - zone: 24 name: pz:TR-101 Drumset/9 instrument: 101 KICK 1 (index 196) key_range: 0 - 128 vel_range: 0 - 128 - zone: 25 name: pz:TR-101 Drumset/8 instrument: 101 SNARE EXTRA #02 (index 197) key_range: 0 - 128 vel_range: 0 - 128 - zone: 26 name: pz:TR-101 Drumset/7 instrument: 101 HAT OP EXTRA (index 198) key_range: 0 - 128 vel_range: 0 - 128 - zone: 27 name: pz:TR-101 Drumset/6 instrument: 101 HAT CL EXTRA (index 199) key_range: 0 - 128 vel_range: 0 - 128 - zone: 28 name: pz:TR-101 Drumset/5 instrument: 101 SNARE EXTRA 3 (index 200) key_range: 0 - 128 vel_range: 0 - 128 - zone: 29 name: pz:TR-101 Drumset/4 instrument: 101 KICK EXTRA 3 (index 201) key_range: 0 - 128 vel_range: 0 - 128 - zone: 30 name: pz:TR-101 Drumset/3 instrument: 101 SNARE EXTRA 2 (index 202) key_range: 0 - 128 vel_range: 0 - 128 - zone: 31 name: pz:TR-101 Drumset/2 instrument: 101 SNARE EXTRA 1 (index 203) key_range: 0 - 128 vel_range: 0 - 128 - zone: 32 name: pz:TR-101 Drumset/1 instrument: 101 KICK EXTRA 2 (index 204) key_range: 0 - 128 vel_range: 0 - 128 - zone: 33 name: pz:TR-101 Drumset/0 instrument: 101 KICK EXTRA 1 (index 205) key_range: 0 - 128 vel_range: 0 - 128 - preset: 129 name: CR-78 Drumset bank: 128 num: 1 zones: - zone: 0 name: pz:CR-78 Drumset/21 instrument: CR78 HI TOM 2 (index 150) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:CR-78 Drumset/20 instrument: CR78 HI TOM 1 (index 151) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:CR-78 Drumset/19 instrument: CR78 MID TOM 2 (index 152) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:CR-78 Drumset/18 instrument: CR78 MID TOM 1 (index 153) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:CR-78 Drumset/17 instrument: CR78 LO TOM 2 (index 154) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:CR-78 Drumset/16 instrument: CR78 LO TOM 1 (index 155) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:CR-78 Drumset/15 instrument: CR78 BONGO LO (index 156) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:CR-78 Drumset/14 instrument: CR78 BONGO MID (index 157) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:CR-78 Drumset/13 instrument: CR78 BONGO HI (index 158) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:CR-78 Drumset/12 instrument: CR78 COWBELL (index 159) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:CR-78 Drumset/11 instrument: CR78 TAMBOURINE (index 160) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:CR-78 Drumset/10 instrument: CR78 HAT OPEN (index 161) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:CR-78 Drumset/9 instrument: CR78 HAT CLOSED (index 162) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:CR-78 Drumset/8 instrument: CR78 SNARE 2 (index 163) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:CR-78 Drumset/7 instrument: CR78 METAL (index 164) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:CR-78 Drumset/6 instrument: CR78 SNARE 1 (index 165) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:CR-78 Drumset/5 instrument: CR78 RIM SHOT (index 166) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:CR-78 Drumset/4 instrument: CR78 KICK 2 (index 167) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:CR-78 Drumset/3 instrument: CR78 KICK 1 (index 168) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:CR-78 Drumset/2 instrument: CR78 CLAVE (index 169) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:CR-78 Drumset/1 instrument: CR78 MARACAS (index 170) key_range: 0 - 128 vel_range: 0 - 128 - zone: 21 name: pz:CR-78 Drumset/0 instrument: CR78 GUIRO (index 171) key_range: 0 - 128 vel_range: 0 - 128 - preset: 130 name: TR-808 Drumset bank: 128 num: 2 zones: - zone: 0 name: pz:TR-808 Drumset/28 instrument: 808 Clap (index 21) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:TR-808 Drumset/27 instrument: 808 CLAVES (index 121) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:TR-808 Drumset/26 instrument: 808 MARACAS (index 122) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:TR-808 Drumset/25 instrument: 808 BONGO LO (index 123) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:TR-808 Drumset/24 instrument: 808 BONGO MID (index 124) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:TR-808 Drumset/23 instrument: 808 BONGO HI (index 125) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:TR-808 Drumset/22 instrument: 808 COWBELL (index 126) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:TR-808 Drumset/21 instrument: 808 HI TOM 2 (index 127) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:TR-808 Drumset/20 instrument: 808 CYMBAL (index 128) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:TR-808 Drumset/19 instrument: 808 HI TOM 1 (index 129) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:TR-808 Drumset/18 instrument: 808 MID TOM 2 (index 130) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:TR-808 Drumset/17 instrument: 808 HAT OPEN (index 131) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:TR-808 Drumset/16 instrument: 808 MID TOM 1 (index 132) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:TR-808 Drumset/15 instrument: 808 LO TOM 2 (index 133) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:TR-808 Drumset/14 instrument: 808 HAT CLOSED (index 134) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:TR-808 Drumset/13 instrument: 808 LO TOM 1 (index 135) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:TR-808 Drumset/12 instrument: 808 SNARE DRUM HI (index 136) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:TR-808 Drumset/11 instrument: 808 SNARE DRUM LO (index 138) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:TR-808 Drumset/10 instrument: 808 RIM SHOT (index 139) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:TR-808 Drumset/9 instrument: 808 BASS DRUM HI (index 140) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:TR-808 Drumset/8 instrument: 808 BASS DRUM LO (index 141) key_range: 0 - 128 vel_range: 0 - 128 - zone: 21 name: pz:TR-808 Drumset/7 instrument: 808 SNARE HIGH (index 142) key_range: 0 - 128 vel_range: 0 - 128 - zone: 22 name: pz:TR-808 Drumset/6 instrument: 808 SNARE SHARP (index 143) key_range: 0 - 128 vel_range: 0 - 128 - zone: 23 name: pz:TR-808 Drumset/5 instrument: 808 SNARE FILTERED (index 144) key_range: 0 - 128 vel_range: 0 - 128 - zone: 24 name: pz:TR-808 Drumset/4 instrument: 808 ULTRA SNAPPY (index 145) key_range: 0 - 128 vel_range: 0 - 128 - zone: 25 name: pz:TR-808 Drumset/3 instrument: 808 BASS DRUM MUTE (index 146) key_range: 0 - 128 vel_range: 0 - 128 - zone: 26 name: pz:TR-808 Drumset/2 instrument: 808 BASS DRUM BOOM2 (index 147) key_range: 0 - 128 vel_range: 0 - 128 - zone: 27 name: pz:TR-808 Drumset/1 instrument: 808 BASS DRUM SHORT (index 148) key_range: 0 - 128 vel_range: 0 - 128 - zone: 28 name: pz:TR-808 Drumset/0 instrument: 808 BASS DRUM BOOM1 (index 149) key_range: 0 - 128 vel_range: 0 - 128 - preset: 131 name: TR-909 Drumset bank: 128 num: 3 zones: - zone: 0 name: pz:TR-909 Drumset/18 instrument: 909 BASS BOOM (index 102) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:TR-909 Drumset/17 instrument: 909 TOM HI 2 (index 103) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:TR-909 Drumset/16 instrument: 909 TOM HI 1 (index 104) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:TR-909 Drumset/15 instrument: 909 TOM MID 2 (index 105) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:TR-909 Drumset/14 instrument: 909 HAT OPEN (index 106) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:TR-909 Drumset/13 instrument: 909 TOM MID 1 (index 107) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:TR-909 Drumset/12 instrument: 909 TOM LO 2 (index 108) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:TR-909 Drumset/11 instrument: 909 HAT CLOSED (index 109) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:TR-909 Drumset/10 instrument: 909 TOM LO 1 (index 110) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:TR-909 Drumset/9 instrument: 909 SNARE HI (index 111) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:TR-909 Drumset/8 instrument: 909 CLAP (index 112) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:TR-909 Drumset/7 instrument: 909 SNARE LO (index 113) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:TR-909 Drumset/6 instrument: 909 RIM (index 114) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:TR-909 Drumset/5 instrument: 909 BASS DRUM HI (index 115) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:TR-909 Drumset/4 instrument: 909 BASS DRUM LO (index 116) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:TR-909 Drumset/3 instrument: 909 SNARE 2 (index 117) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:TR-909 Drumset/2 instrument: 909 SNARE 1 (index 118) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:TR-909 Drumset/1 instrument: 909 BASS DRUM 2 (index 119) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:TR-909 Drumset/0 instrument: 909 BASS DRUM 1 (index 120) key_range: 0 - 128 vel_range: 0 - 128 - preset: 132 name: Analog Drumset bank: 128 num: 4 zones: - zone: 0 name: pz:Analog Drumset/48 instrument: ANALOG SNARE 9 (index 53) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Analog Drumset/47 instrument: ANALOG SNARE 8 (index 54) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Analog Drumset/46 instrument: ANALOG SNARE 7 (index 55) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:Analog Drumset/45 instrument: ANALOG SNAPPY BD (index 56) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:Analog Drumset/44 instrument: ANALOG HAT OPEN 3 (index 57) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:Analog Drumset/43 instrument: ANALOG HAT CLOSED 3 (index 58) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:Analog Drumset/42 instrument: ANALOG HAT CLOSED 2 (index 59) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:Analog Drumset/41 instrument: ANALOG HAT OPEN 2 (index 60) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:Analog Drumset/40 instrument: ANALOG SNAP SNARE (index 61) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:Analog Drumset/39 instrument: ANALOG BOOM KICK (index 62) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:Analog Drumset/38 instrument: ANALOG CLAPS 3 (index 63) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:Analog Drumset/37 instrument: ANALOG CLAPS 2 (index 64) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:Analog Drumset/36 instrument: ANALOG ZAP OPEN (index 65) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:Analog Drumset/35 instrument: ANALOG ZAP CLOSED (index 66) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:Analog Drumset/34 instrument: ANALOG CLAVE (index 67) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:Analog Drumset/33 instrument: ANALOG SNARE 6 (index 68) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:Analog Drumset/32 instrument: ANALOG SNARE 5 (index 69) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:Analog Drumset/31 instrument: ANALOG THROB KICK (index 70) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:Analog Drumset/30 instrument: ANALOG POPS OP 2 (index 71) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:Analog Drumset/29 instrument: ANALOG POPS OP 1 (index 72) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:Analog Drumset/28 instrument: ANALOG POPS CL 2 (index 73) key_range: 0 - 128 vel_range: 0 - 128 - zone: 21 name: pz:Analog Drumset/27 instrument: ANALOG POPS CL 1 (index 74) key_range: 0 - 128 vel_range: 0 - 128 - zone: 22 name: pz:Analog Drumset/26 instrument: ANALOG PHASER (index 75) key_range: 0 - 128 vel_range: 0 - 128 - zone: 23 name: pz:Analog Drumset/25 instrument: ANALOG TAMBOURINE (index 76) key_range: 0 - 128 vel_range: 0 - 128 - zone: 24 name: pz:Analog Drumset/24 instrument: ANALOG KICK 3 (index 77) key_range: 0 - 128 vel_range: 0 - 128 - zone: 25 name: pz:Analog Drumset/23 instrument: ANALOG RIM SHOT (index 78) key_range: 0 - 128 vel_range: 0 - 128 - zone: 26 name: pz:Analog Drumset/22 instrument: ANALOG CRASH CYMBAL1 (index 79) key_range: 0 - 128 vel_range: 0 - 128 - zone: 27 name: pz:Analog Drumset/21 instrument: ANALOG COWBELL (index 80) key_range: 0 - 128 vel_range: 0 - 128 - zone: 28 name: pz:Analog Drumset/20 instrument: ANALOG BONGO HI (index 81) key_range: 0 - 128 vel_range: 0 - 128 - zone: 29 name: pz:Analog Drumset/19 instrument: ANALOG BONGO MED (index 82) key_range: 0 - 128 vel_range: 0 - 128 - zone: 30 name: pz:Analog Drumset/18 instrument: ANALOG BONGO LO (index 83) key_range: 0 - 128 vel_range: 0 - 128 - zone: 31 name: pz:Analog Drumset/17 instrument: ANALOG SNARE 4 (index 84) key_range: 0 - 128 vel_range: 0 - 128 - zone: 32 name: pz:Analog Drumset/16 instrument: ANALOG SNARE 3 (index 85) key_range: 0 - 128 vel_range: 0 - 128 - zone: 33 name: pz:Analog Drumset/15 instrument: ANALOG MARACAS (index 86) key_range: 0 - 128 vel_range: 0 - 128 - zone: 34 name: pz:Analog Drumset/14 instrument: ANALOG CLAPS 1 (index 87) key_range: 0 - 128 vel_range: 0 - 128 - zone: 35 name: pz:Analog Drumset/13 instrument: ANALOG HI TOM 2 (index 88) key_range: 0 - 128 vel_range: 0 - 128 - zone: 36 name: pz:Analog Drumset/12 instrument: ANALOG HI TOM 1 (index 89) key_range: 0 - 128 vel_range: 0 - 128 - zone: 37 name: pz:Analog Drumset/11 instrument: ANALOG MID TOM 2 (index 90) key_range: 0 - 128 vel_range: 0 - 128 - zone: 38 name: pz:Analog Drumset/10 instrument: ANALOG MID TOM 1 (index 91) key_range: 0 - 128 vel_range: 0 - 128 - zone: 39 name: pz:Analog Drumset/9 instrument: ANALOG LO TOM 2 (index 92) key_range: 0 - 128 vel_range: 0 - 128 - zone: 40 name: pz:Analog Drumset/8 instrument: ANALOG LO TOM 1 (index 93) key_range: 0 - 128 vel_range: 0 - 128 - zone: 41 name: pz:Analog Drumset/7 instrument: ANALOG CRASH CYMBAL2 (index 94) key_range: 0 - 128 vel_range: 0 - 128 - zone: 42 name: pz:Analog Drumset/6 instrument: ANALOG HAT CLOSED (index 95) key_range: 0 - 128 vel_range: 0 - 128 - zone: 43 name: pz:Analog Drumset/5 instrument: ANALOG HAT OPEN (index 96) key_range: 0 - 128 vel_range: 0 - 128 - zone: 44 name: pz:Analog Drumset/4 instrument: ANALOG LASER (index 97) key_range: 0 - 128 vel_range: 0 - 128 - zone: 45 name: pz:Analog Drumset/3 instrument: ANALOG SNARE 2 (index 98) key_range: 0 - 128 vel_range: 0 - 128 - zone: 46 name: pz:Analog Drumset/2 instrument: ANALOG KICK 2 (index 99) key_range: 0 - 128 vel_range: 0 - 128 - zone: 47 name: pz:Analog Drumset/1 instrument: ANALOG KICK 1 (index 100) key_range: 0 - 128 vel_range: 0 - 128 - zone: 48 name: pz:Analog Drumset/0 instrument: ANALOG SNARE 1 (index 101) key_range: 0 - 128 vel_range: 0 - 128 - preset: 133 name: Kraftwerk Drumset bank: 128 num: 5 zones: - zone: 0 name: pz:Kraftwerk Drumset/16 instrument: KRAFTWERK BD 5 (index 36) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Kraftwerk Drumset/15 instrument: KRAFTWERK SD 7 (index 37) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Kraftwerk Drumset/14 instrument: KRAFTWERK SD 6 (index 38) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:Kraftwerk Drumset/13 instrument: KRAFTWERK KLAPS (index 39) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:Kraftwerk Drumset/12 instrument: KRAFTWERK SD 5 (index 40) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:Kraftwerk Drumset/11 instrument: KRAFTWERK BD 4 (index 41) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:Kraftwerk Drumset/10 instrument: KRAFTWERK SD 4 (index 42) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:Kraftwerk Drumset/9 instrument: KRAFTWERK BD 3 (index 43) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:Kraftwerk Drumset/8 instrument: KRAFTWERK SD 3 (index 44) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:Kraftwerk Drumset/7 instrument: KRAFTWERK RIMSHOT (index 45) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:Kraftwerk Drumset/6 instrument: KRAFTWERK CYMBAL (index 46) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:Kraftwerk Drumset/5 instrument: KRAFTWERK HAT OP (index 47) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:Kraftwerk Drumset/4 instrument: KRAFTWERK HAT CL (index 48) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:Kraftwerk Drumset/3 instrument: KRAFTWERK SD 2 (index 49) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:Kraftwerk Drumset/2 instrument: KRAFTWERK SD 1 (index 50) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:Kraftwerk Drumset/1 instrument: KRAFTWERK BD 2 (index 51) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:Kraftwerk Drumset/0 instrument: KRAFTWERK BD 1 (index 52) key_range: 0 - 128 vel_range: 0 - 128 - preset: 134 name: Electronic Drumset bank: 128 num: 6 zones: - zone: 0 name: pz:Electronic Drumset/9 instrument: ELECTRONIC KICK 2 (index 26) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Electronic Drumset/8 instrument: ELECTRONIC SD 2 (index 27) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Electronic Drumset/7 instrument: ELECTRONIC SD 1 (index 28) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:Electronic Drumset/6 instrument: ELECTRONIC KICK 1 (index 29) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:Electronic Drumset/5 instrument: ELECTRONIC TOM LO 1 (index 30) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:Electronic Drumset/4 instrument: ELECTRONIC TOM LO 2 (index 31) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:Electronic Drumset/3 instrument: ELECTRONIC TOM MID 1 (index 32) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:Electronic Drumset/2 instrument: ELECTRONIC TOM MID 2 (index 33) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:Electronic Drumset/1 instrument: ELECTRONIC TOM HI 1 (index 34) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:Electronic Drumset/0 instrument: ELECTRONIC TOM HI 2 (index 35) key_range: 0 - 128 vel_range: 0 - 128 - preset: 135 name: TR-808 Drumset 2 bank: 128 num: 7 zones: - zone: 0 name: pz:TR-808 Drumset 2/20 instrument: 808 CLAP (index 137) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:TR-808 Drumset 2/19 instrument: 808 Maracas (index 5) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:TR-808 Drumset 2/18 instrument: 808 Clave (index 6) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:TR-808 Drumset 2/17 instrument: 808 Cymbal (index 7) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:TR-808 Drumset 2/16 instrument: 808 Conga Hi (index 8) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:TR-808 Drumset 2/15 instrument: 808 Conga Med (index 9) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:TR-808 Drumset 2/14 instrument: 808 Conga Lo (index 10) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:TR-808 Drumset 2/13 instrument: 808 Tom Hi 2 (index 11) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:TR-808 Drumset 2/12 instrument: 808 Tom Med 2 (index 12) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:TR-808 Drumset 2/11 instrument: 808 Tom Lo 2 (index 13) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:TR-808 Drumset 2/10 instrument: 808 Cowbell (index 14) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:TR-808 Drumset 2/9 instrument: 808 Tom Med 1 (index 15) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:TR-808 Drumset 2/8 instrument: 808 Tom Hi 1 (index 16) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:TR-808 Drumset 2/7 instrument: 808 Tom Lo 1 (index 17) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:TR-808 Drumset 2/6 instrument: 808 Hat Closed (index 18) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:TR-808 Drumset 2/5 instrument: 808 Rim (index 19) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:TR-808 Drumset 2/4 instrument: 808 Kick 1 (index 20) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:TR-808 Drumset 2/3 instrument: 808 Snare 2 (index 22) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:TR-808 Drumset 2/2 instrument: 808 Hat Open (index 23) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:TR-808 Drumset 2/1 instrument: 808 Snare 1 (index 24) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:TR-808 Drumset 2/0 instrument: 808 Kick 2 (index 25) key_range: 0 - 128 vel_range: 0 - 128 instruments: - instrument: 0 name: Instrument8 global_zone: name: iz:Instrument8/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument8/1 sample: FM4-0440 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 1 name: Instrument15 global_zone: name: iz:Instrument15/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument15/1 sample: Tri2 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 2 name: Instrument16 global_zone: name: iz:Instrument16/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument16/1 sample: Tri-Sq key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 3 name: Instrument9 global_zone: name: iz:Instrument9/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument9/1 sample: FM5-0440 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 4 name: Instrument3 global_zone: name: iz:Instrument3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument3/1 sample: Bell2-0440 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 5 name: 808 Maracas zones: - zone: 0 name: iz:808 Maracas/0 sample: white808-2 key_range: 70 - 70 vel_range: 0 - 128 generators: VOLENVATTACK: -6688.00 VOLENVDECAY: -4351.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -4351.00 FINETUNE: 20.00 OVERRIDEROOTKEY: 44.00 - instrument: 6 name: 808 Clave zones: - zone: 0 name: iz:808 Clave/0 sample: sinehi key_range: 75 - 75 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 87.00 - instrument: 7 name: 808 Cymbal zones: - zone: 0 name: iz:808 Cymbal/2 sample: sinehi key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 500.00 VOLENVDECAY: 1783.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1783.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 64.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 1 name: iz:808 Cymbal/1 sample: sinehi key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 500.00 VOLENVDECAY: 1783.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1783.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 72.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 2 name: iz:808 Cymbal/0 sample: white5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 800.00 VOLENVDECAY: 1820.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1820.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 61.00 - instrument: 8 name: 808 Conga Hi zones: - zone: 0 name: iz:808 Conga Hi/0 sample: sinehi key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 101.00 - instrument: 9 name: 808 Conga Med zones: - zone: 0 name: iz:808 Conga Med/0 sample: bdloop key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 10 name: 808 Conga Lo zones: - zone: 0 name: iz:808 Conga Lo/0 sample: bdloop key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -182.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -182.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - instrument: 11 name: 808 Tom Hi 2 global_zone: name: iz:808 Tom Hi 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Hi 2/1 sample: bdloop key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - instrument: 12 name: 808 Tom Med 2 global_zone: name: iz:808 Tom Med 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Med 2/1 sample: bdloop key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - instrument: 13 name: 808 Tom Lo 2 global_zone: name: iz:808 Tom Lo 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Lo 2/1 sample: bdloop key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 14 name: 808 Cowbell global_zone: name: iz:808 Cowbell/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 4.00 FILTERFC: 11921.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: 467.00 FINETUNE: -20.00 zones: - zone: 0 name: iz:808 Cowbell/2 sample: cowbell key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -10.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - zone: 1 name: iz:808 Cowbell/1 sample: cowbell key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: 454.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 454.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 128.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - instrument: 15 name: 808 Tom Med 1 global_zone: name: iz:808 Tom Med 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Med 1/1 sample: bdloop key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 16 name: 808 Tom Hi 1 global_zone: name: iz:808 Tom Hi 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Hi 1/1 sample: bdloop key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 17 name: 808 Tom Lo 1 global_zone: name: iz:808 Tom Lo 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Lo 1/1 sample: bdloop key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 18 name: 808 Hat Closed global_zone: name: iz:808 Hat Closed/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: iz:808 Hat Closed/2 sample: white5 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 381.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - zone: 1 name: iz:808 Hat Closed/1 sample: sinehi key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 96.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 13.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - instrument: 19 name: 808 Rim zones: - zone: 0 name: iz:808 Rim/1 sample: sinehi key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -5530.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5530.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 12.00 FINETUNE: -40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 67.00 - zone: 1 name: iz:808 Rim/0 sample: claploop key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -4351.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -4351.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - instrument: 20 name: 808 Kick 1 zones: - zone: 0 name: iz:808 Kick 1/0 sample: bdloop key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODENVTOPITCH: 1200.00 REVERBSEND: 40.00 MODENVATTACK: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDECAY: 1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 15.00 SAMPLEMODE: 1.00 - instrument: 21 name: 808 Clap global_zone: name: iz:808 Clap/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 95.00 REVERBSEND: 20.00 MODLFOFREQ: -1179.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 zones: - zone: 0 name: iz:808 Clap/1 sample: claploop key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 22 name: 808 Snare 2 zones: - zone: 0 name: iz:808 Snare 2/2 sample: white808-2 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 10.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 1 name: iz:808 Snare 2/1 sample: white808-2 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 10.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 2 name: iz:808 Snare 2/0 sample: bdloop key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 87.00 VOLENVDECAY: -5530.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5530.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 120.00 SAMPLEMODE: 1.00 - instrument: 23 name: 808 Hat Open global_zone: name: iz:808 Hat Open/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 VOLENVHOLD: -3162.00 VOLENVDECAY: 702.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1582.00 zones: - zone: 0 name: iz:808 Hat Open/2 sample: white5 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 381.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 1 name: iz:808 Hat Open/1 sample: sinehi key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: 454.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 96.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 13.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 54.00 - instrument: 24 name: 808 Snare 1 zones: - zone: 0 name: iz:808 Snare 1/2 sample: white808-2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 40.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 11.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - zone: 1 name: iz:808 Snare 1/1 sample: white808-2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 40.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 11.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - zone: 2 name: iz:808 Snare 1/0 sample: bdloop key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 40.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 46.00 SAMPLEMODE: 1.00 - instrument: 25 name: 808 Kick 2 zones: - zone: 0 name: iz:808 Kick 2/0 sample: bdloop key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODENVTOPITCH: 1200.00 REVERBSEND: 40.00 MODENVATTACK: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDECAY: 316.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 316.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 17.00 SAMPLEMODE: 1.00 - instrument: 26 name: ELECTRONIC KICK 2 global_zone: name: iz:ELECTRONIC KICK 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: -8857.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:ELECTRONIC KICK 2/2 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - zone: 1 name: iz:ELECTRONIC KICK 2/1 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - instrument: 27 name: ELECTRONIC SD 2 zones: - zone: 0 name: iz:ELECTRONIC SD 2/2 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: -500.00 MODENVDECAY: -2613.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 1 name: iz:ELECTRONIC SD 2/1 sample: 808BD key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 MODENVTOPITCH: 1200.00 MODENVSUSTAIN: 1000.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - zone: 2 name: iz:ELECTRONIC SD 2/0 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 28 name: ELECTRONIC SD 1 zones: - zone: 0 name: iz:ELECTRONIC SD 1/2 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10223.00 FILTERQ: 80.00 MODENVTOFILTERFC: 1500.00 CHORUSSEND: 200.00 PAN: -500.00 MODENVDECAY: -2613.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2613.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 1 name: iz:ELECTRONIC SD 1/1 sample: 808BD key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 MODENVTOPITCH: 1200.00 MODENVSUSTAIN: 1000.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - zone: 2 name: iz:ELECTRONIC SD 1/0 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10223.00 FILTERQ: 80.00 MODENVTOFILTERFC: 1500.00 CHORUSSEND: 200.00 PAN: 500.00 MODENVDECAY: -2613.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2613.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 29 name: ELECTRONIC KICK 1 global_zone: name: iz:ELECTRONIC KICK 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 368.00 VIBLFOFREQ: -155.00 VOLENVATTACK: -8857.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -2.00 zones: - zone: 0 name: iz:ELECTRONIC KICK 1/3 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 VIBLFOTOPITCH: -1200.00 FILTERFC: 11273.00 VIBLFOFREQ: -15597.00 VOLENVATTACK: -11959.00 VOLENVDECAY: -2786.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:ELECTRONIC KICK 1/2 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - zone: 2 name: iz:ELECTRONIC KICK 1/1 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - instrument: 30 name: ELECTRONIC TOM LO 1 global_zone: name: iz:ELECTRONIC TOM LO 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -11.00 zones: - zone: 0 name: iz:ELECTRONIC TOM LO 1/1 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 31 name: ELECTRONIC TOM LO 2 global_zone: name: iz:ELECTRONIC TOM LO 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -9.00 zones: - zone: 0 name: iz:ELECTRONIC TOM LO 2/1 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 32 name: ELECTRONIC TOM MID 1 global_zone: name: iz:ELECTRONIC TOM MID 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -7.00 zones: - zone: 0 name: iz:ELECTRONIC TOM MID 1/1 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 33 name: ELECTRONIC TOM MID 2 global_zone: name: iz:ELECTRONIC TOM MID 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -5.00 zones: - zone: 0 name: iz:ELECTRONIC TOM MID 2/1 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 34 name: ELECTRONIC TOM HI 1 global_zone: name: iz:ELECTRONIC TOM HI 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -3.00 zones: - zone: 0 name: iz:ELECTRONIC TOM HI 1/1 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 35 name: ELECTRONIC TOM HI 2 global_zone: name: iz:ELECTRONIC TOM HI 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:ELECTRONIC TOM HI 2/1 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 36 name: KRAFTWERK BD 5 zones: - zone: 0 name: iz:KRAFTWERK BD 5/2 sample: FM90000 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VIBLFOTOPITCH: 1200.00 MODENVTOPITCH: 1200.00 VIBLFOFREQ: 410.00 MODENVDECAY: -2959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2959.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -386.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 120.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 70.00 - zone: 1 name: iz:KRAFTWERK BD 5/1 sample: RIM key_range: 31 - 31 vel_range: 0 - 128 generators: VOLENVSUSTAIN: 960.00 OVERRIDEROOTKEY: 29.00 - zone: 2 name: iz:KRAFTWERK BD 5/0 sample: SINE32 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 55.00 - instrument: 37 name: KRAFTWERK SD 7 global_zone: name: iz:KRAFTWERK SD 7/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 300.00 VOLENVDECAY: -182.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -182.00 zones: - zone: 0 name: iz:KRAFTWERK SD 7/3 sample: SINE32 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 26.00 - zone: 1 name: iz:KRAFTWERK SD 7/2 sample: NOISE3 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8837.00 FILTERQ: 180.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:KRAFTWERK SD 7/1 sample: NOISE3 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8789.00 FILTERQ: 180.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - instrument: 38 name: KRAFTWERK SD 6 global_zone: name: iz:KRAFTWERK SD 6/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 300.00 VOLENVDECAY: -2613.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2613.00 zones: - zone: 0 name: iz:KRAFTWERK SD 6/3 sample: FM90000 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VIBLFOTOPITCH: 420.00 MODENVTOPITCH: 1200.00 CHORUSSEND: 400.00 MODENVDECAY: -2959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2959.00 VOLENVDECAY: -1498.00 VOLENVRELEASE: -1498.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 63.00 - zone: 1 name: iz:KRAFTWERK SD 6/2 sample: NOISE3 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 35.00 - zone: 2 name: iz:KRAFTWERK SD 6/1 sample: NOISE3 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 35.00 - instrument: 39 name: KRAFTWERK KLAPS global_zone: name: iz:KRAFTWERK KLAPS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 40.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -14.00 zones: - zone: 0 name: iz:KRAFTWERK KLAPS/2 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 42.00 - zone: 1 name: iz:KRAFTWERK KLAPS/1 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 40 name: KRAFTWERK SD 5 zones: - zone: 0 name: iz:KRAFTWERK SD 5/2 sample: NOISE3 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 6237.00 FILTERQ: 120.00 MODENVTOFILTERFC: 1500.00 CHORUSSEND: 400.00 PAN: -500.00 MODENVDECAY: -2613.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2613.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 1 name: iz:KRAFTWERK SD 5/1 sample: 808BD key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 MODENVTOPITCH: 1200.00 MODENVSUSTAIN: 1000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - zone: 2 name: iz:KRAFTWERK SD 5/0 sample: NOISE3 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 6237.00 FILTERQ: 120.00 MODENVTOFILTERFC: 1500.00 CHORUSSEND: 400.00 PAN: 500.00 MODENVDECAY: -2613.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2613.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 41 name: KRAFTWERK BD 4 zones: - zone: 0 name: iz:KRAFTWERK BD 4/1 sample: RIM key_range: 33 - 33 vel_range: 0 - 128 generators: VOLENVSUSTAIN: 960.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:KRAFTWERK BD 4/0 sample: SINE32 key_range: 33 - 33 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 VIBLFOTOPITCH: 1200.00 VIBLFOFREQ: 320.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 49.00 - instrument: 42 name: KRAFTWERK SD 4 zones: - zone: 0 name: iz:KRAFTWERK SD 4/2 sample: NOISE3 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 VOLENVDECAY: -5530.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5530.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:KRAFTWERK SD 4/1 sample: NOISE3 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 7728.00 FILTERQ: 180.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -182.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -182.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:KRAFTWERK SD 4/0 sample: NOISE3 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 7728.00 FILTERQ: 180.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -182.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -182.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - instrument: 43 name: KRAFTWERK BD 3 zones: - zone: 0 name: iz:KRAFTWERK BD 3/1 sample: RIM key_range: 34 - 34 vel_range: 0 - 128 generators: VOLENVSUSTAIN: 960.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:KRAFTWERK BD 3/0 sample: SINE32 key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 VOLENVDECAY: -2028.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2028.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - instrument: 44 name: KRAFTWERK SD 3 zones: - zone: 0 name: iz:KRAFTWERK SD 3/1 sample: NOISE3 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -2959.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2959.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 42.00 - zone: 1 name: iz:KRAFTWERK SD 3/0 sample: NOISE3 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -2959.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2959.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 42.00 - instrument: 45 name: KRAFTWERK RIMSHOT zones: - zone: 0 name: iz:KRAFTWERK RIMSHOT/0 sample: CLICK key_range: 37 - 37 vel_range: 0 - 128 generators: CHORUSSEND: 40.00 REVERBSEND: 20.00 VOLENVDECAY: -6688.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -6688.00 OVERRIDEROOTKEY: 62.00 - instrument: 46 name: KRAFTWERK CYMBAL zones: - zone: 0 name: iz:KRAFTWERK CYMBAL/0 sample: NOISE5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 MODENVTOFILTERFC: -1837.00 MODLFOTOVOL: 15.00 CHORUSSEND: 320.00 MODLFOFREQ: -336.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -881.00 MODENVDECAY: 6076.00 MODENVRELEASE: 6139.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 47 name: KRAFTWERK HAT OP zones: - zone: 0 name: iz:KRAFTWERK HAT OP/1 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 180.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -30.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:KRAFTWERK HAT OP/0 sample: NOISE5 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 FILTERQ: 150.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 48 name: KRAFTWERK HAT CL zones: - zone: 0 name: iz:KRAFTWERK HAT CL/1 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 180.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -30.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:KRAFTWERK HAT CL/0 sample: NOISE5 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 FILTERQ: 150.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 48.00 - instrument: 49 name: KRAFTWERK SD 2 zones: - zone: 0 name: iz:KRAFTWERK SD 2/2 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERFC: 6691.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 PAN: -500.00 MODENVDECAY: -1129.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -1064.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 COARSETUNE: 0.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:KRAFTWERK SD 2/1 sample: LASER1 key_range: 40 - 40 vel_range: 0 - 128 generators: VOLENVDECAY: -1582.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1582.00 OVERRIDEROOTKEY: 35.00 - zone: 2 name: iz:KRAFTWERK SD 2/0 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERFC: 6691.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 PAN: 500.00 MODENVDECAY: -1129.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -1064.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 COARSETUNE: 1.00 OVERRIDEROOTKEY: 33.00 - instrument: 50 name: KRAFTWERK SD 1 zones: - zone: 0 name: iz:KRAFTWERK SD 1/3 sample: WHITE4 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 54.00 - zone: 1 name: iz:KRAFTWERK SD 1/2 sample: WHITE4 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 54.00 - zone: 2 name: iz:KRAFTWERK SD 1/1 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 REVERBSEND: 20.00 VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3986.00 COARSETUNE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 3 name: iz:KRAFTWERK SD 1/0 sample: SINE32 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 FILTERFC: 10721.00 VOLENVDECAY: -3284.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3284.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 51 name: KRAFTWERK BD 2 zones: - zone: 0 name: iz:KRAFTWERK BD 2/1 sample: RIM key_range: 36 - 36 vel_range: 0 - 128 generators: VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3986.00 OVERRIDEROOTKEY: 54.00 - zone: 1 name: iz:KRAFTWERK BD 2/0 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: 1200.00 MODENVDECAY: -5186.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 5628.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 55.00 - instrument: 52 name: KRAFTWERK BD 1 zones: - zone: 0 name: iz:KRAFTWERK BD 1/1 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: 1200.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 5628.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - zone: 1 name: iz:KRAFTWERK BD 1/0 sample: LASER1 key_range: 35 - 35 vel_range: 0 - 128 generators: FILTERFC: 9521.00 VOLENVDECAY: -4842.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -4842.00 OVERRIDEROOTKEY: 54.00 - instrument: 53 name: ANALOG SNARE 9 zones: - zone: 0 name: iz:ANALOG SNARE 9/1 sample: NOISE5 key_range: 72 - 72 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 REVERBSEND: 40.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 77.00 - zone: 1 name: iz:ANALOG SNARE 9/0 sample: SINE32 key_range: 72 - 72 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -5186.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -5186.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 54.00 - instrument: 54 name: ANALOG SNARE 8 zones: - zone: 0 name: iz:ANALOG SNARE 8/2 sample: BD1 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -12.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - zone: 1 name: iz:ANALOG SNARE 8/1 sample: NOISE3 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8321.00 FILTERQ: 80.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 68.00 - zone: 2 name: iz:ANALOG SNARE 8/0 sample: NOISE3 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8321.00 FILTERQ: 80.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 66.00 - instrument: 55 name: ANALOG SNARE 7 zones: - zone: 0 name: iz:ANALOG SNARE 7/2 sample: BD1 key_range: 70 - 70 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -9.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - zone: 1 name: iz:ANALOG SNARE 7/1 sample: NOISE3 key_range: 70 - 70 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10223.00 FILTERQ: 80.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 68.00 - zone: 2 name: iz:ANALOG SNARE 7/0 sample: NOISE3 key_range: 70 - 70 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10223.00 FILTERQ: 80.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 66.00 - instrument: 56 name: ANALOG SNAPPY BD zones: - zone: 0 name: iz:ANALOG SNAPPY BD/0 sample: SINE32 key_range: 68 - 68 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: 1200.00 CHORUSSEND: 40.00 MODENVATTACK: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - instrument: 57 name: ANALOG HAT OPEN 3 zones: - zone: 0 name: iz:ANALOG HAT OPEN 3/0 sample: NOISE5 key_range: 66 - 66 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVHOLD: -2332.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 68.00 - instrument: 58 name: ANALOG HAT CLOSED 3 zones: - zone: 0 name: iz:ANALOG HAT CLOSED 3/0 sample: NOISE5 key_range: 65 - 65 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: -1035.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1035.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 67.00 - instrument: 59 name: ANALOG HAT CLOSED 2 zones: - zone: 0 name: iz:ANALOG HAT CLOSED 2/1 sample: SINEHI key_range: 60 - 60 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 180.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -30.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 1 name: iz:ANALOG HAT CLOSED 2/0 sample: NOISE5 key_range: 60 - 60 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 FILTERQ: 150.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 66.00 - instrument: 60 name: ANALOG HAT OPEN 2 global_zone: name: iz:ANALOG HAT OPEN 2/0 key_range: 0 - 128 vel_range: 0 - 128 zones: - zone: 0 name: iz:ANALOG HAT OPEN 2/2 sample: SINEHI key_range: 61 - 61 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 180.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -30.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 1 name: iz:ANALOG HAT OPEN 2/1 sample: NOISE5 key_range: 61 - 61 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 FILTERQ: 150.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 67.00 - instrument: 61 name: ANALOG SNAP SNARE global_zone: name: iz:ANALOG SNAP SNARE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: -14.00 zones: - zone: 0 name: iz:ANALOG SNAP SNARE/4 sample: SINE32 key_range: 59 - 59 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -25.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 15.00 - zone: 1 name: iz:ANALOG SNAP SNARE/3 sample: NOISE5 key_range: 59 - 59 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - zone: 2 name: iz:ANALOG SNAP SNARE/2 sample: NOISE6 key_range: 59 - 59 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 REVERBSEND: 20.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - zone: 3 name: iz:ANALOG SNAP SNARE/1 sample: NOISE5 key_range: 59 - 59 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - instrument: 62 name: ANALOG BOOM KICK zones: - zone: 0 name: iz:ANALOG BOOM KICK/0 sample: THROB DRUM key_range: 58 - 58 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODLFOTOVOL: 100.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - instrument: 63 name: ANALOG CLAPS 3 global_zone: name: iz:ANALOG CLAPS 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 80.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 COARSETUNE: -27.00 zones: - zone: 0 name: iz:ANALOG CLAPS 3/2 sample: WHITE4 key_range: 55 - 55 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 1 name: iz:ANALOG CLAPS 3/1 sample: WHITE4 key_range: 55 - 55 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 64 name: ANALOG CLAPS 2 global_zone: name: iz:ANALOG CLAPS 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 80.00 COARSETUNE: -27.00 zones: - zone: 0 name: iz:ANALOG CLAPS 2/3 sample: WHITE4 key_range: 53 - 53 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 1 name: iz:ANALOG CLAPS 2/2 sample: NOISE3 key_range: 53 - 53 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1271.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 2 name: iz:ANALOG CLAPS 2/1 sample: WHITE4 key_range: 53 - 53 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 65 name: ANALOG ZAP OPEN zones: - zone: 0 name: iz:ANALOG ZAP OPEN/0 sample: NOISE5 key_range: 52 - 52 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8270.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 MODENVDECAY: -6071.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -6071.00 VOLENVDECAY: 3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 61.00 - instrument: 66 name: ANALOG ZAP CLOSED zones: - zone: 0 name: iz:ANALOG ZAP CLOSED/0 sample: NOISE5 key_range: 51 - 51 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8270.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 MODENVDECAY: -6071.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -6071.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 - instrument: 67 name: ANALOG CLAVE zones: - zone: 0 name: iz:ANALOG CLAVE/0 sample: Tri_a3 key_range: 67 - 67 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3986.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 2.00 OVERRIDEROOTKEY: 42.00 - instrument: 68 name: ANALOG SNARE 6 zones: - zone: 0 name: iz:ANALOG SNARE 6/3 sample: WHITE4 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 1 name: iz:ANALOG SNARE 6/2 sample: NOISE3 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1271.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 2 name: iz:ANALOG SNARE 6/1 sample: WHITE4 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 3 name: iz:ANALOG SNARE 6/0 sample: BD1 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -9.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 4.00 - instrument: 69 name: ANALOG SNARE 5 zones: - zone: 0 name: iz:ANALOG SNARE 5/2 sample: BD1 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -9.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 6.00 - zone: 1 name: iz:ANALOG SNARE 5/1 sample: NOISE3 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 28.00 - zone: 2 name: iz:ANALOG SNARE 5/0 sample: NOISE3 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 26.00 - instrument: 70 name: ANALOG THROB KICK zones: - zone: 0 name: iz:ANALOG THROB KICK/0 sample: THROB DRUM key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODLFOTOVOL: 120.00 REVERBSEND: 20.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 71 name: ANALOG POPS OP 2 zones: - zone: 0 name: iz:ANALOG POPS OP 2/0 sample: Tri_a3 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 2.00 OVERRIDEROOTKEY: 6.00 - instrument: 72 name: ANALOG POPS OP 1 zones: - zone: 0 name: iz:ANALOG POPS OP 1/0 sample: Tri_a3 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 1.00 OVERRIDEROOTKEY: 17.00 - instrument: 73 name: ANALOG POPS CL 2 zones: - zone: 0 name: iz:ANALOG POPS CL 2/0 sample: Tri_a3 key_range: 25 - 25 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 2.00 OVERRIDEROOTKEY: 4.00 - instrument: 74 name: ANALOG POPS CL 1 zones: - zone: 0 name: iz:ANALOG POPS CL 1/0 sample: Tri_a3 key_range: 24 - 24 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 1.00 OVERRIDEROOTKEY: 15.00 - instrument: 75 name: ANALOG PHASER zones: - zone: 0 name: iz:ANALOG PHASER/0 sample: NOISE5 key_range: 44 - 44 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8511.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 MODENVDECAY: -3986.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -3986.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -386.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 - instrument: 76 name: ANALOG TAMBOURINE global_zone: name: iz:ANALOG TAMBOURINE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 120.00 CHORUSSEND: 700.00 REVERBSEND: 40.00 MODLFOFREQ: -353.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1817.00 zones: - zone: 0 name: iz:ANALOG TAMBOURINE/2 sample: SINEHI key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 110.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 65.20 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 1 name: iz:ANALOG TAMBOURINE/1 sample: NOISE5 key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 77 name: ANALOG KICK 3 zones: - zone: 0 name: iz:ANALOG KICK 3/1 sample: WHITE4 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVSUSTAIN: 960.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 16.00 - zone: 1 name: iz:ANALOG KICK 3/0 sample: TR909BD key_range: 31 - 31 vel_range: 0 - 128 generators: CHORUSSEND: 100.00 REVERBSEND: 30.00 VOLENVDECAY: 316.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 316.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 42.00 - instrument: 78 name: ANALOG RIM SHOT zones: - zone: 0 name: iz:ANALOG RIM SHOT/1 sample: WHITE4 key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 15.00 VOLENVSUSTAIN: 960.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 31.00 - zone: 1 name: iz:ANALOG RIM SHOT/0 sample: TR909BD key_range: 37 - 37 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 CHORUSSEND: 100.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -5530.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5530.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 12.00 - instrument: 79 name: ANALOG CRASH CYMBAL1 zones: - zone: 0 name: iz:ANALOG CRASH CYMBAL1/1 sample: WHITE3 key_range: 57 - 57 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 - zone: 1 name: iz:ANALOG CRASH CYMBAL1/0 sample: NOISE5 key_range: 57 - 57 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 VOLENVSUSTAIN: 960.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 58.00 - instrument: 80 name: ANALOG COWBELL zones: - zone: 0 name: iz:ANALOG COWBELL/2 sample: SINEHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1200.00 PAN: 100.00 MODENVATTACK: -6688.00 MODENVDECAY: 4355.00 MODENVRELEASE: 3926.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 82.00 - zone: 1 name: iz:ANALOG COWBELL/1 sample: SINEHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1200.00 PAN: -100.00 MODENVATTACK: -6688.00 MODENVDECAY: 4769.00 MODENVRELEASE: 5349.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 70.00 - zone: 2 name: iz:ANALOG COWBELL/0 sample: NOISE5 key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -11959.00 MODENVSUSTAIN: 0.00 VOLENVDECAY: -2267.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2267.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 70.00 - instrument: 81 name: ANALOG BONGO HI global_zone: name: iz:ANALOG BONGO HI/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 COARSETUNE: -8.00 zones: - zone: 0 name: iz:ANALOG BONGO HI/2 sample: NOISE3 key_range: 62 - 62 vel_range: 0 - 128 generators: VOLENVDECAY: -3068.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3068.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG BONGO HI/1 sample: SINE32 key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 82 name: ANALOG BONGO MED global_zone: name: iz:ANALOG BONGO MED/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 VOLENVDECAY: -1271.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1271.00 COARSETUNE: -8.00 zones: - zone: 0 name: iz:ANALOG BONGO MED/2 sample: NOISE3 key_range: 63 - 63 vel_range: 0 - 128 generators: VOLENVDECAY: -2613.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2613.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG BONGO MED/1 sample: SINE32 key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 83 name: ANALOG BONGO LO global_zone: name: iz:ANALOG BONGO LO/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -8.00 zones: - zone: 0 name: iz:ANALOG BONGO LO/2 sample: NOISE3 key_range: 64 - 64 vel_range: 0 - 128 generators: VOLENVDECAY: -2613.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2613.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG BONGO LO/1 sample: SINE32 key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 51.00 - instrument: 84 name: ANALOG SNARE 4 global_zone: name: iz:ANALOG SNARE 4/0 key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: 0.00 zones: - zone: 0 name: iz:ANALOG SNARE 4/3 sample: NOISE3 key_range: 33 - 33 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 22.00 - zone: 1 name: iz:ANALOG SNARE 4/2 sample: NOISE3 key_range: 33 - 33 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 23.00 - zone: 2 name: iz:ANALOG SNARE 4/1 sample: BD1 key_range: 33 - 33 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -11.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 8.00 - instrument: 85 name: ANALOG SNARE 3 zones: - zone: 0 name: iz:ANALOG SNARE 3/2 sample: 808BD key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 CHORUSSEND: 100.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 20.00 - zone: 1 name: iz:ANALOG SNARE 3/1 sample: NOISE5 key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 1000.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - zone: 2 name: iz:ANALOG SNARE 3/0 sample: NOISE5 key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 1000.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - instrument: 86 name: ANALOG MARACAS zones: - zone: 0 name: iz:ANALOG MARACAS/0 sample: WHITE3 key_range: 69 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 110.00 CHORUSSEND: 235.00 REVERBSEND: 20.00 VOLENVATTACK: -4604.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - instrument: 87 name: ANALOG CLAPS 1 global_zone: name: iz:ANALOG CLAPS 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 90.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 MODLFOFREQ: -983.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: 2.00 zones: - zone: 0 name: iz:ANALOG CLAPS 1/2 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 1 name: iz:ANALOG CLAPS 1/1 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 88 name: ANALOG HI TOM 2 global_zone: name: iz:ANALOG HI TOM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:ANALOG HI TOM 2/2 sample: NOISE3 key_range: 50 - 50 vel_range: 0 - 128 generators: VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG HI TOM 2/1 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 89 name: ANALOG HI TOM 1 global_zone: name: iz:ANALOG HI TOM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -3.00 zones: - zone: 0 name: iz:ANALOG HI TOM 1/2 sample: NOISE3 key_range: 48 - 48 vel_range: 0 - 128 generators: VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG HI TOM 1/1 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 90 name: ANALOG MID TOM 2 global_zone: name: iz:ANALOG MID TOM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -5.00 zones: - zone: 0 name: iz:ANALOG MID TOM 2/2 sample: NOISE3 key_range: 47 - 47 vel_range: 0 - 128 generators: VOLENVDECAY: -2769.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG MID TOM 2/1 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 91 name: ANALOG MID TOM 1 global_zone: name: iz:ANALOG MID TOM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -7.00 zones: - zone: 0 name: iz:ANALOG MID TOM 1/2 sample: NOISE3 key_range: 45 - 45 vel_range: 0 - 128 generators: VOLENVDECAY: -2769.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG MID TOM 1/1 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 92 name: ANALOG LO TOM 2 global_zone: name: iz:ANALOG LO TOM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -9.00 zones: - zone: 0 name: iz:ANALOG LO TOM 2/2 sample: NOISE3 key_range: 43 - 43 vel_range: 0 - 128 generators: VOLENVDECAY: -2769.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG LO TOM 2/1 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 93 name: ANALOG LO TOM 1 global_zone: name: iz:ANALOG LO TOM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -11.00 zones: - zone: 0 name: iz:ANALOG LO TOM 1/2 sample: NOISE3 key_range: 41 - 41 vel_range: 0 - 128 generators: VOLENVDECAY: -2769.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG LO TOM 1/1 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 94 name: ANALOG CRASH CYMBAL2 zones: - zone: 0 name: iz:ANALOG CRASH CYMBAL2/1 sample: SINEHI key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 300.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 124.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 1 name: iz:ANALOG CRASH CYMBAL2/0 sample: NOISE5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 CHORUSSEND: 200.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -1.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 95 name: ANALOG HAT CLOSED zones: - zone: 0 name: iz:ANALOG HAT CLOSED/1 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 REVERBSEND: 20.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 124.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:ANALOG HAT CLOSED/0 sample: NOISE5 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 REVERBSEND: 20.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - instrument: 96 name: ANALOG HAT OPEN zones: - zone: 0 name: iz:ANALOG HAT OPEN/1 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 REVERBSEND: 20.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 124.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:ANALOG HAT OPEN/0 sample: NOISE5 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 REVERBSEND: 20.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 97 name: ANALOG LASER zones: - zone: 0 name: iz:ANALOG LASER/0 sample: LASER1 key_range: 32 - 32 vel_range: 0 - 128 generators: CHORUSSEND: 0.00 REVERBSEND: 10.00 VOLENVDECAY: 4123.00 VOLENVRELEASE: 3217.00 OVERRIDEROOTKEY: 32.00 - instrument: 98 name: ANALOG SNARE 2 global_zone: name: iz:ANALOG SNARE 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: 0.00 zones: - zone: 0 name: iz:ANALOG SNARE 2/4 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 42.00 - zone: 1 name: iz:ANALOG SNARE 2/3 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 2 name: iz:ANALOG SNARE 2/2 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - zone: 3 name: iz:ANALOG SNARE 2/1 sample: BD1 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -11.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 14.00 - instrument: 99 name: ANALOG KICK 2 zones: - zone: 0 name: iz:ANALOG KICK 2/0 sample: TR909BD key_range: 36 - 36 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 100.00 REVERBSEND: 30.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -797.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -797.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 35.00 - instrument: 100 name: ANALOG KICK 1 zones: - zone: 0 name: iz:ANALOG KICK 1/0 sample: TR909BD key_range: 35 - 35 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 CHORUSSEND: 100.00 REVERBSEND: 40.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 44.00 - instrument: 101 name: ANALOG SNARE 1 zones: - zone: 0 name: iz:ANALOG SNARE 1/3 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 1 name: iz:ANALOG SNARE 1/2 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:ANALOG SNARE 1/1 sample: WHITE2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 FILTERQ: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 3 name: iz:ANALOG SNARE 1/0 sample: BD1 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -9.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 14.00 - instrument: 102 name: 909 BASS BOOM zones: - zone: 0 name: iz:909 BASS BOOM/1 sample: SINE32 key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 VOLENVATTACK: -8857.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1018.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1018.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 5.00 FINETUNE: -2.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - zone: 1 name: iz:909 BASS BOOM/0 sample: TR909BD key_range: 34 - 34 vel_range: 0 - 128 generators: ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 100.00 REVERBSEND: 60.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 ATTENUATION: 0.00 COARSETUNE: 0.00 OVERRIDEROOTKEY: 31.00 - instrument: 103 name: 909 TOM HI 2 zones: - zone: 0 name: iz:909 TOM HI 2 /0 sample: 909BD11 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1231.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 104 name: 909 TOM HI 1 zones: - zone: 0 name: iz:909 TOM HI 1 /0 sample: 909BD11 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1076.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1076.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 35.00 - instrument: 105 name: 909 TOM MID 2 zones: - zone: 0 name: iz:909 TOM MID 2 /0 sample: 909BD11 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -931.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -931.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - instrument: 106 name: 909 HAT OPEN zones: - zone: 0 name: iz:909 HAT OPEN /3 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 9.00 ENDLOOPADDROFS: -3.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 304.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 140.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 1 name: iz:909 HAT OPEN /2 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 9.00 ENDLOOPADDROFS: -3.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 304.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 140.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - zone: 2 name: iz:909 HAT OPEN /1 sample: NOISE6 key_range: 46 - 46 vel_range: 0 - 128 generators: FILTERQ: 105.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2701.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2701.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 58.00 - zone: 3 name: iz:909 HAT OPEN /0 sample: NOISE5 key_range: 46 - 46 vel_range: 0 - 128 generators: FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 304.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 ATTENUATION: 6.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 107 name: 909 TOM MID 1 zones: - zone: 0 name: iz:909 TOM MID 1 /0 sample: 909BD11 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -797.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -797.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 108 name: 909 TOM LO 2 zones: - zone: 0 name: iz:909 TOM LO 2 /0 sample: 909BD11 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6667.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -672.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 109 name: 909 HAT CLOSED zones: - zone: 0 name: iz:909 HAT CLOSED /3 sample: NOISE6 key_range: 42 - 42 vel_range: 0 - 128 generators: FILTERQ: 105.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2701.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2701.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 54.00 - zone: 1 name: iz:909 HAT CLOSED /2 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 9.00 ENDLOOPADDROFS: -3.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 140.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 2 name: iz:909 HAT CLOSED /1 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 9.00 ENDLOOPADDROFS: -3.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 140.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 3 name: iz:909 HAT CLOSED /0 sample: NOISE5 key_range: 42 - 42 vel_range: 0 - 128 generators: FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 ATTENUATION: 6.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - instrument: 110 name: 909 TOM LO 1 zones: - zone: 0 name: iz:909 TOM LO 1 /0 sample: 909BD11 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - instrument: 111 name: 909 SNARE HI zones: - zone: 0 name: iz:909 SNARE HI /3 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 ATTENUATION: 36.00 FINETUNE: 116.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 1 name: iz:909 SNARE HI /2 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1231.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1231.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - zone: 2 name: iz:909 SNARE HI /1 sample: SINE32 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 27.00 - zone: 3 name: iz:909 SNARE HI /0 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1231.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1231.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - instrument: 112 name: 909 CLAP zones: - zone: 0 name: iz:909 CLAP /0 sample: CLAP21 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 313.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1487.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: 1487.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 46.00 - instrument: 113 name: 909 SNARE LO zones: - zone: 0 name: iz:909 SNARE LO /4 sample: SINE32 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 27.00 - zone: 1 name: iz:909 SNARE LO /3 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: REVERBSEND: 23.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - zone: 2 name: iz:909 SNARE LO /2 sample: NOISE6 key_range: 38 - 38 vel_range: 0 - 128 generators: CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -931.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -931.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 3 name: iz:909 SNARE LO /1 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: REVERBSEND: 23.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 ATTENUATION: 50.80 FINETUNE: 116.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 4 name: iz:909 SNARE LO /0 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: REVERBSEND: 23.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - instrument: 114 name: 909 RIM zones: - zone: 0 name: iz:909 RIM /1 sample: NOISE6 key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 141.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 80.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 64.00 - zone: 1 name: iz:909 RIM /0 sample: SQUARELO key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 111.00 ENDLOOPADDROFS: -6.00 FILTERFC: 6608.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 115 name: 909 BASS DRUM HI zones: - zone: 0 name: iz:909 BASS DRUM HI /1 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -4603.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -251.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -251.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 2.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - zone: 1 name: iz:909 BASS DRUM HI /0 sample: TR909BD key_range: 36 - 36 vel_range: 0 - 128 generators: ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 ATTENUATION: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 31.00 - instrument: 116 name: 909 BASS DRUM LO zones: - zone: 0 name: iz:909 BASS DRUM LO /0 sample: TR909BD key_range: 35 - 35 vel_range: 0 - 128 generators: ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 87.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 87.00 ATTENUATION: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 32.00 - instrument: 117 name: 909 SNARE 2 zones: - zone: 0 name: iz:909 SNARE 2 /2 sample: NOISE5 key_range: 27 - 27 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:909 SNARE 2 /1 sample: SINE32 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 11.00 - zone: 2 name: iz:909 SNARE 2 /0 sample: NOISE5 key_range: 27 - 27 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - instrument: 118 name: 909 SNARE 1 zones: - zone: 0 name: iz:909 SNARE 1 /3 sample: NOISE6 key_range: 26 - 26 vel_range: 0 - 128 generators: FILTERQ: 75.00 CHORUSSEND: 995.00 REVERBSEND: 23.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 1 name: iz:909 SNARE 1 /2 sample: NOISE5 key_range: 26 - 26 vel_range: 0 - 128 generators: FILTERQ: 105.00 REVERBSEND: 23.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -672.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:909 SNARE 1 /1 sample: SINE32 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 13.00 - zone: 3 name: iz:909 SNARE 1 /0 sample: NOISE5 key_range: 26 - 26 vel_range: 0 - 128 generators: FILTERQ: 105.00 REVERBSEND: 23.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -672.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - instrument: 119 name: 909 BASS DRUM 2 zones: - zone: 0 name: iz:909 BASS DRUM 2 /1 sample: SINE32 key_range: 25 - 25 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 392.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -4603.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 304.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 304.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 1 name: iz:909 BASS DRUM 2 /0 sample: TR909BD key_range: 25 - 25 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 156.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 87.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 87.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 26.00 - instrument: 120 name: 909 BASS DRUM 1 zones: - zone: 0 name: iz:909 BASS DRUM 1 /1 sample: SINE32 key_range: 24 - 24 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 300.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -8857.00 VOLENVHOLD: -4842.00 VOLENVDECAY: 316.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 316.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 1 name: iz:909 BASS DRUM 1 /0 sample: TR909BD key_range: 24 - 24 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 87.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 87.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 20.00 - instrument: 121 name: 808 CLAVES zones: - zone: 0 name: iz:808 CLAVES /0 sample: TRIANGLE key_range: 75 - 75 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -3.00 CHORUSSEND: 0.00 REVERBSEND: 10.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 122 name: 808 MARACAS zones: - zone: 0 name: iz:808 MARACAS /0 sample: NOISE5 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 REVERBSEND: 23.00 VOLENVATTACK: -5186.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 5.00 FINETUNE: 100.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 69.00 - instrument: 123 name: 808 BONGO LO zones: - zone: 0 name: iz:808 BONGO LO /0 sample: SINE32 key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -347.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -347.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 60.00 - instrument: 124 name: 808 BONGO MID zones: - zone: 0 name: iz:808 BONGO MID /0 sample: SINE32 key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -672.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - instrument: 125 name: 808 BONGO HI zones: - zone: 0 name: iz:808 BONGO HI /0 sample: SINE32 key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - instrument: 126 name: 808 COWBELL zones: - zone: 0 name: iz:808 COWBELL /1 sample: TRIANGLE key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -3.00 MODLFOTOVOL: 33.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 MODLFOFREQ: -42.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -347.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -347.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 126.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - zone: 1 name: iz:808 COWBELL /0 sample: SQUARE2 key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 24.00 ENDLOOPADDROFS: -4.00 FILTERFC: 10325.00 MODLFOTOVOL: 28.00 CHORUSSEND: 548.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 80.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - instrument: 127 name: 808 HI TOM 2 zones: - zone: 0 name: iz:808 HI TOM 2 /0 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 128 name: 808 CYMBAL zones: - zone: 0 name: iz:808 CYMBAL /3 sample: SINEHI key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: 22.00 CHORUSSEND: 250.00 REVERBSEND: 7.00 MODLFOFREQ: -451.00 VIBLFOFREQ: -725.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1800.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1800.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 96.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 35.00 - zone: 1 name: iz:808 CYMBAL /2 sample: NOISE5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 55.00 - zone: 2 name: iz:808 CYMBAL /1 sample: SINEHI key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 392.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1800.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1800.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 80.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 98.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 3 name: iz:808 CYMBAL /0 sample: NOISE5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 CHORUSSEND: 627.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1800.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1800.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 129 name: 808 HI TOM 1 zones: - zone: 0 name: iz:808 HI TOM 1 /0 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -22.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 130 name: 808 MID TOM 2 zones: - zone: 0 name: iz:808 MID TOM 2 /0 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1231.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 131 name: 808 HAT OPEN zones: - zone: 0 name: iz:808 HAT OPEN /1 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 126.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:808 HAT OPEN /0 sample: NOISE5 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 132 name: 808 MID TOM 1 zones: - zone: 0 name: iz:808 MID TOM 1 /0 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1076.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1076.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 133 name: 808 LO TOM 2 zones: - zone: 0 name: iz:808 LO TOM 2 /0 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -931.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -931.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 134 name: 808 HAT CLOSED zones: - zone: 0 name: iz:808 HAT CLOSED /1 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 126.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:808 HAT CLOSED /0 sample: NOISE5 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 48.00 - instrument: 135 name: 808 LO TOM 1 zones: - zone: 0 name: iz:808 LO TOM 1 /0 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -797.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -797.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 136 name: 808 SNARE DRUM HI zones: - zone: 0 name: iz:808 SNARE DRUM HI /2 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 124.00 CHORUSSEND: 533.00 REVERBSEND: 47.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:808 SNARE DRUM HI /1 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 121.00 CHORUSSEND: 556.00 REVERBSEND: 47.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 2 name: iz:808 SNARE DRUM HI /0 sample: SINE32 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 109.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - instrument: 137 name: 808 CLAP zones: - zone: 0 name: iz:808 CLAP /0 sample: CLAP2 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1487.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: 1487.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 16.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 45.00 - instrument: 138 name: 808 SNARE DRUM LO zones: - zone: 0 name: iz:808 SNARE DRUM LO /2 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: CHORUSSEND: 572.00 REVERBSEND: 54.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 1 name: iz:808 SNARE DRUM LO /1 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: CHORUSSEND: 595.00 REVERBSEND: 54.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 2 name: iz:808 SNARE DRUM LO /0 sample: SINE32 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 FILTERFC: 11151.00 CHORUSSEND: 109.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 28.00 - instrument: 139 name: 808 RIM SHOT zones: - zone: 0 name: iz:808 RIM SHOT /1 sample: NOISE3 key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 38.00 - zone: 1 name: iz:808 RIM SHOT /0 sample: TRIANGLE key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -3.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 21.00 - instrument: 140 name: 808 BASS DRUM HI zones: - zone: 0 name: iz:808 BASS DRUM HI /0 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 39.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -386.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 141 name: 808 BASS DRUM LO zones: - zone: 0 name: iz:808 BASS DRUM LO /0 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 702.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 702.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 142 name: 808 SNARE HIGH zones: - zone: 0 name: iz:808 SNARE HIGH /2 sample: NOISE5 key_range: 31 - 31 vel_range: 0 - 128 generators: FILTERQ: 190.00 CHORUSSEND: 729.00 REVERBSEND: 54.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - zone: 1 name: iz:808 SNARE HIGH /1 sample: NOISE5 key_range: 31 - 31 vel_range: 0 - 128 generators: FILTERQ: 190.00 CHORUSSEND: 697.00 REVERBSEND: 54.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 2 name: iz:808 SNARE HIGH /0 sample: SINE32 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 143 name: 808 SNARE SHARP zones: - zone: 0 name: iz:808 SNARE SHARP /2 sample: NOISE5 key_range: 30 - 30 vel_range: 0 - 128 generators: MODENVTOFILTERFC: 7087.00 CHORUSSEND: 493.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 1 name: iz:808 SNARE SHARP /1 sample: NOISE5 key_range: 30 - 30 vel_range: 0 - 128 generators: MODENVTOFILTERFC: 7087.00 CHORUSSEND: 415.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:808 SNARE SHARP /0 sample: SINE32 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 17.00 - instrument: 144 name: 808 SNARE FILTERED zones: - zone: 0 name: iz:808 SNARE FILTERED /2 sample: NOISE5 key_range: 29 - 29 vel_range: 0 - 128 generators: FILTERQ: 138.00 CHORUSSEND: 650.00 REVERBSEND: 78.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 21.00 - zone: 1 name: iz:808 SNARE FILTERED /1 sample: NOISE5 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 138.00 CHORUSSEND: 627.00 REVERBSEND: 78.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 23.00 - zone: 2 name: iz:808 SNARE FILTERED /0 sample: SINE32 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 145 name: 808 ULTRA SNAPPY zones: - zone: 0 name: iz:808 ULTRA SNAPPY /2 sample: NOISE5 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 75.00 MODENVTOFILTERFC: 7087.00 CHORUSSEND: 431.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:808 ULTRA SNAPPY /1 sample: NOISE5 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 75.00 MODENVTOFILTERFC: 7087.00 CHORUSSEND: 423.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 2 name: iz:808 ULTRA SNAPPY /0 sample: SINE32 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 109.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 16.00 - instrument: 146 name: 808 BASS DRUM MUTE zones: - zone: 0 name: iz:808 BASS DRUM MUTE /0 sample: SINE32 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOFILTERFC: -7087.00 CHORUSSEND: 313.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1076.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1076.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 147 name: 808 BASS DRUM BOOM2 zones: - zone: 0 name: iz:808 BASS DRUM BOOM2/0 sample: SINE32 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 42.00 - instrument: 148 name: 808 BASS DRUM SHORT zones: - zone: 0 name: iz:808 BASS DRUM SHORT/0 sample: SINE32 key_range: 25 - 25 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 149 name: 808 BASS DRUM BOOM1 zones: - zone: 0 name: iz:808 BASS DRUM BOOM1/0 sample: SINE32 key_range: 24 - 24 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: 1200.00 CHORUSSEND: 250.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -11959.00 MODENVDECAY: -1064.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -1064.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1487.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1487.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - instrument: 150 name: CR78 HI TOM 2 zones: - zone: 0 name: iz:CR78 HI TOM 2/0 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 151 name: CR78 HI TOM 1 zones: - zone: 0 name: iz:CR78 HI TOM 1/0 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -22.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 152 name: CR78 MID TOM 2 zones: - zone: 0 name: iz:CR78 MID TOM 2/0 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 153 name: CR78 MID TOM 1 zones: - zone: 0 name: iz:CR78 MID TOM 1/0 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 154 name: CR78 LO TOM 2 zones: - zone: 0 name: iz:CR78 LO TOM 2/0 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 155 name: CR78 LO TOM 1 zones: - zone: 0 name: iz:CR78 LO TOM 1/0 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 156 name: CR78 BONGO LO global_zone: name: iz:CR78 BONGO LO/0 key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 VOLENVDECAY: -1868.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1868.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 zones: - instrument: 157 name: CR78 BONGO MID global_zone: name: iz:CR78 BONGO MID/0 key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 VOLENVDECAY: -2463.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2463.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 FINETUNE: 40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 74.00 zones: - instrument: 158 name: CR78 BONGO HI global_zone: name: iz:CR78 BONGO HI/0 key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 VOLENVDECAY: -3969.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3969.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -3.00 FINETUNE: -60.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 61.00 zones: - instrument: 159 name: CR78 COWBELL zones: - zone: 0 name: iz:CR78 COWBELL /1 sample: TRIHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: -108.00 CHORUSSEND: 274.00 REVERBSEND: 15.00 MODLFOFREQ: 114.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 65.00 - zone: 1 name: iz:CR78 COWBELL /0 sample: TRIHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: -97.00 CHORUSSEND: 274.00 REVERBSEND: 15.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 25.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 65.00 - instrument: 160 name: CR78 TAMBOURINE zones: - zone: 0 name: iz:CR78 TAMBOURINE /1 sample: SINEHI key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 190.00 MODLFOTOVOL: 80.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVDECAY: -2325.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2325.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 32.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - zone: 1 name: iz:CR78 TAMBOURINE /0 sample: WHITE3 key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 MODLFOTOVOL: 93.00 REVERBSEND: 0.00 MODLFOFREQ: 114.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2325.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2325.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 65.00 - instrument: 161 name: CR78 HAT OPEN zones: - zone: 0 name: iz:CR78 HAT OPEN /1 sample: WHITE3 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 52.00 - zone: 1 name: iz:CR78 HAT OPEN /0 sample: WHITE3 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 135.00 CHORUSSEND: 313.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1105.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - instrument: 162 name: CR78 HAT CLOSED zones: - zone: 0 name: iz:CR78 HAT CLOSED /1 sample: WHITE3 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 47.00 - zone: 1 name: iz:CR78 HAT CLOSED /0 sample: WHITE3 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 135.00 CHORUSSEND: 313.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 163 name: CR78 SNARE 2 zones: - zone: 0 name: iz:CR78 SNARE 2 /3 sample: WHITE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 15.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -21.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:CR78 SNARE 2 /2 sample: WHITE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 15.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 2 name: iz:CR78 SNARE 2 /1 sample: WHITE2 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 6.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 3 name: iz:CR78 SNARE 2 /0 sample: BD1 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 123.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 5.00 - instrument: 164 name: CR78 METAL zones: - zone: 0 name: iz:CR78 METAL /1 sample: SINEHI key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -5293.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 65.20 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:CR78 METAL /0 sample: WHITE2 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 FILTERFC: 11092.00 FILTERQ: 190.00 CHORUSSEND: 470.00 REVERBSEND: 0.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -5293.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 165 name: CR78 SNARE 1 zones: - zone: 0 name: iz:CR78 SNARE 1 /3 sample: WHITE2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 36.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 1 name: iz:CR78 SNARE 1 /2 sample: WHITE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 15.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 3.00 OVERRIDEROOTKEY: 50.00 - zone: 2 name: iz:CR78 SNARE 1 /1 sample: WHITE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 15.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -21.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - zone: 3 name: iz:CR78 SNARE 1 /0 sample: BD1 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 123.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 15.00 - instrument: 166 name: CR78 RIM SHOT zones: - zone: 0 name: iz:CR78 RIM SHOT /0 sample: TRIANGLE key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -3.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 OVERRIDEROOTKEY: 21.00 - instrument: 167 name: CR78 KICK 2 zones: - zone: 0 name: iz:CR78 KICK 2 /0 sample: TR909BD key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODENVTOPITCH: -1190.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 3.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 43.00 - instrument: 168 name: CR78 KICK 1 zones: - zone: 0 name: iz:CR78 KICK 1 /0 sample: TR909BD key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODENVTOPITCH: -1190.00 VIBLFOFREQ: -15597.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 46.00 - instrument: 169 name: CR78 CLAVE zones: - zone: 0 name: iz:CR78 CLAVE /0 sample: TRIANGLE key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 16.00 ENDLOOPADDROFS: -3.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 84.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 13.00 - instrument: 170 name: CR78 MARACAS zones: - zone: 0 name: iz:CR78 MARACAS /0 sample: WHITE2 key_range: 33 - 33 vel_range: 0 - 128 generators: CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 ATTENUATION: 20.80 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 30.00 - instrument: 171 name: CR78 GUIRO zones: - zone: 0 name: iz:CR78 GUIRO /0 sample: 1 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 120.00 MODLFOTOVOL: -56.00 REVERBSEND: 0.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -3161.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - instrument: 172 name: 101 LASER 3 zones: - zone: 0 name: iz:101 LASER 3/0 sample: LASER1 key_range: 53 - 53 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 60.00 REVERBSEND: 10.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 52.00 - instrument: 173 name: 101 LASER 2 zones: - zone: 0 name: iz:101 LASER 2/0 sample: RIM key_range: 52 - 52 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 CHORUSSEND: 313.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 3217.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 3688.00 ATTENUATION: 0.00 FINETUNE: 45.00 OVERRIDEROOTKEY: 58.00 - instrument: 174 name: 101 LASER 1 zones: - zone: 0 name: iz:101 LASER 1/0 sample: CLICK key_range: 51 - 51 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 548.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVDECAY: 4968.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 3919.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 51.00 - instrument: 175 name: 101 MARACAS zones: - zone: 0 name: iz:101 MARACAS /0 sample: WHITE3 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 190.00 MODLFOTOVOL: -103.00 CHORUSSEND: 995.00 REVERBSEND: 23.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -1674.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 74.00 - instrument: 176 name: 101 SHAKER zones: - zone: 0 name: iz:101 SHAKER /0 sample: WHITE3 key_range: 69 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 235.00 REVERBSEND: 15.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -1674.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -4603.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - instrument: 177 name: 101 BONGO LO zones: - zone: 0 name: iz:101 BONGO LO /0 sample: SINE32 key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 60.00 - instrument: 178 name: 101 BONGO MID zones: - zone: 0 name: iz:101 BONGO MID /0 sample: SINE32 key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - instrument: 179 name: 101 BONGO HI zones: - zone: 0 name: iz:101 BONGO HI /0 sample: SINE32 key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -3140.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -3140.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3284.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3284.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - instrument: 180 name: 101 COWBELL zones: - zone: 0 name: iz:101 COWBELL /1 sample: TRIHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 117.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 97.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 63.00 - zone: 1 name: iz:101 COWBELL /0 sample: TRIHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: 86.00 CHORUSSEND: 117.00 REVERBSEND: 15.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 97.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 63.00 - instrument: 181 name: 101 TAMBOURINE zones: - zone: 0 name: iz:101 TAMBOURINE /1 sample: TRIHI key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: 60.00 CHORUSSEND: 117.00 REVERBSEND: 31.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 110.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - zone: 1 name: iz:101 TAMBOURINE /0 sample: WHITE3 key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 111.00 MODLFOTOVOL: -103.00 CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -42.00 VIBLFOFREQ: -1674.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 61.00 - instrument: 182 name: 101 HI TOM 2 zones: - zone: 0 name: iz:101 HI TOM 2 /0 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 183 name: 101 CYMBAL zones: - zone: 0 name: iz:101 CYMBAL /1 sample: WHITE2 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - zone: 1 name: iz:101 CYMBAL /0 sample: WHITE3 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 33.00 MODLFOTOFILTERFC: 112.00 MODLFOTOVOL: 9.00 CHORUSSEND: 893.00 REVERBSEND: 23.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 2295.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 2065.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 55.00 - instrument: 184 name: 101 HI TOM 1 zones: - zone: 0 name: iz:101 HI TOM 1 /0 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -22.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 185 name: 101 MID TOM 2 zones: - zone: 0 name: iz:101 MID TOM 2 /0 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 186 name: 101 HAT OPEN zones: - zone: 0 name: iz:101 HAT OPEN /0 sample: WHITE3 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1105.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - instrument: 187 name: 101 MID TOM 1 zones: - zone: 0 name: iz:101 MID TOM 1 /0 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 188 name: 101 LO TOM 2 zones: - zone: 0 name: iz:101 LO TOM 2 /0 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 189 name: 101 HAT CLOSED zones: - zone: 0 name: iz:101 HAT CLOSED /0 sample: WHITE3 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 190 name: 101 LO TOM 1 zones: - zone: 0 name: iz:101 LO TOM 1 /0 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 191 name: 101 SNARE 2 zones: - zone: 0 name: iz:101 SNARE 2 /3 sample: WHITE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 90.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:101 SNARE 2 /2 sample: WHITE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 88.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 49.00 - zone: 2 name: iz:101 SNARE 2 /1 sample: WHITE2 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - zone: 3 name: iz:101 SNARE 2 /0 sample: BD1 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 192 name: 101 CLAPS zones: - zone: 0 name: iz:101 CLAPS /1 sample: WHITE2 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 MODLFOTOVOL: 93.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 MODLFOFREQ: 114.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 49.00 - zone: 1 name: iz:101 CLAPS /0 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 MODLFOTOVOL: 93.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 MODLFOFREQ: 46.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 193 name: 101 SNARE 1 zones: - zone: 0 name: iz:101 SNARE 1 /3 sample: WHITE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - zone: 1 name: iz:101 SNARE 1 /2 sample: WHITE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 2 name: iz:101 SNARE 1 /1 sample: WHITE2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 3 name: iz:101 SNARE 1 /0 sample: BD1 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 194 name: 101 RIM SHOT zones: - zone: 0 name: iz:101 RIM SHOT /1 sample: WHITE4 key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 37.00 - zone: 1 name: iz:101 RIM SHOT /0 sample: TRIANGLE key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 15.00 ENDLOOPADDROFS: -3.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 22.00 - instrument: 195 name: 101 KICK 2 zones: - zone: 0 name: iz:101 KICK 2 /0 sample: 808BD key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - instrument: 196 name: 101 KICK 1 zones: - zone: 0 name: iz:101 KICK 1 /0 sample: TR909BD key_range: 35 - 35 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 CHORUSSEND: 70.00 REVERBSEND: 15.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -159.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -159.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 49.00 - instrument: 197 name: 101 SNARE EXTRA #02 zones: - zone: 0 name: iz:101 SNARE EXTRA #02/2 sample: WHITE3 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 1 name: iz:101 SNARE EXTRA #02/1 sample: WHITE3 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 2 name: iz:101 SNARE EXTRA #02/0 sample: BD1 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 19.00 - instrument: 198 name: 101 HAT OP EXTRA zones: - zone: 0 name: iz:101 HAT OP EXTRA /0 sample: WHITE3 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 190.00 CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 653.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 284.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 199 name: 101 HAT CL EXTRA zones: - zone: 0 name: iz:101 HAT CL EXTRA /0 sample: WHITE3 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 190.00 CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 200 name: 101 SNARE EXTRA 3 zones: - zone: 0 name: iz:101 SNARE EXTRA 3 /2 sample: WHITE3 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 75.00 CHORUSSEND: 995.00 REVERBSEND: 31.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 61.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 1 name: iz:101 SNARE EXTRA 3 /1 sample: WHITE3 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 75.00 CHORUSSEND: 995.00 REVERBSEND: 31.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 2 name: iz:101 SNARE EXTRA 3 /0 sample: BD1 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 13.00 - instrument: 201 name: 101 KICK EXTRA 3 zones: - zone: 0 name: iz:101 KICK EXTRA 3 /0 sample: TR909BD key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VIBLFOTOPITCH: -1190.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 33.00 - instrument: 202 name: 101 SNARE EXTRA 2 zones: - zone: 0 name: iz:101 SNARE EXTRA 2 /3 sample: WHITE2 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:101 SNARE EXTRA 2 /2 sample: WHITE3 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 2 name: iz:101 SNARE EXTRA 2 /1 sample: WHITE3 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 3 name: iz:101 SNARE EXTRA 2 /0 sample: BD1 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 14.00 - instrument: 203 name: 101 SNARE EXTRA 1 zones: - zone: 0 name: iz:101 SNARE EXTRA 1 /3 sample: WHITE2 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - zone: 1 name: iz:101 SNARE EXTRA 1 /2 sample: WHITE3 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 2 name: iz:101 SNARE EXTRA 1 /1 sample: WHITE3 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 3 name: iz:101 SNARE EXTRA 1 /0 sample: BD1 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 14.00 - instrument: 204 name: 101 KICK EXTRA 2 zones: - zone: 0 name: iz:101 KICK EXTRA 2 /0 sample: TR909BD key_range: 25 - 25 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 70.00 REVERBSEND: 7.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -797.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -797.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 24.00 - instrument: 205 name: 101 KICK EXTRA 1 zones: - zone: 0 name: iz:101 KICK EXTRA 1 /0 sample: TR909BD key_range: 24 - 24 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 FILTERFC: 10266.00 CHORUSSEND: 70.00 REVERBSEND: 15.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 34.00 - instrument: 206 name: ADDITIVE BELL global_zone: name: iz:ADDITIVE BELL/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 60.00 MODENVATTACK: -5186.00 MODENVDECAY: -5186.00 MODENVRELEASE: -5186.00 VOLENVATTACK: -8857.00 VOLENVDECAY: -3986.00 VOLENVRELEASE: -3986.00 COARSETUNE: 12.00 zones: - zone: 0 name: iz:ADDITIVE BELL/4 sample: wav1_1 key_range: 0 - 57 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:ADDITIVE BELL/3 sample: wav1_3 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - zone: 2 name: iz:ADDITIVE BELL/2 sample: wav1_2 key_range: 58 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 - zone: 3 name: iz:ADDITIVE BELL/1 sample: wav1_4 key_range: 82 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - instrument: 207 name: SMOOTH SAW global_zone: name: iz:SMOOTH SAW/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:SMOOTH SAW/4 sample: SAW4 key_range: 94 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 1 name: iz:SMOOTH SAW/3 sample: SAW3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -7.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - zone: 2 name: iz:SMOOTH SAW/2 sample: SAW2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - zone: 3 name: iz:SMOOTH SAW/1 sample: SAW1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 - instrument: 208 name: MELODIC VIBRATO global_zone: name: iz:MELODIC VIBRATO/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: 12.00 zones: - zone: 0 name: iz:MELODIC VIBRATO/4 sample: W12_4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -65.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 95.00 - zone: 1 name: iz:MELODIC VIBRATO/3 sample: W13_3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 91.00 - zone: 2 name: iz:MELODIC VIBRATO/2 sample: W12_2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 3 name: iz:MELODIC VIBRATO/1 sample: W12_1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - instrument: 209 name: FM CLANG global_zone: name: iz:FM CLANG/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FM CLANG/1 sample: W10000 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 23.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 77.00 - instrument: 210 name: FM3 global_zone: name: iz:FM3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -5530.00 MODENVATTACK: -4842.00 MODENVHOLD: -4842.00 MODENVDECAY: -4842.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:FM3/4 sample: dm4_a4 key_range: 94 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 92.00 - zone: 1 name: iz:FM3/3 sample: dm4_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 13.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 80.00 - zone: 2 name: iz:FM3/2 sample: dm4_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 68.00 - zone: 3 name: iz:FM3/1 sample: dm4_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - instrument: 211 name: FM BELL 2 global_zone: name: iz:FM BELL 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -5530.00 MODENVATTACK: -4842.00 MODENVHOLD: -4842.00 MODENVDECAY: -4842.00 MODENVRELEASE: -3986.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FM BELL 2/4 sample: W7_4 key_range: 94 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 96.00 - zone: 1 name: iz:FM BELL 2/3 sample: W7_3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 2 name: iz:FM BELL 2/2 sample: W7_2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - zone: 3 name: iz:FM BELL 2/1 sample: W7_1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -1.00 SAMPLEMODE: 1.00 - instrument: 212 name: HARSH FM BASS global_zone: name: iz:HARSH FM BASS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:HARSH FM BASS/4 sample: W6_4 key_range: 94 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -48.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 96.00 - zone: 1 name: iz:HARSH FM BASS/3 sample: W6_3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 65.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 2 name: iz:HARSH FM BASS/2 sample: W6_2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 71.00 - zone: 3 name: iz:HARSH FM BASS/1 sample: W6_1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 28.00 SAMPLEMODE: 1.00 - instrument: 213 name: FAERIE CHORAL global_zone: name: iz:FAERIE CHORAL /0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FAERIE CHORAL /1 sample: MOD2_C3 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: -30.00 ENDLOOPADDROFS: 4.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 82.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - instrument: 214 name: AAA global_zone: name: iz:AAA/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -5530.00 MODENVATTACK: -4842.00 MODENVHOLD: -4842.00 MODENVDECAY: -4842.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:AAA/1 sample: 191 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 31.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 80.00 - instrument: 215 name: OINK global_zone: name: iz:OINK/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:OINK/1 sample: WAV5 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 77.00 - instrument: 216 name: CLINK global_zone: name: iz:CLINK/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:CLINK/1 sample: FM90000 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 94.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - instrument: 217 name: SMOOTH SQUARE global_zone: name: iz:SMOOTH SQUARE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -8590.00 VOLENVHOLD: -8359.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 FINETUNE: 0.00 zones: - zone: 0 name: iz:SMOOTH SQUARE/4 sample: SQ4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -48.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 1 name: iz:SMOOTH SQUARE/3 sample: SQ3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - zone: 2 name: iz:SMOOTH SQUARE/2 sample: SQ2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -26.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - zone: 3 name: iz:SMOOTH SQUARE/1 sample: SQ1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -15.00 SAMPLEMODE: 1.00 - instrument: 218 name: HALF RECTIFICATION global_zone: name: iz:HALF RECTIFICATION/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 FINETUNE: 0.00 zones: - zone: 0 name: iz:HALF RECTIFICATION/4 sample: HR4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -48.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 1 name: iz:HALF RECTIFICATION/3 sample: HR3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 29.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 80.00 - zone: 2 name: iz:HALF RECTIFICATION/2 sample: HR2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 10.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 73.00 - zone: 3 name: iz:HALF RECTIFICATION/1 sample: HR1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -26.00 SAMPLEMODE: 1.00 - instrument: 219 name: VIBRAPHONE global_zone: name: iz:VIBRAPHONE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:VIBRAPHONE/1 sample: HOLLOW key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 47.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - instrument: 220 name: GRUNGE global_zone: name: iz:GRUNGE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 FINETUNE: 0.00 zones: - zone: 0 name: iz:GRUNGE/1 sample: GRUNGE key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 83.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 78.00 - instrument: 221 name: PIPES global_zone: name: iz:PIPES/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:PIPES/1 sample: BELL4 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 78.00 - instrument: 222 name: BELLS global_zone: name: iz:BELLS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:BELLS/1 sample: BELL10000 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 78.00 - instrument: 223 name: BREEZE global_zone: name: iz:BREEZE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:BREEZE/1 sample: HISS key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 11921.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 0.00 FINETUNE: 57.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 82.00 - instrument: 224 name: AAHS global_zone: name: iz:AAHS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 FINETUNE: 0.00 zones: - zone: 0 name: iz:AAHS/1 sample: AAH key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 28.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 76.00 - instrument: 225 name: ELECTRIC BASS global_zone: name: iz:ELECTRIC BASS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:ELECTRIC BASS/4 sample: W8_4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 96.00 - zone: 1 name: iz:ELECTRIC BASS/3 sample: W8_3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 91.00 - zone: 2 name: iz:ELECTRIC BASS/2 sample: W8_2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 15.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 3 name: iz:ELECTRIC BASS/1 sample: W8_1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - instrument: 226 name: DM 3 global_zone: name: iz:DM 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: -12.00 zones: - zone: 0 name: iz:DM 3/4 sample: dm3_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:DM 3/3 sample: dm3_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 14.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:DM 3/2 sample: dm3_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:DM 3/1 sample: dm3_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 227 name: DM 2 global_zone: name: iz:DM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:DM 2/4 sample: dm2_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:DM 2/3 sample: dm2_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:DM 2/2 sample: dm2_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:DM 2/1 sample: dm2_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 228 name: DM 1 global_zone: name: iz:DM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:DM 1/4 sample: dm1_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:DM 1/3 sample: dm1_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -39.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:DM 1/2 sample: dm1_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -48.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:DM 1/1 sample: dm1_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 229 name: WHITENOISE 3 global_zone: name: iz:WHITENOISE 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:WHITENOISE 3/1 sample: Noise3 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 2.00 ENDLOOPADDROFS: 4.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 75.00 - instrument: 230 name: TRIANGLE WAVE global_zone: name: iz:TRIANGLE WAVE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -9559.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:TRIANGLE WAVE/4 sample: Tri_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:TRIANGLE WAVE/3 sample: Tri_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: 4.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:TRIANGLE WAVE/2 sample: Tri_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:TRIANGLE WAVE/1 sample: Tri_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 231 name: SQUARE WAVE global_zone: name: iz:SQUARE WAVE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -8857.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:SQUARE WAVE/4 sample: Sq_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:SQUARE WAVE/3 sample: Sq_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:SQUARE WAVE/2 sample: Sq_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:SQUARE WAVE/1 sample: Sq_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 232 name: SINE WAVE global_zone: name: iz:SINE WAVE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:SINE WAVE/4 sample: Sine_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:SINE WAVE/3 sample: Sine_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:SINE WAVE/2 sample: Sine_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:SINE WAVE/1 sample: Sine_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 233 name: SAWTOOTH global_zone: name: iz:SAWTOOTH/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:SAWTOOTH/4 sample: Saw_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:SAWTOOTH/3 sample: Saw_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:SAWTOOTH/2 sample: Saw_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:SAWTOOTH/1 sample: Saw_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 234 name: ADDITIVE 3 global_zone: name: iz:ADDITIVE 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:ADDITIVE 3/4 sample: add3_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:ADDITIVE 3/3 sample: add3_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:ADDITIVE 3/2 sample: add3_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:ADDITIVE 3/1 sample: add3_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 235 name: WHITENOISE 0 global_zone: name: iz:WHITENOISE 0/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:WHITENOISE 0/1 sample: Noise4 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 75.00 - instrument: 236 name: FM 2 global_zone: name: iz:FM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FM 2/4 sample: Fm2_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:FM 2/3 sample: Fm2_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:FM 2/2 sample: Fm2_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:FM 2/1 sample: Fm2_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 237 name: FM 1 global_zone: name: iz:FM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FM 1/4 sample: Fm3_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:FM 1/3 sample: Fm3_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:FM 1/2 sample: Fm3_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:FM 1/1 sample: Fm3_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 samples: - sample: 0 name: FM4-0440 source_start: 0 source_end: 107 source_loopstart: 30 source_loopend: 102 start: 0 end: 107 loopstart: 30 loopend: 102 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 1 name: Tri2 source_start: 140 source_end: 251 source_loopstart: 170 source_loopend: 244 start: 140 end: 251 loopstart: 170 loopend: 244 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 2 name: Tri-Sq source_start: 284 source_end: 466 source_loopstart: 313 source_loopend: 459 start: 284 end: 466 loopstart: 313 loopend: 459 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 3 name: FM5-0440 source_start: 499 source_end: 706 source_loopstart: 552 source_loopend: 698 start: 499 end: 706 loopstart: 552 loopend: 698 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 4 name: Bell2-0440 source_start: 739 source_end: 854 source_loopstart: 776 source_loopend: 849 start: 739 end: 854 loopstart: 776 loopend: 849 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 5 name: cowbell source_start: 887 source_end: 1037 source_loopstart: 895 source_loopend: 1008 start: 887 end: 1037 loopstart: 895 loopend: 1008 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 6 name: claploop source_start: 1070 source_end: 5479 source_loopstart: 1078 source_loopend: 5472 start: 1070 end: 5479 loopstart: 1078 loopend: 5472 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 7 name: white5 source_start: 5512 source_end: 9921 source_loopstart: 5534 source_loopend: 9909 start: 5512 end: 9921 loopstart: 5534 loopend: 9909 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 8 name: sinehi source_start: 9954 source_end: 10076 source_loopstart: 9973 source_loopend: 10070 start: 9954 end: 10076 loopstart: 9973 loopend: 10070 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 9 name: white808-2 source_start: 10109 source_end: 16523 source_loopstart: 10142 source_loopend: 16490 start: 10109 end: 16523 loopstart: 10142 loopend: 16490 samplerate: 32075 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 10 name: bdloop source_start: 16556 source_end: 17057 source_loopstart: 16566 source_loopend: 16960 start: 16556 end: 17057 loopstart: 16566 loopend: 16960 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 11 name: THROB DRUM source_start: 17090 source_end: 20594 source_loopstart: 19465 source_loopend: 20516 start: 17090 end: 20594 loopstart: 19465 loopend: 20516 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 12 name: 909BD11 source_start: 20627 source_end: 20958 source_loopstart: 20628 source_loopend: 20959 start: 20627 end: 20958 loopstart: 20628 loopend: 20959 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 13 name: CLAP21 source_start: 20991 source_end: 25786 source_loopstart: 20992 source_loopend: 25787 start: 20991 end: 25786 loopstart: 20992 loopend: 25787 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 14 name: SQUARELO source_start: 25819 source_end: 26255 source_loopstart: 25820 source_loopend: 26256 start: 25819 end: 26255 loopstart: 25820 loopend: 26256 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 15 name: NOISE6 source_start: 26288 source_end: 31514 source_loopstart: 26289 source_loopend: 31515 start: 26288 end: 31514 loopstart: 26289 loopend: 31515 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 16 name: SQUARE2 source_start: 31547 source_end: 31659 source_loopstart: 31548 source_loopend: 31660 start: 31547 end: 31659 loopstart: 31548 loopend: 31660 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 17 name: CLAP2 source_start: 31692 source_end: 36487 source_loopstart: 31693 source_loopend: 36488 start: 31692 end: 36487 loopstart: 31693 loopend: 36488 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 18 name: NOISE3 source_start: 36520 source_end: 49347 source_loopstart: 36521 source_loopend: 49348 start: 36520 end: 49347 loopstart: 36521 loopend: 49348 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 19 name: NOISE5 source_start: 49380 source_end: 52685 source_loopstart: 49381 source_loopend: 52686 start: 49380 end: 52685 loopstart: 49381 loopend: 52686 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 20 name: SINEHI source_start: 52718 source_end: 52739 source_loopstart: 52719 source_loopend: 52740 start: 52718 end: 52739 loopstart: 52719 loopend: 52740 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 21 name: 1 source_start: 52772 source_end: 53558 source_loopstart: 52773 source_loopend: 53559 start: 52772 end: 53558 loopstart: 52773 loopend: 53559 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 22 name: LASER1 source_start: 53591 source_end: 61087 source_loopstart: 53592 source_loopend: 61088 start: 53591 end: 61087 loopstart: 53592 loopend: 61088 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 23 name: RIM source_start: 61120 source_end: 61734 source_loopstart: 61121 source_loopend: 61735 start: 61120 end: 61734 loopstart: 61121 loopend: 61735 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 24 name: CLICK source_start: 61767 source_end: 62188 source_loopstart: 61768 source_loopend: 62189 start: 61767 end: 62188 loopstart: 61768 loopend: 62189 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 25 name: TRIHI source_start: 62221 source_end: 62269 source_loopstart: 62222 source_loopend: 62270 start: 62221 end: 62269 loopstart: 62222 loopend: 62270 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 26 name: SINE32 source_start: 62302 source_end: 62702 source_loopstart: 62303 source_loopend: 62703 start: 62302 end: 62702 loopstart: 62303 loopend: 62703 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 27 name: WHITE4 source_start: 62735 source_end: 67144 source_loopstart: 62736 source_loopend: 67145 start: 62735 end: 67144 loopstart: 62736 loopend: 67145 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 28 name: TRIANGLE source_start: 67177 source_end: 67254 source_loopstart: 67178 source_loopend: 67255 start: 67177 end: 67254 loopstart: 67178 loopend: 67255 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 29 name: 808BD source_start: 67287 source_end: 67965 source_loopstart: 67288 source_loopend: 67966 start: 67287 end: 67965 loopstart: 67288 loopend: 67966 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 30 name: WHITE2 source_start: 67998 source_end: 72407 source_loopstart: 67999 source_loopend: 72408 start: 67998 end: 72407 loopstart: 67999 loopend: 72408 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 31 name: WHITE3 source_start: 72440 source_end: 76849 source_loopstart: 72441 source_loopend: 76850 start: 72440 end: 76849 loopstart: 72441 loopend: 76850 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 32 name: BD1 source_start: 76882 source_end: 77561 source_loopstart: 76883 source_loopend: 77562 start: 76882 end: 77561 loopstart: 76883 loopend: 77562 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 33 name: TR909BD source_start: 77594 source_end: 81547 source_loopstart: 77595 source_loopend: 81548 start: 77594 end: 81547 loopstart: 77595 loopend: 81548 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 34 name: wav1_1 source_start: 81580 source_end: 82310 source_loopstart: 81613 source_loopend: 82195 start: 81580 end: 82310 loopstart: 81613 loopend: 82195 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 35 name: wav1_3 source_start: 82343 source_end: 82634 source_loopstart: 82359 source_loopend: 82505 start: 82343 end: 82634 loopstart: 82359 loopend: 82505 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 36 name: wav1_2 source_start: 82667 source_end: 83029 source_loopstart: 82699 source_loopend: 82989 start: 82667 end: 83029 loopstart: 82699 loopend: 82989 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 37 name: wav1_4 source_start: 83062 source_end: 83242 source_loopstart: 83072 source_loopend: 83218 start: 83062 end: 83242 loopstart: 83072 loopend: 83218 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 38 name: SAW4 source_start: 83275 source_end: 83344 source_loopstart: 83301 source_loopend: 83343 start: 83275 end: 83344 loopstart: 83301 loopend: 83343 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 39 name: SAW3 source_start: 83377 source_end: 83475 source_loopstart: 83416 source_loopend: 83472 start: 83377 end: 83475 loopstart: 83416 loopend: 83472 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 40 name: SAW2 source_start: 83508 source_end: 83635 source_loopstart: 83547 source_loopend: 83631 start: 83508 end: 83635 loopstart: 83547 loopend: 83631 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 41 name: SAW1 source_start: 83668 source_end: 83937 source_loopstart: 83759 source_loopend: 83927 start: 83668 end: 83937 loopstart: 83759 loopend: 83927 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 42 name: W12_4 source_start: 83970 source_end: 84026 source_loopstart: 83984 source_loopend: 84027 start: 83970 end: 84026 loopstart: 83984 loopend: 84027 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 43 name: W13_3 source_start: 84059 source_end: 84127 source_loopstart: 84060 source_loopend: 84116 start: 84059 end: 84127 loopstart: 84060 loopend: 84116 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 44 name: W12_2 source_start: 84160 source_end: 84268 source_loopstart: 84163 source_loopend: 84247 start: 84160 end: 84268 loopstart: 84163 loopend: 84247 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 45 name: W12_1 source_start: 84301 source_end: 84496 source_loopstart: 84325 source_loopend: 84494 start: 84301 end: 84496 loopstart: 84325 loopend: 84494 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 46 name: W10000 source_start: 84529 source_end: 84688 source_loopstart: 84539 source_loopend: 84667 start: 84529 end: 84688 loopstart: 84539 loopend: 84667 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 47 name: W7_4 source_start: 84721 source_end: 84832 source_loopstart: 84742 source_loopend: 84826 start: 84721 end: 84832 loopstart: 84742 loopend: 84826 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 48 name: W7_3 source_start: 84865 source_end: 85091 source_loopstart: 84903 source_loopend: 85072 start: 84865 end: 85091 loopstart: 84903 loopend: 85072 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 49 name: W7_2 source_start: 85124 source_end: 85548 source_loopstart: 85202 source_loopend: 85539 start: 85124 end: 85548 loopstart: 85202 loopend: 85539 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 50 name: W7_1 source_start: 85581 source_end: 86408 source_loopstart: 85731 source_loopend: 86405 start: 85581 end: 86408 loopstart: 85731 loopend: 86405 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 51 name: W6_4 source_start: 86441 source_end: 86571 source_loopstart: 86476 source_loopend: 86558 start: 86441 end: 86571 loopstart: 86476 loopend: 86558 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 52 name: W6_3 source_start: 86604 source_end: 86830 source_loopstart: 86633 source_loopend: 86808 start: 86604 end: 86830 loopstart: 86633 loopend: 86808 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 53 name: W6_2 source_start: 86863 source_end: 87342 source_loopstart: 86968 source_loopend: 87324 start: 86863 end: 87342 loopstart: 86968 loopend: 87324 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 54 name: W6_1 source_start: 87375 source_end: 88368 source_loopstart: 87378 source_loopend: 88063 start: 87375 end: 88368 loopstart: 87378 loopend: 88063 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 55 name: MOD2_C3 source_start: 88401 source_end: 88518 source_loopstart: 88431 source_loopend: 88515 start: 88401 end: 88518 loopstart: 88431 loopend: 88515 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 56 name: FM90000 source_start: 88551 source_end: 89007 source_loopstart: 88697 source_loopend: 88994 start: 88551 end: 89007 loopstart: 88697 loopend: 88994 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 57 name: 191 source_start: 89040 source_end: 89230 source_loopstart: 89114 source_loopend: 89216 start: 89040 end: 89230 loopstart: 89114 loopend: 89216 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 58 name: WAV5 source_start: 89263 source_end: 89382 source_loopstart: 89271 source_loopend: 89332 start: 89263 end: 89382 loopstart: 89271 loopend: 89332 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 59 name: SQ4 source_start: 89415 source_end: 89477 source_loopstart: 89434 source_loopend: 89475 start: 89415 end: 89477 loopstart: 89434 loopend: 89475 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 60 name: SQ3 source_start: 89510 source_end: 89591 source_loopstart: 89532 source_loopend: 89588 start: 89510 end: 89591 loopstart: 89532 loopend: 89588 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 61 name: SQ2 source_start: 89624 source_end: 89745 source_loopstart: 89659 source_loopend: 89742 start: 89624 end: 89745 loopstart: 89659 loopend: 89742 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 62 name: SQ1 source_start: 89778 source_end: 90014 source_loopstart: 89843 source_loopend: 90010 start: 89778 end: 90014 loopstart: 89843 loopend: 90010 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 63 name: HR4 source_start: 90047 source_end: 90104 source_loopstart: 90061 source_loopend: 90102 start: 90047 end: 90104 loopstart: 90061 loopend: 90102 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 64 name: HR3 source_start: 90137 source_end: 90213 source_loopstart: 90156 source_loopend: 90210 start: 90137 end: 90213 loopstart: 90156 loopend: 90210 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 65 name: HR2 source_start: 90246 source_end: 90349 source_loopstart: 90262 source_loopend: 90342 start: 90246 end: 90349 loopstart: 90262 loopend: 90342 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 66 name: HR1 source_start: 90382 source_end: 90582 source_loopstart: 90411 source_loopend: 90577 start: 90382 end: 90582 loopstart: 90411 loopend: 90577 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 67 name: HOLLOW source_start: 90615 source_end: 90971 source_loopstart: 90745 source_loopend: 90951 start: 90615 end: 90971 loopstart: 90745 loopend: 90951 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 68 name: BELL4 source_start: 91004 source_end: 91404 source_loopstart: 91034 source_loopend: 91401 start: 91004 end: 91404 loopstart: 91034 loopend: 91401 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 69 name: GRUNGE source_start: 91437 source_end: 91956 source_loopstart: 91704 source_loopend: 91954 start: 91437 end: 91956 loopstart: 91704 loopend: 91954 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 70 name: BELL10000 source_start: 91989 source_end: 92449 source_loopstart: 92339 source_loopend: 92400 start: 91989 end: 92449 loopstart: 92339 loopend: 92400 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 71 name: HISS source_start: 92482 source_end: 102344 source_loopstart: 92482 source_loopend: 102345 start: 92482 end: 102344 loopstart: 92482 loopend: 102345 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 72 name: AAH source_start: 102377 source_end: 102471 source_loopstart: 102397 source_loopend: 102465 start: 102377 end: 102471 loopstart: 102397 loopend: 102465 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 73 name: W8_4 source_start: 102504 source_end: 102567 source_loopstart: 102519 source_loopend: 102561 start: 102504 end: 102567 loopstart: 102519 loopend: 102561 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 74 name: W8_3 source_start: 102600 source_end: 102685 source_loopstart: 102622 source_loopend: 102678 start: 102600 end: 102685 loopstart: 102622 loopend: 102678 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 75 name: W8_2 source_start: 102718 source_end: 102837 source_loopstart: 102738 source_loopend: 102823 start: 102718 end: 102837 loopstart: 102738 loopend: 102823 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 76 name: W8_1 source_start: 102870 source_end: 103098 source_loopstart: 102902 source_loopend: 103070 start: 102870 end: 103098 loopstart: 102902 loopend: 103070 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 77 name: dm4_a4 source_start: 103131 source_end: 103270 source_loopstart: 103145 source_loopend: 103245 start: 103131 end: 103270 loopstart: 103145 loopend: 103245 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 78 name: dm4_a3 source_start: 103303 source_end: 103480 source_loopstart: 103321 source_loopend: 103422 start: 103303 end: 103480 loopstart: 103321 loopend: 103422 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 79 name: dm4_a2 source_start: 103513 source_end: 103815 source_loopstart: 103513 source_loopend: 103713 start: 103513 end: 103815 loopstart: 103513 loopend: 103713 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 80 name: dm4_a1 source_start: 103848 source_end: 104365 source_loopstart: 103848 source_loopend: 104249 start: 103848 end: 104365 loopstart: 103848 loopend: 104249 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 81 name: dm3_a4 source_start: 104398 source_end: 104474 source_loopstart: 104398 source_loopend: 104448 start: 104398 end: 104474 loopstart: 104398 loopend: 104448 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 82 name: dm3_a3 source_start: 104507 source_end: 104660 source_loopstart: 104507 source_loopend: 104608 start: 104507 end: 104660 loopstart: 104507 loopend: 104608 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 83 name: dm3_a2 source_start: 104693 source_end: 104944 source_loopstart: 104693 source_loopend: 104894 start: 104693 end: 104944 loopstart: 104693 loopend: 104894 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 84 name: dm3_a1 source_start: 104977 source_end: 105453 source_loopstart: 105004 source_loopend: 105405 start: 104977 end: 105453 loopstart: 105004 loopend: 105405 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 85 name: dm2_a4 source_start: 105486 source_end: 105605 source_loopstart: 105489 source_loopend: 105589 start: 105486 end: 105605 loopstart: 105489 loopend: 105589 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 86 name: dm2_a3 source_start: 105638 source_end: 105758 source_loopstart: 105645 source_loopend: 105745 start: 105638 end: 105758 loopstart: 105645 loopend: 105745 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 87 name: dm2_a2 source_start: 105791 source_end: 106028 source_loopstart: 105803 source_loopend: 106004 start: 105791 end: 106028 loopstart: 105803 loopend: 106004 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 88 name: dm2_a1 source_start: 106061 source_end: 106738 source_loopstart: 106084 source_loopend: 106486 start: 106061 end: 106738 loopstart: 106084 loopend: 106486 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 89 name: dm1_a4 source_start: 106771 source_end: 106895 source_loopstart: 106784 source_loopend: 106884 start: 106771 end: 106895 loopstart: 106784 loopend: 106884 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 90 name: dm1_a3 source_start: 106928 source_end: 107052 source_loopstart: 106930 source_loopend: 107028 start: 106928 end: 107052 loopstart: 106930 loopend: 107028 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 91 name: dm1_a2 source_start: 107085 source_end: 107322 source_loopstart: 107093 source_loopend: 107288 start: 107085 end: 107322 loopstart: 107093 loopend: 107288 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 92 name: dm1_a1 source_start: 107355 source_end: 107826 source_loopstart: 107360 source_loopend: 107762 start: 107355 end: 107826 loopstart: 107360 loopend: 107762 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 93 name: Noise3 source_start: 107859 source_end: 112268 source_loopstart: 107867 source_loopend: 112261 start: 107859 end: 112268 loopstart: 107867 loopend: 112261 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 94 name: Tri_a4 source_start: 112301 source_end: 112427 source_loopstart: 112365 source_loopend: 112415 start: 112301 end: 112427 loopstart: 112365 loopend: 112415 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 95 name: Tri_a3 source_start: 112460 source_end: 112661 source_loopstart: 112536 source_loopend: 112636 start: 112460 end: 112661 loopstart: 112536 loopend: 112636 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 96 name: Tri_a2 source_start: 112694 source_end: 112995 source_loopstart: 112744 source_loopend: 112945 start: 112694 end: 112995 loopstart: 112744 loopend: 112945 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 97 name: Tri_a1 source_start: 113028 source_end: 113629 source_loopstart: 113129 source_loopend: 113530 start: 113028 end: 113629 loopstart: 113129 loopend: 113530 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 98 name: Sq_a4 source_start: 113662 source_end: 113788 source_loopstart: 113736 source_loopend: 113786 start: 113662 end: 113788 loopstart: 113736 loopend: 113786 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 99 name: Sq_a3 source_start: 113821 source_end: 114022 source_loopstart: 113872 source_loopend: 113972 start: 113821 end: 114022 loopstart: 113872 loopend: 113972 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 100 name: Sq_a2 source_start: 114055 source_end: 114356 source_loopstart: 114152 source_loopend: 114353 start: 114055 end: 114356 loopstart: 114152 loopend: 114353 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 101 name: Sq_a1 source_start: 114389 source_end: 114990 source_loopstart: 114586 source_loopend: 114986 start: 114389 end: 114990 loopstart: 114586 loopend: 114986 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 102 name: Sine_a4 source_start: 115023 source_end: 115149 source_loopstart: 115087 source_loopend: 115137 start: 115023 end: 115149 loopstart: 115087 loopend: 115137 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 103 name: Sine_a3 source_start: 115182 source_end: 115383 source_loopstart: 115259 source_loopend: 115359 start: 115182 end: 115383 loopstart: 115259 loopend: 115359 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 104 name: Sine_a2 source_start: 115416 source_end: 115717 source_loopstart: 115469 source_loopend: 115669 start: 115416 end: 115717 loopstart: 115469 loopend: 115669 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 105 name: Sine_a1 source_start: 115750 source_end: 116351 source_loopstart: 115860 source_loopend: 116262 start: 115750 end: 116351 loopstart: 115860 loopend: 116262 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 106 name: Saw_a4 source_start: 116384 source_end: 116510 source_loopstart: 116435 source_loopend: 116485 start: 116384 end: 116510 loopstart: 116435 loopend: 116485 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 107 name: Saw_a3 source_start: 116543 source_end: 116744 source_loopstart: 116578 source_loopend: 116678 start: 116543 end: 116744 loopstart: 116578 loopend: 116678 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 108 name: Saw_a2 source_start: 116777 source_end: 117078 source_loopstart: 116785 source_loopend: 116985 start: 116777 end: 117078 loopstart: 116785 loopend: 116985 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 109 name: Saw_a1 source_start: 117111 source_end: 117712 source_loopstart: 117120 source_loopend: 117521 start: 117111 end: 117712 loopstart: 117120 loopend: 117521 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 110 name: add3_a4 source_start: 117745 source_end: 117917 source_loopstart: 117847 source_loopend: 117897 start: 117745 end: 117917 loopstart: 117847 loopend: 117897 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 111 name: add3_a3 source_start: 117950 source_end: 118123 source_loopstart: 118003 source_loopend: 118103 start: 117950 end: 118123 loopstart: 118003 loopend: 118103 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 112 name: add3_a2 source_start: 118156 source_end: 118439 source_loopstart: 118195 source_loopend: 118395 start: 118156 end: 118439 loopstart: 118195 loopend: 118395 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 113 name: add3_a1 source_start: 118472 source_end: 118982 source_loopstart: 118569 source_loopend: 118970 start: 118472 end: 118982 loopstart: 118569 loopend: 118970 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 114 name: Noise4 source_start: 119015 source_end: 123424 source_loopstart: 119015 source_loopend: 123230 start: 119015 end: 123424 loopstart: 119015 loopend: 123230 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 115 name: Fm2_a4 source_start: 123457 source_end: 123583 source_loopstart: 123478 source_loopend: 123578 start: 123457 end: 123583 loopstart: 123478 loopend: 123578 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 116 name: Fm2_a3 source_start: 123616 source_end: 123767 source_loopstart: 123624 source_loopend: 123724 start: 123616 end: 123767 loopstart: 123624 loopend: 123724 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 117 name: Fm2_a2 source_start: 123800 source_end: 124050 source_loopstart: 123816 source_loopend: 124016 start: 123800 end: 124050 loopstart: 123816 loopend: 124016 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 118 name: Fm2_a1 source_start: 124083 source_end: 124584 source_loopstart: 124160 source_loopend: 124561 start: 124083 end: 124584 loopstart: 124160 loopend: 124561 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 119 name: Fm3_a4 source_start: 124617 source_end: 124796 source_loopstart: 124727 source_loopend: 124777 start: 124617 end: 124796 loopstart: 124727 loopend: 124777 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 120 name: Fm3_a3 source_start: 124829 source_end: 125024 source_loopstart: 124910 source_loopend: 125010 start: 124829 end: 125024 loopstart: 124910 loopend: 125010 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 121 name: Fm3_a2 source_start: 125057 source_end: 125440 source_loopstart: 125219 source_loopend: 125419 start: 125057 end: 125440 loopstart: 125219 loopend: 125419 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 - sample: 122 name: Fm3_a1 source_start: 125473 source_end: 126234 source_loopstart: 125797 source_loopend: 126198 start: 125473 end: 126234 loopstart: 125797 loopend: 126198 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 1 fluidsynth-2.2.5/sf2/VintageDreamsWaves-v2.sf3000066400000000000000000017662041417326347500211400ustar00rootroot00000000000000RIFF|sfbkLISTINFOifilINAMVintage Dreams Waves v 2.0isngEMU8000IPRDSBAWE32IENG Ian WilsonISFTSFEDT v1.00:SFEDT v1.10ICRDOct 24, 1996ICMTCreated by Ian Wilson Last Update - January 1998 eMail : aztec1@bellatlantic.net iwilson@butlerintl.com Downloaded from HammerSound http://www.pvv.org/~thammer/HammerSound/ICOP(Copyright (c) Ian Wilson - October 1996LISTsdtasmplOggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS;7'(/.1Tit컼)h3|X 7{j.̚}1xUG$g# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggSl;7\B :=DY*@bV=5ت a[ ([2T_V_fC^ DS}Ud%$`ҁ9@'!:i4AQT&`& IOggS;7vorbis}@OggS;7<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggSp;7>`7<_7P 82#sʴ鈕~-/[Ka y.?[70v?1 t!;O{XuKD$)k:2MCOggS;7vorbis}@OggS;7<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggS;7~~421S=ƗϽDߝ1yV&&+]_X0MZ_l}q| W>oJtoӽiN<dC< $F?s}L7s-ͤpm!~)5OggS;7vorbis}@OggS;7<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggS;7ɖ385|WM@*~P췿g_{=HeaR̃]) Y{%%tWkT%E6Jt]ގϮܟ9/A&ݻdUL=&6*n/PǪwZf~/ |wA" OggS;7vorbis}@OggS;7<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggSt;7*ym98R8*'\-&oƹA[E竻M p X?|ɆۿC Kȯ_>גxd thr߭N/_0OggS;7vorbis}@OggS;7<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggS;7(:69U @x0d3(3hrR# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS:;7Χ.,0/l>.!WyWʋ:_VI!OC^O8}m^&qLһ#ԡRal-%R=x،~a$8 I a}p&Glu/2 Jv'5ifT2FwxZh 1][k*+JT{yE,R$'Fkb'wcGJBO 2^YZ]ǖ 509}{%Py51$ʑl|ff]RFZeݔ,L_z.z@Rdf1 h1ݗO؜| fy>7QzQD'_휛9H9`-*⇀?M6 Ja]cr8CO2im0=^)0m ]KJ{Xw=C:Z_Տ;DݱqYY[x%_ T,n1-'"#` l̺;( 俰9 cηv1a{g.2Cw^u=x٥#jw:LZmr8{K=<ߊjW'K!@OYb\|m o>7m$[R[UJK|e v~f ׆z^poGBPu-S+lcr K`@bL9yͣ>ż}w:n!OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS:;7_ +++/Ƕ^?S~6`ݛ;$7I י`ޞnbAe?k]6 3l7:| #ffFeRq Eb lQiW8'48>pǞ%՞"VG=ث6-ĤUmMU2lwڔ1xڍ'A{V-ȼ_tflfD;0mYbk&joFuZ /2Q2l5sl"oG%CA NoU&hlA%B\^+ZCOiURc, 3g"+e^EHlDQ%h>r8yS5 ;frM|`InSd||dv_-L2Ej݋RYC>{noe#3xQB},0Gzp;JT>s'[{Eާ_a^3rqRTU0ź>}lC"&9݆&=^)0 XRb+,Įt?) r yHRQ7RyݷMushp|lFY&(PW3ѣ#8/CY(۾c1TzM>q 3U/ΥTzDy͸ ̻zƃn1Q7W厔e{>M 9jxcŦzbkv7>wƑnA`Gr`loIa2&5ex 3Mŗ$øns|BdY/1?3c$oc%~.$ WٗtY/V@xOggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS{;7ˇB"m?L8Ho @$kE]lRA g:'6MF,sOggS;7x[vorbisK}@OggS;7<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggS;7l 83*,*+*,+774IMѝ9/tui]b-l{,g.~.b=rLyvI!I1&JBMGlnnÁsSQ>-S\3Eݺ.測X0- 8u}P|;|;?͍:`dz= $Q%&kϾ$r󎤵#]9!jy{NLzhVr7 Ώ H{{k&bbt{ f50,RE?Ljn&Fnwx ѫA/ꀊD\Ukߗr۸Fi.R|RM },lʞO/SvaHb#BXĆxS8349p;bPE$sdhTݶyc;|mK~Yoh?j<FϚB(5X`,#CGbTR435y7m (9)'o-h%^zc7%ut4 AS4u:Տ"pF'YrqTboPbicv@W6T@z6ͮ #uI!jw-{Q=eta>ƓxPIx- p+V̶C[lS3K8M}1|~t ˛Gk{=ݫ+~YNRjOyc?7lem[~щ kx܋CQzRXpq5,R?"'^W>Z}mh!kÓߺd,,٬B@j @{ gL =P?lx-yECѾPf4B8҆lЋcبە h4ubyE,H CcZgf6#33=I,Q/9˼L}?~O_<^1ţu`dx]EzBjF!`(uWv꼀yiq\G(9;szcAXKZ %I?H{z' AW5#/;5eYB=9 L,iTڇwK_ٌ|uSy:ߗ4;(B!]tb++ji-ۂ G'ϔ+??Kugo dPKA|1ږ԰וi1_q!Sm$q™IMИ.#26rۇ@O=B!3NJUʀOV|xjoNf-`D/?6_zL[tM>loũppUE'g܊yM{T3}=Ga>v׮/Alhn/}AcBs|G* ^YCNrS21m4yZ&W#6LVomVNg9u 0gT֭SFuBՖ-ֻֆPPI0Ӗ+gN>AH:kTnnح,P݄qoMjqR^2czFШa)Lؽ 8G.0</˧e]=2 ^DN OggS;7vorbis}@OggS;7<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggS;7-FY6^;RC`{U^{O>ݧ[ZkfU^u~z]כoFa8 7]Ὸ3 Lܒ4&1T:w}0ޟ)79y3= 3{Xi7}lر{ѡ1Ѻ$.qw9+ F%OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS ;7d@q,/+\a@#$~i(>kx;px{ӖlFf{瑑Azao~ߺ:j5|{WWlk'N.P.VL|֯p՛]5aQ_ {V+< v{R1ddhHF;[ѽM87۷~{?GB bFrhEdzΟ}XdoV[~j\ kYN&wm\\eڵ{^Zu@LuelTth8B}tFeA|W@ƿ7nv;O&wphZ)pCFn?:8cr&x&g?hD4q=GG!r!xd\BRi|ml$YE$B P~ u0l`Poл#<.&~&)ͮxm)JeW5@\@[z@J wT5!~%1b4X͔ƃecVvtYӔH3Z/=>ɼ#s$KHyM39>$CG#T+~n'i0 N}jyu48djo{w-jOZHS]MΖ/ VlBk=@Fx P!WV,//O؊| ˼;,=Ρ pڲ&'OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSL;7e,')+g,v} Ffz.Jϟ?vnvn馛.M4MoJc;%ݟ&QCG|KHG@:L3'*O}?Tzzt ~>;@ 3:qUmlVW/DOggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS;7-0//,-.3%$$$.,,4kUкQ52A \="YYޞmTʶ&lDkU=2384&:пܭt1Z^a/* a:X60ghzVkb'ty[+jDmH9 726G ZywWl,\ZJ=/d|7z6=4m^)@$ l~"T^38B*wmd dRR$# RNfIZDs~O$HN0_J$kRYW}oڵGzQ|xKehҷm~ׯDӏr1j6df1*F&K|??Kx_xSĈdh+ձx|X ˪zN? Joj|.+ѹOߣtm=WLy8E/T(?la–ɳh7Ob\ݬoj܊f If#YdQhø9" bRƟ#H:_-/[*v3`Xrh][c?nGJȽv# Xq}sAWV;γX-|fVoKĨZV(߇OJAh jq!06R*L t튆& $[g=J`%5Ҭdiz^LRZSyql`U ym}舴u_{V҆9Ȗ@2fM9@iy˶4ڏ%OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS;7}4,+))ir&a> Rs URFm{o# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSk;77.,&&$$)(,/d͆T )X=&/[?%ZC*<`j]1$X9gטsH™#Ѣ=sZg8xu}}E=h;@i$#.hgK%b#7%+:ѳ trlu|'(= þҫ㭠=ZgO_|RW(~ؕ6FȌEI &$_*Ze}D[ :,ba} g^VJ^%=7:m8_x4y~-xC!DBEwȮ&{/ZwMwB ҈J[ o-_ږ$E}(ܗp"q%>g(C̰ccj]2czN .Yܶjȉ\w5%4E"^swyKJ+RLqM~s98x^aAcngB䛡X@lS׭3srVNn9C^FST/AA+oWEއ?ʡTD"֋iIvMn 6tH[@"|piݕ+hRГʓWWƆe=y +sC/gpn1J2_7.=Y*႘7׬E=,H_̲f g2f̈́C:ւ91P8,JrtQT/춊Ocޔx<<_$o  d#[bD|k*g I; hXPfDPDOKVM{r =*Rތ jXIQD?AL Z]͐"rd7PjRƂz=q]QjXbTC5vEɗ%j  ݉'q~F#2SF'j~ izt8nHop[;0nK~ l3Β}HWH[tr'tjXz8 FI4|Əymo=Ovҍ԰nV7hY50q7<: wlnt-8Guf7 jOggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSq;7s{)*/aHl{\1I=k/6Lapwkm| pVlcobyyu# :[_gV OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS;7--21++.2#!$#./,diLݲ{ҿOZrVjlZ??=.lm 7&7LퟡɭYiDWe}$>l>yz9ۉSDho5DkEp֠ ^k׊_Yk=.E&ۚ?Dm)7nӃ@)`XSnߛ}L-^v$cnX* y2#$2i2e[D}>ߵOMXT">+K&wJ=^aś߭Wω<4aTn^xZ4އkH m@emKྙ 2(8ZD8R3[|kLVؕfge; P6FX.rvτY%Ab! +nĺGm6FK+*Ked4*:t M7Sk1ݎo^odE=K4vzPFs\ZZ{ϴϓ#-Q_PݬA}8V7UǢt4 heNboKݲhVP^ o6;[8rؒ9GF՗y( ɼ"E c\`An'Wq[EOÙ3VX_ ~<$|{0j$?*ZO]TlT[c=uCĨX6^AyDMmل|,TYjydNY6}t* M~V\ X'/󹖝گ7}IHUD6`,A"AzI$"KHLeҨimf@ 0 4rt8w/Q}KI1{OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS2;7}1/,¼ν%*,0T[k,` }㎆bnƿ~xG;)lJ7;Rts$Tc)Dwk{K@J2svֽjņs,7uÂQL%^L;q*D1qFbP\Zs]zާjw|DEvzf6TM:dk:uI+e Ktz}VRXĐ*d&}dwGID^^q["M!7T#8Yt]ld)F"ֶӔK-;K0x7z)T8'Rن++eU?9}吳Mw{;~.w^+ 2eBe%K6O5+ =[0ݜm3L%I?c4!zփFד>yd.L%RtM_*Gvz޴-B&JYYh/GlF(5ܦ~iC}tk^lSTWVV>|Vno7o󣂱YO!c3^{@ Ba3~H[wc).$ y_sig7bfJCfZ s|7Hznj駍K( "S!}Qh)rɗz?"Qqbep}٬7|5Ҩ#6iMfϛ]TWٍdgB4N_:s.Qc_1j?gz;$Z H `Q;D %@юiǟsJޒ eb#z~~ QҩJ`>WA*B2Nt#Sv+6+^F( dC_Wcy1׺=L>T(cHSQHT8S8,bN,mJgs$Y&(XcjGJϖAd^!'e& i0'n, Dsb:?ޫށ Z6ׅ]e6^ 0PXw>b#1E%1(DZH^)N8ul*ۭ ~ p"h$l- .BbI VAsE[Џ)|AvQ8u[%Ϯ>iR萢}CMT=p> pwgEL>) A{ޭ"yN A/jBcʱҝh*Qygb}{tL:-ҧG@r6.cAEb#pާ։AFF'8.0Ԁa(#j_Vm|k&?wIOzi>ET?͎iOPQ>LPPRt$b67i(W };N'xydꐾ%zLmk)5 PQ%oZJ"e,r)HPT TaE4zxlDC;lx+xda\DJIKTvG Vk6Rӣ ~vLxT])bLo zV[ݻ(z":@RBB䈆#ƞ8ϕꗿH }/95OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS ;7@ .-(+(((*%,/fGn2R #[IoAݎnY?#OjRYdVn,~9)h\;|i&\L9NȺ=3%P#;V,9ε'cyuuɘlgH+'HG 9ӗ').G2Ri'.k 9s' 7˰ӊ3YR~U0m'FE?w=ftRٗ갺)*V*K֫ɳ1yNr~h<dwsR޾moŠHP<~_OM'wtm[}D3]y={I>{d$oVՀ 7},=jX\@!;9=Rou)'ojXPbWm,`9.!B{#Wݔ\kSjXJU#i3}YJr.n#C]{jXCÞdJ-ߙduܹ5H~KUƧjGMeR+M7`gIex͛S.j8h5xxZ_8٠t˩|lXfC˾@Vyؗ$GZKA{:rD瘉RX&WM}s_rB& hR}щMT6 _p<.wt *sl%^OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS;7., $Sstd 5Hv;;ώ=M窴W}ϳI? Y=Tx.s&ʿr# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS;74Y0-/.&$aH8޺A >S뚜Op#q{][NwkL-(H]cE?^ym2[ m5}:{,g:/Jt|)+\yy=T_hmZ}?鬷[?2,N{ږyzf_pғo ?`6^KX[x8|i?ϯذ=yn~FW OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSI;7  42&ݥOFQo/,\zjk_׽S`\O||Qf #a1Fn.J?Uxm~- )Q-ul,-{KdiZW:t~#'(tcK~Zj"MpyTgޓy1TFI H$_}O\}򪍒틢U] !g͂`6OKLZy>KOC 7t <==p2p>Dz,X(L¶Y\Ы@[ԓa&B%05J_J* &k1u*.U^hi ߶-"| NT2>5/Q6LoWȘj )͌LI?o V['u lj0A~s5$8$+8@Y8Ic ۷m ;6K*)g &o+ؿevY (R,#z@$J eC՛.\W_p^ltmF00Ԟ@[p=`T?/l>|b #`,`q PFw@8}^0E h}&V1%Xo5^@-D f;K*0#L P_'+)@ i8)s|k#p@9`R?JiW ``@;T p z0?#@>G@\yQhirM  .x{`O/Ms> | >pMXg >m<<>>*^]]Y_]EgXhQ5 - OֲW Ż O.TY͜[2,OggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSg;7jp -'#"'41F}̴ PVNù|7\鱻*pW_u`^W?9(ԸcFf$󧄣Q¶&hs [5jMxsMdj?waG 3Έ5;p{¬ok`@-]Ocˁ6sM<|߽ X p,wcO_N4׌uej={ h\ROggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS;7 f31/-)n7'uۿ]s \y&߉Q{|M^ >> # G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS1;7ZQ \124a"9-1XQCsI6H *QvFeY w3eg0'Ic> Bc,xj{ a;KMOggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS;7~0%*(az{A:~9G<]]ݵϟ?[LL7] to7=0+4E)9qSc"io6=:Lt-Ul#B?#[W$rMSrVNSY$K,#`m/nV>S=d>eOggS;7ebvorbisD8OggS;73<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS:;7u=-0,0W0}) `\]^}e~unvL!-tfuMR7D8Zz7]nPᮬv+8O7?*^5j(4`6ŧ>$~ydBYT&@Y}֡rp K3]U>~v;P<|\ƫ 5K_3G&8N7PCtz׵aLrbO|Gפ?»$1j24f)@[%Jpxg0K.AZ\BY|^xNQ#ex8ݞۊT&_du6^Y+h~xYPݐcV LFRܜ%׏#wh ]is])/@*}a4lŊH׸]L NcEΉF[zWlϑ :ŮSŠنf6,9;#Nܡ4j9.^q`e&=Xb$UWLF^8TEF6Qn1EX/Fe4%?W˄}ń>d<< !4_ L]Xf;[gggӚQ: ~ =gb0aqȲ3{|} ͝6cC>7v!ݖ0x<D_kl^HfZBb82Xx 8yfXǯ^M-KBnzLeA^ݑ=#Ou)_;ugb;SE֬5+xin핌O|yu,nA%k[fuGۯ ZKuɮڵrv\#99Ӫ(.IVn0^2Z<7}i7wRR(g\<=L4ٮMק!; |ZYwY mEXR} 3H\P`2ٝ3(p|fOhT$ϴ OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSN&->**\gO*\ߘe\yFA3?y`^_nL/dx!1Û JNd4Z9̷ 8OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-%1, (,gmPRj,۹㻦h$_e|7L7]ۄE+/ޏm%[5fܭtcx+8 ŮA:%нfl&yOөt],(X>\h"^;AEpA0c36E;|K>#pXU*SQ's.o&Ȕ{?2=Ds_AQyLmc~Z:\NozkppivOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS:&->1/10h,sd D}hǧo1^mLd|n\x=5jRʌ!Auv_=,Vy懗{^u[Z·5 =>twI̴]AH/v<ѷ\Np2(Tx1XJEsC6bK*n(4]3lyTX}*EKr&viIZ{JϠ\ՠi $ͰH]vC1*w 0zgAڀ!4H=K4NqLmLÄ z픐/=#?KxFhi9>-g՟P9檣(WHeDGA7;9η ÷~wCƍ_\L:GGySnnnmxw.ƾ-8r%E'w;[^LOGݺm(0%$`k7sd$2K&x|vSU]worp{)卐Q4zx{!uahx^껛qY/עS)nXR$gsqg~Wcq  Ҳo3zO70Z@mOvEko8)DRHz򝹥56zoPPLdϷ@@f1JUO8j7qd: o=iSg Du*vmbH]# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS:&-]j*+ľ-/ԝH$jYjF_in?mǛ2T>,V%X2?\uNK~o7TFy𵺹>6:~m76b3d7\>}:CSHkݦ4~\6̌.MfFPW73}#Y=}`ٺ ":<$$V-0I}[t.Qr}z^"LRjP; vt@mKn:{(yB*t];fB>yꋶTa&}^NjT35[[#hwRU˙^Ⱦ]7; ,cd J=.eC{DS Q!Vץ)S:f턩35!9߱z]?/wXDFSx5Xd:b%c y|)nfE0VFE+6ƙ,],n[mij;RDIB|fulhRvإM6]7ɰAacIgY}SAae3Vg֭eM>.Y! 7xm}h'uc. OJ}VфfP:{Ij u' n-$5lK/< z[k1TqH1A .&+/(R]+.jY[`}o=nYԷNo&qF oro\* # G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-?--ePgw9zeԲG6a|趯T}W]ݵL&E.V/J٘qŧ6jJփRތj1cD cLo>>-kkٍ?mݭWN;uo't%wWw̿=>؁ yW*|Z4aGzX_a@C4Mtaa0 ×뺮4naH&:Er!+{]OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSr&-BmL""Ep qw @˹O:\$l_*S\  L]Jn'HZrM+*.X6LJ3//C<(IM4p*m8t:%_朝L41ɏ# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggS&-?.3/7223,|:C^߰Q/Q2egk)]ϺhC%lĀ=*5)eN;rWyMgӽk4>s,vdt)V-LK]7Y)uEgl쀂FH[[,Yy{pYO(!?l@R 0N9*r/8cIgo*" $Qt}׬Sdi/|C)͘?adֳ֯}]i=p # G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggS$&-H 5877|I4M## б48V)B)6&3FAk0Fѻ/c7dlˡ7pXh42WϮXFrNOɭPSM TMi\4R|IֽT=lK@u>2{dG7q`OI 0LDގwm,4!tHvϮe~:#˚ OggS&-vorbis}@OggS&-@{<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggSk&-R1752l\w׊5[UYc+[}i[TZ<: L2B՚9XApت7;8} tw45lt/dZ[4gn'|Zcr(҂*-OggS&-vorbis}@OggS&-@{<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RFGHH!DD4OggS&-)2;=;LM,` )t'ຢ(?/UT[*(BfV$qݍdi|G˔@?]f` $8:@KjtȲXsLXPnI GpI!\?5vl'c0l/wmEcbMS OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSF&-@ ,-,]0jgl;hp޷wZZ>SqH2c v(4ޫ&1 ):imR{O4OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSc&-)+\)]d `_l?5hr.Ώ`L_Md;м='k?]E/Yբr OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-gD.*|eoޡwo5u?>\sC+0(sJ붾Jp'\r1,6p\v#o\BuQ4}K" OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-GN_'*'ǡ~f$,cձ^ 7_I6_·g0 |̋w[u _W?ܷɣWN{xb$a# ]߷ںTSK 6$K= 7#矮OU0 !OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS9&-4 57D7e^`Ap+"eAźZZ邋KW6r qAg? }gDb+6]3Vsc-OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSE&-v}02LCs=w8e)S͋I԰u+E9$ً60ᾚN[RwyUgZ5c-POggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSm&-+m.1]dUһlH 7/q=rZ9>).v t]vQwsW6&!__yz74ٶ +]i0OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-:> +.*la(HUR뾩B/Ћ)W ~Y8|cq::-"вf¯+ Йh"Ҫ< ʼ9t#-][>?g5OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-H..0<]ڝD'eߝV'gWu 33O&r#CeL_hWhOfiKdt俖t٤ É26M Jnz̭&`Lx:=tSOium庅pHOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSp&-ͼK46dVOϯp>T$ tuA؛X.ǡib1*r9H/+7t @7Lݟ'N xyln}#OMN;0P`;ôY @OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&- 1<2\Y"] A 2MQj^L]%H7!p>7|" l_.gYW<,D8h4L-`әSCPPJ@  $@^%l?E+LWhՅpߝ6p\`',N!F䷰X xnOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-Lg..(.-]wa9ǀz UM@Ү \ ;n ߀mu/gRs!n.|ejӓ&# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS<&-L.+,""$++tm M4`ߩ~I҃Jcꝗ?M2|:jժJD4'ck*mdmd=aݏ'SR'5<V3sZd"g}EOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-B433TU(9y8GoQ JXILtI'/2I*1~ )\WQ)71rkiS/H$724# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-H02/dYH,bހV>Z?À?XpnmO2 |[#3xN:rd~V%-:\n@?,\1$|U~ygy3py9dQp:Oam$OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-[;t,1(.,tp֛zm)6Ѭߌ~{MǦ#VLct(X"IT_WN&Kw}26m~vE[ \ʼ <S@wF8w&vsE4v\e{[2/?O\&AAcUf~teگܙ06ѶW'-N[I;tOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-bM' -+#%"#$*(dcjYff^?>E 7dVwr TWز}obJo}0q;yef5A.\W4B) ͅxRko⊳ tSZ=JiMW"\_6+;Ht|LT'9+1񾆤2@vmͩ|gtSG(3yO=/5Do.{tccnF`I弁|SK̄l= ]N]rӺ~zzS+e𦝕)漗MpD㳳}} @rdOjo7[",=OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSv&-)OL#%d\3>V B.+ i0TBtgkE(&t_bE}߱OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-k%@36263# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-D34t<k5;ϳ1< dPcb<< &@1;CƜQy֠IcTZk}w';egGKy)Gwwwwoz Cz|M]ws81OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSx&-pM(-/# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS?&-S-.4)\u2sӴls<\Ot<{ xs c( {=n&L̡q5?k"e]cOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSR&-gp-+TYnyQztڟjG7UWJkJTgOIABkIC5}7 UOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSz&-eY*)$l?wk|F!A[ů_챨mEolH =҆6S?O]k?n\:9bIs3OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-AKS5-%%CxN&F«+o9{ʕ]]]] #q=n ;O߮\|S?ְ׋bZfO^pg̦ W-6sKOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS:&-tNU**l[+vT# `;}a}|n M(O4O_}Ragn*ot'v + 7P~=OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSM&-cV)*t߁zwצFZy++l 0Pk?H[oG*Nɿ]ǕN!Eg;OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSh&-ސ+-t㑍}ZKO+4S+γʔdۃ[}s{kO?o7襼򴵠jOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-A'%畱~@P?SO*n-gEgmԦ:::Rs#^1}u+>JkW: !OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSe&-J#8/24804cuǭMx*/p '2%0K\ɟ~ e(. @`!A)) {6m^^5eax l 0U0,}Y0V@׷raZ$@Y?ԊqPf&4[kثB,4ڨFkė?}qL$YOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-jC37/57|gu%p${R,E^oOmQw?MjpmtC;s&2hԈn -|X}.J>o%` Olͧ!w եXxx&%-He:b~>&D<:'O0H痘,&W‰ ~NIрjxߴC}.:HE 9υw*@DZ*pyl^ OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-q,-,*.-tWnuf)Fٚy[3fēqOYtcA62HXu{2B=uQUK6{uc%|_п -3K?wT&u%C*Lq*(cqt]Kp4N{2g^d׾`uԻ"*c;[- d[jIqNR:zl;5ϥ+/?ԋ4ڒ9+Ʊ*lpӌ1 ۓf>L|CC9 OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-l69165LoȩIY`,mN |`z.Lo"VXk([ ',ۤ0 z4z| pNY[v ' D!=u` zvGODqUM~,}LX[Dʞn1\/IC97ܻaD7kXDEГ tpߣH@ tOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&&-Ch'00()/2-,YCK8w-gn[JeU!gX +/cdVDQBLYE:#(HU,%KO|pC봹ԩ/u!E Z:#)DGKI-@d/mْ 2DPܷ>:-N!%$R;u?VoƆ_o̘Ӕ +K##"aLJ}պy\<UQcRTgO+\!UnYOGgtWJ<(z[JlAN,yظF#,q;zIE G~*0Wɣ W:"}fcq5mul+"(yv 5hogMZ3}ҭS3x]CQmE6W;C!X?koփh|ħ//Jc[̼5Lu6́H؝q$`&{к=_>r:! ?;Q+!`1!u&PLBx:#'g`}.֒մ`n-7z L-@7BPV =+gxl] (D4JwoUP4VSb$zS60+5$g+ u>1?(-p C z00@}=et3ڤ@lPoxp ׯ1mԅt 2l~`6h-KT,:XeH Yc!-$ ":;HoP$uܓK)?Lߌ'v=\zeY0: YFWƢDlHLzT>v0f9Q Kz`9\)Ce Uַ\ C)V4jf >G5gyZ@R99 1>п9dXHu1EPU h #9/Tuz{>ղn|W$)_{⦙](փ!z/]&b;V`q^yYiJ]-WoM!HrMu8d=$"NPЬQC~iVeF Q00ulU629֕bA}}j ЃD=* `;Z0YUkw^ԇO+lWEg|OYpV1°Oɯ 0,xE8-, HɢԔ1yU] p:Cގ.s# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS_&-,,T]~{d(y@L?<<밀L;׏\]?Bɭon# z_F^;qȚm3w-OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS@&-(G)# kp5nzFhIU3rr?Tʕ*0 Ck>ۓ9 ,/p%AyOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSV&-'"髸tοTkPS^^o*b0 0 C&Lcݿ7x# OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSx&-_+$Յ`hfz^U^U޽ʫʫJ^i <딖]oc OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-)N)df kjA5f̚Yzw}i)tퟛ/9;<|Azhmÿ6( 8p۷o߾}۷oO`ywrd}ͬ5Ҿ88o#~T giPu/O7Bj;቗Rt#TeіOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-/_6;:$iU@o{&6,HLz_޾XoKMޏ4^ lRjt^֚>w^O7đb Sq_>RAN(AN?S 1%x56 p.3 Pm+6k# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-!b...l[ u`MtV@x''#|]g* '&bzoakv^"oDOF+91r+sSOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS/&-##!&lMx)6]Cq\/-X~Ӵu (V7{gؤ{te{]}eH=i/ 9%,C_뾛 N!3]fWjewLOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-B;V( xdrbsE袒[jjf"2Yзw lcn$\cVb.cں b酩߿>u@<  ]1RM}Fe#Ӿj*-z:=_njOE 2j49-OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSM&-mE 20Ŧ^4& )gXC`<{܅҇C(S#ŖL"n`q܌KOaY\~ XQq>OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-.{/3.ZGpFTώ-ѕkYWY]?x91 aQw8C iЇ'|f0J$i\otSڷ(Syrgؘ*[; ~oLJקIOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-b'X-2),a(#wYר4}G 16 Yg4c(#U; F60K_k(# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-}((()(t#w}+l`mVgV|Vk*0&(铆-M]7ו}~ڵ&L Z|/5Z/- ^tr<';mZ:d;.?l஽p<s`y h.˗ X3X4ˢQ-\V/ҺZٛ~Yx%OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSx&-D<0/Lm .b{2Xp=Cqyk5@xq`(3ceZ9# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSy&-++LiB0fqHkѵU baٶqOPDkU8HޠS`oqV8%U}>OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-'..*d*V;Tq9ƵZx`XpmfSa/\kҁiz8ѯC=8Sh{wu~ /cDĆ5H}F{-*kN;OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-//+2*-)l[&u N3:IY"_fHj&M\mU-`wrf01 属Lƶ<}ʃg5A o垷56JٽMW^Y'k_ute„]&,cT:go3;~mui eԀzI9KDKd{P`㬮3!?8*g |g3M@CGԍf˭85vo5SZo1,IC_A!HeKSGAtgcsX`0OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS}&-YVD.3DmU[kks<0 9L Fs}ݖmSm'# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS}&-.-|eT2 ^-/WOYČ QD|g~lyD()Mgw=Tպ6,Mpv^OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-)/0tèՙA`{(2u;%¹$lka1@d+%;Q>,(IIN!p)j4|@P=64Xϭ~ho0^έLS^MzB$Fob aOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-)rfG01,*T_1)`A;gfv,,Y?yfҙ_FQ&ti( Uwzmw =]ququuot\q_tg ~@%H OMoP{<"0񄘵|5s[u#ażX@Z\j̄Ǜ:ɿ-9ƻx>ڞ3MiիW'<<# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS:&-}//10fXnC=A)gԾ׽RgGky{K9_c{.<R`÷'q'dtE"+.֞L=ݾWyO#csKTҌ]$Wogl_7Ⱦ=2"USCZJgulz+g/wTqnm$D-Zޚo'EQoy8+[ٲǿ,cu,҆* c0z+<1L IO]E}S3ryI>Λ4{m0mu[:pz :?yw ʝkۇ]=34YG喛QGC$|YCg5fsq@VN/.N_^})q?mKbZ{kAD?8ww2k,{ܯ=AR~&#[9A.A!r uߣ)/ՠ3E{*_LNr^яǧ~TC&6Gj<EQoYXޢ0#arl+TZȅk) 'ofƔEN\uFj!i2cbF gKwg[H$,39U$ڬm[aD ˕u- ȨJMp}zYhF}N/GV4TұjXbC32jKig\~>:?pv5]1U,`t8r}dmv ͓]wnj?>OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-H"*/i([sg6&]`7dxWL]d$i(#w'}]nf΀Nt$:a63b]0aQ.ScOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-H)'(|#-~Z'+0>UR.m~xد DwjV[\x}irxmxsU3=A0?셶{’ʾ|OXROggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS.&-+(|s@-DžѨ:i_qog5:hĤoDF|퓶` HM6[k/nn>x]vz#7 m_c1Ȥ$ ZFEȕ[߾q|>>>X^^#e}pZuU~ɪPR񲺚ЖЖoWP]myp2b^^6yH`^^H-//!r2eX^6yq OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSZ&-hX$&('(%_`'@(@`EΘMc- J{ ><=} h?mBVtF{qit {@!p&eu.qv7s W%;!&tm늠 $a @8>.9Mm|s!p7`fT걍OW=aߴ6/\e LۢQfkTGhNh OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&- Z47\guڛ>nyp^u`:b!>64G~iY* 0,k(Qi*낭(q\ \'0 `q*3uCHMWpcOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&- \(3+an~fitp$V?ZHA8]?;ePp{hnIR]ZM:9ċ l6[_{ zvd]–uVOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS.&-q=',/+'e!}b:k :Mu'|kJwgy',OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSZ&-670/+*+Ŝe= z r? {|7\^e?#`Ζp?/s7:9slãm$*eFv2g=;oK rO7 O oWr`e-Hs rZ{7MVݿPN5vvZ U:<4#S-z)ג^X {N)*w~իy9c$DI x ÏFX^^Ue`nTWW׹  LJ LJЖLJ,/////+||||8jOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-(% m(G*~=R)OO7|][)J$m(_n6Q Hp䴩׾v]eNZuPOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-Ht"<ۋ8@Kw\{TFznlNԝ#IP0hnsiOKowwzH?pZ| KGmhO'ke:͓u>N=~ d5&'E7OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS.&-|+Z"E }`9].WkOM=05:Kެzo.R*"h~uŷcM=ھ}ӷi)@ RߋZO$5vҳR[Kk쫕 |3|_zdVqs?;w dOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSZ&-eձb'vCا=  p>*/8TJwXVZG$0+@ sX}ٽyɜ#/<[q;_EEa|;ij^\-^*KFa֩d `O< o!=T@:>^r [cf`@i8;&I_kkajiTOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-0 *+# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-|4-)'t]Z؝>a)4Ǒ{o[=LMGa/ #+R0_:i4># G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS.&-N**+'|_= _S rO qhӿ:gnHG=Q aj>`dcBckZmOl7h{^f]C?8U tw2)xԿ;[=aIQ?lWa@f'_^4͏,s$,OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggSZ&-@7k&/+#'&l˲ IFySy*UX,VateF?*;ff?p(?_=VIeVF?v2, ՝Ƿ%['Z}hy9y|0lD6=Vrg#?AI]|KJw@t3+ =pk.oì7KV)S=U kŸZ&,/j\/%$OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-?/.-# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-1y*#tc.ANN{q"/F9 |7|0t{I9ċڳHmQ_%[ӈ~gO7^zjycdFJb*}055[lO7@uuuuKR]]]] nQe-"TW/L LJ,////////+쏏ewOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-* _z# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-˘x"S|鯵dtV-4άF7 ~o n67 0kY1:\=A"Vjc8KPOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS:&-*.1-)"ɜcA X&UڃC Eɋ ], <B*`iߗ]ct]5܊\[qM)?&_ts=FCtdR#P=Gpxn1z}aQCEtτ/B^?jOe R a~6Saq5x ^jM)+L"/;*SG8%gդȼ}dd.:,c8*qgLʛYtvi286D%O  3뛚,:xw>sAy:GHW ZI2=D*϶N'+]C>:|ȏt>>OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&- ##ϯ)XO}4;L-ߝs&p mT hk`eޠ^i(I{r^OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-K1?#|)6nbB`6paagdP|Tlǿ[4Tw>7~eYr_9Jf~d6n߾}{@ ! xs0q{o߾wwwwo[]Pk=>K5^VWٰeg饼[ ?exOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-})!LI{|^?5i.}𕓥<ǭg@9:TTTG& 8Uڇu"!JIܾ}^)pօ+$}Ǒc~QUSMіy`^^f|Жy>>hLJ2Жy>(alљ&Cd>nj;CzWW OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-)Ѷ("|_v i!]mo[~wߺ!O{A{+=B@@bShg 74 7'~eU"胒!}x,;G?n?ҹ<zhm)}ϒMn %k=K8/ $S=q+ o#kF)ucTNr,}$,u'.# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-=0+*+f(UdW V %:] Du-G N i(]Y +u@ 5MulX c(|#s=[IKJagOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-`ʺ.$)tg,_KL6w"{ϯ\9Xa2z?XOs%)o=" ?2O|ˎ8L;F п  0//wOggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-Ο't|{:= ! }›ʕ3W'l;uVA >zo v=LJeW옆`a"AUL鹎t} eo'Gn27joiApGl5?{pMKΡsH`OggS&-BvorbisD8OggS&-<<vorbis,Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)vorbis"BCV@$s*FsBPBkBL2L[%s!B[(АU@AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d((  @Qqɑɱ  YHHH$Y%Y%Y扪,˲,˲,2 HPQ Eq Yd8Xh爎4CS# G,]6MuC74ӴmUut]_uhPTU]WeUW}[}UUYՖa} Uum]X~2tu[h뺱̾L(CLABH)R9)sRB)RR9&%sNJ(PJKB)RZl՚Z5Z(PJ[k5FAȜ9'Z(9*:)Z,)X9'%J!JL%C*b,)ZlŘs(ŒJl%X[L9s9'%sNJ(RRksR:)eJ*)XJJ1sNJ!BJ%SJRb+)XJjŘsK1PR%KJ1snAh-c(%cC)b,)cŘs(%ƒJ%X[sNZkmsЩZSLsYsZ(PJZ[9Rb+)XJŘskPJ%XKJ5k5ZŘkjs1Sk5kNZsc&BCVQ!J1A141朔1 R1R2 RRR RkRRj4%(4d% `pA'@pBdH4,1$&(@Et.:B H 7<'RpFGHH!DD4OggS&-}u!]l+. @EQ3l+:uW(+x:kח M]V$Z@!HIU7bł0c[ ^IT-%[cC htXߜV0$~b3ιs;ιs{ f A(A#vEb_3%ڴEhdΚR9mphꗿG笉3-RȜmFA?]WJ^rsOB|3[@LISTpdtaphdrVSpace Flute wWonderland Xylo~DxGated Screamer}xNew Age Organ|y STR-808 Drumset 2 |{Electronic DrumsetKraftwerk Drumset)Analog Drumset:v := TR-909 DrumsetkN0TR-808 Drumset~CR-78 DrumsetU} 0TR-101 DrumsetGpy0 TX81Z Sqncr Bass 2{Filtered StackzWoody BassyDelicate MarimbaxDelicate BellswWind DownvWarbling Bird 2uU* UWarbling Bird 1tejiWjLaser PopssYWind Blastrr2Warbleq8uSinging StringspuDistorted LeadouDHeavy Squarenu(Cheap Synthmu$Polysynth 2lPing-PongkVenus Violin Hitj Polysynth HitiMono Analog BasshCheesy PadfNew Life 2gMonster StringseStab Bass StringsdKiller Bassc Thunderb#Zippy Bassa'Yazoo Bass Hit`+Yazoo Zips_/Oingo-Boingo^3Sawtooth Hit]7Warehouse Percussion\;Breath Pad[>Dreamy PadZATechno BellsYDRough StringsXIAwesome StringsWLSustained HarpVPBonky OrganUSHouse OrganTWNoisy PopsS\Ethnic BowR_Panned StackQ`Monster Stack LeadPcAcid Bass 2OhEcho Pop BassNkAcid Sub BassMoTX81Z Sqncr BassLsYazoo BlipsKvWarble PadJySpudge BassI|Undulating PadHBanshee PadGLong BassFNew LifeEXylophoneDClick PopsCBlistering BellsBWahATwips Ring@Melodic Vibrato?Screaming Wavepad>Stacked Mega-Phaser=Pop Bass 2<Pop Bass 1Wavepad;Polysynth Warp:Rubber Bass9China Voices8FM Clang7Phasing Choir6Singing Bells5Harsh FM Bass4Gated FM Bass3Dream Hit2FM Bass Hit1Faerie Chorale0Metallic Clink/Oink Grind.Wailing Hit-Casio VL-1 Pops,Square Pop Flute"Lead Synth 3+Lead Synth 2*Lead Synth 1)Aluminium Plate(Space Warp'Sine Bongos&Sine Whistle%Vatican Bell$Phasing Strings#Square FluteWater Triangle!Cosmic Vibraphone Bass Dragon ChoirVatican PipesFantasyElectric Slap BassBingo Bells D50-ish Bells Breezy CalliopeChoraleDistorted Sweep BassTX81Z Lately BassFM Electric BassSmooth Strings 1 Smooth Flute#Triangle Simple&Triangle Dream Flute)Thin Sawtooth,Classic FM Bass .Church Organ 1Meat Grinder 4FM Christmas Bells 7Hard Grunge Bass :Resonating Pad=Sheet Bass@Dragon SweepCEl Cheapo OrganFDetuned SawsIGrungy Ramp BassMSimple Square FlootPFM CarillionRFM Bells 1UXpbagd !$&:;GKPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     !')3=GQ]hjlz|~ %')+:<ACKMPS[\lnq$(,/ADHKWZ\_ruvy#'035ADGHRUWbglq| -03<DJPZ[_imt#&).89CP]gjlnq  !#./=>@FUYhios{}  %(*47<DFHPRT_aqtv}    & 0 5 8 : A D F P R U _ b d m p r w x      & ( * 2 3 @ C E R U W pmod pgen`)<3&n$%% `,%#"r!)3 n%$ &  x#n"n!n8)) 3" L#!<P ,(&)"n3n$ &2 #n!n  ) 4))))))))))))) ) ) ) ) ))))))#)")!) )))))))4)3)2)1)0)/).)-),)+)*)))()')&)%)$)e)d)c)b)a)`)_)^)])\)[)Z)Y)X)W)V)U)T)S)R)Q)P)O)N)M)L)K)J)I)H)G)F)E)D)C)B)A)@)?)>)=)<);):)9)8)7)6)5)x)w)v)u)t)s)r)q)p)o)n)m)l)k)j)i)h)g)f)))))))))))))))))))))))~)})|){)z)y)))))))))))))))))))))))))))))))))))))))))))))))))))))))))!%$` # "& L#"!) )#  &` $` "!,d < d 4) (6n 4) 6  4) P 6-U4)3`  L#!"& $%,4)4)3$&%% `,%#"!))3,&$%#"!4)4)&$n%  #"!3<X%4)%4)  &$n%#"!3<X< P g%)&$%) L&$n% #"!3<X P )%)&$n%  #"!3<X))&$) #$%&% "!3<,)) n&2$n"!3, P )%) 6S&4 $ )=%")" #&` $` !, )4)4) n n&2$n"!3,4)S&3$G)4)#n"n !n&4)4&n)4&n) &#"!)  0&n%$` #n L "n!n4),4)& L x#"!( 4)!S4)  L&P$#n"!nd4)4)&%$` #n L9 0"n!n4)4)$#n L9 `"n!n34)4)#  &` "!n,)4)&$% L#"!&< )) L&n#n!n"n3 4 )4) L&n#n!n"n 3 %4 )4)&n#n!n"n 3 %  4 )4)3, L j#n!"& (4) 4) ()f L&$%#  "!(4)$~&~3)4) L&$%#  "!(4)3)4) d&$ x%#  "!(4))4)&$` %4 #"!(P %$P&PX!"#)3)4%$P&PX!"#) d%# "n!nd4 )4))$n L!#%"   4))$% (n &n"X4)$% ` &n"X4)% Ln &` $B!"#(X) $` 4#"!P &`  &  (4) nP( :4)$` %4 #"!(P &20,"4)0,4")%4$P&PX!"#)03)% L!# (,"P&n4)) L!#2 2(,"&n4)4))!#2 2(,"3&4)) L#"!<~ &0d"4)0d"4)(0d3")4 #"!( &" 04)" 04)%4$P&PX!"#)" 3)$&%#!"(P )d L3&$)P)  ) #!"n,  H ))  ) d#  &n$n"!, 4)4)3 L)$` &`  ) L X%$ #%!"34)4)$ L&. n#%!n" 4)4) $&%#n!n"n) ($& n#%!n" 4)4) $&%#n!n"n)!%$` # "& L#"!) 0)0<(&$!% #") &J$) L ~d"P# 2P!&n&X$ %)$) %"&n 4)&~$`n#%!"3  4)4) d "%# P!&n4)4) L~"P# P!&n4)4)%$` # !"&))$~&d% L#"!4))% L $&.% #n"n!n4)4)!$% L&#"),(&,$,3) ($2%&#"!: :4)4) dP #%$n&  "!)) #%$n&   n"n!n,4)4)X"&$) L&#"!&< ) ,!n"n  (& $ 4% T L#"!)4% T L#"!)#  &` $` "!, 4)4)4) 4)n L#$%&P!n"3  4)4) $&%#n!n"n) L#$~%&!n"3 ,n 4)4),!n"n  (&2$2&` $` #"` )  `  H#)% l L#"!& $ )~ %&$` n,n | n"n!n3,4)4)&!nn"n3( n,n 4)4)3!nnn$ &n#n"n)4)$ %  &#n"n!n)  & #n!` "n)4)%(&$` #n"n!n ( X) nnn&$n#n"n!n%  ,)4) L#"!)() % n,n&$` #n"n!n  X#"!)(% nN &$` #n"n!n ( X)<&~  ,%#n"n!n)  &$n"!n)4) (&$%#n"n!n4)) #n"n!n), n  & $nP#n"n!n4)) ($&%#n!n"n(P 4))n&P ( n T(#n"n!n)!n"n  &2$2,34)4)  &2$2 n"n!n,4)4)n&2$nnnn | n"n!n3,4)4) 3&"PP )) &<"n4)4)!&#$%" LP )4)< "&P #4))$ !#%&", P  4) $n&n4) "#n P!n&4)4)"#n  ~!n&4)4)#"!,P (P ~&4) n%$ &  x#n"n!n4))&< "n  4)) "&P 4)4) 2!#%$ P,"&` 4))% $&#n!n"n 3<    0P4)$&)#n$n% &"n  ,4"n)"n) &n"P  &n"n)") P"P  ))4)4 "y&2 P 4)4) n$#n!n"n&. 3   4)34)4 $.&.%#n!n"n)4 ,#n!n"n%&$3 ) ,#n!n"n%&$3 ) $.&.%#n!n"n) ,!n"n&P4))n  "&4)) n!n(P ,&2")4)#n !n"n(P &4)) !n(P ,& "4)) "&)$#n% 9 "n!n34)4) &d4)4)#n!n"n 3P    4)43) !n<P ,(&` "n4)) "n!n3(n nP 4)43)#r n, !n& "))#n!n"n 3    4 )4)#n!n"n x n3    4 )4) #"!( &4)4) "n!n3,4 )4))"n!n3n nP 4)4)#n"n !n&)$#% !n(P ,(&"n4))$` #% !n(P ,(&"n4))instInstrument8Instrument15Instrument16Instrument9Instrument3808 Maracas 808 Clave 808 Cymbal 808 Conga Hi808 Conga Med808 Conga Lo808 Tom Hi 2808 Tom Med 2808 Tom Lo 2808 Cowbell808 Tom Med 1808 Tom Hi 1808 Tom Lo 1808 Hat Closed!808 Rim$808 Kick 1&808 Clap'808 Snare 2)808 Hat Open,808 Snare 1/808 Kick 22ELECTRONIC KICK 23ELECTRONIC SD 26ELECTRONIC SD 19ELECTRONIC KICK 1<ELECTRONIC TOM LO 1@ELECTRONIC TOM LO 2BELECTRONIC TOM MID 1DELECTRONIC TOM MID 2FELECTRONIC TOM HI 1HELECTRONIC TOM HI 2JKRAFTWERK BD 5LKRAFTWERK SD 7OKRAFTWERK SD 6SKRAFTWERK KLAPSWKRAFTWERK SD 5ZKRAFTWERK BD 4]KRAFTWERK SD 4_KRAFTWERK BD 3bKRAFTWERK SD 3dKRAFTWERK RIMSHOTfKRAFTWERK CYMBALgKRAFTWERK HAT OPhKRAFTWERK HAT CLjKRAFTWERK SD 2lKRAFTWERK SD 1oKRAFTWERK BD 2sKRAFTWERK BD 1uANALOG SNARE 9wANALOG SNARE 8yANALOG SNARE 7|ANALOG SNAPPY BDANALOG HAT OPEN 3ANALOG HAT CLOSED 3ANALOG HAT CLOSED 2ANALOG HAT OPEN 2ANALOG SNAP SNAREANALOG BOOM KICKANALOG CLAPS 3ANALOG CLAPS 2ANALOG ZAP OPENANALOG ZAP CLOSEDANALOG CLAVEANALOG SNARE 6ANALOG SNARE 5ANALOG THROB KICKANALOG POPS OP 2ANALOG POPS OP 1ANALOG POPS CL 2ANALOG POPS CL 1ANALOG PHASERANALOG TAMBOURINEANALOG KICK 3ANALOG RIM SHOTANALOG CRASH CYMBAL1ANALOG COWBELLANALOG BONGO HIANALOG BONGO MEDANALOG BONGO LOANALOG SNARE 4ANALOG SNARE 3ANALOG MARACASANALOG CLAPS 1ANALOG HI TOM 2ANALOG HI TOM 1ANALOG MID TOM 2ANALOG MID TOM 1ANALOG LO TOM 2ANALOG LO TOM 1ANALOG CRASH CYMBAL2ANALOG HAT CLOSEDANALOG HAT OPENANALOG LASERANALOG SNARE 2ANALOG KICK 2ANALOG KICK 1ANALOG SNARE 1909 BASS BOOM909 TOM HI 2 909 TOM HI 1 909 TOM MID 2 909 HAT OPEN 909 TOM MID 1 909 TOM LO 2 909 HAT CLOSED 909 TOM LO 1 909 SNARE HI 909 CLAP 909 SNARE LO 909 RIM 909 BASS DRUM HI 909 BASS DRUM LO 909 SNARE 2 909 SNARE 1 909 BASS DRUM 2 909 BASS DRUM 1 808 CLAVES 808 MARACAS 808 BONGO LO 808 BONGO MID 808 BONGO HI 808 COWBELL 808 HI TOM 2 808 CYMBAL 808 HI TOM 1 808 MID TOM 2 808 HAT OPEN 808 MID TOM 1 "808 LO TOM 2 #808 HAT CLOSED $808 LO TOM 1 &808 SNARE DRUM HI '808 CLAP *808 SNARE DRUM LO +808 RIM SHOT .808 BASS DRUM HI 0808 BASS DRUM LO 1808 SNARE HIGH 2808 SNARE SHARP 5808 SNARE FILTERED 8808 ULTRA SNAPPY ;808 BASS DRUM MUTE >808 BASS DRUM BOOM2?808 BASS DRUM SHORT@808 BASS DRUM BOOM1ACR78 HI TOM 2BCR78 HI TOM 1CCR78 MID TOM 2DCR78 MID TOM 1ECR78 LO TOM 2FCR78 LO TOM 1GCR78 BONGO LOHCR78 BONGO MIDICR78 BONGO HIJCR78 COWBELL KCR78 TAMBOURINE MCR78 HAT OPEN OCR78 HAT CLOSED QCR78 SNARE 2 SCR78 METAL WCR78 SNARE 1 YCR78 RIM SHOT ]CR78 KICK 2 ^CR78 KICK 1 _CR78 CLAVE `CR78 MARACAS aCR78 GUIRO b101 LASER 3c101 LASER 2d101 LASER 1e101 MARACAS f101 SHAKER g101 BONGO LO h101 BONGO MID i101 BONGO HI j101 COWBELL k101 TAMBOURINE m101 HI TOM 2 o101 CYMBAL p101 HI TOM 1 r101 MID TOM 2 s101 HAT OPEN t101 MID TOM 1 u101 LO TOM 2 v101 HAT CLOSED w101 LO TOM 1 x101 SNARE 2 y101 CLAPS }101 SNARE 1 101 RIM SHOT 101 KICK 2 101 KICK 1 101 SNARE EXTRA #02101 HAT OP EXTRA 101 HAT CL EXTRA 101 SNARE EXTRA 3 101 KICK EXTRA 3 101 SNARE EXTRA 2 101 SNARE EXTRA 1 101 KICK EXTRA 2 101 KICK EXTRA 1 ADDITIVE BELLSMOOTH SAWMELODIC VIBRATOFM CLANGFM3FM BELL 2HARSH FM BASSFAERIE CHORAL AAAOINKCLINKSMOOTH SQUAREHALF RECTIFICATIONVIBRAPHONEGRUNGEPIPESBELLSBREEZEAAHSELECTRIC BASSDM 3DM 2DM 1WHITENOISE 3TRIANGLE WAVESQUARE WAVESINE WAVESAWTOOTHADDITIVE 3WHITENOISE 0FM 2 FM 1ibagP &29ELX_gs$+6=IMWamz!+;IZn{!)48JN[hx|"/:>LYau,29K^k{'';OP[ep!.<JXkx-=MRZ`emsx   " - 5 ; F N T _ g m x    ( 6 C W e x  # 6 K ` x   0 A V g w   & < N ` v ,CZq %:Og  1BTf|*<Og%7DR`v 2H^u,=Shy$;Qi 6J^u  4I[o !5Kav $,5>GS\enw&.;CPXckw'1>FR[dmv&/8AJV_hqz )2;DMXajs}imod igenu" d.#&$:962-5" d.#&$:962-5" d.#&$:962-5" d.#&$:962-5" d.#&$:962-5+FF4:,"&$%5 +KK&$%4:W62-5 +11  $&%:=62-5+110&%$4:+62-5 +110$&%4:,62-5 +>>&$%:e62-5 +??:'&%$62-5 +@@4(:/&J%$J62-5 ,IIIII+22:&%$62-5 ,IIIII+//:!&%$62-5 ,IIIII+++:$&%$62-5 .4 +880@&$%62-:85+884&$%62-:85,IIIII+--:"&%$62-5 ,IIIII+00:&%$62-5 ,IIIII+))4:&&%$62-5 %$&+**:203 62-5 +**} 62-:55+%%3: &$%62-5+%%4&f$f:C%3 62-5 +##(&%$362-5 e _&$%+''062-:'5+(( W&f%$f3x62-5 +((3  &$%:&62-5 +((3 &$%:&62-5 & #%$+..$:603 62-5 +..} 62-:95+&&0d&%$3.62-5 +&&3 ( &$%:$62-5 +&& (3 &$%:$62-5 +$$(&<%$<362-5 "g3&P$P%+$$ 2-:.6K5+$$42-:.6K5+((("r4&P$P%:(62-5+(("r2-%:6w$&5+((("r 4&P$P%:(62-5+&&4 P '&$%:(62-5+&&2-%:6w$&5+&& ' P 4&$%:(62-5pe"g3&P$P%+## 2-:.6K5+##42-:/6K5+## ,"I&P$2-:6K5"r1%3%&$+))2-:(6K5"r1%3%&$+++2-:(6K5"r1%3%&$+--2-:(6K5"r1%3%&$+//2-:(6K5"r1%3%&$+002-:(6K5"r1%3%&$+222-:(6K5+$2-%:76L&5+:%5+0,:F%&~$~qq62-59,$J&J%+ 62- U":!5+ 62-"  :!5+ 2-%# :6K" $&0N5,$&%+//4 2-:#65+//2-:#65+//:?qq$&&&62-59(%&P$P (3+'' 62-:+5+''62-:*5+-- x4 ]&$%:+62-5+--2-%:6w$&5+-- x ] 4&$%:+62-5+!!@2-%:16L&$5+!!:%5+++62- 03:!$J&J%5+++62-0 3 :!$J&J%5+++&f$f62-3:0%5+""2-%:56L&$5+"":%5+))4 &q$q2-%:*65+))&q$q2-%:*65+%%&$(:>%5+11  @2-:46$` &` %5+..) d2-# +:46 " $&1%5+.. 32-# +6:! " $&1%5+**) d2-%# +:06 " $n&5+** 32-%# +6: " $n&5+((, # 3:!$&%5+((:#&$%5+((,#  3 :!$&%5+&&)2-%:$6L&,$,5+&&3:!$n&n%5+&&&&$&%:6 62-5+&&3&&$&%:662-5+$$2-%:76L&P$P5+$$:6&n$n%5+##1%:6&$%5+##III2-%:56L&$5+HH2-%# +4-:66L" $&Z 5+HH:M%# 6( " $&5+GG:B P 6 (2-&$%5+GG:D P 6(2-&$%5+GG32-%# +:.6z " $&5!+FF:B P'6 (2-&$%5+FF:D P'6(2-&$%5+FF32-%# +:.6z " $&5!+DDI:O2-%# 6LI" &$ (5+BB :D#d&$%62-5+AA :Cd&$%62-5+<<:B) d2-# +6 " $&%5+<< 32-# +6:+ " $&%5+==:C) d2-# +6 " $&1%5+== 32-# +6:, " $&1%53+;;:0%4-6$& 5+;;:2%4-6$&5+;;:2%4-6$&5+;;32-%4-:6L$&Z 5+:: d$&%62-:85 &$%( P3+77 62-:+5+7762-:)5 P3+55( %&$62-:+5+5562-:& $P%5+55(%&$62-:)5+44:= , & $%IIN 62-5+33 , &$%IIN 62-5+CC9&n$n%:*62-5`+32-%# +:6z " $&5!+( %&$62-:+5+62-:& $P%5+(%&$62-:)5+6 (2-:&P$P%5+6(2-:&P$P%5+32-%# +:60z " $&5!+ x&$%62-:5 +9d&$%:62-5`+9d&$%:62-5`+9d&&$&%:62-5`+9d&&$&%:62-5`+,,, &~$~%nn?! 62-5 x%(&$+66 n2-:465+660 n2-6:95+%6d$<&<:*5"+%2-:65+%%&f$f%# 460" dZ: 5"+%%2-%:605+99::%2-65+99d%&n$` 62-5 +88I n:F$%2-+4-6&%%5+88P$%:F2-6&5+88VPd$%:R2-6&53%&$+>>:2-6K5+>>0d$&%53%& $ +??:'2-6K5+??$&0d%53%&P$P+@@:32-6K5+@@$&0d%53+!!d32-%# +:60z " $P&P5!+!!62- n :(&P$P%5+!!62- n%&P$P:(5+""( &$%:062-5+""(&$%:/62-5+""d2-%:6w$&5+EE n2-%6"&$:Q5 )X(&P$P% Z3+''62-:+5+''362-:+53%I)&$+222-:(6K5+22&/$&0d%53%I)&$+002-:(6K5+00&/$&0d%53%I)&$+//2-:(6K5+//&/$/0d%53%I)&$+--2-:(6K5+--&&$/0d%53%I)&$+++2-:(6K5+++&/$/0d%53%)&$+))2-:(6K5+))$/&/0d%5+113 n$n2-04-:46 &` %5+113,$n062-6: &` %5+** n$2-0# +4-:06 &%5+**$062-6:&%5+.. n2-0+4-:46 &%5+..062-6:!&%5+  $& : 53+((d32-%# +:60z " $&5!+((62- :-(&$%5+((62-%&$:)(5+((62-$&%:*(5+$$%# 460" dZ$&:#Z 5"+##%6( dZ$&:,5"+&&32-%# +:60z " $&5!+&&( 2-%# +:6 " $&5+&&6 2-:!&$%5+&&62-: &$%5+""3d<%# +:0" Z $&5"+""4$"g32-%# :46K&5+222-%# +4-604;:"" Z$1&1 F5 +002-%# +4-604;:#" Z$& F5 +//2-%# +4-604;:%" Z$]&] F5 +..%# +4-:40 " $0&6 5+..%# +4-::6 ss" $o&o0 i5+..2-%# +4-:$60`  " $0& 5+..2-%# +4-:%6  " $0&0` 5+--2-%# +4-604;:&" Z$& F5 +++2-%# +4-604;:(" Z$`&`  5 +**%# +4-:00 " $s&s6 5+**2-%# +4-:!6  " $s&s0` 5+**2-%# +4-: 60`  " $s&s 5+**%# +4-:66 ss" $&0 i5+))2-%# +4-604;:)" Z$& F5 +((%# +4-:,6 " $1&1 0 K5+((2-%# +4-:60L" $&Z 5+((%# +4-:-6 " $1&10 K5+((%# +:,6 " $&4t K0Z5+''2-# +4-%:.60 " $&95+&&%# +4-:,60 " $& 5+&&0%# +:,6 " $&4t5+&&%# +4-:,60 " $]&]5+&&%# +4-:-60 " $&5+&&2-%# +4-:60L" $&Z 5+%%2-%# +4-:&6o0 " $&5+%%2-%# +4-:@6 " $&05+$$%# +4:0" Z N$&5"+$$32-%# +:46K "$&0N5+##%# +4: 0$W&W" Z '5"+%# +4-: 6 " $& 0 K5+2-%# +4-: 60L" $&Z 5+%# +4-:!6 " $&0 K5+%# +4-: 60 " $`&` i5+2-%# +4-: 60L" $&Z 5+%# +4-:!60 " $`&` i5+%# +4-: 60 " $& K5+2-%# +4:0$W&W" Z 5"+2-%# +4-:+6K0 "$0&05+2-%# +4:0$W&W" Z d5"+32-%#+:(6K0 "g$<&<,5+KK3 2-%# +4-:6 " $o&o5+GG2-%# 6" $&:E34d5+@@2-%# +:<6K " $&04_'5+??2-%# +:56K " $`&`04_'5+>>2-%# +:,6K " $&04_'5+882-%# +4:26$&$ " 0 U(5+882-%# 4-:860; ' " $& !5+222-%# +:6K" $&0(Z 4N5+112-%# +4-:+6 " $& 0s5+112-%# +:(60 " $&4b5+112-%# +4-:76 " 0$& 5+112-%# 4-60:# " $& =+5+002-%# +:6K" $&0(Z 4N5+//2-%# +4-:"6K" $1&10(Z N5+..2-0# +4-:46 " $&1% 5+..2-# +4-60;:! " $&1% 5+--2-%# +:$6K" $&0(Z 4N5+++2-%# +:&6K" $]&]0(Z N45+**2-0%# +4-:06  " $s&s5+**2-%# +4-60;: " $s&s 5+))2-%# +4-:(6K" $&(Z N5+((2-%# +4-:6L " &$m5+((%# +4-:,6/ " $&, y5+((%# +4-:06 " $&/ | 5+''0(32-# %:-6 " $&5+&&2-%# +4-:6L " &$m+5+&&%# +4-:%6 " $&6S5+&&%# +4-:&66 " $& <5+%%2-%$ # +4-:6  " 5+%%2-%$ # +4-:&6 " 5+$$2-%# +4-:(6L" &~$~Z '5+##2-%# +4-:46L " &$N5+2-%# +4-:6L " &s$s5+%# +4-:6 " $&6 5+%# +4-:66 " $&  5+2-%# +4-:6L " &$N5+%# +4-:!6' " $&  5+%# +4-: 6 " $&'  5+2-%# +4-:6L " &s$s5+2-%# +4-:6N " $&  s5+%# +4-:6 " $&N 5+2-%# +4-:6L " &$sm5+2-%# +4-: 6 " $&'  K5+2-%# +4-:!6' " $&  K5+2-%# +4-:(6L " &$ Q95+2-%# +4-:*6L" $ &5+2-%# +4-:(6L" &$ 5+2-%# +4-:/6LI" &$5+222-%# :6K" $&0'45+002-%# :6K" $&0'45+//2-%# 4-:"6K" $&0'5+--'2-%# :$6K" $&045+++'2-%# :&6K" $&045+))2-%# +4-:(6K" $&0'5+@@4)&$%:T62-5+??4(3)&a$a%:J62-5+>>43)&$%:=62-5+882-0%# :A6  " & $45+882-0%# :A6  " $&4_ r5+6662-%# 4 " &0r ]:A$5 +66 P2-%+4-60P:' " $& 5+..2-%# +4:/60 " &$Q 95 +..2-%# +4:460 " $o&o5 +**2-%# +4:+60 " $s&s 95 +**2-%$ # +4:/6 " 05 +((2-%# +4:60{ " $&5!+((2-%# +4:%6 " 0$&L5+((2-%# +4:06 " 04$&5 +((2-%# +:0604 " &$ 45 +''2-%4:'60T+  " $&#S5+''2-%#S+4-60: " $& 5+&&2-%# +4:60{ " &o$o5!+&&2-%# +:26 " &$ 4045 +&&2-%# +4:26 " $&045 +&&2-%# +4:%6 " 0Z&L$5+%%2-%$ # +4-:0  " 5+$$32-%# 60" &:+Z $5"+##Z2-%# 6" &:. $5"+""2-%# +: 6 " 4T0$o&o5+!!%# +4:604 " $&5+ 2-%4-: 60 " #$& x 5+55<2-# +4%604  " $J&J:45+44::$ # +4-%0&h " 95+332-:3%+4$h&O $5+GG2-%# 46 " &$ v :J5 +EE2-%# 46 "&$v:Q5 +@@2-%# +:<6K " $&04_N5+??2-%# +:56K " $&04_N5+>>%2-# +:,6K " &,04_N$,5+882-%# :?6  " &s V04$s4au5+882-%# +4a:?6  " $s&s04u5+662-%# 460 " &$v := o5 +662-%# 4-:"6  " $&0 <u5+222-%# +:6K" $&0(Z 4N5+112-0%# 4:76 " &$  !} p5 +112-%# +4:.6 " &0$5+002-%# +:6K" $&0(Z 4N5+//2-%# +4-:"6K" $&0(Z N5+..2-%# +4:/60 " &$Q5 +--2-%# +:$6K" $&0(Z 4N5+++2-%# +:&6K" $&0(Z N45+**2-%# +4:+60 " $s&s5 +))2-%# +4-:(6K" $&0(Z N5+((2-%# +4:60z " $o&o5!+((2-%# +4:$6 " 0$&5+((2-%# +4:16 " 0$& X'5 +((2-%# +4:060' " &$  Z5 +''2-0%# 4:'6' " $& ].5+''2-0%# 4:16' " $&r ]5+&&2-%# +4:60z " $&5!+&&2-%# +4:%6 " 0$s&sL5+&&2-%# +4:06 " '0$&5 +&&2-%# +4:/60' " &$ 5 +%%2-%$ # +4-:60 " 5+%%2-%# +4:%60 " $&5+$$2-%# +4:)60w " $&N5+##%# 460 " FZ$a&a:15"+ 2-%# +4:60z " $&5!+ 2-%# +4:+6 " '0$&5 + 2-%# +4:(60' " &$ 5 +2-%# +4:60 " &$ 5 +2-%# +4:60 " $s&s 5 +2-%# +4: 60z " $&5!+2-%# +4:&6 " 0$& K5 +2-%# +:&60 " &$  K4=5 +2-%# 460" Z$&:!Z '5"+2-%# +4:60z " $&5!+2-%# +4:&60' " &$ 5 +2-%# +4:)6 " '0$&`5 +2-%# +4:6 " 0$&L5+2-%# +4:60z " $&5!+2-%# +4:+60' " &$ 5 +2-%# +4:,6 " '0$&5 +2-%# +4:6 " 0$s&sL5+%# 460" FZ$&:Z 5"+%# 460 " FZ$&:"(5"&n$n <"g3 +R:T62-5&+:E62-5%+FQ:H62-5$+9:062-5#" d.#&$+E462-5*+FQ4:H62-5)+R]4:O62-5(+^4:T62-5'3 " d.#&$+E4:H62-5.+FQ4:T62-5-+R]4:[62-5,+^i4:_62-5+" d.#&$4:M62-5/3" d.#f&$+E4:862-5Q+FQ4:D62-5P+R]4 :P62-5O+^4:\62-5Nn" d.#f&$+E462-53+FQ4:H62-52+R]4:T62-51+^4:`62-50" d.#&$+E462-57+FQ4:G62-56+R]4A:T62-55+^4:`62-54" d.#&$64R2-:O583" d.#f&$4:P62-5:3" d.#&$4):M62-5;" d#&$4^:O62-59$4"r d#Y&+E462-5?+FQ4:H62-5>+R]4:O62-5=+^i4:T62-5<4" d.#&$+E462-5C+FQ4 :I62-5B+R]4:P62-5A+^i4:T62-5@" d.#&$4/:Q62-5D4" d.#&$4S:N62-5F" d.#&$4-:N62-5E" d.#&$4):N62-5G" d.#&$.493:R62-5H4" d.#&$4:L62-5I" d.#&$+E4:H62-5M+FQ4:T62-5L+R]4:[62-5K+^i4:`62-5J3" d.#&$+E4:-62-5U+FQ4:962-5T+R]4:E62-5S+^i4:Q62-5R" d.#&$+E4:-62-5Y+FQ4:962-5X+R]4:E62-5W+^i4:Q62-5V" d.#&$+E4:-62-5]+FQ4:962-5\+R]4:E62-5[+^i4:Q62-5Z" d.#&$:K62-5^" d.#&$+E4:-62-5b+FQ4:962-5a+R]4:E62-5`+^i4:Q62-5_"g d.#&$+E4:-62-5f+FQ4:962-5e+R]4:E62-5d+^i4:Q62-5c" d.#&$+E4:-62-5j+FQ4:962-5i+R]4:E62-5h+^i4:Q62-5g" d.#&$+E4:-62-5n+FQ4:962-5m+R]4:E62-5l+^i4:Q62-5k" d.#&$+E4:-62-5r+FQ4:962-5q+R]4:E62-5p+^i4:Q62-5o" d.#&$:K62-5s" d.#&$+E4:-62-5w+FQ4:962-5v+R]4:E62-5u+^i4:Q62-5t" d.#&$+E4:-62-5{+FQ4:962-5z+R]462-:E5y+^i4:Q62-5xshdrvSineWave ED<FM4-0440 hf}<Tri2h )h}<Tri-Sq )6}<FM5-04406D5}<Bell2-0440DIR%n}<cowbellIR$`y}<claploop$`4q2D<white54q-D<sinehiUtD<white808-2U!K}<bdloopy }<THROB DRUMyG b D<909BD11LD<CLAP21D<SQUARELOD<NOISE6kD<SQUARE2qD<CLAP2!D<NOISE3!92D<NOISE59I D<SINEHIIMWD<1MWceD<LASER1ce>xID<RIM>xPgD<CLICKPmD<TRIHIm1D<SINE32D<WHITE4.:D<TRIANGLE.ND<808BDD<WHITE2%:D<WHITE3% :D<BD1 S D<TR909BDS rD<wav1_1{*!g}<wav1_3{*8}<wav1_28F B}<wav1_4FpT }<SAW4pTaDD<SAW3ao'_D<SAW2o}'{D<SAW1}Ҋ[D<W12_4Ҋo9D<W13_3o9D<W12_2WD<W12_1GD<W10000G D<W7_4iD<W7_3q&D<W7_2qND<W7_18D<W6_4#uD<W6_3"D<W6_2"0iD<W6_103?D<MOD2_C33?LrD<FM90000LZD<191Z4iJD<WAV54ivED<SQ4vM<D<SQ3M֑ND<SQ2֑Z#vD<SQ1ZAD<HR47D<HR3 ID<HR2 `D<HR1D<HOLLOWPD<BELL4ND<GRUNGEN  D<BELL10000 ^D<HISS1&D<AAH14?XD<W8_44?L9D<W8_3L+ZND<W8_2+ZgiD<W8_1gu D<dm4_a4uprD<dm4_a3p,wD<dm4_a2,D<dm4_a1ެD<dm3_a4ެq2D<dm3_a3q3eD<dm3_a23D<dm3_a1D<dm2_a4zgD<dm2_a3zkD<dm2_a2  D<dm2_a1 *D<dm1_a4*( qD<dm1_a3(H6dD<dm1_a2H6DD<dm1_a1DRD<Noise3R d2D<Tri_a4 dq@rD<Tri_a3qALD<Tri_a2A2D<Tri_a1eD<Sq_a4AJ|D<Sq_a3A3D<Sq_a2a*D<Sq_a1UD<Sine_a40@rD<Sine_a30MD<Sine_a25D<Sine_a1 nD<Saw_a4 {3eD<Saw_a3{*&#D<Saw_a2*&4D<Saw_a14(B D<add3_a4(BOfD<add3_a3O^5D<add3_a2^l'D<add3_a1lyaD<Noise4yފwD<Fm2_a4ފUyD<Fm2_a3UNlD<Fm2_a2NwD<Fm2_a1wMD<Fm3_a4:nD<Fm3_a3:QD<Fm3_a2jD<Fm3_a1DD<fluidsynth-2.2.5/sf2/VintageDreamsWaves-v2.sf3.yml000066400000000000000000015161101417326347500217250ustar00rootroot00000000000000samplepos: 438 samplesize: 457220 sample24pos: 0 sample24size: 0 presets: - preset: 0 name: FM Bells 1 bank: 0 num: 0 global_zone: name: pz:FM Bells 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -884.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:FM Bells 1/2 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:FM Bells 1/1 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 1 name: FM Carillion bank: 0 num: 1 global_zone: name: pz:FM Carillion/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -884.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:FM Carillion/2 instrument: FM 2 (index 236) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:FM Carillion/1 instrument: FM 2 (index 236) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 2 name: Simple Square Floot bank: 0 num: 2 global_zone: name: pz:Simple Square Floot/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Simple Square Floot/1 instrument: SQUARE WAVE (index 231) key_range: 0 - 128 vel_range: 0 - 128 - preset: 3 name: Grungy Ramp Bass bank: 0 num: 3 global_zone: name: pz:Grungy Ramp Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -6500.00 MODLFOTOVOL: 20.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -1200.00 MODENVHOLD: -3986.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Grungy Ramp Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -5.00 - zone: 1 name: pz:Grungy Ramp Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 5.00 - preset: 4 name: Detuned Saws bank: 0 num: 4 global_zone: name: pz:Detuned Saws/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Detuned Saws/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Detuned Saws/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 2 name: pz:Detuned Saws/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 5 name: El Cheapo Organ bank: 0 num: 5 global_zone: name: pz:El Cheapo Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 10.00 CHORUSSEND: 400.00 MODLFOFREQ: -2084.00 VIBLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:El Cheapo Organ/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:El Cheapo Organ/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 6 name: Dragon Sweep bank: 0 num: 6 global_zone: name: pz:Dragon Sweep/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: 110.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Dragon Sweep/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Dragon Sweep/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 7 name: Sheet Bass bank: 0 num: 7 global_zone: name: pz:Sheet Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Sheet Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Sheet Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 8 name: Resonating Pad bank: 0 num: 8 global_zone: name: pz:Resonating Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -1166.00 FILTERQ: 110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODLFOFREQ: -884.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 6386.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 2786.00 zones: - zone: 0 name: pz:Resonating Pad/2 instrument: DM 1 (index 228) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Resonating Pad/1 instrument: DM 1 (index 228) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 9 name: Hard Grunge Bass bank: 0 num: 9 global_zone: name: pz:Hard Grunge Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -7200.00 MODLFOTOVOL: 20.00 CHORUSSEND: 500.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -1200.00 MODENVHOLD: -3986.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Hard Grunge Bass/2 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: 0.00 FINETUNE: -3.00 - zone: 1 name: pz:Hard Grunge Bass/1 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 10 name: FM Christmas Bells bank: 0 num: 10 global_zone: name: pz:FM Christmas Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 60.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:FM Christmas Bells/2 instrument: DM 3 (index 226) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:FM Christmas Bells/1 instrument: DM 3 (index 226) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 11 name: Meat Grinder bank: 0 num: 11 global_zone: name: pz:Meat Grinder/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 80.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Meat Grinder/2 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: 0.00 FINETUNE: -3.00 - zone: 1 name: pz:Meat Grinder/1 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 12 name: Church Organ bank: 0 num: 12 global_zone: name: pz:Church Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 200.00 REVERBSEND: 100.00 VOLENVRELEASE: 454.00 zones: - zone: 0 name: pz:Church Organ/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Church Organ/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 13 name: Classic FM Bass bank: 0 num: 13 global_zone: name: pz:Classic FM Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -50.00 MODENVTOFILTERFC: -7200.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 MODENVATTACK: -617.00 MODENVRELEASE: 3641.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 VOLENVSUSTAIN: 960.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Classic FM Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Classic FM Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 14 name: Thin Sawtooth bank: 0 num: 14 global_zone: name: pz:Thin Sawtooth/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 VOLENVATTACK: -884.00 VOLENVRELEASE: 748.00 zones: - zone: 0 name: pz:Thin Sawtooth/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 - preset: 15 name: Triangle Dream Flute bank: 0 num: 15 global_zone: name: pz:Triangle Dream Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODLFOFREQ: -1817.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3671.00 VOLENVRELEASE: 2786.00 zones: - zone: 0 name: pz:Triangle Dream Flute/2 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Triangle Dream Flute/1 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 16 name: Triangle Simple bank: 0 num: 16 global_zone: name: pz:Triangle Simple/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 17.00 CHORUSSEND: 400.00 MODLFOFREQ: 0.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 454.00 zones: - zone: 0 name: pz:Triangle Simple/2 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Triangle Simple/1 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 17 name: Smooth Flute bank: 0 num: 17 global_zone: name: pz:Smooth Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERFC: -3986.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODLFOFREQ: -1817.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1200.00 VOLENVRELEASE: 1586.00 zones: - zone: 0 name: pz:Smooth Flute/2 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Smooth Flute/1 instrument: TRIANGLE WAVE (index 230) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 18 name: Smooth Strings 1 bank: 0 num: 18 global_zone: name: pz:Smooth Strings 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -3986.00 FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 400.00 MODLFOFREQ: -884.00 VOLENVATTACK: 919.00 VOLENVRELEASE: 1745.00 zones: - zone: 0 name: pz:Smooth Strings 1/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Smooth Strings 1/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 19 name: FM Electric Bass bank: 0 num: 19 global_zone: name: pz:FM Electric Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 300.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: -1200.00 zones: - zone: 0 name: pz:FM Electric Bass/2 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:FM Electric Bass/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 20 name: TX81Z Lately Bass bank: 0 num: 20 zones: - zone: 0 name: pz:TX81Z Lately Bass/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 PAN: 1000.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 814.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 814.00 - zone: 1 name: pz:TX81Z Lately Bass/2 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 300.00 PAN: 1000.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 COARSETUNE: 12.00 - zone: 2 name: pz:TX81Z Lately Bass/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 300.00 PAN: -1000.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 COARSETUNE: 12.00 FINETUNE: -1.00 - zone: 3 name: pz:TX81Z Lately Bass/0 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 PAN: -1000.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 814.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 814.00 FINETUNE: -1.00 - preset: 21 name: Pop Bass 1 bank: 0 num: 21 global_zone: name: pz:Pop Bass 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Pop Bass 1/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Pop Bass 1/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 5.00 - preset: 22 name: Distorted Sweep Bass bank: 0 num: 22 global_zone: name: pz:Distorted Sweep Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 1516.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 VOLENVRELEASE: 814.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Distorted Sweep Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: -12.00 FINETUNE: -5.00 - zone: 1 name: pz:Distorted Sweep Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 5.00 - preset: 23 name: Square Flute bank: 0 num: 23 global_zone: name: pz:Square Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 10.00 CHORUSSEND: 200.00 MODLFOFREQ: -386.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 3802.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Square Flute/2 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - zone: 1 name: pz:Square Flute/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - preset: 24 name: Chorale bank: 0 num: 24 global_zone: name: pz:Chorale/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 17.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VOLENVATTACK: 2169.00 VOLENVRELEASE: 1586.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:Chorale/2 instrument: AAHS (index 224) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -2.00 - zone: 1 name: pz:Chorale/1 instrument: AAHS (index 224) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 2.00 - preset: 25 name: Breezy Calliope bank: 0 num: 25 global_zone: name: pz:Breezy Calliope/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 500.00 REVERBSEND: 80.00 MODLFOFREQ: -1586.00 VOLENVATTACK: -1200.00 zones: - zone: 0 name: pz:Breezy Calliope/3 instrument: AAHS (index 224) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 1 name: pz:Breezy Calliope/2 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Breezy Calliope/1 instrument: AAHS (index 224) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 26 name: D50-ish Bells bank: 0 num: 26 global_zone: name: pz:D50-ish Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 500.00 MODLFOFREQ: -1586.00 VOLENVATTACK: -1200.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:D50-ish Bells/2 instrument: BELLS (index 222) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: -617.00 - zone: 1 name: pz:D50-ish Bells/1 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: -3986.00 VOLENVRELEASE: 1902.00 - preset: 27 name: Bingo Bells bank: 0 num: 27 global_zone: name: pz:Bingo Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 25.00 CHORUSSEND: 300.00 MODLFOFREQ: 0.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Bingo Bells/2 instrument: BELLS (index 222) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 VOLENVATTACK: -3986.00 - zone: 1 name: pz:Bingo Bells/1 instrument: BELLS (index 222) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 VOLENVATTACK: -3986.00 FINETUNE: 1.00 - preset: 28 name: Electric Slap Bass bank: 0 num: 28 global_zone: name: pz:Electric Slap Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 200.00 REVERBSEND: 60.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3073.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Electric Slap Bass/2 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 VOLENVDECAY: 17.00 VOLENVRELEASE: 17.00 - zone: 1 name: pz:Electric Slap Bass/1 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -1200.00 FILTERQ: -110.00 ATTENUATION: 80.00 FINETUNE: -3.00 - preset: 29 name: Fantasy bank: 0 num: 29 global_zone: name: pz:Fantasy/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 50.00 MODLFOTOVOL: 80.00 CHORUSSEND: 300.00 MODLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Fantasy/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Fantasy/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 30 name: Vatican Pipes bank: 0 num: 30 global_zone: name: pz:Vatican Pipes/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 200.00 REVERBSEND: 400.00 MODLFOFREQ: -1200.00 VOLENVATTACK: -884.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Vatican Pipes/2 instrument: PIPES (index 221) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:Vatican Pipes/1 instrument: PIPES (index 221) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - preset: 31 name: Bass Dragon Choir bank: 0 num: 31 global_zone: name: pz:Bass Dragon Choir/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -110.00 MODLFOTOVOL: 16.00 CHORUSSEND: 500.00 MODLFOFREQ: -1586.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: 316.00 zones: - zone: 0 name: pz:Bass Dragon Choir/2 instrument: GRUNGE (index 220) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Bass Dragon Choir/1 instrument: GRUNGE (index 220) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 32 name: Cosmic Vibraphone bank: 0 num: 32 global_zone: name: pz:Cosmic Vibraphone/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 30.00 FILTERQ: -110.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 MODENVDELAY: -3986.00 MODENVATTACK: 0.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Cosmic Vibraphone/2 instrument: VIBRAPHONE (index 219) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Cosmic Vibraphone/1 instrument: VIBRAPHONE (index 219) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 33 name: Water Triangle bank: 0 num: 33 global_zone: name: pz:Water Triangle/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 80.00 VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 17.00 CHORUSSEND: 300.00 MODLFOFREQ: -386.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 919.00 zones: - zone: 0 name: pz:Water Triangle/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - preset: 34 name: Square Pop Flute bank: 0 num: 34 global_zone: name: pz:Square Pop Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -3500.00 MODLFOTOVOL: 40.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 MODLFOFREQ: -1200.00 MODENVDELAY: -2875.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVRELEASE: 2837.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: -884.00 zones: - zone: 0 name: pz:Square Pop Flute/1 instrument: SQUARE WAVE (index 231) key_range: 0 - 128 vel_range: 0 - 128 - preset: 35 name: Phasing Strings bank: 0 num: 35 global_zone: name: pz:Phasing Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1200.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Phasing Strings/2 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:Phasing Strings/1 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - preset: 36 name: Vatican Bell bank: 0 num: 36 global_zone: name: pz:Vatican Bell/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 300.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -884.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Vatican Bell/2 instrument: BELLS (index 222) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1902.00 FINETUNE: -2.00 - zone: 1 name: pz:Vatican Bell/1 instrument: PIPES (index 221) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 FINETUNE: 2.00 - preset: 37 name: Sine Whistle bank: 0 num: 37 global_zone: name: pz:Sine Whistle/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 60.00 FILTERQ: -110.00 MODLFOTOVOL: 35.00 CHORUSSEND: 200.00 MODLFOFREQ: -1200.00 VOLENVATTACK: 3986.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Sine Whistle/2 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Sine Whistle/1 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 38 name: Sine Bongos bank: 0 num: 38 global_zone: name: pz:Sine Bongos/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODLFOTOVOL: 15.00 CHORUSSEND: 150.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 zones: - zone: 0 name: pz:Sine Bongos/2 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 1 name: pz:Sine Bongos/1 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 39 name: Space Warp bank: 0 num: 39 global_zone: name: pz:Space Warp/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: 316.00 zones: - zone: 0 name: pz:Space Warp/2 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 FINETUNE: -1.00 - zone: 1 name: pz:Space Warp/1 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 FINETUNE: 1.00 - preset: 40 name: Aluminium Plate bank: 0 num: 40 global_zone: name: pz:Aluminium Plate/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -110.00 MODLFOTOVOL: 15.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VOLENVATTACK: -1200.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Aluminium Plate/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Aluminium Plate/1 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 41 name: Lead Synth 1 bank: 0 num: 41 global_zone: name: pz:Lead Synth 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 MODENVTOFILTERFC: -3716.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVDECAY: 1902.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1586.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Lead Synth 1/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:Lead Synth 1/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 42 name: Lead Synth 2 bank: 0 num: 42 global_zone: name: pz:Lead Synth 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: 110.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 1586.00 VOLENVRELEASE: 1586.00 zones: - zone: 0 name: pz:Lead Synth 2/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:Lead Synth 2/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 43 name: Lead Synth 3 bank: 0 num: 43 global_zone: name: pz:Lead Synth 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 1586.00 VOLENVRELEASE: 1586.00 zones: - zone: 0 name: pz:Lead Synth 3/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:Lead Synth 3/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: -12.00 FINETUNE: 3.00 - preset: 44 name: Casio VL-1 Pops bank: 0 num: 44 global_zone: name: pz:Casio VL-1 Pops/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 17.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 MODLFOFREQ: 0.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Casio VL-1 Pops/2 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Casio VL-1 Pops/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 45 name: Wailing Hit bank: 0 num: 45 global_zone: name: pz:Wailing Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 19.00 FILTERQ: -110.00 MODENVTOFILTERFC: -5100.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODLFOFREQ: -1067.00 VIBLFOFREQ: -261.00 MODENVDELAY: -3986.00 MODENVATTACK: -1200.00 MODENVHOLD: -3986.00 MODENVDECAY: -617.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 VOLENVRELEASE: 2786.00 zones: - zone: 0 name: pz:Wailing Hit/2 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Wailing Hit/1 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 46 name: Oink Grind bank: 0 num: 46 global_zone: name: pz:Oink Grind/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 200.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 zones: - zone: 0 name: pz:Oink Grind/1 instrument: OINK (index 215) key_range: 0 - 128 vel_range: 0 - 128 - preset: 47 name: Metallic Clink bank: 0 num: 47 global_zone: name: pz:Metallic Clink/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 zones: - zone: 0 name: pz:Metallic Clink/2 instrument: CLINK (index 216) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Metallic Clink/1 instrument: CLINK (index 216) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 48 name: Faerie Chorale bank: 0 num: 48 global_zone: name: pz:Faerie Chorale/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -746.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 3986.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Faerie Chorale/2 instrument: FAERIE CHORAL (index 213) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:Faerie Chorale/1 instrument: FAERIE CHORAL (index 213) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 - preset: 49 name: FM Bass Hit bank: 0 num: 49 global_zone: name: pz:FM Bass Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 60.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -386.00 zones: - zone: 0 name: pz:FM Bass Hit/1 instrument: HARSH FM BASS (index 212) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 - preset: 50 name: Dream Hit bank: 0 num: 50 global_zone: name: pz:Dream Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -7200.00 MODLFOTOVOL: 40.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 MODENVDELAY: -2786.00 MODENVATTACK: 2894.00 MODENVHOLD: -3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Dream Hit/1 instrument: FM BELL 2 (index 211) key_range: 0 - 128 vel_range: 0 - 128 - preset: 51 name: Gated FM Bass bank: 0 num: 51 global_zone: name: pz:Gated FM Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 MODENVDELAY: -3986.00 MODENVATTACK: -3284.00 MODENVHOLD: -3986.00 MODENVRELEASE: 3369.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Gated FM Bass/1 instrument: HARSH FM BASS (index 212) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 - preset: 52 name: Harsh FM Bass bank: 0 num: 52 global_zone: name: pz:Harsh FM Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 MODENVDELAY: -3986.00 MODENVATTACK: 1902.00 MODENVHOLD: -3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 260.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Harsh FM Bass/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 700.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 - zone: 1 name: pz:Harsh FM Bass/2 instrument: HARSH FM BASS (index 212) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - zone: 2 name: pz:Harsh FM Bass/1 instrument: HARSH FM BASS (index 212) key_range: 0 - 128 vel_range: 0 - 128 - preset: 53 name: Singing Bells bank: 0 num: 53 global_zone: name: pz:Singing Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 20.00 FILTERQ: -110.00 MODLFOTOVOL: 40.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Singing Bells/1 instrument: FM BELL 2 (index 211) key_range: 0 - 128 vel_range: 0 - 128 - preset: 54 name: Phasing Choir bank: 0 num: 54 global_zone: name: pz:Phasing Choir/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -40.00 FILTERQ: -110.00 MODLFOTOFILTERFC: 747.00 MODLFOTOVOL: 20.00 CHORUSSEND: 220.00 MODLFOFREQ: -884.00 VIBLFOFREQ: -884.00 VOLENVDELAY: 2400.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Phasing Choir/2 instrument: FM3 (index 210) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:Phasing Choir/1 instrument: FM3 (index 210) key_range: 0 - 128 vel_range: 0 - 128 - preset: 55 name: FM Clang bank: 0 num: 55 global_zone: name: pz:FM Clang/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 25.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:FM Clang/1 instrument: FM CLANG (index 209) key_range: 0 - 128 vel_range: 0 - 128 - preset: 56 name: China Voices bank: 0 num: 56 global_zone: name: pz:China Voices/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -600.00 MODLFOTOVOL: 25.00 CHORUSSEND: 200.00 MODLFOFREQ: 0.00 MODENVDELAY: -2084.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVRELEASE: 1902.00 COARSETUNE: 6.00 zones: - zone: 0 name: pz:China Voices/2 instrument: FM CLANG (index 209) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - zone: 1 name: pz:China Voices/1 instrument: FM CLANG (index 209) key_range: 0 - 128 vel_range: 0 - 128 - preset: 57 name: Rubber Bass bank: 0 num: 57 global_zone: name: pz:Rubber Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVRELEASE: -2084.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Rubber Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -5.00 - zone: 1 name: pz:Rubber Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 5.00 - preset: 58 name: Polysynth Warp bank: 0 num: 58 global_zone: name: pz:Polysynth Warp/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 MODENVTOFILTERFC: -3716.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -386.00 MODENVDELAY: -1586.00 MODENVATTACK: -3986.00 MODENVHOLD: -3986.00 MODENVDECAY: -3284.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2786.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Polysynth Warp/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - zone: 1 name: pz:Polysynth Warp/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 59 name: Wavepad bank: 0 num: 59 global_zone: name: pz:Wavepad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 40.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 1586.00 VOLENVRELEASE: 1586.00 zones: - zone: 0 name: pz:Wavepad/3 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -4500.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 0.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6602.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 3102.00 VOLENVRELEASE: 3102.00 - zone: 1 name: pz:Wavepad/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -884.00 FILTERQ: 180.00 MODENVTOFILTERFC: -3000.00 MODENVDELAY: -7973.00 MODENVATTACK: 2400.00 MODENVHOLD: -7973.00 MODENVDECAY: 2786.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6663.00 VOLENVHOLD: -7973.00 - zone: 2 name: pz:Wavepad/1 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: 2400.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 - preset: 60 name: Pop Bass 2 bank: 0 num: 60 global_zone: name: pz:Pop Bass 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 20.00 CHORUSSEND: 400.00 REVERBSEND: 0.00 MODLFOFREQ: -617.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 702.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Pop Bass 2/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 - zone: 1 name: pz:Pop Bass 2/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 2 name: pz:Pop Bass 2/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 61 name: Stacked Mega-Phaser bank: 0 num: 61 global_zone: name: pz:Stacked Mega-Phaser/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Stacked Mega-Phaser/4 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: 1046.00 FINETUNE: -4.00 - zone: 1 name: pz:Stacked Mega-Phaser/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -2.00 - zone: 2 name: pz:Stacked Mega-Phaser/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 2.00 - zone: 3 name: pz:Stacked Mega-Phaser/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: -1344.00 FINETUNE: 3.00 - preset: 62 name: Screaming Wavepad bank: 0 num: 62 global_zone: name: pz:Screaming Wavepad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 VIBLFOTOPITCH: 10.00 MODLFOTOVOL: 40.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVDECAY: 2786.00 VOLENVRELEASE: 2786.00 zones: - zone: 0 name: pz:Screaming Wavepad/2 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -3500.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 0.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6602.00 VOLENVHOLD: -7973.00 FINETUNE: -1.00 - zone: 1 name: pz:Screaming Wavepad/1 instrument: AAA (index 214) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -3500.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 0.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6602.00 VOLENVHOLD: -7973.00 FINETUNE: 0.00 - preset: 63 name: Melodic Vibrato bank: 0 num: 63 global_zone: name: pz:Melodic Vibrato/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 38.00 FILTERQ: -180.00 MODLFOTOVOL: 18.00 CHORUSSEND: 500.00 MODLFOFREQ: 316.00 VIBLFOFREQ: -365.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 643.00 zones: - zone: 0 name: pz:Melodic Vibrato/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 - preset: 64 name: Twips Ring bank: 0 num: 64 global_zone: name: pz:Twips Ring/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERFC: -2786.00 FILTERQ: 110.00 MODENVTOFILTERFC: 7200.00 MODLFOTOVOL: 30.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVATTACK: -884.00 MODENVHOLD: -7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 zones: - zone: 0 name: pz:Twips Ring/3 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 VOLENVATTACK: 2067.00 VOLENVDECAY: 3600.00 VOLENVRELEASE: 1200.00 - zone: 1 name: pz:Twips Ring/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -3.00 - zone: 2 name: pz:Twips Ring/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 3.00 - preset: 65 name: Wah bank: 0 num: 65 global_zone: name: pz:Wah/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERFC: -3671.00 FILTERQ: 100.00 MODENVTOFILTERFC: 7200.00 MODLFOTOVOL: 30.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: -884.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 0.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Wah/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Wah/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 66 name: Blistering Bells bank: 0 num: 66 global_zone: name: pz:Blistering Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 58.00 FILTERQ: 180.00 MODLFOTOVOL: 58.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 MODLFOFREQ: -884.00 VIBLFOFREQ: -365.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -365.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Blistering Bells/2 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -2.00 - zone: 1 name: pz:Blistering Bells/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 2.00 - preset: 67 name: Click Pops bank: 0 num: 67 global_zone: name: pz:Click Pops/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 REVERBSEND: 20.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -5186.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5186.00 zones: - zone: 0 name: pz:Click Pops/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 300.00 REVERBSEND: 40.00 VOLENVDECAY: -3284.00 VOLENVRELEASE: -3284.00 COARSETUNE: -20.00 - zone: 1 name: pz:Click Pops/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 - preset: 68 name: Xylophone bank: 0 num: 68 global_zone: name: pz:Xylophone/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 30.00 FILTERQ: -180.00 MODENVTOFILTERFC: -6000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: 0.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 814.00 zones: - zone: 0 name: pz:Xylophone/2 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -2.00 - zone: 1 name: pz:Xylophone/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 2.00 - preset: 69 name: New Life bank: 0 num: 69 global_zone: name: pz:New Life/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 500.00 REVERBSEND: 100.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -746.00 zones: - zone: 0 name: pz:New Life/2 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:New Life/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 70 name: Long Bass bank: 0 num: 70 global_zone: name: pz:Long Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 500.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: pz:Long Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -4604.00 - zone: 1 name: pz:Long Bass/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 - preset: 71 name: Banshee Pad bank: 0 num: 71 global_zone: name: pz:Banshee Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 0.00 VIBLFOTOPITCH: 140.00 FILTERQ: -180.00 MODLFOTOVOL: 30.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VIBLFOFREQ: -386.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 4688.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Banshee Pad/2 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 1 name: pz:Banshee Pad/1 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 72 name: Undulating Pad bank: 0 num: 72 global_zone: name: pz:Undulating Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 0.00 FILTERQ: 100.00 MODLFOTOFILTERFC: 1675.00 MODLFOTOVOL: 30.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Undulating Pad/2 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - zone: 1 name: pz:Undulating Pad/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - preset: 73 name: Spudge Bass bank: 0 num: 73 global_zone: name: pz:Spudge Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -6000.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3102.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3102.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 5728.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -386.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Spudge Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Spudge Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 5.00 - preset: 74 name: Warble Pad bank: 0 num: 74 global_zone: name: pz:Warble Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 100.00 MODLFOTOFILTERFC: 2380.00 MODLFOTOVOL: 50.00 CHORUSSEND: 500.00 MODLFOFREQ: -1200.00 VIBLFOFREQ: -386.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 4688.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Warble Pad/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: -1344.00 VOLENVATTACK: -365.00 VOLENVDECAY: 3369.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 FINETUNE: 3.00 - zone: 1 name: pz:Warble Pad/1 instrument: FM BELL 2 (index 211) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 856.00 - preset: 75 name: Yazoo Blips bank: 0 num: 75 global_zone: name: pz:Yazoo Blips/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1544.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1544.00 ATTENUATION: 24.00 zones: - zone: 0 name: pz:Yazoo Blips/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: 4379.00 VOLENVDECAY: -856.00 VOLENVRELEASE: 330.00 - zone: 1 name: pz:Yazoo Blips/1 instrument: OINK (index 215) key_range: 0 - 128 vel_range: 0 - 128 - preset: 76 name: TX81Z Sqncr Bass bank: 0 num: 76 global_zone: name: pz:TX81Z Sqncr Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 500.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: pz:TX81Z Sqncr Bass/2 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -7200.00 CHORUSSEND: 560.00 MODENVDELAY: -7973.00 MODENVATTACK: -1544.00 MODENVHOLD: -7973.00 - zone: 1 name: pz:TX81Z Sqncr Bass/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 700.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 - preset: 77 name: Acid Sub Bass bank: 0 num: 77 global_zone: name: pz:Acid Sub Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 40.00 MODLFOTOFILTERFC: 7200.00 MODENVTOFILTERFC: -5500.00 CHORUSSEND: 400.00 REVERBSEND: 0.00 MODLFOFREQ: -746.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 454.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -498.00 zones: - zone: 0 name: pz:Acid Sub Bass/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 - zone: 1 name: pz:Acid Sub Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 2 name: pz:Acid Sub Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 78 name: Echo Pop Bass bank: 0 num: 78 global_zone: name: pz:Echo Pop Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODLFOTOFILTERFC: 7200.00 MODENVTOFILTERFC: -5500.00 CHORUSSEND: 400.00 REVERBSEND: 0.00 MODLFOFREQ: -884.00 MODENVDELAY: -3986.00 MODENVATTACK: -3986.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 814.00 zones: - zone: 0 name: pz:Echo Pop Bass/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 400.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 - zone: 1 name: pz:Echo Pop Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 2 name: pz:Echo Pop Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 79 name: Acid Bass 2 bank: 0 num: 79 global_zone: name: pz:Acid Bass 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODLFOTOFILTERFC: -7200.00 MODENVTOFILTERFC: -5800.00 CHORUSSEND: 400.00 REVERBSEND: 0.00 MODLFODELAY: 7973.00 MODLFOFREQ: -1035.00 MODENVDELAY: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Acid Bass 2/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Acid Bass 2/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 80 name: Monster Stack Lead bank: 0 num: 80 global_zone: name: pz:Monster Stack Lead/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: 100.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Monster Stack Lead/4 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -4871.00 MODENVTOFILTERFC: 7200.00 MODENVDELAY: -7973.00 MODENVATTACK: 17.00 MODENVHOLD: -7973.00 MODENVDECAY: -7973.00 MODENVRELEASE: 702.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 - zone: 1 name: pz:Monster Stack Lead/3 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 500.00 COARSETUNE: -12.00 - zone: 2 name: pz:Monster Stack Lead/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - zone: 3 name: pz:Monster Stack Lead/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: -793.00 FINETUNE: 1.00 - preset: 81 name: Panned Stack bank: 0 num: 81 global_zone: name: pz:Panned Stack/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 zones: - zone: 0 name: pz:Panned Stack/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -2786.00 MODENVTOFILTERFC: 3000.00 MODLFOTOVOL: 240.00 PAN: -1000.00 MODLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: 3369.00 MODENVHOLD: -7973.00 - zone: 1 name: pz:Panned Stack/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -3000.00 MODLFOTOVOL: -240.00 PAN: 1000.00 MODLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: 3102.00 MODENVHOLD: -7973.00 - preset: 82 name: Ethnic Bow bank: 0 num: 82 zones: - zone: 0 name: pz:Ethnic Bow/0 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -2786.00 MODENVTOFILTERFC: 3000.00 MODLFOTOVOL: 240.00 CHORUSSEND: 400.00 REVERBSEND: 80.00 MODLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: 3369.00 MODENVHOLD: -7973.00 - preset: 83 name: Noisy Pops bank: 0 num: 83 global_zone: name: pz:Noisy Pops/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 MODLFOTOVOL: 17.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 MODLFOFREQ: 0.00 VIBLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 zones: - zone: 0 name: pz:Noisy Pops/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 700.00 REVERBSEND: 100.00 VOLENVDECAY: -617.00 VOLENVRELEASE: -617.00 COARSETUNE: -12.00 - zone: 1 name: pz:Noisy Pops/1 instrument: SMOOTH SQUARE (index 217) key_range: 0 - 128 vel_range: 0 - 128 - preset: 84 name: House Organ bank: 0 num: 84 global_zone: name: pz:House Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 10.00 CHORUSSEND: 400.00 MODLFOFREQ: -2084.00 VIBLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: -2084.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:House Organ/4 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 VOLENVATTACK: 2786.00 COARSETUNE: -12.00 - zone: 1 name: pz:House Organ/3 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 REVERBSEND: 140.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 FINETUNE: 3.00 - zone: 2 name: pz:House Organ/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: 2786.00 ATTENUATION: 84.00 FINETUNE: 0.00 - zone: 3 name: pz:House Organ/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: 2786.00 ATTENUATION: 84.00 FINETUNE: 1.00 - preset: 85 name: Bonky Organ bank: 0 num: 85 global_zone: name: pz:Bonky Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 0.00 VIBLFOTOPITCH: 60.00 FILTERQ: -180.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -2084.00 VIBLFOFREQ: -386.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Bonky Organ/3 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: REVERBSEND: 40.00 VOLENVATTACK: -7973.00 ATTENUATION: 40.00 COARSETUNE: -12.00 - zone: 1 name: pz:Bonky Organ/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 VOLENVATTACK: -7973.00 ATTENUATION: 40.00 FINETUNE: -2.00 - zone: 2 name: pz:Bonky Organ/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 VOLENVATTACK: -7973.00 ATTENUATION: 40.00 FINETUNE: 2.00 - preset: 86 name: Sustained Harp bank: 0 num: 86 global_zone: name: pz:Sustained Harp/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 19.00 MODLFOTOVOL: 9.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: 2098.00 VIBLFOFREQ: 1586.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 2067.00 zones: - zone: 0 name: pz:Sustained Harp/2 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Sustained Harp/1 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 87 name: Awesome Strings bank: 0 num: 87 global_zone: name: pz:Awesome Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 19.00 FILTERFC: -4373.00 FILTERQ: -180.00 MODLFOTOVOL: 9.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: 2098.00 VIBLFOFREQ: 1586.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6386.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Awesome Strings/3 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Awesome Strings/2 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 2 name: pz:Awesome Strings/1 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 88 name: Rough Strings bank: 0 num: 88 global_zone: name: pz:Rough Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 7973.00 FILTERQ: -180.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 4688.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Rough Strings/2 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Rough Strings/1 instrument: SMOOTH SAW (index 207) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 89 name: Techno Bells bank: 0 num: 89 global_zone: name: pz:Techno Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -1200.00 VIBLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1586.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:Techno Bells/4 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 ATTENUATION: 80.00 COARSETUNE: -12.00 - zone: 1 name: pz:Techno Bells/3 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 REVERBSEND: 140.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 FINETUNE: 3.00 - zone: 2 name: pz:Techno Bells/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 VOLENVATTACK: 3986.00 ATTENUATION: 120.00 FINETUNE: -1.00 - zone: 3 name: pz:Techno Bells/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 VOLENVATTACK: 3986.00 ATTENUATION: 120.00 FINETUNE: 1.00 - preset: 90 name: Dreamy Pad bank: 0 num: 90 global_zone: name: pz:Dreamy Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 5888.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:Dreamy Pad/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 MODENVTOFILTERFC: -3526.00 VIBLFOFREQ: -1200.00 MODENVATTACK: 2565.00 MODENVHOLD: -7973.00 MODENVDECAY: 2014.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 1902.00 FINETUNE: 0.00 - zone: 1 name: pz:Dreamy Pad/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 MODENVTOFILTERFC: -3426.00 VIBLFOFREQ: -1035.00 MODENVATTACK: 2854.00 MODENVHOLD: -7973.00 MODENVDECAY: 2484.00 MODENVSUSTAIN: 1000.00 FINETUNE: 1.00 - preset: 91 name: Breath Pad bank: 0 num: 91 zones: - zone: 0 name: pz:Breath Pad/2 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -7200.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 MODENVDELAY: 1902.00 MODENVATTACK: 1200.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 6386.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 5186.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 - zone: 1 name: pz:Breath Pad/1 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 20.00 MODENVTOFILTERFC: -100.00 MODLFOTOVOL: 30.00 CHORUSSEND: 600.00 MODLFOFREQ: -1586.00 VIBLFOFREQ: -617.00 MODENVATTACK: 2400.00 VOLENVATTACK: 6429.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 FINETUNE: 1.00 - zone: 2 name: pz:Breath Pad/0 instrument: HALF RECTIFICATION (index 218) key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 20.00 MODENVTOFILTERFC: -100.00 MODLFOTOVOL: 40.00 CHORUSSEND: 600.00 MODLFOFREQ: -1586.00 VIBLFOFREQ: -593.00 MODENVATTACK: 1902.00 VOLENVATTACK: 6386.00 VOLENVDECAY: 3600.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 FINETUNE: -1.00 - preset: 92 name: Warehouse Percussion bank: 0 num: 92 global_zone: name: pz:Warehouse Percussion/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -180.00 MODLFOTOVOL: 15.00 CHORUSSEND: 800.00 REVERBSEND: 160.00 MODLFOFREQ: -1586.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 zones: - zone: 0 name: pz:Warehouse Percussion/2 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Warehouse Percussion/1 instrument: BREEZE (index 223) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -1.00 - preset: 93 name: Sawtooth Hit bank: 0 num: 93 global_zone: name: pz:Sawtooth Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 100.00 CHORUSSEND: 500.00 REVERBSEND: 100.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -7973.00 VOLENVSUSTAIN: 960.00 zones: - zone: 0 name: pz:Sawtooth Hit/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Sawtooth Hit/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -10.00 - zone: 2 name: pz:Sawtooth Hit/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 10.00 - preset: 94 name: Oingo-Boingo bank: 0 num: 94 global_zone: name: pz:Oingo-Boingo/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -110.00 MODLFOTOVOL: 30.00 CHORUSSEND: 400.00 MODLFOFREQ: -1200.00 VIBLFOFREQ: -884.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -365.00 FINETUNE: 0.00 zones: - zone: 0 name: pz:Oingo-Boingo/3 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 REVERBSEND: 140.00 PAN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 FINETUNE: 1.00 - zone: 1 name: pz:Oingo-Boingo/2 instrument: SINE WAVE (index 232) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 COARSETUNE: -12.00 - zone: 2 name: pz:Oingo-Boingo/1 instrument: FM 1 (index 237) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 600.00 REVERBSEND: 140.00 PAN: -1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 - preset: 95 name: Yazoo Zips bank: 0 num: 95 global_zone: name: pz:Yazoo Zips/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: 100.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 MODENVDELAY: -856.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: -7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Yazoo Zips/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Yazoo Zips/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Yazoo Zips/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 96 name: Yazoo Bass Hit bank: 0 num: 96 global_zone: name: pz:Yazoo Bass Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -180.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Yazoo Bass Hit/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Yazoo Bass Hit/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: -12.00 - zone: 2 name: pz:Yazoo Bass Hit/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 97 name: Zippy Bass bank: 0 num: 97 global_zone: name: pz:Zippy Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -180.00 MODENVTOFILTERFC: -5500.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVATTACK: -1544.00 MODENVHOLD: -7973.00 MODENVDECAY: 5222.00 MODENVRELEASE: 6296.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: pz:Zippy Bass/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: -1.00 - zone: 1 name: pz:Zippy Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDECAY: -386.00 VOLENVRELEASE: -386.00 COARSETUNE: -12.00 - zone: 2 name: pz:Zippy Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 98 name: Thunder bank: 0 num: 98 global_zone: name: pz:Thunder/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODLFOTOFILTERFC: 4202.00 PAN: 300.00 MODLFOFREQ: -2084.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: 1902.00 VOLENVRELEASE: 2786.00 COARSETUNE: -34.00 zones: - zone: 0 name: pz:Thunder/3 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 40.00 MODLFOFREQ: -1586.00 - zone: 1 name: pz:Thunder/2 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: -40.00 PAN: 1000.00 FINETUNE: 1.00 - zone: 2 name: pz:Thunder/1 instrument: WHITENOISE 3 (index 229) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 40.00 PAN: -1000.00 FINETUNE: -1.00 - preset: 99 name: Killer Bass bank: 0 num: 99 global_zone: name: pz:Killer Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -110.00 MODENVTOFILTERFC: -6500.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -884.00 MODENVATTACK: 2983.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1902.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Killer Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Killer Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 100 name: Stab Bass Strings bank: 0 num: 100 global_zone: name: pz:Stab Bass Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 3986.00 FILTERQ: -180.00 MODENVTOFILTERFC: -7200.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVATTACK: 702.00 MODENVHOLD: -7973.00 MODENVDECAY: 3986.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1902.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1902.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Stab Bass Strings/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Stab Bass Strings/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 101 name: Monster Strings bank: 0 num: 101 global_zone: name: pz:Monster Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -4169.00 FILTERQ: -180.00 MODLFOTOVOL: 10.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1902.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 1902.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Monster Strings/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -10.00 - zone: 1 name: pz:Monster Strings/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 10.00 - preset: 102 name: Cheesy Pad bank: 0 num: 102 global_zone: name: pz:Cheesy Pad/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: 180.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 3986.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Cheesy Pad/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 1.00 - zone: 1 name: pz:Cheesy Pad/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 103 name: New Life 2 bank: 0 num: 103 global_zone: name: pz:New Life 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 38.00 FILTERQ: -180.00 MODLFOTOVOL: 18.00 CHORUSSEND: 500.00 REVERBSEND: 160.00 MODLFOFREQ: 316.00 VIBLFOFREQ: -365.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 zones: - zone: 0 name: pz:New Life 2/2 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:New Life 2/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 104 name: Mono Analog Bass bank: 0 num: 104 global_zone: name: pz:Mono Analog Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -4000.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -2084.00 MODENVHOLD: -7973.00 MODENVRELEASE: 3641.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Mono Analog Bass/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Mono Analog Bass/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 105 name: Polysynth Hit bank: 0 num: 105 global_zone: name: pz:Polysynth Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -2000.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -2084.00 MODENVHOLD: -7973.00 MODENVRELEASE: 3641.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Polysynth Hit/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -5.00 - zone: 1 name: pz:Polysynth Hit/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 5.00 - preset: 106 name: Venus Violin Hit bank: 0 num: 106 global_zone: name: pz:Venus Violin Hit/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 FILTERQ: -180.00 MODLFOTOVOL: 25.00 CHORUSSEND: 400.00 REVERBSEND: 100.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -617.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 1200.00 VOLENVRELEASE: -1200.00 zones: - zone: 0 name: pz:Venus Violin Hit/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: -2.00 - zone: 1 name: pz:Venus Violin Hit/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 FINETUNE: 2.00 - preset: 107 name: Ping-Pong bank: 0 num: 107 global_zone: name: pz:Ping-Pong/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 40.00 FILTERQ: -180.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 10.00 CHORUSSEND: 400.00 MODLFOFREQ: -2084.00 VIBLFOFREQ: -884.00 MODENVDELAY: -7973.00 MODENVATTACK: -2786.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1200.00 zones: - zone: 0 name: pz:Ping-Pong/2 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 VOLENVDELAY: 595.00 FINETUNE: -1.00 - zone: 1 name: pz:Ping-Pong/1 instrument: ADDITIVE 3 (index 234) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 FINETUNE: 1.00 - preset: 108 name: Polysynth 2 bank: 0 num: 108 global_zone: name: pz:Polysynth 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 20.00 FILTERQ: -180.00 MODENVTOFILTERFC: -2000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -884.00 MODENVHOLD: -7973.00 MODENVRELEASE: 6922.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 zones: - zone: 0 name: pz:Polysynth 2/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 300.00 FINETUNE: -3.00 - zone: 1 name: pz:Polysynth 2/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - preset: 109 name: Cheap Synth bank: 0 num: 109 global_zone: name: pz:Cheap Synth/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 10.00 FILTERQ: 180.00 CHORUSSEND: 400.00 VIBLFOFREQ: -365.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -884.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1687.00 zones: - zone: 0 name: pz:Cheap Synth/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 - preset: 110 name: Heavy Square bank: 0 num: 110 global_zone: name: pz:Heavy Square/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVRELEASE: 702.00 zones: - zone: 0 name: pz:Heavy Square/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVRELEASE: 1902.00 FINETUNE: -2.00 - zone: 1 name: pz:Heavy Square/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVRELEASE: 1902.00 FINETUNE: 2.00 - zone: 2 name: pz:Heavy Square/1 instrument: SQUARE WAVE (index 231) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - preset: 111 name: Distorted Lead bank: 0 num: 111 global_zone: name: pz:Distorted Lead/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 MODENVTOFILTERFC: -3426.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODENVDELAY: -7973.00 MODENVATTACK: -365.00 MODENVHOLD: -7973.00 MODENVDECAY: 1200.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 1902.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1586.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Distorted Lead/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 3.00 - zone: 1 name: pz:Distorted Lead/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 1000.00 MODENVDELAY: 595.00 VOLENVDECAY: 2119.00 VOLENVRELEASE: 1843.00 - zone: 2 name: pz:Distorted Lead/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -3.00 - preset: 112 name: Singing Strings bank: 0 num: 112 global_zone: name: pz:Singing Strings/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 1200.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Singing Strings/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -5.00 - zone: 1 name: pz:Singing Strings/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 5.00 - zone: 2 name: pz:Singing Strings/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 20.00 - preset: 113 name: Warble bank: 0 num: 113 global_zone: name: pz:Warble/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 110.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVDECAY: 1902.00 VOLENVRELEASE: 1586.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Warble/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 215.00 MODENVTOPITCH: 1341.00 CHORUSSEND: 1000.00 MODLFOFREQ: 7414.00 MODENVATTACK: 4263.00 MODENVRELEASE: 7973.00 VOLENVATTACK: 17.00 - zone: 1 name: pz:Warble/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOFILTERFC: -1738.00 CHORUSSEND: 1000.00 MODENVDELAY: 595.00 VOLENVDECAY: 2565.00 VOLENVRELEASE: 2356.00 - zone: 2 name: pz:Warble/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -1639.00 MODLFOTOFILTERFC: 3625.00 MODLFOTOVOL: 80.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 - preset: 114 name: Wind Blast bank: 0 num: 114 global_zone: name: pz:Wind Blast/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: 180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 300.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -365.00 MODENVHOLD: -7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 7973.00 VOLENVRELEASE: 7973.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Wind Blast/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - zone: 1 name: pz:Wind Blast/1 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - preset: 115 name: Laser Pops bank: 0 num: 115 global_zone: name: pz:Laser Pops/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: 180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 600.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Laser Pops/3 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVDECAY: -2786.00 VOLENVRELEASE: -1586.00 - zone: 1 name: pz:Laser Pops/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - zone: 2 name: pz:Laser Pops/1 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - preset: 116 name: Warbling Bird 1 bank: 0 num: 116 global_zone: name: pz:Warbling Bird 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: -180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 600.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Warbling Bird 1/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -1639.00 MODLFOTOFILTERFC: 3625.00 MODLFOTOVOL: 80.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 - preset: 117 name: Warbling Bird 2 bank: 0 num: 117 global_zone: name: pz:Warbling Bird 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: 180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 600.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Warbling Bird 2/2 instrument: WHITENOISE 0 (index 235) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 7973.00 VOLENVDECAY: -1586.00 VOLENVRELEASE: -884.00 - zone: 1 name: pz:Warbling Bird 2/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 60.00 MODENVTOPITCH: 1639.00 MODLFOTOFILTERFC: 7200.00 MODLFOTOVOL: 80.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 - preset: 118 name: Wind Down bank: 0 num: 118 global_zone: name: pz:Wind Down/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: -7973.00 FILTERQ: 180.00 MODENVTOFILTERFC: 7200.00 CHORUSSEND: 600.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Wind Down/2 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -1639.00 PAN: 1000.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 FINETUNE: -2.00 - zone: 1 name: pz:Wind Down/1 instrument: MELODIC VIBRATO (index 208) key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: -1639.00 PAN: -1000.00 MODENVATTACK: 7556.00 MODENVRELEASE: 7973.00 FINETUNE: 2.00 - preset: 119 name: Delicate Bells bank: 0 num: 119 global_zone: name: pz:Delicate Bells/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 5.00 CHORUSSEND: 300.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 7088.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 6386.00 COARSETUNE: 3.00 zones: - zone: 0 name: pz:Delicate Bells/2 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -1.00 - zone: 1 name: pz:Delicate Bells/1 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 1.00 - preset: 120 name: Delicate Marimba bank: 0 num: 120 global_zone: name: pz:Delicate Marimba/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -4000.00 CHORUSSEND: 300.00 MODENVDELAY: -7973.00 MODENVATTACK: 3804.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 5888.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 5888.00 COARSETUNE: 3.00 zones: - zone: 0 name: pz:Delicate Marimba/2 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 1000.00 - zone: 1 name: pz:Delicate Marimba/1 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: -1000.00 - preset: 121 name: Woody Bass bank: 0 num: 121 global_zone: name: pz:Woody Bass/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 MODENVTOFILTERFC: -5500.00 CHORUSSEND: 300.00 MODENVATTACK: 2400.00 MODENVRELEASE: 7312.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 7973.00 VOLENVRELEASE: 6155.00 COARSETUNE: 3.00 zones: - zone: 0 name: pz:Woody Bass/2 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: -2.00 - zone: 1 name: pz:Woody Bass/1 instrument: ADDITIVE BELL (index 206) key_range: 0 - 128 vel_range: 0 - 128 generators: FINETUNE: 2.00 - preset: 122 name: Filtered Stack bank: 0 num: 122 global_zone: name: pz:Filtered Stack/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 10.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 MODLFOFREQ: -617.00 VOLENVDELAY: -7973.00 VOLENVATTACK: 1209.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVRELEASE: 2400.00 zones: - zone: 0 name: pz:Filtered Stack/4 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: -10.00 FILTERQ: 180.00 MODLFOTOFILTERFC: 80.00 MODENVTOFILTERFC: -2334.00 MODENVDELAY: 2218.00 MODENVATTACK: 1365.00 MODENVHOLD: -7973.00 MODENVDECAY: 1837.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6198.00 FINETUNE: -2.00 - zone: 1 name: pz:Filtered Stack/3 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 MODENVTOFILTERFC: -6500.00 MODENVDELAY: 1783.00 MODENVATTACK: 1018.00 MODENVHOLD: -7973.00 MODENVDECAY: 2817.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6198.00 FINETUNE: -2.00 - zone: 2 name: pz:Filtered Stack/2 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 40.00 MODENVTOFILTERFC: -6500.00 MODENVDELAY: 1200.00 MODENVATTACK: 1268.00 MODENVHOLD: -7973.00 MODENVDECAY: 1902.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6198.00 FINETUNE: 2.00 - zone: 3 name: pz:Filtered Stack/1 instrument: SAWTOOTH (index 233) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 100.00 MODENVTOFILTERFC: -5214.00 MODENVDELAY: 2404.00 MODENVATTACK: 316.00 MODENVHOLD: -7973.00 MODENVDECAY: 2246.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 6164.00 FINETUNE: 3.00 - preset: 123 name: TX81Z Sqncr Bass 2 bank: 0 num: 123 global_zone: name: pz:TX81Z Sqncr Bass 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 500.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: pz:TX81Z Sqncr Bass 2/2 instrument: DM 2 (index 227) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 - zone: 1 name: pz:TX81Z Sqncr Bass 2/1 instrument: ELECTRIC BASS (index 225) key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -180.00 CHORUSSEND: 700.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 - preset: 124 name: New Age Organ bank: 0 num: 124 global_zone: name: pz:New Age Organ/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: -110.00 CHORUSSEND: 200.00 MODENVDELAY: -3986.00 MODENVATTACK: 0.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: 1902.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVRELEASE: 1586.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:New Age Organ/2 instrument: Instrument3 (index 4) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: -9.00 MODLFOTOVOL: -17.00 MODLFOFREQ: -1004.00 FINETUNE: -3.00 - zone: 1 name: pz:New Age Organ/1 instrument: Instrument3 (index 4) key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 10.00 MODLFOTOVOL: 20.00 MODLFOFREQ: -884.00 - preset: 125 name: Gated Screamer bank: 0 num: 125 global_zone: name: pz:Gated Screamer/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 60.00 FILTERFC: -4604.00 FILTERQ: -180.00 MODENVTOFILTERFC: 7200.00 MODLFOTOVOL: 20.00 CHORUSSEND: 300.00 REVERBSEND: 40.00 MODLFOFREQ: -617.00 VIBLFOFREQ: -1200.00 MODENVDELAY: -7973.00 MODENVATTACK: -7973.00 MODENVHOLD: -7973.00 MODENVDECAY: -7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -7973.00 VOLENVRELEASE: 1200.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Gated Screamer/1 instrument: Instrument9 (index 3) key_range: 0 - 128 vel_range: 0 - 128 - preset: 126 name: Wonderland Xylo bank: 0 num: 126 global_zone: name: pz:Wonderland Xylo/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 30.00 FILTERQ: -110.00 MODENVTOFILTERFC: -5000.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: -884.00 MODENVDELAY: -3986.00 MODENVATTACK: 0.00 MODENVDECAY: 3986.00 MODENVRELEASE: 3986.00 VOLENVDELAY: -3986.00 VOLENVATTACK: -3986.00 VOLENVHOLD: -3986.00 VOLENVDECAY: 2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 702.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Wonderland Xylo/2 instrument: Instrument15 (index 1) key_range: 0 - 128 vel_range: 0 - 128 generators: PAN: 200.00 - zone: 1 name: pz:Wonderland Xylo/1 instrument: Instrument16 (index 2) key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 400.00 PAN: -200.00 - preset: 127 name: Space Flute bank: 0 num: 127 global_zone: name: pz:Space Flute/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOFILTERFC: -4000.00 CHORUSSEND: 300.00 REVERBSEND: 60.00 MODENVDELAY: -7973.00 MODENVATTACK: 3804.00 MODENVHOLD: -7973.00 MODENVDECAY: 7973.00 MODENVRELEASE: 7973.00 VOLENVDELAY: -7973.00 VOLENVATTACK: -2702.00 VOLENVHOLD: -7973.00 VOLENVDECAY: 5888.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 COARSETUNE: -12.00 zones: - zone: 0 name: pz:Space Flute/1 instrument: Instrument8 (index 0) key_range: 0 - 128 vel_range: 0 - 128 - preset: 128 name: TR-101 Drumset bank: 128 num: 0 zones: - zone: 0 name: pz:TR-101 Drumset/33 instrument: 101 LASER 3 (index 172) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:TR-101 Drumset/32 instrument: 101 LASER 2 (index 173) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:TR-101 Drumset/31 instrument: 101 LASER 1 (index 174) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:TR-101 Drumset/30 instrument: 101 MARACAS (index 175) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:TR-101 Drumset/29 instrument: 101 SHAKER (index 176) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:TR-101 Drumset/28 instrument: 101 BONGO LO (index 177) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:TR-101 Drumset/27 instrument: 101 BONGO MID (index 178) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:TR-101 Drumset/26 instrument: 101 BONGO HI (index 179) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:TR-101 Drumset/25 instrument: 101 COWBELL (index 180) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:TR-101 Drumset/24 instrument: 101 TAMBOURINE (index 181) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:TR-101 Drumset/23 instrument: 101 HI TOM 2 (index 182) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:TR-101 Drumset/22 instrument: 101 CYMBAL (index 183) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:TR-101 Drumset/21 instrument: 101 HI TOM 1 (index 184) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:TR-101 Drumset/20 instrument: 101 MID TOM 2 (index 185) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:TR-101 Drumset/19 instrument: 101 HAT OPEN (index 186) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:TR-101 Drumset/18 instrument: 101 MID TOM 1 (index 187) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:TR-101 Drumset/17 instrument: 101 LO TOM 2 (index 188) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:TR-101 Drumset/16 instrument: 101 HAT CLOSED (index 189) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:TR-101 Drumset/15 instrument: 101 LO TOM 1 (index 190) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:TR-101 Drumset/14 instrument: 101 SNARE 2 (index 191) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:TR-101 Drumset/13 instrument: 101 CLAPS (index 192) key_range: 0 - 128 vel_range: 0 - 128 - zone: 21 name: pz:TR-101 Drumset/12 instrument: 101 SNARE 1 (index 193) key_range: 0 - 128 vel_range: 0 - 128 - zone: 22 name: pz:TR-101 Drumset/11 instrument: 101 RIM SHOT (index 194) key_range: 0 - 128 vel_range: 0 - 128 - zone: 23 name: pz:TR-101 Drumset/10 instrument: 101 KICK 2 (index 195) key_range: 0 - 128 vel_range: 0 - 128 - zone: 24 name: pz:TR-101 Drumset/9 instrument: 101 KICK 1 (index 196) key_range: 0 - 128 vel_range: 0 - 128 - zone: 25 name: pz:TR-101 Drumset/8 instrument: 101 SNARE EXTRA #02 (index 197) key_range: 0 - 128 vel_range: 0 - 128 - zone: 26 name: pz:TR-101 Drumset/7 instrument: 101 HAT OP EXTRA (index 198) key_range: 0 - 128 vel_range: 0 - 128 - zone: 27 name: pz:TR-101 Drumset/6 instrument: 101 HAT CL EXTRA (index 199) key_range: 0 - 128 vel_range: 0 - 128 - zone: 28 name: pz:TR-101 Drumset/5 instrument: 101 SNARE EXTRA 3 (index 200) key_range: 0 - 128 vel_range: 0 - 128 - zone: 29 name: pz:TR-101 Drumset/4 instrument: 101 KICK EXTRA 3 (index 201) key_range: 0 - 128 vel_range: 0 - 128 - zone: 30 name: pz:TR-101 Drumset/3 instrument: 101 SNARE EXTRA 2 (index 202) key_range: 0 - 128 vel_range: 0 - 128 - zone: 31 name: pz:TR-101 Drumset/2 instrument: 101 SNARE EXTRA 1 (index 203) key_range: 0 - 128 vel_range: 0 - 128 - zone: 32 name: pz:TR-101 Drumset/1 instrument: 101 KICK EXTRA 2 (index 204) key_range: 0 - 128 vel_range: 0 - 128 - zone: 33 name: pz:TR-101 Drumset/0 instrument: 101 KICK EXTRA 1 (index 205) key_range: 0 - 128 vel_range: 0 - 128 - preset: 129 name: CR-78 Drumset bank: 128 num: 1 zones: - zone: 0 name: pz:CR-78 Drumset/21 instrument: CR78 HI TOM 2 (index 150) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:CR-78 Drumset/20 instrument: CR78 HI TOM 1 (index 151) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:CR-78 Drumset/19 instrument: CR78 MID TOM 2 (index 152) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:CR-78 Drumset/18 instrument: CR78 MID TOM 1 (index 153) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:CR-78 Drumset/17 instrument: CR78 LO TOM 2 (index 154) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:CR-78 Drumset/16 instrument: CR78 LO TOM 1 (index 155) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:CR-78 Drumset/15 instrument: CR78 BONGO LO (index 156) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:CR-78 Drumset/14 instrument: CR78 BONGO MID (index 157) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:CR-78 Drumset/13 instrument: CR78 BONGO HI (index 158) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:CR-78 Drumset/12 instrument: CR78 COWBELL (index 159) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:CR-78 Drumset/11 instrument: CR78 TAMBOURINE (index 160) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:CR-78 Drumset/10 instrument: CR78 HAT OPEN (index 161) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:CR-78 Drumset/9 instrument: CR78 HAT CLOSED (index 162) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:CR-78 Drumset/8 instrument: CR78 SNARE 2 (index 163) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:CR-78 Drumset/7 instrument: CR78 METAL (index 164) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:CR-78 Drumset/6 instrument: CR78 SNARE 1 (index 165) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:CR-78 Drumset/5 instrument: CR78 RIM SHOT (index 166) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:CR-78 Drumset/4 instrument: CR78 KICK 2 (index 167) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:CR-78 Drumset/3 instrument: CR78 KICK 1 (index 168) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:CR-78 Drumset/2 instrument: CR78 CLAVE (index 169) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:CR-78 Drumset/1 instrument: CR78 MARACAS (index 170) key_range: 0 - 128 vel_range: 0 - 128 - zone: 21 name: pz:CR-78 Drumset/0 instrument: CR78 GUIRO (index 171) key_range: 0 - 128 vel_range: 0 - 128 - preset: 130 name: TR-808 Drumset bank: 128 num: 2 zones: - zone: 0 name: pz:TR-808 Drumset/28 instrument: 808 Clap (index 21) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:TR-808 Drumset/27 instrument: 808 CLAVES (index 121) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:TR-808 Drumset/26 instrument: 808 MARACAS (index 122) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:TR-808 Drumset/25 instrument: 808 BONGO LO (index 123) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:TR-808 Drumset/24 instrument: 808 BONGO MID (index 124) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:TR-808 Drumset/23 instrument: 808 BONGO HI (index 125) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:TR-808 Drumset/22 instrument: 808 COWBELL (index 126) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:TR-808 Drumset/21 instrument: 808 HI TOM 2 (index 127) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:TR-808 Drumset/20 instrument: 808 CYMBAL (index 128) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:TR-808 Drumset/19 instrument: 808 HI TOM 1 (index 129) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:TR-808 Drumset/18 instrument: 808 MID TOM 2 (index 130) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:TR-808 Drumset/17 instrument: 808 HAT OPEN (index 131) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:TR-808 Drumset/16 instrument: 808 MID TOM 1 (index 132) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:TR-808 Drumset/15 instrument: 808 LO TOM 2 (index 133) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:TR-808 Drumset/14 instrument: 808 HAT CLOSED (index 134) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:TR-808 Drumset/13 instrument: 808 LO TOM 1 (index 135) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:TR-808 Drumset/12 instrument: 808 SNARE DRUM HI (index 136) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:TR-808 Drumset/11 instrument: 808 SNARE DRUM LO (index 138) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:TR-808 Drumset/10 instrument: 808 RIM SHOT (index 139) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:TR-808 Drumset/9 instrument: 808 BASS DRUM HI (index 140) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:TR-808 Drumset/8 instrument: 808 BASS DRUM LO (index 141) key_range: 0 - 128 vel_range: 0 - 128 - zone: 21 name: pz:TR-808 Drumset/7 instrument: 808 SNARE HIGH (index 142) key_range: 0 - 128 vel_range: 0 - 128 - zone: 22 name: pz:TR-808 Drumset/6 instrument: 808 SNARE SHARP (index 143) key_range: 0 - 128 vel_range: 0 - 128 - zone: 23 name: pz:TR-808 Drumset/5 instrument: 808 SNARE FILTERED (index 144) key_range: 0 - 128 vel_range: 0 - 128 - zone: 24 name: pz:TR-808 Drumset/4 instrument: 808 ULTRA SNAPPY (index 145) key_range: 0 - 128 vel_range: 0 - 128 - zone: 25 name: pz:TR-808 Drumset/3 instrument: 808 BASS DRUM MUTE (index 146) key_range: 0 - 128 vel_range: 0 - 128 - zone: 26 name: pz:TR-808 Drumset/2 instrument: 808 BASS DRUM BOOM2 (index 147) key_range: 0 - 128 vel_range: 0 - 128 - zone: 27 name: pz:TR-808 Drumset/1 instrument: 808 BASS DRUM SHORT (index 148) key_range: 0 - 128 vel_range: 0 - 128 - zone: 28 name: pz:TR-808 Drumset/0 instrument: 808 BASS DRUM BOOM1 (index 149) key_range: 0 - 128 vel_range: 0 - 128 - preset: 131 name: TR-909 Drumset bank: 128 num: 3 zones: - zone: 0 name: pz:TR-909 Drumset/18 instrument: 909 BASS BOOM (index 102) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:TR-909 Drumset/17 instrument: 909 TOM HI 2 (index 103) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:TR-909 Drumset/16 instrument: 909 TOM HI 1 (index 104) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:TR-909 Drumset/15 instrument: 909 TOM MID 2 (index 105) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:TR-909 Drumset/14 instrument: 909 HAT OPEN (index 106) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:TR-909 Drumset/13 instrument: 909 TOM MID 1 (index 107) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:TR-909 Drumset/12 instrument: 909 TOM LO 2 (index 108) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:TR-909 Drumset/11 instrument: 909 HAT CLOSED (index 109) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:TR-909 Drumset/10 instrument: 909 TOM LO 1 (index 110) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:TR-909 Drumset/9 instrument: 909 SNARE HI (index 111) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:TR-909 Drumset/8 instrument: 909 CLAP (index 112) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:TR-909 Drumset/7 instrument: 909 SNARE LO (index 113) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:TR-909 Drumset/6 instrument: 909 RIM (index 114) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:TR-909 Drumset/5 instrument: 909 BASS DRUM HI (index 115) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:TR-909 Drumset/4 instrument: 909 BASS DRUM LO (index 116) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:TR-909 Drumset/3 instrument: 909 SNARE 2 (index 117) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:TR-909 Drumset/2 instrument: 909 SNARE 1 (index 118) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:TR-909 Drumset/1 instrument: 909 BASS DRUM 2 (index 119) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:TR-909 Drumset/0 instrument: 909 BASS DRUM 1 (index 120) key_range: 0 - 128 vel_range: 0 - 128 - preset: 132 name: Analog Drumset bank: 128 num: 4 zones: - zone: 0 name: pz:Analog Drumset/48 instrument: ANALOG SNARE 9 (index 53) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Analog Drumset/47 instrument: ANALOG SNARE 8 (index 54) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Analog Drumset/46 instrument: ANALOG SNARE 7 (index 55) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:Analog Drumset/45 instrument: ANALOG SNAPPY BD (index 56) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:Analog Drumset/44 instrument: ANALOG HAT OPEN 3 (index 57) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:Analog Drumset/43 instrument: ANALOG HAT CLOSED 3 (index 58) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:Analog Drumset/42 instrument: ANALOG HAT CLOSED 2 (index 59) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:Analog Drumset/41 instrument: ANALOG HAT OPEN 2 (index 60) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:Analog Drumset/40 instrument: ANALOG SNAP SNARE (index 61) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:Analog Drumset/39 instrument: ANALOG BOOM KICK (index 62) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:Analog Drumset/38 instrument: ANALOG CLAPS 3 (index 63) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:Analog Drumset/37 instrument: ANALOG CLAPS 2 (index 64) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:Analog Drumset/36 instrument: ANALOG ZAP OPEN (index 65) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:Analog Drumset/35 instrument: ANALOG ZAP CLOSED (index 66) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:Analog Drumset/34 instrument: ANALOG CLAVE (index 67) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:Analog Drumset/33 instrument: ANALOG SNARE 6 (index 68) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:Analog Drumset/32 instrument: ANALOG SNARE 5 (index 69) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:Analog Drumset/31 instrument: ANALOG THROB KICK (index 70) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:Analog Drumset/30 instrument: ANALOG POPS OP 2 (index 71) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:Analog Drumset/29 instrument: ANALOG POPS OP 1 (index 72) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:Analog Drumset/28 instrument: ANALOG POPS CL 2 (index 73) key_range: 0 - 128 vel_range: 0 - 128 - zone: 21 name: pz:Analog Drumset/27 instrument: ANALOG POPS CL 1 (index 74) key_range: 0 - 128 vel_range: 0 - 128 - zone: 22 name: pz:Analog Drumset/26 instrument: ANALOG PHASER (index 75) key_range: 0 - 128 vel_range: 0 - 128 - zone: 23 name: pz:Analog Drumset/25 instrument: ANALOG TAMBOURINE (index 76) key_range: 0 - 128 vel_range: 0 - 128 - zone: 24 name: pz:Analog Drumset/24 instrument: ANALOG KICK 3 (index 77) key_range: 0 - 128 vel_range: 0 - 128 - zone: 25 name: pz:Analog Drumset/23 instrument: ANALOG RIM SHOT (index 78) key_range: 0 - 128 vel_range: 0 - 128 - zone: 26 name: pz:Analog Drumset/22 instrument: ANALOG CRASH CYMBAL1 (index 79) key_range: 0 - 128 vel_range: 0 - 128 - zone: 27 name: pz:Analog Drumset/21 instrument: ANALOG COWBELL (index 80) key_range: 0 - 128 vel_range: 0 - 128 - zone: 28 name: pz:Analog Drumset/20 instrument: ANALOG BONGO HI (index 81) key_range: 0 - 128 vel_range: 0 - 128 - zone: 29 name: pz:Analog Drumset/19 instrument: ANALOG BONGO MED (index 82) key_range: 0 - 128 vel_range: 0 - 128 - zone: 30 name: pz:Analog Drumset/18 instrument: ANALOG BONGO LO (index 83) key_range: 0 - 128 vel_range: 0 - 128 - zone: 31 name: pz:Analog Drumset/17 instrument: ANALOG SNARE 4 (index 84) key_range: 0 - 128 vel_range: 0 - 128 - zone: 32 name: pz:Analog Drumset/16 instrument: ANALOG SNARE 3 (index 85) key_range: 0 - 128 vel_range: 0 - 128 - zone: 33 name: pz:Analog Drumset/15 instrument: ANALOG MARACAS (index 86) key_range: 0 - 128 vel_range: 0 - 128 - zone: 34 name: pz:Analog Drumset/14 instrument: ANALOG CLAPS 1 (index 87) key_range: 0 - 128 vel_range: 0 - 128 - zone: 35 name: pz:Analog Drumset/13 instrument: ANALOG HI TOM 2 (index 88) key_range: 0 - 128 vel_range: 0 - 128 - zone: 36 name: pz:Analog Drumset/12 instrument: ANALOG HI TOM 1 (index 89) key_range: 0 - 128 vel_range: 0 - 128 - zone: 37 name: pz:Analog Drumset/11 instrument: ANALOG MID TOM 2 (index 90) key_range: 0 - 128 vel_range: 0 - 128 - zone: 38 name: pz:Analog Drumset/10 instrument: ANALOG MID TOM 1 (index 91) key_range: 0 - 128 vel_range: 0 - 128 - zone: 39 name: pz:Analog Drumset/9 instrument: ANALOG LO TOM 2 (index 92) key_range: 0 - 128 vel_range: 0 - 128 - zone: 40 name: pz:Analog Drumset/8 instrument: ANALOG LO TOM 1 (index 93) key_range: 0 - 128 vel_range: 0 - 128 - zone: 41 name: pz:Analog Drumset/7 instrument: ANALOG CRASH CYMBAL2 (index 94) key_range: 0 - 128 vel_range: 0 - 128 - zone: 42 name: pz:Analog Drumset/6 instrument: ANALOG HAT CLOSED (index 95) key_range: 0 - 128 vel_range: 0 - 128 - zone: 43 name: pz:Analog Drumset/5 instrument: ANALOG HAT OPEN (index 96) key_range: 0 - 128 vel_range: 0 - 128 - zone: 44 name: pz:Analog Drumset/4 instrument: ANALOG LASER (index 97) key_range: 0 - 128 vel_range: 0 - 128 - zone: 45 name: pz:Analog Drumset/3 instrument: ANALOG SNARE 2 (index 98) key_range: 0 - 128 vel_range: 0 - 128 - zone: 46 name: pz:Analog Drumset/2 instrument: ANALOG KICK 2 (index 99) key_range: 0 - 128 vel_range: 0 - 128 - zone: 47 name: pz:Analog Drumset/1 instrument: ANALOG KICK 1 (index 100) key_range: 0 - 128 vel_range: 0 - 128 - zone: 48 name: pz:Analog Drumset/0 instrument: ANALOG SNARE 1 (index 101) key_range: 0 - 128 vel_range: 0 - 128 - preset: 133 name: Kraftwerk Drumset bank: 128 num: 5 zones: - zone: 0 name: pz:Kraftwerk Drumset/16 instrument: KRAFTWERK BD 5 (index 36) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Kraftwerk Drumset/15 instrument: KRAFTWERK SD 7 (index 37) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Kraftwerk Drumset/14 instrument: KRAFTWERK SD 6 (index 38) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:Kraftwerk Drumset/13 instrument: KRAFTWERK KLAPS (index 39) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:Kraftwerk Drumset/12 instrument: KRAFTWERK SD 5 (index 40) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:Kraftwerk Drumset/11 instrument: KRAFTWERK BD 4 (index 41) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:Kraftwerk Drumset/10 instrument: KRAFTWERK SD 4 (index 42) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:Kraftwerk Drumset/9 instrument: KRAFTWERK BD 3 (index 43) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:Kraftwerk Drumset/8 instrument: KRAFTWERK SD 3 (index 44) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:Kraftwerk Drumset/7 instrument: KRAFTWERK RIMSHOT (index 45) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:Kraftwerk Drumset/6 instrument: KRAFTWERK CYMBAL (index 46) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:Kraftwerk Drumset/5 instrument: KRAFTWERK HAT OP (index 47) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:Kraftwerk Drumset/4 instrument: KRAFTWERK HAT CL (index 48) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:Kraftwerk Drumset/3 instrument: KRAFTWERK SD 2 (index 49) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:Kraftwerk Drumset/2 instrument: KRAFTWERK SD 1 (index 50) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:Kraftwerk Drumset/1 instrument: KRAFTWERK BD 2 (index 51) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:Kraftwerk Drumset/0 instrument: KRAFTWERK BD 1 (index 52) key_range: 0 - 128 vel_range: 0 - 128 - preset: 134 name: Electronic Drumset bank: 128 num: 6 zones: - zone: 0 name: pz:Electronic Drumset/9 instrument: ELECTRONIC KICK 2 (index 26) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:Electronic Drumset/8 instrument: ELECTRONIC SD 2 (index 27) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:Electronic Drumset/7 instrument: ELECTRONIC SD 1 (index 28) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:Electronic Drumset/6 instrument: ELECTRONIC KICK 1 (index 29) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:Electronic Drumset/5 instrument: ELECTRONIC TOM LO 1 (index 30) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:Electronic Drumset/4 instrument: ELECTRONIC TOM LO 2 (index 31) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:Electronic Drumset/3 instrument: ELECTRONIC TOM MID 1 (index 32) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:Electronic Drumset/2 instrument: ELECTRONIC TOM MID 2 (index 33) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:Electronic Drumset/1 instrument: ELECTRONIC TOM HI 1 (index 34) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:Electronic Drumset/0 instrument: ELECTRONIC TOM HI 2 (index 35) key_range: 0 - 128 vel_range: 0 - 128 - preset: 135 name: TR-808 Drumset 2 bank: 128 num: 7 zones: - zone: 0 name: pz:TR-808 Drumset 2/20 instrument: 808 CLAP (index 137) key_range: 0 - 128 vel_range: 0 - 128 - zone: 1 name: pz:TR-808 Drumset 2/19 instrument: 808 Maracas (index 5) key_range: 0 - 128 vel_range: 0 - 128 - zone: 2 name: pz:TR-808 Drumset 2/18 instrument: 808 Clave (index 6) key_range: 0 - 128 vel_range: 0 - 128 - zone: 3 name: pz:TR-808 Drumset 2/17 instrument: 808 Cymbal (index 7) key_range: 0 - 128 vel_range: 0 - 128 - zone: 4 name: pz:TR-808 Drumset 2/16 instrument: 808 Conga Hi (index 8) key_range: 0 - 128 vel_range: 0 - 128 - zone: 5 name: pz:TR-808 Drumset 2/15 instrument: 808 Conga Med (index 9) key_range: 0 - 128 vel_range: 0 - 128 - zone: 6 name: pz:TR-808 Drumset 2/14 instrument: 808 Conga Lo (index 10) key_range: 0 - 128 vel_range: 0 - 128 - zone: 7 name: pz:TR-808 Drumset 2/13 instrument: 808 Tom Hi 2 (index 11) key_range: 0 - 128 vel_range: 0 - 128 - zone: 8 name: pz:TR-808 Drumset 2/12 instrument: 808 Tom Med 2 (index 12) key_range: 0 - 128 vel_range: 0 - 128 - zone: 9 name: pz:TR-808 Drumset 2/11 instrument: 808 Tom Lo 2 (index 13) key_range: 0 - 128 vel_range: 0 - 128 - zone: 10 name: pz:TR-808 Drumset 2/10 instrument: 808 Cowbell (index 14) key_range: 0 - 128 vel_range: 0 - 128 - zone: 11 name: pz:TR-808 Drumset 2/9 instrument: 808 Tom Med 1 (index 15) key_range: 0 - 128 vel_range: 0 - 128 - zone: 12 name: pz:TR-808 Drumset 2/8 instrument: 808 Tom Hi 1 (index 16) key_range: 0 - 128 vel_range: 0 - 128 - zone: 13 name: pz:TR-808 Drumset 2/7 instrument: 808 Tom Lo 1 (index 17) key_range: 0 - 128 vel_range: 0 - 128 - zone: 14 name: pz:TR-808 Drumset 2/6 instrument: 808 Hat Closed (index 18) key_range: 0 - 128 vel_range: 0 - 128 - zone: 15 name: pz:TR-808 Drumset 2/5 instrument: 808 Rim (index 19) key_range: 0 - 128 vel_range: 0 - 128 - zone: 16 name: pz:TR-808 Drumset 2/4 instrument: 808 Kick 1 (index 20) key_range: 0 - 128 vel_range: 0 - 128 - zone: 17 name: pz:TR-808 Drumset 2/3 instrument: 808 Snare 2 (index 22) key_range: 0 - 128 vel_range: 0 - 128 - zone: 18 name: pz:TR-808 Drumset 2/2 instrument: 808 Hat Open (index 23) key_range: 0 - 128 vel_range: 0 - 128 - zone: 19 name: pz:TR-808 Drumset 2/1 instrument: 808 Snare 1 (index 24) key_range: 0 - 128 vel_range: 0 - 128 - zone: 20 name: pz:TR-808 Drumset 2/0 instrument: 808 Kick 2 (index 25) key_range: 0 - 128 vel_range: 0 - 128 instruments: - instrument: 0 name: Instrument8 global_zone: name: iz:Instrument8/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument8/1 sample: FM4-0440 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 1 name: Instrument15 global_zone: name: iz:Instrument15/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument15/1 sample: Tri2 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 2 name: Instrument16 global_zone: name: iz:Instrument16/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument16/1 sample: Tri-Sq key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 3 name: Instrument9 global_zone: name: iz:Instrument9/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument9/1 sample: FM5-0440 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 4 name: Instrument3 global_zone: name: iz:Instrument3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:Instrument3/1 sample: Bell2-0440 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - instrument: 5 name: 808 Maracas zones: - zone: 0 name: iz:808 Maracas/0 sample: white808-2 key_range: 70 - 70 vel_range: 0 - 128 generators: VOLENVATTACK: -6688.00 VOLENVDECAY: -4351.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -4351.00 FINETUNE: 20.00 OVERRIDEROOTKEY: 44.00 - instrument: 6 name: 808 Clave zones: - zone: 0 name: iz:808 Clave/0 sample: sinehi key_range: 75 - 75 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 87.00 - instrument: 7 name: 808 Cymbal zones: - zone: 0 name: iz:808 Cymbal/2 sample: sinehi key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 500.00 VOLENVDECAY: 1783.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1783.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 64.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 1 name: iz:808 Cymbal/1 sample: sinehi key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 500.00 VOLENVDECAY: 1783.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1783.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 72.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 2 name: iz:808 Cymbal/0 sample: white5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 800.00 VOLENVDECAY: 1820.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1820.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 61.00 - instrument: 8 name: 808 Conga Hi zones: - zone: 0 name: iz:808 Conga Hi/0 sample: sinehi key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 101.00 - instrument: 9 name: 808 Conga Med zones: - zone: 0 name: iz:808 Conga Med/0 sample: bdloop key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 10 name: 808 Conga Lo zones: - zone: 0 name: iz:808 Conga Lo/0 sample: bdloop key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -182.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -182.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - instrument: 11 name: 808 Tom Hi 2 global_zone: name: iz:808 Tom Hi 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Hi 2/1 sample: bdloop key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - instrument: 12 name: 808 Tom Med 2 global_zone: name: iz:808 Tom Med 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Med 2/1 sample: bdloop key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - instrument: 13 name: 808 Tom Lo 2 global_zone: name: iz:808 Tom Lo 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Lo 2/1 sample: bdloop key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 14 name: 808 Cowbell global_zone: name: iz:808 Cowbell/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOPITCH: 4.00 FILTERFC: 11921.00 MODLFOTOVOL: 20.00 CHORUSSEND: 200.00 MODLFOFREQ: 467.00 FINETUNE: -20.00 zones: - zone: 0 name: iz:808 Cowbell/2 sample: cowbell key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -10.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - zone: 1 name: iz:808 Cowbell/1 sample: cowbell key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: 454.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 454.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 128.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - instrument: 15 name: 808 Tom Med 1 global_zone: name: iz:808 Tom Med 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Med 1/1 sample: bdloop key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 16 name: 808 Tom Hi 1 global_zone: name: iz:808 Tom Hi 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Hi 1/1 sample: bdloop key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 17 name: 808 Tom Lo 1 global_zone: name: iz:808 Tom Lo 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 300.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -11959.00 zones: - zone: 0 name: iz:808 Tom Lo 1/1 sample: bdloop key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 18 name: 808 Hat Closed global_zone: name: iz:808 Hat Closed/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 zones: - zone: 0 name: iz:808 Hat Closed/2 sample: white5 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 381.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - zone: 1 name: iz:808 Hat Closed/1 sample: sinehi key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 96.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 13.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - instrument: 19 name: 808 Rim zones: - zone: 0 name: iz:808 Rim/1 sample: sinehi key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -5530.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5530.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 12.00 FINETUNE: -40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 67.00 - zone: 1 name: iz:808 Rim/0 sample: claploop key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -4351.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -4351.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - instrument: 20 name: 808 Kick 1 zones: - zone: 0 name: iz:808 Kick 1/0 sample: bdloop key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODENVTOPITCH: 1200.00 REVERBSEND: 40.00 MODENVATTACK: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDECAY: 1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 15.00 SAMPLEMODE: 1.00 - instrument: 21 name: 808 Clap global_zone: name: iz:808 Clap/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 95.00 REVERBSEND: 20.00 MODLFOFREQ: -1179.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 zones: - zone: 0 name: iz:808 Clap/1 sample: claploop key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 22 name: 808 Snare 2 zones: - zone: 0 name: iz:808 Snare 2/2 sample: white808-2 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 10.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 1 name: iz:808 Snare 2/1 sample: white808-2 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 10.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 2 name: iz:808 Snare 2/0 sample: bdloop key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 87.00 VOLENVDECAY: -5530.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5530.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 120.00 SAMPLEMODE: 1.00 - instrument: 23 name: 808 Hat Open global_zone: name: iz:808 Hat Open/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 180.00 VOLENVHOLD: -3162.00 VOLENVDECAY: 702.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1582.00 zones: - zone: 0 name: iz:808 Hat Open/2 sample: white5 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 381.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 1 name: iz:808 Hat Open/1 sample: sinehi key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: 454.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 96.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 13.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 54.00 - instrument: 24 name: 808 Snare 1 zones: - zone: 0 name: iz:808 Snare 1/2 sample: white808-2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 40.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 11.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - zone: 1 name: iz:808 Snare 1/1 sample: white808-2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 40.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 11.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - zone: 2 name: iz:808 Snare 1/0 sample: bdloop key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 40.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 46.00 SAMPLEMODE: 1.00 - instrument: 25 name: 808 Kick 2 zones: - zone: 0 name: iz:808 Kick 2/0 sample: bdloop key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODENVTOPITCH: 1200.00 REVERBSEND: 40.00 MODENVATTACK: -7973.00 MODENVSUSTAIN: 1000.00 VOLENVDECAY: 316.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 316.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 17.00 SAMPLEMODE: 1.00 - instrument: 26 name: ELECTRONIC KICK 2 global_zone: name: iz:ELECTRONIC KICK 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VOLENVATTACK: -8857.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:ELECTRONIC KICK 2/2 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - zone: 1 name: iz:ELECTRONIC KICK 2/1 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - instrument: 27 name: ELECTRONIC SD 2 zones: - zone: 0 name: iz:ELECTRONIC SD 2/2 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: -500.00 MODENVDECAY: -2613.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 1 name: iz:ELECTRONIC SD 2/1 sample: 808BD key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 MODENVTOPITCH: 1200.00 MODENVSUSTAIN: 1000.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - zone: 2 name: iz:ELECTRONIC SD 2/0 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 28 name: ELECTRONIC SD 1 zones: - zone: 0 name: iz:ELECTRONIC SD 1/2 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10223.00 FILTERQ: 80.00 MODENVTOFILTERFC: 1500.00 CHORUSSEND: 200.00 PAN: -500.00 MODENVDECAY: -2613.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2613.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 1 name: iz:ELECTRONIC SD 1/1 sample: 808BD key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 MODENVTOPITCH: 1200.00 MODENVSUSTAIN: 1000.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - zone: 2 name: iz:ELECTRONIC SD 1/0 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10223.00 FILTERQ: 80.00 MODENVTOFILTERFC: 1500.00 CHORUSSEND: 200.00 PAN: 500.00 MODENVDECAY: -2613.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2613.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 29 name: ELECTRONIC KICK 1 global_zone: name: iz:ELECTRONIC KICK 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: VIBLFOTOPITCH: 368.00 VIBLFOFREQ: -155.00 VOLENVATTACK: -8857.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -2.00 zones: - zone: 0 name: iz:ELECTRONIC KICK 1/3 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 VIBLFOTOPITCH: -1200.00 FILTERFC: 11273.00 VIBLFOFREQ: -15597.00 VOLENVATTACK: -11959.00 VOLENVDECAY: -2786.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:ELECTRONIC KICK 1/2 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - zone: 2 name: iz:ELECTRONIC KICK 1/1 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - instrument: 30 name: ELECTRONIC TOM LO 1 global_zone: name: iz:ELECTRONIC TOM LO 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -11.00 zones: - zone: 0 name: iz:ELECTRONIC TOM LO 1/1 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 31 name: ELECTRONIC TOM LO 2 global_zone: name: iz:ELECTRONIC TOM LO 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -9.00 zones: - zone: 0 name: iz:ELECTRONIC TOM LO 2/1 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 32 name: ELECTRONIC TOM MID 1 global_zone: name: iz:ELECTRONIC TOM MID 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -7.00 zones: - zone: 0 name: iz:ELECTRONIC TOM MID 1/1 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 33 name: ELECTRONIC TOM MID 2 global_zone: name: iz:ELECTRONIC TOM MID 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -5.00 zones: - zone: 0 name: iz:ELECTRONIC TOM MID 2/1 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 34 name: ELECTRONIC TOM HI 1 global_zone: name: iz:ELECTRONIC TOM HI 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -3.00 zones: - zone: 0 name: iz:ELECTRONIC TOM HI 1/1 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 35 name: ELECTRONIC TOM HI 2 global_zone: name: iz:ELECTRONIC TOM HI 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 9521.00 CHORUSSEND: 200.00 MODENVDECAY: -2084.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2084.00 VOLENVATTACK: -8590.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:ELECTRONIC TOM HI 2/1 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 36 name: KRAFTWERK BD 5 zones: - zone: 0 name: iz:KRAFTWERK BD 5/2 sample: FM90000 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VIBLFOTOPITCH: 1200.00 MODENVTOPITCH: 1200.00 VIBLFOFREQ: 410.00 MODENVDECAY: -2959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2959.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -386.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 120.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 70.00 - zone: 1 name: iz:KRAFTWERK BD 5/1 sample: RIM key_range: 31 - 31 vel_range: 0 - 128 generators: VOLENVSUSTAIN: 960.00 OVERRIDEROOTKEY: 29.00 - zone: 2 name: iz:KRAFTWERK BD 5/0 sample: SINE32 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 55.00 - instrument: 37 name: KRAFTWERK SD 7 global_zone: name: iz:KRAFTWERK SD 7/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 300.00 VOLENVDECAY: -182.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -182.00 zones: - zone: 0 name: iz:KRAFTWERK SD 7/3 sample: SINE32 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 26.00 - zone: 1 name: iz:KRAFTWERK SD 7/2 sample: NOISE3 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8837.00 FILTERQ: 180.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:KRAFTWERK SD 7/1 sample: NOISE3 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8789.00 FILTERQ: 180.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - instrument: 38 name: KRAFTWERK SD 6 global_zone: name: iz:KRAFTWERK SD 6/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 300.00 VOLENVDECAY: -2613.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2613.00 zones: - zone: 0 name: iz:KRAFTWERK SD 6/3 sample: FM90000 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VIBLFOTOPITCH: 420.00 MODENVTOPITCH: 1200.00 CHORUSSEND: 400.00 MODENVDECAY: -2959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2959.00 VOLENVDECAY: -1498.00 VOLENVRELEASE: -1498.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 63.00 - zone: 1 name: iz:KRAFTWERK SD 6/2 sample: NOISE3 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 35.00 - zone: 2 name: iz:KRAFTWERK SD 6/1 sample: NOISE3 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 35.00 - instrument: 39 name: KRAFTWERK KLAPS global_zone: name: iz:KRAFTWERK KLAPS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 40.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -14.00 zones: - zone: 0 name: iz:KRAFTWERK KLAPS/2 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 42.00 - zone: 1 name: iz:KRAFTWERK KLAPS/1 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 40 name: KRAFTWERK SD 5 zones: - zone: 0 name: iz:KRAFTWERK SD 5/2 sample: NOISE3 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 6237.00 FILTERQ: 120.00 MODENVTOFILTERFC: 1500.00 CHORUSSEND: 400.00 PAN: -500.00 MODENVDECAY: -2613.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2613.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 1 name: iz:KRAFTWERK SD 5/1 sample: 808BD key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 MODENVTOPITCH: 1200.00 MODENVSUSTAIN: 1000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - zone: 2 name: iz:KRAFTWERK SD 5/0 sample: NOISE3 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 6237.00 FILTERQ: 120.00 MODENVTOFILTERFC: 1500.00 CHORUSSEND: 400.00 PAN: 500.00 MODENVDECAY: -2613.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2613.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -20.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 41 name: KRAFTWERK BD 4 zones: - zone: 0 name: iz:KRAFTWERK BD 4/1 sample: RIM key_range: 33 - 33 vel_range: 0 - 128 generators: VOLENVSUSTAIN: 960.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:KRAFTWERK BD 4/0 sample: SINE32 key_range: 33 - 33 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 VIBLFOTOPITCH: 1200.00 VIBLFOFREQ: 320.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 49.00 - instrument: 42 name: KRAFTWERK SD 4 zones: - zone: 0 name: iz:KRAFTWERK SD 4/2 sample: NOISE3 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 VOLENVDECAY: -5530.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5530.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:KRAFTWERK SD 4/1 sample: NOISE3 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 7728.00 FILTERQ: 180.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -182.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -182.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:KRAFTWERK SD 4/0 sample: NOISE3 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 7728.00 FILTERQ: 180.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -182.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -182.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - instrument: 43 name: KRAFTWERK BD 3 zones: - zone: 0 name: iz:KRAFTWERK BD 3/1 sample: RIM key_range: 34 - 34 vel_range: 0 - 128 generators: VOLENVSUSTAIN: 960.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:KRAFTWERK BD 3/0 sample: SINE32 key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 VOLENVDECAY: -2028.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2028.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - instrument: 44 name: KRAFTWERK SD 3 zones: - zone: 0 name: iz:KRAFTWERK SD 3/1 sample: NOISE3 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -2959.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2959.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 42.00 - zone: 1 name: iz:KRAFTWERK SD 3/0 sample: NOISE3 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -2959.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2959.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 20.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 42.00 - instrument: 45 name: KRAFTWERK RIMSHOT zones: - zone: 0 name: iz:KRAFTWERK RIMSHOT/0 sample: CLICK key_range: 37 - 37 vel_range: 0 - 128 generators: CHORUSSEND: 40.00 REVERBSEND: 20.00 VOLENVDECAY: -6688.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -6688.00 OVERRIDEROOTKEY: 62.00 - instrument: 46 name: KRAFTWERK CYMBAL zones: - zone: 0 name: iz:KRAFTWERK CYMBAL/0 sample: NOISE5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 MODENVTOFILTERFC: -1837.00 MODLFOTOVOL: 15.00 CHORUSSEND: 320.00 MODLFOFREQ: -336.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -881.00 MODENVDECAY: 6076.00 MODENVRELEASE: 6139.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 47 name: KRAFTWERK HAT OP zones: - zone: 0 name: iz:KRAFTWERK HAT OP/1 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 180.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -30.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:KRAFTWERK HAT OP/0 sample: NOISE5 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 FILTERQ: 150.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 48 name: KRAFTWERK HAT CL zones: - zone: 0 name: iz:KRAFTWERK HAT CL/1 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 180.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -30.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:KRAFTWERK HAT CL/0 sample: NOISE5 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 FILTERQ: 150.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 48.00 - instrument: 49 name: KRAFTWERK SD 2 zones: - zone: 0 name: iz:KRAFTWERK SD 2/2 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERFC: 6691.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 PAN: -500.00 MODENVDECAY: -1129.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -1064.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 COARSETUNE: 0.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:KRAFTWERK SD 2/1 sample: LASER1 key_range: 40 - 40 vel_range: 0 - 128 generators: VOLENVDECAY: -1582.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1582.00 OVERRIDEROOTKEY: 35.00 - zone: 2 name: iz:KRAFTWERK SD 2/0 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERFC: 6691.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 REVERBSEND: 20.00 PAN: 500.00 MODENVDECAY: -1129.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -1064.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 COARSETUNE: 1.00 OVERRIDEROOTKEY: 33.00 - instrument: 50 name: KRAFTWERK SD 1 zones: - zone: 0 name: iz:KRAFTWERK SD 1/3 sample: WHITE4 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 54.00 - zone: 1 name: iz:KRAFTWERK SD 1/2 sample: WHITE4 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 54.00 - zone: 2 name: iz:KRAFTWERK SD 1/1 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 REVERBSEND: 20.00 VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3986.00 COARSETUNE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 3 name: iz:KRAFTWERK SD 1/0 sample: SINE32 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 FILTERFC: 10721.00 VOLENVDECAY: -3284.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3284.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 51 name: KRAFTWERK BD 2 zones: - zone: 0 name: iz:KRAFTWERK BD 2/1 sample: RIM key_range: 36 - 36 vel_range: 0 - 128 generators: VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3986.00 OVERRIDEROOTKEY: 54.00 - zone: 1 name: iz:KRAFTWERK BD 2/0 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: 1200.00 MODENVDECAY: -5186.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 5628.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 55.00 - instrument: 52 name: KRAFTWERK BD 1 zones: - zone: 0 name: iz:KRAFTWERK BD 1/1 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: 1200.00 MODENVDELAY: -11959.00 MODENVATTACK: -11959.00 MODENVHOLD: -11959.00 MODENVDECAY: -7973.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 5628.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - zone: 1 name: iz:KRAFTWERK BD 1/0 sample: LASER1 key_range: 35 - 35 vel_range: 0 - 128 generators: FILTERFC: 9521.00 VOLENVDECAY: -4842.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -4842.00 OVERRIDEROOTKEY: 54.00 - instrument: 53 name: ANALOG SNARE 9 zones: - zone: 0 name: iz:ANALOG SNARE 9/1 sample: NOISE5 key_range: 72 - 72 vel_range: 0 - 128 generators: CHORUSSEND: 500.00 REVERBSEND: 40.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 77.00 - zone: 1 name: iz:ANALOG SNARE 9/0 sample: SINE32 key_range: 72 - 72 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -5186.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -5186.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 54.00 - instrument: 54 name: ANALOG SNARE 8 zones: - zone: 0 name: iz:ANALOG SNARE 8/2 sample: BD1 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -12.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - zone: 1 name: iz:ANALOG SNARE 8/1 sample: NOISE3 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8321.00 FILTERQ: 80.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 68.00 - zone: 2 name: iz:ANALOG SNARE 8/0 sample: NOISE3 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8321.00 FILTERQ: 80.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 66.00 - instrument: 55 name: ANALOG SNARE 7 zones: - zone: 0 name: iz:ANALOG SNARE 7/2 sample: BD1 key_range: 70 - 70 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -9.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - zone: 1 name: iz:ANALOG SNARE 7/1 sample: NOISE3 key_range: 70 - 70 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10223.00 FILTERQ: 80.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 68.00 - zone: 2 name: iz:ANALOG SNARE 7/0 sample: NOISE3 key_range: 70 - 70 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10223.00 FILTERQ: 80.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 66.00 - instrument: 56 name: ANALOG SNAPPY BD zones: - zone: 0 name: iz:ANALOG SNAPPY BD/0 sample: SINE32 key_range: 68 - 68 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: 1200.00 CHORUSSEND: 40.00 MODENVATTACK: -11959.00 MODENVDECAY: -11959.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - instrument: 57 name: ANALOG HAT OPEN 3 zones: - zone: 0 name: iz:ANALOG HAT OPEN 3/0 sample: NOISE5 key_range: 66 - 66 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVHOLD: -2332.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 68.00 - instrument: 58 name: ANALOG HAT CLOSED 3 zones: - zone: 0 name: iz:ANALOG HAT CLOSED 3/0 sample: NOISE5 key_range: 65 - 65 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 180.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: -1035.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1035.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 67.00 - instrument: 59 name: ANALOG HAT CLOSED 2 zones: - zone: 0 name: iz:ANALOG HAT CLOSED 2/1 sample: SINEHI key_range: 60 - 60 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 180.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -30.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 1 name: iz:ANALOG HAT CLOSED 2/0 sample: NOISE5 key_range: 60 - 60 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 FILTERQ: 150.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 66.00 - instrument: 60 name: ANALOG HAT OPEN 2 global_zone: name: iz:ANALOG HAT OPEN 2/0 key_range: 0 - 128 vel_range: 0 - 128 zones: - zone: 0 name: iz:ANALOG HAT OPEN 2/2 sample: SINEHI key_range: 61 - 61 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 180.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -30.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 1 name: iz:ANALOG HAT OPEN 2/1 sample: NOISE5 key_range: 61 - 61 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 FILTERQ: 150.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 67.00 - instrument: 61 name: ANALOG SNAP SNARE global_zone: name: iz:ANALOG SNAP SNARE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: -14.00 zones: - zone: 0 name: iz:ANALOG SNAP SNARE/4 sample: SINE32 key_range: 59 - 59 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -25.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 15.00 - zone: 1 name: iz:ANALOG SNAP SNARE/3 sample: NOISE5 key_range: 59 - 59 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - zone: 2 name: iz:ANALOG SNAP SNARE/2 sample: NOISE6 key_range: 59 - 59 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 REVERBSEND: 20.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - zone: 3 name: iz:ANALOG SNAP SNARE/1 sample: NOISE5 key_range: 59 - 59 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2400.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - instrument: 62 name: ANALOG BOOM KICK zones: - zone: 0 name: iz:ANALOG BOOM KICK/0 sample: THROB DRUM key_range: 58 - 58 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODLFOTOVOL: 100.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - instrument: 63 name: ANALOG CLAPS 3 global_zone: name: iz:ANALOG CLAPS 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 80.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 COARSETUNE: -27.00 zones: - zone: 0 name: iz:ANALOG CLAPS 3/2 sample: WHITE4 key_range: 55 - 55 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 PAN: 500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 1 name: iz:ANALOG CLAPS 3/1 sample: WHITE4 key_range: 55 - 55 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 PAN: -500.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 64 name: ANALOG CLAPS 2 global_zone: name: iz:ANALOG CLAPS 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 80.00 COARSETUNE: -27.00 zones: - zone: 0 name: iz:ANALOG CLAPS 2/3 sample: WHITE4 key_range: 53 - 53 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 1 name: iz:ANALOG CLAPS 2/2 sample: NOISE3 key_range: 53 - 53 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1271.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 2 name: iz:ANALOG CLAPS 2/1 sample: WHITE4 key_range: 53 - 53 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 65 name: ANALOG ZAP OPEN zones: - zone: 0 name: iz:ANALOG ZAP OPEN/0 sample: NOISE5 key_range: 52 - 52 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8270.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 MODENVDECAY: -6071.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -6071.00 VOLENVDECAY: 3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 61.00 - instrument: 66 name: ANALOG ZAP CLOSED zones: - zone: 0 name: iz:ANALOG ZAP CLOSED/0 sample: NOISE5 key_range: 51 - 51 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8270.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 MODENVDECAY: -6071.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -6071.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 - instrument: 67 name: ANALOG CLAVE zones: - zone: 0 name: iz:ANALOG CLAVE/0 sample: Tri_a3 key_range: 67 - 67 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 20.00 VOLENVDECAY: -3986.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3986.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 2.00 OVERRIDEROOTKEY: 42.00 - instrument: 68 name: ANALOG SNARE 6 zones: - zone: 0 name: iz:ANALOG SNARE 6/3 sample: WHITE4 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 1 name: iz:ANALOG SNARE 6/2 sample: NOISE3 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 20.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1271.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 2 name: iz:ANALOG SNARE 6/1 sample: WHITE4 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 400.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1382.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1382.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 3 name: iz:ANALOG SNARE 6/0 sample: BD1 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -9.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 4.00 - instrument: 69 name: ANALOG SNARE 5 zones: - zone: 0 name: iz:ANALOG SNARE 5/2 sample: BD1 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -9.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 6.00 - zone: 1 name: iz:ANALOG SNARE 5/1 sample: NOISE3 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 28.00 - zone: 2 name: iz:ANALOG SNARE 5/0 sample: NOISE3 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 26.00 - instrument: 70 name: ANALOG THROB KICK zones: - zone: 0 name: iz:ANALOG THROB KICK/0 sample: THROB DRUM key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODLFOTOVOL: 120.00 REVERBSEND: 20.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 71 name: ANALOG POPS OP 2 zones: - zone: 0 name: iz:ANALOG POPS OP 2/0 sample: Tri_a3 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 2.00 OVERRIDEROOTKEY: 6.00 - instrument: 72 name: ANALOG POPS OP 1 zones: - zone: 0 name: iz:ANALOG POPS OP 1/0 sample: Tri_a3 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 1.00 OVERRIDEROOTKEY: 17.00 - instrument: 73 name: ANALOG POPS CL 2 zones: - zone: 0 name: iz:ANALOG POPS CL 2/0 sample: Tri_a3 key_range: 25 - 25 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 2.00 OVERRIDEROOTKEY: 4.00 - instrument: 74 name: ANALOG POPS CL 1 zones: - zone: 0 name: iz:ANALOG POPS CL 1/0 sample: Tri_a3 key_range: 24 - 24 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 EXCLUSIVECLASS: 1.00 OVERRIDEROOTKEY: 15.00 - instrument: 75 name: ANALOG PHASER zones: - zone: 0 name: iz:ANALOG PHASER/0 sample: NOISE5 key_range: 44 - 44 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 8511.00 FILTERQ: 180.00 MODENVTOFILTERFC: 3600.00 CHORUSSEND: 300.00 MODENVDECAY: -3986.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -3986.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -386.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 - instrument: 76 name: ANALOG TAMBOURINE global_zone: name: iz:ANALOG TAMBOURINE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 120.00 CHORUSSEND: 700.00 REVERBSEND: 40.00 MODLFOFREQ: -353.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1817.00 zones: - zone: 0 name: iz:ANALOG TAMBOURINE/2 sample: SINEHI key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 110.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 65.20 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 1 name: iz:ANALOG TAMBOURINE/1 sample: NOISE5 key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 77 name: ANALOG KICK 3 zones: - zone: 0 name: iz:ANALOG KICK 3/1 sample: WHITE4 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VOLENVSUSTAIN: 960.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 16.00 - zone: 1 name: iz:ANALOG KICK 3/0 sample: TR909BD key_range: 31 - 31 vel_range: 0 - 128 generators: CHORUSSEND: 100.00 REVERBSEND: 30.00 VOLENVDECAY: 316.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 316.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 42.00 - instrument: 78 name: ANALOG RIM SHOT zones: - zone: 0 name: iz:ANALOG RIM SHOT/1 sample: WHITE4 key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 15.00 VOLENVSUSTAIN: 960.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 31.00 - zone: 1 name: iz:ANALOG RIM SHOT/0 sample: TR909BD key_range: 37 - 37 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 CHORUSSEND: 100.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -5530.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -5530.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 12.00 - instrument: 79 name: ANALOG CRASH CYMBAL1 zones: - zone: 0 name: iz:ANALOG CRASH CYMBAL1/1 sample: WHITE3 key_range: 57 - 57 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 100.00 VOLENVDECAY: 2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 1902.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 - zone: 1 name: iz:ANALOG CRASH CYMBAL1/0 sample: NOISE5 key_range: 57 - 57 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 VOLENVSUSTAIN: 960.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 58.00 - instrument: 80 name: ANALOG COWBELL zones: - zone: 0 name: iz:ANALOG COWBELL/2 sample: SINEHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1200.00 PAN: 100.00 MODENVATTACK: -6688.00 MODENVDECAY: 4355.00 MODENVRELEASE: 3926.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 82.00 - zone: 1 name: iz:ANALOG COWBELL/1 sample: SINEHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1200.00 PAN: -100.00 MODENVATTACK: -6688.00 MODENVDECAY: 4769.00 MODENVRELEASE: 5349.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 70.00 - zone: 2 name: iz:ANALOG COWBELL/0 sample: NOISE5 key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -11959.00 MODENVSUSTAIN: 0.00 VOLENVDECAY: -2267.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2267.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 70.00 - instrument: 81 name: ANALOG BONGO HI global_zone: name: iz:ANALOG BONGO HI/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 COARSETUNE: -8.00 zones: - zone: 0 name: iz:ANALOG BONGO HI/2 sample: NOISE3 key_range: 62 - 62 vel_range: 0 - 128 generators: VOLENVDECAY: -3068.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3068.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG BONGO HI/1 sample: SINE32 key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 82 name: ANALOG BONGO MED global_zone: name: iz:ANALOG BONGO MED/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 VOLENVDECAY: -1271.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1271.00 COARSETUNE: -8.00 zones: - zone: 0 name: iz:ANALOG BONGO MED/2 sample: NOISE3 key_range: 63 - 63 vel_range: 0 - 128 generators: VOLENVDECAY: -2613.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2613.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG BONGO MED/1 sample: SINE32 key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 83 name: ANALOG BONGO LO global_zone: name: iz:ANALOG BONGO LO/0 key_range: 0 - 128 vel_range: 0 - 128 generators: CHORUSSEND: 200.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: -8.00 zones: - zone: 0 name: iz:ANALOG BONGO LO/2 sample: NOISE3 key_range: 64 - 64 vel_range: 0 - 128 generators: VOLENVDECAY: -2613.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2613.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG BONGO LO/1 sample: SINE32 key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 51.00 - instrument: 84 name: ANALOG SNARE 4 global_zone: name: iz:ANALOG SNARE 4/0 key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: 0.00 zones: - zone: 0 name: iz:ANALOG SNARE 4/3 sample: NOISE3 key_range: 33 - 33 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 22.00 - zone: 1 name: iz:ANALOG SNARE 4/2 sample: NOISE3 key_range: 33 - 33 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 23.00 - zone: 2 name: iz:ANALOG SNARE 4/1 sample: BD1 key_range: 33 - 33 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1200.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -11.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 8.00 - instrument: 85 name: ANALOG SNARE 3 zones: - zone: 0 name: iz:ANALOG SNARE 3/2 sample: 808BD key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 CHORUSSEND: 100.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 20.00 - zone: 1 name: iz:ANALOG SNARE 3/1 sample: NOISE5 key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 1000.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - zone: 2 name: iz:ANALOG SNARE 3/0 sample: NOISE5 key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 1000.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -617.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -617.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - instrument: 86 name: ANALOG MARACAS zones: - zone: 0 name: iz:ANALOG MARACAS/0 sample: WHITE3 key_range: 69 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 110.00 CHORUSSEND: 235.00 REVERBSEND: 20.00 VOLENVATTACK: -4604.00 VOLENVDECAY: -2400.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - instrument: 87 name: ANALOG CLAPS 1 global_zone: name: iz:ANALOG CLAPS 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODLFOTOVOL: 90.00 CHORUSSEND: 600.00 REVERBSEND: 40.00 MODLFOFREQ: -983.00 VOLENVDECAY: -1200.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1200.00 COARSETUNE: 2.00 zones: - zone: 0 name: iz:ANALOG CLAPS 1/2 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 1 name: iz:ANALOG CLAPS 1/1 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 88 name: ANALOG HI TOM 2 global_zone: name: iz:ANALOG HI TOM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:ANALOG HI TOM 2/2 sample: NOISE3 key_range: 50 - 50 vel_range: 0 - 128 generators: VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG HI TOM 2/1 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 89 name: ANALOG HI TOM 1 global_zone: name: iz:ANALOG HI TOM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -3.00 zones: - zone: 0 name: iz:ANALOG HI TOM 1/2 sample: NOISE3 key_range: 48 - 48 vel_range: 0 - 128 generators: VOLENVDECAY: -2778.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG HI TOM 1/1 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 90 name: ANALOG MID TOM 2 global_zone: name: iz:ANALOG MID TOM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -5.00 zones: - zone: 0 name: iz:ANALOG MID TOM 2/2 sample: NOISE3 key_range: 47 - 47 vel_range: 0 - 128 generators: VOLENVDECAY: -2769.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG MID TOM 2/1 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 91 name: ANALOG MID TOM 1 global_zone: name: iz:ANALOG MID TOM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -7.00 zones: - zone: 0 name: iz:ANALOG MID TOM 1/2 sample: NOISE3 key_range: 45 - 45 vel_range: 0 - 128 generators: VOLENVDECAY: -2769.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2778.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG MID TOM 1/1 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 92 name: ANALOG LO TOM 2 global_zone: name: iz:ANALOG LO TOM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVATTACK: -11959.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -9.00 zones: - zone: 0 name: iz:ANALOG LO TOM 2/2 sample: NOISE3 key_range: 43 - 43 vel_range: 0 - 128 generators: VOLENVDECAY: -2769.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG LO TOM 2/1 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 93 name: ANALOG LO TOM 1 global_zone: name: iz:ANALOG LO TOM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: MODENVTOPITCH: 1200.00 FILTERFC: 10721.00 CHORUSSEND: 200.00 MODENVDECAY: -2325.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2325.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 COARSETUNE: -11.00 zones: - zone: 0 name: iz:ANALOG LO TOM 1/2 sample: NOISE3 key_range: 41 - 41 vel_range: 0 - 128 generators: VOLENVDECAY: -2769.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2769.00 ATTENUATION: 40.00 - zone: 1 name: iz:ANALOG LO TOM 1/1 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 94 name: ANALOG CRASH CYMBAL2 zones: - zone: 0 name: iz:ANALOG CRASH CYMBAL2/1 sample: SINEHI key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 300.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 124.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 1 name: iz:ANALOG CRASH CYMBAL2/0 sample: NOISE5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 CHORUSSEND: 200.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVDECAY: 1902.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 2400.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -1.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 95 name: ANALOG HAT CLOSED zones: - zone: 0 name: iz:ANALOG HAT CLOSED/1 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 REVERBSEND: 20.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 124.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:ANALOG HAT CLOSED/0 sample: NOISE5 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 REVERBSEND: 20.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2786.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2786.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - instrument: 96 name: ANALOG HAT OPEN zones: - zone: 0 name: iz:ANALOG HAT OPEN/1 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 REVERBSEND: 20.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 124.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:ANALOG HAT OPEN/0 sample: NOISE5 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 110.00 REVERBSEND: 20.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 97 name: ANALOG LASER zones: - zone: 0 name: iz:ANALOG LASER/0 sample: LASER1 key_range: 32 - 32 vel_range: 0 - 128 generators: CHORUSSEND: 0.00 REVERBSEND: 10.00 VOLENVDECAY: 4123.00 VOLENVRELEASE: 3217.00 OVERRIDEROOTKEY: 32.00 - instrument: 98 name: ANALOG SNARE 2 global_zone: name: iz:ANALOG SNARE 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: COARSETUNE: 0.00 zones: - zone: 0 name: iz:ANALOG SNARE 2/4 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 VOLENVDECAY: -884.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -884.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 42.00 - zone: 1 name: iz:ANALOG SNARE 2/3 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 2 name: iz:ANALOG SNARE 2/2 sample: NOISE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - zone: 3 name: iz:ANALOG SNARE 2/1 sample: BD1 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 CHORUSSEND: 100.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -11.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 14.00 - instrument: 99 name: ANALOG KICK 2 zones: - zone: 0 name: iz:ANALOG KICK 2/0 sample: TR909BD key_range: 36 - 36 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 100.00 REVERBSEND: 30.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -797.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -797.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 35.00 - instrument: 100 name: ANALOG KICK 1 zones: - zone: 0 name: iz:ANALOG KICK 1/0 sample: TR909BD key_range: 35 - 35 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 CHORUSSEND: 100.00 REVERBSEND: 40.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVDECAY: 0.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 44.00 - instrument: 101 name: ANALOG SNARE 1 zones: - zone: 0 name: iz:ANALOG SNARE 1/3 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: 500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 1 name: iz:ANALOG SNARE 1/2 sample: NOISE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 200.00 REVERBSEND: 20.00 PAN: -500.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:ANALOG SNARE 1/1 sample: WHITE2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 FILTERQ: 0.00 CHORUSSEND: 200.00 REVERBSEND: 40.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 3 name: iz:ANALOG SNARE 1/0 sample: BD1 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2084.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2084.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -9.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 14.00 - instrument: 102 name: 909 BASS BOOM zones: - zone: 0 name: iz:909 BASS BOOM/1 sample: SINE32 key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 VOLENVATTACK: -8857.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1018.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1018.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 5.00 FINETUNE: -2.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - zone: 1 name: iz:909 BASS BOOM/0 sample: TR909BD key_range: 34 - 34 vel_range: 0 - 128 generators: ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 100.00 REVERBSEND: 60.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 ATTENUATION: 0.00 COARSETUNE: 0.00 OVERRIDEROOTKEY: 31.00 - instrument: 103 name: 909 TOM HI 2 zones: - zone: 0 name: iz:909 TOM HI 2 /0 sample: 909BD11 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1231.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 104 name: 909 TOM HI 1 zones: - zone: 0 name: iz:909 TOM HI 1 /0 sample: 909BD11 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1076.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1076.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 35.00 - instrument: 105 name: 909 TOM MID 2 zones: - zone: 0 name: iz:909 TOM MID 2 /0 sample: 909BD11 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -931.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -931.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - instrument: 106 name: 909 HAT OPEN zones: - zone: 0 name: iz:909 HAT OPEN /3 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 9.00 ENDLOOPADDROFS: -3.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 304.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 140.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 1 name: iz:909 HAT OPEN /2 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 9.00 ENDLOOPADDROFS: -3.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 304.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 140.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - zone: 2 name: iz:909 HAT OPEN /1 sample: NOISE6 key_range: 46 - 46 vel_range: 0 - 128 generators: FILTERQ: 105.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2701.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2701.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 58.00 - zone: 3 name: iz:909 HAT OPEN /0 sample: NOISE5 key_range: 46 - 46 vel_range: 0 - 128 generators: FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 304.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 ATTENUATION: 6.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 107 name: 909 TOM MID 1 zones: - zone: 0 name: iz:909 TOM MID 1 /0 sample: 909BD11 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -797.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -797.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 108 name: 909 TOM LO 2 zones: - zone: 0 name: iz:909 TOM LO 2 /0 sample: 909BD11 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6667.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -672.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 109 name: 909 HAT CLOSED zones: - zone: 0 name: iz:909 HAT CLOSED /3 sample: NOISE6 key_range: 42 - 42 vel_range: 0 - 128 generators: FILTERQ: 105.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2701.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2701.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 54.00 - zone: 1 name: iz:909 HAT CLOSED /2 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 9.00 ENDLOOPADDROFS: -3.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 140.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 2 name: iz:909 HAT CLOSED /1 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 9.00 ENDLOOPADDROFS: -3.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 140.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 3 name: iz:909 HAT CLOSED /0 sample: NOISE5 key_range: 42 - 42 vel_range: 0 - 128 generators: FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 ATTENUATION: 6.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - instrument: 110 name: 909 TOM LO 1 zones: - zone: 0 name: iz:909 TOM LO 1 /0 sample: 909BD11 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 59.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 6726.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - instrument: 111 name: 909 SNARE HI zones: - zone: 0 name: iz:909 SNARE HI /3 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 ATTENUATION: 36.00 FINETUNE: 116.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 1 name: iz:909 SNARE HI /2 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1231.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1231.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - zone: 2 name: iz:909 SNARE HI /1 sample: SINE32 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 27.00 - zone: 3 name: iz:909 SNARE HI /0 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1231.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1231.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - instrument: 112 name: 909 CLAP zones: - zone: 0 name: iz:909 CLAP /0 sample: CLAP21 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 313.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1487.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: 1487.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 46.00 - instrument: 113 name: 909 SNARE LO zones: - zone: 0 name: iz:909 SNARE LO /4 sample: SINE32 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 27.00 - zone: 1 name: iz:909 SNARE LO /3 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: REVERBSEND: 23.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - zone: 2 name: iz:909 SNARE LO /2 sample: NOISE6 key_range: 38 - 38 vel_range: 0 - 128 generators: CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -931.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -931.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 3 name: iz:909 SNARE LO /1 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: REVERBSEND: 23.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 ATTENUATION: 50.80 FINETUNE: 116.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 4 name: iz:909 SNARE LO /0 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: REVERBSEND: 23.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - instrument: 114 name: 909 RIM zones: - zone: 0 name: iz:909 RIM /1 sample: NOISE6 key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 141.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 80.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 64.00 - zone: 1 name: iz:909 RIM /0 sample: SQUARELO key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 111.00 ENDLOOPADDROFS: -6.00 FILTERFC: 6608.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 115 name: 909 BASS DRUM HI zones: - zone: 0 name: iz:909 BASS DRUM HI /1 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -4603.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -251.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -251.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 2.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - zone: 1 name: iz:909 BASS DRUM HI /0 sample: TR909BD key_range: 36 - 36 vel_range: 0 - 128 generators: ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 ATTENUATION: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 31.00 - instrument: 116 name: 909 BASS DRUM LO zones: - zone: 0 name: iz:909 BASS DRUM LO /0 sample: TR909BD key_range: 35 - 35 vel_range: 0 - 128 generators: ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 87.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 87.00 ATTENUATION: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 32.00 - instrument: 117 name: 909 SNARE 2 zones: - zone: 0 name: iz:909 SNARE 2 /2 sample: NOISE5 key_range: 27 - 27 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:909 SNARE 2 /1 sample: SINE32 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 11.00 - zone: 2 name: iz:909 SNARE 2 /0 sample: NOISE5 key_range: 27 - 27 vel_range: 0 - 128 generators: FILTERQ: 75.00 REVERBSEND: 23.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - instrument: 118 name: 909 SNARE 1 zones: - zone: 0 name: iz:909 SNARE 1 /3 sample: NOISE6 key_range: 26 - 26 vel_range: 0 - 128 generators: FILTERQ: 75.00 CHORUSSEND: 995.00 REVERBSEND: 23.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 1 name: iz:909 SNARE 1 /2 sample: NOISE5 key_range: 26 - 26 vel_range: 0 - 128 generators: FILTERQ: 105.00 REVERBSEND: 23.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -672.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:909 SNARE 1 /1 sample: SINE32 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 13.00 - zone: 3 name: iz:909 SNARE 1 /0 sample: NOISE5 key_range: 26 - 26 vel_range: 0 - 128 generators: FILTERQ: 105.00 REVERBSEND: 23.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -672.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 ATTENUATION: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - instrument: 119 name: 909 BASS DRUM 2 zones: - zone: 0 name: iz:909 BASS DRUM 2 /1 sample: SINE32 key_range: 25 - 25 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 392.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -4603.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 304.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 304.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 1 name: iz:909 BASS DRUM 2 /0 sample: TR909BD key_range: 25 - 25 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 156.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 87.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 87.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 26.00 - instrument: 120 name: 909 BASS DRUM 1 zones: - zone: 0 name: iz:909 BASS DRUM 1 /1 sample: SINE32 key_range: 24 - 24 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 300.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -8857.00 VOLENVHOLD: -4842.00 VOLENVDECAY: 316.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 316.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 1 name: iz:909 BASS DRUM 1 /0 sample: TR909BD key_range: 24 - 24 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: -1.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 100.00 REVERBSEND: 20.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -3140.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 87.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 87.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 20.00 - instrument: 121 name: 808 CLAVES zones: - zone: 0 name: iz:808 CLAVES /0 sample: TRIANGLE key_range: 75 - 75 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -3.00 CHORUSSEND: 0.00 REVERBSEND: 10.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 122 name: 808 MARACAS zones: - zone: 0 name: iz:808 MARACAS /0 sample: NOISE5 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 REVERBSEND: 23.00 VOLENVATTACK: -5186.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 5.00 FINETUNE: 100.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 69.00 - instrument: 123 name: 808 BONGO LO zones: - zone: 0 name: iz:808 BONGO LO /0 sample: SINE32 key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -347.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -347.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 60.00 - instrument: 124 name: 808 BONGO MID zones: - zone: 0 name: iz:808 BONGO MID /0 sample: SINE32 key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -672.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - instrument: 125 name: 808 BONGO HI zones: - zone: 0 name: iz:808 BONGO HI /0 sample: SINE32 key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - instrument: 126 name: 808 COWBELL zones: - zone: 0 name: iz:808 COWBELL /1 sample: TRIANGLE key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -3.00 MODLFOTOVOL: 33.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 MODLFOFREQ: -42.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -347.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -347.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 126.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - zone: 1 name: iz:808 COWBELL /0 sample: SQUARE2 key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 24.00 ENDLOOPADDROFS: -4.00 FILTERFC: 10325.00 MODLFOTOVOL: 28.00 CHORUSSEND: 548.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 80.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - instrument: 127 name: 808 HI TOM 2 zones: - zone: 0 name: iz:808 HI TOM 2 /0 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 128 name: 808 CYMBAL zones: - zone: 0 name: iz:808 CYMBAL /3 sample: SINEHI key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: 22.00 CHORUSSEND: 250.00 REVERBSEND: 7.00 MODLFOFREQ: -451.00 VIBLFOFREQ: -725.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1800.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1800.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 96.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 35.00 - zone: 1 name: iz:808 CYMBAL /2 sample: NOISE5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 55.00 - zone: 2 name: iz:808 CYMBAL /1 sample: SINEHI key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 392.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1800.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1800.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 80.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 98.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 3 name: iz:808 CYMBAL /0 sample: NOISE5 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 CHORUSSEND: 627.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1800.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1800.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 129 name: 808 HI TOM 1 zones: - zone: 0 name: iz:808 HI TOM 1 /0 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1405.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1405.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -22.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 130 name: 808 MID TOM 2 zones: - zone: 0 name: iz:808 MID TOM 2 /0 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1231.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 131 name: 808 HAT OPEN zones: - zone: 0 name: iz:808 HAT OPEN /1 sample: SINEHI key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 126.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:808 HAT OPEN /0 sample: NOISE5 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: -1231.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 132 name: 808 MID TOM 1 zones: - zone: 0 name: iz:808 MID TOM 1 /0 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1076.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1076.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 133 name: 808 LO TOM 2 zones: - zone: 0 name: iz:808 LO TOM 2 /0 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -931.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -931.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 134 name: 808 HAT CLOSED zones: - zone: 0 name: iz:808 HAT CLOSED /1 sample: SINEHI key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 126.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:808 HAT CLOSED /0 sample: NOISE5 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 48.00 - instrument: 135 name: 808 LO TOM 1 zones: - zone: 0 name: iz:808 LO TOM 1 /0 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -797.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -797.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 136 name: 808 SNARE DRUM HI zones: - zone: 0 name: iz:808 SNARE DRUM HI /2 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 124.00 CHORUSSEND: 533.00 REVERBSEND: 47.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:808 SNARE DRUM HI /1 sample: NOISE5 key_range: 40 - 40 vel_range: 0 - 128 generators: FILTERQ: 121.00 CHORUSSEND: 556.00 REVERBSEND: 47.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 2 name: iz:808 SNARE DRUM HI /0 sample: SINE32 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 109.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - instrument: 137 name: 808 CLAP zones: - zone: 0 name: iz:808 CLAP /0 sample: CLAP2 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 500.00 REVERBSEND: 20.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1487.00 VOLENVSUSTAIN: 0.00 VOLENVRELEASE: 1487.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 16.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 45.00 - instrument: 138 name: 808 SNARE DRUM LO zones: - zone: 0 name: iz:808 SNARE DRUM LO /2 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: CHORUSSEND: 572.00 REVERBSEND: 54.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 1 name: iz:808 SNARE DRUM LO /1 sample: NOISE5 key_range: 38 - 38 vel_range: 0 - 128 generators: CHORUSSEND: 595.00 REVERBSEND: 54.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 2 name: iz:808 SNARE DRUM LO /0 sample: SINE32 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 FILTERFC: 11151.00 CHORUSSEND: 109.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 28.00 - instrument: 139 name: 808 RIM SHOT zones: - zone: 0 name: iz:808 RIM SHOT /1 sample: NOISE3 key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 38.00 - zone: 1 name: iz:808 RIM SHOT /0 sample: TRIANGLE key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -3.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 21.00 - instrument: 140 name: 808 BASS DRUM HI zones: - zone: 0 name: iz:808 BASS DRUM HI /0 sample: SINE32 key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 39.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -386.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -386.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 141 name: 808 BASS DRUM LO zones: - zone: 0 name: iz:808 BASS DRUM LO /0 sample: SINE32 key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 702.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 702.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 52.00 - instrument: 142 name: 808 SNARE HIGH zones: - zone: 0 name: iz:808 SNARE HIGH /2 sample: NOISE5 key_range: 31 - 31 vel_range: 0 - 128 generators: FILTERQ: 190.00 CHORUSSEND: 729.00 REVERBSEND: 54.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - zone: 1 name: iz:808 SNARE HIGH /1 sample: NOISE5 key_range: 31 - 31 vel_range: 0 - 128 generators: FILTERQ: 190.00 CHORUSSEND: 697.00 REVERBSEND: 54.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 2 name: iz:808 SNARE HIGH /0 sample: SINE32 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 143 name: 808 SNARE SHARP zones: - zone: 0 name: iz:808 SNARE SHARP /2 sample: NOISE5 key_range: 30 - 30 vel_range: 0 - 128 generators: MODENVTOFILTERFC: 7087.00 CHORUSSEND: 493.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 1 name: iz:808 SNARE SHARP /1 sample: NOISE5 key_range: 30 - 30 vel_range: 0 - 128 generators: MODENVTOFILTERFC: 7087.00 CHORUSSEND: 415.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 2 name: iz:808 SNARE SHARP /0 sample: SINE32 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 17.00 - instrument: 144 name: 808 SNARE FILTERED zones: - zone: 0 name: iz:808 SNARE FILTERED /2 sample: NOISE5 key_range: 29 - 29 vel_range: 0 - 128 generators: FILTERQ: 138.00 CHORUSSEND: 650.00 REVERBSEND: 78.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 21.00 - zone: 1 name: iz:808 SNARE FILTERED /1 sample: NOISE5 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 138.00 CHORUSSEND: 627.00 REVERBSEND: 78.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 23.00 - zone: 2 name: iz:808 SNARE FILTERED /0 sample: SINE32 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 145 name: 808 ULTRA SNAPPY zones: - zone: 0 name: iz:808 ULTRA SNAPPY /2 sample: NOISE5 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 75.00 MODENVTOFILTERFC: 7087.00 CHORUSSEND: 431.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 33.00 - zone: 1 name: iz:808 ULTRA SNAPPY /1 sample: NOISE5 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 75.00 MODENVTOFILTERFC: 7087.00 CHORUSSEND: 423.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - zone: 2 name: iz:808 ULTRA SNAPPY /0 sample: SINE32 key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 109.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 16.00 - instrument: 146 name: 808 BASS DRUM MUTE zones: - zone: 0 name: iz:808 BASS DRUM MUTE /0 sample: SINE32 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOFILTERFC: -7087.00 CHORUSSEND: 313.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1076.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1076.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 147 name: 808 BASS DRUM BOOM2 zones: - zone: 0 name: iz:808 BASS DRUM BOOM2/0 sample: SINE32 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 42.00 - instrument: 148 name: 808 BASS DRUM SHORT zones: - zone: 0 name: iz:808 BASS DRUM SHORT/0 sample: SINE32 key_range: 25 - 25 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 149 name: 808 BASS DRUM BOOM1 zones: - zone: 0 name: iz:808 BASS DRUM BOOM1/0 sample: SINE32 key_range: 24 - 24 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 76.00 ENDLOOPADDROFS: -4.00 MODENVTOPITCH: 1200.00 CHORUSSEND: 250.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -11959.00 MODENVDECAY: -1064.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -1064.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1487.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 1487.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - instrument: 150 name: CR78 HI TOM 2 zones: - zone: 0 name: iz:CR78 HI TOM 2/0 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 151 name: CR78 HI TOM 1 zones: - zone: 0 name: iz:CR78 HI TOM 1/0 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -22.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 152 name: CR78 MID TOM 2 zones: - zone: 0 name: iz:CR78 MID TOM 2/0 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 153 name: CR78 MID TOM 1 zones: - zone: 0 name: iz:CR78 MID TOM 1/0 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 154 name: CR78 LO TOM 2 zones: - zone: 0 name: iz:CR78 LO TOM 2/0 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 155 name: CR78 LO TOM 1 zones: - zone: 0 name: iz:CR78 LO TOM 1/0 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 FILTERFC: 10223.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3656.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3656.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 156 name: CR78 BONGO LO global_zone: name: iz:CR78 BONGO LO/0 key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 VOLENVDECAY: -1868.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -1868.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 zones: - instrument: 157 name: CR78 BONGO MID global_zone: name: iz:CR78 BONGO MID/0 key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 VOLENVDECAY: -2463.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -2463.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 1.00 FINETUNE: 40.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 74.00 zones: - instrument: 158 name: CR78 BONGO HI global_zone: name: iz:CR78 BONGO HI/0 key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 10721.00 VOLENVDECAY: -3969.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3969.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: -3.00 FINETUNE: -60.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 61.00 zones: - instrument: 159 name: CR78 COWBELL zones: - zone: 0 name: iz:CR78 COWBELL /1 sample: TRIHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: -108.00 CHORUSSEND: 274.00 REVERBSEND: 15.00 MODLFOFREQ: 114.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 65.00 - zone: 1 name: iz:CR78 COWBELL /0 sample: TRIHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: -97.00 CHORUSSEND: 274.00 REVERBSEND: 15.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 25.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 65.00 - instrument: 160 name: CR78 TAMBOURINE zones: - zone: 0 name: iz:CR78 TAMBOURINE /1 sample: SINEHI key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 190.00 MODLFOTOVOL: 80.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVDECAY: -2325.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2325.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 32.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - zone: 1 name: iz:CR78 TAMBOURINE /0 sample: WHITE3 key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 MODLFOTOVOL: 93.00 REVERBSEND: 0.00 MODLFOFREQ: 114.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2325.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2325.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 65.00 - instrument: 161 name: CR78 HAT OPEN zones: - zone: 0 name: iz:CR78 HAT OPEN /1 sample: WHITE3 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 52.00 - zone: 1 name: iz:CR78 HAT OPEN /0 sample: WHITE3 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 135.00 CHORUSSEND: 313.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1105.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - instrument: 162 name: CR78 HAT CLOSED zones: - zone: 0 name: iz:CR78 HAT CLOSED /1 sample: WHITE3 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 47.00 - zone: 1 name: iz:CR78 HAT CLOSED /0 sample: WHITE3 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 135.00 CHORUSSEND: 313.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 163 name: CR78 SNARE 2 zones: - zone: 0 name: iz:CR78 SNARE 2 /3 sample: WHITE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 15.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -21.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:CR78 SNARE 2 /2 sample: WHITE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 15.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 2 name: iz:CR78 SNARE 2 /1 sample: WHITE2 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 6.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 3 name: iz:CR78 SNARE 2 /0 sample: BD1 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 123.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 5.00 - instrument: 164 name: CR78 METAL zones: - zone: 0 name: iz:CR78 METAL /1 sample: SINEHI key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 8.00 ENDLOOPADDROFS: -5.00 FILTERQ: 190.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -5293.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 65.20 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:CR78 METAL /0 sample: WHITE2 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 FILTERFC: 11092.00 FILTERQ: 190.00 CHORUSSEND: 470.00 REVERBSEND: 0.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -5293.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 165 name: CR78 SNARE 1 zones: - zone: 0 name: iz:CR78 SNARE 1 /3 sample: WHITE2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 36.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 1 name: iz:CR78 SNARE 1 /2 sample: WHITE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 15.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 3.00 OVERRIDEROOTKEY: 50.00 - zone: 2 name: iz:CR78 SNARE 1 /1 sample: WHITE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 15.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -21.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 50.00 - zone: 3 name: iz:CR78 SNARE 1 /0 sample: BD1 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 123.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 15.00 - instrument: 166 name: CR78 RIM SHOT zones: - zone: 0 name: iz:CR78 RIM SHOT /0 sample: TRIANGLE key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -3.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 OVERRIDEROOTKEY: 21.00 - instrument: 167 name: CR78 KICK 2 zones: - zone: 0 name: iz:CR78 KICK 2 /0 sample: TR909BD key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODENVTOPITCH: -1190.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 3.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 43.00 - instrument: 168 name: CR78 KICK 1 zones: - zone: 0 name: iz:CR78 KICK 1 /0 sample: TR909BD key_range: 35 - 35 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 MODENVTOPITCH: -1190.00 VIBLFOFREQ: -15597.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1586.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1586.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 46.00 - instrument: 169 name: CR78 CLAVE zones: - zone: 0 name: iz:CR78 CLAVE /0 sample: TRIANGLE key_range: 34 - 34 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 16.00 ENDLOOPADDROFS: -3.00 CHORUSSEND: 156.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 84.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 13.00 - instrument: 170 name: CR78 MARACAS zones: - zone: 0 name: iz:CR78 MARACAS /0 sample: WHITE2 key_range: 33 - 33 vel_range: 0 - 128 generators: CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 ATTENUATION: 20.80 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 30.00 - instrument: 171 name: CR78 GUIRO zones: - zone: 0 name: iz:CR78 GUIRO /0 sample: 1 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERQ: 120.00 MODLFOTOVOL: -56.00 REVERBSEND: 0.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -3161.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 32.00 - instrument: 172 name: 101 LASER 3 zones: - zone: 0 name: iz:101 LASER 3/0 sample: LASER1 key_range: 53 - 53 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 60.00 REVERBSEND: 10.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 586.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 586.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 52.00 - instrument: 173 name: 101 LASER 2 zones: - zone: 0 name: iz:101 LASER 2/0 sample: RIM key_range: 52 - 52 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 CHORUSSEND: 313.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 3217.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 3688.00 ATTENUATION: 0.00 FINETUNE: 45.00 OVERRIDEROOTKEY: 58.00 - instrument: 174 name: 101 LASER 1 zones: - zone: 0 name: iz:101 LASER 1/0 sample: CLICK key_range: 51 - 51 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 548.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVDECAY: 4968.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: 3919.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 OVERRIDEROOTKEY: 51.00 - instrument: 175 name: 101 MARACAS zones: - zone: 0 name: iz:101 MARACAS /0 sample: WHITE3 key_range: 71 - 71 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 190.00 MODLFOTOVOL: -103.00 CHORUSSEND: 995.00 REVERBSEND: 23.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -1674.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 74.00 - instrument: 176 name: 101 SHAKER zones: - zone: 0 name: iz:101 SHAKER /0 sample: WHITE3 key_range: 69 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 235.00 REVERBSEND: 15.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -1674.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -4603.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - instrument: 177 name: 101 BONGO LO zones: - zone: 0 name: iz:101 BONGO LO /0 sample: SINE32 key_range: 64 - 64 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 60.00 - instrument: 178 name: 101 BONGO MID zones: - zone: 0 name: iz:101 BONGO MID /0 sample: SINE32 key_range: 63 - 63 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -2352.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -2352.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 53.00 - instrument: 179 name: 101 BONGO HI zones: - zone: 0 name: iz:101 BONGO HI /0 sample: SINE32 key_range: 62 - 62 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 78.00 REVERBSEND: 7.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVDECAY: -3140.00 MODENVSUSTAIN: 1000.00 MODENVRELEASE: -3140.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3284.00 VOLENVSUSTAIN: 960.00 VOLENVRELEASE: -3284.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 95.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - instrument: 180 name: 101 COWBELL zones: - zone: 0 name: iz:101 COWBELL /1 sample: TRIHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 CHORUSSEND: 117.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 97.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 63.00 - zone: 1 name: iz:101 COWBELL /0 sample: TRIHI key_range: 56 - 56 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: 86.00 CHORUSSEND: 117.00 REVERBSEND: 15.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 20.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 97.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 63.00 - instrument: 181 name: 101 TAMBOURINE zones: - zone: 0 name: iz:101 TAMBOURINE /1 sample: TRIHI key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 13.00 ENDLOOPADDROFS: -5.00 MODLFOTOVOL: 60.00 CHORUSSEND: 117.00 REVERBSEND: 31.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 110.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - zone: 1 name: iz:101 TAMBOURINE /0 sample: WHITE3 key_range: 54 - 54 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 111.00 MODLFOTOVOL: -103.00 CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -42.00 VIBLFOFREQ: -1674.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 61.00 - instrument: 182 name: 101 HI TOM 2 zones: - zone: 0 name: iz:101 HI TOM 2 /0 sample: SINE32 key_range: 50 - 50 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 183 name: 101 CYMBAL zones: - zone: 0 name: iz:101 CYMBAL /1 sample: WHITE2 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 46.00 - zone: 1 name: iz:101 CYMBAL /0 sample: WHITE3 key_range: 49 - 49 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 33.00 MODLFOTOFILTERFC: 112.00 MODLFOTOVOL: 9.00 CHORUSSEND: 893.00 REVERBSEND: 23.00 MODLFOFREQ: 460.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 2295.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 2065.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 55.00 - instrument: 184 name: 101 HI TOM 1 zones: - zone: 0 name: iz:101 HI TOM 1 /0 sample: SINE32 key_range: 48 - 48 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -22.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 185 name: 101 MID TOM 2 zones: - zone: 0 name: iz:101 MID TOM 2 /0 sample: SINE32 key_range: 47 - 47 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 34.00 - instrument: 186 name: 101 HAT OPEN zones: - zone: 0 name: iz:101 HAT OPEN /0 sample: WHITE3 key_range: 46 - 46 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 1105.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - instrument: 187 name: 101 MID TOM 1 zones: - zone: 0 name: iz:101 MID TOM 1 /0 sample: SINE32 key_range: 45 - 45 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - instrument: 188 name: 101 LO TOM 2 zones: - zone: 0 name: iz:101 LO TOM 2 /0 sample: SINE32 key_range: 43 - 43 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - instrument: 189 name: 101 HAT CLOSED zones: - zone: 0 name: iz:101 HAT CLOSED /0 sample: WHITE3 key_range: 42 - 42 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - instrument: 190 name: 101 LO TOM 1 zones: - zone: 0 name: iz:101 LO TOM 1 /0 sample: SINE32 key_range: 41 - 41 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 75.00 ENDLOOPADDROFS: -5.00 MODENVTOPITCH: -1190.00 FILTERFC: 10384.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - instrument: 191 name: 101 SNARE 2 zones: - zone: 0 name: iz:101 SNARE 2 /3 sample: WHITE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 90.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:101 SNARE 2 /2 sample: WHITE3 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 88.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1599.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1599.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 49.00 - zone: 2 name: iz:101 SNARE 2 /1 sample: WHITE2 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 36.00 - zone: 3 name: iz:101 SNARE 2 /0 sample: BD1 key_range: 40 - 40 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3729.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3729.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 192 name: 101 CLAPS zones: - zone: 0 name: iz:101 CLAPS /1 sample: WHITE2 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 MODLFOTOVOL: 93.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 MODLFOFREQ: 114.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 49.00 - zone: 1 name: iz:101 CLAPS /0 sample: WHITE4 key_range: 39 - 39 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 MODLFOTOVOL: 93.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 MODLFOFREQ: 46.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 50.80 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 39.00 - instrument: 193 name: 101 SNARE 1 zones: - zone: 0 name: iz:101 SNARE 1 /3 sample: WHITE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 47.00 - zone: 1 name: iz:101 SNARE 1 /2 sample: WHITE3 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 2 name: iz:101 SNARE 1 /1 sample: WHITE2 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 37.00 - zone: 3 name: iz:101 SNARE 1 /0 sample: BD1 key_range: 38 - 38 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 18.00 - instrument: 194 name: 101 RIM SHOT zones: - zone: 0 name: iz:101 RIM SHOT /1 sample: WHITE4 key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -4603.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -4603.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 37.00 - zone: 1 name: iz:101 RIM SHOT /0 sample: TRIANGLE key_range: 37 - 37 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 15.00 ENDLOOPADDROFS: -3.00 REVERBSEND: 15.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -12000.00 VOLENVSUSTAIN: 1000.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 22.00 - instrument: 195 name: 101 KICK 2 zones: - zone: 0 name: iz:101 KICK 2 /0 sample: 808BD key_range: 36 - 36 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 119.00 ENDLOOPADDROFS: -8.00 CHORUSSEND: 78.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - instrument: 196 name: 101 KICK 1 zones: - zone: 0 name: iz:101 KICK 1 /0 sample: TR909BD key_range: 35 - 35 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 CHORUSSEND: 70.00 REVERBSEND: 15.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -159.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -159.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 49.00 - instrument: 197 name: 101 SNARE EXTRA #02 zones: - zone: 0 name: iz:101 SNARE EXTRA #02/2 sample: WHITE3 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 40.00 - zone: 1 name: iz:101 SNARE EXTRA #02/1 sample: WHITE3 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -74.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -74.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 2 name: iz:101 SNARE EXTRA #02/0 sample: BD1 key_range: 32 - 32 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 19.00 - instrument: 198 name: 101 HAT OP EXTRA zones: - zone: 0 name: iz:101 HAT OP EXTRA /0 sample: WHITE3 key_range: 31 - 31 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 190.00 CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: 653.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: 284.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - instrument: 199 name: 101 HAT CL EXTRA zones: - zone: 0 name: iz:101 HAT CL EXTRA /0 sample: WHITE3 key_range: 30 - 30 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 190.00 CHORUSSEND: 995.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 30.00 - instrument: 200 name: 101 SNARE EXTRA 3 zones: - zone: 0 name: iz:101 SNARE EXTRA 3 /2 sample: WHITE3 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 75.00 CHORUSSEND: 995.00 REVERBSEND: 31.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 61.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 1 name: iz:101 SNARE EXTRA 3 /1 sample: WHITE3 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 FILTERQ: 75.00 CHORUSSEND: 995.00 REVERBSEND: 31.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 2 name: iz:101 SNARE EXTRA 3 /0 sample: BD1 key_range: 29 - 29 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 13.00 - instrument: 201 name: 101 KICK EXTRA 3 zones: - zone: 0 name: iz:101 KICK EXTRA 3 /0 sample: TR909BD key_range: 28 - 28 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 VIBLFOTOPITCH: -1190.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 39.00 REVERBSEND: 7.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 33.00 - instrument: 202 name: 101 SNARE EXTRA 2 zones: - zone: 0 name: iz:101 SNARE EXTRA 2 /3 sample: WHITE2 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2352.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2352.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 29.00 - zone: 1 name: iz:101 SNARE EXTRA 2 /2 sample: WHITE3 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -672.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 41.00 - zone: 2 name: iz:101 SNARE EXTRA 2 /1 sample: WHITE3 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 38.00 - zone: 3 name: iz:101 SNARE EXTRA 2 /0 sample: BD1 key_range: 27 - 27 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2061.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2061.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 14.00 - instrument: 203 name: 101 SNARE EXTRA 1 zones: - zone: 0 name: iz:101 SNARE EXTRA 1 /3 sample: WHITE2 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 3.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 588.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -2701.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -2701.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 31.00 - zone: 1 name: iz:101 SNARE EXTRA 1 /2 sample: WHITE3 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: 500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 44.00 - zone: 2 name: iz:101 SNARE EXTRA 1 /1 sample: WHITE3 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: -4.00 CHORUSSEND: 995.00 REVERBSEND: 39.00 PAN: -500.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -1817.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -1817.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 43.00 - zone: 3 name: iz:101 SNARE EXTRA 1 /0 sample: BD1 key_range: 26 - 26 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 122.00 ENDLOOPADDROFS: -6.00 REVERBSEND: 0.00 MODLFOFREQ: -725.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -3140.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -3140.00 STARTLOOPADDRCOARSEOFS: 0.00 ATTENUATION: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 14.00 - instrument: 204 name: 101 KICK EXTRA 2 zones: - zone: 0 name: iz:101 KICK EXTRA 2 /0 sample: TR909BD key_range: 25 - 25 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 MODENVTOPITCH: -1190.00 CHORUSSEND: 70.00 REVERBSEND: 7.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -4603.00 MODENVSUSTAIN: 0.00 MODENVRELEASE: 3084.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -797.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -797.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 24.00 - instrument: 205 name: 101 KICK EXTRA 1 zones: - zone: 0 name: iz:101 KICK EXTRA 1 /0 sample: TR909BD key_range: 24 - 24 vel_range: 0 - 128 generators: VIBLFOTOPITCH: -1190.00 FILTERFC: 10266.00 CHORUSSEND: 70.00 REVERBSEND: 15.00 MODLFOFREQ: -15600.00 VIBLFOFREQ: -15600.00 MODENVATTACK: -12000.00 MODENVSUSTAIN: 0.00 VOLENVATTACK: -12000.00 VOLENVHOLD: -12000.00 VOLENVDECAY: -559.00 VOLENVSUSTAIN: 1000.00 VOLENVRELEASE: -559.00 ATTENUATION: 0.00 FINETUNE: 0.00 SAMPLEMODE: 0.00 OVERRIDEROOTKEY: 34.00 - instrument: 206 name: ADDITIVE BELL global_zone: name: iz:ADDITIVE BELL/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 60.00 MODENVATTACK: -5186.00 MODENVDECAY: -5186.00 MODENVRELEASE: -5186.00 VOLENVATTACK: -8857.00 VOLENVDECAY: -3986.00 VOLENVRELEASE: -3986.00 COARSETUNE: 12.00 zones: - zone: 0 name: iz:ADDITIVE BELL/4 sample: wav1_1 key_range: 0 - 57 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 48.00 - zone: 1 name: iz:ADDITIVE BELL/3 sample: wav1_3 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - zone: 2 name: iz:ADDITIVE BELL/2 sample: wav1_2 key_range: 58 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 - zone: 3 name: iz:ADDITIVE BELL/1 sample: wav1_4 key_range: 82 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - instrument: 207 name: SMOOTH SAW global_zone: name: iz:SMOOTH SAW/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:SMOOTH SAW/4 sample: SAW4 key_range: 94 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 1 name: iz:SMOOTH SAW/3 sample: SAW3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -7.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - zone: 2 name: iz:SMOOTH SAW/2 sample: SAW2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - zone: 3 name: iz:SMOOTH SAW/1 sample: SAW1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 - instrument: 208 name: MELODIC VIBRATO global_zone: name: iz:MELODIC VIBRATO/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: 12.00 zones: - zone: 0 name: iz:MELODIC VIBRATO/4 sample: W12_4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -65.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 95.00 - zone: 1 name: iz:MELODIC VIBRATO/3 sample: W13_3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 91.00 - zone: 2 name: iz:MELODIC VIBRATO/2 sample: W12_2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 3 name: iz:MELODIC VIBRATO/1 sample: W12_1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - instrument: 209 name: FM CLANG global_zone: name: iz:FM CLANG/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FM CLANG/1 sample: W10000 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 23.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 77.00 - instrument: 210 name: FM3 global_zone: name: iz:FM3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -5530.00 MODENVATTACK: -4842.00 MODENVHOLD: -4842.00 MODENVDECAY: -4842.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:FM3/4 sample: dm4_a4 key_range: 94 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 92.00 - zone: 1 name: iz:FM3/3 sample: dm4_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 13.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 80.00 - zone: 2 name: iz:FM3/2 sample: dm4_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 68.00 - zone: 3 name: iz:FM3/1 sample: dm4_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 56.00 - instrument: 211 name: FM BELL 2 global_zone: name: iz:FM BELL 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -5530.00 MODENVATTACK: -4842.00 MODENVHOLD: -4842.00 MODENVDECAY: -4842.00 MODENVRELEASE: -3986.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FM BELL 2/4 sample: W7_4 key_range: 94 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 96.00 - zone: 1 name: iz:FM BELL 2/3 sample: W7_3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 2 name: iz:FM BELL 2/2 sample: W7_2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - zone: 3 name: iz:FM BELL 2/1 sample: W7_1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -1.00 SAMPLEMODE: 1.00 - instrument: 212 name: HARSH FM BASS global_zone: name: iz:HARSH FM BASS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:HARSH FM BASS/4 sample: W6_4 key_range: 94 - 127 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -48.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 96.00 - zone: 1 name: iz:HARSH FM BASS/3 sample: W6_3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 65.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 2 name: iz:HARSH FM BASS/2 sample: W6_2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 71.00 - zone: 3 name: iz:HARSH FM BASS/1 sample: W6_1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 28.00 SAMPLEMODE: 1.00 - instrument: 213 name: FAERIE CHORAL global_zone: name: iz:FAERIE CHORAL /0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FAERIE CHORAL /1 sample: MOD2_C3 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: -30.00 ENDLOOPADDROFS: 4.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 82.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - instrument: 214 name: AAA global_zone: name: iz:AAA/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -5530.00 MODENVATTACK: -4842.00 MODENVHOLD: -4842.00 MODENVDECAY: -4842.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:AAA/1 sample: 191 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 31.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 80.00 - instrument: 215 name: OINK global_zone: name: iz:OINK/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: -1.00 zones: - zone: 0 name: iz:OINK/1 sample: WAV5 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 77.00 - instrument: 216 name: CLINK global_zone: name: iz:CLINK/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:CLINK/1 sample: FM90000 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 94.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - instrument: 217 name: SMOOTH SQUARE global_zone: name: iz:SMOOTH SQUARE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -8590.00 VOLENVHOLD: -8359.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 FINETUNE: 0.00 zones: - zone: 0 name: iz:SMOOTH SQUARE/4 sample: SQ4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -48.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 1 name: iz:SMOOTH SQUARE/3 sample: SQ3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 79.00 - zone: 2 name: iz:SMOOTH SQUARE/2 sample: SQ2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -26.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - zone: 3 name: iz:SMOOTH SQUARE/1 sample: SQ1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -15.00 SAMPLEMODE: 1.00 - instrument: 218 name: HALF RECTIFICATION global_zone: name: iz:HALF RECTIFICATION/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 FINETUNE: 0.00 zones: - zone: 0 name: iz:HALF RECTIFICATION/4 sample: HR4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -48.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 1 name: iz:HALF RECTIFICATION/3 sample: HR3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 29.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 80.00 - zone: 2 name: iz:HALF RECTIFICATION/2 sample: HR2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 10.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 73.00 - zone: 3 name: iz:HALF RECTIFICATION/1 sample: HR1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -26.00 SAMPLEMODE: 1.00 - instrument: 219 name: VIBRAPHONE global_zone: name: iz:VIBRAPHONE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:VIBRAPHONE/1 sample: HOLLOW key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 47.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - instrument: 220 name: GRUNGE global_zone: name: iz:GRUNGE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 FINETUNE: 0.00 zones: - zone: 0 name: iz:GRUNGE/1 sample: GRUNGE key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 83.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 78.00 - instrument: 221 name: PIPES global_zone: name: iz:PIPES/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:PIPES/1 sample: BELL4 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 45.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 78.00 - instrument: 222 name: BELLS global_zone: name: iz:BELLS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:BELLS/1 sample: BELL10000 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 41.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 78.00 - instrument: 223 name: BREEZE global_zone: name: iz:BREEZE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:BREEZE/1 sample: HISS key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 FILTERFC: 11921.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 COARSETUNE: 0.00 FINETUNE: 57.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 82.00 - instrument: 224 name: AAHS global_zone: name: iz:AAHS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 FINETUNE: 0.00 zones: - zone: 0 name: iz:AAHS/1 sample: AAH key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 28.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 76.00 - instrument: 225 name: ELECTRIC BASS global_zone: name: iz:ELECTRIC BASS/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:ELECTRIC BASS/4 sample: W8_4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -6.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 96.00 - zone: 1 name: iz:ELECTRIC BASS/3 sample: W8_3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -8.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 91.00 - zone: 2 name: iz:ELECTRIC BASS/2 sample: W8_2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 15.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 84.00 - zone: 3 name: iz:ELECTRIC BASS/1 sample: W8_1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 72.00 - instrument: 226 name: DM 3 global_zone: name: iz:DM 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 COARSETUNE: -12.00 zones: - zone: 0 name: iz:DM 3/4 sample: dm3_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:DM 3/3 sample: dm3_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 14.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:DM 3/2 sample: dm3_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:DM 3/1 sample: dm3_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 1.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 227 name: DM 2 global_zone: name: iz:DM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:DM 2/4 sample: dm2_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:DM 2/3 sample: dm2_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:DM 2/2 sample: dm2_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:DM 2/1 sample: dm2_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 228 name: DM 1 global_zone: name: iz:DM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:DM 1/4 sample: dm1_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:DM 1/3 sample: dm1_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -39.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:DM 1/2 sample: dm1_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -48.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:DM 1/1 sample: dm1_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 229 name: WHITENOISE 3 global_zone: name: iz:WHITENOISE 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:WHITENOISE 3/1 sample: Noise3 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 2.00 ENDLOOPADDROFS: 4.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 75.00 - instrument: 230 name: TRIANGLE WAVE global_zone: name: iz:TRIANGLE WAVE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -9559.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:TRIANGLE WAVE/4 sample: Tri_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:TRIANGLE WAVE/3 sample: Tri_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 4.00 ENDLOOPADDROFS: 4.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:TRIANGLE WAVE/2 sample: Tri_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:TRIANGLE WAVE/1 sample: Tri_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 231 name: SQUARE WAVE global_zone: name: iz:SQUARE WAVE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -8857.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:SQUARE WAVE/4 sample: Sq_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:SQUARE WAVE/3 sample: Sq_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:SQUARE WAVE/2 sample: Sq_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:SQUARE WAVE/1 sample: Sq_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 232 name: SINE WAVE global_zone: name: iz:SINE WAVE/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:SINE WAVE/4 sample: Sine_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:SINE WAVE/3 sample: Sine_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:SINE WAVE/2 sample: Sine_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:SINE WAVE/1 sample: Sine_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 5.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 233 name: SAWTOOTH global_zone: name: iz:SAWTOOTH/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -4842.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:SAWTOOTH/4 sample: Saw_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:SAWTOOTH/3 sample: Saw_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:SAWTOOTH/2 sample: Saw_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:SAWTOOTH/1 sample: Saw_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 234 name: ADDITIVE 3 global_zone: name: iz:ADDITIVE 3/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:ADDITIVE 3/4 sample: add3_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:ADDITIVE 3/3 sample: add3_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:ADDITIVE 3/2 sample: add3_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:ADDITIVE 3/1 sample: add3_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 235 name: WHITENOISE 0 global_zone: name: iz:WHITENOISE 0/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:WHITENOISE 0/1 sample: Noise4 key_range: 0 - 128 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 75.00 - instrument: 236 name: FM 2 global_zone: name: iz:FM 2/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 CHORUSSEND: 0.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FM 2/4 sample: Fm2_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:FM 2/3 sample: Fm2_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:FM 2/2 sample: Fm2_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:FM 2/1 sample: Fm2_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 - instrument: 237 name: FM 1 global_zone: name: iz:FM 1/0 key_range: 0 - 128 vel_range: 0 - 128 generators: FILTERFC: 11921.00 FILTERQ: 100.00 MODENVDELAY: -6688.00 MODENVATTACK: 0.00 MODENVHOLD: 0.00 MODENVDECAY: 0.00 MODENVRELEASE: 0.00 VOLENVATTACK: -7973.00 VOLENVHOLD: -2786.00 VOLENVDECAY: 0.00 VOLENVRELEASE: 0.00 zones: - zone: 0 name: iz:FM 1/4 sample: Fm3_a4 key_range: 94 - 105 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 CHORUSSEND: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 81.00 - zone: 1 name: iz:FM 1/3 sample: Fm3_a3 key_range: 82 - 93 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 69.00 - zone: 2 name: iz:FM 1/2 sample: Fm3_a2 key_range: 70 - 81 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: -4.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 57.00 - zone: 3 name: iz:FM 1/1 sample: Fm3_a1 key_range: 0 - 69 vel_range: 0 - 128 generators: STARTLOOPADDROFS: 0.00 ENDLOOPADDROFS: 0.00 STARTLOOPADDRCOARSEOFS: 0.00 ENDLOOPADDRCOARSEOFS: 0.00 FINETUNE: 0.00 SAMPLEMODE: 1.00 OVERRIDEROOTKEY: 45.00 samples: - sample: 0 name: FM4-0440 source_start: 3520 source_end: 7015 source_loopstart: 30 source_loopend: 102 start: 0 end: 107 loopstart: 30 loopend: 102 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 1 name: Tri2 source_start: 7016 source_end: 10507 source_loopstart: 30 source_loopend: 104 start: 0 end: 111 loopstart: 30 loopend: 104 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 2 name: Tri-Sq source_start: 10508 source_end: 14036 source_loopstart: 29 source_loopend: 175 start: 0 end: 182 loopstart: 29 loopend: 175 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 3 name: FM5-0440 source_start: 14037 source_end: 17574 source_loopstart: 53 source_loopend: 199 start: 0 end: 207 loopstart: 53 loopend: 199 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 4 name: Bell2-0440 source_start: 17575 source_end: 21064 source_loopstart: 37 source_loopend: 110 start: 0 end: 115 loopstart: 37 loopend: 110 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 5 name: cowbell source_start: 21065 source_end: 24611 source_loopstart: 8 source_loopend: 121 start: 0 end: 150 loopstart: 8 loopend: 121 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 6 name: claploop source_start: 24612 source_end: 28979 source_loopstart: 8 source_loopend: 4402 start: 0 end: 4409 loopstart: 8 loopend: 4402 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 7 name: white5 source_start: 28980 source_end: 33250 source_loopstart: 22 source_loopend: 4397 start: 0 end: 4409 loopstart: 22 loopend: 4397 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 8 name: sinehi source_start: 33251 source_end: 36692 source_loopstart: 19 source_loopend: 116 start: 0 end: 122 loopstart: 19 loopend: 116 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 9 name: white808-2 source_start: 36693 source_end: 41880 source_loopstart: 33 source_loopend: 6381 start: 0 end: 6414 loopstart: 33 loopend: 6381 samplerate: 32075 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 10 name: bdloop source_start: 41881 source_end: 45432 source_loopstart: 10 source_loopend: 404 start: 0 end: 501 loopstart: 10 loopend: 404 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 11 name: THROB DRUM source_start: 45433 source_end: 49612 source_loopstart: 2375 source_loopend: 3426 start: 0 end: 3504 loopstart: 2375 loopend: 3426 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 12 name: 909BD11 source_start: 49613 source_end: 53158 source_loopstart: 1 source_loopend: 332 start: 0 end: 331 loopstart: 1 loopend: 332 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 13 name: CLAP21 source_start: 53159 source_end: 57828 source_loopstart: 1 source_loopend: 4796 start: 0 end: 4795 loopstart: 1 loopend: 4796 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 14 name: SQUARELO source_start: 57829 source_end: 61429 source_loopstart: 1 source_loopend: 437 start: 0 end: 436 loopstart: 1 loopend: 437 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 15 name: NOISE6 source_start: 61430 source_end: 66042 source_loopstart: 1 source_loopend: 5227 start: 0 end: 5226 loopstart: 1 loopend: 5227 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 16 name: SQUARE2 source_start: 66043 source_end: 69508 source_loopstart: 1 source_loopend: 113 start: 0 end: 112 loopstart: 1 loopend: 113 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 17 name: CLAP2 source_start: 69509 source_end: 74152 source_loopstart: 1 source_loopend: 4796 start: 0 end: 4795 loopstart: 1 loopend: 4796 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 18 name: NOISE3 source_start: 74153 source_end: 80151 source_loopstart: 1 source_loopend: 12828 start: 0 end: 12827 loopstart: 1 loopend: 12828 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 19 name: NOISE5 source_start: 80152 source_end: 84417 source_loopstart: 1 source_loopend: 3306 start: 0 end: 3305 loopstart: 1 loopend: 3306 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 20 name: SINEHI source_start: 84418 source_end: 87884 source_loopstart: 1 source_loopend: 22 start: 0 end: 21 loopstart: 1 loopend: 22 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 21 name: 1 source_start: 87885 source_end: 91490 source_loopstart: 1 source_loopend: 787 start: 0 end: 786 loopstart: 1 loopend: 787 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 22 name: LASER1 source_start: 91491 source_end: 96317 source_loopstart: 1 source_loopend: 7497 start: 0 end: 7496 loopstart: 1 loopend: 7497 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 23 name: RIM source_start: 96318 source_end: 99919 source_loopstart: 1 source_loopend: 615 start: 0 end: 614 loopstart: 1 loopend: 615 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 24 name: CLICK source_start: 99920 source_end: 103532 source_loopstart: 1 source_loopend: 422 start: 0 end: 421 loopstart: 1 loopend: 422 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 25 name: TRIHI source_start: 103533 source_end: 107008 source_loopstart: 1 source_loopend: 49 start: 0 end: 48 loopstart: 1 loopend: 49 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 26 name: SINE32 source_start: 107009 source_end: 110585 source_loopstart: 1 source_loopend: 401 start: 0 end: 400 loopstart: 1 loopend: 401 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 27 name: WHITE4 source_start: 110586 source_end: 114989 source_loopstart: 1 source_loopend: 4410 start: 0 end: 4409 loopstart: 1 loopend: 4410 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 28 name: TRIANGLE source_start: 114990 source_end: 118450 source_loopstart: 1 source_loopend: 78 start: 0 end: 77 loopstart: 1 loopend: 78 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 29 name: 808BD source_start: 118451 source_end: 122100 source_loopstart: 1 source_loopend: 679 start: 0 end: 678 loopstart: 1 loopend: 679 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 30 name: WHITE2 source_start: 122101 source_end: 126500 source_loopstart: 1 source_loopend: 4410 start: 0 end: 4409 loopstart: 1 loopend: 4410 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 31 name: WHITE3 source_start: 126501 source_end: 130824 source_loopstart: 1 source_loopend: 4410 start: 0 end: 4409 loopstart: 1 loopend: 4410 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 32 name: BD1 source_start: 130825 source_end: 134482 source_loopstart: 1 source_loopend: 680 start: 0 end: 679 loopstart: 1 loopend: 680 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 33 name: TR909BD source_start: 134483 source_end: 138216 source_loopstart: 1 source_loopend: 3954 start: 0 end: 3953 loopstart: 1 loopend: 3954 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 34 name: wav1_1 source_start: 138217 source_end: 141946 source_loopstart: 33 source_loopend: 615 start: 0 end: 730 loopstart: 33 loopend: 615 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 35 name: wav1_3 source_start: 141947 source_end: 145544 source_loopstart: 16 source_loopend: 162 start: 0 end: 291 loopstart: 16 loopend: 162 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 36 name: wav1_2 source_start: 145545 source_end: 149130 source_loopstart: 32 source_loopend: 322 start: 0 end: 362 loopstart: 32 loopend: 322 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 37 name: wav1_4 source_start: 149131 source_end: 152687 source_loopstart: 10 source_loopend: 156 start: 0 end: 180 loopstart: 10 loopend: 156 samplerate: 32000 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 38 name: SAW4 source_start: 152688 source_end: 156153 source_loopstart: 26 source_loopend: 68 start: 0 end: 69 loopstart: 26 loopend: 68 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 39 name: SAW3 source_start: 156154 source_end: 159614 source_loopstart: 39 source_loopend: 95 start: 0 end: 98 loopstart: 39 loopend: 95 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 40 name: SAW2 source_start: 159615 source_end: 163079 source_loopstart: 39 source_loopend: 123 start: 0 end: 127 loopstart: 39 loopend: 123 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 41 name: SAW1 source_start: 163080 source_end: 166609 source_loopstart: 91 source_loopend: 259 start: 0 end: 269 loopstart: 91 loopend: 259 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 42 name: W12_4 source_start: 166610 source_end: 170094 source_loopstart: 14 source_loopend: 57 start: 0 end: 56 loopstart: 14 loopend: 57 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 43 name: W13_3 source_start: 170095 source_end: 173569 source_loopstart: 1 source_loopend: 57 start: 0 end: 68 loopstart: 1 loopend: 57 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 44 name: W12_2 source_start: 173570 source_end: 177041 source_loopstart: 3 source_loopend: 87 start: 0 end: 108 loopstart: 3 loopend: 87 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 45 name: W12_1 source_start: 177042 source_end: 180550 source_loopstart: 24 source_loopend: 193 start: 0 end: 195 loopstart: 24 loopend: 193 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 46 name: W10000 source_start: 180551 source_end: 184068 source_loopstart: 10 source_loopend: 138 start: 0 end: 159 loopstart: 10 loopend: 138 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 47 name: W7_4 source_start: 184069 source_end: 187551 source_loopstart: 21 source_loopend: 105 start: 0 end: 111 loopstart: 21 loopend: 105 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 48 name: W7_3 source_start: 187552 source_end: 191088 source_loopstart: 38 source_loopend: 207 start: 0 end: 226 loopstart: 38 loopend: 207 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 49 name: W7_2 source_start: 191089 source_end: 194691 source_loopstart: 78 source_loopend: 415 start: 0 end: 424 loopstart: 78 loopend: 415 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 50 name: W7_1 source_start: 194692 source_end: 198397 source_loopstart: 150 source_loopend: 824 start: 0 end: 827 loopstart: 150 loopend: 824 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 51 name: W6_4 source_start: 198398 source_end: 201929 source_loopstart: 35 source_loopend: 117 start: 0 end: 130 loopstart: 35 loopend: 117 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 52 name: W6_3 source_start: 201930 source_end: 205452 source_loopstart: 29 source_loopend: 204 start: 0 end: 226 loopstart: 29 loopend: 204 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 53 name: W6_2 source_start: 205453 source_end: 209055 source_loopstart: 105 source_loopend: 461 start: 0 end: 479 loopstart: 105 loopend: 461 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 54 name: W6_1 source_start: 209056 source_end: 212786 source_loopstart: 3 source_loopend: 688 start: 0 end: 993 loopstart: 3 loopend: 688 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 55 name: MOD2_C3 source_start: 212787 source_end: 216235 source_loopstart: 30 source_loopend: 114 start: 0 end: 117 loopstart: 30 loopend: 114 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 56 name: FM90000 source_start: 216236 source_end: 219875 source_loopstart: 146 source_loopend: 443 start: 0 end: 456 loopstart: 146 loopend: 443 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 57 name: 191 source_start: 219876 source_end: 223539 source_loopstart: 74 source_loopend: 176 start: 0 end: 190 loopstart: 74 loopend: 176 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 58 name: WAV5 source_start: 223540 source_end: 227008 source_loopstart: 8 source_loopend: 69 start: 0 end: 119 loopstart: 8 loopend: 69 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 59 name: SQ4 source_start: 227009 source_end: 230476 source_loopstart: 19 source_loopend: 60 start: 0 end: 62 loopstart: 19 loopend: 60 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 60 name: SQ3 source_start: 230477 source_end: 233941 source_loopstart: 22 source_loopend: 78 start: 0 end: 81 loopstart: 22 loopend: 78 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 61 name: SQ2 source_start: 233942 source_end: 237401 source_loopstart: 35 source_loopend: 118 start: 0 end: 121 loopstart: 35 loopend: 118 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 62 name: SQ1 source_start: 237402 source_end: 240898 source_loopstart: 65 source_loopend: 232 start: 0 end: 236 loopstart: 65 loopend: 232 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 63 name: HR4 source_start: 240899 source_end: 244359 source_loopstart: 14 source_loopend: 55 start: 0 end: 57 loopstart: 14 loopend: 55 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 64 name: HR3 source_start: 244360 source_end: 247819 source_loopstart: 19 source_loopend: 73 start: 0 end: 76 loopstart: 19 loopend: 73 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 65 name: HR2 source_start: 247820 source_end: 251284 source_loopstart: 16 source_loopend: 96 start: 0 end: 103 loopstart: 16 loopend: 96 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 66 name: HR1 source_start: 251285 source_end: 254739 source_loopstart: 29 source_loopend: 195 start: 0 end: 200 loopstart: 29 loopend: 195 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 67 name: HOLLOW source_start: 254740 source_end: 258324 source_loopstart: 130 source_loopend: 336 start: 0 end: 356 loopstart: 130 loopend: 336 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 68 name: BELL4 source_start: 258325 source_end: 261965 source_loopstart: 30 source_loopend: 397 start: 0 end: 400 loopstart: 30 loopend: 397 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 69 name: GRUNGE source_start: 261966 source_end: 265612 source_loopstart: 267 source_loopend: 517 start: 0 end: 519 loopstart: 267 loopend: 517 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 70 name: BELL10000 source_start: 265613 source_end: 269259 source_loopstart: 350 source_loopend: 411 start: 0 end: 460 loopstart: 350 loopend: 411 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 71 name: HISS source_start: 269260 source_end: 274858 source_loopstart: 0 source_loopend: 9863 start: 0 end: 9862 loopstart: 0 loopend: 9863 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 72 name: AAH source_start: 274859 source_end: 278323 source_loopstart: 20 source_loopend: 88 start: 0 end: 94 loopstart: 20 loopend: 88 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 73 name: W8_4 source_start: 278324 source_end: 281776 source_loopstart: 15 source_loopend: 57 start: 0 end: 63 loopstart: 15 loopend: 57 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 74 name: W8_3 source_start: 281777 source_end: 285226 source_loopstart: 22 source_loopend: 78 start: 0 end: 85 loopstart: 22 loopend: 78 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 75 name: W8_2 source_start: 285227 source_end: 288673 source_loopstart: 20 source_loopend: 105 start: 0 end: 119 loopstart: 20 loopend: 105 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 76 name: W8_1 source_start: 288674 source_end: 292242 source_loopstart: 32 source_loopend: 200 start: 0 end: 228 loopstart: 32 loopend: 200 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 77 name: dm4_a4 source_start: 292243 source_end: 295791 source_loopstart: 14 source_loopend: 114 start: 0 end: 139 loopstart: 14 loopend: 114 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 78 name: dm4_a3 source_start: 295792 source_end: 299307 source_loopstart: 18 source_loopend: 119 start: 0 end: 177 loopstart: 18 loopend: 119 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 79 name: dm4_a2 source_start: 299308 source_end: 302827 source_loopstart: 0 source_loopend: 200 start: 0 end: 302 loopstart: 0 loopend: 200 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 80 name: dm4_a1 source_start: 302828 source_end: 306397 source_loopstart: 0 source_loopend: 401 start: 0 end: 517 loopstart: 0 loopend: 401 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 81 name: dm3_a4 source_start: 306398 source_end: 309872 source_loopstart: 0 source_loopend: 50 start: 0 end: 76 loopstart: 0 loopend: 50 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 82 name: dm3_a3 source_start: 309873 source_end: 313394 source_loopstart: 0 source_loopend: 101 start: 0 end: 153 loopstart: 0 loopend: 101 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 83 name: dm3_a2 source_start: 313395 source_end: 316908 source_loopstart: 0 source_loopend: 201 start: 0 end: 251 loopstart: 0 loopend: 201 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 84 name: dm3_a1 source_start: 316909 source_end: 320489 source_loopstart: 27 source_loopend: 428 start: 0 end: 476 loopstart: 27 loopend: 428 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 85 name: dm2_a4 source_start: 320490 source_end: 323961 source_loopstart: 3 source_loopend: 103 start: 0 end: 119 loopstart: 3 loopend: 103 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 86 name: dm2_a3 source_start: 323962 source_end: 327424 source_loopstart: 7 source_loopend: 107 start: 0 end: 120 loopstart: 7 loopend: 107 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 87 name: dm2_a2 source_start: 327425 source_end: 330936 source_loopstart: 12 source_loopend: 213 start: 0 end: 237 loopstart: 12 loopend: 213 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 88 name: dm2_a1 source_start: 330937 source_end: 334633 source_loopstart: 23 source_loopend: 425 start: 0 end: 677 loopstart: 23 loopend: 425 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 89 name: dm1_a4 source_start: 334634 source_end: 338107 source_loopstart: 13 source_loopend: 113 start: 0 end: 124 loopstart: 13 loopend: 113 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 90 name: dm1_a3 source_start: 338108 source_end: 341575 source_loopstart: 2 source_loopend: 100 start: 0 end: 124 loopstart: 2 loopend: 100 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 91 name: dm1_a2 source_start: 341576 source_end: 345089 source_loopstart: 8 source_loopend: 203 start: 0 end: 237 loopstart: 8 loopend: 203 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 92 name: dm1_a1 source_start: 345090 source_end: 348877 source_loopstart: 5 source_loopend: 407 start: 0 end: 471 loopstart: 5 loopend: 407 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 93 name: Noise3 source_start: 348878 source_end: 353292 source_loopstart: 8 source_loopend: 4402 start: 0 end: 4409 loopstart: 8 loopend: 4402 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 94 name: Tri_a4 source_start: 353293 source_end: 356758 source_loopstart: 64 source_loopend: 114 start: 0 end: 126 loopstart: 64 loopend: 114 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 95 name: Tri_a3 source_start: 356759 source_end: 360256 source_loopstart: 76 source_loopend: 176 start: 0 end: 201 loopstart: 76 loopend: 176 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 96 name: Tri_a2 source_start: 360257 source_end: 363913 source_loopstart: 50 source_loopend: 251 start: 0 end: 301 loopstart: 50 loopend: 251 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 97 name: Tri_a1 source_start: 363914 source_end: 367524 source_loopstart: 101 source_loopend: 502 start: 0 end: 601 loopstart: 101 loopend: 502 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 98 name: Sq_a4 source_start: 367525 source_end: 371008 source_loopstart: 74 source_loopend: 124 start: 0 end: 126 loopstart: 74 loopend: 124 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 99 name: Sq_a3 source_start: 371009 source_end: 374520 source_loopstart: 51 source_loopend: 151 start: 0 end: 201 loopstart: 51 loopend: 151 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 100 name: Sq_a2 source_start: 374521 source_end: 378072 source_loopstart: 97 source_loopend: 298 start: 0 end: 301 loopstart: 97 loopend: 298 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 101 name: Sq_a1 source_start: 378073 source_end: 381873 source_loopstart: 197 source_loopend: 597 start: 0 end: 601 loopstart: 197 loopend: 597 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 102 name: Sine_a4 source_start: 381874 source_end: 385327 source_loopstart: 64 source_loopend: 114 start: 0 end: 126 loopstart: 64 loopend: 114 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 103 name: Sine_a3 source_start: 385328 source_end: 388870 source_loopstart: 77 source_loopend: 177 start: 0 end: 201 loopstart: 77 loopend: 177 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 104 name: Sine_a2 source_start: 388871 source_end: 392418 source_loopstart: 53 source_loopend: 253 start: 0 end: 301 loopstart: 53 loopend: 253 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 105 name: Sine_a1 source_start: 392419 source_end: 396020 source_loopstart: 110 source_loopend: 512 start: 0 end: 601 loopstart: 110 loopend: 512 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 106 name: Saw_a4 source_start: 396021 source_end: 399482 source_loopstart: 51 source_loopend: 101 start: 0 end: 126 loopstart: 51 loopend: 101 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 107 name: Saw_a3 source_start: 399483 source_end: 402985 source_loopstart: 35 source_loopend: 135 start: 0 end: 201 loopstart: 35 loopend: 135 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 108 name: Saw_a2 source_start: 402986 source_end: 406530 source_loopstart: 8 source_loopend: 208 start: 0 end: 301 loopstart: 8 loopend: 208 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 109 name: Saw_a1 source_start: 406531 source_end: 410151 source_loopstart: 9 source_loopend: 410 start: 0 end: 601 loopstart: 9 loopend: 410 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 110 name: add3_a4 source_start: 410152 source_end: 413667 source_loopstart: 102 source_loopend: 152 start: 0 end: 172 loopstart: 102 loopend: 152 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 111 name: add3_a3 source_start: 413668 source_end: 417293 source_loopstart: 53 source_loopend: 153 start: 0 end: 173 loopstart: 53 loopend: 153 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 112 name: add3_a2 source_start: 417294 source_end: 420878 source_loopstart: 39 source_loopend: 239 start: 0 end: 283 loopstart: 39 loopend: 239 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 113 name: add3_a1 source_start: 420879 source_end: 424372 source_loopstart: 97 source_loopend: 498 start: 0 end: 510 loopstart: 97 loopend: 498 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 114 name: Noise4 source_start: 424373 source_end: 428765 source_loopstart: 0 source_loopend: 4215 start: 0 end: 4409 loopstart: 0 loopend: 4215 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 115 name: Fm2_a4 source_start: 428766 source_end: 432212 source_loopstart: 21 source_loopend: 121 start: 0 end: 126 loopstart: 21 loopend: 121 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 116 name: Fm2_a3 source_start: 432213 source_end: 435789 source_loopstart: 8 source_loopend: 108 start: 0 end: 151 loopstart: 8 loopend: 108 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 117 name: Fm2_a2 source_start: 435790 source_end: 439414 source_loopstart: 16 source_loopend: 216 start: 0 end: 250 loopstart: 16 loopend: 216 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 118 name: Fm2_a1 source_start: 439415 source_end: 443015 source_loopstart: 77 source_loopend: 478 start: 0 end: 501 loopstart: 77 loopend: 478 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 119 name: Fm3_a4 source_start: 443016 source_end: 446521 source_loopstart: 110 source_loopend: 160 start: 0 end: 179 loopstart: 110 loopend: 160 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 120 name: Fm3_a3 source_start: 446522 source_end: 450022 source_loopstart: 81 source_loopend: 181 start: 0 end: 195 loopstart: 81 loopend: 181 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 121 name: Fm3_a2 source_start: 450023 source_end: 453585 source_loopstart: 162 source_loopend: 362 start: 0 end: 383 loopstart: 162 loopend: 362 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 - sample: 122 name: Fm3_a1 source_start: 453586 source_end: 457219 source_loopstart: 324 source_loopend: 725 start: 0 end: 761 loopstart: 324 loopend: 725 samplerate: 44100 origpitch: 60 pitchadj: 0 sampletype: 17 fluidsynth-2.2.5/sf2/VìntàgèDrèàmsWàvès-v2.sf2000077700000000000000000000000001417326347500312502VintageDreamsWaves-v2.sf2ustar00rootroot00000000000000fluidsynth-2.2.5/sf2/■VintageDreamsWaves-v2■.sf2000077700000000000000000000000001417326347500276722VintageDreamsWaves-v2.sf2ustar00rootroot00000000000000fluidsynth-2.2.5/sonar-project.properties000066400000000000000000000007021417326347500206300ustar00rootroot00000000000000sonar.projectKey=FluidSynth_fluidsynth sonar.organization=fluidsynth sonar.cfamily.threads=4 sonar.cfamily.cache.enabled=false # This is the name and version displayed in the SonarCloud UI. #sonar.projectName=fluidsynth #sonar.projectVersion=1.0 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. #sonar.sources=. # Encoding of the source code. Default is default system encoding #sonar.sourceEncoding=UTF-8 fluidsynth-2.2.5/src/000077500000000000000000000000001417326347500145145ustar00rootroot00000000000000fluidsynth-2.2.5/src/CMakeLists.txt000066400000000000000000000317761417326347500172720ustar00rootroot00000000000000# 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 ) include_directories ( SYSTEM ${GLIB_INCLUDE_DIRS} ${PTHREADS_INCLUDE_DIR} ${SDL2_INCLUDE_DIR} ${LIBINSTPATCH_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_INCLUDE_DIRS} ) endif ( PULSE_SUPPORT ) if ( ALSA_SUPPORT ) set ( fluid_alsa_SOURCES drivers/fluid_alsa.c ) include_directories ( ${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_INCLUDE_DIRS} ) endif ( DBUS_SUPPORT ) if ( JACK_SUPPORT ) set ( fluid_jack_SOURCES drivers/fluid_jack.c ) include_directories ( ${JACK_INCLUDE_DIRS} ) endif ( JACK_SUPPORT ) if ( PORTAUDIO_SUPPORT ) set ( fluid_portaudio_SOURCES drivers/fluid_portaudio.c ) include_directories ( ${PORTAUDIO_INCLUDE_DIRS} ) endif ( PORTAUDIO_SUPPORT ) if ( DSOUND_SUPPORT ) set ( fluid_dsound_SOURCES drivers/fluid_dsound.c ) endif ( DSOUND_SUPPORT ) if ( WASAPI_SUPPORT ) set ( fluid_wasapi_SOURCES drivers/fluid_wasapi.c ) endif ( WASAPI_SUPPORT ) if ( WAVEOUT_SUPPORT ) set ( fluid_waveout_SOURCES drivers/fluid_waveout.c ) endif ( WAVEOUT_SUPPORT ) if ( WINMIDI_SUPPORT ) set ( fluid_winmidi_SOURCES drivers/fluid_winmidi.c ) endif ( WINMIDI_SUPPORT ) if ( SDL2_SUPPORT ) set ( fluid_sdl2_SOURCES drivers/fluid_sdl2.c ) include_directories ( ${SDL2_INCLUDE_DIRS} ) endif ( SDL2_SUPPORT ) if ( OSS_SUPPORT ) set ( fluid_oss_SOURCES drivers/fluid_oss.c ) endif ( OSS_SUPPORT ) if ( LASH_SUPPORT ) set ( fluid_lash_SOURCES bindings/fluid_lash.c bindings/fluid_lash.h ) include_directories ( ${LASH_INCLUDE_DIRS}) endif ( LASH_SUPPORT ) if ( SYSTEMD_SUPPORT ) include_directories ( ${SYSTEMD_INCLUDE_DIRS}) endif ( SYSTEMD_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_INCLUDE_DIRS} ) endif ( LIBSNDFILE_SUPPORT ) if ( MIDISHARE_SUPPORT ) set ( fluid_midishare_SOURCES drivers/fluid_midishare.c ) include_directories ( ${MidiShare_INCLUDE_DIRS} ) endif ( MIDISHARE_SUPPORT ) if ( AUFILE_SUPPORT ) set ( fluid_aufile_SOURCES drivers/fluid_aufile.c ) endif ( AUFILE_SUPPORT ) if ( LIBINSTPATCH_SUPPORT ) set ( fluid_libinstpatch_SOURCES sfloader/fluid_instpatch.c sfloader/fluid_instpatch.h ) endif ( LIBINSTPATCH_SUPPORT ) if ( OPENSLES_SUPPORT ) set ( fluid_opensles_SOURCES drivers/fluid_opensles.c ) include_directories ( ${OpenSLES_INCLUDE_DIRS} ) endif ( OPENSLES_SUPPORT ) if ( OBOE_SUPPORT ) set ( fluid_oboe_SOURCES drivers/fluid_oboe.cpp ) include_directories ( ${OBOE_INCLUDE_DIRS} ) endif ( OBOE_SUPPORT ) set ( config_SOURCES ${CMAKE_BINARY_DIR}/config.h ) 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_sfont.h sfloader/fluid_sfont.c sfloader/fluid_sffile.c sfloader/fluid_sffile.h sfloader/fluid_samplecache.c sfloader/fluid_samplecache.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.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_synth_monopoly.c 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_seqbind_notes.cpp midi/fluid_seq.c midi/fluid_seq_queue.cpp drivers/fluid_adriver.c drivers/fluid_adriver.h drivers/fluid_mdriver.c drivers/fluid_mdriver.h bindings/fluid_cmd.c bindings/fluid_cmd.h bindings/fluid_filerenderer.c bindings/fluid_ladspa.c bindings/fluid_ladspa.h ) 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/ladspa.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/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_BINARY_DIR}/include/fluidsynth.h ) configure_file ( ${CMAKE_SOURCE_DIR}/include/fluidsynth/version.h.in ${CMAKE_BINARY_DIR}/include/fluidsynth/version.h ) configure_file ( ${CMAKE_SOURCE_DIR}/include/fluidsynth.cmake ${public_main_HEADER} ) if ( WIN32 ) include(generate_product_version) generate_product_version( VersionFilesOutputVariable NAME "Fluidsynth" BUNDLE "Fluidsynth" VERSION_MAJOR ${FLUIDSYNTH_VERSION_MAJOR} VERSION_MINOR ${FLUIDSYNTH_VERSION_MINOR} VERSION_PATCH ${FLUIDSYNTH_VERSION_MICRO} VERSION_REVISION 0 COMMENTS "Fluidsynth" COMPANY_NAME "Fluidsynth LGPL" ORIGINAL_FILENAME "libfluidsynth.dll" FILE_DESCRIPTION "Fluidsynth" ) endif ( WIN32 ) add_library ( libfluidsynth-OBJ OBJECT ${config_SOURCES} ${fluid_alsa_SOURCES} ${fluid_aufile_SOURCES} ${fluid_coreaudio_SOURCES} ${fluid_coremidi_SOURCES} ${fluid_dart_SOURCES} ${fluid_dbus_SOURCES} ${fluid_jack_SOURCES} ${fluid_lash_SOURCES} ${fluid_midishare_SOURCES} ${fluid_opensles_SOURCES} ${fluid_oboe_SOURCES} ${fluid_oss_SOURCES} ${fluid_portaudio_SOURCES} ${fluid_pulse_SOURCES} ${fluid_dsound_SOURCES} ${fluid_wasapi_SOURCES} ${fluid_waveout_SOURCES} ${fluid_winmidi_SOURCES} ${fluid_sdl2_SOURCES} ${fluid_libinstpatch_SOURCES} ${libfluidsynth_SOURCES} ${public_HEADERS} ${public_main_HEADER} ${VersionFilesOutputVariable} ) if ( LIBFLUID_CPPFLAGS ) set_target_properties ( libfluidsynth-OBJ PROPERTIES COMPILE_FLAGS ${LIBFLUID_CPPFLAGS} ) endif ( LIBFLUID_CPPFLAGS ) # 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. # Further note: The headers must be explicitly added here to have CMake install # them correctly in case of MACOSX_FRAMEWORK add_library ( libfluidsynth $ ${public_main_HEADER} ${public_HEADERS} ) if ( MACOSX_FRAMEWORK ) set_source_files_properties ( ${public_HEADERS} PROPERTIES 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 PUBLIC_HEADER "${public_HEADERS}" OUTPUT_NAME "fluidsynth" VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) elseif ( WIN32 ) set_target_properties ( libfluidsynth PROPERTIES PUBLIC_HEADER "${public_HEADERS}" 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 PUBLIC_HEADER "${public_HEADERS}" PREFIX "lib" OUTPUT_NAME "fluidsynth" VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) endif ( MACOSX_FRAMEWORK ) target_link_libraries ( libfluidsynth ${GLIB_LIBRARIES} ${GMODULE_LIBRARIES} ${LASH_LIBRARIES} ${JACK_LIBRARIES} ${ALSA_LIBRARIES} ${PULSE_LIBRARIES} ${PORTAUDIO_LIBRARIES} ${LIBSNDFILE_LIBRARIES} ${SDL2_LIBRARIES} ${DBUS_LIBRARIES} ${READLINE_LIBS} ${DART_LIBS} ${COREAUDIO_LIBS} ${COREMIDI_LIBS} ${WINDOWS_LIBS} ${MidiShare_LIBS} ${OpenSLES_LIBS} ${OBOE_LIBRARIES} ${LIBFLUID_LIBS} ${LIBINSTPATCH_LIBRARIES} ) # ************ CLI program ************ set ( fluidsynth_SOURCES fluidsynth.c ) if ( WASAPI_SUPPORT ) set ( fluidsynth_SOURCES ${fluidsynth_SOURCES} fluid_wasapi_device_enumerate.c ) endif ( WASAPI_SUPPORT ) add_executable ( fluidsynth ${fluidsynth_SOURCES} ) set_target_properties ( fluidsynth PROPERTIES IMPORT_PREFIX "" ) if ( FLUID_CPPFLAGS ) set_target_properties ( fluidsynth PROPERTIES COMPILE_FLAGS ${FLUID_CPPFLAGS} ) endif ( FLUID_CPPFLAGS ) target_link_libraries ( fluidsynth libfluidsynth ${SYSTEMD_LIBRARIES} ${FLUID_LIBS} ) if ( MACOSX_FRAMEWORK ) install ( TARGETS fluidsynth libfluidsynth RUNTIME DESTINATION ${BIN_INSTALL_DIR} FRAMEWORK DESTINATION "${FRAMEWORK_INSTALL_PREFIX}/${FRAMEWORK_INSTALL_DIR}" ARCHIVE DESTINATION "${FRAMEWORK_INSTALL_PREFIX}/${FRAMEWORK_INSTALL_DIR}" ) else ( MACOSX_FRAMEWORK ) install ( TARGETS fluidsynth libfluidsynth RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/fluidsynth ) install ( FILES ${public_main_HEADER} DESTINATION ${INCLUDE_INSTALL_DIR} ) endif ( MACOSX_FRAMEWORK ) # ******* Auto Generated Lookup Tables ****** include(ExternalProject) set (GENTAB_SDIR ${CMAKE_CURRENT_SOURCE_DIR}/gentables) set (GENTAB_BDIR ${CMAKE_CURRENT_BINARY_DIR}/gentables) # Use external project to ensure that cmake uses the host compiler when building make_tables.exe # To fix cross-compiling fluidsynth from Win32 to ARM (using vcpkg), we need to pass the current generator # on to the external project, otherwise (for some unknown reason) the target compiler will be used rather # than the host compiler. ExternalProject_Add(gentables DOWNLOAD_COMMAND "" SOURCE_DIR ${GENTAB_SDIR} BINARY_DIR ${GENTAB_BDIR} CONFIGURE_COMMAND "${CMAKE_COMMAND}" -DCMAKE_VERBOSE_MAKEFILE=${CMAKE_VERBOSE_MAKEFILE} -G "${CMAKE_GENERATOR}" -B "${GENTAB_BDIR}" "${GENTAB_SDIR}" BUILD_COMMAND "${CMAKE_COMMAND}" --build "${GENTAB_BDIR}" INSTALL_COMMAND ${GENTAB_BDIR}/make_tables.exe "${CMAKE_BINARY_DIR}/" ) add_dependencies(libfluidsynth-OBJ gentables) fluidsynth-2.2.5/src/bindings/000077500000000000000000000000001417326347500163115ustar00rootroot00000000000000fluidsynth-2.2.5/src/bindings/fluid_cmd.c000066400000000000000000004201271417326347500204110ustar00rootroot00000000000000/* 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_cmd.h" #include "fluid_synth.h" #include "fluid_settings.h" #include "fluid_hash.h" #include "fluid_midi_router.h" #include "fluid_sfont.h" #include "fluid_chan.h" /* FIXME: LADSPA used to need a lot of parameters on a single line. This is not * necessary anymore, so the limits below could probably be reduced */ #define MAX_TOKENS 100 #define MAX_COMMAND_LEN 1024 /* max command length accepted by fluid_command() */ #define FLUID_WORKLINELENGTH 1024 #define FLUID_ENTRY_COMMAND(data) fluid_cmd_handler_t* handler=(fluid_cmd_handler_t*)(data) /* the shell cmd handler struct */ struct _fluid_cmd_handler_t { fluid_settings_t *settings; fluid_synth_t *synth; fluid_midi_router_t *router; fluid_player_t *player; fluid_cmd_hash_t *commands; 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_shell_t { fluid_settings_t *settings; fluid_cmd_handler_t *handler; fluid_thread_t *thread; fluid_istream_t in; fluid_ostream_t out; }; static fluid_thread_return_t fluid_shell_run(void *data); 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(void *data, int ac, char **av, fluid_ostream_t out); void fluid_shell_settings(fluid_settings_t *settings) { fluid_settings_register_str(settings, "shell.prompt", "", 0); fluid_settings_register_int(settings, "shell.port", 9800, 1, 65535, 0); } /** the table of all handled commands */ static const fluid_cmd_t fluid_commands[] = { /* general commands */ { "help", "general", fluid_handle_help, "help Shows help topics ('help TOPIC' for more info)" }, { "quit", "general", fluid_handle_quit, "quit Quit the synthesizer" }, { "source", "general", fluid_handle_source, "source filename Loads a file and parse every line as a command" }, /* event commands */ { "noteon", "event", fluid_handle_noteon, "noteon chan key vel Sends noteon" }, { "noteoff", "event", fluid_handle_noteoff, "noteoff chan key Sends noteoff" }, { "pitch_bend", "event", fluid_handle_pitch_bend, "pitch_bend chan offset Bends pitch" }, { "pitch_bend_range", "event", fluid_handle_pitch_bend_range, "pitch_bend_range chn range Sets pitch bend range for the given midi channel" }, { "cc", "event", fluid_handle_cc, "cc chan ctrl value Sends control-change message" }, { "prog", "event", fluid_handle_prog, "prog chan num Sends program-change message" }, { "select", "event", fluid_handle_select, "select chan sfont bank prog Combination of bank-select and program-change" }, { "load", "general", fluid_handle_load, "load file [reset] [bankofs] Loads SoundFont (reset=0|1, def 1; bankofs=n, def 0)" }, { "unload", "general", fluid_handle_unload, "unload id [reset] Unloads SoundFont by ID (reset=0|1, default 1)" }, { "reload", "general", fluid_handle_reload, "reload id Reload the SoundFont with the specified ID" }, { "fonts", "general", fluid_handle_fonts, "fonts Display the list of loaded SoundFonts" }, { "inst", "general", fluid_handle_inst, "inst font Print out the available instruments for the font" }, { "channels", "general", fluid_handle_channels, "channels [-verbose] Print out preset of all channels" }, { "interp", "general", fluid_handle_interp, "interp num Choose interpolation method for all channels" }, { "interpc", "general", fluid_handle_interpc, "interpc chan num Choose interpolation method for one channel" }, /* polymono commands */ { "basicchannels", "polymono", fluid_handle_basicchannels, "basicchannels Prints the list of basic channels" }, { "resetbasicchannels", "polymono", fluid_handle_resetbasicchannels, "resetbasicchannels [chan1 chan2..] Resets all or some basic channels" }, { "setbasicchannels", "polymono", fluid_handle_setbasicchannels, "setbasicchannels [chan mode val...] Sets default, adds basic channels" }, { "channelsmode", "polymono", fluid_handle_channelsmode, "channelsmode [chan1 chan2..] Prints channels mode" }, { "legatomode", "polymono", fluid_handle_legatomode, "legatomode [chan1 chan2..] Prints channels legato mode" }, { "setlegatomode", "polymono", fluid_handle_setlegatomode, "setlegatomode chan mode [chan mode..] Sets legato mode" }, { "portamentomode", "polymono", fluid_handle_portamentomode, "portamentomode [chan1 chan2..] Prints channels portamento mode" }, { "setportamentomode", "polymono", fluid_handle_setportamentomode, "setportamentomode chan mode [chan mode..] Sets portamento mode" }, { "breathmode", "polymono", fluid_handle_breathmode, "breathmode [chan1 chan2..] Prints channels breath mode" }, { "setbreathmode", "polymono", fluid_handle_setbreathmode, "setbreathmode chan poly(1/0) mono(1/0) breath_sync(1/0) [..] Sets breath mode" }, /* reverb commands */ { "rev_preset", "reverb", fluid_handle_reverbpreset, "rev_preset num Load preset num into all reverb unit" }, { "rev_setroomsize", "reverb", fluid_handle_reverbsetroomsize, "rev_setroomsize [group] num Set room size of all or one reverb group to num" }, { "rev_setdamp", "reverb", fluid_handle_reverbsetdamp, "rev_setdamp [group] num Set damping of all or one reverb group to num" }, { "rev_setwidth", "reverb", fluid_handle_reverbsetwidth, "rev_setwidth [group] num Set width of all or one reverb group to num" }, { "rev_setlevel", "reverb", fluid_handle_reverbsetlevel, "rev_setlevel [group] num Set output level of all or one reverb group to num" }, { "reverb", "reverb", fluid_handle_reverb, "reverb [group] 0|1|on|off Turn all or one reverb group on or off" }, /* chorus commands */ { "cho_set_nr", "chorus", fluid_handle_chorusnr, "cho_set_nr [group] n Set n delay lines (default 3) in all or one chorus group" }, { "cho_set_level", "chorus", fluid_handle_choruslevel, "cho_set_level [group] num Set output level of all or one chorus group to num" }, { "cho_set_speed", "chorus", fluid_handle_chorusspeed, "cho_set_speed [group] num Set mod speed of all or one chorus group to num (Hz)" }, { "cho_set_depth", "chorus", fluid_handle_chorusdepth, "cho_set_depth [group] num Set modulation depth of all or one chorus group to num (ms)" }, { "chorus", "chorus", fluid_handle_chorus, "chorus [group] 0|1|on|off Turn all or one chorus group on or off" }, { "gain", "general", fluid_handle_gain, "gain value Set the master gain (0 < gain < 5)" }, { "voice_count", "general", fluid_handle_voice_count, "voice_count Get number of active synthesis voices" }, /* tuning commands */ { "tuning", "tuning", fluid_handle_tuning, "tuning name bank prog Create a tuning with name, bank number, \n" " and program number (0 <= bank,prog <= 127)" }, { "tune", "tuning", fluid_handle_tune, "tune bank prog key pitch Tune a key" }, { "settuning", "tuning", fluid_handle_settuning, "settuning chan bank prog Set the tuning for a MIDI channel" }, { "resettuning", "tuning", fluid_handle_resettuning, "resettuning chan Restore the default tuning of a MIDI channel" }, { "tunings", "tuning", fluid_handle_tunings, "tunings Print the list of available tunings" }, { "dumptuning", "tuning", fluid_handle_dumptuning, "dumptuning bank prog Print the pitch details of the tuning" }, { "reset", "general", fluid_handle_reset, "reset System reset (all notes off, reset controllers)" }, /* settings commands */ { "set", "settings", fluid_handle_set, "set name value Set the value of a setting (must be a real-time setting to take effect immediately)" }, { "get", "settings", fluid_handle_get, "get name Get the value of a setting" }, { "info", "settings", fluid_handle_info, "info name Get information about a setting" }, { "settings", "settings", fluid_handle_settings, "settings Print out all settings" }, { "echo", "general", fluid_handle_echo, "echo arg Print arg" }, /* Sleep command, useful to insert a delay between commands */ { "sleep", "general", fluid_handle_sleep, "sleep duration sleep duration (in ms)" }, /* LADSPA-related commands */ #ifdef LADSPA { "ladspa_effect", "ladspa", fluid_handle_ladspa_effect, "ladspa_effect Create a new effect from a LADSPA plugin" }, { "ladspa_link", "ladspa", fluid_handle_ladspa_link, "ladspa_link Connect an effect port to a host port or buffer" }, { "ladspa_buffer", "ladspa", fluid_handle_ladspa_buffer, "ladspa_buffer Create a LADSPA buffer" }, { "ladspa_set", "ladspa", fluid_handle_ladspa_set, "ladspa_set Set the value of an effect control port" }, { "ladspa_check", "ladspa", fluid_handle_ladspa_check, "ladspa_check Check LADSPA configuration" }, { "ladspa_start", "ladspa", fluid_handle_ladspa_start, "ladspa_start Start LADSPA effects" }, { "ladspa_stop", "ladspa", fluid_handle_ladspa_stop, "ladspa_stop Stop LADSPA effect unit" }, { "ladspa_reset", "ladspa", fluid_handle_ladspa_reset, "ladspa_reset Stop and reset LADSPA effects" }, #endif /* router commands */ { "router_clear", "router", fluid_handle_router_clear, "router_clear Clears all routing rules from the midi router" }, { "router_default", "router", fluid_handle_router_default, "router_default Resets the midi router to default state" }, { "router_begin", "router", fluid_handle_router_begin, "router_begin [note|cc|prog|pbend|cpress|kpress]: Starts a new routing rule" }, { "router_chan", "router", fluid_handle_router_chan, "router_chan min max mul add filters and maps midi channels on current rule" }, { "router_par1", "router", fluid_handle_router_par1, "router_par1 min max mul add filters and maps parameter 1 (key/ctrl nr)" }, { "router_par2", "router", fluid_handle_router_par2, "router_par2 min max mul add filters and maps parameter 2 (vel/cc val)" }, { "router_end", "router", fluid_handle_router_end, "router_end closes and commits the current routing rule" }, /* Midi file player commands */ { "player_start", "player", fluid_handle_player_start, "player_start Start playing from the beginning of current song" }, { "player_stop", "player", fluid_handle_player_stop, "player_stop Stop playing (cannot be executed in a user command file)" }, { "player_cont", "player", fluid_handle_player_continue, "player_cont Continue playing (cannot be executed in a user command file)" }, { "player_seek", "player", fluid_handle_player_seek, "player_seek num Move forward/backward in current song to +/-num ticks" }, { "player_next", "player", fluid_handle_player_next_song, "player_next Move to next song (cannot be executed in a user command file)" }, { "player_loop", "player", fluid_handle_player_loop, "player_loop num Set loop number to num (-1 = loop forever)" }, { "player_tempo_bpm", "player", fluid_handle_player_tempo_bpm, "player_tempo_bpm num Set tempo to num beats per minute" }, { "player_tempo_int", "player", fluid_handle_player_tempo_int, "player_tempo_int [mul] Set internal tempo multiplied by mul (default mul=1.0)" }, #if WITH_PROFILING /* Profiling commands */ { "profile", "profile", fluid_handle_profile, "profile Prints default parameters used by prof_start" }, { "prof_set_notes", "profile", fluid_handle_prof_set_notes, "prof_set_notes nbr [bank prog] Sets notes number generated by prof_start" }, { "prof_set_print", "profile", fluid_handle_prof_set_print, "prof_set_print mode Sets print mode (0:simple, 1:full infos)" }, { "prof_start", "profile", fluid_handle_prof_start, "prof_start [n_prof [dur]] Starts n_prof measures of duration(ms) each" } #endif }; /** * Process a string command. * * @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 * * @note FluidSynth 1.0.8 and above no longer modifies the 'cmd' string. */ 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 FLUID_FAILED; } 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_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) { fluid_return_if_fail(shell != NULL); if(shell->thread != NULL) { delete_fluid_thread(shell->thread); } FLUID_FREE(shell); } static fluid_thread_return_t fluid_shell_run(void *data) { fluid_shell_t *shell = (fluid_shell_t *)data; char workline[FLUID_WORKLINELENGTH]; char *prompt = NULL; int cont = 1; int errors = FALSE; 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) { FLUID_LOG(FLUID_PANIC, "An error occurred while reading from stdin."); break; } /* handle the command */ switch(fluid_command(shell->handler, workline, shell->out)) { case 1: /* empty line or comment */ break; case FLUID_FAILED: /* erroneous command */ errors = TRUE; case FLUID_OK: /* valid command */ break; case -2: /* quit */ cont = 0; break; } if(n == 0) { if(shell->settings) { FLUID_LOG(FLUID_INFO, "Received EOF while reading commands, exiting the shell."); } break; } } FLUID_FREE(prompt); /* -- free prompt */ /* return FLUID_THREAD_RETURN_VALUE on success, something else on failure */ return errors ? (fluid_thread_return_t)(-1) : FLUID_THREAD_RETURN_VALUE; } /** * 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 * * The shell is run in the current thread, this function will only * return after the \c quit command has been issued. */ 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 negative value 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) == FLUID_THREAD_RETURN_VALUE) ? 0 : -1; #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. * * On Windows this is currently @c "%USERPROFILE%\fluidsynth.cfg". * For anything else (except MACOS9) @c "$HOME/.fluidsynth". */ char * fluid_get_userconf(char *buf, int len) { const char *home = NULL; const char *config_file; #if defined(WIN32) home = getenv("USERPROFILE"); config_file = "\\fluidsynth.cfg"; #elif !defined(MACOS9) home = getenv("HOME"); config_file = "/.fluidsynth"; #endif if(home == NULL) { return NULL; } else { FLUID_SNPRINTF(buf, len, "%s%s", home, config_file); return buf; } } /** * 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. * * Windows and MACOS9 do not have a system-wide config file currently. For anything else it * returns @c "/etc/fluidsynth.conf". */ char * fluid_get_sysconf(char *buf, int len) { #if defined(WIN32) || defined(MACOS9) return NULL; #else FLUID_SNPRINTF(buf, len, "/etc/fluidsynth.conf"); return buf; #endif } /* * handlers */ int fluid_handle_noteon(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); if(ac < 3) { fluid_ostream_printf(out, "noteon: too few arguments\n"); return FLUID_FAILED; } 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 FLUID_FAILED; } return fluid_synth_noteon(handler->synth, atoi(av[0]), atoi(av[1]), atoi(av[2])); } int fluid_handle_noteoff(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); if(ac < 2) { fluid_ostream_printf(out, "noteoff: too few arguments\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "noteon: invalid argument\n"); return FLUID_FAILED; } return fluid_synth_noteoff(handler->synth, atoi(av[0]), atoi(av[1])); } int fluid_handle_pitch_bend(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); if(ac < 2) { fluid_ostream_printf(out, "pitch_bend: too few arguments\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "pitch_bend: invalid argument\n"); return FLUID_FAILED; } return fluid_synth_pitch_bend(handler->synth, atoi(av[0]), atoi(av[1])); } int fluid_handle_pitch_bend_range(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int channum; int value; if(ac < 2) { fluid_ostream_printf(out, "pitch_bend_range: too few arguments\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "pitch_bend_range: invalid argument\n"); return FLUID_FAILED; } channum = atoi(av[0]); value = atoi(av[1]); fluid_channel_set_pitch_wheel_sensitivity(handler->synth->channel[channum], value); return FLUID_OK; } int fluid_handle_cc(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); if(ac < 3) { fluid_ostream_printf(out, "cc: too few arguments\n"); return FLUID_FAILED; } 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 FLUID_FAILED; } return fluid_synth_cc(handler->synth, atoi(av[0]), atoi(av[1]), atoi(av[2])); } int fluid_handle_prog(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); if(ac < 2) { fluid_ostream_printf(out, "prog: too few arguments\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "prog: invalid argument\n"); return FLUID_FAILED; } return fluid_synth_program_change(handler->synth, atoi(av[0]), atoi(av[1])); } int fluid_handle_select(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int sfont_id; int chan; int bank; int prog; if(ac < 4) { fluid_ostream_printf(out, "preset: too few arguments\n"); return FLUID_FAILED; } 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 FLUID_FAILED; } 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(handler->synth, chan, sfont_id, bank, prog); } else { if(fluid_synth_bank_select(handler->synth, chan, bank) == FLUID_OK) { return fluid_synth_program_change(handler->synth, chan, prog); } return FLUID_FAILED; } } int fluid_handle_inst(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); 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 FLUID_FAILED; } if(!fluid_is_number(av[0])) { fluid_ostream_printf(out, "inst: invalid argument\n"); return FLUID_FAILED; } font = atoi(av[0]); sfont = fluid_synth_get_sfont_by_id(handler->synth, font); offset = fluid_synth_get_bank_offset(handler->synth, font); if(sfont == NULL) { fluid_ostream_printf(out, "inst: invalid font number\n"); return FLUID_FAILED; } fluid_sfont_iteration_start(sfont); while((preset = fluid_sfont_iteration_next(sfont)) != NULL) { 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 FLUID_OK; } int fluid_handle_channels(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_preset_t *preset; int verbose = 0; int i; if(ac > 0 && FLUID_STRCMP(av[0], "-verbose") == 0) { verbose = 1; } for(i = 0; i < fluid_synth_count_midi_channels(handler->synth); i++) { preset = fluid_synth_get_channel_preset(handler->synth, i); if(preset == NULL) { fluid_ostream_printf(out, "chan %d, no preset\n", i); } else if(!verbose) { fluid_ostream_printf(out, "chan %d, %s\n", i, fluid_preset_get_name(preset)); } else { fluid_ostream_printf(out, "chan %d, sfont %d, bank %d, preset %d, %s\n", i, fluid_sfont_get_id(preset->sfont), fluid_preset_get_banknum(preset), fluid_preset_get_num(preset), fluid_preset_get_name(preset)); } } return FLUID_OK; } int fluid_handle_load(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); char buf[1024]; int id; int reset = 1; int offset = 0; if(ac < 1) { fluid_ostream_printf(out, "load: too few arguments\n"); return FLUID_FAILED; } 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(handler->synth, fluid_expand_path(av[0], buf, 1024), 0); if(id == -1) { fluid_ostream_printf(out, "failed to load the SoundFont\n"); return FLUID_FAILED; } else { fluid_ostream_printf(out, "loaded SoundFont has ID %d\n", id); } if(offset) { fluid_synth_set_bank_offset(handler->synth, id, offset); } /* The reset should be done after the offset is set. */ if(reset) { fluid_synth_program_reset(handler->synth); } return FLUID_OK; } int fluid_handle_unload(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int reset = 1; if(ac < 1) { fluid_ostream_printf(out, "unload: too few arguments\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0])) { fluid_ostream_printf(out, "unload: expected a number as argument\n"); return FLUID_FAILED; } if(ac == 2) { reset = atoi(av[1]); } if(fluid_synth_sfunload(handler->synth, atoi(av[0]), reset) != 0) { fluid_ostream_printf(out, "failed to unload the SoundFont\n"); return FLUID_FAILED; } return FLUID_OK; } int fluid_handle_reload(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); if(ac < 1) { fluid_ostream_printf(out, "reload: too few arguments\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0])) { fluid_ostream_printf(out, "reload: expected a number as argument\n"); return FLUID_FAILED; } if(fluid_synth_sfreload(handler->synth, atoi(av[0])) == -1) { fluid_ostream_printf(out, "failed to reload the SoundFont\n"); return FLUID_FAILED; } return FLUID_OK; } int fluid_handle_fonts(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int i; fluid_sfont_t *sfont; int num; num = fluid_synth_sfcount(handler->synth); if(num == 0) { fluid_ostream_printf(out, "no SoundFont loaded (try load)\n"); return FLUID_OK; } fluid_ostream_printf(out, "ID Name\n"); for(i = 0; i < num; i++) { sfont = fluid_synth_get_sfont(handler->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 FLUID_OK; } /* Purpose: * Response to 'rev_preset' command. * Load the values from a reverb preset into the reverb unit. */ int fluid_handle_reverbpreset(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int reverb_preset_number; fluid_ostream_printf(out, "rev_preset is deprecated and will be removed in a future release!\n"); if(ac < 1) { fluid_ostream_printf(out, "rev_preset: too few arguments\n"); return FLUID_FAILED; } reverb_preset_number = atoi(av[0]); if(fluid_synth_set_reverb_preset(handler->synth, reverb_preset_number) != FLUID_OK) { fluid_ostream_printf(out, "rev_preset: Failed. Parameter out of range?\n"); return FLUID_FAILED; }; return FLUID_OK; } /* The function is useful for reverb and chorus commands which have 1 or 2 parameters. The function checks that there is 1 or 2 arguments. When there is 2 parameters it checks the first argument that must be an fx group index in the range[0..synth->effects_groups-1]. return the group index: -1, when the command is for all fx groups. 0 to synth->effects_groups-1, when the command is for this group index. -2 if error. */ static int check_fx_group_idx(int ac, char **av, fluid_ostream_t out, fluid_synth_t *synth, const char *name_cde) { int fx_group; /* fx unit index */ int ngroups; /* count of fx groups */ /* One or 2 arguments allowed */ if(ac < 1 || ac > 2) { fluid_ostream_printf(out, "%s: needs 1 or 2 arguments\n", name_cde); return -2; } /* check optional first argument which is a fx group index */ fx_group = -1; if(ac > 1) { fx_group = atoi(av[0]); /* get fx group index */ ngroups = fluid_synth_count_effects_groups(synth); if(!fluid_is_number(av[0]) || fx_group < 0 || fx_group >= ngroups) { fluid_ostream_printf(out, "%s: group index \"%s\" must be in range [%d..%d]\n", name_cde, av[0], 0, ngroups - 1); return -2; } } return fx_group; } /* parameter value */ struct value { const char *name; double min; double max; }; /* check 2 arguments for reverb commands : fx group index , value - group index must be an integer in the range [-1..synth->effects_groups]. - value must be a double in the range [min..max] @param param a pointer on a value to return the second value argument. return the fx group index: -1 when the command is for all fx group. 0 to synth->effects_groups-1 when the command is for this group index. -2 if error. */ static int check_fx_reverb_param(int ac, char **av, fluid_ostream_t out, fluid_synth_t *synth, const char *name_cde, const struct value *value, fluid_real_t *param) { /* get and check fx group index argument */ int fx_group = check_fx_group_idx(ac, av, out, synth, name_cde); if(fx_group >= -1) { fluid_real_t val; /* get and check value argument */ ac--; val = atof(av[ac]); if(!fluid_is_number(av[ac]) || val < value->min || val > value->max) { fluid_ostream_printf(out, "%s: %s \"%s\" must be in range [%f..%f]\n", name_cde, value->name, av[ac], value->min, value->max); return -2; } *param = val; } return fx_group; } /* Purpose: * Response to fluid_handle_reverbsetxxxx commands */ static int fluid_handle_reverb_command(void *data, int ac, char **av, fluid_ostream_t out, int param) { int fx_group; /* reverb commands name table */ static const char *const name_cde[FLUID_REVERB_PARAM_LAST] = {"rev_setroomsize", "rev_setdamp", "rev_setwidth", "rev_setlevel"}; /* name and min/max values table */ static struct value values[FLUID_REVERB_PARAM_LAST] = { {"room size", 0, 0}, {"damp", 0, 0}, {"width", 0, 0}, {"level", 0, 0} }; FLUID_ENTRY_COMMAND(data); fluid_real_t value; fluid_settings_getnum_range(handler->settings, "synth.reverb.room-size", &values[FLUID_REVERB_ROOMSIZE].min, &values[FLUID_REVERB_ROOMSIZE].max); fluid_settings_getnum_range(handler->settings, "synth.reverb.damp", &values[FLUID_REVERB_DAMP].min, &values[FLUID_REVERB_DAMP].max); fluid_settings_getnum_range(handler->settings, "synth.reverb.width", &values[FLUID_REVERB_WIDTH].min, &values[FLUID_REVERB_WIDTH].max); fluid_settings_getnum_range(handler->settings, "synth.reverb.level", &values[FLUID_REVERB_LEVEL].min, &values[FLUID_REVERB_LEVEL].max); /* get and check command arguments */ fx_group = check_fx_reverb_param(ac, av, out, handler->synth, name_cde[param], &values[param], &value); if(fx_group >= -1) { /* run reverb function */ fluid_synth_reverb_set_param(handler->synth, fx_group, param, value); return FLUID_OK; } return FLUID_FAILED; } /* Purpose: * Response to 'rev_setroomsize' command. * Load the new room size into the reverb fx group. * Example: rev_setroomzize 0 0.5 * load roomsize 0.5 in the reverb fx group at index 0 */ int fluid_handle_reverbsetroomsize(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_reverb_command(data, ac, av, out, FLUID_REVERB_ROOMSIZE); } /* Purpose: * Response to 'rev_setdamp' command. * Load the new damp factor into the reverb fx group. * Example: rev_setdamp 1 0.5 * load damp 0.5 in the reverb fx group at index 1 */ int fluid_handle_reverbsetdamp(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_reverb_command(data, ac, av, out, FLUID_REVERB_DAMP); } /* Purpose: * Response to 'rev_setwidth' command. * Load the new width into the reverb fx group. * Example: rev_setwidth 1 0.5 * load width 0.5 in the reverb fx group at index 1. */ int fluid_handle_reverbsetwidth(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_reverb_command(data, ac, av, out, FLUID_REVERB_WIDTH); } /* Purpose: * Response to 'rev_setlevel' command. * Load the new level into the reverb fx group. * Example: rev_setlevel 1 0.5 * load level 0.5 in the reverb fx group at index 1. */ int fluid_handle_reverbsetlevel(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_reverb_command(data, ac, av, out, FLUID_REVERB_LEVEL); } /* reverb/chorus on/off commands enum */ enum rev_chor_on_cde { REVERB_ON_CDE, CHORUS_ON_CDE, NBR_REV_CHOR_ON_CDE }; /* Purpose: * Set one or all reverb/chorus units on or off */ static int fluid_handle_reverb_chorus_on_command(void *data, int ac, char **av, fluid_ostream_t out, enum rev_chor_on_cde cde) { /* commands name table */ static const char *const name_cde[NBR_REV_CHOR_ON_CDE] = {"reverb", "chorus"}; /* functions table */ static int (*onoff_func[NBR_REV_CHOR_ON_CDE])(fluid_synth_t *, int, int) = { fluid_synth_reverb_on, fluid_synth_chorus_on }; FLUID_ENTRY_COMMAND(data); int onoff; /* get and check fx group index argument */ int fx_group = check_fx_group_idx(ac, av, out, handler->synth, name_cde[cde]); if(fx_group >= -1) { ac--; /* check argument value */ if((FLUID_STRCMP(av[ac], "0") == 0) || (FLUID_STRCMP(av[ac], "off") == 0)) { onoff = 0; } else if((FLUID_STRCMP(av[ac], "1") == 0) || (FLUID_STRCMP(av[ac], "on") == 0)) { onoff = 1; } else { fluid_ostream_printf(out, "%s: invalid arguments %s [0|1|on|off]\n", name_cde[cde], av[ac]); return FLUID_FAILED; } /* run on/off function */ return onoff_func[cde](handler->synth, fx_group, onoff); } return FLUID_FAILED; } /* Purpose: * Response to: reverb [fx group] on command. * Examples: * reverb off ,disable all reverb groups. * reverb 1 on ,enable reverb group at index 1. */ int fluid_handle_reverb(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_reverb_chorus_on_command(data, ac, av, out, REVERB_ON_CDE); } /* Purpose: * Response to fluid_handle_chorus_xxx commands */ static int fluid_handle_chorus_command(void *data, int ac, char **av, fluid_ostream_t out, int param) { /* chorus commands name table */ static const char *const name_cde[FLUID_CHORUS_PARAM_LAST - 1] = {"cho_set_nr", "cho_set_level", "cho_set_speed", "cho_set_depth"}; /* value name table */ static const char *const name_value[FLUID_CHORUS_PARAM_LAST - 1] = {"nr", "level", "speed", "depth"}; /* setting name (except lfo waveform type) */ static const char *name[FLUID_CHORUS_PARAM_LAST-1] = { "synth.chorus.nr", "synth.chorus.level", "synth.chorus.speed", "synth.chorus.depth" }; FLUID_ENTRY_COMMAND(data); /* get and check index fx group index argument */ int fx_group = check_fx_group_idx(ac, av, out, handler->synth, name_cde[param]); if(fx_group >= -1) { double value; /* get and check value argument */ ac--; if(!fluid_is_number(av[ac])) { fluid_ostream_printf(out, "%s: %s \"%s\" must be a number\n", name_cde[param], name_value[param], av[ac]); return FLUID_FAILED; } if(param == FLUID_CHORUS_NR) /* commands with integer parameter */ { int min, max; int int_value = atoi(av[ac]); fluid_settings_getint_range(handler->settings, name[param], &min, &max); if(int_value < min || int_value > max) { fluid_ostream_printf(out, "%s: %s \"%s\" must be in range [%d..%d]\n", name_cde[param], name_value[param], av[ac], min, max); return FLUID_FAILED; } value = (double)int_value; } else /* commands with float parameter */ { double min, max; value = atof(av[ac]); fluid_settings_getnum_range(handler->settings, name[param], &min, &max); if(value < min || value > max) { fluid_ostream_printf(out, "%s: %s \"%s\" must be in range [%f..%f]\n", name_cde[param], name_value[param], av[ac], min, max); return FLUID_FAILED; } } /* run chorus function */ fluid_synth_chorus_set_param(handler->synth, fx_group, param, value); return FLUID_OK; } return FLUID_FAILED; } /* Purpose: * Response to 'cho_set_nr' command * Load the new voice count into the chorus fx group. * Example: cho_set_nr 1 3 * load 3 voices in the chorus fx group at index 1. */ int fluid_handle_chorusnr(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_chorus_command(data, ac, av, out, FLUID_CHORUS_NR); } /* Purpose: * Response to 'cho_setlevel' command * Example: cho_set_level 1 3 * load level 3 in the chorus fx group at index 1. */ int fluid_handle_choruslevel(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_chorus_command(data, ac, av, out, FLUID_CHORUS_LEVEL); } /* Purpose: * Response to 'cho_setspeed' command * Example: cho_set_speed 1 0.1 * load speed 0.1 in the chorus fx group at index 1. */ int fluid_handle_chorusspeed(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_chorus_command(data, ac, av, out, FLUID_CHORUS_SPEED); } /* Purpose: * Response to 'cho_setdepth' command * Example: cho_set_depth 1 0.3 * load depth 0.3 in the chorus fx group at index 1. */ int fluid_handle_chorusdepth(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_chorus_command(data, ac, av, out, FLUID_CHORUS_DEPTH); } /* Purpose: * Response to: chorus [fx group] on command. * Examples: * chorus off ,disable all chorus groups. * chorus 1 on ,enable chorus group at index 1. */ int fluid_handle_chorus(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_reverb_chorus_on_command(data, ac, av, out, CHORUS_ON_CDE); } /* 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(void *data, int ac, char **av, fluid_ostream_t out) { if(ac < 1) { fluid_ostream_printf(out, "echo: too few arguments.\n"); return FLUID_FAILED; } fluid_ostream_printf(out, "%s\n", av[0]); return FLUID_OK; } /* Purpose: * Sleep during a time in ms * The command itself is useful to insert a delay between commands. * It can help for example to build a small song using noteon/noteoff commands * in a command file. */ int fluid_handle_sleep(void *data, int ac, char **av, fluid_ostream_t out) { if(ac < 1) { fluid_ostream_printf(out, "sleep: too few arguments.\n"); return -1; } if(!fluid_is_number(av[0])) { fluid_ostream_printf(out, "sleep: argument should be a number in ms.\n"); return -1; } fluid_msleep(atoi(av[0])); /* delay in milliseconds */ return 0; } int fluid_handle_source(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); if(ac < 1) { fluid_ostream_printf(out, "source: too few arguments.\n"); return FLUID_FAILED; } fluid_source(handler, av[0]); return FLUID_OK; } /* Purpose: * Response to 'gain' command. */ int fluid_handle_gain(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); float gain; if(ac < 1) { fluid_ostream_printf(out, "gain: too few arguments.\n"); return FLUID_FAILED; } 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 FLUID_FAILED; }; fluid_synth_set_gain(handler->synth, gain); return FLUID_OK; } /* Response to voice_count command */ static int fluid_handle_voice_count(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_ostream_printf(out, "voice_count: %d\n", fluid_synth_get_active_voice_count(handler->synth)); return FLUID_OK; } /* Purpose: * Response to 'interp' command. */ int fluid_handle_interp(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int interp; int chan = -1; /* -1: Set all channels */ if(ac < 1) { fluid_ostream_printf(out, "interp: too few arguments.\n"); return FLUID_FAILED; } interp = atoi(av[0]); if((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) { fluid_ostream_printf(out, "interp: Bad value\n"); return FLUID_FAILED; }; fluid_synth_set_interp_method(handler->synth, chan, interp); return FLUID_OK; } /* Purpose: * Response to 'interp' command. */ int fluid_handle_interpc(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int interp; int chan; if(ac < 2) { fluid_ostream_printf(out, "interpc: too few arguments.\n"); return FLUID_FAILED; } chan = atoi(av[0]); interp = atoi(av[1]); if((chan < 0) || (chan >= fluid_synth_count_midi_channels(handler->synth))) { fluid_ostream_printf(out, "interp: Bad value for channel number.\n"); return FLUID_FAILED; }; if((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) { fluid_ostream_printf(out, "interp: Bad value for interpolation method.\n"); return FLUID_FAILED; }; fluid_synth_set_interp_method(handler->synth, chan, interp); return FLUID_OK; } int fluid_handle_tuning(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); char *name; int bank, prog; if(ac < 3) { fluid_ostream_printf(out, "tuning: too few arguments.\n"); return FLUID_FAILED; } name = av[0]; if(!fluid_is_number(av[1])) { fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n"); return FLUID_FAILED; } bank = atoi(av[1]); if((bank < 0) || (bank >= 128)) { fluid_ostream_printf(out, "tuning: invalid bank number.\n"); return FLUID_FAILED; }; if(!fluid_is_number(av[2])) { fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n"); return FLUID_FAILED; } prog = atoi(av[2]); if((prog < 0) || (prog >= 128)) { fluid_ostream_printf(out, "tuning: invalid program number.\n"); return FLUID_FAILED; }; fluid_synth_activate_key_tuning(handler->synth, bank, prog, name, NULL, FALSE); return FLUID_OK; } int fluid_handle_tune(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int bank, prog, key; double pitch; if(ac < 4) { fluid_ostream_printf(out, "tune: too few arguments.\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0])) { fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); return FLUID_FAILED; } bank = atoi(av[0]); if((bank < 0) || (bank >= 128)) { fluid_ostream_printf(out, "tune: invalid bank number.\n"); return FLUID_FAILED; }; if(!fluid_is_number(av[1])) { fluid_ostream_printf(out, "tune: 2nd argument should be a number.\n"); return FLUID_FAILED; } prog = atoi(av[1]); if((prog < 0) || (prog >= 128)) { fluid_ostream_printf(out, "tune: invalid program number.\n"); return FLUID_FAILED; }; if(!fluid_is_number(av[2])) { fluid_ostream_printf(out, "tune: 3rd argument should be a number.\n"); return FLUID_FAILED; } key = atoi(av[2]); if((key < 0) || (key >= 128)) { fluid_ostream_printf(out, "tune: invalid key number.\n"); return FLUID_FAILED; }; pitch = atof(av[3]); if(pitch < 0.0f) { fluid_ostream_printf(out, "tune: invalid pitch.\n"); return FLUID_FAILED; }; fluid_synth_tune_notes(handler->synth, bank, prog, 1, &key, &pitch, 0); return FLUID_OK; } int fluid_handle_settuning(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int chan, bank, prog; if(ac < 3) { fluid_ostream_printf(out, "settuning: too few arguments.\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0])) { fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); return FLUID_FAILED; } chan = atoi(av[0]); if((chan < 0) || (chan >= fluid_synth_count_midi_channels(handler->synth))) { fluid_ostream_printf(out, "tune: invalid channel number.\n"); return FLUID_FAILED; }; if(!fluid_is_number(av[1])) { fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n"); return FLUID_FAILED; } bank = atoi(av[1]); if((bank < 0) || (bank >= 128)) { fluid_ostream_printf(out, "tuning: invalid bank number.\n"); return FLUID_FAILED; }; if(!fluid_is_number(av[2])) { fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n"); return FLUID_FAILED; } prog = atoi(av[2]); if((prog < 0) || (prog >= 128)) { fluid_ostream_printf(out, "tuning: invalid program number.\n"); return FLUID_FAILED; }; fluid_synth_activate_tuning(handler->synth, chan, bank, prog, FALSE); return FLUID_OK; } int fluid_handle_resettuning(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int chan; if(ac < 1) { fluid_ostream_printf(out, "resettuning: too few arguments.\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0])) { fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); return FLUID_FAILED; } chan = atoi(av[0]); if((chan < 0) || (chan >= fluid_synth_count_midi_channels(handler->synth))) { fluid_ostream_printf(out, "tune: invalid channel number.\n"); return FLUID_FAILED; }; fluid_synth_deactivate_tuning(handler->synth, chan, FALSE); return FLUID_OK; } int fluid_handle_tunings(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int bank, prog; char name[256]; int count = 0; fluid_synth_tuning_iteration_start(handler->synth); while(fluid_synth_tuning_iteration_next(handler->synth, &bank, &prog)) { fluid_synth_tuning_dump(handler->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 FLUID_OK; } int fluid_handle_dumptuning(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int bank, prog, i, res; double pitch[128]; char name[256]; if(ac < 2) { fluid_ostream_printf(out, "dumptuning: too few arguments.\n"); return FLUID_FAILED; } if(!fluid_is_number(av[0])) { fluid_ostream_printf(out, "dumptuning: 1st argument should be a number.\n"); return FLUID_FAILED; } bank = atoi(av[0]); if((bank < 0) || (bank >= 128)) { fluid_ostream_printf(out, "dumptuning: invalid bank number.\n"); return FLUID_FAILED; }; if(!fluid_is_number(av[1])) { fluid_ostream_printf(out, "dumptuning: 2nd argument should be a number.\n"); return FLUID_FAILED; } prog = atoi(av[1]); if((prog < 0) || (prog >= 128)) { fluid_ostream_printf(out, "dumptuning: invalid program number.\n"); return FLUID_FAILED; }; res = fluid_synth_tuning_dump(handler->synth, bank, prog, name, 256, pitch); if(FLUID_OK != res) { fluid_ostream_printf(out, "Tuning %03d-%03d does not exist.\n", bank, prog); return FLUID_FAILED; } 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 FLUID_OK; } int fluid_handle_set(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); int hints; int ival, icur; double fval, fcur; char *scur; int ret = FLUID_FAILED; if(ac < 2) { fluid_ostream_printf(out, "set: Too few arguments.\n"); return ret; } switch(fluid_settings_get_type(handler->settings, av[0])) { case FLUID_NO_TYPE: fluid_ostream_printf(out, "set: Parameter '%s' not found.\n", av[0]); return ret; case FLUID_INT_TYPE: if(fluid_settings_get_hints(handler->settings, av[0], &hints) == FLUID_OK && hints & FLUID_HINT_TOGGLED) { if(FLUID_STRCASECMP(av[1], "yes") == 0 || FLUID_STRCASECMP(av[1], "true") == 0 || FLUID_STRCASECMP(av[1], "t") == 0) { ival = 1; } else { ival = atoi(av[1]); } } else { ival = atoi(av[1]); } fluid_settings_getint(handler->settings, av[0], &icur); if (icur == ival) { return FLUID_OK; } ret = fluid_settings_setint(handler->settings, av[0], ival); break; case FLUID_NUM_TYPE: fval = atof(av[1]); fluid_settings_getnum(handler->settings, av[0], &fcur); if (fcur == fval) { return FLUID_OK; } ret = fluid_settings_setnum(handler->settings, av[0], fval); break; case FLUID_STR_TYPE: fluid_settings_dupstr(handler->settings, av[0], &scur); if(scur && !FLUID_STRCMP(scur, av[1])) { FLUID_FREE(scur); return FLUID_OK; } ret = fluid_settings_setstr(handler->settings, av[0], av[1]); FLUID_FREE(scur); break; case FLUID_SET_TYPE: fluid_ostream_printf(out, "set: Parameter '%s' is a node.\n", av[0]); return FLUID_FAILED; default: fluid_ostream_printf(out, "Unhandled settings type."); return FLUID_FAILED; } if(ret == FLUID_FAILED) { fluid_ostream_printf(out, "set: Value out of range. Try 'info %s' for valid ranges\n", av[0]); } if((handler->synth != NULL || handler->router != NULL) && !fluid_settings_is_realtime(handler->settings, av[0])) { fluid_ostream_printf(out, "Warning: '%s' is not a realtime setting, changes won't take effect.\n", av[0]); } return ret; } int fluid_handle_get(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); if(ac < 1) { fluid_ostream_printf(out, "get: too few arguments.\n"); return FLUID_FAILED; } switch(fluid_settings_get_type(handler->settings, av[0])) { case FLUID_NO_TYPE: fluid_ostream_printf(out, "get: no such setting '%s'.\n", av[0]); return FLUID_FAILED; case FLUID_NUM_TYPE: { double value; fluid_settings_getnum(handler->settings, av[0], &value); fluid_ostream_printf(out, "%.3f\n", value); break; } case FLUID_INT_TYPE: { int value; fluid_settings_getint(handler->settings, av[0], &value); fluid_ostream_printf(out, "%d\n", value); break; } case FLUID_STR_TYPE: { char *s = NULL; fluid_settings_dupstr(handler->settings, av[0], &s); /* ++ alloc string */ fluid_ostream_printf(out, "%s\n", s ? s : "NULL"); FLUID_FREE(s); /* -- free string */ break; } case FLUID_SET_TYPE: fluid_ostream_printf(out, "%s is a node\n", av[0]); break; } return FLUID_OK; } struct _fluid_handle_settings_data_t { size_t len; fluid_settings_t *settings; fluid_ostream_t out; }; static void fluid_handle_settings_iter1(void *data, const char *name, int type) { struct _fluid_handle_settings_data_t *d = (struct _fluid_handle_settings_data_t *) data; size_t len = FLUID_STRLEN(name); if(len > d->len) { d->len = len; } } static void fluid_handle_settings_iter2(void *data, const char *name, int type) { struct _fluid_handle_settings_data_t *d = (struct _fluid_handle_settings_data_t *) data; size_t 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(d->settings, name)) { case FLUID_NUM_TYPE: { double value; fluid_settings_getnum(d->settings, name, &value); fluid_ostream_printf(d->out, "%.3f\n", value); break; } case FLUID_INT_TYPE: { int value, hints; fluid_settings_getint(d->settings, name, &value); if(fluid_settings_get_hints(d->settings, name, &hints) == FLUID_OK) { 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 = NULL; fluid_settings_dupstr(d->settings, name, &s); /* ++ alloc string */ fluid_ostream_printf(d->out, "%s\n", s ? s : "NULL"); FLUID_FREE(s); /* -- free string */ break; } } } int fluid_handle_settings(void *d, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(d); struct _fluid_handle_settings_data_t data; data.len = 0; data.settings = handler->settings; data.out = out; fluid_settings_foreach(handler->settings, &data, fluid_handle_settings_iter1); fluid_settings_foreach(handler->settings, &data, fluid_handle_settings_iter2); return FLUID_OK; } struct _fluid_handle_option_data_t { int first; fluid_ostream_t out; }; void fluid_handle_print_option(void *data, const char *name, const 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(void *d, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(d); fluid_settings_t *settings = handler->settings; struct _fluid_handle_option_data_t data; if(ac < 1) { fluid_ostream_printf(out, "info: too few arguments.\n"); return FLUID_FAILED; } 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 FLUID_FAILED; case FLUID_NUM_TYPE: { double value, min, max, def; if(fluid_settings_getnum_range(settings, av[0], &min, &max) == FLUID_OK && fluid_settings_getnum(settings, av[0], &value) == FLUID_OK && fluid_settings_getnum_default(settings, av[0], &def) == FLUID_OK) { 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", def); fluid_ostream_printf(out, "Real-time: %s\n", fluid_settings_is_realtime(settings, av[0]) ? "yes" : "no"); } else { fluid_ostream_printf(out, "An error occurred when processing %s\n", av[0]); } break; } case FLUID_INT_TYPE: { int value, min, max, def, hints; if(fluid_settings_getint_range(settings, av[0], &min, &max) == FLUID_OK && fluid_settings_getint(settings, av[0], &value) == FLUID_OK && fluid_settings_get_hints(settings, av[0], &hints) == FLUID_OK && fluid_settings_getint_default(settings, av[0], &def) == FLUID_OK) { 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"); } else { fluid_ostream_printf(out, "An error occurred when processing %s\n", av[0]); } break; } case FLUID_STR_TYPE: { char *s = NULL; 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_FREE(s); /* -- free string */ fluid_settings_getstr_default(settings, av[0], &s); fluid_ostream_printf(out, "Default value: %s\n", 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 FLUID_OK; } int fluid_handle_reset(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_synth_system_reset(handler->synth); return FLUID_OK; } int fluid_handle_quit(void *data, int ac, char **av, fluid_ostream_t out) { fluid_ostream_printf(out, "cheers!\n"); return -2; } int fluid_handle_help(void *data, 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; unsigned int i; fluid_ostream_printf(out, "\n"); /* 1st argument (optional): help topic */ if(ac >= 1) { topic = av[0]; } if(FLUID_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; i < FLUID_N_ELEMENTS(fluid_commands); i++) { int listed_first_time = 1; unsigned int ii; for(ii = 0; ii < i; ii++) { if(FLUID_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; i < FLUID_N_ELEMENTS(fluid_commands); i++) { if(fluid_commands[i].help != NULL) { if(FLUID_STRCMP(topic, "all") == 0 || FLUID_STRCMP(topic, fluid_commands[i].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 FLUID_OK; } #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_handle_router_clear(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_midi_router_t *router = handler->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_handle_router_default(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_midi_router_t *router = handler->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_handle_router_begin(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_midi_router_t *router = handler->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) { handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_NOTE; } else if(FLUID_STRCMP(av[0], "cc") == 0) { handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_CC; } else if(FLUID_STRCMP(av[0], "prog") == 0) { handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_PROG_CHANGE; } else if(FLUID_STRCMP(av[0], "pbend") == 0) { handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_PITCH_BEND; } else if(FLUID_STRCMP(av[0], "cpress") == 0) { handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE; } else if(FLUID_STRCMP(av[0], "kpress") == 0) { handler->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(handler->cmd_rule) { delete_fluid_midi_router_rule(handler->cmd_rule); } handler->cmd_rule = new_fluid_midi_router_rule(); if(!handler->cmd_rule) { return FLUID_FAILED; } return FLUID_OK; } /* Command handler for "router_end" command */ int fluid_handle_router_end(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_midi_router_t *router = handler->router; if(ac != 0) { fluid_ostream_printf(out, "router_end needs no arguments.\n"); return FLUID_FAILED; } CHECK_VALID_ROUTER(router, out); if(!handler->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, handler->cmd_rule, handler->cmd_rule_type) != FLUID_OK) { delete_fluid_midi_router_rule(handler->cmd_rule); /* Free on failure */ } handler->cmd_rule = NULL; return FLUID_OK; } /* Command handler for "router_chan" command */ int fluid_handle_router_chan(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_midi_router_t *router = handler->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(!handler->cmd_rule) { fluid_ostream_printf(out, "No active router_begin command.\n"); return FLUID_FAILED; } fluid_midi_router_rule_set_chan(handler->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_handle_router_par1(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_midi_router_t *router = handler->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(!handler->cmd_rule) { fluid_ostream_printf(out, "No active router_begin command.\n"); return FLUID_FAILED; } fluid_midi_router_rule_set_param1(handler->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_handle_router_par2(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_midi_router_t *router = handler->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(!handler->cmd_rule) { fluid_ostream_printf(out, "No active router_begin command.\n"); return FLUID_FAILED; } fluid_midi_router_rule_set_param2(handler->cmd_rule, atoi(av[0]), atoi(av[1]), atof(av[2]), atoi(av[3])); return FLUID_OK; } /** commands Poly/mono mode *************************************************/ static const char *const mode_name[] = { "poly omni on (0)", "mono omni on (1)", "poly omni off(2)", "mono omni off(3)" }; /* Prints result message for commands: basicchannels, resetbasicchannels. Prints all basic channels and print a warning if there is no basic channel. @param synth the synth instance. @param out output stream. */ static int print_basic_channels(fluid_synth_t *synth, fluid_ostream_t out) { static const char warning_msg[] = "Warning: no basic channels. All MIDI channels are disabled.\n" "Make use of setbasicchannels to set at least a default basic channel.\n"; int n_chan = synth->midi_channels; int i, n = 0; /* prints all basic channels */ for(i = 0; i < n_chan; i++) { int basic_chan, mode_chan, val; if(fluid_synth_get_basic_channel(synth, i, &basic_chan, &mode_chan, &val) == FLUID_OK) { if(basic_chan == i) { n++; fluid_ostream_printf(out, "Basic channel:%3d, %s, nbr:%3d\n", i, mode_name[mode_chan & FLUID_CHANNEL_MODE_MASK ], val); } } else { return FLUID_FAILED; /* error */ } } /* prints a warning if there is no basic channel */ if(n == 0) { fluid_ostream_printf(out, warning_msg); } return FLUID_OK; } /*----------------------------------------------------------------------------- basicchannels Prints the list of all MIDI basic channels information example: Basic channel: 0, poly omni on (0), nbr: 3 Basic channel: 3, poly omni off(2), nbr: 1 Basic channel: 8, mono omni off(3), nbr: 2 Basic channel: 13, mono omni on (1), nbr: 3 */ int fluid_handle_basicchannels(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; return print_basic_channels(synth, out); } /* Searches a mode name and returns the channel mode number. name must be: poly_omnion, mono_omnion, poly_omnioff, mono_omnioff. @param name to search. @return channel mode number (0 to 3) if name is valid, -1 otherwise. */ static int get_channel_mode_num(char *name) { /* argument names for channel mode parameter (see resetbasicchannels and setbasicchannels commands*/ static const char *const name_channel_mode [FLUID_CHANNEL_MODE_LAST] = {"poly_omnion", "mono_omnion", "poly_omnioff", "mono_omnioff"}; int i; for(i = 0 ; i < FLUID_CHANNEL_MODE_LAST; i++) { if(! FLUID_STRCMP(name, name_channel_mode[i])) { return i; } } return -1; } static const char invalid_arg_msg[] = "invalid argument\n"; /* checks basic channels arguments: chan1 mode1 val chan2 mode2 val2 ... All arguments can be numeric. mode parameter can be a name. Each group entry must have 3 parameters (chan,mode,val). @param ac argument count. @param av argument table. @param out output stream. @param name_cde command name prefix. @return 0 if arguments are valid, -1 otherwise. */ static int check_basicchannels_arguments(int ac, char **av, fluid_ostream_t out, char const *name_cde) { static const char too_few_arg_msg[] = "too few argument, chan mode val [chan mode val]...\n"; int i; for(i = 0; i < ac; i++) { /* checks parameters for list entries: chan1 mode1 val chan2 mode2 val2 ...*/ /* all parameters can be numeric. mode parameter can be a name. */ if(!fluid_is_number(av[i]) && ((i % 3 != 1) || get_channel_mode_num(av[i]) < 0)) { fluid_ostream_printf(out, "%s: %s", name_cde, invalid_arg_msg); return -1; } } if(ac % 3) { /* each group entry needs 3 parameters: basicchan,mode,val */ fluid_ostream_printf(out, "%s: channel %d, %s\n", name_cde, atoi(av[((ac / 3) * 3)]), too_few_arg_msg); return -1; } return 0; } /* checks channels arguments: chan1 chan2 ... all arguments must be numeric. @param ac argument count. @param av argument table. @param out output stream. @param name_cde command name prefix. @return 0 if arguments are valid, -1 otherwise. */ static int check_channels_arguments(int ac, char **av, fluid_ostream_t out, char const *name_cde) { int i; for(i = 0; i < ac; i++) { if(!fluid_is_number(av[i])) { fluid_ostream_printf(out, "%s: %s", name_cde, invalid_arg_msg); return -1; } } return 0; } /*----------------------------------------------------------------------------- resetbasicchannels With no parameters the command resets all basic channels. Note: Be aware than when a synth instance has no basic channels, all channels are disabled. In the intend to get some MIDI channels enabled, use the command setbasicchannels. resetbasicchannels chan1 [chan2 . . .] Resets basic channel group chan1, basic channel group chan2 . . . */ int fluid_handle_resetbasicchannels(void *data, int ac, char **av, fluid_ostream_t out) { static const char name_cde[] = "resetbasicchannels"; FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; /* checks channels arguments: chan1 chan2 .... */ if(check_channels_arguments(ac, av, out, name_cde) < 0) { return -1; } if(ac) { /* resetbasicchannels chan1 [chan2 . . .] */ int i; for(i = 0; i < ac; i++) { int chan = atoi(av[i]); int result = fluid_synth_reset_basic_channel(synth, chan); if(result == FLUID_FAILED) { fluid_ostream_printf(out, "%s: channel %3d, %s", name_cde, chan, invalid_arg_msg); } } } else { /* resets all basic channels */ fluid_synth_reset_basic_channel(synth, -1); } /* prints result */ return print_basic_channels(synth, out); } /*----------------------------------------------------------------------------- setbasicchannels With no parameters the command sets one channel basic at basic channel 0 in Omni On Poly (i.e all the MIDI channels are polyphonic). setbasicchannels chan1 mode1 nbr1 [chan2 mode2 nbr2] ... ... Adds basic channel 1 and 2 The command fails if any channels overlaps any existing basic channel groups. To make room if necessary, existing basic channel groups can be cleared using resetbasicchannels command. Mode can be a numeric value or a name: numeric: 0 to 3 or name: poly_omnion , mono_omnion, poly_omnioff, mono_omnioff. */ int fluid_handle_setbasicchannels(void *data, int ac, char **av, fluid_ostream_t out) { static const char name_cde[] = "setbasicchannels"; FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; int result; int i, n ; if(!ac) { /* sets one default basic channel */ fluid_synth_reset_basic_channel(synth, -1); /* reset all basic channels */ /* sets one basic channel Omni On Poly (i.e all the MIDI channels are polyphonic) */ fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, 0); return 0; } /* checks parameters: chan1 mode1 val1 chan2 mode2 val2 */ if(check_basicchannels_arguments(ac, av, out, name_cde) < 0) { return -1; } n = ac / 3; /* number of basic channel information */ for(i = 0; i < n; i++) { int basicchan, mode, val; basicchan = atoi(av[(i * 3)]); /* chan is numeric */ if(fluid_is_number(av[(i * 3) + 1])) { /* chan is numeric */ mode = atoi(av[(i * 3) + 1]); } else { /* mode is a name */ mode = get_channel_mode_num(av[(i * 3) + 1]); } val = atoi(av[(i * 3) + 2]); /* val is numeric */ /* changes or sets basic channels */ result = fluid_synth_set_basic_channel(synth, basicchan, mode, val); if(result == FLUID_FAILED) { fluid_ostream_printf(out, "%s: channel %3d, mode %3d, nbr %3d, %s", name_cde, basicchan, mode, val, invalid_arg_msg); } } return 0; } /* Print result message : "channel:x is outside MIDI channel count(y)" for commands: channelsmode, portamentomode, legatomode, breathmode,setbreathmode. @param out output stream. @param name_cde command name prefix. @param chan, MIDI channel number x. @param n_chan, number of MIDI channels y. */ static void print_channel_is_outside_count(fluid_ostream_t out, char const *name_cde, int chan, int n_chan) { fluid_ostream_printf(out, "%s: channel %3d is outside MIDI channel count(%d)\n", name_cde, chan, n_chan); } /*----------------------------------------------------------------------------- channelsmode Prints channel mode of all MIDI channels (Poly/mono, Enabled, Basic Channel) example Channel , Status , Type , Mode , Nbr of channels channel: 0, disabled channel: 1, disabled channel: 2, disabled channel: 3, disabled channel: 4, disabled channel: 5, enabled, basic channel, mono omni off(3), nbr: 2 channel: 6, enabled, -- , mono , -- channel: 7, disabled channel: 8, disabled channel: 9, disabled channel: 10, enabled, basic channel, mono omni off(3), nbr: 4 channel: 11, enabled, -- , mono , -- channel: 12, enabled, -- , mono , -- channel: 13, enabled, -- , mono , -- channel: 14, disabled channel: 15, disabled channelsmode chan1 chan2 Prints only channel mode of MIDI channels chan1, chan2 */ int fluid_handle_channelsmode(void *data, int ac, char **av, fluid_ostream_t out) { static const char header[] = "Channel , Status , Type , Mode , Nbr of channels\n"; static const char name_cde[] = "channelsmode"; FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; int i, n, n_chan = synth->midi_channels; /* checks parameters: chan1 chan2 .... */ if(check_channels_arguments(ac, av, out, name_cde) < 0) { return -1; } if(ac) { n = ac; /* prints ac MIDI channels number */ } else { n = n_chan; /* prints all MIDI channels number */ } /* prints header */ fluid_ostream_printf(out, header); for(i = 0; i < n; i++) { int basic_chan, mode, val; int chan = ac ? atoi(av[i]) : i; int result = fluid_synth_get_basic_channel(synth, chan, &basic_chan, &mode, &val); if(result == FLUID_OK) { if(basic_chan != FLUID_FAILED) { /* This channel is enabled */ const char *p_basicchan; /* field basic channel */ const char *p_mode; /* field mode */ const char *p_nbr; /* field Nbr */ static const char blank[] = "--"; /* field empty */ if(chan == basic_chan) { /* This channel is a basic channel */ char nbr[10]; /* field Nbr */ FLUID_SNPRINTF(nbr, sizeof(nbr), "nbr:%3d", val); p_nbr = nbr; p_mode = mode_name[mode]; p_basicchan = "basic channel"; } else { /* This channel is member of a basic channel group */ p_basicchan = blank; if(mode & FLUID_CHANNEL_POLY_OFF) { p_mode = "mono"; } else { p_mode = "poly"; } p_nbr = blank; } fluid_ostream_printf(out, "channel:%3d, enabled, %-13s, %-16s, %s\n", chan, p_basicchan, p_mode, p_nbr); } else { fluid_ostream_printf(out, "channel:%3d, disabled\n", chan); } } else { print_channel_is_outside_count(out, name_cde, chan, n_chan); if(i < n - 1) { fluid_ostream_printf(out, header); } } } return 0; } /** commands mono legato mode ***********************************************/ /* Prints result message for commands: legatomode, portamentomode. @param result result from the command (FLUID_OK,FLUID_FAILED). @param out output stream. @param name_cde command name prefix. @param chan MIDI channel number to display. @param name_mode name of the mode to display. @param n_chan, number of MIDI channels. */ static void print_result_get_channel_mode(int result, fluid_ostream_t out, char const *name_cde, int chan, char const *name_mode, int n_chan) { if(result == FLUID_OK) { fluid_ostream_printf(out, "%s: channel %3d, %s\n", name_cde, chan, name_mode); } else { print_channel_is_outside_count(out, name_cde, chan, n_chan); } } /*----------------------------------------------------------------------------- legatomode Prints legato mode of all MIDI channels example channel: 0, (1)multi-retrigger channel: 1, (0)retrigger channel: 2, (1)multi-retrigger ..... legatomode chan1 chan2 Prints only legato mode of MIDI channels chan1, chan2 */ int fluid_handle_legatomode(void *data, int ac, char **av, fluid_ostream_t out) { static const char name_cde[] = "legatomode"; static const char *const name_legato_mode[FLUID_CHANNEL_LEGATO_MODE_LAST] = { "(0)retrigger", "(1)multi-retrigger" }; FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; int mode = 0; int i, n, n_chan = synth->midi_channels; /* checks channels arguments: chan1 chan2 .... */ if(check_channels_arguments(ac, av, out, name_cde) < 0) { return -1; } if(ac) { n = ac; /* prints ac MIDI channels number */ } else { n = n_chan; /* prints all MIDI channels number */ } /* prints header */ fluid_ostream_printf(out, "Channel , legato mode\n"); for(i = 0; i < n; i++) { int chan = ac ? atoi(av[i]) : i; int result = fluid_synth_get_legato_mode(synth, chan, &mode); print_result_get_channel_mode(result, out, name_cde, chan, name_legato_mode[mode], n_chan); } return 0; } /* checks channels arguments by group: -example by group of 2 arguments: chan1 val1 chan2 val2 .. .. -example by group of 4 arguments: chan1 val1 val2 val3 chan2 val1 val2 val3 .... all arguments must be numeric. @param ac argument count. @param av argument table. @param nbr_arg_group number of arguments by group expected. @param out output stream. @param name_cde command name prefix. @param nbr_arg_group_msg message when the number of argument by group is invalid. @return 0 if arguments are valid, -1 otherwise. */ static int check_channels_group_arguments(int ac, char **av, int nbr_arg_group, fluid_ostream_t out, char const *name_cde, char const *nbr_arg_group_msg ) { if(ac) { /* checks channels numeric arguments */ if(check_channels_arguments(ac, av, out, name_cde) < 0) { return -1; } if(ac % nbr_arg_group) { /* each group entry needs nbr_arg_group parameters */ fluid_ostream_printf(out, "%s: channel %d, %s\n", name_cde, atoi(av[((ac / nbr_arg_group) * nbr_arg_group)]), nbr_arg_group_msg); return -1; } } else { fluid_ostream_printf(out, "%s: %s", name_cde, nbr_arg_group_msg); return -1; } return 0; } /* Prints result message for commands: setlegatomode, setportamentomode. @param result result from the command (FLUID_FAILED). @param out output stream. @param name_cde command name prefix. @param chan, MIDI channel number to display. @param mode, mode value to display. */ static void print_result_set_channel_mode(int result, fluid_ostream_t out, char const *name_cde, int chan, int mode) { if(result == FLUID_FAILED) { fluid_ostream_printf(out, "%s: channel %3d, mode %3d, %s", name_cde, chan, mode, invalid_arg_msg); } } static const char too_few_arg_chan_mode_msg[] = "too few argument, chan mode [chan mode]...\n"; /*----------------------------------------------------------------------------- setlegatomode chan0 mode1 [chan1 mode0] .. .. Changes legato mode for channels chan0 and [chan1] */ int fluid_handle_setlegatomode(void *data, int ac, char **av, fluid_ostream_t out) { static const char name_cde[] = "setlegatomode"; FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; int i, n ; /* checks channels arguments by group of 2: chan1 val1 chan2 val1 .. ..*/ if(check_channels_group_arguments(ac, av, 2, out, name_cde, too_few_arg_chan_mode_msg) < 0) { return -1; } n = ac / 2; /* number of legato groups information */ for(i = 0; i < n; i++) { int chan = atoi(av[(i * 2)]); int mode = atoi(av[(i * 2) + 1]); /* changes legato mode */ int result = fluid_synth_set_legato_mode(synth, chan, mode); print_result_set_channel_mode(result, out, name_cde, chan, mode); } return 0; } /** commands mono/poly portamento mode **************************************/ /*----------------------------------------------------------------------------- portamentomode Prints portamento mode of all MIDI channels example channel: 0, (2)staccato only channel: 1, (1)legato only channel: 2, (0)each note channel: 3, (1)legato only ..... portamentomode chan1 chan2 Prints only portamento mode of MIDI channels chan1, chan2 */ int fluid_handle_portamentomode(void *data, int ac, char **av, fluid_ostream_t out) { static const char name_cde[] = "portamentomode"; static const char *const name_portamento_mode[FLUID_CHANNEL_PORTAMENTO_MODE_LAST] = { "(0)each note", "(1)legato only", "(2)staccato only" }; FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; int mode = 0; int i, n, n_chan = synth->midi_channels; /* checks channels arguments: chan1 chan2 . . . */ if(check_channels_arguments(ac, av, out, name_cde) < 0) { return -1; } if(ac) { n = ac; /* prints ac MIDI channels number */ } else { n = n_chan; /* prints all MIDI channels number */ } /* prints header */ fluid_ostream_printf(out, "Channel , portamento mode\n"); for(i = 0; i < n; i++) { int chan = ac ? atoi(av[i]) : i; int result = fluid_synth_get_portamento_mode(synth, chan, &mode); print_result_get_channel_mode(result, out, name_cde, chan, name_portamento_mode[mode], n_chan); } return 0; } /*----------------------------------------------------------------------------- setportamentomode chan1 mode1 [chan2 mode2] .. .. Changes portamento mode for channels chan1 and [chan2] */ int fluid_handle_setportamentomode(void *data, int ac, char **av, fluid_ostream_t out) { static const char name_cde[] = "setportamentomode"; FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; int i, n ; /* checks channels arguments by group of 2: chan1 val1 chan2 val1 .. .. */ if(check_channels_group_arguments(ac, av, 2, out, name_cde, too_few_arg_chan_mode_msg) < 0) { return -1; } n = ac / 2; /* number of portamento groups information */ for(i = 0; i < n; i++) { int chan = atoi(av[(i * 2)]); int mode = atoi(av[(i * 2) + 1]); /* changes portamento mode */ int result = fluid_synth_set_portamento_mode(synth, chan, mode); print_result_set_channel_mode(result, out, name_cde, chan, mode); } return 0; } /** commands mono/poly breath mode *******************************************/ /*----------------------------------------------------------------------------- breathmode Prints breath options of all MIDI channels. poly breath on/off, mono breath on/off, breath sync on/off example Channel , poly breath , mono breath , breath sync channel: 0, off , off , off channel: 1, off , off , off channel: 2, off , off , off ..... breathmode chan1 chan2 Prints only breath mode of MIDI channels chan1, chan2 */ int fluid_handle_breathmode(void *data, int ac, char **av, fluid_ostream_t out) { static const char name_cde[] = "breathmode"; static const char *const header = "Channel , poly breath , mono breath , breath sync\n"; FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; int breathmode; int i, n, n_chan = synth->midi_channels; /* checks channels arguments: chan1 chan2 . . . */ if(check_channels_arguments(ac, av, out, name_cde) < 0) { return -1; } if(ac) { n = ac; /* prints ac MIDI channels number */ } else { n = n_chan; /* prints all MIDI channels number */ } /* prints header */ fluid_ostream_printf(out, header); for(i = 0; i < n; i++) { int chan = ac ? atoi(av[i]) : i; int result = fluid_synth_get_breath_mode(synth, chan, &breathmode); if(result == FLUID_OK) { static const char on_msg[] = "on"; static const char off_msg[] = "off"; const char *msg_poly_breath, * msg_mono_breath, * msg_breath_sync; if(breathmode & FLUID_CHANNEL_BREATH_POLY) { msg_poly_breath = on_msg; } else { msg_poly_breath = off_msg; } if(breathmode & FLUID_CHANNEL_BREATH_MONO) { msg_mono_breath = on_msg; } else { msg_mono_breath = off_msg; } if(breathmode & FLUID_CHANNEL_BREATH_SYNC) { msg_breath_sync = on_msg; } else { msg_breath_sync = off_msg; } fluid_ostream_printf(out, "channel:%3d, %-12s, %-12s, %-11s\n", chan, msg_poly_breath, msg_mono_breath, msg_breath_sync); } else { print_channel_is_outside_count(out, name_cde, chan, n_chan); if(i < n - 1) { fluid_ostream_printf(out, header); } } } return 0; } /*----------------------------------------------------------------------------- setbreathmode chan1 poly_breath_mode(1/0) mono_breath_mode(1/0) mono_breath_sync(1/0) Changes breath options for channels chan1 [chan2] .. .. Example: setbreathmode 4 0 1 1 Parameter 1 is the channel number (i.e 4). Parameter 2 is the " Breath modulator " enable/disable for poly mode (i.e disabled). Parameter 3 is the " Breath modulator " enable/disable for mono mode (i.e enabled). Parameter 4 is "breath sync noteOn/Off" enable/disable for mono mode only (i.e enabled). */ int fluid_handle_setbreathmode(void *data, int ac, char **av, fluid_ostream_t out) { static const char name_cde[] = "setbreathmode"; static const char too_few_arg_breath_msg[] = "too few argument:\nchan 1/0(breath poly) 1/0(breath mono) 1/0(breath sync mono)[..]\n"; FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; int i, n, n_chan = synth->midi_channels; /* checks channels arguments by group of 4: chan1 val1 val2 val3 chan2 val1 val2 val3 .... ....*/ if(check_channels_group_arguments(ac, av, 4, out, name_cde, too_few_arg_breath_msg) < 0) { return -1; } n = ac / 4; /* number of breath groups information */ for(i = 0; i < n; i++) { int result; int chan = atoi(av[(i * 4)]); int poly_breath = atoi(av[(i * 4) + 1]); int mono_breath = atoi(av[(i * 4) + 2]); int breath_sync = atoi(av[(i * 4) + 3]); int breath_infos = 0; /* changes breath infos */ if(poly_breath) { breath_infos |= FLUID_CHANNEL_BREATH_POLY; } if(mono_breath) { breath_infos |= FLUID_CHANNEL_BREATH_MONO; } if(breath_sync) { breath_infos |= FLUID_CHANNEL_BREATH_SYNC; } result = fluid_synth_set_breath_mode(synth, chan, breath_infos); if(result == FLUID_FAILED) { print_channel_is_outside_count(out, name_cde, chan, n_chan); } } return 0; } /** commands for Midi file player ******************************************/ /* check player argument */ int player_check_arg(const char *name_cde, int ac, char **av, fluid_ostream_t out) { /* check if there is one argument that is a number */ if(ac != 1 || !fluid_is_number(av[0])) { fluid_ostream_printf(out, "%s: %s", name_cde, invalid_arg_msg); return FLUID_FAILED; } return FLUID_OK; } /* print current position, total ticks, and tempo * @current_tick position to display. if -1 the function displays the value * returned by fluid_player_get_current_tick(). */ void player_print_position(fluid_player_t *player, int current_tick, fluid_ostream_t out) { int total_ticks = fluid_player_get_total_ticks(player); int tempo_bpm = fluid_player_get_bpm(player); if(current_tick == -1) { current_tick = fluid_player_get_current_tick(player); } fluid_ostream_printf(out, "player current pos:%d, end:%d, bpm:%d\n\n", current_tick, total_ticks, tempo_bpm); } /* player commands enum */ enum { PLAYER_LOOP_CDE, /* player_loop num,(Set loop number to num) */ PLAYER_SEEK_CDE, /* player_seek num (Move forward/backward to +/-num ticks) */ PLAYER_STOP_CDE, /* player_stop (Stop playing) */ PLAYER_CONT_CDE, /* player_cont (Continue playing) */ PLAYER_NEXT_CDE, /* player_next (Move to next song) */ PLAYER_START_CDE /* player_start (Move to start of song) */ }; /* Command handler: see player commands enum (above) * @cmd player commands enumeration value. */ int fluid_handle_player_cde(void *data, int ac, char **av, fluid_ostream_t out, int cmd) { FLUID_ENTRY_COMMAND(data); int arg = 0, was_running; int seek = -1; /* current seek position in tick */ /* commands name table */ static const char *name_cde[] = {"player_loop", "player_seek"}; /* get argument for PLAYER_LOOP_CDE, PLAYER_SEEK_CDE */ if(cmd <= PLAYER_SEEK_CDE) { /* check argument */ if(player_check_arg(name_cde[cmd], ac, av, out) == FLUID_FAILED) { return FLUID_FAILED; } arg = atoi(av[0]); } if(cmd == PLAYER_LOOP_CDE) /* player_loop */ { fluid_player_set_loop(handler->player, arg); return FLUID_OK; } if(cmd == PLAYER_CONT_CDE) /* player_cont */ { fluid_player_play(handler->player); return FLUID_OK; } was_running = fluid_player_get_status(handler->player) == FLUID_PLAYER_PLAYING; if(was_running) { fluid_player_stop(handler->player); /* player_stop */ } if(cmd != PLAYER_STOP_CDE) { /* seek for player_next, player_seek, player_start */ /* set seek to maximum position */ seek = fluid_player_get_total_ticks(handler->player); if(cmd == PLAYER_SEEK_CDE) { /* Move position forward/backward +/- num ticks*/ arg += fluid_player_get_current_tick(handler->player); /* keep seek between minimum and maximum in current song */ if(arg < 0) { seek = 0; /* minimum position */ } else if(!was_running || arg < seek) { seek = arg; /* seek < maximum position */ } } if(cmd == PLAYER_START_CDE) /* player_start */ { seek = 0; /* beginning of the current song */ } fluid_player_seek(handler->player, seek); if(was_running) { fluid_player_play(handler->player); } } /* display position */ player_print_position(handler->player, seek, out); return FLUID_OK; } /* Command handler for "player_start" command */ int fluid_handle_player_start(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_player_cde(data, ac, av, out, PLAYER_START_CDE); } /* Command handler for "player_stop" command */ int fluid_handle_player_stop(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_player_cde(data, ac, av, out, PLAYER_STOP_CDE); } /* Command handler for "player_continue" command */ int fluid_handle_player_continue(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_player_cde(data, ac, av, out, PLAYER_CONT_CDE); } /* Command handler for "player_seek" command */ int fluid_handle_player_seek(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_player_cde(data, ac, av, out, PLAYER_SEEK_CDE); } /* Command handler for "player_next" command */ int fluid_handle_player_next_song(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_player_cde(data, ac, av, out, PLAYER_NEXT_CDE); } /* Command handler for "player_loop" command */ int fluid_handle_player_loop(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_player_cde(data, ac, av, out, PLAYER_LOOP_CDE); } /* Command handler for player tempo commands: player_tempo_int [mul], set the player to internal tempo multiplied by mul player_tempo_bpm bpm, set the player to external tempo in beat per minute. examples: player_tempo_int set the player to internal tempo with a default multiplier set to 1.0. player_tempo_int 0.5 set the player to internal tempo divided by 2. player_tempo_bpm 75, set the player to external tempo of 75 beats per minute. */ int fluid_handle_player_tempo_cde(void *data, int ac, char **av, fluid_ostream_t out, int cmd) { FLUID_ENTRY_COMMAND(data); /* default multiplier for player_tempo_int command without argument*/ double arg = 1.0F; /* commands name table */ static const char *name_cde[] = {"player_tempo_int", "player_tempo_bpm"}; static const struct /* argument infos */ { double min; double max; char *name; }argument[2] = {{0.1F, 10.F, "multiplier"}, {1.0F, 600.0F, "bpm"}}; /* get argument for: player_tempo_int [mul], player_tempo_bpm bpm */ if((cmd == FLUID_PLAYER_TEMPO_EXTERNAL_BPM) || ac) { /* check argument presence */ if(player_check_arg(name_cde[cmd], ac, av, out) == FLUID_FAILED) { return FLUID_FAILED; } arg = atof(av[0]); /* check if argument is in valid range */ if(arg < argument[cmd].min || arg > argument[cmd].max) { fluid_ostream_printf(out, "%s: %s %f must be in range [%f..%f]\n", name_cde[cmd], argument[cmd].name, arg, argument[cmd].min, argument[cmd].max); return FLUID_FAILED; } } fluid_player_set_tempo(handler->player, cmd, arg); return FLUID_OK; } /* Command handler for "player_tempo_int [mul]" command */ int fluid_handle_player_tempo_int(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_player_tempo_cde(data, ac, av, out, FLUID_PLAYER_TEMPO_INTERNAL); } /* Command handler for "player_tempo_bpm bmp" command */ int fluid_handle_player_tempo_bpm(void *data, int ac, char **av, fluid_ostream_t out) { return fluid_handle_player_tempo_cde(data, ac, av, out, FLUID_PLAYER_TEMPO_EXTERNAL_BPM); } #ifdef LADSPA #define CHECK_LADSPA_ENABLED(_fx, _out) \ if (_fx == NULL) \ { \ fluid_ostream_printf(_out, "LADSPA is not enabled.\n"); \ return FLUID_FAILED; \ } #define CHECK_LADSPA_INACTIVE(_fx, _out) \ if (fluid_ladspa_is_active(_fx)) \ { \ fluid_ostream_printf(_out, "LADSPA already started.\n"); \ return FLUID_FAILED; \ } #define LADSPA_ERR_LEN (1024) /** * ladspa_start */ int fluid_handle_ladspa_start(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; char error[LADSPA_ERR_LEN]; if(ac != 0) { fluid_ostream_printf(out, "ladspa_start does not accept any arguments\n"); return FLUID_FAILED; } CHECK_LADSPA_ENABLED(fx, out); CHECK_LADSPA_INACTIVE(fx, out); if(fluid_ladspa_check(fx, error, LADSPA_ERR_LEN) != FLUID_OK) { fluid_ostream_printf(out, "Unable to start LADSPA: %s", error); return FLUID_FAILED; } if(fluid_ladspa_activate(fx) != FLUID_OK) { fluid_ostream_printf(out, "Unable to start LADSPA.\n"); return FLUID_FAILED; } return FLUID_OK; } /** * ladspa_stop */ int fluid_handle_ladspa_stop(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; if(ac != 0) { fluid_ostream_printf(out, "ladspa_stop does not accept any arguments\n"); return FLUID_FAILED; } CHECK_LADSPA_ENABLED(fx, out); if(!fluid_ladspa_is_active(fx)) { fluid_ostream_printf(out, "LADSPA has not been started.\n"); } if(fluid_ladspa_deactivate(fx) != FLUID_OK) { fluid_ostream_printf(out, "Unable to stop LADSPA.\n"); return FLUID_FAILED; } return FLUID_OK; } /** * ladspa_reset */ int fluid_handle_ladspa_reset(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; if(ac != 0) { fluid_ostream_printf(out, "ladspa_reset does not accept any arguments\n"); return FLUID_FAILED; } CHECK_LADSPA_ENABLED(fx, out); fluid_ladspa_reset(fx); return FLUID_OK; } /** * ladspa_check */ int fluid_handle_ladspa_check(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; char error[LADSPA_ERR_LEN]; if(ac != 0) { fluid_ostream_printf(out, "ladspa_reset does not accept any arguments\n"); return FLUID_FAILED; } CHECK_LADSPA_ENABLED(fx, out); if(fluid_ladspa_check(fx, error, LADSPA_ERR_LEN) != FLUID_OK) { fluid_ostream_printf(out, "LADSPA check failed: %s", error); return FLUID_FAILED; } fluid_ostream_printf(out, "LADSPA check ok\n"); return FLUID_OK; } /** * ladspa_set */ int fluid_handle_ladspa_set(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; if(ac != 3) { fluid_ostream_printf(out, "ladspa_set needs three arguments: \n"); return FLUID_FAILED; }; CHECK_LADSPA_ENABLED(fx, out); /* Redundant check, just here to give a more detailed error message */ if(!fluid_ladspa_effect_port_exists(fx, av[0], av[1])) { fluid_ostream_printf(out, "Port '%s' not found on effect '%s'\n", av[1], av[0]); return FLUID_FAILED; } if(fluid_ladspa_effect_set_control(fx, av[0], av[1], atof(av[2])) != FLUID_OK) { fluid_ostream_printf(out, "Failed to set port '%s' on effect '%s', " "maybe it is not a control port?\n", av[1], av[0]); return FLUID_FAILED; } return FLUID_OK; }; /** * ladspa_buffer */ int fluid_handle_ladspa_buffer(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; if(ac != 1) { fluid_ostream_printf(out, "ladspa_buffer needs one argument: \n"); return FLUID_FAILED; }; CHECK_LADSPA_ENABLED(fx, out); CHECK_LADSPA_INACTIVE(fx, out); if(fluid_ladspa_add_buffer(fx, av[0]) != FLUID_OK) { fluid_ostream_printf(out, "Failed to add buffer\n"); return FLUID_FAILED; } return FLUID_OK; }; /** * ladspa_effect [plugin] [--mix [gain]] */ int fluid_handle_ladspa_effect(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; char *plugin_name = NULL; int pos; int mix = FALSE; float gain = 1.0f; if(ac < 2 || ac > 5) { fluid_ostream_printf(out, "ladspa_effect invalid arguments: " " [plugin] [--mix [gain]]\n"); return FLUID_FAILED; } pos = 2; /* If the first optional arg is not --mix, then it must be the plugin label */ if((pos < ac) && (FLUID_STRCMP(av[pos], "--mix") != 0)) { plugin_name = av[pos]; pos++; } /* If this optional arg is --mix and there's an argument after it, that that * must be the gain */ if((pos < ac) && (FLUID_STRCMP(av[pos], "--mix") == 0)) { mix = TRUE; if(pos + 1 < ac) { gain = atof(av[pos + 1]); } } CHECK_LADSPA_ENABLED(fx, out); CHECK_LADSPA_INACTIVE(fx, out); if(fluid_ladspa_add_effect(fx, av[0], av[1], plugin_name) != FLUID_OK) { fluid_ostream_printf(out, "Failed to create effect\n"); return FLUID_FAILED; } if(mix) { if(!fluid_ladspa_effect_can_mix(fx, av[0])) { fluid_ostream_printf(out, "Effect '%s' does not support --mix mode\n", av[0]); return FLUID_FAILED; } if(fluid_ladspa_effect_set_mix(fx, av[0], mix, gain) != FLUID_OK) { fluid_ostream_printf(out, "Failed to set --mix mode\n"); return FLUID_FAILED; } } return FLUID_OK; } /* * ladspa_link */ int fluid_handle_ladspa_link(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; if(ac != 3) { fluid_ostream_printf(out, "ladspa_link needs 3 arguments: " " \n"); return FLUID_FAILED; } CHECK_LADSPA_ENABLED(fx, out); CHECK_LADSPA_INACTIVE(fx, out); if(!fluid_ladspa_effect_port_exists(fx, av[0], av[1])) { fluid_ostream_printf(out, "Port '%s' not found on effect '%s'\n", av[1], av[0]); return FLUID_FAILED; } if(!fluid_ladspa_host_port_exists(fx, av[2]) && !fluid_ladspa_buffer_exists(fx, av[2])) { fluid_ostream_printf(out, "Host port or buffer '%s' not found.\n", av[2]); return FLUID_FAILED; } if(fluid_ladspa_effect_link(fx, av[0], av[1], av[2]) != FLUID_OK) { fluid_ostream_printf(out, "Failed to link port\n"); return FLUID_FAILED; } return FLUID_OK; } #endif /* LADSPA */ #if WITH_PROFILING /* * Locks profile command to prevent simultaneous changes by an other shell * (may be a server shell (tcp)). * * @return FLUID_OK if success , otherwise FLUID_FAILED. */ static int fluid_profile_lock_command(fluid_ostream_t out) { if(! fluid_atomic_int_compare_and_exchange(&fluid_profile_lock, 0, 1)) { fluid_ostream_printf(out, "profile command in use in another shell. Try later!\n"); return FLUID_FAILED; } return FLUID_OK; } /* * Unlocks profile command. */ static void fluid_profile_unlock_command(void) { fluid_atomic_int_set(&fluid_profile_lock, 0); } /* * command: profile * * Prints default parameters used by prof_start command. * * Notes:0, bank:0, prog:16, print:0, n_prof:1, dur:500 ms. * * @return FLUID_OK if success , otherwise FLUID_FAILED. */ int fluid_handle_profile(void *data, int ac, char **av, fluid_ostream_t out) { /* locks to prevent simultaneous changes by an other shell */ /* (may be a server shell (tcp)) */ if(fluid_profile_lock_command(out) == FLUID_FAILED) { return FLUID_FAILED; } /* prints default parameters */ fluid_ostream_printf(out, " Notes:%d, bank:%d, prog:%d, print:%d, n_prof:%d, dur:%d ms\n", fluid_profile_notes, fluid_profile_bank, fluid_profile_prog, fluid_profile_print, fluid_profile_n_prof, fluid_profile_dur); /* unlocks */ fluid_profile_unlock_command(); return FLUID_OK; } /* * command: prof_set_notes nbr [bank prog] * * Sets notes number generated by prof_start command. * * nbr: notes numbers (generated during command "prof_start"). * bank, prog: preset bank and program number (default value if not specified). * * @return FLUID_OK if success , otherwise FLUID_FAILED. */ int fluid_handle_prof_set_notes(void *data, int ac, char **av, fluid_ostream_t out) { unsigned short nbr; /* previous parameters */ unsigned char bank, prog; /* previous parameters */ int r; /* return */ /* locks to prevent simultaneous changes by an other shell */ if(fluid_profile_lock_command(out) == FLUID_FAILED) { return FLUID_FAILED; } /* checks parameters */ if(ac < 1) { fluid_ostream_printf(out, "profile_notes: too few arguments\n"); fluid_profile_unlock_command(); return FLUID_FAILED; } /* gets default parameters */ nbr = fluid_profile_notes, bank = fluid_profile_bank; prog = fluid_profile_prog; r = fluid_is_number(av[0]); if(r) { /* checks nbr */ nbr = atoi(av[0]); /* get nbr parameter */ if(ac >= 2) { /* [bank prog] are optional */ if(ac >= 3) { r = fluid_is_number(av[1]) && fluid_is_number(av[2]); if(r) { bank = atoi(av[1]); /* gets bank parameter */ prog = atoi(av[2]); /* gets prog parameter */ } } else { /* prog is needed */ fluid_ostream_printf(out, "profile_set_notes: too few arguments\n"); fluid_profile_unlock_command(); return FLUID_FAILED; } } } if(!r) { fluid_ostream_printf(out, "profile_set_notes: invalid argument\n"); fluid_profile_unlock_command(); return FLUID_FAILED; } /* Saves new parameters */ fluid_profile_notes = nbr; fluid_profile_bank = bank; fluid_profile_prog = prog; /* unlocks */ fluid_profile_unlock_command(); return FLUID_OK; } /* * command: prof_set_print mode * * The command sets the print mode. * * mode: result print mode(used by prof_start"). * 0: simple printing, >0: full printing * * @return FLUID_OK if success , otherwise FLUID_FAILED. */ int fluid_handle_prof_set_print(void *data, int ac, char **av, fluid_ostream_t out) { int r; /* locks to prevent simultaneous changes by an other shell */ if(fluid_profile_lock_command(out) == FLUID_FAILED) { return FLUID_FAILED; } /* checks parameters */ if(ac < 1) { fluid_ostream_printf(out, "profile_set_print: too few arguments\n"); fluid_profile_unlock_command(); return FLUID_FAILED; } /* gets parameters */ if(fluid_is_number(av[0])) { /* checks and gets mode */ fluid_profile_print = atoi(av[0]); /* gets and saves mode parameter */ r = FLUID_OK; } else { fluid_ostream_printf(out, "profile_set_print: invalid argument\n"); r = FLUID_FAILED; } /* unlocks */ fluid_profile_unlock_command(); return r; } /* * Generates simultaneous notes for precise profiling. * * @param synth, synthesizer instance. * @param notes, the number of notes to generate. * @param bank, prog, soundfont bank preset number used. * @param out, stream output device. * @return the number of voices generated. It can be lower than notes number * when the preset have instrument only on few key range. */ static unsigned short fluid_profile_send_notes(fluid_synth_t *synth, int notes, int bank, int prog, fluid_ostream_t out) { int n; /* number of notes generated */ int n_voices, n_actives = 0; /* Maximum voices, voices generated */ int n_chan, chan, key ; /* MIDI channels count and maximum polyphony */ n_chan = fluid_synth_count_midi_channels(synth); /* channels count */ n_voices = fluid_synth_get_polyphony(synth); /* maximum voices */ /* */ fluid_ostream_printf(out, "Generating %d notes, ", notes); for(n = 0, key = FLUID_PROFILE_LAST_KEY + 1, chan = -1; n < notes; n++, key++) { if(key > FLUID_PROFILE_LAST_KEY) { /* next channel */ chan++; if(chan >= n_chan) { break; /* stops generation */ } /* select preset */ fluid_synth_bank_select(synth, chan, bank); fluid_synth_program_change(synth, chan, prog); key = FLUID_PROFILE_FIRST_KEY; } fluid_synth_noteon(synth, chan, key, FLUID_PROFILE_DEFAULT_VEL); n_actives = fluid_synth_get_active_voice_count(synth); /* running voices */ if(n_actives >= n_voices) { fluid_ostream_printf(out, "max polyphony reached:%d, ", n_voices); break; /* stops notes generation */ } } fluid_ostream_printf(out, "generated voices:%d\n", n_actives); return n_actives; } /* * command: prof_start [n_prof [dur] ] * * Starts n_prof measures of dur duration(ms) each. * * n_prof number of measures (default value if not specified). * dur: measure duration (ms) (default value if not specified). * * The result of each measure is displayed. * * Note: The command ends when the last measure ends or when the user * cancels the command using key (cancellation using * is implemented using FLUID_PROFILE_CANCEL macro in fluid_sys.h). * * @return FLUID_OK if success , otherwise FLUID_FAILED. */ int fluid_handle_prof_start(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_synth_t *synth = handler->synth; unsigned short n_prof, n, dur; /* previous parameters */ unsigned int total_dur, rem_dur; /* total and remainder duration (ms) */ unsigned short notes; /* notes number to generate */ int n_actives = 0; /* actives voices */ float gain; /* current gain */ int r = 1; /* checking parameter result */ /* Locks to prevent simultaneous command by an other shell */ if(fluid_profile_lock_command(out) == FLUID_FAILED) { return FLUID_FAILED; } /* Gets previous parameters values */ n_prof = fluid_profile_n_prof; /* number of measuses */ dur = fluid_profile_dur; /* duration of one measure (ms) */ /* check parameters */ if(ac >= 1) { r = fluid_is_number(av[0]); if(r) { n_prof = atoi(av[0]); /* gets n_prof parameter */ if(ac >= 2) { r = fluid_is_number(av[1]); if(r) { dur = atoi(av[1]);/* gets dur parameter */ } } } } if(!r || n_prof < 1 || dur < 1) { fluid_ostream_printf(out, "profile_start: invalid argument\n"); fluid_profile_unlock_command(); return FLUID_FAILED; } /* Saves new parameters */ fluid_profile_n_prof = n_prof; fluid_profile_dur = dur; /* Saves current gain */ gain = fluid_synth_get_gain(synth); /* Generates notes if any */ notes = fluid_profile_notes; if(notes) { /* checks if the synth is playing */ /* Warn the user */ if(fluid_synth_get_active_voice_count(synth)) { fluid_ostream_printf(out, "Warning: can't generate notes, please stop any playing\n"); } else { float send_gain; /* sets low gain before sending notes */ fluid_synth_set_gain(synth, 0.01); /* sends notes */ n_actives = fluid_profile_send_notes(synth, notes, fluid_profile_bank, fluid_profile_prog, out); /* compensates gain to avoid a loud sound */ send_gain = 1.0 * pow(10, (n_actives * FLUID_PROFILE_VOICE_ATTEN) / 20); fluid_synth_set_gain(synth, send_gain); /* Before starting profiling immediately we wait to ensures that voices are currently synthesized by audio rendering API. This ensure that macro probes will register the expected number of actives voices. */ fluid_msleep(200); /* wait 200 ms */ } } /* Starts - waits - prints n_prof measures */ fluid_ostream_printf(out, "Number of measures(n_prof):%d, duration of one measure(dur):%dms\n", n_prof, dur); /* Clears any previous pending key */ fluid_profile_is_cancel_req(); total_dur = rem_dur = n_prof * dur; for(n = 0 ; n < n_prof; rem_dur -= dur, n++) { unsigned int end_ticks;/* ending position (in ticks) */ unsigned int tm, ts, rm, rs; int status; ts = total_dur / 1000; tm = ts / 60; ts = ts % 60; /* total minutes and seconds */ rs = rem_dur / 1000; rm = rs / 60; rs = rs % 60; /* remainder minutes and seconds */ /* Prints total and remainder duration */ #ifdef FLUID_PROFILE_CANCEL fluid_ostream_printf(out, "\nProfiling time(mm:ss): Total=%d:%d Remainder=%d:%d, press to cancel\n", tm, ts, rm, rs); #else fluid_ostream_printf(out, "\nProfiling time(mm:ss): Total=%d:%d Remainder=%d:%d\n", tm, ts, rm, rs); #endif /* converts duration(ms) in end position in ticks. */ end_ticks = fluid_atomic_int_get(&synth->ticks_since_start) + dur * synth->sample_rate / 1000; /* requests to start the measurement in audio rendering API */ fluid_profile_start_stop(end_ticks, n); /* waits while running */ do { /* passive waiting */ fluid_msleep(500); /* wait 500 ms */ status = fluid_profile_get_status(); } while(status == PROFILE_RUNNING); /* checks if data are ready */ if(status == PROFILE_READY) { /* profiling data are ready, prints profile data */ fluid_profiling_print_data(synth->sample_rate, out); } /* checks if the measurement has been cancelled */ else if(status == PROFILE_CANCELED || status == PROFILE_STOP) { fluid_ostream_printf(out, "Profiling cancelled.\n"); break; /* cancel the command */ } } /* Stops voices if any had been generated */ if(n_actives) { fluid_ostream_printf(out, "Stopping %d voices...", n_actives); fluid_synth_system_reset(synth); /* waits until all voices become inactives */ do { fluid_msleep(10); /* wait 10 ms */ n_actives = fluid_synth_get_active_voice_count(synth); } while(n_actives); fluid_ostream_printf(out, "voices stopped.\n"); } /* Restores initial gain */ fluid_synth_set_gain(synth, gain); /* Unlocks */ fluid_profile_unlock_command(); return FLUID_OK; } #endif /* WITH_PROFILING */ int fluid_is_number(char *a) { while(*a != 0) { if(((*a < '0') || (*a > '9')) && (*a != '-') && (*a != '+') && (*a != '.')) { return FALSE; } a++; } return TRUE; } char * fluid_expand_path(char *path, char *new_path, int len) { #if defined(WIN32) || defined(MACOS9) FLUID_SNPRINTF(new_path, len - 1, "%s", path); #else if((path[0] == '~') && (path[1] == '/')) { char *home = getenv("HOME"); if(home == NULL) { FLUID_SNPRINTF(new_path, len - 1, "%s", path); } else { FLUID_SNPRINTF(new_path, len - 1, "%s%s", home, &path[1]); } } else { FLUID_SNPRINTF(new_path, len - 1, "%s", path); } #endif new_path[len - 1] = 0; return new_path; } /* * Command */ fluid_cmd_t *fluid_cmd_copy(const 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; return copy; } void delete_fluid_cmd(fluid_cmd_t *cmd) { fluid_return_if_fail(cmd != NULL); FLUID_FREE(cmd->name); FLUID_FREE(cmd->topic); 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. * * See new_fluid_cmd_handler2() for more information. */ fluid_cmd_handler_t *new_fluid_cmd_handler(fluid_synth_t *synth, fluid_midi_router_t *router) { return new_fluid_cmd_handler2(fluid_synth_get_settings(synth), synth, router, NULL); } /** * Create a new command handler. * * @param settings If not NULL, all the settings related commands will be added to the new handler. The @p settings * object must be the same as the one you used for creating the @p synth and @p router. Otherwise the * behaviour is undefined. * @param synth If not NULL, all the default synthesizer commands will be added to the new handler. * @param router If not NULL, all the default midi_router commands will be added to the new handler. * @param player If not NULL, all the default midi file player commands will be added to the new handler. * @return New command handler, or NULL if alloc failed */ fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t *settings, fluid_synth_t *synth, fluid_midi_router_t *router, fluid_player_t *player) { unsigned int i; fluid_cmd_handler_t *handler; handler = FLUID_NEW(fluid_cmd_handler_t); if(handler == NULL) { return NULL; } FLUID_MEMSET(handler, 0, sizeof(*handler)); handler->commands = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, NULL, fluid_cmd_handler_destroy_hash_value); if(handler->commands == NULL) { FLUID_FREE(handler); return NULL; } handler->settings = settings; handler->synth = synth; handler->router = router; handler->player = player; for(i = 0; i < FLUID_N_ELEMENTS(fluid_commands); i++) { const fluid_cmd_t *cmd = &fluid_commands[i]; int is_settings_cmd = FLUID_STRCMP(cmd->topic, "settings") == 0; int is_router_cmd = FLUID_STRCMP(cmd->topic, "router") == 0; int is_player_cmd = FLUID_STRCMP(cmd->topic, "player") == 0; int is_synth_cmd = !(is_settings_cmd || is_router_cmd || is_player_cmd); int no_cmd = is_settings_cmd && settings == NULL; /* no settings command */ no_cmd = no_cmd || (is_router_cmd && router == NULL); /* no router command */ no_cmd = no_cmd || (is_player_cmd && player == NULL); /* no player command */ no_cmd = no_cmd || (is_synth_cmd && synth == NULL); /* no synth command */ if(no_cmd) { /* register a no-op command, this avoids an unknown command error later on */ fluid_cmd_t noop = *cmd; noop.handler = NULL; fluid_cmd_handler_register(handler, &noop); } else { fluid_cmd_handler_register(handler, cmd); } } return handler; } /** * Delete a command handler. * * @param handler Command handler to delete */ void delete_fluid_cmd_handler(fluid_cmd_handler_t *handler) { fluid_return_if_fail(handler != NULL); delete_fluid_hashtable(handler->commands); FLUID_FREE(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, const fluid_cmd_t *cmd) { fluid_cmd_t *copy = fluid_cmd_copy(cmd); fluid_hashtable_insert(handler->commands, 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->commands, cmd); } int fluid_cmd_handler_handle(void *data, int ac, char **av, fluid_ostream_t out) { FLUID_ENTRY_COMMAND(data); fluid_cmd_t *cmd; cmd = fluid_hashtable_lookup(handler->commands, av[0]); if(cmd) { if(cmd->handler) { return (*cmd->handler)(handler, ac - 1, av + 1, out); } else { /* no-op command */ return 1; } } else { fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]); return FLUID_FAILED; } } #ifdef NETWORK_SUPPORT struct _fluid_server_t { fluid_server_socket_t *socket; fluid_settings_t *settings; fluid_synth_t *synth; fluid_midi_router_t *router; fluid_player_t *player; fluid_list_t *clients; fluid_mutex_t mutex; }; static void fluid_server_close(fluid_server_t *server) { fluid_list_t *list; fluid_list_t *clients; fluid_client_t *client; fluid_return_if_fail(server != NULL); 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; client = new_fluid_client(server, server->settings, 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); } 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 fluid_thread_return_t fluid_client_run(void *data) { fluid_shell_t shell; fluid_client_t *client = (fluid_client_t *)data; 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); return FLUID_THREAD_RETURN_VALUE; } fluid_client_t * new_fluid_client(fluid_server_t *server, fluid_settings_t *settings, 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 = new_fluid_cmd_handler2(fluid_synth_get_settings(server->synth), server->synth, server->router, server->player); client->thread = new_fluid_thread("client", fluid_client_run, client, 0, FALSE); if(client->handler == NULL || client->thread == NULL) { goto error_recovery; } return client; error_recovery: FLUID_LOG(FLUID_ERR, "Out of memory"); delete_fluid_client(client); return NULL; } void fluid_client_quit(fluid_client_t *client) { fluid_socket_close(client->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) { fluid_return_if_fail(client != NULL); delete_fluid_cmd_handler(client->handler); fluid_socket_close(client->socket); delete_fluid_thread(client->thread); FLUID_FREE(client); } #endif /* NETWORK_SUPPORT */ /** * Create a new TCP/IP command shell server. * * See new_fluid_server2() for more information. */ fluid_server_t * new_fluid_server(fluid_settings_t *settings, fluid_synth_t *synth, fluid_midi_router_t *router) { return new_fluid_server2(settings, synth, router, NULL); } /** * Create a new TCP/IP command shell server. * * @param settings Settings instance to use for the shell * @param synth If not NULL, the synth instance for the command handler to be used by the client * @param router If not NULL, the midi_router instance for the command handler to be used by the client * @param player If not NULL, the player instance for the command handler to be used by the client * @return New shell server instance or NULL on error */ fluid_server_t * new_fluid_server2(fluid_settings_t *settings, fluid_synth_t *synth, fluid_midi_router_t *router, fluid_player_t *player) { #ifdef NETWORK_SUPPORT 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->synth = synth; server->router = router; server->player = player; 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; #else FLUID_LOG(FLUID_WARN, "Network support disabled on this platform."); return NULL; #endif } /** * Delete a TCP/IP shell server. * * @param server Shell server instance */ void delete_fluid_server(fluid_server_t *server) { #ifdef NETWORK_SUPPORT fluid_return_if_fail(server != NULL); fluid_server_close(server); FLUID_FREE(server); #endif } /** * 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) { #ifdef NETWORK_SUPPORT return fluid_server_socket_join(server->socket); #else return FLUID_FAILED; #endif } fluidsynth-2.2.5/src/bindings/fluid_cmd.h000066400000000000000000000221021417326347500204050ustar00rootroot00000000000000/* 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 "fluid_sys.h" void fluid_shell_settings(fluid_settings_t *settings); /** some help functions */ int fluid_is_number(char *a); char *fluid_expand_path(char *path, char *new_path, int len); /** the handlers for the command lines */ int fluid_handle_help(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_quit(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_noteon(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_noteoff(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_pitch_bend(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_pitch_bend_range(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_cc(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_prog(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_select(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_inst(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_channels(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_load(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_unload(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_reload(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_fonts(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_reverbpreset(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_reverbsetroomsize(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_reverbsetdamp(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_reverbsetwidth(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_reverbsetlevel(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_chorusnr(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_choruslevel(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_chorusspeed(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_chorusdepth(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_chorus(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_reverb(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_gain(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_interp(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_interpc(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_tuning(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_tune(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_settuning(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_resettuning(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_tunings(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_dumptuning(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_reset(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_source(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_echo(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_set(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_get(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_info(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_settings(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_router_clear(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_router_default(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_router_begin(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_router_end(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_router_chan(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_router_par1(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_router_par2(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_player_start(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_player_stop(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_player_continue(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_player_next_song(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_player_seek(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_player_loop(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_player_tempo_bpm(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_player_tempo_int(void *data, int ac, char **av, fluid_ostream_t out); #if WITH_PROFILING int fluid_handle_profile(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_prof_set_notes(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_prof_set_print(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_prof_start(void *data, int ac, char **av, fluid_ostream_t out); #endif int fluid_handle_basicchannels(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_resetbasicchannels(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_setbasicchannels(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_channelsmode(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_legatomode(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_setlegatomode(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_portamentomode(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_setportamentomode(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_breathmode(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_setbreathmode(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_sleep(void *data, int ac, char **av, fluid_ostream_t out); #ifdef LADSPA int fluid_handle_ladspa_effect(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_ladspa_link(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_ladspa_buffer(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_ladspa_set(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_ladspa_check(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_ladspa_start(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_ladspa_stop(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_ladspa_reset(void *data, int ac, char **av, fluid_ostream_t out); #endif /** * 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 */ char *help; /**< A help string */ } fluid_cmd_t; fluid_cmd_t *fluid_cmd_copy(const fluid_cmd_t *cmd); void delete_fluid_cmd(fluid_cmd_t *cmd); int fluid_cmd_handler_handle(void *data, int ac, char **av, fluid_ostream_t out); int fluid_cmd_handler_register(fluid_cmd_handler_t *handler, const fluid_cmd_t *cmd); int fluid_cmd_handler_unregister(fluid_cmd_handler_t *handler, const char *cmd); 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_socket_t sock); void delete_fluid_client(fluid_client_t *client); void fluid_client_quit(fluid_client_t *client); #endif /* _FLUID_CMD_H */ fluidsynth-2.2.5/src/bindings/fluid_filerenderer.c000066400000000000000000000366101417326347500223140ustar00rootroot00000000000000/* 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 "fluid_sys.h" #include "fluid_synth.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[] */ static const char *const format_names[] = { "s8", "s16", "s24", "s32", "u8", "float", "double" }; /* File audio format IDs. * !! Keep in sync with format_names[] */ static 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[] */ static const char *const endian_names[] = { "auto", "little", "big", "cpu" }; /* File endian byte order ids. * !! Keep in sync with endian_names[] */ static 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); #endif void fluid_file_renderer_settings(fluid_settings_t *settings) { #if LIBSNDFILE_SUPPORT SF_FORMAT_INFO finfo, cmpinfo; int major_count; int i, i2; unsigned int n; fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.wav", 0); fluid_settings_register_str(settings, "audio.file.type", "auto", 0); fluid_settings_register_str(settings, "audio.file.format", "s16", 0); fluid_settings_register_str(settings, "audio.file.endian", "auto", 0); 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(n = 0; n < FLUID_N_ELEMENTS(format_names); n++) { fluid_settings_add_option(settings, "audio.file.format", format_names[n]); } for(n = 0; n < FLUID_N_ELEMENTS(endian_names); n++) { fluid_settings_add_option(settings, "audio.file.endian", endian_names[n]); } #else fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.raw", 0); fluid_settings_register_str(settings, "audio.file.type", "raw", 0); fluid_settings_add_option(settings, "audio.file.type", "raw"); fluid_settings_register_str(settings, "audio.file.format", "s16", 0); fluid_settings_add_option(settings, "audio.file.format", "s16"); fluid_settings_register_str(settings, "audio.file.endian", "cpu", 0); 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 * * @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: * - \ref settings_audio_file_name : Output filename * - \ref settings_audio_file_type : File type, "auto" tries to determine type from filename * extension with fallback to "wav". * - \ref settings_audio_file_format : Audio format * - \ref settings_audio_file_endian : Endian byte order, "auto" for file type's default byte order * - \ref settings_audio_period-size : Size of audio blocks to process * - \ref settings_synth_sample-rate : Sample rate to use * * @since 1.1.0 */ 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 int audio_channels; 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); fluid_settings_getint(synth->settings, "synth.audio-channels", &audio_channels); 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 = FLUID_FOPEN(filename, "wb"); if(dev->file == NULL) { FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename); goto error_recovery; } #endif if(audio_channels != 1) { FLUID_LOG(FLUID_WARN, "The file-renderer currently only supports a single stereo channel. You have provided %d stereo channels. Audio may sound strange or incomplete.", audio_channels); } FLUID_FREE(filename); return dev; error_recovery: FLUID_FREE(filename); delete_fluid_file_renderer(dev); return NULL; } /** * Set vbr encoding quality (only available with libsndfile support) * @param dev File renderer object. * @param q The encoding quality, see libsndfile documentation of \c SFC_SET_VBR_ENCODING_QUALITY * @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 *dev, double q) { #if LIBSNDFILE_SUPPORT if(sf_command(dev->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) { fluid_return_if_fail(dev != NULL); #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 FLUID_FREE(dev->buf); FLUID_FREE(dev); } /** * 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 */ size_t res, nmemb = dev->buf_size; fluid_synth_write_s16(dev->synth, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2); res = fwrite(dev->buf, 1, nmemb, dev->file); if(res < nmemb) { 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; unsigned 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; i < FLUID_N_ELEMENTS(format_names); i++) { if(FLUID_STRCMP(format, format_names[i]) == 0) { break; } } if(i >= FLUID_N_ELEMENTS(format_names)) { 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; i < FLUID_N_ELEMENTS(endian_names); i++) { if(FLUID_STRCMP(endian, endian_names[i]) == 0) { break; } } if(i >= FLUID_N_ELEMENTS(endian_names)) { 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 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-2.2.5/src/bindings/fluid_ladspa.c000066400000000000000000001440671417326347500211200ustar00rootroot00000000000000/* 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 * * Complete rewrite: 10/2017 * Author: Marcus Weseloh */ #include "fluid_ladspa.h" #include "fluid_list.h" #if defined(LADSPA) || defined(__DOXYGEN__) #include #include typedef enum _fluid_ladspa_state_t { FLUID_LADSPA_INACTIVE = 0, FLUID_LADSPA_ACTIVE, FLUID_LADSPA_RUNNING } fluid_ladspa_state_t; typedef enum _fluid_ladspa_dir_t { FLUID_LADSPA_INPUT, FLUID_LADSPA_OUTPUT, } fluid_ladspa_dir_t; typedef enum _fluid_ladspa_node_type_t { FLUID_LADSPA_NODE_AUDIO = 1, FLUID_LADSPA_NODE_CONTROL = 2, FLUID_LADSPA_NODE_EFFECT = 4, FLUID_LADSPA_NODE_HOST = 8, FLUID_LADSPA_NODE_USER = 16, } fluid_ladspa_node_type_t; typedef struct _fluid_ladspa_node_t { char *name; fluid_ladspa_node_type_t type; /* The buffer that LADSPA effects read and/or write to. * If FluidSynth has been compiled WITH_FLOAT, then this * points to the host buffer, otherwise it's a separate * buffer. */ LADSPA_Data *effect_buffer; /* Only set for host nodes, points to the host buffer */ fluid_real_t *host_buffer; int num_inputs; int num_outputs; } fluid_ladspa_node_t; typedef struct _fluid_ladspa_effect_t { char *name; /* Pointer to the library that this effect was loaded from */ fluid_module_t *lib; /* The descriptor defines the plugin implementation, the * handle points to an instance of that plugin */ const LADSPA_Descriptor *desc; LADSPA_Handle *handle; int active; /* Decides if we should call the run (mix = 0) or run_adding (mix = 1) * plugin interface */ int mix; /* Used to keep track of the port connection state */ fluid_ladspa_node_t **port_nodes; } fluid_ladspa_effect_t; struct _fluid_ladspa_fx_t { unsigned long sample_rate; /* The buffer size for all audio buffers (in samples) */ int buffer_size; fluid_list_t *host_nodes; fluid_list_t *user_nodes; fluid_list_t *effects; fluid_rec_mutex_t api_mutex; fluid_atomic_int_t state; int pending_deactivation; fluid_cond_mutex_t *run_finished_mutex; fluid_cond_t *run_finished_cond; }; #define LADSPA_API_ENTER(_fx) (fluid_rec_mutex_lock((_fx)->api_mutex)) #define LADSPA_API_RETURN(_fx, _ret) \ fluid_rec_mutex_unlock((_fx)->api_mutex); \ return (_ret) static void clear_ladspa(fluid_ladspa_fx_t *fx); /* Node helpers */ static fluid_ladspa_node_t *new_fluid_ladspa_node(fluid_ladspa_fx_t *fx, const char *name, fluid_ladspa_node_type_t type, fluid_real_t *host_buffer); static void delete_fluid_ladspa_node(fluid_ladspa_node_t *node); static fluid_ladspa_node_t *get_node(fluid_ladspa_fx_t *fx, const char *name); /* Effect helpers */ static fluid_ladspa_effect_t * new_fluid_ladspa_effect(fluid_ladspa_fx_t *fx, const char *lib_name, const char *plugin_name); static void delete_fluid_ladspa_effect(fluid_ladspa_effect_t *effect); static void activate_effect(fluid_ladspa_effect_t *effect); static void deactivate_effect(fluid_ladspa_effect_t *effect); static fluid_ladspa_effect_t *get_effect(fluid_ladspa_fx_t *fx, const char *name); static int get_effect_port_idx(const fluid_ladspa_effect_t *effect, const char *name); static LADSPA_Data get_default_port_value(fluid_ladspa_effect_t *effect, unsigned int port_idx, int sample_rate); static void connect_node_to_port(fluid_ladspa_node_t *node, fluid_ladspa_dir_t dir, fluid_ladspa_effect_t *effect, int port_idx); static int create_control_port_nodes(fluid_ladspa_fx_t *fx, fluid_ladspa_effect_t *effect); /* Plugin helpers */ static const LADSPA_Descriptor *get_plugin_descriptor(fluid_module_t *lib, const char *name); /* Sanity checks */ static int check_all_ports_connected(fluid_ladspa_effect_t *effect, const char **name); static int check_no_inplace_broken(fluid_ladspa_effect_t *effect, const char **name1, const char **name2); static int check_host_output_used(fluid_ladspa_fx_t *fx); static int check_all_audio_nodes_connected(fluid_ladspa_fx_t *fx, const char **name); #ifndef WITH_FLOAT static FLUID_INLINE void copy_host_to_effect_buffers(fluid_ladspa_fx_t *fx, int num_samples); static FLUID_INLINE void copy_effect_to_host_buffers(fluid_ladspa_fx_t *fx, int num_samples); #endif /** * Creates a new LADSPA effects unit. * * @param sample_rate sample_rate for the LADSPA effects * @param buffer_size size of all audio buffers * @return pointer to the new LADSPA effects unit */ fluid_ladspa_fx_t *new_fluid_ladspa_fx(fluid_real_t sample_rate, int buffer_size) { fluid_ladspa_fx_t *fx; fx = FLUID_NEW(fluid_ladspa_fx_t); if(fx == NULL) { return NULL; } FLUID_MEMSET(fx, 0, sizeof(fluid_ladspa_fx_t)); /* Setup recursive mutex to protect access to public API */ fluid_rec_mutex_init(fx->api_mutex); fluid_atomic_int_set(&fx->state, FLUID_LADSPA_INACTIVE); fx->buffer_size = buffer_size; /* add 0.5 to minimize overall casting error */ fx->sample_rate = (unsigned long)(sample_rate + 0.5); /* Setup mutex and cond used to signal that fluid_ladspa_run has finished */ fx->run_finished_mutex = new_fluid_cond_mutex(); if(fx->run_finished_mutex == NULL) { goto error_recovery; } fx->run_finished_cond = new_fluid_cond(); if(fx->run_finished_cond == NULL) { goto error_recovery; } return fx; error_recovery: delete_fluid_ladspa_fx(fx); return NULL; } /** * Destroy and free a LADSPA effects unit previously created * with new_fluid_ladspa_fx(). * * @param fx LADSPA effects instance * * @note This function does not check the engine state for * possible users, so make sure that you only call this * if you are sure nobody is using the engine anymore (especially * that nobody calls fluid_ladspa_run) * */ void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx) { fluid_list_t *list; fluid_ladspa_node_t *node; fluid_return_if_fail(fx != NULL); clear_ladspa(fx); /* Delete the remaining host nodes */ for(list = fx->host_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); delete_fluid_ladspa_node(node); } delete_fluid_list(fx->host_nodes); if(fx->run_finished_cond != NULL) { delete_fluid_cond(fx->run_finished_cond); } if(fx->run_finished_mutex != NULL) { delete_fluid_cond_mutex(fx->run_finished_mutex); } fluid_rec_mutex_destroy(fx->api_mutex); FLUID_FREE(fx); } /** * Add host buffers to the LADSPA engine. * * @param fx LADSPA fx instance * @param prefix common name prefix for the created nodes * @param num_buffers number of of buffers buffer array * @param buffers array of sample buffers * @param buf_stride number of samples contained in one buffer * @return FLUID_OK on success, otherwise FLUID_FAILED * * @note The size of the buffers pointed to by the buffers array must be * at least as large as the buffer size given to new_fluid_ladspa_fx. * */ int fluid_ladspa_add_host_ports(fluid_ladspa_fx_t *fx, const char *prefix, int num_buffers, fluid_real_t buffers[], int buf_stride) { int i; char name[99]; fluid_ladspa_node_t *node; LADSPA_API_ENTER(fx); if(fluid_ladspa_is_active(fx)) { LADSPA_API_RETURN(fx, FLUID_FAILED); } /* Create nodes for all channels */ for(i = 0; i < num_buffers; i++) { /* If there is more than one buffer, then append a 1-based index to each node name */ if(num_buffers > 1) { FLUID_SNPRINTF(name, sizeof(name), "%s%d", prefix, (i + 1)); } else { FLUID_STRNCPY(name, prefix, sizeof(name)); } node = new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_AUDIO | FLUID_LADSPA_NODE_HOST, &buffers[i * buf_stride]); if (node == NULL) { LADSPA_API_RETURN(fx, FLUID_FAILED); } fx->host_nodes = fluid_list_append(fx->host_nodes, node); } LADSPA_API_RETURN(fx, FLUID_OK); } /** * Set the sample rate of the LADSPA effects. * * @param fx LADSPA fx instance * @param sample_rate new sample rate * @return FLUID_OK on success, otherwise FLUID_FAILED * * Resets the LADSPA effects if the sample rate is different from the * previous sample rate. * */ int fluid_ladspa_set_sample_rate(fluid_ladspa_fx_t *fx, fluid_real_t sample_rate) { unsigned long new_sample_rate; LADSPA_API_ENTER(fx); /* Add 0.5 to minimize rounding errors */ new_sample_rate = (unsigned long)(sample_rate + 0.5); if(fx->sample_rate == new_sample_rate) { LADSPA_API_RETURN(fx, FLUID_OK); } if(fluid_ladspa_is_active(fx)) { if(fluid_ladspa_reset(fx) != FLUID_OK) { FLUID_LOG(FLUID_ERR, "Failed to reset LADSPA, unable to change sample rate"); LADSPA_API_RETURN(fx, FLUID_FAILED); } } fx->sample_rate = new_sample_rate; LADSPA_API_RETURN(fx, FLUID_OK); } /** * Check if the LADSPA engine is currently used to render audio * * @param fx LADSPA fx instance * @return TRUE if LADSPA effects engine is active, otherwise FALSE * * If an engine is active, the only allowed user actions are deactivation or * setting values of control ports on effects. Anything else, especially * adding or removing effects, buffers or ports, is only allowed in deactivated * state. */ int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx) { int is_active; fluid_return_val_if_fail(fx != NULL, FALSE); LADSPA_API_ENTER(fx); is_active = (fluid_atomic_int_get(&fx->state) != FLUID_LADSPA_INACTIVE); LADSPA_API_RETURN(fx, is_active); } /** * Activate the LADSPA fx instance and each configured effect. * * @param fx LADSPA fx instance * @return FLUID_OK if activation succeeded or already active, otherwise FLUID_FAILED */ int fluid_ladspa_activate(fluid_ladspa_fx_t *fx) { fluid_list_t *list; fluid_ladspa_effect_t *effect; fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); LADSPA_API_ENTER(fx); if(fluid_ladspa_is_active(fx)) { LADSPA_API_RETURN(fx, FLUID_FAILED); } if(fluid_ladspa_check(fx, NULL, 0) != FLUID_OK) { FLUID_LOG(FLUID_ERR, "LADSPA check failed, unable to activate effects"); LADSPA_API_RETURN(fx, FLUID_FAILED); } for(list = fx->effects; list; list = fluid_list_next(list)) { effect = (fluid_ladspa_effect_t *) fluid_list_get(list); activate_effect(effect); } if(!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_INACTIVE, FLUID_LADSPA_ACTIVE)) { for(list = fx->effects; list; list = fluid_list_next(list)) { effect = (fluid_ladspa_effect_t *) fluid_list_get(list); deactivate_effect(effect); } LADSPA_API_RETURN(fx, FLUID_FAILED); } LADSPA_API_RETURN(fx, FLUID_OK); } /** * Deactivate a LADSPA fx instance and all configured effects. * * @param fx LADSPA fx instance * @return FLUID_OK if deactivation succeeded, otherwise FLUID_FAILED * * @note This function may sleep. */ int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx) { fluid_list_t *list; fluid_ladspa_effect_t *effect; fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); LADSPA_API_ENTER(fx); /* If we are already inactive, then simply return success */ if(fluid_atomic_int_get(&fx->state) == FLUID_LADSPA_INACTIVE) { LADSPA_API_RETURN(fx, FLUID_OK); } /* Notify fluid_ladspa_run that we would like to deactivate and that it should * send us a signal when its done if it is currently running */ fx->pending_deactivation = 1; fluid_cond_mutex_lock(fx->run_finished_mutex); while(!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_ACTIVE, FLUID_LADSPA_INACTIVE)) { fluid_cond_wait(fx->run_finished_cond, fx->run_finished_mutex); } fluid_cond_mutex_unlock(fx->run_finished_mutex); /* Now that we're inactive, deactivate all effects and return success */ for(list = fx->effects; list; list = fluid_list_next(list)) { effect = (fluid_ladspa_effect_t *) fluid_list_get(list); deactivate_effect(effect); } fx->pending_deactivation = 0; LADSPA_API_RETURN(fx, FLUID_OK); } /** * Reset the LADSPA effects engine. * * @param fx LADSPA fx instance * @return FLUID_OK on success, otherwise FLUID_FAILED * * Deactivate LADSPA if currently active, remove all * effects, remove all user nodes and unload all libraries. */ int fluid_ladspa_reset(fluid_ladspa_fx_t *fx) { fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); LADSPA_API_ENTER(fx); if(fluid_ladspa_is_active(fx)) { if(fluid_ladspa_deactivate(fx) != FLUID_OK) { LADSPA_API_RETURN(fx, FLUID_FAILED); } } clear_ladspa(fx); LADSPA_API_RETURN(fx, FLUID_OK); } /** * Processes audio data via the LADSPA effects unit. * * @param fx LADSPA effects instance * @param block_count number of blocks to render * @param block_size number of samples in a block * * FluidSynth calls this function during main output mixing, just after * the internal reverb and chorus effects have been processed. * * It copies audio data from the supplied buffers, runs all effects and copies the * resulting audio back into the same buffers. */ void fluid_ladspa_run(fluid_ladspa_fx_t *fx, int block_count, int block_size) { int num_samples; fluid_list_t *list; fluid_ladspa_node_t *node; fluid_ladspa_effect_t *effect; /* Somebody wants to deactivate the engine, so let's give them a chance to do that. * And check that there is at least one effect, to avoid the overhead of the * atomic compare and exchange on an unconfigured LADSPA engine. */ if(fx->pending_deactivation || fx->effects == NULL) { return; } /* Inform the engine that we are now running pluings, and bail out if it's not active */ if(!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_ACTIVE, FLUID_LADSPA_RUNNING)) { return; } num_samples = block_count * block_size; #ifndef WITH_FLOAT copy_host_to_effect_buffers(fx, num_samples); #endif for(list = fx->user_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); FLUID_MEMSET(node->effect_buffer, 0, fx->buffer_size * sizeof(LADSPA_Data)); } /* Run each effect in the order that they were added */ for(list = fx->effects; list; list = fluid_list_next(list)) { effect = (fluid_ladspa_effect_t *) fluid_list_get(list); if(effect->mix) { effect->desc->run_adding(effect->handle, num_samples); } else { effect->desc->run(effect->handle, num_samples); } } #ifndef WITH_FLOAT copy_effect_to_host_buffers(fx, num_samples); #endif if(!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_RUNNING, FLUID_LADSPA_ACTIVE)) { FLUID_LOG(FLUID_ERR, "Unable to reset LADSPA running state!"); } /* If deactivation was requested while in running state, notify that we've finished now * and deactivation can proceed */ if(fx->pending_deactivation) { fluid_cond_mutex_lock(fx->run_finished_mutex); fluid_cond_broadcast(fx->run_finished_cond); fluid_cond_mutex_unlock(fx->run_finished_mutex); } } /** * Check if the effect plugin supports the run_adding and set_run_adding_gain * interfaces necessary for output mixing * * @param fx LADSPA fx * @param name the name of the effect * @return TRUE if mix mode is supported, otherwise FALSE */ int fluid_ladspa_effect_can_mix(fluid_ladspa_fx_t *fx, const char *name) { int can_mix; fluid_ladspa_effect_t *effect; fluid_return_val_if_fail(fx != NULL, FALSE); fluid_return_val_if_fail(name != NULL, FALSE); LADSPA_API_ENTER(fx); effect = get_effect(fx, name); if(effect == NULL) { LADSPA_API_RETURN(fx, FALSE); } can_mix = (effect->desc->run_adding != NULL && effect->desc->set_run_adding_gain != NULL); LADSPA_API_RETURN(fx, can_mix); } /** * Set if the effect should replace everything in the output buffers (mix = 0, default) * or add to the buffers with a fixed gain (mix = 1). * * @param fx LADSPA fx instance * @param name the name of the effect * @param mix (boolean) if to enable mix mode * @param gain the gain to apply to the effect output before adding to output. * @return FLUID_OK on success, otherwise FLUID_FAILED */ int fluid_ladspa_effect_set_mix(fluid_ladspa_fx_t *fx, const char *name, int mix, float gain) { fluid_ladspa_effect_t *effect; fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); LADSPA_API_ENTER(fx); effect = get_effect(fx, name); if(effect == NULL) { LADSPA_API_RETURN(fx, FLUID_FAILED); } if(mix) { if(!fluid_ladspa_effect_can_mix(fx, name)) { FLUID_LOG(FLUID_ERR, "Effect '%s' does not support mix mode", name); LADSPA_API_RETURN(fx, FLUID_FAILED); } effect->desc->set_run_adding_gain(effect->handle, gain); } effect->mix = mix; LADSPA_API_RETURN(fx, FLUID_OK); } static void clear_ladspa(fluid_ladspa_fx_t *fx) { fluid_list_t *list; fluid_ladspa_node_t *node; fluid_ladspa_effect_t *effect; /* Deactivate and free all effects */ for(list = fx->effects; list; list = fluid_list_next(list)) { effect = (fluid_ladspa_effect_t *) fluid_list_get(list); deactivate_effect(effect); delete_fluid_ladspa_effect(effect); } delete_fluid_list(fx->effects); fx->effects = NULL; /* Delete all user nodes */ for(list = fx->user_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); delete_fluid_ladspa_node(node); } delete_fluid_list(fx->user_nodes); fx->user_nodes = NULL; /* Mark all host nodes as unconnected */ for(list = fx->host_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); node->num_inputs = 0; node->num_outputs = 0; } } /** * Check if a named host port exists * * @param fx LADSPA fx instance * @param name the port name * @return TRUE if the host port exists, otherwise FALSE */ int fluid_ladspa_host_port_exists(fluid_ladspa_fx_t *fx, const char *name) { fluid_ladspa_node_t *node; fluid_return_val_if_fail(fx != NULL, FALSE); fluid_return_val_if_fail(name != NULL, FALSE); LADSPA_API_ENTER(fx); node = get_node(fx, name); if(node == NULL) { LADSPA_API_RETURN(fx, FALSE); } if(node->type & FLUID_LADSPA_NODE_HOST) { LADSPA_API_RETURN(fx, TRUE); } LADSPA_API_RETURN(fx, FALSE); } /** * Check if a named user buffer exists * * @param fx LADSPA fx instance * @param name the buffer name * @return TRUE if the buffer exists, otherwise FALSE */ int fluid_ladspa_buffer_exists(fluid_ladspa_fx_t *fx, const char *name) { int exists; fluid_ladspa_node_t *node; fluid_return_val_if_fail(fx != NULL, FALSE); fluid_return_val_if_fail(name != NULL, FALSE); LADSPA_API_ENTER(fx); node = get_node(fx, name); if(node == NULL) { LADSPA_API_RETURN(fx, FALSE); } exists = ((node->type & FLUID_LADSPA_NODE_AUDIO) && (node->type & FLUID_LADSPA_NODE_USER)); LADSPA_API_RETURN(fx, exists); } /** * Check if the named port exists on an effect * * @param fx LADSPA fx instance * @param effect_name name of the effect * @param port_name the port name * @return TRUE if port was found, otherwise FALSE */ int fluid_ladspa_effect_port_exists(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name) { fluid_ladspa_effect_t *effect; int port_exists; fluid_return_val_if_fail(fx != NULL, FALSE); fluid_return_val_if_fail(effect_name != NULL, FALSE); fluid_return_val_if_fail(port_name != NULL, FALSE); LADSPA_API_ENTER(fx); effect = get_effect(fx, effect_name); if(effect == NULL) { LADSPA_API_RETURN(fx, FALSE); } port_exists = (get_effect_port_idx(effect, port_name) != -1); LADSPA_API_RETURN(fx, port_exists); } /** * Create and add a new audio buffer. * * @param fx LADSPA effects instance * @param name name of the new buffer * @return FLUID_OK on success, FLUID_FAILED on error */ int fluid_ladspa_add_buffer(fluid_ladspa_fx_t *fx, const char *name) { fluid_ladspa_node_t *node; fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); LADSPA_API_ENTER(fx); if(fluid_ladspa_is_active(fx)) { LADSPA_API_RETURN(fx, FLUID_FAILED); } node = new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_AUDIO | FLUID_LADSPA_NODE_USER, NULL); if(node == NULL) { LADSPA_API_RETURN(fx, FLUID_FAILED); } fx->user_nodes = fluid_list_append(fx->user_nodes, node); LADSPA_API_RETURN(fx, FLUID_OK); } /** * Set the value of an effect control port * * @param fx LADSPA fx instance * @param effect_name name of the effect * @param port_name name of the port * @param val floating point value * @return FLUID_OK on success, FLUID_FAILED on error */ int fluid_ladspa_effect_set_control(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name, float val) { fluid_ladspa_node_t *node; fluid_ladspa_effect_t *effect; int port_idx; fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); fluid_return_val_if_fail(effect_name != NULL, FLUID_FAILED); fluid_return_val_if_fail(port_name != NULL, FLUID_FAILED); LADSPA_API_ENTER(fx); effect = get_effect(fx, effect_name); if(effect == NULL) { LADSPA_API_RETURN(fx, FLUID_FAILED); } port_idx = get_effect_port_idx(effect, port_name); if(port_idx < 0) { LADSPA_API_RETURN(fx, FLUID_FAILED); } if(!LADSPA_IS_PORT_CONTROL(effect->desc->PortDescriptors[port_idx])) { LADSPA_API_RETURN(fx, FLUID_FAILED); } node = effect->port_nodes[port_idx]; if(node == NULL) { LADSPA_API_RETURN(fx, FLUID_FAILED); } node->effect_buffer[0] = val; LADSPA_API_RETURN(fx, FLUID_OK); } /** * Create an instance of a LADSPA plugin as an effect * * @param fx LADSPA effects instance * @param effect_name name of the effect * @param lib_name filename of ladspa plugin library * @param plugin_name optional, plugin name if there is more than one plugin in the library * @return FLUID_OK on success, otherwise FLUID_FAILED */ int fluid_ladspa_add_effect(fluid_ladspa_fx_t *fx, const char *effect_name, const char *lib_name, const char *plugin_name) { fluid_ladspa_effect_t *effect; fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); fluid_return_val_if_fail(effect_name != NULL, FLUID_FAILED); fluid_return_val_if_fail(lib_name != NULL, FLUID_FAILED); LADSPA_API_ENTER(fx); if(fluid_ladspa_is_active(fx)) { LADSPA_API_RETURN(fx, FLUID_FAILED); } effect = new_fluid_ladspa_effect(fx, lib_name, plugin_name); if(effect == NULL) { LADSPA_API_RETURN(fx, FLUID_FAILED); } effect->name = FLUID_STRDUP(effect_name); if(effect->name == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); delete_fluid_ladspa_effect(effect); LADSPA_API_RETURN(fx, FLUID_FAILED); } if(create_control_port_nodes(fx, effect) != FLUID_OK) { delete_fluid_ladspa_effect(effect); LADSPA_API_RETURN(fx, FLUID_FAILED); } fx->effects = fluid_list_append(fx->effects, effect); LADSPA_API_RETURN(fx, FLUID_OK); } /** * Connect an effect audio port to a host port or buffer * * @param fx LADSPA effects instance * @param effect_name name of the effect * @param port_name the audio port name to connect to (case-insensitive prefix match) * @param name the host port or buffer to connect to (case-insensitive) * @return FLUID_OK on success, otherwise FLUID_FAILED * * @note There is no corresponding disconnect function. If the connections need to be changed, * clear everything with fluid_ladspa_reset and start again from scratch. */ int fluid_ladspa_effect_link(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name, const char *name) { fluid_ladspa_effect_t *effect; fluid_ladspa_node_t *node; int port_idx; int port_flags; int dir; fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); fluid_return_val_if_fail(effect_name != NULL, FLUID_FAILED); fluid_return_val_if_fail(port_name != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); LADSPA_API_ENTER(fx); if(fluid_ladspa_is_active(fx)) { LADSPA_API_RETURN(fx, FLUID_FAILED); } effect = get_effect(fx, effect_name); if(effect == NULL) { FLUID_LOG(FLUID_ERR, "Effect '%s' not found", effect_name); LADSPA_API_RETURN(fx, FLUID_FAILED); } port_idx = get_effect_port_idx(effect, port_name); if(port_idx < 0) { FLUID_LOG(FLUID_ERR, "Port '%s' not found on effect '%s'", port_name, effect_name); LADSPA_API_RETURN(fx, FLUID_FAILED); } port_flags = effect->desc->PortDescriptors[port_idx]; if(!LADSPA_IS_PORT_AUDIO(port_flags)) { FLUID_LOG(FLUID_ERR, "Only audio effect ports can be linked to buffers or host ports"); LADSPA_API_RETURN(fx, FLUID_FAILED); } node = get_node(fx, name); if(node == NULL) { FLUID_LOG(FLUID_ERR, "Link target '%s' not found", name); LADSPA_API_RETURN(fx, FLUID_FAILED); } if(!(node->type & FLUID_LADSPA_NODE_AUDIO)) { FLUID_LOG(FLUID_ERR, "Link target '%s' needs to be an audio port or buffer", name); LADSPA_API_RETURN(fx, FLUID_FAILED); } if(LADSPA_IS_PORT_INPUT(port_flags)) { dir = FLUID_LADSPA_INPUT; } else { dir = FLUID_LADSPA_OUTPUT; } connect_node_to_port(node, dir, effect, port_idx); LADSPA_API_RETURN(fx, FLUID_OK); } /** * Do a sanity check for problems in the LADSPA setup * * @param fx LADSPA fx instance * @param err externally provided buffer for the error message. Set to NULL if you don't need an error message. * @param err_size size of the err buffer * @return FLUID_OK if setup is valid, otherwise FLUID_FAILED (err will contain the error message) * * If the check detects problems and the err pointer is not NULL, a description of the first found * problem is written to that string (up to err_size - 1 characters). */ int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size) { const char *str; const char *str2; fluid_list_t *list; fluid_ladspa_effect_t *effect; fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); fluid_return_val_if_fail(err == NULL || err_size >= 0, FLUID_FAILED); LADSPA_API_ENTER(fx); /* Check that there is at least one effect */ if(fx->effects == NULL) { FLUID_SNPRINTF(err, err_size, "No effects configured\n"); LADSPA_API_RETURN(fx, FLUID_FAILED); } for(list = fx->effects; list; list = fluid_list_next(list)) { effect = (fluid_ladspa_effect_t *) fluid_list_get(list); if(check_all_ports_connected(effect, &str) == FLUID_FAILED) { if(err != NULL) { FLUID_SNPRINTF(err, err_size, "Port '%s' on effect '%s' is not connected\n", str, effect->name); } LADSPA_API_RETURN(fx, FLUID_FAILED); } if(check_no_inplace_broken(effect, &str, &str2) == FLUID_FAILED) { if(err != NULL) { FLUID_SNPRINTF(err, err_size, "effect '%s' is in-place broken, '%s' and '%s' are not allowed " "to connect to the same node\n", effect->name, str, str2); } LADSPA_API_RETURN(fx, FLUID_FAILED); } } if(check_host_output_used(fx) == FLUID_FAILED) { if(err != NULL) { FLUID_SNPRINTF(err, err_size, "No effect outputs to one the host nodes\n"); } LADSPA_API_RETURN(fx, FLUID_FAILED); } if(check_all_audio_nodes_connected(fx, &str) == FLUID_FAILED) { if(err != NULL) { FLUID_SNPRINTF(err, err_size, "Audio node '%s' is not fully connected\n", str); } LADSPA_API_RETURN(fx, FLUID_FAILED); } LADSPA_API_RETURN(fx, FLUID_OK); } static void activate_effect(fluid_ladspa_effect_t *effect) { if(!effect->active) { effect->active = TRUE; if(effect->desc->activate != NULL) { effect->desc->activate(effect->handle); } } } static void deactivate_effect(fluid_ladspa_effect_t *effect) { if(effect->active) { effect->active = FALSE; if(effect->desc->deactivate != NULL) { effect->desc->deactivate(effect->handle); } } } /** * Return a LADSPA node by name. Nodes are searched case insensitive. * * @param fx LADSPA fx instance * @param name the node name string * @return a fluid_ladspa_node_t pointer or NULL if not found */ static fluid_ladspa_node_t *get_node(fluid_ladspa_fx_t *fx, const char *name) { fluid_list_t *list; fluid_ladspa_node_t *node; for(list = fx->host_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); if(FLUID_STRCASECMP(node->name, name) == 0) { return node; } } for(list = fx->user_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); if(FLUID_STRCASECMP(node->name, name) == 0) { return node; } } return NULL; } /** * Return a LADSPA effect port index by name, using a 'fuzzy match'. * * @param effect pointer to fluid_ladspa_effect_t * @param name the port name * @return index of the port in the effect or -1 on error * * Returns the first effect port which matches the name. If no exact match is * found, returns the port that starts with the specified name, but only if there is * only one such match. */ static int get_effect_port_idx(const fluid_ladspa_effect_t *effect, const char *name) { unsigned int i; int port = -1; for(i = 0; i < effect->desc->PortCount; i++) { if(FLUID_STRNCASECMP(effect->desc->PortNames[i], name, FLUID_STRLEN(name)) == 0) { /* exact match, return immediately */ if(FLUID_STRLEN(effect->desc->PortNames[i]) == FLUID_STRLEN(name)) { return i; } /* more than one prefix match should be treated as not found */ if(port != -1) { return -1; } port = i; } } return port; } /** * Return a LADSPA descriptor structure for a plugin in a LADSPA library. * * @param lib pointer to the dynamically loaded library * @param name name (LADSPA Label) of the plugin * @return pointer to LADSPA_Descriptor, NULL on error or if not found * * The name is optional if the library contains only one plugin. */ static const LADSPA_Descriptor *get_plugin_descriptor(fluid_module_t *lib, const char *name) { const LADSPA_Descriptor *desc; const LADSPA_Descriptor *last_desc = NULL; LADSPA_Descriptor_Function ladspa_descriptor; int i; if(!fluid_module_symbol(lib, "ladspa_descriptor", (void *)&ladspa_descriptor)) { FLUID_LOG(FLUID_ERR, "Unable to find ladspa_descriptor in '%s'. " "Is this really a LADSPA plugin?", fluid_module_name(lib)); return NULL; } for(i = 0; /* endless */; i++) { desc = ladspa_descriptor(i); if(desc == NULL) { break; } if(name != NULL && FLUID_STRCMP(desc->Label, name) == 0) { return desc; } last_desc = desc; } if(name == NULL) { if(i == 1) { return last_desc; } FLUID_LOG(FLUID_ERR, "Library contains more than one plugin, please specify " "the plugin label"); } return NULL; } /** * Instantiate a LADSPA plugin from a library and set up the associated * control structures needed by the LADSPA fx engine. * * @param fx LADSPA fx instance * @param lib_name file path of the plugin library * @param plugin_name (optional) string name of the plugin (the LADSPA Label) * @return pointer to the new ladspa_plugin_t structure or NULL on error * * If the library contains only one plugin, then the name is optional. * Plugins are identified by their "Label" in the plugin descriptor structure. */ static fluid_ladspa_effect_t * new_fluid_ladspa_effect(fluid_ladspa_fx_t *fx, const char *lib_name, const char *plugin_name) { fluid_ladspa_effect_t *effect; effect = FLUID_NEW(fluid_ladspa_effect_t); if(effect == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(effect, 0, sizeof(fluid_ladspa_effect_t)); effect->lib = fluid_module_open(lib_name); if(effect->lib == NULL) { FLUID_LOG(FLUID_ERR, "Unable to load LADSPA library '%s': %s", lib_name, fluid_module_error()); delete_fluid_ladspa_effect(effect); return NULL; } effect->desc = get_plugin_descriptor(effect->lib, plugin_name); if(effect->desc == NULL) { delete_fluid_ladspa_effect(effect); return NULL; } effect->handle = effect->desc->instantiate(effect->desc, fx->sample_rate); if(effect->handle == NULL) { delete_fluid_ladspa_effect(effect); FLUID_LOG(FLUID_ERR, "Unable to instantiate plugin '%s' from '%s'", plugin_name, lib_name); return NULL; } effect->port_nodes = FLUID_ARRAY(fluid_ladspa_node_t *, effect->desc->PortCount); if(effect->port_nodes == NULL) { delete_fluid_ladspa_effect(effect); FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(effect->port_nodes, 0, effect->desc->PortCount * sizeof(fluid_ladspa_node_t *)); return effect; } static void delete_fluid_ladspa_effect(fluid_ladspa_effect_t *effect) { unsigned int i; fluid_ladspa_node_t *node; fluid_return_if_fail(effect != NULL); /* Control nodes are created automatically when the effect is instantiated and * are private to this effect, so we can safely remove them here. Nodes connected * to audio ports might be connected to other effects as well, so we simply remove * any pointers to them from the effect. */ if(effect->desc != NULL) { for(i = 0; i < effect->desc->PortCount; i++) { node = (fluid_ladspa_node_t *) effect->port_nodes[i]; if(node && node->type & FLUID_LADSPA_NODE_CONTROL) { delete_fluid_ladspa_node(node); } } } FLUID_FREE(effect->port_nodes); if(effect->handle != NULL && effect->desc != NULL && effect->desc->cleanup != NULL) { effect->desc->cleanup(effect->handle); } if(effect->lib != NULL) { fluid_module_close(effect->lib); } FLUID_FREE(effect->name); FLUID_FREE(effect); } static fluid_ladspa_node_t *new_fluid_ladspa_node(fluid_ladspa_fx_t *fx, const char *name, fluid_ladspa_node_type_t type, fluid_real_t *host_buffer) { int buffer_size; fluid_ladspa_node_t *node; /* For named nodes, make sure that the name is unique */ if(FLUID_STRLEN(name) > 0 && get_node(fx, name) != NULL) { return NULL; } node = FLUID_NEW(fluid_ladspa_node_t); if(node == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(node, 0, sizeof(fluid_ladspa_node_t)); node->name = FLUID_STRDUP(name); if(node->name == NULL) { delete_fluid_ladspa_node(node); FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } node->type = type; node->host_buffer = host_buffer; /* host audio nodes need a host buffer set */ if((type & FLUID_LADSPA_NODE_AUDIO) && (type & FLUID_LADSPA_NODE_HOST)) { if(node->host_buffer == NULL) { delete_fluid_ladspa_node(node); return NULL; } #ifdef WITH_FLOAT /* If the host uses the same floating-point width as LADSPA, then effects * can work in-place on the host buffer. Otherwise well need a separate * buffer for type conversion. */ node->effect_buffer = node->host_buffer; #endif } if(node->effect_buffer == NULL) { /* Control nodes only store a single floating-point value */ buffer_size = (type & FLUID_LADSPA_NODE_CONTROL) ? 1 : fx->buffer_size; node->effect_buffer = FLUID_ARRAY(LADSPA_Data, buffer_size); if(node->effect_buffer == NULL) { delete_fluid_ladspa_node(node); FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(node->effect_buffer, 0, buffer_size * sizeof(LADSPA_Data)); } return node; } static void delete_fluid_ladspa_node(fluid_ladspa_node_t *node) { fluid_return_if_fail(node != NULL); /* If effect_buffer the same as host_buffer, then the effect_buffer has been * provided externally, so don't free */ if((node->effect_buffer != NULL) && ((void *)node->effect_buffer != (void *)node->host_buffer)) { FLUID_FREE(node->effect_buffer); } FLUID_FREE(node->name); FLUID_FREE(node); } /** * Retrieve a ladspa_effect_t instance by it's name. * * @param fx LADSPA effects instance * @param name effect name * @return pointer to effect or NULL if not found */ static fluid_ladspa_effect_t *get_effect(fluid_ladspa_fx_t *fx, const char *name) { fluid_list_t *list; fluid_ladspa_effect_t *effect; LADSPA_API_ENTER(fx); for(list = fx->effects; list; list = fluid_list_next(list)) { effect = (fluid_ladspa_effect_t *) fluid_list_get(list); if(FLUID_STRNCASECMP(effect->name, name, FLUID_STRLEN(name)) == 0) { LADSPA_API_RETURN(fx, effect); } } LADSPA_API_RETURN(fx, NULL); } /** * Set the passed in float pointer to the default value of a effect port, as specified * by the LADSPA port hints. If no default hints are found or the port is not a control * node, it returns 0.0f; * * The sample rate is needed because some LADSPA port default hints are expressed as a * fraction of the current sample rate. * * @param effect pointer to effect instance * @param port_idx index of the port in the effect * @param sample_rate the current sample rate of the LADSPA fx * @return default port value or 0.0f */ static LADSPA_Data get_default_port_value(fluid_ladspa_effect_t *effect, unsigned int port_idx, int sample_rate) { const LADSPA_PortRangeHint *hint; LADSPA_PortRangeHintDescriptor flags; LADSPA_Data value = 0.0; float low_factor = 0.0; float high_factor = 0.0; if(port_idx >= effect->desc->PortCount) { return value; } hint = &effect->desc->PortRangeHints[port_idx]; flags = hint->HintDescriptor; if(!LADSPA_IS_HINT_HAS_DEFAULT(flags)) { return value; } if(LADSPA_IS_HINT_DEFAULT_0(flags)) { value = 0.0; } else if(LADSPA_IS_HINT_DEFAULT_1(flags)) { value = 1.0; } else if(LADSPA_IS_HINT_DEFAULT_100(flags)) { value = 100.0; } else if(LADSPA_IS_HINT_DEFAULT_440(flags)) { value = 440.0; } /* defaults based on lower or upper bounds must consider HINT_SAMPLE_RATE */ else { if(LADSPA_IS_HINT_DEFAULT_MINIMUM(flags)) { low_factor = 1.0; high_factor = 0.0; } else if(LADSPA_IS_HINT_DEFAULT_LOW(flags)) { low_factor = 0.75; high_factor = 0.25; } else if(LADSPA_IS_HINT_DEFAULT_MIDDLE(flags)) { low_factor = 0.5; high_factor = 0.5; } else if(LADSPA_IS_HINT_DEFAULT_HIGH(flags)) { low_factor = 0.25; high_factor = 0.75; } else if(LADSPA_IS_HINT_DEFAULT_MAXIMUM(flags)) { low_factor = 0.0; high_factor = 1.0; } if(LADSPA_IS_HINT_LOGARITHMIC(flags) && low_factor > 0 && high_factor > 0) { value = exp(log(hint->LowerBound) * low_factor + log(hint->UpperBound) * high_factor); } else { value = (hint->LowerBound * low_factor + hint->UpperBound * high_factor); } if(LADSPA_IS_HINT_SAMPLE_RATE(flags)) { value *= sample_rate; } } if(LADSPA_IS_HINT_INTEGER(flags)) { /* LADSPA doesn't specify which rounding method to use, so lets keep it simple... */ value = floor(value + 0.5); } return value; } /** * Create a control node for each control port on the passed in effect. The value of the * node is taken from port hint defaults, if available. This gets run automatically after * an effect has been added. * * @param fx LADSPA fx instance * @param effect effect instance * @return FLUID_OK on success, otherwise FLUID_FAILED */ static int create_control_port_nodes(fluid_ladspa_fx_t *fx, fluid_ladspa_effect_t *effect) { unsigned int i; fluid_ladspa_node_t *node; fluid_ladspa_dir_t dir; int port_flags; for(i = 0; i < effect->desc->PortCount; i++) { port_flags = effect->desc->PortDescriptors[i]; if(!LADSPA_IS_PORT_CONTROL(port_flags)) { continue; } node = new_fluid_ladspa_node(fx, "", FLUID_LADSPA_NODE_EFFECT | FLUID_LADSPA_NODE_CONTROL, NULL); if(node == NULL) { return FLUID_FAILED; } node->effect_buffer[0] = get_default_port_value(effect, i, fx->sample_rate); dir = (LADSPA_IS_PORT_INPUT(port_flags)) ? FLUID_LADSPA_INPUT : FLUID_LADSPA_OUTPUT; connect_node_to_port(node, dir, effect, i); } return FLUID_OK; } static void connect_node_to_port(fluid_ladspa_node_t *node, fluid_ladspa_dir_t dir, fluid_ladspa_effect_t *effect, int port_idx) { effect->desc->connect_port(effect->handle, port_idx, node->effect_buffer); effect->port_nodes[port_idx] = node; /* Mark node as connected in the respective direction */ if(dir == FLUID_LADSPA_INPUT) { node->num_outputs++; } else { node->num_inputs++; } } /** * Check that all ports on the effect are connected to a node. * * @param effect LADSPA effect instance * @param name if check fails, points to the name of first failed port * @return FLUID_OK on successful check, otherwise FLUID_FAILED */ static int check_all_ports_connected(fluid_ladspa_effect_t *effect, const char **name) { unsigned int i; for(i = 0; i < effect->desc->PortCount; i++) { if(effect->port_nodes[i] == NULL) { *name = effect->desc->PortNames[i]; return FLUID_FAILED; } } return FLUID_OK; } /** * In-place broken plugins can't cope with input and output audio ports connected * to the same buffer. Check for this condition in the effect. * * @param effect effect instance * @param name1 if check fails, points to the first port name * @param name2 if check fails, points to the second port name * @return FLUID_OK on successful check, otherwise FLUID_FAILED */ static int check_no_inplace_broken(fluid_ladspa_effect_t *effect, const char **name1, const char **name2) { unsigned int i, k; LADSPA_PortDescriptor flags1, flags2; if(!LADSPA_IS_INPLACE_BROKEN(effect->desc->Properties)) { return FLUID_OK; } for(i = 0; i < effect->desc->PortCount; i++) { flags1 = effect->desc->PortDescriptors[i]; for(k = 0; k < effect->desc->PortCount; k++) { flags2 = effect->desc->PortDescriptors[k]; if(i != k && effect->port_nodes[i]->effect_buffer == effect->port_nodes[k]->effect_buffer && (flags1 & 0x3) != (flags2 & 0x3) /* first two bits encode direction */ && LADSPA_IS_PORT_AUDIO(flags1) && LADSPA_IS_PORT_AUDIO(flags2)) { *name1 = effect->desc->PortNames[i]; *name2 = effect->desc->PortNames[k]; return FLUID_FAILED; } } } return FLUID_OK; } /** * Check that at least one host node is used by an effect * * @param fx LADSPA fx instance * @return FLUID_OK on successful check, otherwise FLUID_FAILED */ static int check_host_output_used(fluid_ladspa_fx_t *fx) { fluid_list_t *list; fluid_ladspa_node_t *node; for(list = fx->host_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); if(node->num_inputs) { return FLUID_OK; } } return FLUID_FAILED; } /** * Check that all user audio nodes have an input and an output * * @param fx LADSPA fx instance * @param name if check fails, points to the name of first failed node * @return FLUID_OK on successful check, otherwise FLUID_FAILED */ static int check_all_audio_nodes_connected(fluid_ladspa_fx_t *fx, const char **name) { fluid_list_t *list; fluid_ladspa_node_t *node; for(list = fx->user_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); if(node->num_inputs == 0 || node->num_outputs == 0) { *name = node->name; return FLUID_FAILED; } } return FLUID_OK; } #ifndef WITH_FLOAT /** * Copy and type convert host buffers to effect buffers. Used only if host and LADSPA * use different float types. */ static FLUID_INLINE void copy_host_to_effect_buffers(fluid_ladspa_fx_t *fx, int num_samples) { int i; fluid_list_t *list; fluid_ladspa_node_t *node; for(list = fx->host_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); /* Only copy host nodes that have at least one output or output, i.e. * that are connected to at least one effect port. */ if(node->num_inputs > 0 || node->num_outputs > 0) { for(i = 0; i < num_samples; i++) { node->effect_buffer[i] = (LADSPA_Data)node->host_buffer[i]; } } } } /** * Copy and type convert effect buffers to host buffers. Used only if host and LADSPA * use different float types. */ static FLUID_INLINE void copy_effect_to_host_buffers(fluid_ladspa_fx_t *fx, int num_samples) { int i; fluid_list_t *list; fluid_ladspa_node_t *node; for(list = fx->host_nodes; list; list = fluid_list_next(list)) { node = (fluid_ladspa_node_t *) fluid_list_get(list); /* Only copy effect nodes that have at least one input, i.e. that are connected to * at least one effect output */ if(node->num_inputs > 0) { for(i = 0; i < num_samples; i++) { node->host_buffer[i] = (fluid_real_t)node->effect_buffer[i]; } } } } #endif /* WITH_FLOAT */ #else /* ifdef LADSPA */ /* Dummy functions to use if LADSPA is not compiled in, to keep the * FluidSynth library ABI stable */ int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx) { return FALSE; } int fluid_ladspa_activate(fluid_ladspa_fx_t *fx) { return FLUID_FAILED; } int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx) { return FLUID_FAILED; } int fluid_ladspa_reset(fluid_ladspa_fx_t *fx) { return FLUID_FAILED; } int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size) { return FLUID_FAILED; } int fluid_ladspa_host_port_exists(fluid_ladspa_fx_t *fx, const char *name) { return FALSE; } int fluid_ladspa_add_buffer(fluid_ladspa_fx_t *fx, const char *name) { return FLUID_FAILED; } int fluid_ladspa_buffer_exists(fluid_ladspa_fx_t *fx, const char *name) { return FALSE; } int fluid_ladspa_add_effect(fluid_ladspa_fx_t *fx, const char *effect_name, const char *lib_name, const char *plugin_name) { return FLUID_FAILED; } int fluid_ladspa_effect_can_mix(fluid_ladspa_fx_t *fx, const char *name) { return FALSE; } int fluid_ladspa_effect_set_mix(fluid_ladspa_fx_t *fx, const char *name, int mix, float gain) { return FLUID_FAILED; } int fluid_ladspa_effect_port_exists(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name) { return FALSE; } int fluid_ladspa_effect_set_control(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name, float val) { return FLUID_FAILED; } int fluid_ladspa_effect_link(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name, const char *name) { return FLUID_FAILED; } #endif /* ifdef LADSPA */ fluidsynth-2.2.5/src/bindings/fluid_ladspa.h000066400000000000000000000026021417326347500211110ustar00rootroot00000000000000/* 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_LADSPA_H #define _FLUID_LADSPA_H #include "fluid_sys.h" fluid_ladspa_fx_t *new_fluid_ladspa_fx(fluid_real_t sample_rate, int buffer_size); void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx); int fluid_ladspa_set_sample_rate(fluid_ladspa_fx_t *fx, fluid_real_t sample_rate); void fluid_ladspa_run(fluid_ladspa_fx_t *fx, int block_count, int block_size); int fluid_ladspa_add_host_ports(fluid_ladspa_fx_t *fx, const char *prefix, int num_buffers, fluid_real_t buffers[], int buf_stride); #endif /* _FLUID_LADSPA_H */ fluidsynth-2.2.5/src/bindings/fluid_lash.c000066400000000000000000000110051417326347500205640ustar00rootroot00000000000000/* 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" #ifdef HAVE_LASH 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. */ lash_client_t *fluid_lash_client; static pthread_t fluid_lash_thread; 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(FLUID_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; } #endif /* #if HAVE_LASH #else */ fluidsynth-2.2.5/src/bindings/fluid_lash.h000066400000000000000000000026511417326347500206000ustar00rootroot00000000000000/* 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_LASH_H #define _FLUID_LASH_H #include "config.h" #ifdef HAVE_LASH #include "fluid_synth.h" #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 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) */ #endif /* _FLUID_LASH_H */ fluidsynth-2.2.5/src/bindings/fluid_rtkit.c000066400000000000000000000237061417326347500210050ustar00rootroot00000000000000/*-*- 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 "fluid_sys.h" #ifdef DBUS_SUPPORT #include "fluid_rtkit.h" #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #if defined(__FreeBSD__) || defined(__DragonFly__) #include #endif static pid_t _gettid(void) { #if defined(__FreeBSD__) || defined(__DragonFly__) return pthread_getthreadid_np(); #else return (pid_t) syscall(SYS_gettid); #endif } static int translate_error(const char *name) { if(FLUID_STRCMP(name, DBUS_ERROR_NO_MEMORY) == 0) { return -ENOMEM; } if(FLUID_STRCMP(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 || FLUID_STRCMP(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0) { return -ENOENT; } if(FLUID_STRCMP(name, DBUS_ERROR_ACCESS_DENIED) == 0 || FLUID_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-2.2.5/src/bindings/fluid_rtkit.h000066400000000000000000000040011417326347500207750ustar00rootroot00000000000000/*-*- 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-2.2.5/src/config.cmake000066400000000000000000000210031417326347500167570ustar00rootroot00000000000000#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_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@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IO_H @HAVE_IO_H@ /* whether or not we are supporting lash */ #cmakedefine HAVE_LASH @HAVE_LASH@ /* Define if systemd support is enabled */ #cmakedefine SYSTEMD_SUPPORT @SYSTEMD_SUPPORT@ /* 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_LINUX_SOUNDCARD_H @HAVE_LINUX_SOUNDCARD_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_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 if compiling the mixer with multi-thread support */ #cmakedefine ENABLE_MIXER_THREADS @ENABLE_MIXER_THREADS@ /* Define if compiling with openMP to enable parallel audio rendering */ #cmakedefine HAVE_OPENMP @HAVE_OPENMP@ /* 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_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@ /* Define to enable network support */ #cmakedefine NETWORK_SUPPORT @NETWORK_SUPPORT@ /* Defined when fluidsynth is build in an automated environment, where no MSVC++ Runtime Debug Assertion dialogs should pop up */ #cmakedefine NO_GUI @NO_GUI@ /* libinstpatch for DLS and GIG */ #cmakedefine LIBINSTPATCH_SUPPORT @LIBINSTPATCH_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 enable OSS driver */ #cmakedefine OSS_SUPPORT @OSS_SUPPORT@ /* Define to enable OPENSLES driver */ #cmakedefine OPENSLES_SUPPORT @OPENSLES_SUPPORT@ /* Define to enable Oboe driver */ #cmakedefine OBOE_SUPPORT @OBOE_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 enable DirectSound driver */ #cmakedefine DSOUND_SUPPORT @DSOUND_SUPPORT@ /* Define to enable Windows WASAPI driver */ #cmakedefine WASAPI_SUPPORT @WASAPI_SUPPORT@ /* Define to enable Windows WaveOut driver */ #cmakedefine WAVEOUT_SUPPORT @WAVEOUT_SUPPORT@ /* Define to enable Windows MIDI driver */ #cmakedefine WINMIDI_SUPPORT @WINMIDI_SUPPORT@ /* Define to enable SDL2 audio driver */ #cmakedefine SDL2_SUPPORT @SDL2_SUPPORT@ /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS @STDC_HEADERS@ /* Soundfont to load for unit testing */ #cmakedefine TEST_SOUNDFONT "@TEST_SOUNDFONT@" /* Soundfont to load for UTF-8 unit testing */ #cmakedefine TEST_SOUNDFONT_UTF8_1 "@TEST_SOUNDFONT_UTF8_1@" #cmakedefine TEST_SOUNDFONT_UTF8_2 "@TEST_SOUNDFONT_UTF8_2@" #cmakedefine TEST_MIDI_UTF8 "@TEST_MIDI_UTF8@" /* SF3 Soundfont to load for unit testing */ #cmakedefine TEST_SOUNDFONT_SF3 "@TEST_SOUNDFONT_SF3@" /* Define to enable SIGFPE assertions */ #cmakedefine TRAP_ON_FPE @TRAP_ON_FPE@ /* 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 /* Define to 1 if you have the sinf() function. */ #cmakedefine HAVE_SINF @HAVE_SINF@ /* Define to 1 if you have the cosf() function. */ #cmakedefine HAVE_COSF @HAVE_COSF@ /* Define to 1 if you have the fabsf() function. */ #cmakedefine HAVE_FABSF @HAVE_FABSF@ /* Define to 1 if you have the powf() function. */ #cmakedefine HAVE_POWF @HAVE_POWF@ /* Define to 1 if you have the sqrtf() function. */ #cmakedefine HAVE_SQRTF @HAVE_SQRTF@ /* Define to 1 if you have the logf() function. */ #cmakedefine HAVE_LOGF @HAVE_LOGF@ /* Define to 1 if you have the socklen_t type. */ #cmakedefine HAVE_SOCKLEN_T @HAVE_SOCKLEN_T@ #endif /* CONFIG_H */ fluidsynth-2.2.5/src/drivers/000077500000000000000000000000001417326347500161725ustar00rootroot00000000000000fluidsynth-2.2.5/src/drivers/fluid_adriver.c000066400000000000000000000337041417326347500211640ustar00rootroot00000000000000/* 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_sys.h" #include "fluid_settings.h" /* * fluid_adriver_definition_t */ struct _fluid_audriver_definition_t { const 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); void (*free)(fluid_audio_driver_t *driver); void (*settings)(fluid_settings_t *settings); }; /* Available audio drivers, listed in order of preference */ static const fluid_audriver_definition_t fluid_audio_drivers[] = { #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 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 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 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 OBOE_SUPPORT { "oboe", new_fluid_oboe_audio_driver, NULL, delete_fluid_oboe_audio_driver, fluid_oboe_audio_driver_settings }, #endif #if OPENSLES_SUPPORT { "opensles", new_fluid_opensles_audio_driver, NULL, delete_fluid_opensles_audio_driver, fluid_opensles_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, new_fluid_dsound_audio_driver2, delete_fluid_dsound_audio_driver, fluid_dsound_audio_driver_settings }, #endif #if WASAPI_SUPPORT { "wasapi", new_fluid_wasapi_audio_driver, new_fluid_wasapi_audio_driver2, delete_fluid_wasapi_audio_driver, fluid_wasapi_audio_driver_settings }, #endif #if WAVEOUT_SUPPORT { "waveout", new_fluid_waveout_audio_driver, new_fluid_waveout_audio_driver2, delete_fluid_waveout_audio_driver, fluid_waveout_audio_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 PORTAUDIO_SUPPORT { "portaudio", new_fluid_portaudio_driver, NULL, delete_fluid_portaudio_driver, fluid_portaudio_driver_settings }, #endif #if DART_SUPPORT { "dart", new_fluid_dart_audio_driver, NULL, delete_fluid_dart_audio_driver, fluid_dart_audio_driver_settings }, #endif #if SDL2_SUPPORT { "sdl2", new_fluid_sdl2_audio_driver, NULL, delete_fluid_sdl2_audio_driver, fluid_sdl2_audio_driver_settings }, #endif #if AUFILE_SUPPORT { "file", new_fluid_file_audio_driver, NULL, delete_fluid_file_audio_driver, NULL }, #endif /* NULL terminator to avoid zero size array if no driver available */ { NULL, NULL, NULL, NULL, NULL } }; #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_t 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; const char *def_name = NULL; fluid_settings_register_str(settings, "audio.sample-format", "16bits", 0); fluid_settings_add_option(settings, "audio.sample-format", "16bits"); fluid_settings_add_option(settings, "audio.sample-format", "float"); #if defined(WIN32) fluid_settings_register_int(settings, "audio.period-size", 512, 64, 8192, 0); fluid_settings_register_int(settings, "audio.periods", 8, 2, 64, 0); #elif defined(MACOS9) fluid_settings_register_int(settings, "audio.period-size", 64, 64, 8192, 0); fluid_settings_register_int(settings, "audio.periods", 8, 2, 64, 0); #else fluid_settings_register_int(settings, "audio.period-size", 64, 64, 8192, 0); fluid_settings_register_int(settings, "audio.periods", 16, 2, 64, 0); #endif fluid_settings_register_int(settings, "audio.realtime-prio", FLUID_DEFAULT_AUDIO_RT_PRIO, 0, 99, 0); fluid_settings_register_str(settings, "audio.driver", "", 0); for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++) { /* Select the default driver */ if (def_name == NULL) { def_name = fluid_audio_drivers[i].name; } /* Add the driver to the list of options */ fluid_settings_add_option(settings, "audio.driver", fluid_audio_drivers[i].name); if(fluid_audio_drivers[i].settings != NULL && IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i)) { fluid_audio_drivers[i].settings(settings); } } /* Set the default driver, if any */ if(def_name != NULL) { fluid_settings_setstr(settings, "audio.driver", def_name); } } static const fluid_audriver_definition_t * 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) - 1; 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]; } } fluid_settings_dupstr(settings, "audio.driver", &name); /* ++ alloc name */ FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver '%s'.", name ? name : "NULL"); allnames = fluid_settings_option_concat(settings, "audio.driver", NULL); if(allnames != NULL) { if(allnames[0] != '\0') { FLUID_LOG(FLUID_INFO, "This build of fluidsynth supports the following audio drivers: %s", allnames); } else { FLUID_LOG(FLUID_INFO, "This build of fluidsynth doesn't support any audio drivers."); } FLUID_FREE(allnames); } FLUID_FREE(name); 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 or NULL on error * * Creates a new audio driver for a given \p synth instance with a defined set * of configuration \p settings. The \p settings instance must be the same that * you have passed to new_fluid_synth() when creating the \p synth instance. * Otherwise the behaviour is undefined. * * @note As soon as an audio driver is created, the \p synth starts rendering audio. * This means that all necessary sound-setup should be completed after this point, * thus of all object types in use (synth, midi player, sequencer, etc.) the audio * driver should always be the last one to be created and the first one to be deleted! * Also refer to the order of object creation in the code examples. */ 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->define = def; } 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 \p func * @return The new audio driver instance or NULL on error * * 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 * \p func to render the audio into the buffers. If \p func uses a fluid_synth_t \p synth, * the \p settings instance must be the same that you have passed to new_fluid_synth() * when creating the \p synth instance. Otherwise the behaviour is undefined. * * @note Not as efficient as new_fluid_audio_driver(). * * @note As soon as an audio driver is created, a new thread is spawned starting to make * callbacks to \p func. * This means that all necessary sound-setup should be completed after this point, * thus of all object types in use (synth, midi player, sequencer, etc.) the audio * driver should always be the last one to be created and the first one to be deleted! * Also refer to the order of object creation in the code examples. */ 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->define = def; } } 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) { fluid_return_if_fail(driver != NULL); driver->define->free(driver); } /** * Registers audio drivers to use * * @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. * * 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 initial creation). Else the behaviour is undefined. * Furtermore any attempt of using audio drivers that have not been registered is undefined behaviour! * * @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_t 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 enable it if found */ for(j = 0; j < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; j++) { if(FLUID_STRCMP(adrivers[i], fluid_audio_drivers[j].name) == 0) { ENABLE_AUDIO_DRIVER(disable_mask, j); break; } } if(j >= FLUID_N_ELEMENTS(fluid_audio_drivers) - 1) { /* requested driver not found, failure */ return FLUID_FAILED; } } /* Update list of activated drivers */ FLUID_MEMCPY(fluid_adriver_disable_mask, disable_mask, sizeof(disable_mask)); return FLUID_OK; } fluidsynth-2.2.5/src/drivers/fluid_adriver.h000066400000000000000000000144201417326347500211630ustar00rootroot00000000000000/* 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 "fluidsynth_priv.h" /* * fluid_audio_driver_t */ typedef struct _fluid_audriver_definition_t fluid_audriver_definition_t; struct _fluid_audio_driver_t { const fluid_audriver_definition_t *define; }; void fluid_audio_driver_settings(fluid_settings_t *settings); /* Defined in fluid_filerenderer.c */ void fluid_file_renderer_settings(fluid_settings_t *settings); #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); void 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); void 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); void delete_fluid_oss_audio_driver(fluid_audio_driver_t *p); void fluid_oss_audio_driver_settings(fluid_settings_t *settings); #endif #if OPENSLES_SUPPORT fluid_audio_driver_t* new_fluid_opensles_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); void delete_fluid_opensles_audio_driver(fluid_audio_driver_t* p); void fluid_opensles_audio_driver_settings(fluid_settings_t* settings); #endif #if OBOE_SUPPORT fluid_audio_driver_t* new_fluid_oboe_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); void delete_fluid_oboe_audio_driver(fluid_audio_driver_t* p); void fluid_oboe_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); void 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); fluid_audio_driver_t *new_fluid_dsound_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data); void delete_fluid_dsound_audio_driver(fluid_audio_driver_t *p); void fluid_dsound_audio_driver_settings(fluid_settings_t *settings); #endif #if WASAPI_SUPPORT fluid_audio_driver_t *new_fluid_wasapi_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth); fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data); void delete_fluid_wasapi_audio_driver(fluid_audio_driver_t *p); void fluid_wasapi_audio_driver_settings(fluid_settings_t *settings); #endif #if WAVEOUT_SUPPORT fluid_audio_driver_t *new_fluid_waveout_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth); fluid_audio_driver_t *new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data); void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *p); void fluid_waveout_audio_driver_settings(fluid_settings_t *settings); #endif #if PORTAUDIO_SUPPORT void fluid_portaudio_driver_settings(fluid_settings_t *settings); fluid_audio_driver_t *new_fluid_portaudio_driver(fluid_settings_t *settings, fluid_synth_t *synth); void 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); void delete_fluid_jack_audio_driver(fluid_audio_driver_t *p); void fluid_jack_audio_driver_settings(fluid_settings_t *settings); int fluid_jack_obtain_synth(fluid_settings_t *settings, fluid_synth_t **synth); #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); void 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); void delete_fluid_dart_audio_driver(fluid_audio_driver_t *p); void fluid_dart_audio_driver_settings(fluid_settings_t *settings); #endif #if SDL2_SUPPORT fluid_audio_driver_t *new_fluid_sdl2_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth); void delete_fluid_sdl2_audio_driver(fluid_audio_driver_t *p); void fluid_sdl2_audio_driver_settings(fluid_settings_t *settings); #endif #if AUFILE_SUPPORT fluid_audio_driver_t *new_fluid_file_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth); void delete_fluid_file_audio_driver(fluid_audio_driver_t *p); #endif #endif /* _FLUID_AUDRIVER_H */ fluidsynth-2.2.5/src/drivers/fluid_alsa.c000066400000000000000000001116641417326347500204520ustar00rootroot00000000000000/* 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 "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; static fluid_thread_return_t fluid_alsa_audio_run_float(void *d); static fluid_thread_return_t fluid_alsa_audio_run_s16(void *d); typedef struct { char *name; snd_pcm_format_t format; snd_pcm_access_t access; fluid_thread_func_t run; } fluid_alsa_formats_t; static const 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; fluid_atomic_int_t should_quit; unsigned char buffer[BUFFER_LENGTH]; fluid_midi_parser_t *parser; } fluid_alsa_rawmidi_driver_t; static fluid_thread_return_t 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; fluid_atomic_int_t should_quit; int port_count; int autoconn_inputs; snd_seq_addr_t autoconn_dest; } fluid_alsa_seq_driver_t; static fluid_thread_return_t 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); } 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) { FLUID_LOG(FLUID_DBG, "snd_pcm_hw_params_set_access() failed with audio format '%s'", fluid_alsa_formats[i].name); continue; } if(snd_pcm_hw_params_set_format(dev->pcm, hwparams, fluid_alsa_formats[i].format) < 0) { FLUID_LOG(FLUID_DBG, "snd_pcm_hw_params_set_format() failed with audio format '%s'", fluid_alsa_formats[i].name); 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 %u, got %u 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 an audio format supported by alsa"); 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) { FLUID_LOG(FLUID_ERR, "Failed to start the alsa-audio 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; } void delete_fluid_alsa_audio_driver(fluid_audio_driver_t *p) { fluid_alsa_audio_driver_t *dev = (fluid_alsa_audio_driver_t *) p; fluid_return_if_fail(dev != NULL); dev->cont = 0; if(dev->thread) { fluid_thread_join(dev->thread); delete_fluid_thread(dev->thread); } if(dev->pcm) { snd_pcm_close(dev->pcm); } FLUID_FREE(dev); } /* 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 doesn't 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 fluid_thread_return_t 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) { FLUID_MEMSET(left, 0, buffer_size * sizeof(*left)); FLUID_MEMSET(right, 0, buffer_size * sizeof(*right)); 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); return FLUID_THREAD_RETURN_VALUE; } static fluid_thread_return_t 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) { FLUID_MEMSET(left, 0, buffer_size * sizeof(*left)); FLUID_MEMSET(right, 0, buffer_size * sizeof(*right)); (*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); return FLUID_THREAD_RETURN_VALUE; } /************************************************************** * * Alsa MIDI driver * */ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) { fluid_settings_register_str(settings, "midi.alsa.device", "default", 0); } /* * 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: %s", snd_strerror(err)); 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); fluid_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 */ void delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t *p) { fluid_alsa_rawmidi_driver_t *dev = (fluid_alsa_rawmidi_driver_t *) p; fluid_return_if_fail(dev != NULL); /* cancel the thread and wait for it before cleaning up */ fluid_atomic_int_set(&dev->should_quit, 1); if(dev->thread) { fluid_thread_join(dev->thread); delete_fluid_thread(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); } /* * fluid_alsa_midi_run */ fluid_thread_return_t 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(!fluid_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"); fluid_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); } } } } return FLUID_THREAD_RETURN_VALUE; } /************************************************************** * * Alsa sequencer * */ void fluid_alsa_seq_driver_settings(fluid_settings_t *settings) { fluid_settings_register_str(settings, "midi.alsa_seq.device", "default", 0); fluid_settings_register_str(settings, "midi.alsa_seq.id", "pid", 0); } static char *fluid_alsa_seq_full_id(char *id, char *buf, int len) { if(id != NULL) { if(FLUID_STRCMP(id, "pid") == 0) { FLUID_SNPRINTF(buf, len, "FLUID Synth (%d)", getpid()); } else { FLUID_SNPRINTF(buf, len, "FLUID Synth (%s)", id); } } else { FLUID_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) { FLUID_SNPRINTF(buf, len, "Synth input port (%d:%d)", getpid(), port); } else { FLUID_SNPRINTF(buf, len, "Synth input port (%s:%d)", id, port); } } else { FLUID_SNPRINTF(buf, len, "Synth input port"); } return buf; } // Connect a single port_info to autoconnect_dest if it has right type/capabilities static void fluid_alsa_seq_autoconnect_port_info(fluid_alsa_seq_driver_t *dev, snd_seq_port_info_t *pinfo) { snd_seq_port_subscribe_t *subs; snd_seq_t *seq = dev->seq_handle; const unsigned int needed_type = SND_SEQ_PORT_TYPE_MIDI_GENERIC; const unsigned int needed_cap = SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; const snd_seq_addr_t *sender = snd_seq_port_info_get_addr(pinfo); const char *pname = snd_seq_port_info_get_name(pinfo); if((snd_seq_port_info_get_type(pinfo) & needed_type) != needed_type) { return; } if((snd_seq_port_info_get_capability(pinfo) & needed_cap) != needed_cap) { return; } snd_seq_port_subscribe_alloca(&subs); snd_seq_port_subscribe_set_sender(subs, sender); snd_seq_port_subscribe_set_dest(subs, &dev->autoconn_dest); if(snd_seq_get_port_subscription(seq, subs) == 0) { FLUID_LOG(FLUID_WARN, "Connection %s is already subscribed", pname); return; } if(snd_seq_subscribe_port(seq, subs) < 0) { FLUID_LOG(FLUID_ERR, "Connection of %s failed (%s)", pname, snd_strerror(errno)); return; } FLUID_LOG(FLUID_INFO, "Connection of %s succeeded", pname); } // Autoconnect a single client port (by id) to autoconnect_dest if it has right type/capabilities static void fluid_alsa_seq_autoconnect_port(fluid_alsa_seq_driver_t *dev, int client_id, int port_id) { int err; snd_seq_t *seq = dev->seq_handle; snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca(&pinfo); if((err = snd_seq_get_any_port_info(seq, client_id, port_id, pinfo)) < 0) { FLUID_LOG(FLUID_ERR, "snd_seq_get_any_port_info() failed: %s", snd_strerror(err)); return; } fluid_alsa_seq_autoconnect_port_info(dev, pinfo); } // Connect available ALSA MIDI inputs to the provided port_info static void fluid_alsa_seq_autoconnect(fluid_alsa_seq_driver_t *dev, const snd_seq_port_info_t *dest_pinfo) { int err; snd_seq_t *seq = dev->seq_handle; snd_seq_client_info_t *cinfo; snd_seq_port_info_t *pinfo; // subscribe to future new clients/ports showing up if((err = snd_seq_connect_from(seq, snd_seq_port_info_get_port(dest_pinfo), SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE)) < 0) { FLUID_LOG(FLUID_ERR, "snd_seq_connect_from() failed: %s", snd_strerror(err)); } snd_seq_client_info_alloca(&cinfo); snd_seq_port_info_alloca(&pinfo); dev->autoconn_dest = *snd_seq_port_info_get_addr(dest_pinfo); snd_seq_client_info_set_client(cinfo, -1); while(snd_seq_query_next_client(seq, cinfo) >= 0) { snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); snd_seq_port_info_set_port(pinfo, -1); while(snd_seq_query_next_port(seq, pinfo) >= 0) { fluid_alsa_seq_autoconnect_port_info(dev, pinfo); } } } /* * 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) != FLUID_OK) /* ++ alloc device name */ { goto error_recovery; } if(fluid_settings_dupstr(settings, "midi.alsa_seq.id", &id) != FLUID_OK) /* ++ 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; } } fluid_settings_getint(settings, "midi.autoconnect", &dev->autoconn_inputs); if(dev->autoconn_inputs) { fluid_alsa_seq_autoconnect(dev, port_info); } /* tell the lash server our client id */ #ifdef HAVE_LASH { 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 /* HAVE_LASH */ fluid_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 */ void delete_fluid_alsa_seq_driver(fluid_midi_driver_t *p) { fluid_alsa_seq_driver_t *dev = (fluid_alsa_seq_driver_t *) p; fluid_return_if_fail(dev != NULL); /* cancel the thread and wait for it before cleaning up */ fluid_atomic_int_set(&dev->should_quit, 1); if(dev->thread) { fluid_thread_join(dev->thread); delete_fluid_thread(dev->thread); } if(dev->seq_handle) { snd_seq_close(dev->seq_handle); } if(dev->pfd) { FLUID_FREE(dev->pfd); } FLUID_FREE(dev); } /* * fluid_alsa_seq_run */ fluid_thread_return_t 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(!fluid_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); fluid_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; case SND_SEQ_EVENT_PORT_START: { if(dev->autoconn_inputs) { fluid_alsa_seq_autoconnect_port(dev, seq_ev->data.addr.client, seq_ev->data.addr.port); } } 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) */ return FLUID_THREAD_RETURN_VALUE; } #endif /* #if ALSA_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_aufile.c000066400000000000000000000067641417326347500210030ustar00rootroot00000000000000/* 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_sys.h" #include "fluid_adriver.h" #include "fluid_settings.h" #if AUFILE_SUPPORT /** 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; 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; } void delete_fluid_file_audio_driver(fluid_audio_driver_t *p) { fluid_file_audio_driver_t *dev = (fluid_file_audio_driver_t *) p; fluid_return_if_fail(dev != NULL); delete_fluid_timer(dev->timer); delete_fluid_file_renderer(dev->renderer); FLUID_FREE(dev); } 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; } #endif /* AUFILE_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_coreaudio.c000066400000000000000000000336221417326347500215010ustar00rootroot00000000000000/* 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_adriver.h" #include "fluid_settings.h" /* * !!! Make sure that no include above includes !!! * It #defines some macros that collide with enum definitions of OpenTransportProviders.h, which is included from OSServices.h, included from CoreServices.h * * https://trac.macports.org/ticket/36962 */ #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; OSStatus fluid_core_audio_callback(void *data, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); /************************************************************** * * 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)) && size > 0) { AudioBufferList *bufList = FLUID_MALLOC(size); if(bufList == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return 0; } 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; } } FLUID_FREE(bufList); } 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); 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, NULL, 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 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 ComponentDescription desc; #else AudioComponentDescription desc; #endif desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_HALOutput; //kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 Component comp = FindNextComponent(NULL, &desc); #else AudioComponent comp = AudioComponentFindNext(NULL, &desc); #endif if(comp == NULL) { FLUID_LOG(FLUID_ERR, "Failed to get the default audio device"); goto error_recovery; } #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 status = OpenAComponent(comp, &dev->outputUnit); #else status = AudioComponentInstanceNew(comp, &dev->outputUnit); #endif 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) == FLUID_OK /* 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 && FLUID_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; } } } } } } } 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); if(dev->buffers[0] == NULL || dev->buffers[1] == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory."); goto error_recovery; } // Initialize the audio unit status = AudioUnitInitialize(dev->outputUnit); 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 */ void delete_fluid_core_audio_driver(fluid_audio_driver_t *p) { fluid_core_audio_driver_t *dev = (fluid_core_audio_driver_t *) p; fluid_return_if_fail(dev != NULL); #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 CloseComponent(dev->outputUnit); #else AudioComponentInstanceDispose(dev->outputUnit); #endif if(dev->buffers[0]) { FLUID_FREE(dev->buffers[0]); } if(dev->buffers[1]) { FLUID_FREE(dev->buffers[1]); } FLUID_FREE(dev); } 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]; FLUID_MEMSET(left, 0, len * sizeof(float)); FLUID_MEMSET(right, 0, len * sizeof(float)); (*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-2.2.5/src/drivers/fluid_coremidi.c000066400000000000000000000164001417326347500213150ustar00rootroot00000000000000/* 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; MIDIPortRef input_port; fluid_midi_parser_t *parser; int autoconn_inputs; } fluid_coremidi_driver_t; 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); } static void fluid_coremidi_autoconnect(fluid_coremidi_driver_t *dev, MIDIPortRef input_port) { int i; int source_count = MIDIGetNumberOfSources(); for(i = 0; i < source_count; ++i) { MIDIEndpointRef source = MIDIGetSource(i); CFStringRef externalName; OSStatus result = MIDIObjectGetStringProperty(source, kMIDIPropertyName, &externalName); const char *source_name = CFStringGetCStringPtr(externalName, kCFStringEncodingASCII); CFRelease(externalName); result = MIDIPortConnectSource(input_port, source, NULL); if(result != noErr) { FLUID_LOG(FLUID_ERR, "Failed to connect \"%s\" device to input port.", source_name); } else { FLUID_LOG(FLUID_DBG, "Connected input port to \"%s\".", source_name); } } } /* * 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) { FLUID_SNPRINTF(clientid, sizeof(clientid), " (%d)", getpid()); } else { FLUID_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); CFRelease(str_clientname); 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; } CFStringRef str_input_portname = CFSTR("input"); result = MIDIInputPortCreate(client, str_input_portname, fluid_coremidi_callback, (void *)dev, &dev->input_port); CFRelease(str_input_portname); if(result != noErr) { FLUID_LOG(FLUID_ERR, "Failed to create input port."); goto error_recovery; } fluid_settings_getint(settings, "midi.autoconnect", &dev->autoconn_inputs); if(dev->autoconn_inputs) { fluid_coremidi_autoconnect(dev, dev->input_port); } 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 */ void delete_fluid_coremidi_driver(fluid_midi_driver_t *p) { fluid_coremidi_driver_t *dev = (fluid_coremidi_driver_t *) p; fluid_return_if_fail(dev != NULL); if(dev->input_port != NULL) { MIDIPortDispose(dev->input_port); } 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); } 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-2.2.5/src/drivers/fluid_dart.c000066400000000000000000000202161417326347500204540ustar00rootroot00000000000000/* 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 #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; 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); } 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; } void delete_fluid_dart_audio_driver(fluid_audio_driver_t *p) { fluid_dart_audio_driver_t *dev = (fluid_dart_audio_driver_t *) p; fluid_return_if_fail(dev != NULL); 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); } 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-2.2.5/src/drivers/fluid_dsound.c000066400000000000000000000606761417326347500210340ustar00rootroot00000000000000/* 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_synth.h" #include "fluid_adriver.h" #include "fluid_settings.h" #if DSOUND_SUPPORT #include #include #include /* Those two includes are required on Windows 9x/ME */ #include #include static DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter); static int fluid_dsound_write_processed_channels(fluid_synth_t *data, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]); static char *fluid_win32_error(HRESULT hr); /** * The driver handle multiple channels. * Actually the number maximum of channels is limited to 2 * DSOUND_MAX_STEREO_CHANNELS. * The only reason of this limitation is because we dont know how to define the mapping * of speakers for stereo output number above DSOUND_MAX_STEREO_CHANNELS. */ /* Maximum number of stereo outputs */ #define DSOUND_MAX_STEREO_CHANNELS 4 /* Speakers mapping */ static const DWORD channel_mask_speakers[DSOUND_MAX_STEREO_CHANNELS] = { /* 1 stereo output */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, /* 2 stereo outputs */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, /* 3 stereo outputs */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, /* 4 stereo outputs */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT }; typedef struct { fluid_audio_driver_t driver; LPDIRECTSOUND direct_sound; /* dsound instance */ LPDIRECTSOUNDBUFFER prim_buffer; /* dsound buffer*/ LPDIRECTSOUNDBUFFER sec_buffer; /* dsound buffer */ HANDLE thread; /* driver task */ DWORD threadID; void *synth; /* fluidsynth instance, or user pointer if custom processing function is defined */ fluid_audio_func_t func; /* callback called by the task for audio rendering in dsound buffers */ fluid_audio_channels_callback_t write; HANDLE quit_ev; /* Event object to request the audio task to stop */ float **drybuf; int bytes_per_second; /* number of bytes per second */ DWORD buffer_byte_size; /* size of one buffer in bytes */ DWORD queue_byte_size; /* total size of all buffers in bytes */ DWORD frame_size; /* frame size in bytes */ int channels_count; /* number of channels in audio stream */ } fluid_dsound_audio_driver_t; typedef struct { LPGUID devGUID; char *devname; } fluid_dsound_devsel_t; /* enumeration callback to add "device name" option on setting "audio.dsound.device" */ 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; } /* enumeration callback to look if a certain device exists and return its GUID. @context, (fluid_dsound_devsel_t *) context->devname provide the device name to look for. (fluid_dsound_devsel_t *) context->devGUID pointer to return device GUID. @return TRUE to continue enumeration, FALSE otherwise. */ 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(FLUID_STRCASECMP(devsel->devname, description) == 0) { /* The device exists, return a copy of its GUID */ devsel->devGUID = FLUID_NEW(GUID); if(devsel->devGUID) { /* return GUID */ memcpy(devsel->devGUID, guid, sizeof(GUID)); FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %p", devsel->devGUID); return FALSE; } } return TRUE; } /* - register setting "audio.dsound.device". - add list of dsound device name as option of "audio.dsound.device" setting. */ void fluid_dsound_audio_driver_settings(fluid_settings_t *settings) { fluid_settings_register_str(settings, "audio.dsound.device", "default", 0); fluid_settings_add_option(settings, "audio.dsound.device", "default"); DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback, settings); } /* * new_fluid_dsound_audio_driver * The driver handle the case of multiple stereo buffers provided by fluidsynth * mixer. * Each stereo buffers (left, right) are written to respective channels pair * of the audio device card. * For example, if the number of internal mixer buffer is 2, the audio device * must have at least 4 channels: * - buffer 0 (left, right) will be written to channel pair (0, 1). * - buffer 1 (left, right) will be written to channel pair (2, 3). * * @param setting. The settings the driver looks for: * "synth.sample-rate", the sample rate. * "audio.periods", the number of buffers and * "audio.period-size", the size of each buffer. * "audio.sample-format",the sample format, 16bits or float. * @param synth, fluidsynth synth instance to associate to the driver. * * Note: The number of internal mixer buffer is indicated by synth->audio_channels. * If the audio device cannot handle the format or do not have enough channels, * the driver fails and return NULL. */ fluid_audio_driver_t * new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) { return new_fluid_dsound_audio_driver2(settings, NULL, synth); } fluid_audio_driver_t * new_fluid_dsound_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data) { HRESULT hr; DSBUFFERDESC desc; fluid_dsound_audio_driver_t *dev = NULL; DSCAPS caps; char *buf1; DWORD bytes1; double sample_rate; int periods, period_size; int audio_channels; int i; fluid_dsound_devsel_t devsel; WAVEFORMATEXTENSIBLE format; /* 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 = data; dev->func = func; 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); fluid_settings_getint(settings, "synth.audio-channels", &audio_channels); /* Clear format structure*/ ZeroMemory(&format, sizeof(WAVEFORMATEXTENSIBLE)); /* Set this early so that if buffer allocation failed we can free the memory */ dev->channels_count = audio_channels * 2; /* check the format */ if(!func) { if(fluid_settings_str_equal(settings, "audio.sample-format", "float")) { FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format"); dev->write = fluid_synth_write_float_channels; /* sample container size in bits: 32 bits */ format.Format.wBitsPerSample = 8 * sizeof(float); format.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; format.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; } else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) { FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format"); dev->write = fluid_synth_write_s16_channels; /* sample container size in bits: 16bits */ format.Format.wBitsPerSample = 8 * sizeof(short); format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; format.Format.wFormatTag = WAVE_FORMAT_PCM; } else { FLUID_LOG(FLUID_ERR, "Unhandled sample format"); goto error_recovery; } } else { FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format"); dev->write = fluid_dsound_write_processed_channels; /* sample container size in bits: 32 bits */ format.Format.wBitsPerSample = 8 * sizeof(float); format.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; format.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; dev->drybuf = FLUID_ARRAY(float*, audio_channels * 2); if(dev->drybuf == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } FLUID_MEMSET(dev->drybuf, 0, sizeof(float*) * audio_channels * 2); for(i = 0; i < audio_channels * 2; ++i) { dev->drybuf[i] = FLUID_ARRAY(float, periods * period_size); if(dev->drybuf[i] == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } } } /* Finish to initialize the format structure */ /* number of channels in a frame */ format.Format.nChannels = audio_channels * 2; if(audio_channels > DSOUND_MAX_STEREO_CHANNELS) { FLUID_LOG(FLUID_ERR, "Channels number %d exceed internal limit %d", format.Format.nChannels, DSOUND_MAX_STEREO_CHANNELS * 2); goto error_recovery; } /* size of frame in bytes */ format.Format.nBlockAlign = format.Format.nChannels * format.Format.wBitsPerSample / 8; format.Format.nSamplesPerSec = (DWORD) sample_rate; format.Format.nAvgBytesPerSec = format.Format.nBlockAlign * format.Format.nSamplesPerSec; /* extension */ if(format.Format.nChannels > 2) { format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; format.Format.cbSize = 22; format.Samples.wValidBitsPerSample = format.Format.wBitsPerSample; /* CreateSoundBuffer accepts only format.dwChannelMask compatible with format.Format.nChannels */ format.dwChannelMask = channel_mask_speakers[audio_channels - 1]; } /* Finish to initialize dev structure */ dev->frame_size = format.Format.nBlockAlign; dev->buffer_byte_size = period_size * dev->frame_size; dev->queue_byte_size = periods * dev->buffer_byte_size; dev->bytes_per_second = format.Format.nAvgBytesPerSec; 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) == FLUID_OK /* ++ 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(devsel.devGUID) { FLUID_FREE(devsel.devGUID); /* -- free device GUID */ } if(hr != DS_OK) { FLUID_LOG(FLUID_ERR, "Failed to create the DirectSound object: '%s'", fluid_win32_error(hr)); 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: '%s'", fluid_win32_error(hr)); 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: '%s'", fluid_win32_error(hr)); 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: '%s'", fluid_win32_error(hr)); goto error_recovery; } /* set the primary sound buffer to this format. if it fails, just print a warning. */ hr = IDirectSoundBuffer_SetFormat(dev->prim_buffer, (WAVEFORMATEX *)&format); if(hr != DS_OK) { FLUID_LOG(FLUID_WARN, "Can't set format of primary sound buffer: '%s'", fluid_win32_error(hr)); } /* initialize the buffer description */ ZeroMemory(&desc, sizeof(DSBUFFERDESC)); desc.dwSize = sizeof(DSBUFFERDESC); desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; /* CreateSoundBuffer accepts only format.dwChannelMask compatible with format.Format.nChannels */ desc.lpwfxFormat = (WAVEFORMATEX *)&format; desc.dwBufferBytes = dev->queue_byte_size; 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 and get dsound buffer */ 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: '%s'", fluid_win32_error(hr)); goto error_recovery; } /* fill the buffer with silence */ memset(buf1, 0, bytes1); /* Unlock dsound buffer */ IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, 0, 0); /* Create object to signal thread exit */ dev->quit_ev = CreateEvent(NULL, FALSE, FALSE, NULL); if(dev->quit_ev == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create quit Event: '%s'", fluid_get_windows_error()); goto error_recovery; } /* start the audio thread */ dev->thread = CreateThread(NULL, 0, fluid_dsound_audio_run, (LPVOID) dev, 0, &dev->threadID); if(dev->thread == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create DSound audio thread: '%s'", fluid_get_windows_error()); goto error_recovery; } return (fluid_audio_driver_t *) dev; error_recovery: delete_fluid_dsound_audio_driver((fluid_audio_driver_t *) dev); return NULL; } void delete_fluid_dsound_audio_driver(fluid_audio_driver_t *d) { int i; fluid_dsound_audio_driver_t *dev = (fluid_dsound_audio_driver_t *) d; fluid_return_if_fail(dev != NULL); /* First tell dsound to stop playing its buffer now. Doing this before stopping audio thread avoid dsound playing transient glitches between the time audio task is stopped and dsound will be released. These glitches are particularly audible when a reverb is connected on output. */ if(dev->sec_buffer != NULL) { IDirectSoundBuffer_Stop(dev->sec_buffer); } /* request the audio task to stop and wait till the audio thread exits */ if(dev->thread != NULL) { /* tell the audio thread to stop its loop */ SetEvent(dev->quit_ev); 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 the thread object */ CloseHandle(dev->thread); } /* Release the event object */ if(dev->quit_ev != NULL) { CloseHandle(dev->quit_ev); } /* Finish to release all the dsound allocated resources */ if(dev->sec_buffer != NULL) { 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); } if(dev->drybuf != NULL) { for(i = 0; i < dev->channels_count; ++i) { FLUID_FREE(dev->drybuf[i]); } } FLUID_FREE(dev->drybuf); FLUID_FREE(dev); } static 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; int ms; /* pointers table on output first sample channels */ void *channels_out[DSOUND_MAX_STEREO_CHANNELS * 2]; int channels_off[DSOUND_MAX_STEREO_CHANNELS * 2]; int channels_incr[DSOUND_MAX_STEREO_CHANNELS * 2]; int i; /* initialize write callback constant parameters: dsound expects interleaved channels in a unique buffer. For example 4 channels (c1, c2, c3, c4) and n samples: { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,... sn:c1, sn:c2, sn:c3, sn:c4 }. So, channels_off[], channnel_incr[] tables should initialized like this: channels_off[0] = 0 channels_incr[0] = 4 channels_off[1] = 1 channels_incr[1] = 4 channels_off[2] = 2 channels_incr[2] = 4 channels_off[3] = 3 channels_incr[3] = 4 channels_out[], table will be initialized later, just before calling the write callback function. channels_out[0] = address of dsound buffer channels_out[1] = address of dsound buffer channels_out[2] = address of dsound buffer channels_out[3] = address of dsound buffer */ for(i = 0; i < dev->channels_count; i++) { channels_off[i] = i; channels_incr[i] = dev->channels_count; } 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); for(;;) { 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((UINT)-1); } /* fill the first part of the buffer */ if(bytes1 > 0) { frames = bytes1 / dev->frame_size; /* Before calling write function, finish to initialize channels_out[] table parameter: dsound expects interleaved channels in a unique buffer. So, channels_out[] table must be initialized with the address of the same buffer (buf1). */ i = dev->channels_count; do { channels_out[--i] = buf1; } while(i); /* calling write function */ if(dev->func == NULL) { dev->write(dev->synth, frames, dev->channels_count, channels_out, channels_off, channels_incr); } else { dev->write((fluid_synth_t*)dev, frames, dev->channels_count, channels_out, channels_off, channels_incr); } cur_position += frames * dev->frame_size; } /* fill the second part of the buffer */ if((buf2 != NULL) && (bytes2 > 0)) { frames = bytes2 / dev->frame_size; /* Before calling write function, finish to initialize channels_out[] table parameter: dsound expects interleaved channels in a unique buffer. So, channels_out[] table must be initialized with the address of the same buffer (buf2). */ i = dev->channels_count; do { channels_out[--i] = buf2; } while(i); /* calling write function */ if(dev->func == NULL) { dev->write(dev->synth, frames, dev->channels_count, channels_out, channels_off, channels_incr); } else { dev->write((fluid_synth_t*)dev, frames, dev->channels_count, channels_out, channels_off, channels_incr); } 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; } /* 1 ms of wait */ ms = 1; } else { /* Calculate how many milliseconds to sleep (minus 1 for safety) */ ms = (dev->buffer_byte_size - bytes) * 1000 / dev->bytes_per_second - 1; if(ms < 1) { ms = 1; } } /* Wait quit event or timeout */ if(WaitForSingleObject(dev->quit_ev, ms) == WAIT_OBJECT_0) { break; } } return 0; } static int fluid_dsound_write_processed_channels(fluid_synth_t *data, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]) { int i, ch; int ret; fluid_dsound_audio_driver_t *drv = (fluid_dsound_audio_driver_t*) data; float *optr[DSOUND_MAX_STEREO_CHANNELS * 2]; for(ch = 0; ch < drv->channels_count; ++ch) { FLUID_MEMSET(drv->drybuf[ch], 0, len * sizeof(float)); optr[ch] = (float*)channels_out[ch] + channels_off[ch]; } ret = drv->func(drv->synth, len, 0, NULL, drv->channels_count, drv->drybuf); for(ch = 0; ch < drv->channels_count; ++ch) { for(i = 0; i < len; ++i) { *optr[ch] = drv->drybuf[ch][i]; optr[ch] += channels_incr[ch]; } } return ret; } static 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; } #endif /* DSOUND_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_jack.c000066400000000000000000000665731417326347500204520ustar00rootroot00000000000000/* 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 "fluid_synth.h" #include "fluid_adriver.h" #include "fluid_mdriver.h" #include "fluid_settings.h" #if JACK_SUPPORT #include #include #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; 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; int midi_port_count; jack_port_t **midi_port; // array of midi port handles fluid_midi_parser_t *parser; int autoconnect_inputs; fluid_atomic_int_t autoconnect_is_outdated; }; 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); void fluid_jack_driver_shutdown(void *arg); int fluid_jack_driver_srate(jack_nframes_t nframes, void *arg); int fluid_jack_driver_bufsize(jack_nframes_t nframes, void *arg); int fluid_jack_driver_process(jack_nframes_t nframes, void *arg); void fluid_jack_port_registration(jack_port_id_t port, int is_registering, void *arg); static fluid_mutex_t last_client_mutex = FLUID_MUTEX_INIT; /* Probably not necessary, but just in case drivers are created by multiple threads */ static fluid_jack_client_t *last_client = NULL; /* Last unpaired client. For audio/MIDI driver pairing. */ void fluid_jack_audio_driver_settings(fluid_settings_t *settings) { fluid_settings_register_str(settings, "audio.jack.id", "fluidsynth", 0); fluid_settings_register_int(settings, "audio.jack.multi", 0, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_int(settings, "audio.jack.autoconnect", 0, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_str(settings, "audio.jack.server", "", 0); } /* * Connect all midi input ports to all terminal midi output ports */ void fluid_jack_midi_autoconnect(jack_client_t *client, fluid_jack_midi_driver_t *midi_driver) { int i, j; const char **midi_source_ports; midi_source_ports = jack_get_ports(client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput | JackPortIsTerminal); if(midi_source_ports != NULL) { for(j = 0; midi_source_ports[j] != NULL; j++) { for(i = 0; i < midi_driver->midi_port_count; i++) { FLUID_LOG(FLUID_INFO, "jack midi autoconnect \"%s\" to \"%s\"", midi_source_ports[j], jack_port_name(midi_driver->midi_port[i])); jack_connect(client, midi_source_ports[j], jack_port_name(midi_driver->midi_port[i])); } } jack_free(midi_source_ports); } fluid_atomic_int_set(&midi_driver->autoconnect_is_outdated, FALSE); } /* * 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]; if(fluid_settings_dupstr(settings, isaudio ? "audio.jack.server" /* ++ alloc server name */ : "midi.jack.server", &server) != FLUID_OK) { return NULL; } 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 != NULL && server != NULL && FLUID_STRCMP(last_client->server, server) == 0) && ((!isaudio && last_client->midi_driver == NULL) || (isaudio && last_client->audio_driver == NULL))) { client_ref = last_client; /* Register ports */ if(fluid_jack_client_register_ports(driver, isaudio, client_ref->client, settings) == FLUID_OK) { last_client = NULL; /* No more pairing for this client */ if(isaudio) { fluid_atomic_pointer_set(&client_ref->audio_driver, driver); } else { fluid_atomic_pointer_set(&client_ref->midi_driver, driver); } } else { // do not free client_ref and do not goto error_recovery // client_ref is being used by another audio or midi driver. Freeing it here will lead to a double free. client_ref = NULL; } 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_PANIC, "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) { FLUID_SNPRINTF(name, sizeof(name), "%s", client_name); } else { FLUID_STRNCPY(name, "fluidsynth", sizeof(name)); } 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_port_registration_callback(client_ref->client, fluid_jack_port_registration, client_ref); jack_set_process_callback(client_ref->client, fluid_jack_driver_process, client_ref); jack_set_buffer_size_callback(client_ref->client, fluid_jack_driver_bufsize, client_ref); jack_set_sample_rate_callback(client_ref->client, fluid_jack_driver_srate, client_ref); 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 HAVE_LASH { 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 /* HAVE_LASH */ 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; unsigned long jack_srate; double sample_rate; if(!isaudio) { fluid_jack_midi_driver_t *dev = driver; int midi_channels, ports; fluid_settings_getint(settings, "synth.midi-channels", &midi_channels); ports = midi_channels / 16; if((dev->midi_port = FLUID_ARRAY(jack_port_t *, ports)) == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } for(i = 0; i < ports; i++) { FLUID_SNPRINTF(name, sizeof(name), "midi_%02d", i); dev->midi_port[i] = jack_port_register(client, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput | JackPortIsTerminal, 0); if(dev->midi_port[i] == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create Jack MIDI port '%s'", name); FLUID_FREE(dev->midi_port); dev->midi_port = NULL; return FLUID_FAILED; } } dev->midi_port_count = ports; 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, "Out of memory"); 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); if(dev->output_ports[0] == NULL || dev->output_ports[1] == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create Jack audio port '%s'", (dev->output_ports[0] == NULL ? (dev->output_ports[1] == NULL ? "left & right" : "left") : "right")); goto error_recovery; } } 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"); goto error_recovery; } 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); if((dev->output_ports[2 * i] = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create Jack audio port '%s'", name); goto error_recovery; } sprintf(name, "r_%02d", i); if((dev->output_ports[2 * i + 1] = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create Jack audio port '%s'", name); goto error_recovery; } } fluid_settings_getint(settings, "synth.effects-channels", &dev->num_fx_ports); fluid_settings_getint(settings, "synth.effects-groups", &i); dev->num_fx_ports *= i; 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"); goto error_recovery; } dev->fx_bufs = FLUID_ARRAY(float *, 2 * dev->num_fx_ports); if(dev->fx_bufs == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); goto error_recovery; } 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); if((dev->fx_ports[2 * i] = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create Jack fx audio port '%s'", name); goto error_recovery; } sprintf(name, "fx_r_%02d", i); if((dev->fx_ports[2 * i + 1] = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create Jack fx audio port '%s'", name); goto error_recovery; } } } /* 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((unsigned long)sample_rate != jack_srate) { fluid_synth_t* synth; if(fluid_jack_obtain_synth(settings, &synth) == FLUID_OK) { FLUID_LOG(FLUID_INFO, "Jack sample rate mismatch, adjusting." " (synth.sample-rate=%lu, jackd=%lu)", (unsigned long)sample_rate, jack_srate); fluid_synth_set_sample_rate_immediately(synth, jack_srate); } else { FLUID_LOG(FLUID_WARN, "Jack sample rate mismatch (synth.sample-rate=%lu, jackd=%lu)" " impossible to adjust, because the settings object provided to new_fluid_audio_driver2() was not used to create a synth." , (unsigned long)sample_rate, jack_srate); } } return FLUID_OK; error_recovery: FLUID_FREE(dev->output_ports); dev->output_ports = NULL; FLUID_FREE(dev->fx_ports); dev->fx_ports = NULL; FLUID_FREE(dev->output_bufs); dev->output_bufs = NULL; FLUID_FREE(dev->fx_bufs); dev->fx_bufs = NULL; return FLUID_FAILED; } static void 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) { fluid_msleep(100); /* 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_PANIC, "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. */ /* 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 && jack_ports[0]) { int err, o = 0; int connected = 0; for(i = 0; i < 2 * dev->num_output_ports; ++i) { err = jack_connect(client, jack_port_name(dev->output_ports[i]), jack_ports[o++]); if(err) { FLUID_LOG(FLUID_ERR, "Error connecting jack port"); } else { connected++; } if(!jack_ports[o]) { o = 0; } } o = 0; for(i = 0; i < 2 * dev->num_fx_ports; ++i) { err = jack_connect(client, jack_port_name(dev->fx_ports[i]), jack_ports[o++]); if(err) { FLUID_LOG(FLUID_ERR, "Error connecting jack port"); } else { connected++; } if(!jack_ports[o]) { o = 0; } } 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 */ void delete_fluid_jack_audio_driver(fluid_audio_driver_t *p) { fluid_jack_audio_driver_t *dev = (fluid_jack_audio_driver_t *) p; fluid_return_if_fail(dev != NULL); if(dev->client_ref != NULL) { fluid_jack_client_close(dev->client_ref, dev); } FLUID_FREE(dev->output_bufs); FLUID_FREE(dev->output_ports); FLUID_FREE(dev->fx_bufs); FLUID_FREE(dev->fx_ports); FLUID_FREE(dev); } /* 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; 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) { if(fluid_atomic_int_get(&midi_driver->autoconnect_is_outdated)) { fluid_jack_midi_autoconnect(client->client, midi_driver); } for(i = 0; i < midi_driver->midi_port_count; i++) { midi_buffer = jack_port_get_buffer(midi_driver->midi_port[i], 0); 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) { fluid_midi_event_set_channel(evt, fluid_midi_event_get_channel(evt) + i * 16); midi_driver->driver.handler(midi_driver->driver.data, evt); } } } } } audio_driver = fluid_atomic_pointer_get(&client->audio_driver); if(audio_driver == NULL) { // shutting down return FLUID_OK; } if(audio_driver->callback == NULL && 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); return fluid_synth_write_float(audio_driver->data, nframes, left, 0, 1, right, 0, 1); } else { int res; fluid_audio_func_t callback = (audio_driver->callback != NULL) ? audio_driver->callback : (fluid_audio_func_t) fluid_synth_process; for(i = 0; i < audio_driver->num_output_ports; i++) { int k = i * 2; audio_driver->output_bufs[k] = (float *)jack_port_get_buffer(audio_driver->output_ports[k], nframes); FLUID_MEMSET(audio_driver->output_bufs[k], 0, nframes * sizeof(float)); k = 2 * i + 1; audio_driver->output_bufs[k] = (float *)jack_port_get_buffer(audio_driver->output_ports[k], nframes); FLUID_MEMSET(audio_driver->output_bufs[k], 0, nframes * sizeof(float)); } for(i = 0; i < audio_driver->num_fx_ports; i++) { int k = i * 2; audio_driver->fx_bufs[k] = (float *) jack_port_get_buffer(audio_driver->fx_ports[k], nframes); FLUID_MEMSET(audio_driver->fx_bufs[k], 0, nframes * sizeof(float)); k = 2 * i + 1; audio_driver->fx_bufs[k] = (float *) jack_port_get_buffer(audio_driver->fx_ports[k], nframes); FLUID_MEMSET(audio_driver->fx_bufs[k], 0, nframes * sizeof(float)); } res = callback(audio_driver->data, nframes, audio_driver->num_fx_ports * 2, audio_driver->fx_bufs, audio_driver->num_output_ports * 2, audio_driver->output_bufs); if(res != FLUID_OK) { const char *cb_func_name = (audio_driver->callback != NULL) ? "Custom audio callback function" : "fluid_synth_process()"; FLUID_LOG(FLUID_PANIC, "%s returned an error. As a consequence, fluidsynth will now be removed from Jack's processing loop.", cb_func_name); } return res; } } 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_port_registration(jack_port_id_t port, int is_registering, void *arg) { fluid_jack_client_t *client_ref = (fluid_jack_client_t *)arg; if(client_ref->midi_driver != NULL) { fluid_atomic_int_set(&client_ref->midi_driver->autoconnect_is_outdated, client_ref->midi_driver->autoconnect_inputs && is_registering != 0); } } void fluid_jack_midi_driver_settings(fluid_settings_t *settings) { fluid_settings_register_str(settings, "midi.jack.id", "fluidsynth-midi", 0); fluid_settings_register_str(settings, "midi.jack.server", "", 0); } /* * 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_PANIC, "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_PANIC, "Out of memory"); goto error_recovery; } fluid_settings_getint(settings, "midi.autoconnect", &dev->autoconnect_inputs); fluid_atomic_int_set(&dev->autoconnect_is_outdated, dev->autoconnect_inputs); dev->client_ref = new_fluid_jack_client(settings, FALSE, dev); if(!dev->client_ref) { goto error_recovery; } return (fluid_midi_driver_t *)dev; error_recovery: delete_fluid_jack_midi_driver((fluid_midi_driver_t *)dev); return NULL; } void delete_fluid_jack_midi_driver(fluid_midi_driver_t *p) { fluid_jack_midi_driver_t *dev = (fluid_jack_midi_driver_t *)p; fluid_return_if_fail(dev != NULL); if(dev->client_ref != NULL) { fluid_jack_client_close(dev->client_ref, dev); } delete_fluid_midi_parser(dev->parser); FLUID_FREE(dev->midi_port); FLUID_FREE(dev); } int fluid_jack_obtain_synth(fluid_settings_t *settings, fluid_synth_t **synth) { void *data; if(!fluid_settings_is_realtime(settings, "synth.gain") || (data = fluid_settings_get_user_data(settings, "synth.gain")) == NULL) { return FLUID_FAILED; } *synth = data; return FLUID_OK; } #endif /* JACK_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_mdriver.c000066400000000000000000000127051417326347500211760ustar00rootroot00000000000000/* 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" /* * fluid_mdriver_definition */ struct _fluid_mdriver_definition_t { const char *name; fluid_midi_driver_t *(*new)(fluid_settings_t *settings, handle_midi_event_func_t event_handler, void *event_handler_data); void (*free)(fluid_midi_driver_t *p); void (*settings)(fluid_settings_t *settings); }; static const fluid_mdriver_definition_t fluid_midi_drivers[] = { #if ALSA_SUPPORT { "alsa_seq", new_fluid_alsa_seq_driver, delete_fluid_alsa_seq_driver, fluid_alsa_seq_driver_settings }, { "alsa_raw", new_fluid_alsa_rawmidi_driver, delete_fluid_alsa_rawmidi_driver, fluid_alsa_rawmidi_driver_settings }, #endif #if JACK_SUPPORT { "jack", 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 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 terminator to avoid zero size array if no driver available */ { NULL, NULL, NULL, NULL } }; void fluid_midi_driver_settings(fluid_settings_t *settings) { unsigned int i; const char *def_name = NULL; fluid_settings_register_int(settings, "midi.autoconnect", 0, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_int(settings, "midi.realtime-prio", FLUID_DEFAULT_MIDI_RT_PRIO, 0, 99, 0); fluid_settings_register_str(settings, "midi.driver", "", 0); for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++) { /* Select the default driver */ if (def_name == NULL) { def_name = fluid_midi_drivers[i].name; } /* Add the driver to the list of options */ fluid_settings_add_option(settings, "midi.driver", fluid_midi_drivers[i].name); if(fluid_midi_drivers[i].settings != NULL) { fluid_midi_drivers[i].settings(settings); } } /* Set the default driver, if any */ if(def_name != NULL) { fluid_settings_setstr(settings, "midi.driver", def_name); } } /** * Create a new MIDI driver instance. * * @param settings Settings used to configure new MIDI driver. See \ref settings_midi for available options. * @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 * * Which MIDI driver is actually created depends on the \ref settings_midi_driver option. */ fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_midi_event_func_t handler, void *event_handler_data) { fluid_midi_driver_t *driver = NULL; char *allnames; const fluid_mdriver_definition_t *def; for(def = fluid_midi_drivers; def->name != NULL; def++) { if(fluid_settings_str_equal(settings, "midi.driver", def->name)) { FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", def->name); driver = def->new(settings, handler, event_handler_data); if(driver) { driver->define = def; } return driver; } } FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver."); allnames = fluid_settings_option_concat(settings, "midi.driver", NULL); if(allnames != NULL) { if(allnames[0] != '\0') { FLUID_LOG(FLUID_INFO, "This build of fluidsynth supports the following MIDI drivers: %s", allnames); } else { FLUID_LOG(FLUID_INFO, "This build of fluidsynth doesn't support any MIDI drivers."); } FLUID_FREE(allnames); } return NULL; } /** * Delete a MIDI driver instance. * @param driver MIDI driver to delete */ void delete_fluid_midi_driver(fluid_midi_driver_t *driver) { fluid_return_if_fail(driver != NULL); driver->define->free(driver); } fluidsynth-2.2.5/src/drivers/fluid_mdriver.h000066400000000000000000000065541417326347500212100ustar00rootroot00000000000000/* 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 "fluid_sys.h" /* * fluid_midi_driver_t */ typedef struct _fluid_mdriver_definition_t fluid_mdriver_definition_t; struct _fluid_midi_driver_t { const fluid_mdriver_definition_t *define; handle_midi_event_func_t handler; void *data; }; void fluid_midi_driver_settings(fluid_settings_t *settings); /* 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); void 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); void 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); void 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); void 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); void 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); void 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); void delete_fluid_coremidi_driver(fluid_midi_driver_t *p); void fluid_coremidi_driver_settings(fluid_settings_t *settings); #endif #endif /* _FLUID_AUDRIVER_H */ fluidsynth-2.2.5/src/drivers/fluid_midishare.c000066400000000000000000000322721417326347500214740ustar00rootroot00000000000000/* 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 application 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; 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; FLUID_LOG(FLUID_WARN, "\n\n" "================ MidiShare MIDI driver has been deprecated! =================\n" "You're using the MidiShare driver. This driver is old, unmaintained and believed\n" "to be unused. If you still need it, pls. let us know by posting to our\n" "mailing list at fluid-dev@nongnu.org - otherwise this driver might be removed\n" "in a future release of FluidSynth!\n" "================ MidiShare MIDI driver has been deprecated! =================\n" "\n" ); /* 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 */ void delete_fluid_midishare_midi_driver(fluid_midi_driver_t *p) { fluid_midishare_midi_driver_t *dev = (fluid_midishare_midi_driver_t *) p; fluid_return_if_fail(dev != NULL); 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); } /* * 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-2.2.5/src/drivers/fluid_oboe.cpp000066400000000000000000000243571417326347500210200ustar00rootroot00000000000000/* 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 Library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library 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_oboe.c * * Audio driver for Android Oboe. * * This file may make use of C++14, because it's required by oboe anyway. */ extern "C" { #include "fluid_adriver.h" #include "fluid_settings.h" } // extern "C" #if OBOE_SUPPORT #include #include #include using namespace oboe; constexpr int NUM_CHANNELS = 2; class OboeAudioStreamCallback; class OboeAudioStreamErrorCallback; /** fluid_oboe_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 = nullptr; bool cont = false; std::unique_ptr oboe_callback; std::unique_ptr oboe_error_callback; std::shared_ptr stream; double sample_rate; int is_sample_format_float; int device_id; int sharing_mode; // 0: Shared, 1: Exclusive int performance_mode; // 0: None, 1: PowerSaving, 2: LowLatency oboe::SampleRateConversionQuality srate_conversion_quality; int error_recovery_mode; // 0: Reconnect, 1: Stop } fluid_oboe_audio_driver_t; class OboeAudioStreamCallback : public AudioStreamCallback { public: OboeAudioStreamCallback(void *userData) : user_data(userData) { } DataCallbackResult onAudioReady(AudioStream *stream, void *audioData, int32_t numFrames) { fluid_oboe_audio_driver_t *dev = static_cast(this->user_data); if(!dev->cont) { return DataCallbackResult::Stop; } if(stream->getFormat() == AudioFormat::Float) { fluid_synth_write_float(dev->synth, numFrames, static_cast(audioData), 0, 2, static_cast(audioData), 1, 2); } else { fluid_synth_write_s16(dev->synth, numFrames, static_cast(audioData), 0, 2, static_cast(audioData), 1, 2); } return DataCallbackResult::Continue; } private: void *user_data; }; class OboeAudioStreamErrorCallback : public AudioStreamErrorCallback { fluid_oboe_audio_driver_t *dev; public: OboeAudioStreamErrorCallback(fluid_oboe_audio_driver_t *dev) : dev(dev) {} void onErrorAfterClose(AudioStream *stream, Result result); }; constexpr char OBOE_ID[] = "audio.oboe.id"; constexpr char SHARING_MODE[] = "audio.oboe.sharing-mode"; constexpr char PERF_MODE[] = "audio.oboe.performance-mode"; constexpr char SRCQ_SET[] = "audio.oboe.sample-rate-conversion-quality"; constexpr char RECOVERY_MODE[] = "audio.oboe.error-recovery-mode"; void fluid_oboe_audio_driver_settings(fluid_settings_t *settings) { fluid_settings_register_int(settings, OBOE_ID, 0, 0, 0x7FFFFFFF, 0); fluid_settings_register_str(settings, SHARING_MODE, "Shared", 0); fluid_settings_add_option(settings, SHARING_MODE, "Shared"); fluid_settings_add_option(settings, SHARING_MODE, "Exclusive"); fluid_settings_register_str(settings, PERF_MODE, "None", 0); fluid_settings_add_option(settings, PERF_MODE, "None"); fluid_settings_add_option(settings, PERF_MODE, "PowerSaving"); fluid_settings_add_option(settings, PERF_MODE, "LowLatency"); fluid_settings_register_str(settings, SRCQ_SET, "Medium", 0); fluid_settings_add_option(settings, SRCQ_SET, "None"); fluid_settings_add_option(settings, SRCQ_SET, "Fastest"); fluid_settings_add_option(settings, SRCQ_SET, "Low"); fluid_settings_add_option(settings, SRCQ_SET, "Medium"); fluid_settings_add_option(settings, SRCQ_SET, "High"); fluid_settings_add_option(settings, SRCQ_SET, "Best"); fluid_settings_register_str(settings, RECOVERY_MODE, "Reconnect", 0); fluid_settings_add_option(settings, RECOVERY_MODE, "Reconnect"); fluid_settings_add_option(settings, RECOVERY_MODE, "Stop"); } static oboe::SampleRateConversionQuality get_srate_conversion_quality(fluid_settings_t *settings) { oboe::SampleRateConversionQuality q; if(fluid_settings_str_equal(settings, SRCQ_SET, "None")) { q = oboe::SampleRateConversionQuality::None; } else if(fluid_settings_str_equal(settings, SRCQ_SET, "Fastest")) { q = oboe::SampleRateConversionQuality::Fastest; } else if(fluid_settings_str_equal(settings, SRCQ_SET, "Low")) { q = oboe::SampleRateConversionQuality::Low; } else if(fluid_settings_str_equal(settings, SRCQ_SET, "Medium")) { q = oboe::SampleRateConversionQuality::Medium; } else if(fluid_settings_str_equal(settings, SRCQ_SET, "High")) { q = oboe::SampleRateConversionQuality::High; } else if(fluid_settings_str_equal(settings, SRCQ_SET, "Best")) { q = oboe::SampleRateConversionQuality::Best; } else { char buf[256]; fluid_settings_copystr(settings, SRCQ_SET, buf, sizeof(buf)); std::stringstream ss; ss << "'" << SRCQ_SET << "' has unexpected value '" << buf << "'"; throw std::runtime_error(ss.str()); } return q; } Result fluid_oboe_connect_or_reconnect(fluid_oboe_audio_driver_t *dev) { AudioStreamBuilder builder; builder.setDeviceId(dev->device_id) ->setDirection(Direction::Output) ->setChannelCount(NUM_CHANNELS) ->setSampleRate(dev->sample_rate) ->setFormat(dev->is_sample_format_float ? AudioFormat::Float : AudioFormat::I16) ->setSharingMode(dev->sharing_mode == 1 ? SharingMode::Exclusive : SharingMode::Shared) ->setPerformanceMode( dev->performance_mode == 1 ? PerformanceMode::PowerSaving : dev->performance_mode == 2 ? PerformanceMode::LowLatency : PerformanceMode::None) ->setUsage(Usage::Media) ->setContentType(ContentType::Music) ->setCallback(dev->oboe_callback.get()) ->setErrorCallback(dev->oboe_error_callback.get()) ->setSampleRateConversionQuality(dev->srate_conversion_quality); return builder.openStream(dev->stream); } /* * new_fluid_oboe_audio_driver */ fluid_audio_driver_t * new_fluid_oboe_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) { fluid_oboe_audio_driver_t *dev = nullptr; try { Result result; dev = new fluid_oboe_audio_driver_t(); dev->synth = synth; dev->oboe_callback = std::make_unique(dev); dev->oboe_error_callback = std::make_unique(dev); fluid_settings_getnum(settings, "synth.sample-rate", &dev->sample_rate); dev->is_sample_format_float = fluid_settings_str_equal(settings, "audio.sample-format", "float"); fluid_settings_getint(settings, OBOE_ID, &dev->device_id); dev->sharing_mode = fluid_settings_str_equal(settings, SHARING_MODE, "Exclusive") ? 1 : 0; dev->performance_mode = fluid_settings_str_equal(settings, PERF_MODE, "PowerSaving") ? 1 : fluid_settings_str_equal(settings, PERF_MODE, "LowLatency") ? 2 : 0; dev->srate_conversion_quality = get_srate_conversion_quality(settings); dev->error_recovery_mode = fluid_settings_str_equal(settings, RECOVERY_MODE, "Stop") ? 1 : 0; result = fluid_oboe_connect_or_reconnect(dev); if(result != Result::OK) { FLUID_LOG(FLUID_ERR, "Unable to open Oboe audio stream"); goto error_recovery; } dev->cont = true; FLUID_LOG(FLUID_INFO, "Using Oboe driver"); result = dev->stream->start(); if(result != Result::OK) { FLUID_LOG(FLUID_ERR, "Unable to start Oboe audio stream"); goto error_recovery; } return &dev->driver; } catch(const std::bad_alloc &) { FLUID_LOG(FLUID_ERR, "oboe: std::bad_alloc caught: Out of memory"); } catch(const std::exception &e) { FLUID_LOG(FLUID_ERR, "oboe: std::exception caught: %s", e.what()); } catch(...) { FLUID_LOG(FLUID_ERR, "Unexpected Oboe driver initialization error"); } error_recovery: delete_fluid_oboe_audio_driver(reinterpret_cast(dev)); return nullptr; } void delete_fluid_oboe_audio_driver(fluid_audio_driver_t *p) { fluid_oboe_audio_driver_t *dev = reinterpret_cast(p); fluid_return_if_fail(dev != nullptr); try { dev->cont = false; if(dev->stream != nullptr) { dev->stream->stop(); dev->stream->close(); } } catch(...) { FLUID_LOG(FLUID_ERR, "Exception caught while stopping and closing Oboe stream."); } delete dev; } void OboeAudioStreamErrorCallback::onErrorAfterClose(AudioStream *stream, Result result) { if(dev->error_recovery_mode == 1) // Stop { FLUID_LOG(FLUID_ERR, "Oboe driver encountered an error (such as earphone unplugged). Stopped."); dev->stream.reset(); return; } else { FLUID_LOG(FLUID_WARN, "Oboe driver encountered an error (such as earphone unplugged). Recovering..."); } result = fluid_oboe_connect_or_reconnect(dev); if(result != Result::OK) { FLUID_LOG(FLUID_ERR, "Unable to reconnect Oboe audio stream"); return; // cannot do anything further } // start the new stream. result = dev->stream->start(); if(result != Result::OK) { FLUID_LOG(FLUID_ERR, "Unable to restart Oboe audio stream"); return; // cannot do anything further } } #endif // OBOE_SUPPORT fluidsynth-2.2.5/src/drivers/fluid_opensles.c000066400000000000000000000254041417326347500213560ustar00rootroot00000000000000/* 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 Library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library 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_opensles.c * * Audio driver for OpenSLES. * */ #include "fluid_adriver.h" #if OPENSLES_SUPPORT #include #include static const int NUM_CHANNELS = 2; /** fluid_opensles_audio_driver_t * * This structure should not be accessed directly. Use audio port * functions instead. */ typedef struct { fluid_audio_driver_t driver; SLObjectItf engine; SLObjectItf output_mix_object; SLObjectItf audio_player; SLPlayItf audio_player_interface; SLAndroidSimpleBufferQueueItf player_buffer_queue_interface; void *synth; int period_frames; int is_sample_format_float; /* used only by callback mode */ short *sles_buffer_short; float *sles_buffer_float; int cont; double sample_rate; } fluid_opensles_audio_driver_t; static void opensles_callback(SLAndroidSimpleBufferQueueItf caller, void *pContext); static void process_fluid_buffer(fluid_opensles_audio_driver_t *dev); void fluid_opensles_audio_driver_settings(fluid_settings_t *settings) { } /* * new_fluid_opensles_audio_driver */ fluid_audio_driver_t * new_fluid_opensles_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) { SLresult result; fluid_opensles_audio_driver_t *dev; double sample_rate; int period_size; int realtime_prio = 0; int is_sample_format_float; SLEngineItf engine_interface; dev = FLUID_NEW(fluid_opensles_audio_driver_t); if(dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(*dev)); 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); is_sample_format_float = fluid_settings_str_equal(settings, "audio.sample-format", "float"); dev->synth = synth; dev->is_sample_format_float = is_sample_format_float; dev->period_frames = period_size; dev->sample_rate = sample_rate; dev->cont = 1; result = slCreateEngine(&(dev->engine), 0, NULL, 0, NULL, NULL); if(!dev->engine) { FLUID_LOG(FLUID_ERR, "Failed to create the OpenSL ES engine, error code 0x%lx", (unsigned long)result); goto error_recovery; } result = (*dev->engine)->Realize(dev->engine, SL_BOOLEAN_FALSE); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to realize the OpenSL ES engine, error code 0x%lx", (unsigned long)result); goto error_recovery; } result = (*dev->engine)->GetInterface(dev->engine, SL_IID_ENGINE, &engine_interface); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to retrieve the OpenSL ES engine interface, error code 0x%lx", (unsigned long)result); goto error_recovery; } result = (*engine_interface)->CreateOutputMix(engine_interface, &dev->output_mix_object, 0, 0, 0); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to create the OpenSL ES output mix object, error code 0x%lx", (unsigned long)result); goto error_recovery; } result = (*dev->output_mix_object)->Realize(dev->output_mix_object, SL_BOOLEAN_FALSE); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to realize the OpenSL ES output mix object, error code 0x%lx", (unsigned long)result); goto error_recovery; } { SLDataLocator_AndroidSimpleBufferQueue loc_buffer_queue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 /* number of buffers */ }; SLAndroidDataFormat_PCM_EX format_pcm = { SL_ANDROID_DATAFORMAT_PCM_EX, NUM_CHANNELS, ((SLuint32) sample_rate) * 1000, is_sample_format_float ? SL_PCMSAMPLEFORMAT_FIXED_32 : SL_PCMSAMPLEFORMAT_FIXED_16, is_sample_format_float ? SL_PCMSAMPLEFORMAT_FIXED_32 : SL_PCMSAMPLEFORMAT_FIXED_16, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN, is_sample_format_float ? SL_ANDROID_PCM_REPRESENTATION_FLOAT : SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT }; SLDataSource audio_src = { &loc_buffer_queue, &format_pcm }; SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, dev->output_mix_object }; SLDataSink audio_sink = {&loc_outmix, NULL}; const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; const SLboolean req1[] = {SL_BOOLEAN_TRUE}; result = (*engine_interface)->CreateAudioPlayer(engine_interface, &(dev->audio_player), &audio_src, &audio_sink, 1, ids1, req1); } if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to create the OpenSL ES audio player object, error code 0x%lx", (unsigned long)result); goto error_recovery; } result = (*dev->audio_player)->Realize(dev->audio_player, SL_BOOLEAN_FALSE); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to realize the OpenSL ES audio player object, error code 0x%lx", (unsigned long)result); goto error_recovery; } result = (*dev->audio_player)->GetInterface(dev->audio_player, SL_IID_PLAY, &(dev->audio_player_interface)); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to retrieve the OpenSL ES audio player interface, error code 0x%lx", (unsigned long)result); goto error_recovery; } result = (*dev->audio_player)->GetInterface(dev->audio_player, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(dev->player_buffer_queue_interface)); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to retrieve the OpenSL ES buffer queue interface, error code 0x%lx", (unsigned long)result); goto error_recovery; } if(dev->is_sample_format_float) { dev->sles_buffer_float = FLUID_ARRAY(float, dev->period_frames * NUM_CHANNELS); } else { dev->sles_buffer_short = FLUID_ARRAY(short, dev->period_frames * NUM_CHANNELS); } if(dev->sles_buffer_float == NULL && dev->sles_buffer_short == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory."); goto error_recovery; } result = (*dev->player_buffer_queue_interface)->RegisterCallback(dev->player_buffer_queue_interface, opensles_callback, dev); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to register the opensles_callback, error code 0x%lx", (unsigned long)result); goto error_recovery; } if(dev->is_sample_format_float) { result = (*dev->player_buffer_queue_interface)->Enqueue(dev->player_buffer_queue_interface, dev->sles_buffer_float, dev->period_frames * NUM_CHANNELS * sizeof(float)); } else { result = (*dev->player_buffer_queue_interface)->Enqueue(dev->player_buffer_queue_interface, dev->sles_buffer_short, dev->period_frames * NUM_CHANNELS * sizeof(short)); } if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to add a buffer to the queue, error code 0x%lx", (unsigned long)result); goto error_recovery; } result = (*dev->audio_player_interface)->SetCallbackEventsMask(dev->audio_player_interface, SL_PLAYEVENT_HEADATEND); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to set OpenSL ES audio player callback events, error code 0x%lx", (unsigned long)result); goto error_recovery; } result = (*dev->audio_player_interface)->SetPlayState(dev->audio_player_interface, SL_PLAYSTATE_PLAYING); if(result != SL_RESULT_SUCCESS) { FLUID_LOG(FLUID_ERR, "Failed to set OpenSL ES audio player play state to playing, error code 0x%lx", (unsigned long)result); goto error_recovery; } FLUID_LOG(FLUID_INFO, "Using OpenSLES driver."); return (fluid_audio_driver_t *) dev; error_recovery: delete_fluid_opensles_audio_driver((fluid_audio_driver_t *) dev); return NULL; } void delete_fluid_opensles_audio_driver(fluid_audio_driver_t *p) { fluid_opensles_audio_driver_t *dev = (fluid_opensles_audio_driver_t *) p; fluid_return_if_fail(dev != NULL); dev->cont = 0; if(dev->audio_player) { (*dev->audio_player)->Destroy(dev->audio_player); } if(dev->output_mix_object) { (*dev->output_mix_object)->Destroy(dev->output_mix_object); } if(dev->engine) { (*dev->engine)->Destroy(dev->engine); } if(dev->sles_buffer_float) { FLUID_FREE(dev->sles_buffer_float); } if(dev->sles_buffer_short) { FLUID_FREE(dev->sles_buffer_short); } FLUID_FREE(dev); } void opensles_callback(SLAndroidSimpleBufferQueueItf caller, void *pContext) { fluid_opensles_audio_driver_t *dev = (fluid_opensles_audio_driver_t *) pContext; SLresult result; process_fluid_buffer(dev); if(dev->is_sample_format_float) { result = (*caller)->Enqueue( dev->player_buffer_queue_interface, dev->sles_buffer_float, dev->period_frames * sizeof(float) * NUM_CHANNELS); } else { result = (*caller)->Enqueue( dev->player_buffer_queue_interface, dev->sles_buffer_short, dev->period_frames * sizeof(short) * NUM_CHANNELS); } /* if (result != SL_RESULT_SUCCESS) { // Do not simply break at just one single insufficient buffer. Go on. } */ } void process_fluid_buffer(fluid_opensles_audio_driver_t *dev) { short *out_short = dev->sles_buffer_short; float *out_float = dev->sles_buffer_float; int period_frames = dev->period_frames; if(dev->is_sample_format_float) { fluid_synth_write_float(dev->synth, period_frames, out_float, 0, 2, out_float, 1, 2); } else { fluid_synth_write_s16(dev->synth, period_frames, out_short, 0, 2, out_short, 1, 2); } } #endif fluidsynth-2.2.5/src/drivers/fluid_oss.c000066400000000000000000000507101417326347500203300ustar00rootroot00000000000000/* 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 #if defined(HAVE_SYS_SOUNDCARD_H) #include #elif defined(HAVE_LINUX_SOUNDCARD_H) #include #else #include #endif #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; /* local utilities */ static int fluid_oss_set_queue_size(fluid_oss_audio_driver_t *dev, int ss, int ch, int qs, int bs); static fluid_thread_return_t fluid_oss_audio_run(void *d); static fluid_thread_return_t 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; static fluid_thread_return_t 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); } /* * 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; FLUID_LOG(FLUID_WARN, "\n\n" "================= OSS audio driver has been deprecated! ==================\n" "You're using the OSS driver. This driver is old, unmaintained and believed\n" "to be unused. If you still need it, pls. let us know by posting to our\n" "mailing list at fluid-dev@nongnu.org - otherwise this driver might be removed\n" "in a future release of FluidSynth!\n" "================= OSS audio driver has been deprecated! ==================\n" "\n" ); 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) != FLUID_OK || !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; FLUID_LOG(FLUID_WARN, "\n\n" "================= OSS audio driver has been deprecated! ==================\n" "You're using the OSS driver. This driver is old, unmaintained and believed\n" "to be unused. If you still need it, pls. let us know by posting to our\n" "mailing list at fluid-dev@nongnu.org - otherwise this driver might be removed\n" "in a future release of FluidSynth!\n" "================= OSS audio driver has been deprecated! ==================\n" "\n" ); 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) != FLUID_OK || !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 */ void delete_fluid_oss_audio_driver(fluid_audio_driver_t *p) { fluid_oss_audio_driver_t *dev = (fluid_oss_audio_driver_t *) p; fluid_return_if_fail(dev != NULL); dev->cont = 0; if(dev->thread) { fluid_thread_join(dev->thread); delete_fluid_thread(dev->thread); } if(dev->dspfd >= 0) { close(dev->dspfd); } FLUID_FREE(dev->buffer); FLUID_FREE(dev); } /** * 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 */ fluid_thread_return_t 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"); return FLUID_THREAD_RETURN_VALUE; } /* * fluid_oss_audio_run */ fluid_thread_return_t 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) { FLUID_MEMSET(left, 0, buffer_size * sizeof(float)); FLUID_MEMSET(right, 0, buffer_size * sizeof(float)); (*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"); return FLUID_THREAD_RETURN_VALUE; } void fluid_oss_midi_driver_settings(fluid_settings_t *settings) { fluid_settings_register_str(settings, "midi.oss.device", "/dev/midi", 0); } /* * 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; FLUID_LOG(FLUID_WARN, "\n\n" "================= OSS MIDI driver has been deprecated! ==================\n" "You're using the OSS driver. This driver is old, unmaintained and believed\n" "to be unused. If you still need it, pls. let us know by posting to our\n" "mailing list at fluid-dev@nongnu.org - otherwise this driver might be removed\n" "in a future release of FluidSynth!\n" "================= OSS MIDI driver has been deprecated! ==================\n" "\n" ); /* 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 */ void delete_fluid_oss_midi_driver(fluid_midi_driver_t *p) { fluid_oss_midi_driver_t *dev = (fluid_oss_midi_driver_t *) p; fluid_return_if_fail(dev != NULL); /* cancel the thread and wait for it before cleaning up */ dev->status = FLUID_MIDI_DONE; if(dev->thread) { fluid_thread_join(dev->thread); delete_fluid_thread(dev->thread); } if(dev->fd >= 0) { close(dev->fd); } delete_fluid_midi_parser(dev->parser); FLUID_FREE(dev); } /* * fluid_oss_midi_run */ fluid_thread_return_t 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); } } } return FLUID_THREAD_RETURN_VALUE; } #endif /*#if OSS_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_portaudio.c000066400000000000000000000270211417326347500215310ustar00rootroot00000000000000/* 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_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); #define PORTAUDIO_DEFAULT_DEVICE "PortAudio Default" /** * Checks if device_num is a valid device and returns the name of the portaudio device. * A device is valid if it is an output device with at least 2 channels. * * @param device_num index of the portaudio device to check. * @param name_ptr if device_num is valid, set to a unique device name, ignored otherwise * * The name returned is unique for each num_device index, so this * name is useful to identify any available host audio device. * This name is convenient for audio.portaudio.device setting. * * The format of the name is: device_index:host_api_name:host_device_name * * example: 5:MME:SB PCI * * 5: is the portaudio device index. * MME: is the host API name. * SB PCI: is the host device name. * * @return #FLUID_OK if device_num is a valid output device, #FLUID_FAILED otherwise. * When #FLUID_OK, the name is returned in allocated memory. The caller must check * the name pointer for a valid memory allocation and should free the memory. */ static int fluid_portaudio_get_device_name(int device_num, char **name_ptr) { const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(device_num); if(deviceInfo->maxOutputChannels >= 2) { const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); /* The size of the buffer name for the following format: device_index:host_api_name:host_device_name. */ int i = device_num; size_t size = 0; do { size++; i = i / 10 ; } while(i); /* index size */ /* host API size + host device size + 2 separators + zero termination */ size += FLUID_STRLEN(hostInfo->name) + FLUID_STRLEN(deviceInfo->name) + 3u; *name_ptr = FLUID_MALLOC(size); if(*name_ptr) { /* the name is filled if allocation is successful */ FLUID_SPRINTF(*name_ptr, "%d:%s:%s", device_num, hostInfo->name, deviceInfo->name); } return FLUID_OK; /* device_num is a valid device */ } else { return FLUID_FAILED; /* device_num is an invalid device */ } } /** * Initializes "audio.portaudio.device" setting with an options list of unique device names * of available sound card devices. * @param settings pointer to settings. */ void fluid_portaudio_driver_settings(fluid_settings_t *settings) { int numDevices; PaError err; int i; fluid_settings_register_str(settings, "audio.portaudio.device", PORTAUDIO_DEFAULT_DEVICE, 0); 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); } else { for(i = 0; i < numDevices; i++) { char *name; if(fluid_portaudio_get_device_name(i, &name) == FLUID_OK) { /* the device i is a valid output device */ if(name) { /* registers this name in the option list */ fluid_settings_add_option(settings, "audio.portaudio.device", name); FLUID_FREE(name); } else { FLUID_LOG(FLUID_ERR, "Out of memory"); break; } } } } /* done with PortAudio for now, may get reopened later */ err = Pa_Terminate(); if(err != paNoError) { printf("PortAudio termination error: %s\n", Pa_GetErrorText(err)); } } /** * Creates the portaudio driver and opens the portaudio device * indicated by audio.portaudio.device setting. * * @param settings pointer to settings * @param synth the synthesizer instance * @return pointer to the driver on success, NULL otherwise. */ 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; /* the portaudio device name to work with */ double sample_rate; /* intended sample rate */ int period_size; /* intended buffer 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; /* gets audio parameters from the settings */ 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; /* For stereo output */ outputParams.suggestedLatency = (PaTime)period_size / sample_rate; /* Locate the device if specified */ if(FLUID_STRCMP(device, PORTAUDIO_DEFAULT_DEVICE) != 0) { /* The intended device is not the default device name, so we search a device among available devices */ 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++) { char *name; if(fluid_portaudio_get_device_name(i, &name) == FLUID_OK) { /* the device i is a valid output device */ if(name) { /* We see if the name corresponds to audio.portaudio.device */ char found = (FLUID_STRCMP(device, name) == 0); FLUID_FREE(name); if(found) { /* the device index is found */ outputParams.device = i; /* The search is finished */ break; } } else { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } } } if(i == numDevices) { FLUID_LOG(FLUID_ERR, "PortAudio device '%s' was not found", device); goto error_recovery; } } else { /* the default device will be used */ outputParams.device = Pa_GetDefaultOutputDevice(); } /* The device is found. We set the sample format and the audio rendering function suited to this format. */ 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, /* Output parameters */ sample_rate, period_size, paNoFlag, fluid_portaudio_run, /* callback */ dev); if(err != paNoError) { FLUID_LOG(FLUID_ERR, "Error opening PortAudio stream: %s", Pa_GetErrorText(err)); goto error_recovery; } err = Pa_StartStream(dev->stream); /* starts the I/O 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 */ void delete_fluid_portaudio_driver(fluid_audio_driver_t *p) { fluid_portaudio_driver_t *dev = (fluid_portaudio_driver_t *)p; PaError err; fluid_return_if_fail(dev != NULL); /* 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); } #endif /* PORTAUDIO_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_pulse.c000066400000000000000000000210221417326347500206460ustar00rootroot00000000000000/* 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" #if PULSE_SUPPORT #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; float *left; float *right; float *buf; } fluid_pulse_audio_driver_t; static fluid_thread_return_t fluid_pulse_audio_run(void *d); static fluid_thread_return_t 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); fluid_settings_register_str(settings, "audio.pulseaudio.device", "default", 0); fluid_settings_register_str(settings, "audio.pulseaudio.media-role", "music", 0); fluid_settings_register_int(settings, "audio.pulseaudio.adjust-latency", 1, 0, 1, FLUID_HINT_TOGGLED); } 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; float *left = NULL, *right = NULL, *buf = NULL; 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.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(FLUID_STRCMP(media_role, "") != 0) { g_setenv("PULSE_PROP_media.role", media_role, TRUE); } FLUID_FREE(media_role); /* -- free media_role string */ } if(server && FLUID_STRCMP(server, "default") == 0) { FLUID_FREE(server); /* -- free server string */ server = NULL; } if(device && FLUID_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"); if(func != NULL) { left = FLUID_ARRAY(float, period_size); right = FLUID_ARRAY(float, period_size); if(left == NULL || right == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory."); goto error_recovery; } } buf = FLUID_ARRAY(float, period_size * 2); if(buf == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory."); goto error_recovery; } dev->left = left; dev->right = right; dev->buf = buf; /* 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; } FLUID_FREE(server); /* -- free server string */ FLUID_FREE(device); /* -- free device string */ return (fluid_audio_driver_t *) dev; error_recovery: FLUID_FREE(server); /* -- free server string */ FLUID_FREE(device); /* -- free device string */ FLUID_FREE(left); FLUID_FREE(right); FLUID_FREE(buf); delete_fluid_pulse_audio_driver((fluid_audio_driver_t *) dev); return NULL; } void delete_fluid_pulse_audio_driver(fluid_audio_driver_t *p) { fluid_pulse_audio_driver_t *dev = (fluid_pulse_audio_driver_t *) p; fluid_return_if_fail(dev != NULL); dev->cont = 0; if(dev->thread) { fluid_thread_join(dev->thread); delete_fluid_thread(dev->thread); } if(dev->pa_handle) { pa_simple_free(dev->pa_handle); } FLUID_FREE(dev->left); FLUID_FREE(dev->right); FLUID_FREE(dev->buf); FLUID_FREE(dev); } /* Thread without audio callback, more efficient */ static fluid_thread_return_t fluid_pulse_audio_run(void *d) { fluid_pulse_audio_driver_t *dev = (fluid_pulse_audio_driver_t *) d; float *buf = dev->buf; int buffer_size; int err; buffer_size = dev->buffer_size; 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) */ return FLUID_THREAD_RETURN_VALUE; } static fluid_thread_return_t 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 = dev->left, *right = dev->right, *buf = dev->buf; float *handle[2]; int buffer_size; int err; int i; buffer_size = dev->buffer_size; handle[0] = left; handle[1] = right; while(dev->cont) { FLUID_MEMSET(left, 0, buffer_size * sizeof(float)); FLUID_MEMSET(right, 0, buffer_size * sizeof(float)); (*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) */ return FLUID_THREAD_RETURN_VALUE; } #endif /* PULSE_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_sdl2.c000066400000000000000000000152201417326347500203650ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * Copyright (C) 2018 Carlo Bramini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include "fluid_synth.h" #include "fluid_adriver.h" #include "fluid_settings.h" #if SDL2_SUPPORT #include "SDL.h" typedef struct { fluid_audio_driver_t driver; fluid_synth_t *synth; fluid_audio_callback_t write_ptr; SDL_AudioDeviceID devid; int frame_size; } fluid_sdl2_audio_driver_t; static void SDLAudioCallback(void *data, void *stream, int len) { fluid_sdl2_audio_driver_t *dev = (fluid_sdl2_audio_driver_t *)data; len /= dev->frame_size; dev->write_ptr(dev->synth, len, stream, 0, 2, stream, 1, 2); } void fluid_sdl2_audio_driver_settings(fluid_settings_t *settings) { int n, nDevs; fluid_settings_register_str(settings, "audio.sdl2.device", "default", 0); fluid_settings_add_option(settings, "audio.sdl2.device", "default"); if(!SDL_WasInit(SDL_INIT_AUDIO)) { #if FLUID_VERSION_CHECK(FLUIDSYNTH_VERSION_MAJOR, FLUIDSYNTH_VERSION_MINOR, FLUIDSYNTH_VERSION_MICRO) < FLUID_VERSION_CHECK(2,2,0) FLUID_LOG(FLUID_WARN, "SDL2 not initialized, SDL2 audio driver won't be usable"); #endif return; } nDevs = SDL_GetNumAudioDevices(0); for(n = 0; n < nDevs; n++) { const char *dev_name = SDL_GetAudioDeviceName(n, 0); if(dev_name != NULL) { FLUID_LOG(FLUID_DBG, "SDL2 driver testing audio device: %s", dev_name); fluid_settings_add_option(settings, "audio.sdl2.device", dev_name); } } } /* * new_fluid_sdl2_audio_driver */ fluid_audio_driver_t * new_fluid_sdl2_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) { fluid_sdl2_audio_driver_t *dev = NULL; fluid_audio_callback_t write_ptr; double sample_rate; int period_size, sample_size; SDL_AudioSpec aspec, rspec; char *device; const char *dev_name; /* Check if SDL library has been started */ if(!SDL_WasInit(SDL_INIT_AUDIO)) { FLUID_LOG(FLUID_ERR, "Failed to create SDL2 audio driver, because the audio subsystem of SDL2 is not initialized."); return NULL; } /* Retrieve the settings */ fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); fluid_settings_getint(settings, "audio.period-size", &period_size); /* Lower values do not seem to give good results */ if(period_size < 1024) { period_size = 1024; } else { /* According to documentation, it MUST be a power of two */ if((period_size & (period_size - 1)) != 0) { FLUID_LOG(FLUID_ERR, "\"audio.period-size\" must be a power of 2 for SDL2"); return NULL; } } /* Clear the format buffer */ FLUID_MEMSET(&aspec, 0, sizeof(aspec)); /* Setup mixing frequency */ aspec.freq = (int)sample_rate; /* Check the format */ if(fluid_settings_str_equal(settings, "audio.sample-format", "float")) { FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format"); sample_size = sizeof(float); write_ptr = fluid_synth_write_float; aspec.format = AUDIO_F32SYS; } else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) { FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format"); sample_size = sizeof(short); write_ptr = fluid_synth_write_s16; aspec.format = AUDIO_S16SYS; } else { FLUID_LOG(FLUID_ERR, "Unhandled sample format"); return NULL; } /* Compile the format buffer */ aspec.channels = 2; aspec.samples = aspec.channels * ((period_size + 7) & ~7); aspec.callback = (SDL_AudioCallback)SDLAudioCallback; /* Set default device to use */ device = NULL; dev_name = NULL; /* get the selected device name. if none is specified, use default device. */ if(fluid_settings_dupstr(settings, "audio.sdl2.device", &device) == FLUID_OK && device != NULL && device[0] != '\0') { int n, nDevs = SDL_GetNumAudioDevices(0); for(n = 0; n < nDevs; n++) { dev_name = SDL_GetAudioDeviceName(n, 0); if(FLUID_STRCASECMP(dev_name, device) == 0) { FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %s", dev_name); break; } } if(n >= nDevs) { FLUID_LOG(FLUID_DBG, "Audio device %s, using \"default\"", device); dev_name = NULL; } } if(device != NULL) { FLUID_FREE(device); } do { /* create and clear the driver data */ dev = FLUID_NEW(fluid_sdl2_audio_driver_t); if(dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); break; } FLUID_MEMSET(dev, 0, sizeof(fluid_sdl2_audio_driver_t)); /* set device pointer to userdata */ aspec.userdata = dev; /* Save copy of synth */ dev->synth = synth; /* Save copy of other variables */ dev->write_ptr = write_ptr; dev->frame_size = sample_size * aspec.channels; /* Open audio device */ dev->devid = SDL_OpenAudioDevice(dev_name, 0, &aspec, &rspec, 0); if(!dev->devid) { FLUID_LOG(FLUID_ERR, "Failed to open audio device"); break; } /* Start to play */ SDL_PauseAudioDevice(dev->devid, 0); return (fluid_audio_driver_t *) dev; } while(0); delete_fluid_sdl2_audio_driver(&dev->driver); return NULL; } void delete_fluid_sdl2_audio_driver(fluid_audio_driver_t *d) { fluid_sdl2_audio_driver_t *dev = (fluid_sdl2_audio_driver_t *) d; if(dev != NULL) { if(dev->devid) { /* Stop audio and close */ SDL_PauseAudioDevice(dev->devid, 1); SDL_CloseAudioDevice(dev->devid); } FLUID_FREE(dev); } } #endif /* SDL2_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_sndmgr.c000066400000000000000000000247671417326347500210330ustar00rootroot00000000000000/* 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; 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 */ void delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t *p) { fluid_sndmgr_audio_driver_t *dev = (fluid_sndmgr_audio_driver_t *) p; fluid_return_if_fail(dev != NULL); if(dev->channel != NULL) { SndDisposeChannel(dev->channel, 1); } if(dev->doubleCallbackProc != NULL) { DisposeRoutineDescriptor(dev->doubleCallbackProc); } if(dev->doubleHeader != NULL) { FLUID_FREE(dev->doubleHeader->dbhBufferPtr[0]); FLUID_FREE(dev->doubleHeader->dbhBufferPtr[1]); FLUID_FREE(dev->doubleHeader); } FLUID_FREE(dev->convbuffers[0]); FLUID_FREE(dev->convbuffers[1]); FLUID_FREE(dev); } /* * 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]; FLUID_MEMSET(left, 0, buffer_size * sizeof(float)); FLUID_MEMSET(right, 0, buffer_size * sizeof(float)); (*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 conversion */ 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-2.2.5/src/drivers/fluid_wasapi.c000066400000000000000000000651151417326347500210150ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * Copyright (C) 2021 Chris Xiong * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include "fluid_synth.h" #include "fluid_adriver.h" #include "fluid_settings.h" #if WASAPI_SUPPORT #define CINTERFACE #define COBJMACROS #include #include #include #include #include #include #include #include #include #include #include // these symbols are either never found in headers, or // only defined but there are no library containing the actual symbol... #ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 #endif #ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY #define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 #endif static const CLSID _CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c, {0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e} }; static const IID _IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35, {0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6} }; static const IID _IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, {0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2} }; static const IID _IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, {0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2} }; /* * WASAPI Driver for fluidsynth * * Current limitations: * - Only one stereo audio output. * - If audio.sample-format is "16bits", a conversion from float * without dithering is used. * * Available settings: * - audio.wasapi.exclusive-mode * 0 = shared mode, 1 = exclusive mode * - audio.wasapi.device * Used for device selection. Leave unchanged (or use the value "default") * to use the default Windows audio device for media playback. * * Notes: * - If exclusive mode is selected, audio.period-size is used as the periodicity * of the IAudioClient stream, which is the sole factor of audio latency. * audio.periods still determines the buffer size, but has no direct impact on * the latency (at least according to Microsoft). The valid range for * audio.period-size may vary depending on the driver and sample rate. * - In shared mode, audio.period-size is completely ignored. Instead, a value * provided by the audio driver is used. In theory this means the latency in * shared mode is out of fluidsynth's control, but you may still increase * audio.periods for a larger buffer to fix buffer underruns in case there * are any. * - The sample rate and sample format of fluidsynth must be supported by the * audio device in exclusive mode. Otherwise driver creation will fail. Use * `fluidsynth ---query-audio-devices` to find out the modes supported by * the soundcards installed on the system. * - In shared mode, if the sample rate of the synth doesn't match what is * configured in the 'advanced' tab of the audio device properties dialog, * Windows will automatically resample the output (obviously). Windows * Vista doesn't seem to support the resampling method with better quality. * - Under Windows 10, this driver may report a latency of 0ms in shared mode. * This is nonsensical and should be disregarded. * */ #define FLUID_WASAPI_MAX_OUTPUTS 1 typedef void(*fluid_wasapi_devenum_callback_t)(IMMDevice *, void *); static DWORD WINAPI fluid_wasapi_audio_run(void *p); static int fluid_wasapi_write_processed_channels(void *data, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]); static void fluid_wasapi_foreach_device(fluid_wasapi_devenum_callback_t callback, void *data); static void fluid_wasapi_register_callback(IMMDevice *dev, void *data); static void fluid_wasapi_finddev_callback(IMMDevice *dev, void *data); static IMMDevice *fluid_wasapi_find_device(IMMDeviceEnumerator *denum, const char *name); static FLUID_INLINE int16_t round_clip_to_i16(float x); typedef struct { const char *name; wchar_t *id; } fluid_wasapi_finddev_data_t; typedef struct { fluid_audio_driver_t driver; void *user_pointer; fluid_audio_func_t func; fluid_audio_channels_callback_t write; float **drybuf; UINT32 nframes; double buffer_duration; int channels_count; int float_samples; HANDLE start_ev; HANDLE thread; DWORD thread_id; HANDLE quit_ev; IAudioClient *aucl; IAudioRenderClient *arcl; double sample_rate; int periods, period_size; fluid_long_long_t buffer_duration_reftime; fluid_long_long_t periods_reftime; fluid_long_long_t latency_reftime; int audio_channels; int sample_size; char *dname; int exclusive; unsigned short sample_format; } fluid_wasapi_audio_driver_t; fluid_audio_driver_t *new_fluid_wasapi_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) { return new_fluid_wasapi_audio_driver2(settings, (fluid_audio_func_t)fluid_synth_process, synth); } fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data) { DWORD ret; HANDLE wait_handles[2]; fluid_wasapi_audio_driver_t *dev = NULL; OSVERSIONINFOEXW vi = {sizeof(vi), 6, 0, 0, 0, {0}, 0, 0, 0, 0, 0}; if(!VerifyVersionInfoW(&vi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL))) { FLUID_LOG(FLUID_ERR, "wasapi: this driver requires Windows Vista or newer."); return NULL; } dev = FLUID_NEW(fluid_wasapi_audio_driver_t); if(dev == NULL) { FLUID_LOG(FLUID_ERR, "wasapi: out of memory."); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_wasapi_audio_driver_t)); /* Retrieve the settings */ fluid_settings_getnum(settings, "synth.sample-rate", &dev->sample_rate); fluid_settings_getint(settings, "audio.periods", &dev->periods); fluid_settings_getint(settings, "audio.period-size", &dev->period_size); fluid_settings_getint(settings, "synth.audio-channels", &dev->audio_channels); fluid_settings_getint(settings, "audio.wasapi.exclusive-mode", &dev->exclusive); if(dev->audio_channels > FLUID_WASAPI_MAX_OUTPUTS) { FLUID_LOG(FLUID_ERR, "wasapi: channel configuration with more than one stereo pair is not supported."); goto cleanup; } if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) { dev->sample_size = sizeof(int16_t); dev->sample_format = WAVE_FORMAT_PCM; } else { dev->sample_size = sizeof(float); dev->sample_format = WAVE_FORMAT_IEEE_FLOAT; } if(fluid_settings_dupstr(settings, "audio.wasapi.device", &dev->dname) != FLUID_OK) { FLUID_LOG(FLUID_ERR, "wasapi: out of memory."); goto cleanup; } dev->func = func; dev->user_pointer = data; dev->buffer_duration = dev->periods * dev->period_size / dev->sample_rate; dev->channels_count = dev->audio_channels * 2; dev->float_samples = (dev->sample_format == WAVE_FORMAT_IEEE_FLOAT); dev->buffer_duration_reftime = (fluid_long_long_t)(dev->buffer_duration * 1e7 + .5); dev->periods_reftime = (fluid_long_long_t)(dev->period_size / dev->sample_rate * 1e7 + .5); dev->quit_ev = CreateEvent(NULL, FALSE, FALSE, NULL); if(dev->quit_ev == NULL) { FLUID_LOG(FLUID_ERR, "wasapi: failed to create quit event: '%s'", fluid_get_windows_error()); goto cleanup; } dev->start_ev = CreateEvent(NULL, FALSE, FALSE, NULL); if(dev->start_ev == NULL) { FLUID_LOG(FLUID_ERR, "wasapi: failed to create start event: '%s'", fluid_get_windows_error()); goto cleanup; } dev->thread = CreateThread(NULL, 0, fluid_wasapi_audio_run, dev, 0, &dev->thread_id); if(dev->thread == NULL) { FLUID_LOG(FLUID_ERR, "wasapi: failed to create audio thread: '%s'", fluid_get_windows_error()); goto cleanup; } /* start event must be first */ wait_handles[0] = dev->start_ev; wait_handles[1] = dev->thread; ret = WaitForMultipleObjects(FLUID_N_ELEMENTS(wait_handles), wait_handles, FALSE, 2000); switch(ret) { case WAIT_OBJECT_0: return &dev->driver; case WAIT_TIMEOUT: FLUID_LOG(FLUID_WARN, "wasapi: initialization timeout!"); break; default: break; } cleanup: delete_fluid_wasapi_audio_driver(&dev->driver); return NULL; } void delete_fluid_wasapi_audio_driver(fluid_audio_driver_t *p) { fluid_wasapi_audio_driver_t *dev = (fluid_wasapi_audio_driver_t *) p; int i; fluid_return_if_fail(dev != NULL); if(dev->thread != NULL) { SetEvent(dev->quit_ev); if(WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0) { FLUID_LOG(FLUID_WARN, "wasapi: couldn't join the audio thread. killing it."); TerminateThread(dev->thread, 0); } CloseHandle(dev->thread); } if(dev->quit_ev != NULL) { CloseHandle(dev->quit_ev); } if(dev->start_ev != NULL) { CloseHandle(dev->start_ev); } if(dev->drybuf) { for(i = 0; i < dev->channels_count; ++i) { FLUID_FREE(dev->drybuf[i]); } } FLUID_FREE(dev->dname); FLUID_FREE(dev->drybuf); FLUID_FREE(dev); } void fluid_wasapi_audio_driver_settings(fluid_settings_t *settings) { fluid_settings_register_int(settings, "audio.wasapi.exclusive-mode", 0, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_str(settings, "audio.wasapi.device", "default", 0); fluid_settings_add_option(settings, "audio.wasapi.device", "default"); fluid_wasapi_foreach_device(fluid_wasapi_register_callback, settings); } static DWORD WINAPI fluid_wasapi_audio_run(void *p) { fluid_wasapi_audio_driver_t *dev = (fluid_wasapi_audio_driver_t *)p; DWORD time_to_sleep; UINT32 pos; DWORD len; void *channels_out[2]; int channels_off[2] = {0, 1}; int channels_incr[2] = {2, 2}; BYTE *pbuf; HRESULT ret; IMMDeviceEnumerator *denum = NULL; IMMDevice *mmdev = NULL; DWORD flags = 0; WAVEFORMATEXTENSIBLE wfx; WAVEFORMATEXTENSIBLE *rwfx = NULL; AUDCLNT_SHAREMODE share_mode; OSVERSIONINFOEXW vi = {sizeof(vi), 6, 0, 0, 0, {0}, 0, 0, 0, 0, 0}; int needs_com_uninit = FALSE; int i; /* Clear format structure */ ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE)); wfx.Format.nChannels = 2; wfx.Format.wBitsPerSample = dev->sample_size * 8; wfx.Format.nBlockAlign = dev->sample_size * wfx.Format.nChannels; wfx.Format.nSamplesPerSec = (DWORD) dev->sample_rate; wfx.Format.nAvgBytesPerSec = (DWORD) dev->sample_rate * wfx.Format.nBlockAlign; wfx.Format.wFormatTag = dev->sample_format; //wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; //wfx.Format.cbSize = 22; //wfx.SubFormat = guid_float; //wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; //wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample; /* initialize COM in a worker thread to avoid a potential double initialization in the callers thread */ ret = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot initialize COM. 0x%x", (unsigned)ret); goto cleanup; } needs_com_uninit = TRUE; ret = CoCreateInstance( &_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &_IID_IMMDeviceEnumerator, (void **)&denum); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot create device enumerator. 0x%x", (unsigned)ret); goto cleanup; } mmdev = fluid_wasapi_find_device(denum, dev->dname); if(mmdev == NULL) { goto cleanup; } ret = IMMDevice_Activate(mmdev, &_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&dev->aucl); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot activate audio client. 0x%x", (unsigned)ret); goto cleanup; } if(dev->exclusive) { share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE; FLUID_LOG(FLUID_DBG, "wasapi: using exclusive mode."); } else { fluid_long_long_t defp; share_mode = AUDCLNT_SHAREMODE_SHARED; FLUID_LOG(FLUID_DBG, "wasapi: using shared mode."); dev->periods_reftime = 0; //use default period size of the device if(SUCCEEDED(IAudioClient_GetDevicePeriod(dev->aucl, &defp, NULL))) { dev->period_size = (int)(defp / 1e7 * dev->sample_rate); dev->buffer_duration = dev->periods * dev->period_size / dev->sample_rate; dev->buffer_duration_reftime = (fluid_long_long_t)(dev->buffer_duration * 1e7 + .5); FLUID_LOG(FLUID_DBG, "wasapi: using device period size: %d", dev->period_size); } } ret = IAudioClient_IsFormatSupported(dev->aucl, share_mode, (const WAVEFORMATEX *)&wfx, (WAVEFORMATEX **)&rwfx); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: device doesn't support the mode we want. 0x%x", (unsigned)ret); goto cleanup; } else if(ret == S_FALSE) { //rwfx is non-null only in this case FLUID_LOG(FLUID_INFO, "wasapi: requested mode cannot be fully satisfied."); if(rwfx->Format.nSamplesPerSec != wfx.Format.nSamplesPerSec) // needs resampling { flags = AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; vi.dwMinorVersion = 1; if(VerifyVersionInfoW(&vi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL))) //IAudioClient::Initialize in Vista fails with E_INVALIDARG if this flag is set { flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; } } CoTaskMemFree(rwfx); } ret = IAudioClient_Initialize(dev->aucl, share_mode, flags, dev->buffer_duration_reftime, dev->periods_reftime, (WAVEFORMATEX *)&wfx, &GUID_NULL); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: failed to initialize audio client. 0x%x", (unsigned)ret); if(ret == AUDCLNT_E_INVALID_DEVICE_PERIOD) { fluid_long_long_t defp, minp; FLUID_LOG(FLUID_ERR, "wasapi: the device period size is invalid."); if(SUCCEEDED(IAudioClient_GetDevicePeriod(dev->aucl, &defp, &minp))) { int defpf = (int)(defp / 1e7 * dev->sample_rate); int minpf = (int)(minp / 1e7 * dev->sample_rate); FLUID_LOG(FLUID_ERR, "wasapi: minimum period is %d, default period is %d. selected %d.", minpf, defpf, dev->period_size); } } goto cleanup; } ret = IAudioClient_GetBufferSize(dev->aucl, &dev->nframes); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get audio buffer size. 0x%x", (unsigned)ret); goto cleanup; } FLUID_LOG(FLUID_DBG, "wasapi: requested %d frames of buffers, got %u.", dev->periods * dev->period_size, dev->nframes); dev->buffer_duration = dev->nframes / dev->sample_rate; time_to_sleep = dev->buffer_duration * 1000 / 2; if(time_to_sleep < 1) { time_to_sleep = 1; } dev->drybuf = FLUID_ARRAY(float *, dev->audio_channels * 2); if(dev->drybuf == NULL) { FLUID_LOG(FLUID_ERR, "wasapi: out of memory"); goto cleanup; } FLUID_MEMSET(dev->drybuf, 0, sizeof(float *) * dev->audio_channels * 2); for(i = 0; i < dev->audio_channels * 2; ++i) { dev->drybuf[i] = FLUID_ARRAY(float, dev->nframes); if(dev->drybuf[i] == NULL) { FLUID_LOG(FLUID_ERR, "wasapi: out of memory"); goto cleanup; } } ret = IAudioClient_GetService(dev->aucl, &_IID_IAudioRenderClient, (void **)&dev->arcl); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get audio render device. 0x%x", (unsigned)ret); goto cleanup; } if(SUCCEEDED(IAudioClient_GetStreamLatency(dev->aucl, &dev->latency_reftime))) { FLUID_LOG(FLUID_DBG, "wasapi: latency: %fms.", dev->latency_reftime / 1e4); } ret = IAudioClient_Start(dev->aucl); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: failed to start audio client. 0x%x", (unsigned)ret); goto cleanup; } /* Signal the success of the driver initialization */ SetEvent(dev->start_ev); for(;;) { ret = IAudioClient_GetCurrentPadding(dev->aucl, &pos); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get buffer padding. 0x%x", (unsigned)ret); goto cleanup; } len = dev->nframes - pos; if(len == 0) { Sleep(0); continue; } ret = IAudioRenderClient_GetBuffer(dev->arcl, len, &pbuf); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get buffer. 0x%x", (unsigned)ret); goto cleanup; } channels_out[0] = channels_out[1] = (void *)pbuf; fluid_wasapi_write_processed_channels(dev, len, 2, channels_out, channels_off, channels_incr); ret = IAudioRenderClient_ReleaseBuffer(dev->arcl, len, 0); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: failed to release buffer. 0x%x", (unsigned)ret); goto cleanup; } if(WaitForSingleObject(dev->quit_ev, time_to_sleep) == WAIT_OBJECT_0) { break; } } cleanup: if(dev->aucl != NULL) { IAudioClient_Stop(dev->aucl); IAudioClient_Release(dev->aucl); } if(dev->arcl != NULL) { IAudioRenderClient_Release(dev->arcl); } if(mmdev != NULL) { IMMDevice_Release(mmdev); } if(denum != NULL) { IMMDeviceEnumerator_Release(denum); } if(needs_com_uninit) { CoUninitialize(); } return 0; } static int fluid_wasapi_write_processed_channels(void *data, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]) { int i, ch; int ret; fluid_wasapi_audio_driver_t *drv = (fluid_wasapi_audio_driver_t *) data; float *optr[FLUID_WASAPI_MAX_OUTPUTS * 2]; int16_t *ioptr[FLUID_WASAPI_MAX_OUTPUTS * 2]; int efx_nch = 0; float **efx_buf = NULL; for(ch = 0; ch < drv->channels_count; ++ch) { FLUID_MEMSET(drv->drybuf[ch], 0, len * sizeof(float)); optr[ch] = (float *)channels_out[ch] + channels_off[ch]; ioptr[ch] = (int16_t *)channels_out[ch] + channels_off[ch]; } if(drv->func == (fluid_audio_func_t)fluid_synth_process) { efx_nch = drv->channels_count; efx_buf = drv->drybuf; } ret = drv->func(drv->user_pointer, len, efx_nch, efx_buf, drv->channels_count, drv->drybuf); for(ch = 0; ch < drv->channels_count; ++ch) { for(i = 0; i < len; ++i) { if(drv->float_samples) { *optr[ch] = drv->drybuf[ch][i]; optr[ch] += channels_incr[ch]; } else //This code is taken from fluid_synth.c. No dithering yet. { *ioptr[ch] = round_clip_to_i16(drv->drybuf[ch][i] * 32766.0f); ioptr[ch] += channels_incr[ch]; } } } return ret; } static void fluid_wasapi_foreach_device(fluid_wasapi_devenum_callback_t callback, void *data) { IMMDeviceEnumerator *denum = NULL; IMMDeviceCollection *dcoll = NULL; UINT cnt, i; HRESULT ret; int com_was_initialized = FALSE; ret = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(FAILED(ret)) { if(ret == RPC_E_CHANGED_MODE) { com_was_initialized = TRUE; FLUID_LOG(FLUID_DBG, "wasapi: COM was already initialized"); } else { FLUID_LOG(FLUID_ERR, "wasapi: cannot initialize COM. 0x%x", (unsigned)ret); return; } } ret = CoCreateInstance( &_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &_IID_IMMDeviceEnumerator, (void **)&denum); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot create device enumerator. 0x%x", (unsigned)ret); goto cleanup; } ret = IMMDeviceEnumerator_EnumAudioEndpoints( denum, eRender, DEVICE_STATE_ACTIVE, &dcoll); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot enumerate audio devices. 0x%x", (unsigned)ret); goto cleanup; } ret = IMMDeviceCollection_GetCount(dcoll, &cnt); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get device count. 0x%x", (unsigned)ret); goto cleanup; } for(i = 0; i < cnt; ++i) { IMMDevice *dev = NULL; ret = IMMDeviceCollection_Item(dcoll, i, &dev); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get device #%u. 0x%x", i, (unsigned)ret); continue; } callback(dev, data); IMMDevice_Release(dev); } cleanup: if(dcoll != NULL) { IMMDeviceCollection_Release(dcoll); } if(denum != NULL) { IMMDeviceEnumerator_Release(denum); } if(!com_was_initialized) { CoUninitialize(); } } static void fluid_wasapi_register_callback(IMMDevice *dev, void *data) { fluid_settings_t *settings = (fluid_settings_t *)data; IPropertyStore *prop = NULL; PROPVARIANT var; int ret; ret = IMMDevice_OpenPropertyStore(dev, STGM_READ, &prop); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get properties of device. 0x%x", (unsigned)ret); return; } PropVariantInit(&var); ret = IPropertyStore_GetValue(prop, &PKEY_Device_FriendlyName, &var); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get friendly name of device. 0x%x", (unsigned)ret); } else { int nsz; char *name; nsz = WideCharToMultiByte(CP_ACP, 0, var.pwszVal, -1, 0, 0, 0, 0); name = FLUID_ARRAY(char, nsz + 1); WideCharToMultiByte(CP_ACP, 0, var.pwszVal, -1, name, nsz, 0, 0); fluid_settings_add_option(settings, "audio.wasapi.device", name); FLUID_FREE(name); } IPropertyStore_Release(prop); PropVariantClear(&var); } static void fluid_wasapi_finddev_callback(IMMDevice *dev, void *data) { fluid_wasapi_finddev_data_t *d = (fluid_wasapi_finddev_data_t *)data; int nsz; char *name = NULL; wchar_t *id = NULL; IPropertyStore *prop = NULL; PROPVARIANT var; HRESULT ret; ret = IMMDevice_OpenPropertyStore(dev, STGM_READ, &prop); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get properties of device. 0x%x", (unsigned)ret); return; } PropVariantInit(&var); ret = IPropertyStore_GetValue(prop, &PKEY_Device_FriendlyName, &var); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get friendly name of device. 0x%x", (unsigned)ret); goto cleanup; } nsz = WideCharToMultiByte(CP_ACP, 0, var.pwszVal, -1, 0, 0, 0, 0); name = FLUID_ARRAY(char, nsz + 1); WideCharToMultiByte(CP_ACP, 0, var.pwszVal, -1, name, nsz, 0, 0); if(!FLUID_STRCASECMP(name, d->name)) { ret = IMMDevice_GetId(dev, &id); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get id of device. 0x%x", (unsigned)ret); goto cleanup; } nsz = wcslen(id); d->id = FLUID_ARRAY(wchar_t, nsz + 1); FLUID_MEMCPY(d->id, id, sizeof(wchar_t) * (nsz + 1)); } cleanup: PropVariantClear(&var); IPropertyStore_Release(prop); CoTaskMemFree(id); FLUID_FREE(name); } static IMMDevice *fluid_wasapi_find_device(IMMDeviceEnumerator *denum, const char *name) { fluid_wasapi_finddev_data_t d; IMMDevice *dev; HRESULT ret; d.name = name; d.id = NULL; if(!FLUID_STRCASECMP(name, "default")) { ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint( denum, eRender, eMultimedia, &dev); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot get default audio device. 0x%x", (unsigned)ret); return NULL; } else { return dev; } } fluid_wasapi_foreach_device(fluid_wasapi_finddev_callback, &d); if(d.id != NULL) { ret = IMMDeviceEnumerator_GetDevice(denum, d.id, &dev); FLUID_FREE(d.id); if(FAILED(ret)) { FLUID_LOG(FLUID_ERR, "wasapi: cannot find device with id. 0x%x", (unsigned)ret); return NULL; } return dev; } else { FLUID_LOG(FLUID_ERR, "wasapi: cannot find device \"%s\".", name); return NULL; } } static FLUID_INLINE int16_t round_clip_to_i16(float x) { long i; if(x >= 0.0f) { i = (long)(x + 0.5f); if(FLUID_UNLIKELY(i > 32767)) { i = 32767; } } else { i = (long)(x - 0.5f); if(FLUID_UNLIKELY(i < -32768)) { i = -32768; } } return (int16_t)i; } #endif /* WASAPI_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_waveout.c000066400000000000000000000464371417326347500212310ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * Copyright (C) 2018 Carlo Bramini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include "fluid_synth.h" #include "fluid_adriver.h" #include "fluid_settings.h" #if WAVEOUT_SUPPORT #include #include /* Those two includes are required on Windows 9x/ME */ #include #include /** * The driver handle multiple channels. * Actually the number maximum of channels is limited to 2 * WAVEOUT_MAX_STEREO_CHANNELS. * The only reason of this limitation is because we dont know how to define the mapping * of speakers for stereo output number above WAVEOUT_MAX_STEREO_CHANNELS. */ /* Maximum number of stereo outputs */ #define WAVEOUT_MAX_STEREO_CHANNELS 4 static char *fluid_waveout_error(MMRESULT hr); static int fluid_waveout_write_processed_channels(fluid_synth_t *data, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]); /* Speakers mapping */ static const DWORD channel_mask_speakers[WAVEOUT_MAX_STEREO_CHANNELS] = { /* 1 stereo output */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, /* 2 stereo outputs */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, /* 3 stereo outputs */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, /* 4 stereo outputs */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT }; typedef struct { fluid_audio_driver_t driver; void *synth; fluid_audio_func_t func; fluid_audio_channels_callback_t write_ptr; float **drybuf; HWAVEOUT hWaveOut; WAVEHDR *waveHeader; int periods; /* numbers of internal driver buffers */ int num_frames; HANDLE hThread; DWORD dwThread; int nQuit; HANDLE hQuit; int channels_count; /* number of channels in audio stream */ } fluid_waveout_audio_driver_t; /* Thread for playing sample buffers */ static DWORD WINAPI fluid_waveout_synth_thread(void *data) { fluid_waveout_audio_driver_t *dev; WAVEHDR *pWave; MSG msg; int code; /* pointers table on output first sample channels */ void *channels_out[WAVEOUT_MAX_STEREO_CHANNELS * 2]; int channels_off[WAVEOUT_MAX_STEREO_CHANNELS * 2]; int channels_incr[WAVEOUT_MAX_STEREO_CHANNELS * 2]; int i; dev = (fluid_waveout_audio_driver_t *)data; /* initialize write callback constant parameters: MME expects interleaved channels in a unique buffer. For example 4 channels (c1, c2, c3, c4) and n samples: { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,... sn:c1, sn:c2, sn:c3, sn:c4 }. So, channels_off[], channnel_incr[] tables should initialized like this: channels_off[0] = 0 channels_incr[0] = 4 channels_off[1] = 1 channels_incr[1] = 4 channels_off[2] = 2 channels_incr[2] = 4 channels_off[3] = 3 channels_incr[3] = 4 channels_out[], table will be initialized later, just before calling the write callback function. channels_out[0] = address of dsound buffer channels_out[1] = address of dsound buffer channels_out[2] = address of dsound buffer channels_out[3] = address of dsound buffer */ for(i = 0; i < dev->channels_count; i++) { channels_off[i] = i; channels_incr[i] = dev->channels_count; } /* Forces creation of message queue */ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); for(;;) { code = GetMessage(&msg, NULL, 0, 0); if(code < 0) { FLUID_LOG(FLUID_ERR, "fluid_waveout_synth_thread: GetMessage() failed: '%s'", fluid_get_windows_error()); break; } if(msg.message == WM_CLOSE) { break; } switch(msg.message) { case MM_WOM_DONE: pWave = (WAVEHDR *)msg.lParam; dev = (fluid_waveout_audio_driver_t *)pWave->dwUser; if(dev->nQuit > 0) { /* Release the sample buffer */ waveOutUnprepareHeader((HWAVEOUT)msg.wParam, pWave, sizeof(WAVEHDR)); if(--dev->nQuit == 0) { SetEvent(dev->hQuit); } } else { /* Before calling write function, finish to initialize channels_out[] table parameter: MME expects interleaved channels in a unique buffer. So, channels_out[] table must be initialized with the address of the same buffer (lpData). */ i = dev->channels_count; do { channels_out[--i] = pWave->lpData; } while(i); dev->write_ptr(dev->func ? (fluid_synth_t*)dev : dev->synth, dev->num_frames, dev->channels_count, channels_out, channels_off, channels_incr); waveOutWrite((HWAVEOUT)msg.wParam, pWave, sizeof(WAVEHDR)); } break; } } return 0; } void fluid_waveout_audio_driver_settings(fluid_settings_t *settings) { UINT n, nDevs = waveOutGetNumDevs(); #ifdef _UNICODE char dev_name[MAXPNAMELEN]; #endif fluid_settings_register_str(settings, "audio.waveout.device", "default", 0); fluid_settings_add_option(settings, "audio.waveout.device", "default"); for(n = 0; n < nDevs; n++) { WAVEOUTCAPS caps; MMRESULT res; res = waveOutGetDevCaps(n, &caps, sizeof(caps)); if(res == MMSYSERR_NOERROR) { #ifdef _UNICODE WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, dev_name, MAXPNAMELEN, 0, 0); FLUID_LOG(FLUID_DBG, "Testing audio device: %s", dev_name); fluid_settings_add_option(settings, "audio.waveout.device", dev_name); #else FLUID_LOG(FLUID_DBG, "Testing audio device: %s", caps.szPname); fluid_settings_add_option(settings, "audio.waveout.device", caps.szPname); #endif } } } /* * new_fluid_waveout_audio_driver * The driver handle the case of multiple stereo buffers provided by fluidsynth * mixer. * Each stereo buffers (left, right) are written to respective channels pair * of the audio device card. * For example, if the number of internal mixer buffer is 2, the audio device * must have at least 4 channels: * - buffer 0 (left, right) will be written to channel pair (0, 1). * - buffer 1 (left, right) will be written to channel pair (2, 3). * * @param setting. The settings the driver looks for: * "synth.sample-rate", the sample rate. * "synth.audio-channels", the number of internal mixer stereo buffer. * "audio.periods",the number of buffers. * "audio.period-size",the size of one buffer. * "audio.sample-format",the sample format, 16bits or float. * * @param synth, fluidsynth synth instance to associate to the driver. * * Note: The number of internal mixer stereo buffer is indicated by "synth.audio-channels". * If the audio device cannot handle the format or do not have enough channels, * the driver fails and return NULL. */ fluid_audio_driver_t * new_fluid_waveout_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) { return new_fluid_waveout_audio_driver2(settings, NULL, synth); } fluid_audio_driver_t * new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data) { fluid_waveout_audio_driver_t *dev = NULL; fluid_audio_channels_callback_t write_ptr; double sample_rate; int frequency, sample_size; int periods, period_size; int audio_channels; LPSTR ptrBuffer; int lenBuffer; int device; int i; WAVEFORMATEXTENSIBLE wfx; char dev_name[MAXPNAMELEN]; MMRESULT errCode; /* Retrieve the settings */ fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); fluid_settings_getint(settings, "audio.periods", &periods); fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getint(settings, "synth.audio-channels", &audio_channels); /* Clear format structure */ ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE)); /* check the format */ if(fluid_settings_str_equal(settings, "audio.sample-format", "float") || func) { FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format"); sample_size = sizeof(float); write_ptr = func ? fluid_waveout_write_processed_channels : fluid_synth_write_float_channels; wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; } else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) { FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format"); sample_size = sizeof(short); write_ptr = fluid_synth_write_s16_channels; wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfx.Format.wFormatTag = WAVE_FORMAT_PCM; } else { FLUID_LOG(FLUID_ERR, "Unhandled sample format"); return NULL; } /* Set frequency to integer */ frequency = (int)sample_rate; /* Initialize the format structure */ wfx.Format.nChannels = audio_channels * 2; if(audio_channels > WAVEOUT_MAX_STEREO_CHANNELS) { FLUID_LOG(FLUID_ERR, "Channels number %d exceed internal limit %d", wfx.Format.nChannels, WAVEOUT_MAX_STEREO_CHANNELS * 2); return NULL; } wfx.Format.wBitsPerSample = sample_size * 8; wfx.Format.nBlockAlign = sample_size * wfx.Format.nChannels; wfx.Format.nSamplesPerSec = frequency; wfx.Format.nAvgBytesPerSec = frequency * wfx.Format.nBlockAlign; /* WAVEFORMATEXTENSIBLE extension is used only when channels number is above 2. When channels number is below 2, only WAVEFORMATEX structure will be used by the Windows driver. This ensures compatibility with Windows 9X/NT in the case these versions does not accept the WAVEFORMATEXTENSIBLE structure. */ if(wfx.Format.nChannels > 2) { wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfx.Format.cbSize = 22; wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample; wfx.dwChannelMask = channel_mask_speakers[audio_channels - 1]; } /* allocate the internal waveout buffers: The length of a single buffer in bytes is dependent of period_size. */ lenBuffer = wfx.Format.nBlockAlign * period_size; /* create and clear the driver data */ dev = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(fluid_waveout_audio_driver_t) + lenBuffer * periods + sizeof(WAVEHDR) * periods); if(dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } /* Assign extra memory to WAVEHDR */ dev->waveHeader = (WAVEHDR *)((uintptr_t)(dev + 1) + lenBuffer * periods); /* Save copy of synth */ dev->synth = data; dev->func = func; /* Save copy of other variables */ dev->write_ptr = write_ptr; /* number of frames in a buffer */ dev->num_frames = period_size; /* number of buffers */ dev->periods = periods; dev->channels_count = wfx.Format.nChannels; /* Set default device to use */ device = WAVE_MAPPER; if(func) { /* allocate extra buffer used by fluid_waveout_write_processed_channels(). These buffers are buffer adaptation between the rendering API fluid_synth_process() and the waveout internal buffers Note: the size (in bytes) of these extra buffer (drybuf[]) must be the same that the size of internal waveout buffers. */ dev->drybuf = FLUID_ARRAY(float*, audio_channels * 2); if(dev->drybuf == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); delete_fluid_waveout_audio_driver(&dev->driver); return NULL; } FLUID_MEMSET(dev->drybuf, 0, sizeof(float*) * audio_channels * 2); for(i = 0; i < audio_channels * 2; ++i) { /* The length of a single buffer drybuf[i] is dependent of period_size */ dev->drybuf[i] = FLUID_ARRAY(float, period_size); if(dev->drybuf[i] == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); delete_fluid_waveout_audio_driver(&dev->driver); return NULL; } } } /* get the selected device name. if none is specified, use default device. */ if(fluid_settings_copystr(settings, "audio.waveout.device", dev_name, MAXPNAMELEN) == FLUID_OK && dev_name[0] != '\0') { UINT nDevs = waveOutGetNumDevs(); UINT n; #ifdef _UNICODE WCHAR lpwDevName[MAXPNAMELEN]; MultiByteToWideChar(CP_UTF8, 0, dev_name, -1, lpwDevName, MAXPNAMELEN); #endif for(n = 0; n < nDevs; n++) { WAVEOUTCAPS caps; MMRESULT res; res = waveOutGetDevCaps(n, &caps, sizeof(caps)); if(res == MMSYSERR_NOERROR) { #ifdef _UNICODE if(wcsicmp(lpwDevName, caps.szPname) == 0) #else if(FLUID_STRCASECMP(dev_name, caps.szPname) == 0) #endif { FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %s", dev_name); device = n; break; } } } } do { dev->hQuit = CreateEvent(NULL, FALSE, FALSE, NULL); if(dev->hQuit == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create quit event: '%s'", fluid_get_windows_error()); break; } /* Create thread which processes re-adding SYSEX buffers */ dev->hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) fluid_waveout_synth_thread, dev, 0, &dev->dwThread); if(dev->hThread == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create waveOut thread: '%s'", fluid_get_windows_error()); break; } errCode = waveOutOpen(&dev->hWaveOut, device, (WAVEFORMATEX *)&wfx, (DWORD_PTR)dev->dwThread, 0, CALLBACK_THREAD); if(errCode != MMSYSERR_NOERROR) { FLUID_LOG(FLUID_ERR, "Failed to open waveOut device: '%s'", fluid_waveout_error(errCode)); break; } /* Get pointer to sound buffer memory */ ptrBuffer = (LPSTR)(dev + 1); /* Setup the sample buffers */ for(i = 0; i < periods; i++) { /* Clear the sample buffer */ memset(ptrBuffer, 0, lenBuffer); /* Clear descriptor buffer */ memset(dev->waveHeader + i, 0, sizeof(WAVEHDR)); /* Compile descriptor buffer */ dev->waveHeader[i].lpData = ptrBuffer; dev->waveHeader[i].dwBufferLength = lenBuffer; dev->waveHeader[i].dwUser = (DWORD_PTR)dev; waveOutPrepareHeader(dev->hWaveOut, &dev->waveHeader[i], sizeof(WAVEHDR)); ptrBuffer += lenBuffer; } /* Play the sample buffers */ for(i = 0; i < periods; i++) { waveOutWrite(dev->hWaveOut, &dev->waveHeader[i], sizeof(WAVEHDR)); } return (fluid_audio_driver_t *) dev; } while(0); delete_fluid_waveout_audio_driver(&dev->driver); return NULL; } void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *d) { int i; fluid_waveout_audio_driver_t *dev = (fluid_waveout_audio_driver_t *) d; fluid_return_if_fail(dev != NULL); /* release all the allocated resources */ if(dev->hWaveOut != NULL) { dev->nQuit = dev->periods; WaitForSingleObject(dev->hQuit, INFINITE); waveOutClose(dev->hWaveOut); } if(dev->hThread != NULL) { PostThreadMessage(dev->dwThread, WM_CLOSE, 0, 0); WaitForSingleObject(dev->hThread, INFINITE); CloseHandle(dev->hThread); } if(dev->hQuit != NULL) { CloseHandle(dev->hQuit); } if(dev->drybuf != NULL) { for(i = 0; i < dev->channels_count; ++i) { FLUID_FREE(dev->drybuf[i]); } } FLUID_FREE(dev->drybuf); HeapFree(GetProcessHeap(), 0, dev); } static int fluid_waveout_write_processed_channels(fluid_synth_t *data, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]) { int i, ch; int ret; fluid_waveout_audio_driver_t *drv = (fluid_waveout_audio_driver_t*) data; float *optr[WAVEOUT_MAX_STEREO_CHANNELS * 2]; for(ch = 0; ch < drv->channels_count; ++ch) { FLUID_MEMSET(drv->drybuf[ch], 0, len * sizeof(float)); optr[ch] = (float*)channels_out[ch] + channels_off[ch]; } ret = drv->func(drv->synth, len, 0, NULL, drv->channels_count, drv->drybuf); for(ch = 0; ch < drv->channels_count; ++ch) { for(i = 0; i < len; ++i) { *optr[ch] = drv->drybuf[ch][i]; optr[ch] += channels_incr[ch]; } } return ret; } static char *fluid_waveout_error(MMRESULT hr) { char *s = "Don't know why"; switch(hr) { case MMSYSERR_NOERROR: s = "The operation completed successfully :)"; break; case MMSYSERR_ALLOCATED: s = "Specified resource is already allocated."; break; case MMSYSERR_BADDEVICEID: s = "Specified device identifier is out of range"; break; case MMSYSERR_NODRIVER: s = "No device driver is present"; break; case MMSYSERR_NOMEM: s = "Unable to allocate or lock memory"; break; case WAVERR_BADFORMAT: s = "Attempted to open with an unsupported waveform-audio format"; break; case WAVERR_SYNC: s = "The device is synchronous but waveOutOpen was called without using the WAVE_ALLOWSYNC flag"; break; } return s; } #endif /* WAVEOUT_SUPPORT */ fluidsynth-2.2.5/src/drivers/fluid_winmidi.c000066400000000000000000000503721417326347500211700ustar00rootroot00000000000000/* 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. Lame-o API! :( * * Multiple/single devices handling capabilities: * This driver is able to handle multiple devices chosen by the user through * the settings midi.winmidi.device. * For example, let the following device names: * 0:Port MIDI SB Live! [CE00], 1:SB PCI External MIDI, default, x[;y;z;..] * Then the driver is able receive MIDI messages coming from distinct devices * and forward these messages on distinct MIDI channels set. * 1.1)For example, if the user chooses 2 devices at index 0 and 1, the user * must specify this by putting the name "0;1" in midi.winmidi.device setting. * We get a fictif device composed of real devices (0,1). This fictif device * behaves like a device with 32 MIDI channels whose messages are forwarded to * driver output as this: * - MIDI messages from real device 0 are output to MIDI channels set 0 to 15. * - MIDI messages from real device 1 are output to MIDI channels set 15 to 31. * * 1.2)Now another example with the name "1;0". The driver will forward * MIDI messages as this: * - MIDI messages from real device 1 are output to MIDI channels set 0 to 15. * - MIDI messages from real device 0 are output to MIDI channels set 15 to 31. * So, the device order specified in the setting allows the user to choose the * MIDI channel set associated with this real device at the driver output * according this formula: output_channel = input_channel + device_order * 16. * * 2)Note also that the driver handles single device by putting the device name * in midi.winmidi.device setting. * The user can set the device name "0:Port MIDI SB Live! [CE00]" in the setting. * or use the multi device naming "0" (specifying only device index 0). * Both naming choice allows the driver to handle the same single device. * */ #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_winmidi_driver fluid_winmidi_driver_t; /* device infos structure for only one midi device */ typedef struct device_infos { fluid_winmidi_driver_t *dev; /* driver structure*/ unsigned char midi_num; /* device order number */ unsigned char channel_map; /* MIDI channel mapping from input to output */ UINT dev_idx; /* device index */ HMIDIIN hmidiin; /* device handle */ /* MIDI HDR for SYSEX buffer */ MIDIHDR sysExHdrs[MIDI_SYSEX_BUF_COUNT]; /* Sysex data buffer */ unsigned char sysExBuf[MIDI_SYSEX_BUF_COUNT * MIDI_SYSEX_MAX_SIZE]; } device_infos_t; /* driver structure */ struct fluid_winmidi_driver { fluid_midi_driver_t driver; /* Thread for SYSEX re-add thread */ HANDLE hThread; DWORD dwThread; /* devices information table */ int dev_count; /* device information count in dev_infos[] table */ device_infos_t dev_infos[1]; }; #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) static char * fluid_winmidi_input_error(char *strError, MMRESULT no) { #ifdef _UNICODE WCHAR wStr[MAXERRORLENGTH]; midiInGetErrorText(no, wStr, MAXERRORLENGTH); WideCharToMultiByte(CP_UTF8, 0, wStr, -1, strError, MAXERRORLENGTH, 0, 0); #else midiInGetErrorText(no, strError, MAXERRORLENGTH); #endif return strError; } /* callback function called by any MIDI device sending a MIDI message. @param dwInstance, pointer on device_infos structure of this device. */ static void CALLBACK fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { device_infos_t *dev_infos = (device_infos_t *) dwInstance; fluid_winmidi_driver_t *dev = dev_infos->dev; fluid_midi_event_t event; LPMIDIHDR pMidiHdr; unsigned char *data; unsigned int msg_param = (unsigned int) dwParam1; switch(wMsg) { case MIM_OPEN: break; case MIM_CLOSE: break; case MIM_DATA: event.type = msg_type(msg_param); event.channel = msg_chan(msg_param) + dev_infos->channel_map; FLUID_LOG(FLUID_DBG, "\ndevice at index %d sending MIDI message on channel %d, forwarded on channel: %d", dev_infos->dev_idx, msg_chan(msg_param), event.channel); 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 */ FLUID_LOG(FLUID_DBG, "\ndevice at index %d sending MIDI sysex message", dev_infos->dev_idx); if(dev->hThread == NULL) { break; } pMidiHdr = (LPMIDIHDR)dwParam1; data = (unsigned char *)(pMidiHdr->lpData); /* We only process complete SYSEX messages (discard those that are too small or too large) */ if(pMidiHdr->dwBytesRecorded > 2 && data[0] == 0xF0 && data[pMidiHdr->dwBytesRecorded - 1] == 0xF7) { fluid_midi_event_set_sysex(&event, pMidiHdr->lpData + 1, pMidiHdr->dwBytesRecorded - 2, FALSE); (*dev->driver.handler)(dev->driver.data, &event); } /* request the sysex thread to re-add this buffer into the device dev_infos->midi_num */ PostThreadMessage(dev->dwThread, MM_MIM_LONGDATA, dev_infos->midi_num, dwParam1); break; case MIM_ERROR: break; case MIM_LONGERROR: break; case MIM_MOREDATA: break; } } /** * build a device name prefixed by its index. The format of the returned * name is: dev_idx:dev_name * The name returned is convenient for midi.winmidi.device setting. * It allows the user to identify a device index through its name or vice * versa. This allows the user to specify a multi device name using a list of * devices index (see fluid_winmidi_midi_driver_settings()). * * @param dev_idx, device index. * @param dev_name, name of the device. * @return the new device name (that must be freed when finish with it) or * NULL if memory allocation error. */ static char *fluid_winmidi_get_device_name(int dev_idx, char *dev_name) { char *new_dev_name; int i = dev_idx; size_t size = 0; /* index size */ do { size++; i = i / 10 ; } while(i); /* index size + separator + name length + zero termination */ new_dev_name = FLUID_MALLOC(size + 2 + FLUID_STRLEN(dev_name)); if(new_dev_name) { /* the name is filled if allocation is successful */ FLUID_SPRINTF(new_dev_name, "%d:%s", dev_idx, dev_name); } else { FLUID_LOG(FLUID_ERR, "Out of memory"); } return new_dev_name; } /* Add setting midi.winmidi.device in the settings. MIDI devices names are enumerated and added to midi.winmidi.device setting options. Example: 0:Port MIDI SB Live! [CE00], 1:SB PCI External MIDI, default, x[;y;z;..] Devices name prefixed by index (i.e 1:SB PCI External MIDI) are real devices. "default" name is the default device. "x[;y;z;..]" is the multi device naming. Its purpose is to indicate to the user how he must specify a multi device name in the setting. A multi devices name must be a list of real devices index separated by semicolon: Example: "5;3;0" */ void fluid_winmidi_midi_driver_settings(fluid_settings_t *settings) { MMRESULT res; MIDIINCAPS in_caps; UINT i, num; /* register midi.winmidi.device */ fluid_settings_register_str(settings, "midi.winmidi.device", "default", 0); num = midiInGetNumDevs(); if(num > 0) { fluid_settings_add_option(settings, "midi.winmidi.device", "default"); /* add real devices names in options list */ for(i = 0; i < num; i++) { res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS)); if(res == MMSYSERR_NOERROR) { /* add new device name (prefixed by its index) */ char *new_dev_name = fluid_winmidi_get_device_name(i, in_caps.szPname); if(!new_dev_name) { break; } fluid_settings_add_option(settings, "midi.winmidi.device", new_dev_name); FLUID_FREE(new_dev_name); } } } } /* Thread for re-adding SYSEX buffers */ static DWORD WINAPI fluid_winmidi_add_sysex_thread(void *data) { fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *)data; MSG msg; int code; for(;;) { code = GetMessage(&msg, NULL, 0, 0); if(code < 0) { FLUID_LOG(FLUID_ERR, "fluid_winmidi_add_sysex_thread: GetMessage() failed."); break; } if(msg.message == WM_CLOSE) { break; } switch(msg.message) { case MM_MIM_LONGDATA: /* re-add the buffer into the device designed by msg.wParam parameter */ midiInAddBuffer(dev->dev_infos[msg.wParam].hmidiin, (LPMIDIHDR)msg.lParam, sizeof(MIDIHDR)); break; } } return 0; } /** * Parse device name * @param dev if not NULL pointer on driver structure in which device index * are returned. * @param dev_name device name which is expected to be: * - a multi devices naming (i.e "1;0;2") or * - a single device name (i.e "0:Port MIDI SB Live! [CE00]" * @return count of devices parsed or 0 if device name doesn't exist. */ static int fluid_winmidi_parse_device_name(fluid_winmidi_driver_t *dev, char *dev_name) { int dev_count = 0; /* device count */ int dev_idx; /* device index */ char *cur_idx, *next_idx; /* current and next ascii index pointer */ char cpy_dev_name[MAXPNAMELEN]; int num = midiInGetNumDevs(); /* get number of real devices installed */ /* look for a multi device naming */ /* multi devices name "x;[y;..]". parse devices index: x;y;.. Each ascii index are separated by a semicolon character. */ FLUID_STRCPY(cpy_dev_name, dev_name); /* fluid_strtok() will overwrite */ next_idx = cpy_dev_name; while(NULL != (cur_idx = fluid_strtok(&next_idx, " ;"))) { /* try to convert current ascii index */ char *end_idx = cur_idx; dev_idx = FLUID_STRTOL(cur_idx, &end_idx, 10); if(cur_idx == end_idx /* not an integer number */ || dev_idx < 0 /* invalid device index */ || dev_idx >= num /* invalid device index */ ) { if(dev) { dev->dev_count = 0; } dev_count = 0; /* error, end of parsing */ break; } /* memorize device index in dev_infos table */ if(dev) { dev->dev_infos[dev->dev_count++].dev_idx = dev_idx; } dev_count++; } /* look for single device if multi devices not found */ if(!dev_count) { /* default device index: dev_idx = 0, dev_count = 1 */ dev_count = 1; dev_idx = 0; if(FLUID_STRCASECMP("default", dev_name) != 0) { int i; dev_count = 0; /* reset count of devices found */ for(i = 0; i < num; i++) { char strError[MAXERRORLENGTH]; MIDIINCAPS in_caps; MMRESULT res; res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS)); if(res == MMSYSERR_NOERROR) { int str_cmp_res; char *new_dev_name = fluid_winmidi_get_device_name(i, in_caps.szPname); if(!new_dev_name) { break; } #ifdef _UNICODE WCHAR wDevName[MAXPNAMELEN]; MultiByteToWideChar(CP_UTF8, 0, dev_name, -1, wDevName, MAXPNAMELEN); str_cmp_res = wcsicmp(wDevName, new_dev_name); #else str_cmp_res = FLUID_STRCASECMP(dev_name, new_dev_name); #endif FLUID_LOG(FLUID_DBG, "Testing midi device \"%s\"", new_dev_name); FLUID_FREE(new_dev_name); if(str_cmp_res == 0) { FLUID_LOG(FLUID_DBG, "Selected midi device number: %u", i); dev_idx = i; dev_count = 1; break; } } else { FLUID_LOG(FLUID_DBG, "Error testing midi device %u of %u: %s (error %d)", i, num, fluid_winmidi_input_error(strError, res), res); } } } if(dev && dev_count) { dev->dev_infos[0].dev_idx = dev_idx; dev->dev_count = 1; } } if(num < dev_count) { FLUID_LOG(FLUID_ERR, "not enough MIDI in devices found. Expected:%d found:%d", dev_count, num); dev_count = 0; } return dev_count; } /* * 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; MMRESULT res; int i, j; int max_devices; /* maximum number of devices to handle */ char strError[MAXERRORLENGTH]; char dev_name[MAXPNAMELEN]; /* not much use doing anything */ if(handler == NULL) { FLUID_LOG(FLUID_ERR, "Invalid argument"); return NULL; } /* get the device name. if none is specified, use the default device. */ if(fluid_settings_copystr(settings, "midi.winmidi.device", dev_name, MAXPNAMELEN) != FLUID_OK) { FLUID_LOG(FLUID_DBG, "No MIDI in device selected, using \"default\""); FLUID_STRCPY(dev_name, "default"); } /* parse device name, get the maximum number of devices to handle */ max_devices = fluid_winmidi_parse_device_name(NULL, dev_name); /* check if any device has be found */ if(!max_devices) { FLUID_LOG(FLUID_ERR, "Device \"%s\" does not exists", dev_name); return NULL; } /* allocation of driver structure size dependent of max_devices */ i = sizeof(fluid_winmidi_driver_t) + (max_devices - 1) * sizeof(device_infos_t); dev = FLUID_MALLOC(i); if(dev == NULL) { return NULL; } FLUID_MEMSET(dev, 0, i); /* reset structure members */ /* parse device name, get devices index */ fluid_winmidi_parse_device_name(dev, dev_name); dev->driver.handler = handler; dev->driver.data = data; /* try opening the devices */ for(i = 0; i < dev->dev_count; i++) { device_infos_t *dev_infos = &dev->dev_infos[i]; dev_infos->dev = dev; /* driver structure */ dev_infos->midi_num = i; /* device order number */ dev_infos->channel_map = i * 16; /* map from input to output */ FLUID_LOG(FLUID_DBG, "opening device at index %d", dev_infos->dev_idx); res = midiInOpen(&dev_infos->hmidiin, dev_infos->dev_idx, (DWORD_PTR) fluid_winmidi_callback, (DWORD_PTR) dev_infos, CALLBACK_FUNCTION); if(res != MMSYSERR_NOERROR) { FLUID_LOG(FLUID_ERR, "Couldn't open MIDI input: %s (error %d)", fluid_winmidi_input_error(strError, res), res); goto error_recovery; } /* Prepare and add SYSEX buffers */ for(j = 0; j < MIDI_SYSEX_BUF_COUNT; j++) { MIDIHDR *hdr = &dev_infos->sysExHdrs[j]; hdr->lpData = (LPSTR)&dev_infos->sysExBuf[j * MIDI_SYSEX_MAX_SIZE]; hdr->dwBufferLength = MIDI_SYSEX_MAX_SIZE; /* Prepare a buffer for SYSEX data and add it */ res = midiInPrepareHeader(dev_infos->hmidiin, hdr, sizeof(MIDIHDR)); if(res == MMSYSERR_NOERROR) { res = midiInAddBuffer(dev_infos->hmidiin, hdr, sizeof(MIDIHDR)); if(res != MMSYSERR_NOERROR) { FLUID_LOG(FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)", fluid_winmidi_input_error(strError, res), res); midiInUnprepareHeader(dev_infos->hmidiin, hdr, sizeof(MIDIHDR)); } } else FLUID_LOG(FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)", fluid_winmidi_input_error(strError, res), res); } } /* Create thread which processes re-adding SYSEX buffers */ dev->hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) fluid_winmidi_add_sysex_thread, dev, 0, &dev->dwThread); if(dev->hThread == NULL) { FLUID_LOG(FLUID_ERR, "Failed to create SYSEX buffer processing thread"); goto error_recovery; } /* Start the MIDI input interface */ for(i = 0; i < dev->dev_count; i++) { if(midiInStart(dev->dev_infos[i].hmidiin) != MMSYSERR_NOERROR) { FLUID_LOG(FLUID_ERR, "Failed to start the MIDI input. MIDI input not available."); goto error_recovery; } } return (fluid_midi_driver_t *) dev; error_recovery: delete_fluid_winmidi_driver((fluid_midi_driver_t *) dev); return NULL; } /* * delete_fluid_winmidi_driver */ void delete_fluid_winmidi_driver(fluid_midi_driver_t *p) { int i, j; fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *) p; fluid_return_if_fail(dev != NULL); /* request the sysex thread to terminate */ if(dev->hThread != NULL) { PostThreadMessage(dev->dwThread, WM_CLOSE, 0, 0); WaitForSingleObject(dev->hThread, INFINITE); CloseHandle(dev->hThread); dev->hThread = NULL; } /* stop MIDI in devices and free allocated buffers */ for(i = 0; i < dev->dev_count; i++) { device_infos_t *dev_infos = &dev->dev_infos[i]; if(dev_infos->hmidiin != NULL) { /* stop the device and mark any pending data blocks as being done */ midiInReset(dev_infos->hmidiin); /* free allocated buffers associated to this device */ for(j = 0; j < MIDI_SYSEX_BUF_COUNT; j++) { MIDIHDR *hdr = &dev_infos->sysExHdrs[j]; if((hdr->dwFlags & MHDR_PREPARED)) { midiInUnprepareHeader(dev_infos->hmidiin, hdr, sizeof(MIDIHDR)); } } /* close the device */ midiInClose(dev_infos->hmidiin); } } FLUID_FREE(dev); } #endif /* WINMIDI_SUPPORT */ fluidsynth-2.2.5/src/fluid_wasapi_device_enumerate.c000066400000000000000000000066621417326347500227250ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2021 Chris Xiong 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" #include static char **devs; static int flag; static void devenumcb(void *p, const char *s, const char *opt) { int *c = (int*) p; printf(" %s\n", opt); devs[*c] = malloc(strlen(opt) + 1); strcpy(devs[*c], opt); ++ *c; } static void eatlog(int lvl,const char *m, void* d) { flag = lvl; } void fluid_wasapi_device_enumerate(void) { static const int sample_rates[] = {8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 0}; static const char *sample_formats[3] = {"16bits", "float", "\0"}; int e, d, s, f, i, devcnt; fluid_synth_t *synth; fluid_audio_driver_t *adriver; fluid_settings_t *settings = new_fluid_settings(); fluid_settings_setstr(settings, "audio.driver", "wasapi"); devcnt = fluid_settings_option_count(settings, "audio.wasapi.device"); devs = calloc(devcnt, sizeof(char*)); puts("Available audio devices:"); devcnt = 0; fluid_settings_foreach_option(settings, "audio.wasapi.device", &devcnt, devenumcb); fluid_set_log_function(FLUID_INFO, eatlog, NULL); fluid_set_log_function(FLUID_WARN, eatlog, NULL); fluid_set_log_function(FLUID_ERR, eatlog, NULL); assert(devcnt == fluid_settings_option_count(settings, "audio.wasapi.device")); puts(""); for (e = 0; e < 2; ++e) { puts(e ? "Exclusive mode:" : "Shared mode:"); fluid_settings_setint(settings, "audio.wasapi.exclusive-mode", e); for (d = 0; d < devcnt; ++d) { printf("\t%s\n", devs[d]); fluid_settings_setstr(settings, "audio.wasapi.device", devs[d]); for (s = 0; sample_rates[s]; ++s) { fluid_settings_setnum(settings, "synth.sample-rate", sample_rates[s]); for (f = 0; sample_formats[f][0]; ++f) { int n, supported, c; fluid_settings_setstr(settings, "audio.sample-format", sample_formats[f]); flag = 0; synth = new_fluid_synth(settings); adriver = new_fluid_audio_driver(settings,synth); supported = adriver != NULL; delete_fluid_audio_driver(adriver); delete_fluid_synth(synth); n = printf("\t %dHz, %s ", sample_rates[s], sample_formats[f]); for (c = 50 - n; c; --c) { putchar('.'); } printf(" %s%s\n", supported ? "OK" : "FAILED", flag == FLUID_WARN ? "(W)" : flag == FLUID_INFO ? "(I)" : "\0"); } } puts(""); } } puts("OK : Mode is natively supported by the audio device."); puts("OK(I) : Mode is supported, but resampling may occur deep within WASAPI to satisfy device's needs."); puts("FAILED: Mode is not supported."); delete_fluid_settings(settings); for (i = 0; i < devcnt; ++i) { free(devs[i]); } free(devs); } fluidsynth-2.2.5/src/fluidsynth.c000066400000000000000000001105721417326347500170570ustar00rootroot00000000000000/* 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 !defined(WIN32) && !defined(MACINTOSH) #define _GNU_SOURCE #endif #if defined(HAVE_GETOPT_H) #include #define GETOPT_SUPPORT 1 #endif #ifdef LIBINSTPATCH_SUPPORT #include #endif #include "fluid_lash.h" #ifdef SYSTEMD_SUPPORT #include #endif #if SDL2_SUPPORT #include #endif void print_usage(void); void print_help(fluid_settings_t *settings); void print_welcome(void); void print_configure(void); void fluid_wasapi_device_enumerate(void); /* * the globals */ 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 */ int 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(FLUID_STRCMP(optarg, "help") == 0) { option_help = 1; return FLUID_OK; } if(FLUID_STRCMP(optarg, "") == 0) { fprintf(stderr, "Invalid -o option (name part is empty)\n"); return FLUID_FAILED; } switch(fluid_settings_get_type(settings, optarg)) { case FLUID_NUM_TYPE: if(fluid_settings_setnum(settings, optarg, atof(val)) != FLUID_OK) { fprintf(stderr, "Failed to set floating point parameter '%s'\n", optarg); return FLUID_FAILED; } break; case FLUID_INT_TYPE: if(fluid_settings_get_hints(settings, optarg, &hints) == FLUID_OK && hints & FLUID_HINT_TOGGLED) { if(FLUID_STRCASECMP(val, "yes") == 0 || FLUID_STRCASECMP(val, "true") == 0 || FLUID_STRCASECMP(val, "t") == 0) { ival = 1; } else { ival = atoi(val); } } else { ival = atoi(val); } if(fluid_settings_setint(settings, optarg, ival) != FLUID_OK) { fprintf(stderr, "Failed to set integer parameter '%s'\n", optarg); return FLUID_FAILED; } break; case FLUID_STR_TYPE: if(fluid_settings_setstr(settings, optarg, val) != FLUID_OK) { fprintf(stderr, "Failed to set string parameter '%s'\n", optarg); return FLUID_FAILED; } break; default: fprintf(stderr, "Setting parameter '%s' not found\n", optarg); return FLUID_FAILED; } return FLUID_OK; } static void print_pretty_int(int i) { if(i == INT_MAX) { printf("MAXINT"); } else if(i == INT_MIN) { printf("MININT"); } else { printf("%d", i); } } #ifdef WIN32 /* Function using win32 api to convert ANSI encoding string to UTF8 encoding string */ static char* win32_ansi_to_utf8(const char* ansi_null_terminated_string) { LPWSTR u16_buf = NULL; char *u8_buf = NULL; fluid_return_val_if_fail(ansi_null_terminated_string != NULL, NULL); do { int u16_count, u8_byte_count; u16_count = MultiByteToWideChar(CP_ACP, 0, ansi_null_terminated_string, -1, NULL, 0); if (u16_count == 0) { fprintf(stderr, "Failed to convert ANSI string to wide char string\n"); break; } u16_buf = malloc(u16_count * sizeof(WCHAR)); if (u16_buf == NULL) { fprintf(stderr, "Out of memory\n"); break; } u16_count = MultiByteToWideChar(CP_ACP, 0, ansi_null_terminated_string, -1, u16_buf, u16_count); u8_byte_count = WideCharToMultiByte(CP_UTF8, 0, u16_buf, u16_count, NULL, 0, NULL, NULL); u8_buf = malloc(u8_byte_count); if (u8_buf == NULL) { fprintf(stderr, "Out of memory\n"); break; } WideCharToMultiByte(CP_UTF8, 0, u16_buf, u16_count, u8_buf, u8_byte_count, NULL, NULL); } while (0); free(u16_buf); return u8_buf; } #endif 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, const char *name, const 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, const 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); fluid_settings_getnum_default(settings, name, &ddef); 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); fluid_settings_getint_default(settings, name, &idef); fluid_settings_get_hints(settings, name, &hints); 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); fluid_settings_getstr_default(settings, name, &defstr); 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); } /* * main * Process initialization steps in the following order: 1)creating the settings. 2)reading/setting all options in command line. 3)read configuration file the first time and execute all "set" commands 4)creating the synth. 5)loading the soundfonts specified in command line (multiple soundfonts loading is possible). 6)loading a default soundfont if no soundfont are supplied. 7)create the router. 8)create the midi driver connected to the router. 9)create a player and add it any midifile specified in command line. (multiple midifiles loading is possible). 10)create a command handler. 11)reading the entire configuration file for the second time and submit it to the command handler before starting the player. 12)Start the player. 13)create a tcp shell if any requested. 14)entering fast rendering loop if requested, otherwise 15)create the audio driver (i.e synthesis thread) and a synchronous user shell if interactive. */ int main(int argc, char **argv) { fluid_settings_t *settings; int result = -1; int arg1 = 1; char buf[512]; int c, i; int interactive = 1; int quiet = 0; int midi_in = 1; fluid_player_t *player = NULL; fluid_midi_router_t *router = NULL; fluid_midi_driver_t *mdriver = NULL; fluid_audio_driver_t *adriver = NULL; fluid_synth_t *synth = NULL; fluid_cmd_handler_t *cmd_handler = NULL; #ifdef NETWORK_SUPPORT fluid_server_t *server = NULL; int with_server = 0; #endif char *config_file = NULL; int audio_groups = 0; int audio_channels = 0; int dump = 0; int fast_render = 0; static const char optchars[] = "a:C:c:dE:f:F:G:g:hijK:L:lm:nO:o:p:QqR:r:sT:Vvz:"; #ifdef HAVE_LASH int connect_lash = 1; 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 #if SDL2_SUPPORT if(SDL_Init(SDL_INIT_AUDIO) != 0) { fprintf(stderr, "Warning: Unable to initialize SDL2 Audio: %s", SDL_GetError()); } else { atexit(SDL_Quit); } #endif /* create the settings */ settings = new_fluid_settings(); /* reading / setting options from the command line */ #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'}, {"query-audio-devices", 0, 0, 'Q'}, {"quiet", 0, 0, 'q'}, {"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(); goto cleanup; } else { optarg = argv[i]; if(optarg[0] == '-') { printf("Expected argument to option -%c found switch instead\n", c); print_usage(); goto cleanup; } } } 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"); result = 0; goto cleanup; } else { if(fluid_settings_setstr(settings, "audio.driver", optarg) != FLUID_OK) { goto cleanup; } } break; case 'C': if((optarg != NULL) && ((FLUID_STRCMP(optarg, "0") == 0) || (FLUID_STRCMP(optarg, "no") == 0))) { fluid_settings_setint(settings, "synth.chorus.active", FALSE); } else { fluid_settings_setint(settings, "synth.chorus.active", TRUE); } break; case 'c': if(fluid_settings_setint(settings, "audio.periods", atoi(optarg)) != FLUID_OK) { goto cleanup; } break; case 'd': 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 result = 0; goto cleanup; } else { if(fluid_settings_setstr(settings, "audio.file.endian", optarg) != FLUID_OK) { goto cleanup; } } break; case 'f': config_file = optarg; break; case 'F': if(fluid_settings_setstr(settings, "audio.file.name", optarg) != FLUID_OK) { goto cleanup; } fast_render = 1; break; case 'G': audio_groups = atoi(optarg); break; case 'g': if(fluid_settings_setnum(settings, "synth.gain", atof(optarg)) != FLUID_OK) { goto cleanup; } break; case 'h': print_welcome(); print_help(settings); result = 0; goto cleanup; break; case 'i': interactive = 0; break; case 'j': #if JACK_SUPPORT fluid_settings_setint(settings, "audio.jack.autoconnect", 1); #endif fluid_settings_setint(settings, "midi.autoconnect", 1); break; case 'K': if(fluid_settings_setint(settings, "synth.midi-channels", atoi(optarg)) != FLUID_OK) { goto cleanup; } break; case 'L': audio_channels = atoi(optarg); if(fluid_settings_setint(settings, "synth.audio-channels", audio_channels) != FLUID_OK) { goto cleanup; } break; case 'l': /* disable LASH */ #ifdef HAVE_LASH 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"); result = 0; goto cleanup; } else { if(fluid_settings_setstr(settings, "midi.driver", optarg) != FLUID_OK) { goto cleanup; } } 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 result = 0; goto cleanup; } else { fluid_settings_setstr(settings, "audio.file.format", optarg); } break; case 'o': if(process_o_cmd_line_option(settings, optarg) != FLUID_OK) { goto cleanup; } break; case 'p' : if(fluid_settings_setstr(settings, "midi.portname", optarg) != FLUID_OK) { goto cleanup; } break; case 'Q': print_welcome(); #ifdef WASAPI_SUPPORT fluid_wasapi_device_enumerate(); result = 0; #else fprintf(stderr, "Error: This version of fluidsynth was compiled without WASAPI support. Audio device enumeration is not available."); result = 1; #endif goto cleanup; case 'q': quiet = 1; #if defined(WIN32) /* Windows logs to stdout by default, so make sure anything * lower than PANIC is not printed either */ fluid_set_log_function(FLUID_ERR, NULL, NULL); fluid_set_log_function(FLUID_WARN, NULL, NULL); fluid_set_log_function(FLUID_INFO, NULL, NULL); fluid_set_log_function(FLUID_DBG, NULL, NULL); #endif break; case 'R': if((optarg != NULL) && ((FLUID_STRCMP(optarg, "0") == 0) || (FLUID_STRCMP(optarg, "no") == 0))) { fluid_settings_setint(settings, "synth.reverb.active", FALSE); } else { fluid_settings_setint(settings, "synth.reverb.active", TRUE); } break; case 'r': if(fluid_settings_setnum(settings, "synth.sample-rate", atof(optarg)) != FLUID_OK) { goto cleanup; } break; case 's': #ifdef NETWORK_SUPPORT with_server = 1; #else printf("\nNOTE: FluidSynth compiled without network support, unable to start server!\n"); #endif 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 result = 0; goto cleanup; } else { if(fluid_settings_setstr(settings, "audio.file.type", optarg) != FLUID_OK) { goto cleanup; } } break; case 'V': print_welcome(); print_configure(); result = 0; goto cleanup; case 'v': fluid_settings_setint(settings, "synth.verbose", TRUE); fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL); break; case 'z': if(fluid_settings_setint(settings, "audio.period-size", atoi(optarg)) != FLUID_OK) { goto cleanup; } break; #ifdef GETOPT_SUPPORT case '?': printf("Unknown option %c\n", optopt); print_usage(); goto cleanup; 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(); goto cleanup; #endif } /* end of switch statement */ } /* end of loop */ #ifdef GETOPT_SUPPORT arg1 = optind; #else arg1 = i; #endif if (!quiet) { print_welcome(); } /* option help requested? "-o help" */ if(option_help) { printf("FluidSynth settings:\n"); fluid_settings_foreach(settings, settings, settings_foreach_func); result = 0; goto cleanup; } #ifdef WIN32 SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); #endif #ifdef HAVE_LASH /* 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) { if(fluid_settings_setint(settings, "synth.audio-groups", audio_groups) != FLUID_OK) { goto cleanup; } } if(fast_render) { midi_in = 0; /* disable MIDI driver creation */ interactive = 0; /* disable user shell creation */ #ifdef NETWORK_SUPPORT with_server = 0; /* disable tcp server shell creation */ #endif fluid_settings_setstr(settings, "player.timing-source", "sample"); fluid_settings_setint(settings, "synth.lock-memory", 0); } if(config_file == NULL) { config_file = fluid_get_userconf(buf, sizeof(buf)); if(config_file == NULL) { config_file = fluid_get_sysconf(buf, sizeof(buf)); } /* if the automatically selected command file does not exist, do not even attempt to open it */ if(!g_file_test(config_file, G_FILE_TEST_EXISTS)) { config_file = NULL; } } /* Handle set commands before creating the synth */ if(config_file != NULL) { cmd_handler = new_fluid_cmd_handler2(settings, NULL, NULL, NULL); if(cmd_handler == NULL) { fprintf(stderr, "Failed to create the early command handler\n"); goto cleanup; } if(fluid_source(cmd_handler, config_file) < 0) { fprintf(stderr, "Failed to early-execute command configuration file '%s'\n", config_file); /* the command file seems broken, don't read it again */ config_file = NULL; } delete_fluid_cmd_handler(cmd_handler); cmd_handler = NULL; } /* create the synthesizer */ synth = new_fluid_synth(settings); if(synth == NULL) { fprintf(stderr, "Failed to create the synthesizer\n"); goto cleanup; } /* load the soundfonts (check that all non options are SoundFont or MIDI files) */ for(i = arg1; i < argc; i++) { const char *u8_path = argv[i]; #if defined(WIN32) /* try to convert ANSI encoding path to UTF8 encoding path */ char *u8_buf = win32_ansi_to_utf8(argv[i]); if (u8_buf == NULL) { // error msg. already printed goto cleanup; } u8_path = u8_buf; #endif if(fluid_is_midifile(u8_path)) { continue; } if(fluid_is_soundfont(u8_path)) { if(fluid_synth_sfload(synth, u8_path, 1) == -1) { fprintf(stderr, "Failed to load the SoundFont %s\n", argv[i]); } } else { fprintf(stderr, "Parameter '%s' not a SoundFont or MIDI file or error occurred identifying it.\n", argv[i]); } #if defined(WIN32) free(u8_buf); #endif } /* Try to load the default soundfont, if no soundfont specified */ if(fluid_synth_get_sfont(synth, 0) == NULL) { char *s; if(fluid_settings_dupstr(settings, "synth.default-soundfont", &s) != FLUID_OK) { s = NULL; } if((s != NULL) && (s[0] != '\0')) { fluid_synth_sfload(synth, s, 1); } FLUID_FREE(s); } 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"); } /* start the midi router and link it to the synth */ if(midi_in && router != NULL) { /* 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.. */ 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"); } } /* create the player and add any midi files, if requested */ 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; } if(router != NULL) { fluid_player_set_playback_callback(player, fluid_midi_router_handle_midi_event, router); } } fluid_player_add(player, argv[i]); } } /* try to load and execute the user or system configuration file */ cmd_handler = new_fluid_cmd_handler2(settings, synth, router, player); if(cmd_handler == NULL) { fprintf(stderr, "Failed to create the command handler\n"); goto cleanup; } if(config_file != NULL && fluid_source(cmd_handler, config_file) < 0) { fprintf(stderr, "Failed to execute command configuration file '%s'\n", config_file); } /* start the player. Must be done after executing commands configuration file. This allows any existing player commands to be run prior the player is started. Example: player_tempo_bpm 60 # set a low tempo player_loop -1 # loop song forever */ if(player != NULL) { fluid_player_play(player); } /* run the server, if requested */ #ifdef NETWORK_SUPPORT if(with_server) { server = new_fluid_server2(settings, synth, router, player); if(server == NULL) { fprintf(stderr, "Failed to create the server.\n" "Continuing without it.\n"); } #ifdef SYSTEMD_SUPPORT else { sd_notify(0, "READY=1"); } #endif } #endif #ifdef HAVE_LASH if(enabled_lash) { fluid_lash_create_thread(synth); } #endif /* fast rendering audio file, if requested */ 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); if(filename) { if (!quiet) { printf("Rendering audio to file '%s'..\n", filename); } FLUID_FREE(filename); } fast_render_loop(settings, synth, player); } else /* start the synthesis thread */ { adriver = new_fluid_audio_driver(settings, synth); if(adriver == NULL) { fprintf(stderr, "Failed to create the audio driver. Giving up.\n"); goto cleanup; } /* 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); /* this is a synchronous shell */ } } result = 0; cleanup: #ifdef NETWORK_SUPPORT if(server != NULL) { /* if the user typed 'quit' in the shell, kill the server */ if(!interactive) { fluid_server_join(server); } #ifdef SYSTEMD_SUPPORT sd_notify(0, "STOPPING=1"); #endif delete_fluid_server(server); } #endif /* NETWORK_SUPPORT */ 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_audio_driver(adriver); delete_fluid_player(player); delete_fluid_midi_driver(mdriver); delete_fluid_midi_router(router); delete_fluid_synth(synth); delete_fluid_settings(settings); return result; } /* * print_usage */ void print_usage() { fprintf(stderr, "Usage: fluidsynth [options] [soundfonts]\n"); fprintf(stderr, "Try -h for help.\n"); } void print_welcome() { printf("FluidSynth runtime version %s\n" "Copyright (C) 2000-2022 Peter Hanappe and others.\n" "Distributed under the LGPL license.\n" "SoundFont(R) is a registered trademark of Creative Technology Ltd.\n\n", fluid_version_str()); } void print_configure() { puts("FluidSynth executable version " FLUIDSYNTH_VERSION); puts("Sample type=" #ifdef WITH_FLOAT "float" #else "double" #endif ); } /* * 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"); #ifndef GETOPT_SUPPORT printf("\nNote:" "\n This version of fluidsynth was compiled without getopt support." "\n Thus, the long options are not supported.\n\n"); #endif 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 HAVE_LASH 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"); #ifdef WASAPI_SUPPORT printf(" -Q, --query-audio-devices\n" " Probe all available soundcards for supported modes, sample-rates and sample-formats.\n"); #endif printf(" -q, --quiet\n" " Do not print welcome message or other informational output\n" " (Windows only: also suppress all log messages lower than PANIC\n"); 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 (synth.verbose=1) as well as other debug messages\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); } } fluidsynth-2.2.5/src/gentables/000077500000000000000000000000001417326347500164605ustar00rootroot00000000000000fluidsynth-2.2.5/src/gentables/CMakeLists.txt000066400000000000000000000022221417326347500212160ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.1) # remove $CC from the current environment and by that force cmake to look for a (working) C compiler, # which hopefully will be the host compiler unset(ENV{CC}) # also unset $CFLAGS to avoid passing any cross compilation flags to the host compiler unset(ENV{CFLAGS}) # linker flags as well unset(ENV{LDFLAGS}) project (gentables C) set ( CMAKE_BUILD_TYPE Debug ) # hardcode ".exe" as suffix to the binary, else in case of cross-platform cross-compiling the calling cmake will not know the suffix used here and fail to find the binary set ( CMAKE_EXECUTABLE_SUFFIX ".exe" ) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../) # Add the executable that generates the table add_executable( make_tables make_tables.c gen_conv.c gen_rvoice_dsp.c) if ( WIN32 ) add_definitions ( -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS ) else ( WIN32 ) target_link_libraries (make_tables "m") endif () fluidsynth-2.2.5/src/gentables/gen_conv.c000066400000000000000000000045631417326347500204320ustar00rootroot00000000000000 #include "utils/fluid_conv_tables.h" #include "make_tables.h" /* conversion tables */ static double fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; static double fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; static double fluid_concave_tab[FLUID_VEL_CB_SIZE]; static double fluid_convex_tab[FLUID_VEL_CB_SIZE]; static double fluid_pan_tab[FLUID_PAN_SIZE]; /* * void fluid_synth_init * * Does all the initialization for this module. */ static void fluid_conversion_config(void) { int i; double x; for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++) { // 6,875 is just a factor that we already multiply into the lookup table to save // that multiplication in fluid_ct2hz_real() // 6.875 Hz because 440Hz / 2^6 fluid_ct2hz_tab[i] = 6.875 * powl(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] = powl(10.0, (double) i / -200.0); } /* initialize the conversion tables (see fluid_mod.c fluid_mod_get_value cases 4 and 8) */ /* concave unipolar positive transform curve */ fluid_concave_tab[0] = 0.0; fluid_concave_tab[FLUID_VEL_CB_SIZE - 1] = 1.0; /* convex unipolar positive transform curve */ fluid_convex_tab[0] = 0; fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.0; /* There seems to be an error in the specs. The equations are implemented according to the pictures on SF2.01 page 73. */ for(i = 1; i < FLUID_VEL_CB_SIZE - 1; i++) { x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((double)(i * i) / ((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10; fluid_convex_tab[i] = (1.0 - x); fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = x; } /* initialize the pan conversion table */ x = M_PI / 2.0 / (FLUID_PAN_SIZE - 1.0); for(i = 0; i < FLUID_PAN_SIZE; i++) { fluid_pan_tab[i] = sin(i * x); } } void gen_conv_table(FILE *fp) { /* Calculate the values */ fluid_conversion_config(); /* fluid_ct2hz_tab */ EMIT_ARRAY(fp, fluid_ct2hz_tab); EMIT_ARRAY(fp, fluid_cb2amp_tab); EMIT_ARRAY(fp, fluid_concave_tab); EMIT_ARRAY(fp, fluid_convex_tab); EMIT_ARRAY(fp, fluid_pan_tab); } fluidsynth-2.2.5/src/gentables/gen_rvoice_dsp.c000066400000000000000000000054211417326347500216140ustar00rootroot00000000000000 #include "rvoice/fluid_rvoice_dsp_tables.h" #include "make_tables.h" /* Linear interpolation table (2 coefficients centered on 1st) */ static double interp_coeff_linear[FLUID_INTERP_MAX][2]; /* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */ static double interp_coeff[FLUID_INTERP_MAX][4]; /* 7th order interpolation (7 coefficients centered on 3rd) */ static double sinc_table7[FLUID_INTERP_MAX][SINC_INTERP_ORDER]; static double cb_interp_coeff_linear(int y, int x) { return interp_coeff_linear[y][x]; } static double cb_interp_coeff (int y, int x) { return interp_coeff[y][x]; } static double cb_sinc_table7 (int y, int x) { return sinc_table7[y][x]; } /* Initializes interpolation tables */ void fluid_rvoice_dsp_config(void) { int i, i2; double x, v; double i_shifted; /* Initialize the coefficients for the interpolation. The math comes * from a mail, posted by Olli Niemitalo to the music-dsp mailing * list (I found it in the music-dsp archives * http://www.smartelectronix.com/musicdsp/). */ for(i = 0; i < FLUID_INTERP_MAX; i++) { x = (double) i / (double) FLUID_INTERP_MAX; interp_coeff[i][0] = (x * (-0.5 + x * (1 - 0.5 * x))); interp_coeff[i][1] = (1.0 + x * x * (1.5 * x - 2.5)); interp_coeff[i][2] = (x * (0.5 + x * (2.0 - 1.5 * x))); interp_coeff[i][3] = (0.5 * x * x * (x - 1.0)); interp_coeff_linear[i][0] = (1.0 - x); interp_coeff_linear[i][1] = x; } /* i: Offset in terms of whole samples */ for(i = 0; i < SINC_INTERP_ORDER; i++) { /* i2: Offset in terms of fractional samples ('subsamples') */ for(i2 = 0; i2 < FLUID_INTERP_MAX; i2++) { /* center on middle of table */ i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0) + (double)i2 / (double)FLUID_INTERP_MAX; /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */ if(fabs(i_shifted) > 0.000001) { double arg = M_PI * i_shifted; v = sin(arg) / (arg); /* Hanning window */ v *= 0.5 * (1.0 + cos(2.0 * arg / (double)SINC_INTERP_ORDER)); } else { v = 1.0; } sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v; } } } void gen_rvoice_table_dsp (FILE *fp) { /* Calculate the values */ fluid_rvoice_dsp_config(); /* Emit the matrices */ emit_matrix(fp, "interp_coeff_linear", cb_interp_coeff_linear, FLUID_INTERP_MAX, 2); emit_matrix(fp, "interp_coeff", cb_interp_coeff, FLUID_INTERP_MAX, 4); emit_matrix(fp, "sinc_table7", cb_sinc_table7, FLUID_INTERP_MAX, 7); } fluidsynth-2.2.5/src/gentables/make_tables.c000066400000000000000000000033301417326347500210720ustar00rootroot00000000000000 #include "make_tables.h" static void write_value(FILE *fp, double val, int i) { fprintf(fp, " %.15e%c /* %d */\n", val, ',', i ); } /* Emit an array of real numbers */ void emit_array(FILE *fp, const char *tblname, const double *tbl, int size) { int i; fprintf(fp, "static const fluid_real_t %s[%d] = {\n", tblname, size); for (i = 0; i < size; i++) { write_value(fp, tbl[i], i); } fprintf(fp, "};\n\n"); } /* Emit a matrix of real numbers */ void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel) { int i, j; fprintf(fp, "static const fluid_real_t %s[%d][%d] = {\n {\n", tblname, sizeh, sizel); for (i = 0; i < sizeh; i++) { for (j = 0; j < sizel; j++) { write_value(fp, tbl_cb(i, j), i*sizel+j); } if (i < (sizeh-1)) fprintf(fp, " }, {\n"); else fprintf(fp, " }\n};\n\n"); } } static void open_table(FILE**fp, const char* dir, const char* file) { char buf[2048] = {0}; strcat(buf, dir); strcat(buf, file); /* open the output file */ *fp = fopen(buf, "w"); if (*fp == NULL) { exit(-2); } /* Emit warning header */ fprintf(*fp, "/* THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT. */\n\n"); } int main (int argc, char *argv[]) { FILE *fp; // make sure we have enough arguments if (argc < 2) return -1; open_table(&fp, argv[1], "fluid_conv_tables.inc.h"); gen_conv_table(fp); fclose(fp); open_table(&fp, argv[1], "fluid_rvoice_dsp_tables.inc.h"); gen_rvoice_table_dsp(fp); fclose(fp); return 0; } fluidsynth-2.2.5/src/gentables/make_tables.h000066400000000000000000000012511417326347500210770ustar00rootroot00000000000000 #ifndef _FLUID_MAKE_TABLES_H #define _FLUID_MAKE_TABLES_H #include #include #include #include #define EMIT_ARRAY(__fp__, __arr__) emit_array(__fp__, #__arr__, __arr__, sizeof(__arr__)/sizeof(*__arr__)) /* callback for general access to matrices */ typedef double (*emit_matrix_cb)(int y, int x); /* Generators */ void gen_rvoice_table_dsp(FILE *fp); void gen_conv_table(FILE *fp); /* Emit an array of real numbers */ void emit_array(FILE *fp, const char *tblname, const double *tbl, int size); /* Emit a matrix of real numbers */ void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel); #endif fluidsynth-2.2.5/src/midi/000077500000000000000000000000001417326347500154365ustar00rootroot00000000000000fluidsynth-2.2.5/src/midi/fluid_midi.c000066400000000000000000002160141417326347500177130ustar00rootroot00000000000000/* 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); static int fluid_isasciistring(char *s); static long fluid_getlength(const unsigned char *s); /* 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. */ typedef FILE *fluid_file; static char *fluid_file_read_full(fluid_file fp, size_t *length); static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic); static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size); #define READ_FULL_INITIAL_BUFLEN 1024 static fluid_track_t *new_fluid_track(int num); static void delete_fluid_track(fluid_track_t *track); static int fluid_track_set_name(fluid_track_t *track, char *name); static int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt); static fluid_midi_event_t *fluid_track_next_event(fluid_track_t *track); static int fluid_track_get_duration(fluid_track_t *track); static int fluid_track_reset(fluid_track_t *track); static int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track); static int fluid_player_callback(void *data, unsigned int msec); static int fluid_player_reset(fluid_player_t *player); static int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item); static void fluid_player_advancefile(fluid_player_t *player); static void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec); static void fluid_player_update_tempo(fluid_player_t *player); static fluid_midi_file *new_fluid_midi_file(const char *buffer, size_t length); static void delete_fluid_midi_file(fluid_midi_file *mf); static int fluid_midi_file_read_mthd(fluid_midi_file *midifile); static int fluid_midi_file_load_tracks(fluid_midi_file *midifile, fluid_player_t *player); static int fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num); static int fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track); static int fluid_midi_file_read_varlen(fluid_midi_file *mf); static int fluid_midi_file_getc(fluid_midi_file *mf); static int fluid_midi_file_push(fluid_midi_file *mf, int c); static int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len); static int fluid_midi_file_skip(fluid_midi_file *mf, int len); static int fluid_midi_file_eof(fluid_midi_file *mf); static int fluid_midi_file_read_tracklen(fluid_midi_file *mf); static int fluid_midi_file_eot(fluid_midi_file *mf); static int fluid_midi_file_get_division(fluid_midi_file *midifile); /*************************************************************** * * MIDIFILE */ /** * Check if a file is a MIDI file. * @param filename Path to the file to check * @return TRUE if it could be a MIDI file, FALSE otherwise * * The current implementation only checks for the "MThd" header in the file. * It is useful only to distinguish between SoundFont and MIDI files. */ int fluid_is_midifile(const char *filename) { FILE *fp; uint32_t id; int retcode = FALSE; do { if((fp = fluid_file_open(filename, NULL)) == NULL) { return retcode; } if(FLUID_FREAD(&id, sizeof(id), 1, fp) != 1) { break; } retcode = (id == FLUID_FOURCC('M', 'T', 'h', 'd')); } while(0); FLUID_FCLOSE(fp); return retcode; } /** * Return a new MIDI file handle for parsing an already-loaded MIDI file. * @internal * @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 %lu bytes", (unsigned long)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 %lu bytes; expected %lu", (unsigned long)n, (unsigned long)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) { fluid_return_if_fail(mf != NULL); FLUID_FREE(mf); } /* * Gets the next byte in a MIDI file, taking into account previous running status. * * returns -1 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 -1; } 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) { /* From ctype.h */ #define fluid_isascii(c) (((c) & ~0x7f) == 0) size_t i, len = FLUID_STRLEN(s); for(i = 0; i < len; i++) { if(!fluid_isascii(s[i])) { return 0; } } return 1; #undef fluid_isascii } /* * fluid_getlength */ long fluid_getlength(const 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, "A non-ascii track header found, corrupt file"); return FLUID_FAILED; } else if(FLUID_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: case MIDI_TEXT: { void *tmp; int size = mf->varlen + 1; /* NULL terminate strings for safety */ metadata[size - 1] = '\0'; evt = new_fluid_midi_event(); if(evt == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); result = FLUID_FAILED; break; } evt->dtime = mf->dtime; tmp = FLUID_MALLOC(size); if(tmp == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); delete_fluid_midi_event(evt); evt = NULL; result = FLUID_FAILED; break; } FLUID_MEMCPY(tmp, metadata, size); fluid_midi_event_set_sysex_LOCAL(evt, type, tmp, size, TRUE); fluid_track_add_event(track, evt); mf->dtime = 0; } 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 */ void delete_fluid_midi_event(fluid_midi_event_t *evt) { fluid_midi_event_t *temp; fluid_return_if_fail(evt != NULL); while(evt) { temp = evt->next; /* Dynamic SYSEX event? - free (param2 indicates if dynamic) */ if((evt->type == MIDI_SYSEX || (evt-> type == MIDI_TEXT) || (evt->type == MIDI_LYRIC)) && evt->paramptr && evt->param2) { FLUID_FREE(evt->paramptr); } FLUID_FREE(evt); evt = temp; } } /** * 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 in bytes * @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 */ int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic) { fluid_midi_event_set_sysex_LOCAL(evt, MIDI_SYSEX, data, size, dynamic); return FLUID_OK; } /** * Assign text data to a MIDI event structure. * @param evt MIDI event structure * @param data Pointer to text data * @param size Size of text data in bytes * @param dynamic TRUE if the data has been dynamically allocated and * should be freed when the event is freed via delete_fluid_midi_event() * @return Always returns #FLUID_OK * * @since 2.0.0 */ int fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic) { fluid_midi_event_set_sysex_LOCAL(evt, MIDI_TEXT, data, size, dynamic); return FLUID_OK; } /** * Get the text of a MIDI event structure. * @param evt MIDI event structure * @param data Pointer to return text data on. * @param size Pointer to return text size on. * @return Returns #FLUID_OK if \p data and \p size previously set by * fluid_midi_event_set_text() have been successfully retrieved. * Else #FLUID_FAILED is returned and \p data and \p size are not changed. * @since 2.0.3 */ int fluid_midi_event_get_text(fluid_midi_event_t *evt, void **data, int *size) { fluid_return_val_if_fail(evt != NULL, FLUID_FAILED); fluid_return_val_if_fail(evt->type == MIDI_TEXT, FLUID_FAILED); fluid_midi_event_get_sysex_LOCAL(evt, data, size); return FLUID_OK; } /** * Assign lyric data to a MIDI event structure. * @param evt MIDI event structure * @param data Pointer to lyric data * @param size Size of lyric data in bytes * @param dynamic TRUE if the data has been dynamically allocated and * should be freed when the event is freed via delete_fluid_midi_event() * @return Always returns #FLUID_OK * * @since 2.0.0 */ int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic) { fluid_midi_event_set_sysex_LOCAL(evt, MIDI_LYRIC, data, size, dynamic); return FLUID_OK; } /** * Get the lyric of a MIDI event structure. * @param evt MIDI event structure * @param data Pointer to return lyric data on. * @param size Pointer to return lyric size on. * @return Returns #FLUID_OK if \p data and \p size previously set by * fluid_midi_event_set_lyrics() have been successfully retrieved. * Else #FLUID_FAILED is returned and \p data and \p size are not changed. * @since 2.0.3 */ int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, void **data, int *size) { fluid_return_val_if_fail(evt != NULL, FLUID_FAILED); fluid_return_val_if_fail(evt->type == MIDI_LYRIC, FLUID_FAILED); fluid_midi_event_get_sysex_LOCAL(evt, data, size); return FLUID_OK; } static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic) { evt->type = type; evt->paramptr = data; evt->param1 = size; evt->param2 = dynamic; } static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size) { if(data) { *data = evt->paramptr; } if(size) { *size = evt->param1; } } /****************************************************** * * fluid_track_t */ /* * 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 */ void delete_fluid_track(fluid_track_t *track) { fluid_return_if_fail(track != NULL); FLUID_FREE(track->name); delete_fluid_midi_event(track->first); FLUID_FREE(track); } /* * fluid_track_set_name */ int fluid_track_set_name(fluid_track_t *track, char *name) { size_t 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_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_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_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 */ static void fluid_track_send_events(fluid_track_t *track, fluid_synth_t *synth, fluid_player_t *player, unsigned int ticks, int seek_ticks ) { fluid_midi_event_t *event; int seeking = seek_ticks >= 0; if(seeking) { ticks = seek_ticks; /* update target ticks */ if(track->ticks > ticks) { fluid_track_reset(track); /* reset track if seeking backwards */ } } while(1) { event = track->cur; if(event == NULL) { return; } /* 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; } track->ticks += event->dtime; if(!player || event->type == MIDI_EOT) { /* don't send EOT events to the callback */ } else if(seeking && track->ticks != ticks && (event->type == NOTE_ON || event->type == NOTE_OFF)) { /* skip on/off messages */ } else { if(player->playback_callback) { player->playback_callback(player->playback_userdata, event); if(event->type == NOTE_ON && event->param2 != 0 && !player->channel_isplaying[event->channel]) { player->channel_isplaying[event->channel] = TRUE; } } } if(event->type == MIDI_SET_TEMPO && player != NULL) { /* memorize the tempo change value coming from the MIDI file */ fluid_atomic_int_set(&player->miditempo, event->param1); fluid_player_update_tempo(player); } fluid_track_next_event(track); } } /****************************************************** * * fluid_player */ static void fluid_player_handle_reset_synth(void *data, const char *name, int value) { fluid_player_t *player = data; fluid_return_if_fail(player != NULL); player->reset_synth_between_songs = value; } /** * 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; } fluid_atomic_int_set(&player->status, FLUID_PLAYER_READY); fluid_atomic_int_set(&player->stopping, 0); 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; /* internal tempo (from MIDI file) in micro seconds per quarter note */ player->sync_mode = 1; /* the player follows internal tempo change */ player->miditempo = 500000; /* external tempo in micro seconds per quarter note */ player->exttempo = 500000; /* tempo multiplier */ player->multempo = 1.0F; player->deltatime = 4.0; player->cur_msec = 0; player->cur_ticks = 0; player->last_callback_ticks = -1; fluid_atomic_int_set(&player->seek_ticks, -1); fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth); fluid_player_set_tick_callback(player, NULL, NULL); player->use_system_timer = fluid_settings_str_equal(synth->settings, "player.timing-source", "system"); if(player->use_system_timer) { player->system_timer = new_fluid_timer((int) player->deltatime, fluid_player_callback, player, TRUE, FALSE, TRUE); if(player->system_timer == NULL) { goto err; } } else { player->sample_timer = new_fluid_sample_timer(player->synth, fluid_player_callback, player); if(player->sample_timer == NULL) { goto err; } } fluid_settings_getint(synth->settings, "player.reset-synth", &i); fluid_player_handle_reset_synth(player, NULL, i); fluid_settings_callback_int(synth->settings, "player.reset-synth", fluid_player_handle_reset_synth, player); return player; err: delete_fluid_player(player); return NULL; } /** * Delete a MIDI player instance. * @param player MIDI player instance * @warning Do not call when the synthesizer associated to this \p player renders audio, * i.e. an audio driver is running or any other synthesizer thread concurrently calls * fluid_synth_process() or fluid_synth_nwrite_float() or fluid_synth_write_*() ! */ void delete_fluid_player(fluid_player_t *player) { fluid_list_t *q; fluid_playlist_item *pi; fluid_return_if_fail(player != NULL); fluid_settings_callback_int(player->synth->settings, "player.reset-synth", NULL, NULL); fluid_player_stop(player); fluid_player_reset(player); delete_fluid_timer(player->system_timer); delete_fluid_sample_timer(player->synth, player->sample_timer); 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); } /** * 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); 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); } 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; } } for(i = 0; i < MAX_NUMBER_OF_CHANNELS; i++) { player->channel_isplaying[i] = FALSE; } /* player->current_file = NULL; */ /* player->status = FLUID_PLAYER_READY; */ /* player->loop = 1; */ player->ntracks = 0; player->division = 0; player->miditempo = 500000; 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; } } /** * Change the MIDI callback function. * * @param player MIDI player instance * @param handler Pointer to callback function * @param handler_data Parameter sent to the callback function * @returns FLUID_OK * * 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. * * @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 listener function for every MIDI tick change. * * @param player MIDI player instance * @param handler Pointer to callback function * @param handler_data Opaque parameter to be sent to the callback function * @returns #FLUID_OK * * This callback is not set by default, but can optionally * be changed to a user-defined function for intercepting all MIDI * tick changes and react to them with precision. * * @since 2.2.0 */ int fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data) { player->tick_callback = handler; player->tick_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); FLUID_FCLOSE(fp); if(buffer == NULL) { return FLUID_FAILED; } buffer_owned = 1; } 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_update_tempo(player); // 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 */ fluid_atomic_int_set(&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; 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_midi_event_t mute_event; fluid_player_t *player; fluid_synth_t *synth; player = (fluid_player_t *) data; synth = player->synth; loadnextfile = player->currentfile == NULL ? 1 : 0; fluid_midi_event_set_type(&mute_event, CONTROL_CHANGE); mute_event.param1 = ALL_SOUND_OFF; mute_event.param2 = 1; if(fluid_player_get_status(player) != FLUID_PLAYER_PLAYING) { if(fluid_atomic_int_get(&player->stopping)) { for(i = 0; i < synth->midi_channels; i++) { if(player->channel_isplaying[i]) { fluid_midi_event_set_channel(&mute_event, i); player->playback_callback(player->playback_userdata, &mute_event); } } fluid_atomic_int_set(&player->stopping, 0); } return 1; } do { float deltatime; int seek_ticks; if(loadnextfile) { loadnextfile = 0; fluid_player_playlist_load(player, msec); if(player->currentfile == NULL) { return 0; } } player->cur_msec = msec; deltatime = fluid_atomic_float_get(&player->deltatime); player->cur_ticks = (player->start_ticks + (int)((double)(player->cur_msec - player->start_msec) / deltatime + 0.5)); /* 0.5 to average overall error when casting */ seek_ticks = fluid_atomic_int_get(&player->seek_ticks); if(seek_ticks >= 0) { for(i = 0; i < synth->midi_channels; i++) { if(player->channel_isplaying[i]) { fluid_midi_event_set_channel(&mute_event, i); player->playback_callback(player->playback_userdata, &mute_event); } } } for(i = 0; i < player->ntracks; i++) { if(!fluid_track_eot(player->track[i])) { status = FLUID_PLAYER_PLAYING; fluid_track_send_events(player->track[i], synth, player, player->cur_ticks, seek_ticks); } } if(seek_ticks >= 0) { player->start_ticks = seek_ticks; /* tick position of last tempo value (which is now) */ player->cur_ticks = seek_ticks; player->begin_msec = msec; /* only used to calculate the duration of playing */ player->start_msec = msec; /* should be the (synth)-time of the last tempo change */ fluid_atomic_int_set(&player->seek_ticks, -1); /* clear seek_ticks */ } if(status == FLUID_PLAYER_DONE) { FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__, __LINE__, (msec - player->begin_msec) / 1000.0); if(player->reset_synth_between_songs) { fluid_synth_system_reset(player->synth); } loadnextfile = 1; } if (player->tick_callback != NULL && player->last_callback_ticks != player->cur_ticks) { player->tick_callback(player->tick_userdata, player->cur_ticks); player->last_callback_ticks = player->cur_ticks; } } while(loadnextfile); /* do not update the status if the player has been stopped already */ fluid_atomic_int_compare_and_exchange(&player->status, FLUID_PLAYER_PLAYING, 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 * * If the list of files added to the player has completed its requested number of loops, * the playlist will be restarted from the beginning with a loop count of 1. */ int fluid_player_play(fluid_player_t *player) { if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING || player->playlist == NULL) { return FLUID_OK; } if(!player->use_system_timer) { fluid_sample_timer_reset(player->synth, player->sample_timer); } /* If we're at the end of the playlist and there are no loops left, loop once */ if(player->currentfile == NULL && player->loop == 0) { player->loop = 1; } fluid_atomic_int_set(&player->status, FLUID_PLAYER_PLAYING); return FLUID_OK; } /** * Pauses the MIDI playback. * * @param player MIDI player instance * @return Always returns #FLUID_OK * * It will not rewind to the beginning of the file, use fluid_player_seek() for this purpose. */ int fluid_player_stop(fluid_player_t *player) { fluid_atomic_int_set(&player->status, FLUID_PLAYER_DONE); fluid_atomic_int_set(&player->stopping, 1); fluid_player_seek(player, fluid_player_get_current_tick(player)); 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 fluid_atomic_int_get(&player->status); } /** * Seek in the currently playing file. * * @param player MIDI player instance * @param ticks the position to seek to in the current file * @return #FLUID_FAILED if ticks is negative or after the latest tick of the file * [or, since 2.1.3, if another seek operation is currently in progress], * #FLUID_OK otherwise. * * The actual seek will be performed when the synth calls back the player (i.e. a few * levels above the player's callback set with fluid_player_set_playback_callback()). * If the player's status is #FLUID_PLAYER_PLAYING and a previous seek operation has * not been completed yet, #FLUID_FAILED is returned. * * @since 2.0.0 */ int fluid_player_seek(fluid_player_t *player, int ticks) { if(ticks < 0 || (fluid_player_get_status(player) != FLUID_PLAYER_READY && ticks > fluid_player_get_total_ticks(player))) { return FLUID_FAILED; } if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) { if(fluid_atomic_int_compare_and_exchange(&player->seek_ticks, -1, ticks)) { // new seek position has been set, as no previous seek was in progress return FLUID_OK; } } else { // If the player is not currently playing, a new seek position can be set at any time. This allows // the user to do: // fluid_player_stop(); // fluid_player_seek(0); // to beginning fluid_atomic_int_set(&player->seek_ticks, ticks); return FLUID_OK; } // a previous seek is still in progress or hasn't been processed yet return FLUID_FAILED; } /** * 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 * * For example, if you want to loop the playlist twice, set loop to 2 * and call this function before you start the player. * * @since 1.1.0 */ int fluid_player_set_loop(fluid_player_t *player, int loop) { player->loop = loop; return FLUID_OK; } /** * update the MIDI player internal deltatime dependent of actual tempo. * @param player MIDI player instance */ static void fluid_player_update_tempo(fluid_player_t *player) { int tempo; /* tempo in micro seconds by quarter note */ float deltatime; if(fluid_atomic_int_get(&player->sync_mode)) { /* take internal tempo from MIDI file */ tempo = fluid_atomic_int_get(&player->miditempo); /* compute deltattime (in ms) from current tempo and apply tempo multiplier */ deltatime = (float)tempo / (float)player->division / (float)1000.0; deltatime /= fluid_atomic_float_get(&player->multempo); /* multiply tempo */ } else { /* take external tempo */ tempo = fluid_atomic_int_get(&player->exttempo); /* compute deltattime (in ms) from current tempo */ deltatime = (float)tempo / (float)player->division / (float)1000.0; } fluid_atomic_float_set(&player->deltatime, deltatime); 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); } /** * Set the tempo of a MIDI player. * The player can be controlled by internal tempo coming from MIDI file tempo * change or controlled by external tempo expressed in BPM or in micro seconds * per quarter note. * * @param player MIDI player instance. Must be a valid pointer. * @param tempo_type Must a be value of #fluid_player_set_tempo_type and indicates the * meaning of tempo value and how the player will be controlled, see below. * @param tempo Tempo value or multiplier. * * #FLUID_PLAYER_TEMPO_INTERNAL, the player will be controlled by internal * MIDI file tempo changes. If there are no tempo change in the MIDI file a default * value of 120 bpm is used. The @c tempo parameter is used as a multiplier factor * that must be in the range (0.001 to 1000). * For example, if the current MIDI file tempo is 120 bpm and the multiplier * value is 0.5 then this tempo will be slowed down to 60 bpm. * At creation, the player is set to be controlled by internal tempo with a * multiplier factor set to 1.0. * * #FLUID_PLAYER_TEMPO_EXTERNAL_BPM, the player will be controlled by the * external tempo value provided by the tempo parameter in bpm * (i.e in quarter notes per minute) which must be in the range (1 to 60000000). * * #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI, similar as FLUID_PLAYER_TEMPO_EXTERNAL_BPM, * but the tempo parameter value is in micro seconds per quarter note which * must be in the range (1 to 60000000). * Using micro seconds per quarter note is convenient when the tempo value is * derived from MIDI clock realtime messages. * * @note When the player is controlled by an external tempo * (#FLUID_PLAYER_TEMPO_EXTERNAL_BPM or #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI) it * continues to memorize the most recent internal tempo change coming from the * MIDI file so that next call to fluid_player_set_tempo() with * #FLUID_PLAYER_TEMPO_INTERNAL will set the player to follow this internal * tempo. * * @return #FLUID_OK if success or #FLUID_FAILED otherwise (incorrect parameters). * @since 2.2.0 */ int fluid_player_set_tempo(fluid_player_t *player, int tempo_type, double tempo) { fluid_return_val_if_fail(player != NULL, FLUID_FAILED); fluid_return_val_if_fail(tempo_type >= FLUID_PLAYER_TEMPO_INTERNAL, FLUID_FAILED); fluid_return_val_if_fail(tempo_type < FLUID_PLAYER_TEMPO_NBR, FLUID_FAILED); switch(tempo_type) { /* set the player to be driven by internal tempo coming from MIDI file */ case FLUID_PLAYER_TEMPO_INTERNAL: /* check if the multiplier is in correct range */ fluid_return_val_if_fail(tempo >= MIN_TEMPO_MULTIPLIER, FLUID_FAILED); fluid_return_val_if_fail(tempo <= MAX_TEMPO_MULTIPLIER, FLUID_FAILED); /* set the tempo multiplier */ fluid_atomic_float_set(&player->multempo, (float)tempo); fluid_atomic_int_set(&player->sync_mode, 1); /* set internal mode */ break; /* set the player to be driven by external tempo */ case FLUID_PLAYER_TEMPO_EXTERNAL_BPM: /* value in bpm */ case FLUID_PLAYER_TEMPO_EXTERNAL_MIDI: /* value in us/quarter note */ /* check if tempo is in correct range */ fluid_return_val_if_fail(tempo >= MIN_TEMPO_VALUE, FLUID_FAILED); fluid_return_val_if_fail(tempo <= MAX_TEMPO_VALUE, FLUID_FAILED); /* set the tempo value */ if(tempo_type == FLUID_PLAYER_TEMPO_EXTERNAL_BPM) { tempo = 60000000L / tempo; /* convert tempo in us/quarter note */ } fluid_atomic_int_set(&player->exttempo, (int)tempo); fluid_atomic_int_set(&player->sync_mode, 0); /* set external mode */ break; default: /* shouldn't happen */ break; } /* update deltatime depending of current tempo */ fluid_player_update_tempo(player); 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 * @note Tempo change events contained in the MIDI file can override the specified tempo at any time! * @deprecated Use fluid_player_set_tempo() instead. */ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) { player->miditempo = tempo; fluid_player_update_tempo(player); 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 * @note Tempo change events contained in the MIDI file can override the specified BPM at any time! * @deprecated Use fluid_player_set_tempo() instead. */ int fluid_player_set_bpm(fluid_player_t *player, int bpm) { if(bpm <= 0) { return FLUID_FAILED; /* to avoid a division by 0 */ } return fluid_player_set_midi_tempo(player, 60000000L / bpm); } /** * Wait for a MIDI player until the playback has been stopped. * @param player MIDI player instance * @return Always #FLUID_OK * * @warning The player may still be used by a concurrently running synth context. Hence it is * not safe to immediately delete the player afterwards. Also refer to delete_fluid_player(). */ int fluid_player_join(fluid_player_t *player) { while(fluid_player_get_status(player) != FLUID_PLAYER_DONE) { fluid_msleep(10); } 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 currently used by a MIDI player. * The player can be controlled by internal tempo coming from MIDI file tempo * change or controlled by external tempo see fluid_player_set_tempo(). * @param player MIDI player instance. Must be a valid pointer. * @return MIDI player tempo in BPM or FLUID_FAILED if error. * @since 1.1.7 */ int fluid_player_get_bpm(fluid_player_t *player) { int midi_tempo = fluid_player_get_midi_tempo(player); if(midi_tempo > 0) { midi_tempo = 60000000L / midi_tempo; /* convert in bpm */ } return midi_tempo; } /** * Get the tempo currently used by a MIDI player. * The player can be controlled by internal tempo coming from MIDI file tempo * change or controlled by external tempo see fluid_player_set_tempo(). * @param player MIDI player instance. Must be a valid pointer. * @return Tempo of the MIDI player (in microseconds per quarter note, as per * MIDI file spec) or FLUID_FAILED if error. * @since 1.1.7 */ int fluid_player_get_midi_tempo(fluid_player_t *player) { int midi_tempo; /* value to return */ fluid_return_val_if_fail(player != NULL, FLUID_FAILED); midi_tempo = fluid_atomic_int_get(&player->exttempo); /* look if the player is internally synced */ if(fluid_atomic_int_get(&player->sync_mode)) { midi_tempo = (int)((float)fluid_atomic_int_get(&player->miditempo)/ fluid_atomic_float_get(&player->multempo)); } return midi_tempo; } /************************************************************************ * 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 */ void delete_fluid_midi_parser(fluid_midi_parser_t *parser) { fluid_return_if_fail(parser != NULL); FLUID_FREE(parser); } /** * 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. * @internal Do not expose this function to the public API. It would allow downstream * apps to abuse fluidsynth as midi parser, e.g. feeding it with rawmidi and pull out * the needed midi information using the getter functions of fluid_midi_event_t. * This parser however is incomplete as it e.g. only provides a limited buffer to * store and process SYSEX data (i.e. doesn't allow arbitrary lengths) */ 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-2.2.5/src/midi/fluid_midi.h000066400000000000000000000316031417326347500177170ustar00rootroot00000000000000/* 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); void 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 #define MAX_NUMBER_OF_CHANNELS 16 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 = 0x44, 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_TEXT = 0x01, 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_MANUF_YAMAHA = 0x43, 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 */ #define MIDI_SYSEX_GS_ID 0x42 /**< Model ID (GS) serving as sub-ID #1 for GS messages*/ #define MIDI_SYSEX_XG_ID 0x4C /**< Model ID (XG) serving as sub-ID #1 for XG 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 response (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 */ #define MIDI_SYSEX_GM2_ON 0x03 /**< Enable GM2 mode */ #define MIDI_SYSEX_GS_DT1 0x12 /**< GS DT1 command */ enum fluid_driver_status { FLUID_MIDI_READY, FLUID_MIDI_LISTENING, FLUID_MIDI_DONE }; /*************************************************************** * * TYPE DEFINITIONS & FUNCTION DECLARATIONS */ /* * 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; #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; /* range of tempo values */ #define MIN_TEMPO_VALUE (1.0f) #define MAX_TEMPO_VALUE (60000000.0f) /* range of tempo multiplier values */ #define MIN_TEMPO_MULTIPLIER (0.001f) #define MAX_TEMPO_MULTIPLIER (1000.0f) /* * fluid_player */ struct _fluid_player_t { fluid_atomic_int_t status; fluid_atomic_int_t stopping; /* Flag for sending all_notes_off when player is stopped */ 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 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. */ fluid_atomic_int_t seek_ticks; /* new position in tempo ticks (midi ticks) for seeking */ int start_ticks; /* the number of tempo ticks passed at the last tempo change */ int cur_ticks; /* the number of tempo ticks passed */ int last_callback_ticks; /* the last tick number that was passed to player->tick_callback */ 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 */ /* sync mode: indicates the tempo mode the player is driven by (see fluid_player_set_tempo()): 1, the player is driven by internal tempo (miditempo). This is the default. 0, the player is driven by external tempo (exttempo) */ int sync_mode; /* miditempo: internal tempo coming from MIDI file tempo change events (in micro seconds per quarter note) */ int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */ /* exttempo: external tempo set by fluid_player_set_tempo() (in micro seconds per quarter note) */ int exttempo; /* multempo: tempo multiplier set by fluid_player_set_tempo() */ float multempo; float deltatime; /* milliseconds per midi tick. depends on current tempo mode (see sync_mode) */ 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 */ handle_midi_tick_func_t tick_callback; /* function fired on each tick change */ void *tick_userdata; /* pointer to user-defined data passed to tick_callback function */ int channel_isplaying[MAX_NUMBER_OF_CHANNELS]; /* flags indicating channels on which notes have played */ }; 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; #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. */ }; #endif /* _FLUID_MIDI_H */ fluidsynth-2.2.5/src/midi/fluid_midi_router.c000066400000000000000000000776311417326347500213250ustar00rootroot00000000000000/* 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_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 */ }; 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. * * @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 new router will start with default rules and therefore pass all events unmodified. * * 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->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) */ void 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_if_fail(router != NULL); 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); } /** * Set a MIDI router to use default "unity" rules. * * @param router Router to set to default rules. * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * Such a router will pass all events unmodified. * * @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[i2]); } 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. * * @param router Router to clear all rules from * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * An empty router will drop all events until rules are added. * * @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. * * The new rule is a "unity" rule which will accept any values and wont modify * them. * * @since 1.1.0 */ 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 * * Note that rules which have been added to a router are managed by the router, * so this function should seldom be needed. * * @since 1.1.0 */ 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) * * 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. * * @since 1.1.0 */ 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) * * 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. * * @since 1.1.0 */ 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) * * 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. * * @since 1.1.0 */ 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 if all rules were applied successfully, #FLUID_FAILED if * an error occurred while applying a rule or (since 2.2.2) the event was * ignored because a parameter was out-of-range after the rule had been applied. * See the note below. * * 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 * - ... * * @note Each input event has values (ch, par1, par2) that could be changed by a rule. * After a rule has been applied on any value and the value is out of range, the event * can be either ignored or the value can be clamped depending on the type of the event: * - To get full benefice of the rule the value is clamped and the event passed to the output. * - To avoid MIDI messages conflicts at the output, the event is ignored * (i.e not passed to the output). * - ch out of range: event is ignored regardless of the event type. * - par1 out of range: event is ignored for PROG_CHANGE or CONTROL_CHANGE type, * par1 is clamped otherwise. * - par2 out of range: par2 is clamped regardless of the event type. */ 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 is_par1_ignored = 0; /* Flag, indicates that current event should be ignored/clamped when par1 is getting out of range value after the rule had been applied:1:ignored, 0:clamped */ 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) { /* For NOTE_ON event, par1(pitch) and par2(velocity) will be clamped if they are out of range after the rule had been applied */ case NOTE_ON: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_NOTE]; event_has_par2 = 1; break; /* For NOTE_OFF event, par1(pitch) and par2(velocity) will be clamped if they are out of range after the rule had been applied */ case NOTE_OFF: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_NOTE]; event_has_par2 = 1; break; /* CONTROL_CHANGE event will be ignored if par1 (ctrl num) is out of range after the rule had been applied */ case CONTROL_CHANGE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_CC]; event_has_par2 = 1; is_par1_ignored = 1; break; /* PROGRAM_CHANGE event will be ignored if par1 (program num) is out of range after the rule had been applied */ case PROGRAM_CHANGE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_PROG_CHANGE]; is_par1_ignored = 1; break; /* For PITCH_BEND event, par1(bend value) will be clamped if it is out of range after the rule had been applied */ case PITCH_BEND: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_PITCH_BEND]; par1_max = 16383; break; /* For CHANNEL_PRESSURE event, par1(pressure value) will be clamped if it is out of range after the rule had been applied */ case CHANNEL_PRESSURE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE]; break; /* For KEY_PRESSURE event, par1(pitch) and par2(pressure value) will be clamped if they are out of range after the rule had been applied */ 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 = rule->chan_add + (int)((fluid_real_t)event->channel * rule->chan_mul + (fluid_real_t)0.5); /* We ignore the event if chan is out of range */ if((chan < 0) || (chan >= router->nr_midi_channels)) { ret_val = FLUID_FAILED; continue; /* go to next rule */ } /* par 1 scaling / offset */ par1 = rule->par1_add + (int)((fluid_real_t)event_par1 * rule->par1_mul + (fluid_real_t)0.5); if(is_par1_ignored) { /* We ignore the event if par1 is out of range */ if((par1 < 0) || (par1 > par1_max)) { ret_val = FLUID_FAILED; continue; /* go to next rule */ } } else { /* par1 range clamping */ if(par1 < 0) { par1 = 0; } else if(par1 > par1_max) { par1 = par1_max; } } /* par 2 scaling / offset, if applicable */ if(event_has_par2) { par2 = rule->par2_add + (int)((fluid_real_t)event_par2 * rule->par2_mul + (fluid_real_t)0.5); /* par2 range clamping */ if(par2 < 0) { par2 = 0; } else if(par2 > par2_max) { par2 = par2_max; } } else { par2 = 0; } /* 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; /* On failure, 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; } /** * 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-2.2.5/src/midi/fluid_midi_router.h000066400000000000000000000020261417326347500213140ustar00rootroot00000000000000/* 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" #endif fluidsynth-2.2.5/src/midi/fluid_seq.c000066400000000000000000000406561417326347500175700ustar00rootroot00000000000000/* 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.h" #include "fluid_sys.h" // timer, threads, etc... #include "fluid_list.h" #include "fluid_seq_queue.h" /*************************************************************** * * SEQUENCER */ #define FLUID_SEQUENCER_EVENTS_MAX 1000 /* Private data for SEQUENCER */ struct _fluid_sequencer_t { // A backup of currentMs when we have received the last scale change unsigned int startMs; // The number of milliseconds passed since we have started the sequencer, // as indicated by the synth's sample timer fluid_atomic_int_t currentMs; // A backup of cur_ticks when we have received the last scale change unsigned int start_ticks; // The tick count which we've used for the most recent event dispatching unsigned int cur_ticks; int useSystemTimer; // The current time scale in ticks per second. // If you think of MIDI, this is equivalent to: (PPQN / 1000) / (USPQN / 1000) double scale; fluid_list_t *clients; fluid_seq_id_t clientsID; // Pointer to the C++ event queue void *queue; fluid_rec_mutex_t mutex; }; /* 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; /* API implementation */ /** * Create a new sequencer object which uses the system timer. * * @return New sequencer instance * * Use new_fluid_sequencer2() to specify whether the system timer or * fluid_sequencer_process() is used to advance the sequencer. * * @deprecated As of fluidsynth 2.1.1 the use of the system timer has been deprecated. */ 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 * * @note As of fluidsynth 2.1.1 the use of the system timer has been deprecated. * * @since 1.1.0 */ fluid_sequencer_t * new_fluid_sequencer2(int use_system_timer) { fluid_sequencer_t *seq; if(use_system_timer) { FLUID_LOG(FLUID_WARN, "sequencer: Usage of the system timer has been deprecated!"); } 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 ? 1 : 0; seq->startMs = seq->useSystemTimer ? fluid_curtime() : 0; fluid_rec_mutex_init(seq->mutex); seq->queue = new_fluid_seq_queue(FLUID_SEQUENCER_EVENTS_MAX); if(seq->queue == NULL) { FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); delete_fluid_sequencer(seq); return NULL; } return(seq); } /** * Free a sequencer object. * * @param seq Sequencer to delete * * @note Before fluidsynth 2.1.1 registered sequencer clients may not be fully freed by this function. */ void delete_fluid_sequencer(fluid_sequencer_t *seq) { fluid_return_if_fail(seq != NULL); /* 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_rec_mutex_destroy(seq->mutex); delete_fluid_seq_queue(seq->queue); 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. * * @deprecated As of fluidsynth 2.1.1 the usage of the system timer has been deprecated. * * @since 1.1.0 */ int fluid_sequencer_get_use_system_timer(fluid_sequencer_t *seq) { fluid_return_val_if_fail(seq != NULL, FALSE); return seq->useSystemTimer; } /* 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. * * @note Implementations are encouraged to explicitly unregister any registered client with fluid_sequencer_unregister_client() before deleting the sequencer. */ 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; fluid_return_val_if_fail(seq != NULL, FLUID_FAILED); 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(). * * The client's callback function will receive a FLUID_SEQ_UNREGISTERING event right before it is being unregistered. */ void fluid_sequencer_unregister_client(fluid_sequencer_t *seq, fluid_seq_id_t id) { fluid_list_t *tmp; fluid_event_t evt; unsigned int now = fluid_sequencer_get_tick(seq); fluid_return_if_fail(seq != NULL); fluid_event_clear(&evt); fluid_event_unregistering(&evt); fluid_event_set_dest(&evt, id); fluid_event_set_time(&evt, now); tmp = seq->clients; while(tmp) { fluid_sequencer_client_t *client = (fluid_sequencer_client_t *)tmp->data; if(client->id == id) { // client found, remove it from the list to avoid recursive call when calling callback seq->clients = fluid_list_remove_link(seq->clients, tmp); // call the callback (if any), to free underlying memory (e.g. seqbind structure) if (client->callback != NULL) { (client->callback)(now, &evt, seq, client->data); } if(client->name) { FLUID_FREE(client->name); } delete1_fluid_list(tmp); FLUID_FREE(client); return; } tmp = tmp->next; } 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 == NULL || 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_return_val_if_fail(seq != NULL, FLUID_FAILED); fluid_return_val_if_fail(index >= 0, FLUID_FAILED); 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, fluid_seq_id_t id) { fluid_list_t *tmp; fluid_return_val_if_fail(seq != NULL, 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, fluid_seq_id_t id) { fluid_list_t *tmp; fluid_return_val_if_fail(seq != NULL, 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 (not copied, used directly) */ void fluid_sequencer_send_now(fluid_sequencer_t *seq, fluid_event_t *evt) { fluid_seq_id_t destID; fluid_list_t *tmp; fluid_return_if_fail(seq != NULL); fluid_return_if_fail(evt != NULL); destID = fluid_event_get_dest(evt); /* find callback */ tmp = seq->clients; while(tmp) { fluid_sequencer_client_t *dest = (fluid_sequencer_client_t *)tmp->data; if(dest->id == destID) { if(fluid_event_get_type(evt) == FLUID_SEQ_UNREGISTERING) { fluid_sequencer_unregister_client(seq, destID); } else { 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 (will be copied into internal queue) * @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 * * @note The sequencer sorts events according to their timestamp \c time. For events that have * the same timestamp, fluidsynth (as of version 2.2.0) uses the following order, according to * which events will be dispatched to the client's callback function. * - #FLUID_SEQ_SYSTEMRESET events precede any other event type. * - #FLUID_SEQ_UNREGISTERING events succeed #FLUID_SEQ_SYSTEMRESET and precede other event type. * - #FLUID_SEQ_NOTEON and #FLUID_SEQ_NOTE events succeed any other event type. * - Otherwise the order is undefined. * \n * Or mathematically: #FLUID_SEQ_SYSTEMRESET < #FLUID_SEQ_UNREGISTERING < ... < (#FLUID_SEQ_NOTEON && #FLUID_SEQ_NOTE) * * @warning Be careful with relative ticks when sending many events! See #fluid_event_callback_t for details. */ int fluid_sequencer_send_at(fluid_sequencer_t *seq, fluid_event_t *evt, unsigned int time, int absolute) { int res; unsigned int now = fluid_sequencer_get_tick(seq); fluid_return_val_if_fail(seq != NULL, FLUID_FAILED); fluid_return_val_if_fail(evt != NULL, FLUID_FAILED); /* set absolute */ if(!absolute) { time = now + time; } /* time stamp event */ fluid_event_set_time(evt, time); fluid_rec_mutex_lock(seq->mutex); res = fluid_seq_queue_push(seq->queue, evt); fluid_rec_mutex_unlock(seq->mutex); return res; } /** * 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_return_if_fail(seq != NULL); fluid_rec_mutex_lock(seq->mutex); fluid_seq_queue_remove(seq->queue, source, dest, type); fluid_rec_mutex_unlock(seq->mutex); } /************************************* time **************************************/ static unsigned int fluid_sequencer_get_tick_LOCAL(fluid_sequencer_t *seq, unsigned int cur_msec) { unsigned int absMs; double nowFloat; unsigned int now; fluid_return_val_if_fail(seq != NULL, 0u); absMs = seq->useSystemTimer ? (unsigned int) fluid_curtime() : cur_msec; nowFloat = ((double)(absMs - seq->startMs)) * seq->scale / 1000.0f; now = nowFloat; return seq->start_ticks + now; } /** * Get the current tick of the sequencer scaled by the time scale currently set. * * @param seq Sequencer object * @return Current tick value */ unsigned int fluid_sequencer_get_tick(fluid_sequencer_t *seq) { return fluid_sequencer_get_tick_LOCAL(seq, fluid_atomic_int_get(&seq->currentMs)); } /** * 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) * * If there are already scheduled events in the sequencer and the scale is changed * the events are adjusted accordingly. * * @note May only be called from a sequencer callback or initially when no event dispatching happens. * Otherwise it will mess up your event timing, because you have zero control over which events are * affected by the scale change. */ void fluid_sequencer_set_time_scale(fluid_sequencer_t *seq, double scale) { fluid_return_if_fail(seq != NULL); if(scale != scale) { FLUID_LOG(FLUID_WARN, "sequencer: scale NaN\n"); return; } if(scale <= 0) { FLUID_LOG(FLUID_WARN, "sequencer: scale <= 0 : %f\n", scale); return; } seq->scale = scale; seq->startMs = fluid_atomic_int_get(&seq->currentMs); seq->start_ticks = seq->cur_ticks; } /** * 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) { fluid_return_val_if_fail(seq != NULL, 0); return seq->scale; } /** * Advance a sequencer. * * @param seq Sequencer object * @param msec Time to advance sequencer to (absolute time since sequencer start). * * If you have registered the synthesizer as client (fluid_sequencer_register_fluidsynth()), the synth * will take care of calling fluid_sequencer_process(). Otherwise it is up to the user to * advance the sequencer manually. * * @since 1.1.0 */ void fluid_sequencer_process(fluid_sequencer_t *seq, unsigned int msec) { fluid_atomic_int_set(&seq->currentMs, msec); seq->cur_ticks = fluid_sequencer_get_tick_LOCAL(seq, msec); fluid_rec_mutex_lock(seq->mutex); fluid_seq_queue_process(seq->queue, seq, seq->cur_ticks); fluid_rec_mutex_unlock(seq->mutex); } /** * @internal * only used privately by fluid_seqbind and only from sequencer callback, thus lock acquire is not needed. */ void fluid_sequencer_invalidate_note(fluid_sequencer_t *seq, fluid_seq_id_t dest, fluid_note_id_t id) { fluid_seq_queue_invalidate_note_private(seq->queue, dest, id); } fluidsynth-2.2.5/src/midi/fluid_seq_queue.cpp000066400000000000000000000232151417326347500213240ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2019 Tom Moebert 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_seq_queue.h" #include #include #include /* * This is an implementation of an event queue, sorted according to their timestamp. * * Very similar to std::priority_queue. * * However, we cannot use std::priority_queue, because fluid_sequencer_remove_events() requires * access to the underlying container, which however std::priority_queue doesn't provide (without hacks). * On the other hand, implementing a priority_queue while managing our own heap structure is very * simple to do using std::make_heap() and friends. */ // we are using a deque rather than vector, because: // "Expansion of a deque is cheaper than the expansion of a std::vector because it does not involve // copying of the existing elements to a new memory location." typedef std::deque seq_queue_t; static bool event_compare(const fluid_event_t& left, const fluid_event_t& right) { bool leftIsBeforeRight; unsigned int ltime = left.time, rtime = right.time; if(ltime < rtime) { leftIsBeforeRight = true; } else if (ltime == rtime) { fluid_seq_event_type ltype = static_cast(left.type); fluid_seq_event_type rtype = static_cast(right.type); // Both events have the same tick value. Per MIDI standard, the order is undefined. However, most implementations use a FIFO ordering here, // which we cannot use, because heap sort is not stable. To make sure that fluidsynth behaves correctly from a user perspective, // we do the following: // * System reset events are always first, // * Unregistering events are second (this gives clients the chance to reset themselves before unregistering at the same tick), // * Bank changes must precede Prog changes (to ensure correct preset fallback AND preset selection within a certain bank), // * NoteOn events are always last (this makes sure that all other "state-change" events have been processed and NoteOff events // with the same key as the NoteOn have been processed (zero-length notes are not a use-case here)). // * For any other event type, the order is undefined (because the processing order of those event types doesn't matter). // See: // https://lists.nongnu.org/archive/html/fluid-dev/2019-12/msg00001.html // https://lists.nongnu.org/archive/html/fluid-dev/2017-05/msg00004.html // // The expression below ensures the order described above. // This boolean expression is written in disjunctive normal form and can be obtained by using the Karnaugh map below, // which contains // * possible values of rtype in columns, // * possible values of ltype in rows, // * the boolean values to indicate whether leftIsBeforeRight, // * X meaning any other event type, and // * the '*' means that it could be zero, but making it 1 simplyfies the boolean expression. // // | ltype \ rtype | SYSR | UNREG | BANK | PROG | NOTEON | X | // | SYSR | 1 | 1 | 1 | 1 | 1 | 1 | // | UNREG | 0 | 1 | 1 | 1 | 1 | 1 | // | BANK | 0 | 0 | 1 | 1 | 1 | 1* | // | PROG | 0 | 0 | 0 | 1 | 1 | 1* | // | NOTEON | 0 | 0 | 0 | 0 | 1 | 0 | // | X | 0 | 0 | 0 | 0 | 1 | 1 | // // The values in the diagonal (i.e. comparison with itself) must be true to make them become false after leaving this // function in order to satisfy the irreflexive requirement, i.e. assert(!(a < a)) leftIsBeforeRight = // first row in table (ltype == FLUID_SEQ_SYSTEMRESET) // the rtype NOTEON column || (rtype == FLUID_SEQ_NOTEON || rtype == FLUID_SEQ_NOTE) // the second row in table || (rtype != FLUID_SEQ_SYSTEMRESET && ltype == FLUID_SEQ_UNREGISTERING) // the third row in table || (rtype != FLUID_SEQ_SYSTEMRESET && rtype != FLUID_SEQ_UNREGISTERING && ltype == FLUID_SEQ_BANKSELECT) // the fourth row in table || (rtype != FLUID_SEQ_SYSTEMRESET && rtype != FLUID_SEQ_UNREGISTERING && rtype != FLUID_SEQ_BANKSELECT && ltype == FLUID_SEQ_PROGRAMCHANGE) // the bottom right value, i.e. any other type compared to any other type || (ltype != FLUID_SEQ_SYSTEMRESET && ltype != FLUID_SEQ_UNREGISTERING && ltype != FLUID_SEQ_BANKSELECT && ltype != FLUID_SEQ_PROGRAMCHANGE && ltype != FLUID_SEQ_NOTEON && ltype != FLUID_SEQ_NOTE && rtype != FLUID_SEQ_SYSTEMRESET && rtype != FLUID_SEQ_UNREGISTERING && rtype != FLUID_SEQ_BANKSELECT && rtype != FLUID_SEQ_PROGRAMCHANGE && rtype != FLUID_SEQ_NOTEON && rtype != FLUID_SEQ_NOTE); } else { leftIsBeforeRight = false; } // we must negate the return value, because we are building a max-heap, i.e. // the smallest element is at ::front() return !leftIsBeforeRight; } int event_compare_for_test(const fluid_event_t* left, const fluid_event_t* right) { return event_compare(*left, *right); } void* new_fluid_seq_queue(int nb_events) { try { // As workaround for a missing deque::reserve(), allocate a deque with a size of nb_events seq_queue_t* queue = new seq_queue_t(nb_events); // and clear the queue again queue->clear(); // C++11 introduced a deque::shrink_to_fit(), so hopefully not calling shrink_to_fit() will // leave the previously used memory allocated, avoiding reallocation for subsequent insertions. return queue; } catch(...) { return 0; } } void delete_fluid_seq_queue(void *que) { delete static_cast(que); } int fluid_seq_queue_push(void *que, const fluid_event_t *evt) { try { seq_queue_t& queue = *static_cast(que); queue.push_back(*evt); std::push_heap(queue.begin(), queue.end(), event_compare); return FLUID_OK; } catch(...) { return FLUID_FAILED; } } void fluid_seq_queue_remove(void *que, fluid_seq_id_t src, fluid_seq_id_t dest, int type) { seq_queue_t& queue = *static_cast(que); if(src == -1 && dest == -1 && type == -1) { // shortcut for deleting everything queue.clear(); } else { for (seq_queue_t::iterator it = queue.begin(); it != queue.end();) { if((src == -1 || it->src == src) && (dest == -1 || it->dest == dest) && (type == -1 || it->type == type)) { it = queue.erase(it); } else { ++it; } } std::make_heap(queue.begin(), queue.end(), event_compare); } } void fluid_seq_queue_invalidate_note_private(void *que, fluid_seq_id_t dest, fluid_note_id_t id) { seq_queue_t& queue = *static_cast(que); seq_queue_t::iterator event_to_invalidate = queue.end(); unsigned int earliest_noteoff_tick = std::numeric_limits::max(); for (seq_queue_t::iterator it = queue.begin(); it != queue.end(); it++) { if((it->dest == dest) && (it->type == FLUID_SEQ_NOTEOFF) && (it->id == id) && (it->time < earliest_noteoff_tick)) { earliest_noteoff_tick = it->time; event_to_invalidate = it; } } if(event_to_invalidate != queue.end()) { // Invalidate the event, by setting invalidating its destination. // Instead, removing the event from the queue would mess up the heap structure. We would need to // make_heap again, which costs time, etc... event_to_invalidate->dest = -1; } } static void fluid_seq_queue_pop(seq_queue_t &queue) { std::pop_heap(queue.begin(), queue.end(), event_compare); queue.pop_back(); } void fluid_seq_queue_process(void *que, fluid_sequencer_t *seq, unsigned int cur_ticks) { seq_queue_t& queue = *static_cast(que); while(!queue.empty()) { // Get the top most event. const fluid_event_t& top = queue.front(); if(top.time <= cur_ticks) { // First, copy it to a local buffer. // This is required because the content of the queue should be read-only to the client, // however, most client function receive a non-const fluid_event_t pointer fluid_event_t local_evt = top; // Then, pop the queue, so that client-callbacks may add new events without // messing up the heap structure while we are still processing fluid_seq_queue_pop(queue); fluid_sequencer_send_now(seq, &local_evt); } else { break; } } } fluidsynth-2.2.5/src/midi/fluid_seq_queue.h000066400000000000000000000030651417326347500207720ustar00rootroot00000000000000/* 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_SEQ_QUE_H #define _FLUID_SEQ_QUE_H #include "fluidsynth.h" #ifdef __cplusplus extern "C" { #endif #include "fluid_event.h" #include "fluid_seqbind_notes.h" void* new_fluid_seq_queue(int nbEvents); void delete_fluid_seq_queue(void *queue); int fluid_seq_queue_push(void *queue, const fluid_event_t *evt); void fluid_seq_queue_remove(void *queue, fluid_seq_id_t src, fluid_seq_id_t dest, int type); void fluid_seq_queue_process(void *que, fluid_sequencer_t *seq, unsigned int cur_ticks); void fluid_seq_queue_invalidate_note_private(void *que, fluid_seq_id_t dest, fluid_note_id_t id); int event_compare_for_test(const fluid_event_t* left, const fluid_event_t* right); #ifdef __cplusplus } #endif #endif /* _FLUID_SEQ_QUE_H */ fluidsynth-2.2.5/src/midi/fluid_seqbind.c000066400000000000000000000323341417326347500204170ustar00rootroot00000000000000/* 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.h" #include "fluid_seqbind_notes.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; void* note_container; }; typedef struct _fluid_seqbind_t fluid_seqbind_t; extern void fluid_sequencer_invalidate_note(fluid_sequencer_t *seq, fluid_seq_id_t dest, fluid_note_id_t id); 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) { fluid_return_if_fail(seqbind != NULL); 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; } delete_fluid_note_container(seqbind->note_container); FLUID_FREE(seqbind); } /** * Registers a synthesizer as a destination client of the given sequencer. * * @param seq Sequencer instance * @param synth Synthesizer instance * @returns Sequencer client ID, or #FLUID_FAILED on error. * * A convenience wrapper function around fluid_sequencer_register_client(), that allows you to * easily process and render enqueued sequencer events with fluidsynth's synthesizer. * The client being registered will be named @c fluidsynth. * * @note Implementations are encouraged to explicitly unregister this client either by calling * fluid_sequencer_unregister_client() or by sending an unregistering event to the sequencer. Before * fluidsynth 2.1.1 this was mandatory to avoid memory leaks. * * @code{.cpp} * fluid_seq_id_t seqid = fluid_sequencer_register_fluidsynth(seq, synth); * * // ... do work * * fluid_event_t* evt = new_fluid_event(); * fluid_event_set_source(evt, -1); * fluid_event_set_dest(evt, seqid); * fluid_event_unregistering(evt); * * // unregister the "fluidsynth" client immediately * fluid_sequencer_send_now(seq, evt); * delete_fluid_event(evt); * delete_fluid_synth(synth); * delete_fluid_sequencer(seq); * @endcode */ fluid_seq_id_t fluid_sequencer_register_fluidsynth(fluid_sequencer_t *seq, fluid_synth_t *synth) { fluid_seqbind_t *seqbind; fluid_return_val_if_fail(seq != NULL, FLUID_FAILED); fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); seqbind = FLUID_NEW(fluid_seqbind_t); if(seqbind == NULL) { FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); return FLUID_FAILED; } FLUID_MEMSET(seqbind, 0, sizeof(*seqbind)); seqbind->client_id = -1; seqbind->synth = synth; seqbind->seq = seq; /* 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"); FLUID_FREE(seqbind); return FLUID_FAILED; } } seqbind->note_container = new_fluid_note_container(); if(seqbind->note_container == NULL) { delete_fluid_sample_timer(seqbind->synth, seqbind->sample_timer); FLUID_FREE(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 == FLUID_FAILED) { delete_fluid_note_container(seqbind->note_container); delete_fluid_sample_timer(seqbind->synth, seqbind->sample_timer); FLUID_FREE(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_note_id_t id = fluid_event_get_id(evt); if(id != -1) { fluid_note_container_remove(seqbind->note_container, id); } fluid_synth_noteoff(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt)); } break; case FLUID_SEQ_NOTE: { unsigned int dur = fluid_event_get_duration(evt); short vel = fluid_event_get_velocity(evt); short key = fluid_event_get_key(evt); int chan = fluid_event_get_channel(evt); fluid_note_id_t id = fluid_note_compute_id(chan, key); int res = fluid_note_container_insert(seqbind->note_container, id); if(res == FLUID_FAILED) { goto err; } else if(res) { // Note is already playing ATM, the following call to fluid_synth_noteon() will kill that note. // Thus, we need to remove its noteoff from the queue fluid_sequencer_invalidate_note(seqbind->seq, seqbind->client_id, id); } else { // Note not playing, all good. } fluid_event_noteoff(evt, chan, key); fluid_event_set_id(evt, id); res = fluid_sequencer_send_at(seq, evt, dur, 0); if(res == FLUID_FAILED) { err: FLUID_LOG(FLUID_ERR, "seqbind: Unable to process FLUID_SEQ_NOTE event, something went horribly wrong"); return; } fluid_synth_noteon(synth, chan, key, vel); } break; case FLUID_SEQ_ALLSOUNDSOFF: fluid_note_container_clear(seqbind->note_container); fluid_synth_all_sounds_off(synth, fluid_event_get_channel(evt)); break; case FLUID_SEQ_ALLNOTESOFF: fluid_note_container_clear(seqbind->note_container); fluid_synth_all_notes_off(synth, fluid_event_get_channel(evt)); 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_PITCHBEND: fluid_synth_pitch_bend(synth, fluid_event_get_channel(evt), fluid_event_get_pitch(evt)); break; case FLUID_SEQ_PITCHWHEELSENS: 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: fluid_synth_cc(synth, fluid_event_get_channel(evt), MODULATION_MSB, fluid_event_get_value(evt)); break; case FLUID_SEQ_SUSTAIN: fluid_synth_cc(synth, fluid_event_get_channel(evt), SUSTAIN_SWITCH, fluid_event_get_value(evt)); break; case FLUID_SEQ_PAN: fluid_synth_cc(synth, fluid_event_get_channel(evt), PAN_MSB, fluid_event_get_value(evt)); break; case FLUID_SEQ_VOLUME: fluid_synth_cc(synth, fluid_event_get_channel(evt), VOLUME_MSB, fluid_event_get_value(evt)); break; case FLUID_SEQ_REVERBSEND: fluid_synth_cc(synth, fluid_event_get_channel(evt), EFFECTS_DEPTH1, fluid_event_get_value(evt)); break; case FLUID_SEQ_CHORUSSEND: fluid_synth_cc(synth, fluid_event_get_channel(evt), EFFECTS_DEPTH3, 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_KEYPRESSURE: fluid_synth_key_pressure(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt), fluid_event_get_value(evt)); break; case FLUID_SEQ_SYSTEMRESET: fluid_synth_system_reset(synth); break; case FLUID_SEQ_UNREGISTERING: /* free ourselves */ delete_fluid_seqbind(seqbind); break; case FLUID_SEQ_TIMER: /* nothing in fluidsynth */ break; case FLUID_SEQ_SCALE: fluid_sequencer_set_time_scale(seq, fluid_event_get_scale(evt)); 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 && (FLUID_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 * * The signature of this function is of type #handle_midi_event_func_t. * * @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; int chan; fluid_return_val_if_fail(data != NULL, FLUID_FAILED); fluid_return_val_if_fail(event != NULL, FLUID_FAILED); seq = (fluid_sequencer_t *) data; chan = fluid_midi_event_get_channel(event); fluid_event_clear(&evt); 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 KEY_PRESSURE: fluid_event_key_pressure(&evt, chan, (short)fluid_midi_event_get_key(event), (short)fluid_midi_event_get_value(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-2.2.5/src/midi/fluid_seqbind_notes.cpp000066400000000000000000000077101417326347500221670ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2019 Tom Moebert 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_seqbind_notes.h" #include /* * This is a hash container allows us to detect overlapping notes, by storing a bunch of unique integers, * that allow us to track noteOn events. * If an ID is part of the container, it means that we have received a noteOn on a certain channel and key. * Once we receive a noteOff, we remove that ID again. * * Unfortunately, I can't think of a way to detect overlapping notes by using the synth only. One might * think, that it's possible to use fluid_synth_get_voicelist(), in order to get a list of active voices * and then detect overlaps. However, this doesn't reliably work, because voices may finish, before the * noteOff is received. Think of short percussion samples spawned by long MIDI note durations. * * Here is an example of how it might look like. The "ticks" are equivalent to the time parameter passed * into fluid_event_callback_t. * fluidsynth: debug: Tick 1728: Note on chan 15, key 44, ends at tick 1824 fluidsynth: debug: Tick 1825: Normal NoteOFF on chan 15, key 44 (so far, so usual) fluidsynth: debug: Tick 1825: Note on chan 15, key 44, dur 96, ends at tick 1921 oops, the voice spawned by the previous note already finished at tick 1900, but the noteOff is yet to come fluidsynth: debug: Tick 1920: Note on chan 15, key 44, dur 143, ends at tick 2063 (Shit, we got another noteOn before the last noteOff. If we check for playing voices now, we won't find any, because they have all finished.) fluidsynth: debug: Tick 1921: Normal NoteOFF on chan 15, key 44 (... which means that we cannot detect an overlap, thus we cannot remove this noteoff, thus this noteoff will immediately kill the voice that we've just started 1 tick ago) */ typedef std::set note_container_t; // Compute a unique ID for a given channel-key combination. Think of it as a two-dimensional array index. fluid_note_id_t fluid_note_compute_id(int chan, short key) { return 128 * chan + key; } void* new_fluid_note_container() { try { note_container_t* cont = new note_container_t; return cont; } catch(...) { return 0; } } void delete_fluid_note_container(void *cont) { delete static_cast(cont); } // Returns true, if the ID was already included in the container before, false if it was just inserted and // FLUID_FAILED in case of error. int fluid_note_container_insert(void* cont, fluid_note_id_t id) { try { std::pair res = static_cast(cont)->insert(id); // res.second tells us whether the element was inserted // by inverting it, we know whether it contained the element previously return !res.second; } catch(...) { return FLUID_FAILED; } } void fluid_note_container_remove(void* cont, fluid_note_id_t id) { try { static_cast(cont)->erase(id); } catch(...) { // should never happen } } // Empties the entire collection, e.g. in case of a AllNotesOff event void fluid_note_container_clear(void* cont) { static_cast(cont)->clear(); } fluidsynth-2.2.5/src/midi/fluid_seqbind_notes.h000066400000000000000000000025421417326347500216320ustar00rootroot00000000000000/* 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_SEQBIND_NOTE_H #define _FLUID_SEQBIND_NOTE_H #include "fluidsynth.h" #include "fluid_event.h" #ifdef __cplusplus extern "C" { #endif fluid_note_id_t fluid_note_compute_id(int chan, short key); void* new_fluid_note_container(void); void delete_fluid_note_container(void *cont); int fluid_note_container_insert(void* cont, fluid_note_id_t id); void fluid_note_container_remove(void* cont, fluid_note_id_t id); void fluid_note_container_clear(void* cont); #ifdef __cplusplus } #endif #endif /* _FLUID_SEQBIND_NOTE_H */ fluidsynth-2.2.5/src/rvoice/000077500000000000000000000000001417326347500160035ustar00rootroot00000000000000fluidsynth-2.2.5/src/rvoice/fluid_adsr_env.c000066400000000000000000000025731417326347500211420ustar00rootroot00000000000000/* 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" DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data) { fluid_adsr_env_t *env = obj; fluid_adsr_env_section_t section = param[0].i; unsigned int count = param[1].i; fluid_real_t coeff = param[2].real; fluid_real_t increment = param[3].real; fluid_real_t min = param[4].real; fluid_real_t max = param[5].real; 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-2.2.5/src/rvoice/fluid_adsr_env.h000066400000000000000000000100171417326347500211370ustar00rootroot00000000000000/* 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; } else { env->count++; } env->val = x; } /* This one cannot be inlined since it is referenced in the event queue */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data); static FLUID_INLINE void fluid_adsr_env_reset(fluid_adsr_env_t *env) { env->count = 0; env->section = 0; env->val = 0.0f; } static FLUID_INLINE fluid_real_t fluid_adsr_env_get_val(fluid_adsr_env_t *env) { return env->val; } static FLUID_INLINE void fluid_adsr_env_set_val(fluid_adsr_env_t *env, fluid_real_t val) { env->val = val; } static FLUID_INLINE fluid_adsr_env_section_t fluid_adsr_env_get_section(fluid_adsr_env_t *env) { return env->section; } static FLUID_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 FLUID_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-2.2.5/src/rvoice/fluid_chorus.c000066400000000000000000001135651417326347500206500ustar00rootroot00000000000000/* 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 chorus 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 - Complete rewrite using lfo computed on the fly, first order all-pass interpolator and adding stereo unit: Jean-Jacques Ceresa, Jul 2019 */ /* * Chorus effect. * * Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ): * * ________ * direct signal (not implemented) >-->| | * _________ | | * mono | | | | * input ---+---->| delay 1 |-------------------------->| Stereo |---> right * | |_________| | | output * | /|\ | Unit | * : | | | * : +-----------------+ |(width) | * : | Delay control 1 |<-+ | | * : +-----------------+ | | |---> left * | _________ | | | output * | | | | | | * +---->| delay n |-------------------------->| | * |_________| | | | * /|\ | |________| * | | +--------------+ /|\ * +-----------------+ | |mod depth (ms)| | * | Delay control n |<-*--|lfo speed (Hz)| gain-out * +-----------------+ +--------------+ * * * The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n). * * The chorus unit process a monophonic input signal and produces stereo output * controlled by WIDTH macro. * Actually WIDTH is fixed to maximum value. But in the future, we could add a * setting (e.g "synth.chorus.width") allowing the user to get a gradually stereo * effect from minimum (monophonic) to maximum stereo effect. * * Delays lines are implemented using only one line for all chorus blocks. * Each chorus block has it own lfo (sinus/triangle). Each lfo are out of phase * to produce uncorrelated signal at the output of the delay line (this simulates * the presence of individual line for each block). Each lfo modulates the length * of the line using a depth modulation value and lfo frequency value common to * all lfos. * * LFO modulators are computed on the fly, instead of using lfo lookup table. * The advantages are: * - Avoiding a lost of 608272 memory bytes when lfo speed is low (0.3Hz). * - Allows to diminish the lfo speed lower limit to 0.1Hz instead of 0.3Hz. * A speed of 0.1 is interesting for chorus. Using a lookuptable for 0.1Hz * would require too much memory (1824816 bytes). * - Interpolation make use of first order all-pass interpolator instead of * bandlimited interpolation. * - Although lfo modulator is computed on the fly, cpu load is lower than * using lfo lookup table with bandlimited interpolator. */ #include "fluid_chorus.h" #include "fluid_sys.h" /*------------------------------------------------------------------------------------- Private --------------------------------------------------------------------------------------*/ // #define DEBUG_PRINT // allows message to be printed on the console. #define MAX_CHORUS 99 /* number maximum of block */ #define MAX_LEVEL 10 /* max output level */ #define MIN_SPEED_HZ 0.1 /* min lfo frequency (Hz) */ #define MAX_SPEED_HZ 5 /* max lfo frequency (Hz) */ /* WIDTH [0..10] value define a stereo separation between left and right. When 0, the output is monophonic. When > 0 , the output is stereophonic. Actually WIDTH is fixed to maximum value. But in the future we could add a setting to allow a gradually stereo effect from minimum (monophonic) to maximum stereo effect. */ #define WIDTH 10 /* SCALE_WET_WIDTH is a compensation weight factor to get an output amplitude (wet) rather independent of the width setting. 0: the output amplitude is fully dependent on the width setting. >0: the output amplitude is less dependent on the width setting. With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather independent of width setting (see fluid_chorus_set()). */ #define SCALE_WET_WIDTH 0.2f #define SCALE_WET 1.0f #define MAX_SAMPLES 2048 /* delay length in sample (46.4 ms at sample rate: 44100Hz).*/ #define LOW_MOD_DEPTH 176 /* low mod_depth/2 in samples */ #define HIGH_MOD_DEPTH MAX_SAMPLES/2 /* high mod_depth in sample */ #define RANGE_MOD_DEPTH (HIGH_MOD_DEPTH - LOW_MOD_DEPTH) /* Important min max values for MOD_RATE */ /* mod rate define the rate at which the modulator is updated. Examples 50: the modulator is updated every 50 samples (less cpu cycles expensive). 1: the modulator is updated every sample (more cpu cycles expensive). */ /* MOD_RATE acceptable for max lfo speed (5Hz) and max modulation depth (46.6 ms) */ #define LOW_MOD_RATE 5 /* MOD_RATE acceptable for low modulation depth (8 ms) */ #define HIGH_MOD_RATE 4 /* MOD_RATE acceptable for max modulation depth (46.6 ms) */ /* and max lfo speed (5 Hz) */ #define RANGE_MOD_RATE (HIGH_MOD_RATE - LOW_MOD_RATE) /* some chorus cpu_load measurement dependent of modulation rate: mod_rate (number of chorus blocks: 2) No stero unit: mod_rate | chorus cpu load(%) | one voice cpu load (%) ---------------------------------------------------- 50 | 0.204 | 5 | 0.256 | 0.169 1 | 0.417 | With stero unit: mod_rate | chorus cpu load(%) | one voice cpu load (%) ---------------------------------------------------- 50 | 0.220 | 5 | 0.274 | 0.169 1 | 0.465 | */ /* Number of samples to add to the desired length of the delay line. This allows to take account of rounding error interpolation when using large modulation depth. 1 is sufficient for max modulation depth (46.6 ms) and max lfo speed (5 Hz). */ //#define INTERP_SAMPLES_NBR 0 #define INTERP_SAMPLES_NBR 1 /*----------------------------------------------------------------------------- Sinusoidal modulator -----------------------------------------------------------------------------*/ /* modulator */ typedef struct { fluid_real_t a1; /* Coefficient: a1 = 2 * cos(w) */ fluid_real_t buffer1; /* buffer1 */ fluid_real_t buffer2; /* buffer2 */ fluid_real_t reset_buffer2;/* reset value of buffer2 */ } sinus_modulator; /*----------------------------------------------------------------------------- Triangle modulator -----------------------------------------------------------------------------*/ typedef struct { fluid_real_t freq; /* Osc. Frequency (in Hertz) */ fluid_real_t val; /* internal current value */ fluid_real_t inc; /* increment value */ } triang_modulator; /*----------------------------------------------------------------------------- modulator -----------------------------------------------------------------------------*/ typedef struct { /*-------------*/ int line_out; /* current line out position for this modulator */ /*-------------*/ sinus_modulator sinus; /* sinus lfo */ triang_modulator triang; /* triangle lfo */ /*-------------------------*/ /* first order All-Pass interpolator members */ fluid_real_t frac_pos_mod; /* fractional position part between samples */ /* previous value used when interpolating using fractional */ fluid_real_t buffer; } modulator; /* 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 sample_rate; /* width control: 0 monophonic, > 0 more stereophonic */ fluid_real_t width; fluid_real_t wet1, wet2; fluid_real_t *line; /* buffer line */ int size; /* effective internal size (in samples) */ int line_in; /* line in position */ /* center output position members */ fluid_real_t center_pos_mod; /* center output position modulated by modulator */ int mod_depth; /* modulation depth (in samples) */ /* variable rate control of center output position */ int index_rate; /* index rate to know when to update center_pos_mod */ int mod_rate; /* rate at which center_pos_mod is updated */ /* modulator member */ modulator mod[MAX_CHORUS]; /* sinus/triangle modulator */ }; /*----------------------------------------------------------------------------- Sets the frequency of sinus oscillator. @param mod pointer on modulator structure. @param freq frequency of the oscillator in Hz. @param sample_rate sample rate on audio output in Hz. @param phase initial phase of the oscillator in degree (0 to 360). -----------------------------------------------------------------------------*/ static void set_sinus_frequency(sinus_modulator *mod, float freq, float sample_rate, float phase) { fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* initial angle */ fluid_real_t a; mod->a1 = 2 * FLUID_COS(w); a = (2 * FLUID_M_PI / 360) * phase; mod->buffer2 = FLUID_SIN(a - w); /* y(n-1) = sin(-intial angle) */ mod->buffer1 = FLUID_SIN(a); /* y(n) = sin(initial phase) */ mod->reset_buffer2 = FLUID_SIN(FLUID_M_PI / 2 - w); /* reset value for PI/2 */ } /*----------------------------------------------------------------------------- Gets current value of sinus modulator: y(n) = a1 . y(n-1) - y(n-2) out = a1 . buffer1 - buffer2 @param mod pointer on modulator structure. @return current value of the modulator sine wave. -----------------------------------------------------------------------------*/ static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod) { fluid_real_t out; out = mod->a1 * mod->buffer1 - mod->buffer2; mod->buffer2 = mod->buffer1; if(out >= 1.0f) /* reset in case of instability near PI/2 */ { out = 1.0f; /* forces output to the right value */ mod->buffer2 = mod->reset_buffer2; } if(out <= -1.0f) /* reset in case of instability near -PI/2 */ { out = -1.0f; /* forces output to the right value */ mod->buffer2 = - mod->reset_buffer2; } mod->buffer1 = out; return out; } /*----------------------------------------------------------------------------- Set the frequency of triangular oscillator The frequency is converted in a slope value. The initial value is set according to frac_phase which is a position in the period relative to the beginning of the period. For example: 0 is the beginning of the period, 1/4 is at 1/4 of the period relative to the beginning. @param mod pointer on modulator structure. @param freq frequency of the oscillator in Hz. @param sample_rate sample rate on audio output in Hz. @param frac_phase initial phase (see comment above). -----------------------------------------------------------------------------*/ static void set_triangle_frequency(triang_modulator *mod, float freq, float sample_rate, float frac_phase) { fluid_real_t ns_period; /* period in numbers of sample */ if(freq <= 0.0) { freq = 0.5f; } mod->freq = freq; ns_period = sample_rate / freq; /* the slope of a triangular osc (0 up to +1 down to -1 up to 0....) is equivalent to the slope of a saw osc (0 -> +4) */ mod->inc = 4 / ns_period; /* positive slope */ /* The initial value and the sign of the slope depend of initial phase: initial value = = (ns_period * frac_phase) * slope */ mod->val = ns_period * frac_phase * mod->inc; if(1.0 <= mod->val && mod->val < 3.0) { mod->val = 2.0 - mod->val; /* 1.0 down to -1.0 */ mod->inc = -mod->inc; /* negative slope */ } else if(3.0 <= mod->val) { mod->val = mod->val - 4.0; /* -1.0 up to +1.0. */ } /* else val < 1.0 */ } /*----------------------------------------------------------------------------- Get current value of triangular oscillator y(n) = y(n-1) + dy @param mod pointer on triang_modulator structure. @return current value. -----------------------------------------------------------------------------*/ static FLUID_INLINE fluid_real_t get_mod_triang(triang_modulator *mod) { mod->val = mod->val + mod->inc ; if(mod->val >= 1.0) { mod->inc = -mod->inc; return 1.0; } if(mod->val <= -1.0) { mod->inc = -mod->inc; return -1.0; } return mod->val; } /*----------------------------------------------------------------------------- Reads the sample value out of the modulated delay line. @param chorus pointer on chorus unit. @param mod pointer on modulator structure. @return current value. -----------------------------------------------------------------------------*/ static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus, modulator *mod) { fluid_real_t out_index; /* new modulated index position */ int int_out_index; /* integer part of out_index */ fluid_real_t out; /* value to return */ /* Checks if the modulator must be updated (every mod_rate samples). */ /* Important: center_pos_mod must be used immediately for the first sample. So, mdl->index_rate must be initialized to mdl->mod_rate (new_mod_delay_line()) */ if(chorus->index_rate >= chorus->mod_rate) { /* out_index = center position (center_pos_mod) + sinus waweform */ if(chorus->type == FLUID_CHORUS_MOD_SINE) { out_index = chorus->center_pos_mod + get_mod_sinus(&mod->sinus) * chorus->mod_depth; } else { out_index = chorus->center_pos_mod + get_mod_triang(&mod->triang) * chorus->mod_depth; } /* extracts integer part in int_out_index */ if(out_index >= 0.0f) { int_out_index = (int)out_index; /* current integer part */ /* forces read index (line_out) with integer modulation value */ /* Boundary check and circular motion as needed */ if((mod->line_out = int_out_index) >= chorus->size) { mod->line_out -= chorus->size; } } else /* negative */ { int_out_index = (int)(out_index - 1); /* previous integer part */ /* forces read index (line_out) with integer modulation value */ /* circular motion as needed */ mod->line_out = int_out_index + chorus->size; } /* extracts fractionnal part. (it will be used when interpolating between line_out and line_out +1) and memorize it. Memorizing is necessary for modulation rate above 1 */ mod->frac_pos_mod = out_index - int_out_index; } /* First order all-pass interpolation ----------------------------------*/ /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */ /* begins interpolation: read current sample */ out = chorus->line[mod->line_out]; /* updates line_out to the next sample. Boundary check and circular motion as needed */ if(++mod->line_out >= chorus->size) { mod->line_out -= chorus->size; } /* Fractional interpolation between next sample (at next position) and previous output added to current sample. */ out += mod->frac_pos_mod * (chorus->line[mod->line_out] - mod->buffer); mod->buffer = out; /* memorizes current output */ return out; } /*----------------------------------------------------------------------------- Push a sample val into the delay line @param dl delay line to push value into. @param val the value to push into dl. -----------------------------------------------------------------------------*/ #define push_in_delay_line(dl, val) \ {\ dl->line[dl->line_in] = val;\ /* Incrementation and circular motion if necessary */\ if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\ }\ /*----------------------------------------------------------------------------- Initialize : mod_rate, center_pos_mod, and index rate center_pos_mod is initialized so that the delay between center_pos_mod and line_in is: mod_depth + INTERP_SAMPLES_NBR. @param chorus pointer on chorus unit. -----------------------------------------------------------------------------*/ static void set_center_position(fluid_chorus_t *chorus) { int center; /* Sets the modulation rate. This rate defines how often the center position (center_pos_mod ) is modulated . The value is expressed in samples. The default value is 1 that means that center_pos_mod is updated at every sample. For example with a value of 2, the center position position will be updated only one time every 2 samples only. */ chorus->mod_rate = LOW_MOD_RATE; /* default modulation rate */ /* compensate mod rate for high modulation depth */ if(chorus->mod_depth > LOW_MOD_DEPTH) { int delta_mod_depth = (chorus->mod_depth - LOW_MOD_DEPTH); chorus->mod_rate += (delta_mod_depth * RANGE_MOD_RATE) / RANGE_MOD_DEPTH; } /* Initializes the modulated center position (center_pos_mod) so that: - the delay between center_pos_mod and line_in is: mod_depth + INTERP_SAMPLES_NBR. */ center = chorus->line_in - (INTERP_SAMPLES_NBR + chorus->mod_depth); if(center < 0) { center += chorus->size; } chorus->center_pos_mod = (fluid_real_t)center; /* index rate to control when to update center_pos_mod */ /* Important: must be set to get center_pos_mod immediately used for the reading of first sample (see get_mod_delay()) */ chorus->index_rate = chorus->mod_rate; } /*----------------------------------------------------------------------------- Update internal parameters dependent of sample rate. - mod_depth. - mod_rate, center_pos_mod, and index rate. - modulators frequency. @param chorus, pointer on chorus unit. -----------------------------------------------------------------------------*/ static void update_parameters_from_sample_rate(fluid_chorus_t *chorus) { int i; /* initialize modulation depth (peak to peak) (in samples) */ /* convert modulation depth in ms to sample number */ chorus->mod_depth = (int)(chorus->depth_ms / 1000.0 * chorus->sample_rate); /* the delay line is fixed. So we reduce mod_depth (if necessary) */ if(chorus->mod_depth > MAX_SAMPLES) { FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES); chorus->mod_depth = MAX_SAMPLES; /* set depth_ms to maximum to avoid spamming console with above warning */ chorus->depth_ms = (chorus->mod_depth * 1000) / chorus->sample_rate; } chorus->mod_depth /= 2; /* amplitude is peak to peek / 2 */ #ifdef DEBUG_PRINT printf("depth_ms:%f, depth_samples/2:%d\n", chorus->depth_ms, chorus->mod_depth); #endif /* Initializes the modulated center position: mod_rate, center_pos_mod, and index rate. */ set_center_position(chorus); /* must be called before set_xxxx_frequency() */ #ifdef DEBUG_PRINT printf("mod_rate:%d\n", chorus->mod_rate); #endif /* initialize modulator frequency */ for(i = 0; i < chorus->number_blocks; i++) { set_sinus_frequency(&chorus->mod[i].sinus, chorus->speed_Hz * chorus->mod_rate, chorus->sample_rate, /* phase offset between modulators waveform */ (float)((360.0f / (float) chorus->number_blocks) * i)); set_triangle_frequency(&chorus->mod[i].triang, chorus->speed_Hz * chorus->mod_rate, chorus->sample_rate, /* phase offset between modulators waveform */ (float)i / chorus->number_blocks); } } /*----------------------------------------------------------------------------- Modulated delay line initialization. Sets the length line ( alloc delay samples). Remark: the function sets the internal size according to the length delay_length. The size is augmented by INTERP_SAMPLES_NBR to take account of interpolation. @param chorus, pointer on chorus unit. @param delay_length the length of the delay line in samples. @return FLUID_OK if success , FLUID_FAILED if memory error. Return FLUID_OK if success, FLUID_FAILED if memory error. -----------------------------------------------------------------------------*/ static int new_mod_delay_line(fluid_chorus_t *chorus, int delay_length) { /*-----------------------------------------------------------------------*/ /* checks parameter */ if(delay_length < 1) { return FLUID_FAILED; } chorus->mod_depth = 0; /*----------------------------------------------------------------------- allocates delay_line and initialize members: - line, size, line_in... */ /* total size of the line: size = INTERP_SAMPLES_NBR + delay_length */ chorus->size = delay_length + INTERP_SAMPLES_NBR; chorus->line = FLUID_ARRAY(fluid_real_t, chorus->size); if(! chorus->line) { return FLUID_FAILED; } /* clears the buffer: - delay line - interpolator member: buffer, frac_pos_mod */ fluid_chorus_reset(chorus); /* Initializes line_in to the start of the buffer */ chorus->line_in = 0; /*------------------------------------------------------------------------ Initializes modulation members: - modulation rate (the speed at which center_pos_mod is modulated: mod_rate - modulated center position: center_pos_mod - index rate to know when to update center_pos_mod:index_rate -------------------------------------------------------------------------*/ /* Initializes the modulated center position: mod_rate, center_pos_mod, and index rate */ set_center_position(chorus); return FLUID_OK; } /*----------------------------------------------------------------------------- API ------------------------------------------------------------------------------*/ /** * Create the chorus unit. Once created the chorus have no parameters set, so * fluid_chorus_set() must be called at least one time after calling * new_fluid_chorus(). * * @param sample_rate, audio sample rate in Hz. * @return pointer on chorus unit. */ fluid_chorus_t * new_fluid_chorus(fluid_real_t sample_rate) { 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; #ifdef DEBUG_PRINT printf("fluid_chorus_t:%d bytes\n", sizeof(fluid_chorus_t)); printf("fluid_real_t:%d bytes\n", sizeof(fluid_real_t)); #endif #ifdef DEBUG_PRINT printf("NEW_MOD\n"); #endif if(new_mod_delay_line(chorus, MAX_SAMPLES) == FLUID_FAILED) { delete_fluid_chorus(chorus); return NULL; } return chorus; } /** * Delete the chorus unit. * @param chorus pointer on chorus unit returned by new_fluid_chorus(). */ void delete_fluid_chorus(fluid_chorus_t *chorus) { fluid_return_if_fail(chorus != NULL); FLUID_FREE(chorus->line); FLUID_FREE(chorus); } /** * Clear the internal delay line and associate filter. * @param chorus pointer on chorus unit returned by new_fluid_chorus(). */ void fluid_chorus_reset(fluid_chorus_t *chorus) { int i; unsigned int u; /* reset delay line */ for(i = 0; i < chorus->size; i++) { chorus->line[i] = 0; } /* reset modulators's allpass filter */ for(u = 0; u < FLUID_N_ELEMENTS(chorus->mod); u++) { /* initializes 1st order All-Pass interpolator members */ chorus->mod[u].buffer = 0; /* previous delay sample value */ chorus->mod[u].frac_pos_mod = 0; /* fractional position (between consecutives sample) */ } } /** * 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.1-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, fluid_real_t level, fluid_real_t speed, fluid_real_t depth_ms, int type) { if(set & FLUID_CHORUS_SET_NR) /* number of block */ { chorus->number_blocks = nr; } if(set & FLUID_CHORUS_SET_LEVEL) /* output level */ { chorus->level = level; } if(set & FLUID_CHORUS_SET_SPEED) /* lfo frequency (in Hz) */ { chorus->speed_Hz = speed; } if(set & FLUID_CHORUS_SET_DEPTH) /* modulation depth (in ms) */ { chorus->depth_ms = depth_ms; } if(set & FLUID_CHORUS_SET_TYPE) /* lfo shape (sinus, triangle) */ { chorus->type = type; } /* check min , max parameters */ 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; } 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 > MAX_LEVEL) { FLUID_LOG(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! " "Setting it to 0.1."); chorus->level = 0.1; } /* update parameters dependent of sample rate */ update_parameters_from_sample_rate(chorus); #ifdef DEBUG_PRINT printf("lfo type:%d\n", chorus->type); printf("speed_Hz:%f\n", chorus->speed_Hz); #endif /* Initialize the lfo waveform */ if((chorus->type != FLUID_CHORUS_MOD_SINE) && (chorus->type != FLUID_CHORUS_MOD_TRIANGLE)) { FLUID_LOG(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave."); chorus->type = FLUID_CHORUS_MOD_SINE; } #ifdef DEBUG_PRINT if(chorus->type == FLUID_CHORUS_MOD_SINE) { printf("lfo: sinus\n"); } else { printf("lfo: triangle\n"); } printf("nr:%d\n", chorus->number_blocks); #endif /* Recalculate internal values after parameters change */ /* Note: Actually WIDTH is fixed to maximum value. But in the future we could add a setting "synth.chorus.width" to allow a gradually stereo effect from minimum (monophonic) to maximum stereo effect. If this setting will be added, remove the following instruction. */ chorus->width = WIDTH; { /* The stereo amplitude equation (wet1 and wet2 below) have a tendency to produce high amplitude with high width values ( 1 < width < 10). This results in an unwanted noisy output clipped by the audio card. To avoid this dependency, we divide by (1 + chorus->width * SCALE_WET_WIDTH) Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting), the output amplitude (wet) seems rather independent of width setting */ fluid_real_t wet = chorus->level * SCALE_WET ; /* wet1 and wet2 are used by the stereo effect controlled by the width setting for producing a stereo ouptput from a monophonic chorus signal. Please see the note above about a side effect tendency */ if(chorus->number_blocks > 1) { wet = wet / (1.0f + chorus->width * SCALE_WET_WIDTH); chorus->wet1 = wet * (chorus->width / 2.0f + 0.5f); chorus->wet2 = wet * ((1.0f - chorus->width) / 2.0f); #ifdef DEBUG_PRINT printf("width:%f\n", chorus->width); if(chorus->width > 0) { printf("nr > 1, width > 0 => out stereo\n"); } else { printf("nr > 1, width:0 =>out mono\n"); } #endif } else { /* only one chorus block */ if(chorus->width == 0.0) { /* wet1 and wet2 should make stereo output monomophic */ chorus->wet1 = chorus->wet2 = wet; } else { /* for width > 0, wet1 and wet2 should make stereo output stereo with only one block. This will only possible by inverting the unique signal on each left and right output. Note however that with only one block, it isn't possible to have a graduate width effect */ chorus->wet1 = wet; chorus->wet2 = -wet; /* inversion */ } #ifdef DEBUG_PRINT printf("width:%f\n", chorus->width); if(chorus->width != 0) { printf("one block, width > 0 => out stereo\n"); } else { printf("one block, width:0 => out mono\n"); } #endif } } } /* * Applies a sample rate change on the chorus. * Note that while the chorus is used by calling any fluid_chorus_processXXX() * function, calling fluid_chorus_samplerate_change() isn't multi task safe. * To deal properly with this issue follow the steps: * 1) Stop chorus processing (i.e disable calling to any fluid_chorus_processXXX(). * chorus functions. * 2) Change sample rate by calling fluid_chorus_samplerate_change(). * 3) Restart chorus processing (i.e enabling calling any fluid_chorus_processXXX() * chorus functions. * * Another solution is to substitute step (2): * 2.1) delete the chorus by calling delete_fluid_chorus(). * 2.2) create the chorus by calling new_fluid_chorus(). * * @param chorus pointer on the chorus. * @param sample_rate new sample rate value. */ void fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate) { chorus->sample_rate = sample_rate; /* update parameters dependent of sample rate */ update_parameters_from_sample_rate(chorus); } /** * Process chorus by mixing the result in output buffer. * @param chorus pointer on chorus unit returned by new_fluid_chorus(). * @param in, pointer on monophonic input buffer of FLUID_BUFSIZE samples. * @param left_out, right_out, pointers on stereo output buffers of * FLUID_BUFSIZE samples. */ void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int sample_index; int i; fluid_real_t d_out[2]; /* output stereo Left and Right */ /* foreach sample, process output sample then input sample */ for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { fluid_real_t out; /* block output */ d_out[0] = d_out[1] = 0.0f; /* clear stereo unit input */ #if 0 /* Debug: Listen to the chorus signal only */ left_out[sample_index] = 0; right_out[sample_index] = 0; #endif ++chorus->index_rate; /* modulator rate */ /* foreach chorus block, process output sample */ for(i = 0; i < chorus->number_blocks; i++) { /* get sample from the output of modulated delay line */ out = get_mod_delay(chorus, &chorus->mod[i]); /* accumulate out into stereo unit input */ d_out[i & 1] += out; } /* update modulator index rate and output center position */ if(chorus->index_rate >= chorus->mod_rate) { chorus->index_rate = 0; /* clear modulator index rate */ /* updates center position (center_pos_mod) to the next position specified by modulation rate */ if((chorus->center_pos_mod += chorus->mod_rate) >= chorus->size) { chorus->center_pos_mod -= chorus->size; } } /* Adjust stereo input level in case of number_blocks odd: In those case, d_out[1] level is lower than d_out[0], so we need to add out value to d_out[1] to have d_out[0] and d_out[1] balanced. */ if((i & 1) && i > 2) // i = 3,5,7... { d_out[1] += out ; } /* Write the current input sample into the circular buffer. * Note that 'in' may be aliased with 'left_out'. Hence this must be done * before "processing stereo unit" (below). This ensures input buffer * not being overwritten by stereo unit output. */ push_in_delay_line(chorus, in[sample_index]); /* process stereo unit */ /* Add the chorus stereo unit d_out to left and right output */ left_out[sample_index] += d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2; right_out[sample_index] += d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2; } } /** * Process chorus by putting the result in output buffer (no mixing). * @param chorus pointer on chorus unit returned by new_fluid_chorus(). * @param in, pointer on monophonic input buffer of FLUID_BUFSIZE samples. * @param left_out, right_out, pointers on stereo output buffers of * FLUID_BUFSIZE samples. */ /* Duplication of code ... (replaces sample data instead of mixing) */ void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int sample_index; int i; fluid_real_t d_out[2]; /* output stereo Left and Right */ /* foreach sample, process output sample then input sample */ for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { fluid_real_t out; /* block output */ d_out[0] = d_out[1] = 0.0f; /* clear stereo unit input */ #if 0 /* Debug: Listen to the chorus signal only */ left_out[sample_index] = 0; right_out[sample_index] = 0; #endif ++chorus->index_rate; /* modulator rate */ /* foreach chorus block, process output sample */ for(i = 0; i < chorus->number_blocks; i++) { /* get sample from the output of modulated delay line */ out = get_mod_delay(chorus, &chorus->mod[i]); /* accumulate out into stereo unit input */ d_out[i & 1] += out; } /* update modulator index rate and output center position */ if(chorus->index_rate >= chorus->mod_rate) { chorus->index_rate = 0; /* clear modulator index rate */ /* updates center position (center_pos_mod) to the next position specified by modulation rate */ if((chorus->center_pos_mod += chorus->mod_rate) >= chorus->size) { chorus->center_pos_mod -= chorus->size; } } /* Adjust stereo input level in case of number_blocks odd: In those case, d_out[1] level is lower than d_out[0], so we need to add out value to d_out[1] to have d_out[0] and d_out[1] balanced. */ if((i & 1) && i > 2) // i = 3,5,7... { d_out[1] += out ; } /* Write the current input sample into the circular buffer. * Note that 'in' may be aliased with 'left_out'. Hence this must be done * before "processing stereo unit" (below). This ensures input buffer * not being overwritten by stereo unit output. */ push_in_delay_line(chorus, in[sample_index]); /* process stereo unit */ /* store the chorus stereo unit d_out to left and right output */ left_out[sample_index] = d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2; right_out[sample_index] = d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2; } } fluidsynth-2.2.5/src/rvoice/fluid_chorus.h000066400000000000000000000057601417326347500206520ustar00rootroot00000000000000/* 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; /* enum describing each chorus parameter */ enum fluid_chorus_param { FLUID_CHORUS_NR, /**< number of delay line */ FLUID_CHORUS_LEVEL, /**< output level */ FLUID_CHORUS_SPEED, /**< lfo frequency */ FLUID_CHORUS_DEPTH, /**< modulation depth */ FLUID_CHORUS_TYPE, /**< type of waveform */ FLUID_CHORUS_PARAM_LAST /* number of enum fluid_chorus_param */ }; /* return a bit flag from param: 2^param */ #define FLUID_CHORPARAM_TO_SETFLAG(param) (1 << param) /** Flags for fluid_chorus_set() */ typedef enum { FLUID_CHORUS_SET_NR = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_NR), FLUID_CHORUS_SET_LEVEL = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_LEVEL), FLUID_CHORUS_SET_SPEED = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_SPEED), FLUID_CHORUS_SET_DEPTH = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_DEPTH), FLUID_CHORUS_SET_TYPE = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_TYPE), /** Value for fluid_chorus_set() which sets all chorus parameters. */ FLUID_CHORUS_SET_ALL = FLUID_CHORUS_SET_NR | FLUID_CHORUS_SET_LEVEL | FLUID_CHORUS_SET_SPEED | FLUID_CHORUS_SET_DEPTH | FLUID_CHORUS_SET_TYPE, } fluid_chorus_set_t; /* * chorus */ fluid_chorus_t *new_fluid_chorus(fluid_real_t sample_rate); void delete_fluid_chorus(fluid_chorus_t *chorus); void fluid_chorus_reset(fluid_chorus_t *chorus); void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, fluid_real_t speed, fluid_real_t depth_ms, int type); void fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate); void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); #endif /* _FLUID_CHORUS_H */ fluidsynth-2.2.5/src/rvoice/fluid_iir_filter.c000066400000000000000000000361131417326347500214660ustar00rootroot00000000000000/* 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 low- or high-pass filter with variable cutoff frequency and quality factor * for a given biquad transfer function: * b0 + b1*z^-1 + b2*z^-2 * H(z) = ------------------------ * a0 + a1*z^-1 + a2*z^-2 * * 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: Filter coefficients for the the previously filtered output signal * - dsp_b0, dsp_b1, dsp_b2: Filter coefficients for input signal * - coefficients normalized to a0 * * A couple of variables are used internally, their results are discarded: * - dsp_i: Index through the output buffer * - 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) { if(iir_filter->type == FLUID_IIR_DISABLED || iir_filter->q_lin == 0) { return; } else { /* 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(FLUID_FABS(dsp_hist1) < 1e-20f) { 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 && FLUID_FABS(dsp_b02) > 0.001f) { fluid_real_t compensate = old_b02 / dsp_b02; 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"); } } DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init) { fluid_iir_filter_t *iir_filter = obj; enum fluid_iir_filter_type type = param[0].i; enum fluid_iir_filter_flags flags = param[1].i; iir_filter->type = type; iir_filter->flags = flags; if(type != FLUID_IIR_DISABLED) { fluid_iir_filter_reset(iir_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->q_lin = 0; iir_filter->filter_startup = 1; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres) { fluid_iir_filter_t *iir_filter = obj; fluid_real_t fres = param[0].real; iir_filter->fres = fres; iir_filter->last_fres = -1.; } static fluid_real_t fluid_iir_filter_q_from_dB(fluid_real_t q_dB) { /* The generator contains 'centibels' (1/10 dB) => divide by 10 to * obtain dB */ q_dB /= 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; /* The 'sound font' Q is defined in dB. The filter needs a linear q. Convert. */ return FLUID_POW(10.0f, q_dB / 20.0f); } DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q) { fluid_iir_filter_t *iir_filter = obj; fluid_real_t q = param[0].real; int flags = iir_filter->flags; if(flags & FLUID_IIR_Q_ZERO_OFF && q <= 0.0) { q = 0; } else if(flags & FLUID_IIR_Q_LINEAR) { /* q is linear (only for user-defined filter) * increase to avoid Q being somewhere between zero and one, * which results in some strange amplified lowpass signal */ q++; } else { q = fluid_iir_filter_q_from_dB(q); } iir_filter->q_lin = q; iir_filter->filter_gain = 1.0; if(!(flags & FLUID_IIR_NO_GAIN_AMP)) { /* 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_SQRT(q); } /* The synthesis loop will have to recalculate the filter coefficients. */ iir_filter->last_fres = -1.; } static FLUID_INLINE void fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t *iir_filter, int transition_samples, fluid_real_t output_rate) { /* FLUID_IIR_Q_LINEAR may switch the filter off by setting Q==0 */ if(iir_filter->q_lin == 0) { return; } else { /* * 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 / output_rate); fluid_real_t sin_coeff = FLUID_SIN(omega); fluid_real_t cos_coeff = FLUID_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; */ /* "a" coeffs are same for all 3 available filter types */ 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 b02_temp, b1_temp; switch(iir_filter->type) { case FLUID_IIR_HIGHPASS: b1_temp = (1.0f + cos_coeff) * a0_inv * iir_filter->filter_gain; /* both b0 -and- b2 */ b02_temp = b1_temp * 0.5f; b1_temp *= -1.0f; break; case FLUID_IIR_LOWPASS: b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain; /* both b0 -and- b2 */ b02_temp = b1_temp * 0.5f; break; default: /* filter disabled, should never get here */ return; } 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(FLUID_FABS(iir_filter->b02) > 0.0001f) { fluid_real_t quota = b02_temp / iir_filter->b02; iir_filter->compensate_incr = quota < 0.5f || quota > 2.f; } /* 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.f) { fres = 5.f; } /* if filter enabled and there is a significant frequency change.. */ if(iir_filter->type != FLUID_IIR_DISABLED && FLUID_FABS(fres - iir_filter->last_fres) > 0.01f) { /* 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-2.2.5/src/rvoice/fluid_iir_filter.h000066400000000000000000000056761417326347500215050ustar00rootroot00000000000000/* 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; DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init); DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres); DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q); 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_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 { enum fluid_iir_filter_type type; /* specifies the type of this filter */ enum fluid_iir_filter_flags flags; /* additional flags to customize this filter */ /* 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-2.2.5/src/rvoice/fluid_lfo.c000066400000000000000000000005111417326347500201070ustar00rootroot00000000000000#include "fluid_lfo.h" DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr) { fluid_lfo_t *lfo = obj; fluid_real_t increment = param[0].real; lfo->increment = increment; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay) { fluid_lfo_t *lfo = obj; unsigned int delay = param[0].i; lfo->delay = delay; } fluidsynth-2.2.5/src/rvoice/fluid_lfo.h000066400000000000000000000037721417326347500201300ustar00rootroot00000000000000/* 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 "fluid_sys.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 FLUID_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 DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr); DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay); static FLUID_INLINE fluid_real_t fluid_lfo_get_val(fluid_lfo_t *lfo) { return lfo->val; } static FLUID_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-2.2.5/src/rvoice/fluid_phase.h000066400000000000000000000070371417326347500204460ustar00rootroot00000000000000/* 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 /* * phase */ #define FLUID_INTERP_BITS 8 #define FLUID_INTERP_BITS_MASK 0xff000000 #define FLUID_INTERP_BITS_SHIFT 24 #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 uint64_t 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) = ((uint64_t)(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) = (((uint64_t)(b)) << 32) \ | (uint32_t) (((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) \ ((((uint64_t)(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_t)((_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) -= (uint64_t)(b) << 32) /* Purpose: * Creates the expression a.index++. */ #define fluid_phase_index_plusplus(a) (((a) += 0x100000000LL) #endif /* _FLUID_PHASE_H */ fluidsynth-2.2.5/src/rvoice/fluid_rev.c000066400000000000000000001640131417326347500201330ustar00rootroot00000000000000/****************************************************************************** * 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 * * * FDN REVERB * * Freeverb used by fluidsynth (v.1.1.10 and previous) is based on * Schroeder-Moorer reverberator: * https://ccrma.stanford.edu/~jos/pasp/Freeverb.html * * This FDN reverberation is based on jot FDN reverberator. * https://ccrma.stanford.edu/~jos/Reverb/FDN_Late_Reverberation.html * Like Freeverb it is a late reverb which is convenient for Fluidsynth. * * * .-------------------. * .-----------------| | * | - | Feedback | * | .--------------| Matrix | * | | |___________________| * | | /|\ /|\ * \|/ | .---------. .-------. | - | .------. * .->+ ---->| Delay 0 |-|L.P.F 0|--*-------->| |-> out * .---------. | | |_________| |_______| | | | left * |Tone | | | - - | |Stereo| * In ->|corrector|--* | - - | | unit | * mono |_________| | \|/ .---------. .-------. | | |-> out * ---->+ ->| Delay 7 |-|L.P.F 7|--------*-->| | right * |_________| |_______| |______| * /|\ /|\ /|\ /|\ * | | | | * roomsize --/ | width --/ | * damp ------/ level ------/ * * It takes a monophonic input and produces a stereo output. * * The parameters are the same than for Freeverb. * Also the default response of these parameters are the same than for Freeverb: * - roomsize (0 to 1): control the reverb time from 0.7 to 12.5 s. * This reverberation time is ofen called T60DC. * * - damp (0 to 1): controls the reverb time frequency dependency. * This controls the reverb time for the frequency sample rate/2 * * When 0, the reverb time for high frequencies is the same as * for DC frequency. * When > 0, high frequencies have less reverb time than lower frequencies. * * - width (0 to 100): controls the left/right output separation. * When 0, there are no separation and the signal on left and right. * output is the same. This sounds like a monophonic signal. * When 100, the separation between left and right is maximum. * * - level (0 to 1), controls the output level reverberation. * * This FDN reverb produces a better quality reverberation tail than Freeverb with * far less ringing by using modulated delay lines that help to cancel * the building of a lot of resonances in the reverberation tail even when * using only 8 delays lines (NBR_DELAYS = 8) (default). * * The frequency density (often called "modal density" is one property that * contributes to sound quality. Although 8 lines give good result, using 12 delays * lines brings the overall frequency density quality a bit higher. * This quality augmentation is noticeable particularly when using long reverb time * (roomsize = 1) on solo instrument with long release time. Of course the cpu load * augmentation is +50% relatively to 8 lines. * * As a general rule the reverberation tail quality is easier to perceive by ear * when using: * - percussive instruments (i.e piano and others). * - long reverb time (roomsize = 1). * - no damping (damp = 0). * - Using headphone. Avoid using loud speaker, you will be quickly misguided by the * natural reverberation of the room in which you are. * * The cpu load for 8 lines is a bit lower than for freeverb (- 3%), * but higher for 12 lines (+ 41%). * * * The memory consumption is less than for freeverb * (see the results table below). * * Two macros are usable at compiler time: * - NBR_DELAYS: number of delay lines. 8 (default) or 12. * - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response of * roomsize parameter. * When this macro is not defined (the default), roomsize has the same * response that Freeverb, that is: * - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s). * * When this macro is defined, roomsize behaves linearly: * - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s). * This linear response is convenient when using GUI controls. * * -------------------------------------------------------------------------- * Compare table: * Note: the cpu load in % are relative each to other. These values are * given by the fluidsynth profile commands. * -------------------------------------------------------------------------- * reverb | NBR_DELAYS | Performances | memory size | quality * | | (cpu_load: %) | (bytes)(see note) | * ========================================================================== * freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing * | 2 x 4 all-pass | | | * ----------|--------------------------------------------------------------- * FDN | 8 | 0.650 % | 112480 | far less * modulated | |(feeverb - 3%) | (56% freeverb) | ringing * |--------------------------------------------------------------- * | 12 | 0.942 % | 168720 | best than * | |(freeverb + 41%) | (82 %freeverb) | 8 lines *--------------------------------------------------------------------------- * * Note: * Values in this column is the memory consumption for sample rate <= 44100Hz. * For sample rate > 44100Hz , multiply these values by (sample rate / 44100Hz). * For example: for sample rate 96000Hz, the memory consumed is 244760 bytes * *---------------------------------------------------------------------------- * 'Denormalise' method to avoid loss of performance. * -------------------------------------------------- * 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. */ /* 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. */ #include "fluid_rev.h" #include "fluid_sys.h" /*---------------------------------------------------------------------------- Configuration macros at compiler time. 3 macros are usable at compiler time: - NBR_DELAYs: number of delay lines. 8 (default) or 12. - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response for roomsize parameter. - DENORMALISING enable denormalising handling. -----------------------------------------------------------------------------*/ //#define INFOS_PRINT /* allows message to be printed on the console. */ /* Number of delay lines (must be only 8 or 12) 8 is the default. 12 produces a better quality but is +50% cpu expensive. */ #define NBR_DELAYS 8 /* default*/ /* response curve of parameter roomsize */ /* The default response is the same as Freeverb: - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s). when ROOMSIZE_RESPONSE_LINEAR is defined, the response is: - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s). */ //#define ROOMSIZE_RESPONSE_LINEAR /* DENORMALISING enable denormalising handling */ #define DENORMALISING #ifdef DENORMALISING #define DC_OFFSET 1e-8f #else #define DC_OFFSET 0.0f #endif /*---------------------------------------------------------------------------- Initial internal reverb settings (at reverb creation time) -----------------------------------------------------------------------------*/ /* SCALE_WET_WIDTH is a compensation weight factor to get an output amplitude (wet) rather independent of the width setting. 0: the output amplitude is fully dependent on the width setting. >0: the output amplitude is less dependent on the width setting. With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather independent of width setting (see fluid_revmodel_update()). */ #define SCALE_WET_WIDTH 0.2f /* It is best to inject the input signal less ofen. This contributes to obtain a flatter response on comb filter. So the input gain is set to 0.1 rather 1.0. */ #define FIXED_GAIN 0.1f /* input gain */ /* SCALE_WET is adjusted to 5.0 to get internal output level equivalent to freeverb */ #define SCALE_WET 5.0f /* scale output gain */ /*---------------------------------------------------------------------------- Internal FDN late reverb settings -----------------------------------------------------------------------------*/ /*-- Reverberation time settings ---------------------------------- MIN_DC_REV_TIME est defined egal to the minimum value of freeverb: MAX_DC_REV_TIME est defined egal to the maximum value of freeverb: T60DC is computed from gi and the longuest delay line in freeverb: L8 = 1617 T60 = -3 * Li * T / log10(gi) T60 = -3 * Li * / (log10(gi) * sr) - Li: length of comb filter delay line. - sr: sample rate. - gi: the feedback gain. The minimum value for freeverb correspond to gi = 0.7. with Mi = 1617, sr at 44100 Hz, and gi = 0.7 => MIN_DC_REV_TIME = 0.7 s The maximum value for freeverb correspond to gi = 0.98. with Mi = 1617, sr at 44100 Hz, and gi = 0.98 => MAX_DC_REV_TIME = 12.5 s */ #define MIN_DC_REV_TIME 0.7f /* minimum T60DC reverb time: seconds */ #define MAX_DC_REV_TIME 12.5f /* maximumm T60DC time in seconds */ #define RANGE_REV_TIME (MAX_DC_REV_TIME - MIN_DC_REV_TIME) /* macro to compute internal reverberation time versus roomsize parameter */ #define GET_DC_REV_TIME(roomsize) (MIN_DC_REV_TIME + RANGE_REV_TIME * roomsize) /*-- Modulation related settings ----------------------------------*/ /* For many instruments, the range for MOD_FREQ and MOD_DEPTH should be: MOD_DEPTH: [3..6] (in samples). MOD_FREQ: [0.5 ..2.0] (in Hz). Values below the lower limits are often not sufficient to cancel unwanted "ringing"(resonant frequency). Values above upper limits augment the unwanted "chorus". With NBR_DELAYS to 8: MOD_DEPTH must be >= 4 to cancel the unwanted "ringing".[4..6]. With NBR_DELAYS to 12: MOD_DEPTH to 3 is sufficient to cancel the unwanted "ringing".[3..6] */ #define MOD_DEPTH 4 /* modulation depth (samples)*/ #define MOD_RATE 50 /* modulation rate (samples)*/ #define MOD_FREQ 1.0f /* modulation frequency (Hz) */ /* Number of samples to add to the desired length of a delay line. This allow to take account of modulation interpolation. 1 is sufficient with MOD_DEPTH equal to 4. */ #define INTERP_SAMPLES_NBR 1 /* phase offset between modulators waveform */ #define MOD_PHASE (360.0f/(float) NBR_DELAYS) #if (NBR_DELAYS == 8) #define DELAY_L0 601 #define DELAY_L1 691 #define DELAY_L2 773 #define DELAY_L3 839 #define DELAY_L4 919 #define DELAY_L5 997 #define DELAY_L6 1061 #define DELAY_L7 1129 #elif (NBR_DELAYS == 12) #define DELAY_L0 601 #define DELAY_L1 691 #define DELAY_L2 773 #define DELAY_L3 839 #define DELAY_L4 919 #define DELAY_L5 997 #define DELAY_L6 1061 #define DELAY_L7 1093 #define DELAY_L8 1129 #define DELAY_L9 1151 #define DELAY_L10 1171 #define DELAY_L11 1187 #endif /*---------------------------------------------------------------------------*/ /* The FDN late feed back matrix: A T A = P - 2 / N * u * u N N N N N: the matrix dimension (i.e NBR_DELAYS). P: permutation matrix. u: is a colomn vector of 1. */ #define FDN_MATRIX_FACTOR (fluid_real_t)(-2.0 / NBR_DELAYS) /*---------------------------------------------------------------------------- Internal FDN late structures and static functions -----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- Delay absorbent low pass filter -----------------------------------------------------------------------------*/ typedef struct { fluid_real_t buffer; fluid_real_t b0, a1; /* filter coefficients */ } fdn_delay_lpf; /*----------------------------------------------------------------------------- Sets coefficients for delay absorbent low pass filter. @param lpf pointer on low pass filter structure. @param b0,a1 coefficients. -----------------------------------------------------------------------------*/ static void set_fdn_delay_lpf(fdn_delay_lpf *lpf, fluid_real_t b0, fluid_real_t a1) { lpf->b0 = b0; lpf->a1 = a1; } /*----------------------------------------------------------------------------- Process delay absorbent low pass filter. @param mod_delay modulated delay line. @param in, input sample. @param out output sample. -----------------------------------------------------------------------------*/ /* process low pass damping filter (input, output, delay) */ #define process_damping_filter(in,out,mod_delay) \ {\ out = in * mod_delay->dl.damping.b0 - mod_delay->dl.damping.buffer * \ mod_delay->dl.damping.a1;\ mod_delay->dl.damping.buffer = out;\ }\ /*----------------------------------------------------------------------------- Delay line : The delay line is composed of the line plus an absorbent low pass filter to get frequency dependent reverb time. -----------------------------------------------------------------------------*/ typedef struct { fluid_real_t *line; /* buffer line */ int size; /* effective internal size (in samples) */ /*-------------*/ int line_in; /* line in position */ int line_out; /* line out position */ /*-------------*/ fdn_delay_lpf damping; /* damping low pass filter */ } delay_line; /*----------------------------------------------------------------------------- Clears a delay line to DC_OFFSET float value. @param dl pointer on delay line structure -----------------------------------------------------------------------------*/ static void clear_delay_line(delay_line *dl) { int i; for(i = 0; i < dl->size; i++) { dl->line[i] = DC_OFFSET; } } /*----------------------------------------------------------------------------- Push a sample val into the delay line -----------------------------------------------------------------------------*/ #define push_in_delay_line(dl, val) \ {\ dl->line[dl->line_in] = val;\ /* Incrementation and circular motion if necessary */\ if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\ }\ /*----------------------------------------------------------------------------- Modulator for modulated delay line -----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- Sinusoidal modulator -----------------------------------------------------------------------------*/ /* modulator are integrated in modulated delay line */ typedef struct { fluid_real_t a1; /* Coefficient: a1 = 2 * cos(w) */ fluid_real_t buffer1; /* buffer1 */ fluid_real_t buffer2; /* buffer2 */ fluid_real_t reset_buffer2;/* reset value of buffer2 */ } sinus_modulator; /*----------------------------------------------------------------------------- Sets the frequency of sinus oscillator. @param mod pointer on modulator structure. @param freq frequency of the oscillator in Hz. @param sample_rate sample rate on audio output in Hz. @param phase initial phase of the oscillator in degree (0 to 360). -----------------------------------------------------------------------------*/ static void set_mod_frequency(sinus_modulator *mod, float freq, float sample_rate, float phase) { fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* initial angle */ fluid_real_t a; mod->a1 = 2 * FLUID_COS(w); a = (2 * FLUID_M_PI / 360) * phase; mod->buffer2 = FLUID_SIN(a - w); /* y(n-1) = sin(-intial angle) */ mod->buffer1 = FLUID_SIN(a); /* y(n) = sin(initial phase) */ mod->reset_buffer2 = FLUID_SIN(FLUID_M_PI / 2 - w); /* reset value for PI/2 */ } /*----------------------------------------------------------------------------- Gets current value of sinus modulator: y(n) = a1 . y(n-1) - y(n-2) out = a1 . buffer1 - buffer2 @param pointer on modulator structure. @return current value of the modulator sine wave. -----------------------------------------------------------------------------*/ static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod) { fluid_real_t out; out = mod->a1 * mod->buffer1 - mod->buffer2; mod->buffer2 = mod->buffer1; if(out >= 1.0f) /* reset in case of instability near PI/2 */ { out = 1.0f; /* forces output to the right value */ mod->buffer2 = mod->reset_buffer2; } if(out <= -1.0f) /* reset in case of instability near -PI/2 */ { out = -1.0f; /* forces output to the right value */ mod->buffer2 = - mod->reset_buffer2; } mod->buffer1 = out; return out; } /*----------------------------------------------------------------------------- Modulated delay line. The line is composed of: - the delay line with its damping low pass filter. - the sinusoidal modulator. - center output position modulated by the modulator. - variable rate control of center output position. - first order All-Pass interpolator. -----------------------------------------------------------------------------*/ typedef struct { /* delay line with damping low pass filter member */ delay_line dl; /* delayed line */ /*---------------------------*/ /* Sinusoidal modulator member */ sinus_modulator mod; /* sinus modulator */ /*-------------------------*/ /* center output position members */ fluid_real_t center_pos_mod; /* center output position modulated by modulator */ int mod_depth; /* modulation depth (in samples) */ /*-------------------------*/ /* variable rate control of center output position */ int index_rate; /* index rate to know when to update center_pos_mod */ int mod_rate; /* rate at which center_pos_mod is updated */ /*-------------------------*/ /* first order All-Pass interpolator members */ fluid_real_t frac_pos_mod; /* fractional position part between samples) */ /* previous value used when interpolating using fractional */ fluid_real_t buffer; } mod_delay_line; /*----------------------------------------------------------------------------- Return norminal delay length @param mdl, pointer on modulated delay line. -----------------------------------------------------------------------------*/ static int get_mod_delay_line_length(mod_delay_line *mdl) { return (mdl->dl.size - mdl->mod_depth - INTERP_SAMPLES_NBR); } /*----------------------------------------------------------------------------- Reads the sample value out of the modulated delay line. @param mdl, pointer on modulated delay line. @return the sample value. -----------------------------------------------------------------------------*/ static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl) { fluid_real_t out_index; /* new modulated index position */ int int_out_index; /* integer part of out_index */ fluid_real_t out; /* value to return */ /* Checks if the modulator must be updated (every mod_rate samples). */ /* Important: center_pos_mod must be used immediately for the first sample. So, mdl->index_rate must be initialized to mdl->mod_rate (set_mod_delay_line()) */ if(++mdl->index_rate >= mdl->mod_rate) { mdl->index_rate = 0; /* out_index = center position (center_pos_mod) + sinus waweform */ out_index = mdl->center_pos_mod + get_mod_sinus(&mdl->mod) * mdl->mod_depth; /* extracts integer part in int_out_index */ if(out_index >= 0.0f) { int_out_index = (int)out_index; /* current integer part */ /* forces read index (line_out) with integer modulation value */ /* Boundary check and circular motion as needed */ if((mdl->dl.line_out = int_out_index) >= mdl->dl.size) { mdl->dl.line_out -= mdl->dl.size; } } else /* negative */ { int_out_index = (int)(out_index - 1); /* previous integer part */ /* forces read index (line_out) with integer modulation value */ /* circular motion as needed */ mdl->dl.line_out = int_out_index + mdl->dl.size; } /* extracts fractionnal part. (it will be used when interpolating between line_out and line_out +1) and memorize it. Memorizing is necessary for modulation rate above 1 */ mdl->frac_pos_mod = out_index - int_out_index; /* updates center position (center_pos_mod) to the next position specified by modulation rate */ if((mdl->center_pos_mod += mdl->mod_rate) >= mdl->dl.size) { mdl->center_pos_mod -= mdl->dl.size; } } /* First order all-pass interpolation ----------------------------------*/ /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */ /* begins interpolation: read current sample */ out = mdl->dl.line[mdl->dl.line_out]; /* updates line_out to the next sample. Boundary check and circular motion as needed */ if(++mdl->dl.line_out >= mdl->dl.size) { mdl->dl.line_out -= mdl->dl.size; } /* Fractional interpolation between next sample (at next position) and previous output added to current sample. */ out += mdl->frac_pos_mod * (mdl->dl.line[mdl->dl.line_out] - mdl->buffer); mdl->buffer = out; /* memorizes current output */ return out; } /*----------------------------------------------------------------------------- Late structure -----------------------------------------------------------------------------*/ struct _fluid_late { fluid_real_t samplerate; /* sample rate */ fluid_real_t sample_rate_max; /* sample rate maximum */ /*----- High pass tone corrector -------------------------------------*/ fluid_real_t tone_buffer; fluid_real_t b1, b2; /*----- Modulated delay lines lines ----------------------------------*/ mod_delay_line mod_delay_lines[NBR_DELAYS]; /*-----------------------------------------------------------------------*/ /* Output coefficients for separate Left and right stereo outputs */ fluid_real_t out_left_gain[NBR_DELAYS]; /* Left delay lines' output gains */ fluid_real_t out_right_gain[NBR_DELAYS];/* Right delay lines' output gains*/ }; typedef struct _fluid_late fluid_late; /*----------------------------------------------------------------------------- fluidsynth reverb structure -----------------------------------------------------------------------------*/ struct _fluid_revmodel_t { /* reverb parameters */ fluid_real_t roomsize; /* acting on reverb time */ fluid_real_t damp; /* acting on frequency dependent reverb time */ fluid_real_t level, wet1, wet2; /* output level */ fluid_real_t width; /* width stereo separation */ /* fdn reverberation structure */ fluid_late late; }; /*----------------------------------------------------------------------------- Updates Reverb time and absorbent filters coefficients from parameters: @param late pointer on late structure. @param roomsize (0 to 1): acting on reverb time. @param damping (0 to 1): acting on absorbent damping filter. Design formulas: https://ccrma.stanford.edu/~jos/Reverb/First_Order_Delay_Filter_Design.html https://ccrma.stanford.edu/~jos/Reverb/Tonal_Correction_Filter.html -----------------------------------------------------------------------------*/ static void update_rev_time_damping(fluid_late *late, fluid_real_t roomsize, fluid_real_t damp) { int i; fluid_real_t sample_period = 1 / late->samplerate; /* Sampling period */ int delay_length; /* delay length */ fluid_real_t dc_rev_time; /* Reverb time at 0 Hz (in seconds) */ fluid_real_t alpha, alpha2; /*-------------------------------------------- Computes dc_rev_time and alpha ----------------------------------------------*/ { fluid_real_t gi_tmp, ai_tmp; #ifdef ROOMSIZE_RESPONSE_LINEAR /* roomsize parameter behave linearly: * - roomsize (0 to 1) controls reverb time linearly (0.7 to 10 s). * This linear response is convenient when using GUI controls. */ /*----------------------------------------- Computes dc_rev_time ------------------------------------------*/ dc_rev_time = GET_DC_REV_TIME(roomsize); delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]); /* computes gi_tmp from dc_rev_time using relation E2 */ gi_tmp = FLUID_POW(10, -3 * delay_length * sample_period / dc_rev_time); /* E2 */ #else /* roomsize parameters have the same response that Freeverb, that is: * - roomsize (0 to 1) controls concave reverb time (0.7 to 10 s). */ { /*----------------------------------------- Computes dc_rev_time ------------------------------------------*/ fluid_real_t gi_min, gi_max; /* values gi_min et gi_max are computed using E2 for the line with maximum delay */ delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]); gi_max = FLUID_POW(10, (-3 * delay_length / MAX_DC_REV_TIME) * sample_period); /* E2 */ gi_min = FLUID_POW(10, (-3 * delay_length / MIN_DC_REV_TIME) * sample_period); /* E2 */ /* gi = f(roomsize, gi_max, gi_min) */ gi_tmp = gi_min + roomsize * (gi_max - gi_min); /* Computes T60DC from gi using inverse of relation E2.*/ dc_rev_time = -3 * FLUID_M_LN10 * delay_length * sample_period / FLUID_LOGF(gi_tmp); } #endif /* ROOMSIZE_RESPONSE_LINEAR */ /*-------------------------------------------- Computes alpha ----------------------------------------------*/ /* Computes alpha from damp,ai_tmp,gi_tmp using relation R */ /* - damp (0 to 1) controls concave reverb time for fs/2 frequency (T60DC to 0) */ ai_tmp = 1.0f * damp; /* Preserve the square of R */ alpha2 = 1.f / (1.f - ai_tmp / ((20.f / 80.f) * FLUID_LOGF(gi_tmp))); alpha = FLUID_SQRT(alpha2); /* R */ } /* updates tone corrector coefficients b1,b2 from alpha */ { /* Beta = (1 - alpha) / (1 + alpha) b1 = 1/(1-beta) b2 = beta * b1 */ fluid_real_t beta = (1 - alpha) / (1 + alpha); late->b1 = 1 / (1 - beta); late->b2 = beta * late->b1; late->tone_buffer = 0.0f; } /* updates damping coefficients of all lines (gi , ai) from dc_rev_time, alpha */ for(i = 0; i < NBR_DELAYS; i++) { fluid_real_t gi, ai; /* delay length */ delay_length = get_mod_delay_line_length(&late->mod_delay_lines[i]); /* iir low pass filter gain */ gi = FLUID_POW(10, -3 * delay_length * sample_period / dc_rev_time); /* iir low pass filter feedback gain */ ai = (20.f / 80.f) * FLUID_LOGF(gi) * (1.f - 1.f / alpha2); /* b0 = gi * (1 - ai), a1 = - ai */ set_fdn_delay_lpf(&late->mod_delay_lines[i].dl.damping, gi * (1.f - ai), -ai); } } /*----------------------------------------------------------------------------- Updates stereo coefficients @param late pointer on late structure @param wet level integrated in stereo coefficients. -----------------------------------------------------------------------------*/ static void update_stereo_coefficient(fluid_late *late, fluid_real_t wet1) { int i; fluid_real_t wet; for(i = 0; i < NBR_DELAYS; i++) { /* delay lines output gains vectors Left and Right L R 0 | 1 1| 1 |-1 1| 2 | 1 -1| 3 |-1 -1| 4 | 1 1| 5 |-1 1| stereo gain = 6 | 1 -1| 7 |-1 -1| 8 | 1 1| 9 |-1 1| 10| 1 -1| 11|-1 -1| */ /* for left line: 00, ,02, ,04, ,06, ,08, ,10, ,12,... left_gain = +1 */ /* for left line: ,01, ,03, ,05, ,07, ,09, ,11,... left_gain = -1 */ wet = wet1; if(i & 1) { wet = -wet1; } late->out_left_gain[i] = wet; /* for right line: 00,01, ,04,05, ,08,09, ,12,13 right_gain = +1 */ /* for right line: ,02 ,03, ,06,07, ,10,11,... right_gain = -1 */ wet = wet1; if(i & 2) { wet = -wet1; } late->out_right_gain[i] = wet; } } /*----------------------------------------------------------------------------- fluid_late destructor. @param late pointer on late structure. -----------------------------------------------------------------------------*/ static void delete_fluid_rev_late(fluid_late *late) { int i; fluid_return_if_fail(late != NULL); /* free the delay lines */ for(i = 0; i < NBR_DELAYS; i++) { FLUID_FREE(late->mod_delay_lines[i].dl.line); } } /* Nominal delay lines length table (in samples) */ static const int nom_delay_length[NBR_DELAYS] = { DELAY_L0, DELAY_L1, DELAY_L2, DELAY_L3, DELAY_L4, DELAY_L5, DELAY_L6, DELAY_L7, #if (NBR_DELAYS == 12) DELAY_L8, DELAY_L9, DELAY_L10, DELAY_L11 #endif }; /* 1)"modal density" is one property that contributes to the quality of the reverb tail. The more is the modal density, the less are unwanted resonant frequencies build during the decay time: modal density = total delay / sample rate. Delay line's length given by static table delay_length[] are nominal to get minimum modal density of 0.15 at sample rate 44100Hz. Here we set length_factor to 2 to multiply this nominal modal density by 2. This leads to a default modal density of 0.15 * 2 = 0.3 for sample rate <= 44100. For sample rate > 44100, length_factor is multiplied by sample_rate / 44100. This ensures that the default modal density keeps inchanged. (Without this compensation, the default modal density would be diminished for new sample rate change above 44100Hz). 2)Modulated delay line contributes to diminish resonnant frequencies (often called "ringing"). Modulation depth (mod_depth) is set to nominal value of MOD_DEPTH at sample rate 44100Hz. For sample rate > 44100, mod_depth is multiplied by sample_rate / 44100. This ensures that the effect of modulated delay line remains inchanged. */ static void compensate_from_sample_rate(fluid_real_t sample_rate, fluid_real_t *mod_depth, fluid_real_t *length_factor) { *mod_depth = MOD_DEPTH; *length_factor = 2.0f; if(sample_rate > 44100.0f) { fluid_real_t sample_rate_factor = sample_rate/44100.0f; *length_factor *= sample_rate_factor; *mod_depth *= sample_rate_factor; } } /*----------------------------------------------------------------------------- Creates all modulated lines. @param late, pointer on the fnd late reverb to initialize. @param sample_rate_max, the maximum audio sample rate expected. @return FLUID_OK if success, FLUID_FAILED otherwise. -----------------------------------------------------------------------------*/ static int create_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate_max) { int i; fluid_real_t mod_depth, length_factor; /* compute mod_depth, length factor */ compensate_from_sample_rate(sample_rate_max, &mod_depth, &length_factor); late->sample_rate_max = sample_rate_max; #ifdef INFOS_PRINT // allows message to be printed on the console. printf("length_factor:%f, mod_depth:%f\n", length_factor, mod_depth); /* Print: modal density and total memory bytes */ { int i; int total_delay = 0; /* total delay in samples */ for (i = 0; i < NBR_DELAYS; i++) { int length = (length_factor * nom_delay_length[i]) + mod_depth + INTERP_SAMPLES_NBR; total_delay += length; } /* modal density and total memory bytes */ printf("modal density:%f, total delay:%d, total memory:%d bytes\n", total_delay / sample_rate_max ,total_delay , total_delay * sizeof(fluid_real_t)); } #endif for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */ { int delay_length = nom_delay_length[i] * length_factor; mod_delay_line *mdl = &late->mod_delay_lines[i]; /*-------------------------------------------------------------------*/ /* checks parameter */ if(delay_length < 1) { return FLUID_FAILED; } /* limits mod_depth to the requested delay length */ if(mod_depth >= delay_length) { FLUID_LOG(FLUID_INFO, "fdn reverb: modulation depth has been limited"); mod_depth = delay_length - 1; } /*--------------------------------------------------------------------- allocates delay lines */ /* real size of the line in use (in samples): size = INTERP_SAMPLES_NBR + mod_depth + delay_length */ mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR; mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size); if(! mdl->dl.line) { return FLUID_FAILED; } } return FLUID_OK; } /*----------------------------------------------------------------------------- Initialize all modulated lines. @param late, pointer on the fnd late reverb to initialize. @param sample_rate, the audio sample rate. @return FLUID_OK if success, FLUID_FAILED otherwise. -----------------------------------------------------------------------------*/ static void initialize_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate) { int i; fluid_real_t mod_depth, length_factor; /* update delay line parameter dependent of sample rate */ late->samplerate = sample_rate; /* compute mod_depth, length factor */ compensate_from_sample_rate(sample_rate, &mod_depth, &length_factor); for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */ { mod_delay_line *mdl = &late->mod_delay_lines[i]; int delay_length = nom_delay_length[i] * length_factor; /* limits mod_depth to the requested delay length */ if(mod_depth >= delay_length) { mod_depth = delay_length - 1; } mdl->mod_depth = mod_depth; clear_delay_line(&mdl->dl); /* clears the buffer */ /* Initializes line_in to the start of the buffer */ mdl->dl.line_in = 0; /* Initializes line_out index INTERP_SAMPLES_NBR samples after line_in so that the delay between line_out and line_in is: mod_depth + delay_length */ mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR; /* Damping low pass filter ------------------------------------------*/ mdl->dl.damping.buffer = 0; /*--------------------------------------------------------------------- Initializes modulation members: - modulated center position: center_pos_mod - modulation rate (the speed at which center_pos_mod is modulated: mod_rate - index rate to know when to update center_pos_mod:index_rate - interpolator member: buffer, frac_pos_mod ---------------------------------------------------------------------*/ /* Initializes the modulated center position (center_pos_mod) so that: - the delay between line_out and center_pos_mod is mod_depth. - the delay between center_pos_mod and line_in is delay_length. */ mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth; /* Sets the modulation rate. This rate defines how often the center position (center_pos_mod ) is modulated . The value is expressed in samples. The default value is 1 that means that center_pos_mod is updated at every sample. For example with a value of 2, the center position position will be updated only one time every 2 samples only. */ if(MOD_RATE < 1 || MOD_RATE > mdl->dl.size) { FLUID_LOG(FLUID_INFO, "fdn reverb: modulation rate is out of range"); mdl->mod_rate = 1; /* default modulation rate: every one sample */ } else { mdl->mod_rate = MOD_RATE; } /* index rate to control when to update center_pos_mod. Important: must be set to get center_pos_mod immediately used for the reading of first sample (see get_mod_delay()) */ mdl->index_rate = mdl->mod_rate; /* initializes first order All-Pass interpolator members */ mdl->buffer = 0; /* previous delay sample value */ mdl->frac_pos_mod = 0; /* frac. position (between consecutives sample) */ /* Sets local Modulators parameters: frequency and phase. Each modulateur are shifted of MOD_PHASE degree */ set_mod_frequency(&mdl->mod, MOD_FREQ * MOD_RATE, sample_rate, (float)(MOD_PHASE * i)); } } /* Clears the delay lines. @param rev pointer on the reverb. */ static void fluid_revmodel_init(fluid_revmodel_t *rev) { int i; /* clears all the delay lines */ for(i = 0; i < NBR_DELAYS; i ++) { clear_delay_line(&rev->late.mod_delay_lines[i].dl); } } /* updates internal parameters. @param rev pointer on the reverb. */ static void fluid_revmodel_update(fluid_revmodel_t *rev) { /* Recalculate internal values after parameters change */ /* The stereo amplitude equation (wet1 and wet2 below) have a tendency to produce high amplitude with high width values ( 1 < width < 100). This results in an unwanted noisy output clipped by the audio card. To avoid this dependency, we divide by (1 + rev->width * SCALE_WET_WIDTH) Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting), the output amplitude (wet) seems rather independent of width setting */ fluid_real_t wet = (rev->level * SCALE_WET) / (1.0f + rev->width * SCALE_WET_WIDTH); /* wet1 and wet2 are used by the stereo effect controlled by the width setting for producing a stereo ouptput from a monophonic reverb signal. Please see the note above about a side effect tendency */ rev->wet1 = wet * (rev->width / 2.0f + 0.5f); rev->wet2 = wet * ((1.0f - rev->width) / 2.0f); /* integrates wet1 in stereo coefficient (this will save one multiply) */ update_stereo_coefficient(&rev->late, rev->wet1); if(rev->wet1 > 0.0f) { rev->wet2 /= rev->wet1; } /* Reverberation time and damping */ update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); } /*---------------------------------------------------------------------------- Reverb API -----------------------------------------------------------------------------*/ /* * Creates a reverb. Once created the reverb have no parameters set, so * fluid_revmodel_set() must be called at least one time after calling * new_fluid_revmodel(). * * @param sample_rate_max maximum sample rate expected in Hz. * * @param sample_rate actual sample rate needed in Hz. * @return pointer on the new reverb or NULL if memory error. * Reverb API. */ fluid_revmodel_t * new_fluid_revmodel(fluid_real_t sample_rate_max, fluid_real_t sample_rate) { fluid_revmodel_t *rev; if(sample_rate <= 0) { return NULL; } rev = FLUID_NEW(fluid_revmodel_t); if(rev == NULL) { return NULL; } FLUID_MEMSET(&rev->late, 0, sizeof(fluid_late)); /*-------------------------------------------------------------------------- Create fdn late reverb. */ /* update minimum value for sample_rate_max */ if(sample_rate > sample_rate_max) { sample_rate_max = sample_rate; } /*-------------------------------------------------------------------------- Allocate the modulated delay lines */ if(create_mod_delay_lines(&rev->late, sample_rate_max) == FLUID_FAILED) { delete_fluid_revmodel(rev); return NULL; } /*-------------------------------------------------------------------------- Initialize the fdn reverb */ /* Initialize all modulated lines. */ initialize_mod_delay_lines(&rev->late, sample_rate); return rev; } /* * free the reverb. * Note that while the reverb is used by calling any fluid_revmodel_processXXX() * function, calling delete_fluid_revmodel() isn't multi task safe because * delay line are freed. To deal properly with this issue follow the steps: * * 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX(). * reverb functions. * 2) Delete the reverb by calling delete_fluid_revmodel(). * * @param rev pointer on reverb to free. * Reverb API. */ void delete_fluid_revmodel(fluid_revmodel_t *rev) { fluid_return_if_fail(rev != NULL); delete_fluid_rev_late(&rev->late); FLUID_FREE(rev); } /* * Sets one or more reverb parameters. Note this must be called at least one * time after calling new_fluid_revmodel() and before any call to * fluid_revmodel_processXXX() and fluid_revmodel_samplerate_change(). * * Note that while the reverb is used by calling any fluid_revmodel_processXXX() * function, calling fluid_revmodel_set() could produce audible clics. * If this is a problem, optionally call fluid_revmodel_reset() before calling * fluid_revmodel_set(). * * @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. * * Reverb API. */ void fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, fluid_real_t damping, fluid_real_t width, fluid_real_t level) { fluid_return_if_fail(rev != NULL); /*-----------------------------------*/ if(set & FLUID_REVMODEL_SET_ROOMSIZE) { fluid_clip(roomsize, 0.0f, 1.0f); rev->roomsize = roomsize; } /*-----------------------------------*/ if(set & FLUID_REVMODEL_SET_DAMPING) { fluid_clip(damping, 0.0f, 1.0f); rev->damp = damping; } /*-----------------------------------*/ if(set & FLUID_REVMODEL_SET_WIDTH) { rev->width = width; } /*-----------------------------------*/ if(set & FLUID_REVMODEL_SET_LEVEL) { fluid_clip(level, 0.0f, 1.0f); rev->level = level; } /* updates internal parameters */ fluid_revmodel_update(rev); } /* * Applies a sample rate change on the reverb. * fluid_revmodel_set() must be called at least one time before calling * this function. * * Note that while the reverb is used by calling any fluid_revmodel_processXXX() * function, calling fluid_revmodel_samplerate_change() isn't multi task safe. * To deal properly with this issue follow the steps: * 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX(). * reverb functions. * Optionally, call fluid_revmodel_reset() to damp the reverb. * 2) Change sample rate by calling fluid_revmodel_samplerate_change(). * 3) Restart reverb processing (i.e enabling calling of any fluid_revmodel_processXXX() * reverb functions. * * Another solution is to substitute step (2): * 2.1) delete the reverb by calling delete_fluid_revmodel(). * 2.2) create the reverb by calling new_fluid_revmodel(). * * The best solution would be that this function be called only by the same task * calling fluid_revmodel_processXXX(). * * @param rev the reverb. * @param sample_rate new sample rate value. Must be <= sample_rate_max * @return FLUID_OK if success, FLUID_FAILED if new sample rate is greater * then the maximumum sample rate set at creation time. The reverb will * continue to work but with possible lost of quality. * If this is a problem, the caller should follow steps 2.1 and 2.2. * Reverb API. */ int fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate) { int status = FLUID_OK; fluid_return_val_if_fail(rev != NULL, FLUID_FAILED); if(sample_rate > rev->late.sample_rate_max) { FLUID_LOG(FLUID_WARN, "fdn reverb: sample rate %.0f Hz is deduced to %.0f Hz\n", sample_rate, rev->late.sample_rate_max); /* Reduce sample rate to the maximum value set at creation time. The reverb will continue to work with possible lost of quality. */ sample_rate = rev->late.sample_rate_max; status = FLUID_FAILED; } /* Initialize all modulated lines according to sample rate change. */ initialize_mod_delay_lines(&rev->late, sample_rate); /* updates damping filter coefficients according to sample rate change */ update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); return status; } /* * Damps the reverb by clearing the delay lines. * @param rev the reverb. * * Reverb API. */ void fluid_revmodel_reset(fluid_revmodel_t *rev) { fluid_return_if_fail(rev != NULL); fluid_revmodel_init(rev); } /*----------------------------------------------------------------------------- * fdn reverb process replace. * @param rev pointer on reverb. * @param in monophonic buffer input (FLUID_BUFSIZE sample). * @param left_out stereo left processed output (FLUID_BUFSIZE sample). * @param right_out stereo right processed output (FLUID_BUFSIZE sample). * * The processed reverb is replacing anything there in out. * Reverb API. -----------------------------------------------------------------------------*/ void fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int i, k; fluid_real_t xn; /* mono input x(n) */ fluid_real_t out_tone_filter; /* tone corrector output */ fluid_real_t out_left, out_right; /* output stereo Left and Right */ fluid_real_t matrix_factor; /* partial matrix computation */ fluid_real_t delay_out_s; /* sample */ fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */ for(k = 0; k < FLUID_BUFSIZE; k++) { /* stereo output */ out_left = out_right = 0; #ifdef DENORMALISING /* Input is adjusted by DC_OFFSET. */ xn = (in[k]) * FIXED_GAIN + DC_OFFSET; #else xn = (in[k]) * FIXED_GAIN; #endif /*-------------------------------------------------------------------- tone correction. */ out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer; rev->late.tone_buffer = xn; xn = out_tone_filter; /*-------------------------------------------------------------------- process feedback delayed network: - xn is the input signal. - before inserting in the line input we first we get the delay lines output, filter them and compute output in delay_out[]. - also matrix_factor is computed (to simplify further matrix product) ---------------------------------------------------------------------*/ /* We begin with the modulated output delay line + damping filter */ matrix_factor = 0; for(i = 0; i < NBR_DELAYS; i++) { mod_delay_line *mdl = &rev->late.mod_delay_lines[i]; /* get current modulated output */ delay_out_s = get_mod_delay(mdl); /* process low pass damping filter (input:delay_out_s, output:delay_out_s) */ process_damping_filter(delay_out_s, delay_out_s, mdl); /* Result in delay_out[], and matrix_factor. These will be of use later during input line process */ delay_out[i] = delay_out_s; /* result in delay_out[] */ matrix_factor += delay_out_s; /* result in matrix_factor */ /* Process stereo output */ /* stereo left = left + out_left_gain * delay_out */ out_left += rev->late.out_left_gain[i] * delay_out_s; /* stereo right= right+ out_right_gain * delay_out */ out_right += rev->late.out_right_gain[i] * delay_out_s; } /* now we process the input delay line.Each input is a combination of - xn: input signal - delay_out[] the output of a delay line given by a permutation matrix P - and matrix_factor. This computes: in_delay_line = xn + (delay_out[] * matrix A) with an algorithm equivalent but faster than using a product with matrix A. */ /* matrix_factor = output sum * (-2.0)/N */ matrix_factor *= FDN_MATRIX_FACTOR; matrix_factor += xn; /* adds reverb input signal */ for(i = 1; i < NBR_DELAYS; i++) { /* delay_in[i-1] = delay_out[i] + matrix_factor */ delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl; push_in_delay_line(dl, delay_out[i] + matrix_factor); } /* last line input (NB_DELAY-1) */ /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */ { delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl; push_in_delay_line(dl, delay_out[0] + matrix_factor); } /*-------------------------------------------------------------------*/ #ifdef DENORMALISING /* Removes the DC offset */ out_left -= DC_OFFSET; out_right -= DC_OFFSET; #endif /* Calculates stereo output REPLACING anything already there: */ /* left_out[k] = out_left * rev->wet1 + out_right * rev->wet2; right_out[k] = out_right * rev->wet1 + out_left * rev->wet2; As wet1 is integrated in stereo coefficient wet 1 is now integrated in out_left and out_right, so we simplify previous relation by suppression of one multiply as this: left_out[k] = out_left + out_right * rev->wet2; right_out[k] = out_right + out_left * rev->wet2; */ left_out[k] = out_left + out_right * rev->wet2; right_out[k] = out_right + out_left * rev->wet2; } } /*----------------------------------------------------------------------------- * fdn reverb process mix. * @param rev pointer on reverb. * @param in monophonic buffer input (FLUID_BUFSIZE samples). * @param left_out stereo left processed output (FLUID_BUFSIZE samples). * @param right_out stereo right processed output (FLUID_BUFSIZE samples). * * The processed reverb is mixed in out with samples already there in out. * Reverb API. -----------------------------------------------------------------------------*/ void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int i, k; fluid_real_t xn; /* mono input x(n) */ fluid_real_t out_tone_filter; /* tone corrector output */ fluid_real_t out_left, out_right; /* output stereo Left and Right */ fluid_real_t matrix_factor; /* partial matrix term */ fluid_real_t delay_out_s; /* sample */ fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */ for(k = 0; k < FLUID_BUFSIZE; k++) { /* stereo output */ out_left = out_right = 0; #ifdef DENORMALISING /* Input is adjusted by DC_OFFSET. */ xn = (in[k]) * FIXED_GAIN + DC_OFFSET; #else xn = (in[k]) * FIXED_GAIN; #endif /*-------------------------------------------------------------------- tone correction */ out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer; rev->late.tone_buffer = xn; xn = out_tone_filter; /*-------------------------------------------------------------------- process feedback delayed network: - xn is the input signal. - before inserting in the line input we first we get the delay lines output, filter them and compute output in local delay_out[]. - also matrix_factor is computed (to simplify further matrix product). ---------------------------------------------------------------------*/ /* We begin with the modulated output delay line + damping filter */ matrix_factor = 0; for(i = 0; i < NBR_DELAYS; i++) { mod_delay_line *mdl = &rev->late.mod_delay_lines[i]; /* get current modulated output */ delay_out_s = get_mod_delay(mdl); /* process low pass damping filter (input:delay_out_s, output:delay_out_s) */ process_damping_filter(delay_out_s, delay_out_s, mdl); /* Result in delay_out[], and matrix_factor. These will be of use later during input line process */ delay_out[i] = delay_out_s; /* result in delay_out[] */ matrix_factor += delay_out_s; /* result in matrix_factor */ /* Process stereo output */ /* stereo left = left + out_left_gain * delay_out */ out_left += rev->late.out_left_gain[i] * delay_out_s; /* stereo right= right+ out_right_gain * delay_out */ out_right += rev->late.out_right_gain[i] * delay_out_s; } /* now we process the input delay line. Each input is a combination of: - xn: input signal - delay_out[] the output of a delay line given by a permutation matrix P - and matrix_factor. This computes: in_delay_line = xn + (delay_out[] * matrix A) with an algorithm equivalent but faster than using a product with matrix A. */ /* matrix_factor = output sum * (-2.0)/N */ matrix_factor *= FDN_MATRIX_FACTOR; matrix_factor += xn; /* adds reverb input signal */ for(i = 1; i < NBR_DELAYS; i++) { /* delay_in[i-1] = delay_out[i] + matrix_factor */ delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl; push_in_delay_line(dl, delay_out[i] + matrix_factor); } /* last line input (NB_DELAY-1) */ /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */ { delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl; push_in_delay_line(dl, delay_out[0] + matrix_factor); } /*-------------------------------------------------------------------*/ #ifdef DENORMALISING /* Removes the DC offset */ out_left -= DC_OFFSET; out_right -= DC_OFFSET; #endif /* Calculates stereo output MIXING anything already there: */ /* left_out[k] += out_left * rev->wet1 + out_right * rev->wet2; right_out[k] += out_right * rev->wet1 + out_left * rev->wet2; As wet1 is integrated in stereo coefficient wet 1 is now integrated in out_left and out_right, so we simplify previous relation by suppression of one multiply as this: left_out[k] += out_left + out_right * rev->wet2; right_out[k] += out_right + out_left * rev->wet2; */ left_out[k] += out_left + out_right * rev->wet2; right_out[k] += out_right + out_left * rev->wet2; } } fluidsynth-2.2.5/src/rvoice/fluid_rev.h000066400000000000000000000062511417326347500201370ustar00rootroot00000000000000/* 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; /* enum describing each reverb parameter */ enum fluid_reverb_param { FLUID_REVERB_ROOMSIZE, /**< reverb time */ FLUID_REVERB_DAMP, /**< high frequency damping */ FLUID_REVERB_WIDTH, /**< stereo width */ FLUID_REVERB_LEVEL, /**< output level */ FLUID_REVERB_PARAM_LAST /* number of enum fluid_reverb_param */ }; /* return a bit flag from param: 2^param */ #define FLUID_REVPARAM_TO_SETFLAG(param) (1 << param) /** Flags for fluid_revmodel_set() */ typedef enum { FLUID_REVMODEL_SET_ROOMSIZE = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_ROOMSIZE), FLUID_REVMODEL_SET_DAMPING = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_DAMP), FLUID_REVMODEL_SET_WIDTH = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_WIDTH), FLUID_REVMODEL_SET_LEVEL = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_LEVEL), /** Value for fluid_revmodel_set() which sets all reverb parameters. */ FLUID_REVMODEL_SET_ALL = FLUID_REVMODEL_SET_LEVEL | FLUID_REVMODEL_SET_WIDTH | FLUID_REVMODEL_SET_DAMPING | FLUID_REVMODEL_SET_ROOMSIZE, } fluid_revmodel_set_t; /* * reverb preset */ typedef struct _fluid_revmodel_presets_t { const 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_max, fluid_real_t sample_rate); void delete_fluid_revmodel(fluid_revmodel_t *rev); void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); void fluid_revmodel_processreplace(fluid_revmodel_t *rev, const 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, fluid_real_t roomsize, fluid_real_t damping, fluid_real_t width, fluid_real_t level); int fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate); #endif /* _FLUID_REV_H */ fluidsynth-2.2.5/src/rvoice/fluid_rvoice.c000066400000000000000000000771301417326347500206310ustar00rootroot00000000000000/* 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" static void fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks); /** * @return -1 if voice is quiet, 0 if voice has finished, 1 otherwise */ static FLUID_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_cb2amp(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_cb2amp(voice->dsp.attenuation) * fluid_cb2amp(FLUID_PEAK_ATTENUATION * (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_cb2amp(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 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, NULL); 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_rvoice_dsp.c). */ int fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf) { int ticks = voice->envlfo.ticks; int count, is_looping; fluid_real_t modenv_val; /******************* sample sanity check **********/ 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_LOCAL(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; /* return -1 if voice is quiet, 0 if voice has finished */ } /******************* phase **********************/ /* SF2.04 section 8.1.2 #26: * attack of modEnv is convex ?!? */ modenv_val = (fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK) ? fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv)) : fluid_adsr_env_get_val(&voice->envlfo.modenv); /* Calculate the number of samples, that the DSP loop advances * through the original waveform with each step in the output * buffer. It is the ratio between the frequencies of original * waveform and output waveform.*/ voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + voice->dsp.pitchoffset + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch + modenv_val * voice->envlfo.modenv_to_pitch) / voice->dsp.root_pitch_hz; /******************* portamento ****************/ /* pitchoffset is updated if enabled. Pitchoffset will be added to dsp pitch at next phase calculation time */ /* In most cases portamento will be disabled. Thus first verify that portamento is * enabled before updating pitchoffset and before disabling portamento when necessary, * in order to keep the performance loss at minimum. * If the algorithm would first update pitchoffset and then verify if portamento * needs to be disabled, there would be a significant performance drop on a x87 FPU */ if(voice->dsp.pitchinc > 0.0f) { /* portamento is enabled, so update pitchoffset */ voice->dsp.pitchoffset += voice->dsp.pitchinc; /* when pitchoffset reaches 0.0f, portamento is disabled */ if(voice->dsp.pitchoffset > 0.0f) { voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; } } else if(voice->dsp.pitchinc < 0.0f) { /* portamento is enabled, so update pitchoffset */ voice->dsp.pitchoffset += voice->dsp.pitchinc; /* when pitchoffset reaches 0.0f, portamento is disabled */ if(voice->dsp.pitchoffset < 0.0f) { voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; } } 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 is currently looping? */ 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. */ switch(voice->dsp.interp_method) { case FLUID_INTERP_NONE: count = fluid_rvoice_dsp_interpolate_none(&voice->dsp, dsp_buf, is_looping); break; case FLUID_INTERP_LINEAR: count = fluid_rvoice_dsp_interpolate_linear(&voice->dsp, dsp_buf, is_looping); break; case FLUID_INTERP_4THORDER: default: count = fluid_rvoice_dsp_interpolate_4th_order(&voice->dsp, dsp_buf, is_looping); break; case FLUID_INTERP_7THORDER: count = fluid_rvoice_dsp_interpolate_7th_order(&voice->dsp, dsp_buf, is_looping); 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 + modenv_val * voice->envlfo.modenv_to_fc); fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count); /* additional custom filter - only uses the fixed modulator, no lfos... */ fluid_iir_filter_calc(&voice->resonant_custom_filter, voice->dsp.output_rate, 0); fluid_iir_filter_apply(&voice->resonant_custom_filter, dsp_buf, count); return count; } /** * 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[i].target_amp = 0.0f; buffers->bufs[i].current_amp = 0.0f; } buffers->count = bufnum + 1; return FLUID_OK; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp) { fluid_rvoice_buffers_t *buffers = obj; unsigned int bufnum = param[0].i; fluid_real_t value = param[1].real; if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) { return; } buffers->bufs[bufnum].target_amp = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping) { fluid_rvoice_buffers_t *buffers = obj; unsigned int bufnum = param[0].i; int mapping = param[1].i; if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) { return; } buffers->bufs[bufnum].mapping = mapping; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset) { fluid_rvoice_t *voice = obj; 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 */ /* legato initialization */ voice->dsp.pitchoffset = 0.0; /* portamento initialization */ voice->dsp.pitchinc = 0.0; /* 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); fluid_iir_filter_reset(&voice->resonant_custom_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; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff) { fluid_rvoice_t *rvoice = obj; unsigned int min_ticks = param[0].i; fluid_rvoice_noteoff_LOCAL(rvoice, min_ticks); } static void fluid_rvoice_noteoff_LOCAL(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 equivalent amplitude during the release phase * for seamless volume transition. */ if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0) { fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol; fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * fluid_cb2amp(lfo); fluid_real_t env_value = - (((-200.f / FLUID_M_LN10) * FLUID_LOGF(amp) - lfo) / FLUID_PEAK_ATTENUATION - 1); fluid_clip(env_value, 0.0f, 1.0f); fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); } } if(fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK) { /* A voice is turned off during the attack section of the modulation * envelope. The attack section use convex scaling with pitch and filter * frequency cutoff (see fluid_rvoice_write(): modenv_val = fluid_convex(127 * modenv.val) * The other sections use linear scaling: modenv_val = modenv.val * * Calculate new modenv.val to achieve equivalent modenv_val during the release phase * for seamless pitch and filter frequency cutoff transition. */ if(fluid_adsr_env_get_val(&voice->envlfo.modenv) > 0) { fluid_real_t env_value = fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv)); fluid_clip(env_value, 0.0, 1.0); fluid_adsr_env_set_val(&voice->envlfo.modenv, env_value); } } fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE); fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); } /** * skips to Attack section * * Updates vol and attack data * Correction on volume val to achieve equivalent amplitude at noteOn legato * * @param voice the synthesis voice to be updated */ static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voice) { /* skips to Attack section */ /* Once in Attack section, current count must be reset, to be sure that the section will be not be prematurely finished. */ fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK); { /* Correction on volume val to achieve equivalent amplitude at noteOn legato */ fluid_env_data_t *env_data; fluid_real_t peak = fluid_cb2amp(voice->dsp.attenuation); fluid_real_t prev_peak = fluid_cb2amp(voice->dsp.prev_attenuation); voice->envlfo.volenv.val = (voice->envlfo.volenv.val * prev_peak) / peak; /* Correction on slope direction for Attack section */ env_data = &voice->envlfo.volenv.data[FLUID_VOICE_ENVATTACK]; if(voice->envlfo.volenv.val <= 1.0f) { /* slope attack for legato note needs to be positive from val up to 1 */ env_data->increment = 1.0f / env_data->count; env_data->min = -1.0f; env_data->max = 1.0f; } else { /* slope attack for legato note needs to be negative: from val down to 1 */ env_data->increment = -voice->envlfo.volenv.val / env_data->count; env_data->min = 1.0f; env_data->max = voice->envlfo.volenv.val; } } } /** * Used by legato Mode : multi_retrigger * see fluid_synth_noteon_mono_legato_multi_retrigger() * @param voice the synthesis voice to be updated */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack) { fluid_rvoice_t *voice = obj; int section; /* volume or modulation section */ /*------------------------------------------------------------------------- Section skip for volume envelope --------------------------------------------------------------------------*/ section = fluid_adsr_env_get_section(&voice->envlfo.volenv); if(section >= FLUID_VOICE_ENVHOLD) { /* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new volenv_val to achieve equivalent amplitude during the attack phase for seamless volume transition. */ fluid_real_t amp_cb, env_value; amp_cb = FLUID_PEAK_ATTENUATION * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)); env_value = fluid_cb2amp(amp_cb); /* a bit of optimization */ fluid_clip(env_value, 0.0, 1.0); fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); /* next, skips to Attack section */ } /* skips to Attack section from any section */ /* Update vol and attack data */ fluid_rvoice_local_retrigger_attack(voice); /*------------------------------------------------------------------------- Section skip for modulation envelope --------------------------------------------------------------------------*/ section = fluid_adsr_env_get_section(&voice->envlfo.modenv); if(section >= FLUID_VOICE_ENVHOLD) { /* DECAY, SUSTAIN,RELEASE section use linear scaling. Since v 2.1 , as recommended by soundfont 2.01/2.4 spec, ATTACK section uses convex shape (see fluid_rvoice_write() - fluid_convex()). Calculate new modenv value (new_value) for seamless attack transition. Here we need the inverse of fluid_convex() function defined as: new_value = pow(10, (1 - current_val) . FLUID_PEAK_ATTENUATION / -200 . 2.0) For performance reason we use fluid_cb2amp(Val) = pow(10, val/-200) with val = (1 - current_val) . FLUID_PEAK_ATTENUATION / 2.0 */ fluid_real_t new_value; /* new modenv value */ new_value = fluid_cb2amp((1.0f - fluid_adsr_env_get_val(&voice->envlfo.modenv)) * FLUID_PEAK_ATTENUATION / 2.0); fluid_clip(new_value, 0.0, 1.0); fluid_adsr_env_set_val(&voice->envlfo.modenv, new_value); } /* Skips from any section to ATTACK section */ fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK); } /** * sets the portamento dsp parameters: dsp.pitchoffset, dsp.pitchinc * @param voice rvoice to set portamento. * @param countinc increment count number. * @param pitchoffset pitch offset to apply to voice dsp.pitch. * * Notes: * 1) To get continuous portamento between consecutive noteOn (n1,n2,n3...), * pitchoffset is accumulated in current dsp pitchoffset. * 2) And to get constant portamento duration, dsp pitch increment is updated. */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento) { fluid_rvoice_t *voice = obj; unsigned int countinc = param[0].i; fluid_real_t pitchoffset = param[1].real; if(countinc) { voice->dsp.pitchoffset += pitchoffset; voice->dsp.pitchinc = - voice->dsp.pitchoffset / countinc; } /* Then during the voice processing (in fluid_rvoice_write()), dsp.pitchoffset will be incremented by dsp pitchinc. */ } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->dsp.output_rate = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method) { fluid_rvoice_t *voice = obj; int value = param[0].i; voice->dsp.interp_method = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->dsp.root_pitch_hz = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->dsp.pitch = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->dsp.prev_attenuation = voice->dsp.attenuation; voice->dsp.attenuation = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->dsp.min_attenuation_cB = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->envlfo.viblfo_to_pitch = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->envlfo.modlfo_to_pitch = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->envlfo.modlfo_to_vol = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->envlfo.modlfo_to_fc = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->envlfo.modenv_to_fc = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; voice->envlfo.modenv_to_pitch = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain) { fluid_rvoice_t *voice = obj; fluid_real_t value = param[0].real; 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; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start) { fluid_rvoice_t *voice = obj; int value = param[0].i; voice->dsp.start = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end) { fluid_rvoice_t *voice = obj; int value = param[0].i; voice->dsp.end = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart) { fluid_rvoice_t *voice = obj; int value = param[0].i; voice->dsp.loopstart = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend) { fluid_rvoice_t *voice = obj; int value = param[0].i; voice->dsp.loopend = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode) { fluid_rvoice_t *voice = obj; enum fluid_loop value = param[0].i; voice->dsp.samplemode = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample) { fluid_rvoice_t *voice = obj; fluid_sample_t *value = param[0].ptr; voice->dsp.sample = value; if(value) { voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; } } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff) { fluid_rvoice_t *voice = obj; fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED); fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED); } fluidsynth-2.2.5/src/rvoice/fluid_rvoice.h000066400000000000000000000202131417326347500206240ustar00rootroot00000000000000/* 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 * 24 bits => 144-4 = 140 dB dynamic range => 1.e-7 * 1.e-7 * 2 == 2.e-7 :) */ #define FLUID_NOISE_FLOOR ((fluid_real_t)2.e-7) 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 */ enum fluid_interp interp_method; enum fluid_loop samplemode; /* Flag that is set as soon as the first loop is completed. */ char has_looped; /* Flag that initiates, that sample-related parameters have to be checked. */ char check_sample_sanity_flag; fluid_sample_t *sample; /* 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) */ /* Stuff needed for portamento calculations */ fluid_real_t pitchoffset; /* the portamento range in midicents */ fluid_real_t pitchinc; /* the portamento increment in midicents */ /* 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 */ fluid_real_t attenuation; /* the attenuation in centibels */ fluid_real_t prev_attenuation; /* the previous attenuation in centibels used by fluid_rvoice_multi_retrigger_attack() */ 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 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 */ }; /* 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 { /* the actual, linearly interpolated amplitude with which the dsp sample should be mixed into the buf */ fluid_real_t current_amp; /* the desired amplitude [...] mixed into the buf (directly set by e.g. rapidly changing PAN events) */ fluid_real_t target_amp; /* Mapping to mixdown buffer index */ int mapping; } 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_iir_filter_t resonant_custom_filter; /* optional custom/general-purpose IIR resonant filter */ fluid_rvoice_buffers_t buffers; }; int fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample); /* defined in fluid_rvoice_dsp.c */ void fluid_rvoice_dsp_config(void); int fluid_rvoice_dsp_interpolate_none(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); int fluid_rvoice_dsp_interpolate_linear(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); int fluid_rvoice_dsp_interpolate_4th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); int fluid_rvoice_dsp_interpolate_7th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); /* * Combines the most significant 16 bit part of a sample with a potentially present * least sig. 8 bit part in order to create a 24 bit sample. */ static FLUID_INLINE int32_t fluid_rvoice_get_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx) { /* cast sample to unsigned type, so we can safely shift and bitwise or * without relying on undefined behaviour (should never happen anyway ofc...) */ uint32_t msb = (uint32_t)dsp_msb[idx]; uint8_t lsb = 0U; /* most soundfonts have 16 bit samples, assume that it's unlikely we * experience 24 bit samples here */ if(FLUID_UNLIKELY(dsp_lsb != NULL)) { lsb = (uint8_t)dsp_lsb[idx]; } return (int32_t)((msb << 8) | lsb); } #endif fluidsynth-2.2.5/src/rvoice/fluid_rvoice_dsp.c000066400000000000000000000647021417326347500215000ustar00rootroot00000000000000/* 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" #include "fluid_phase.h" #include "fluid_rvoice.h" #include "fluid_rvoice_dsp_tables.inc.h" /* Purpose: * * Interpolates audio data (obtains values between the samples of the original * waveform data). * * Variables loaded from the voice structure (assigned in fluid_rvoice_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) */ static FLUID_INLINE fluid_real_t fluid_rvoice_get_float_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx) { int32_t sample = fluid_rvoice_get_sample(dsp_msb, dsp_lsb, idx); return (fluid_real_t)sample; } /* 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_real_t *FLUID_RESTRICT dsp_buf, int looping) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; char *dsp_data24 = voice->sample->data24; 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; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); 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 * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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_real_t *FLUID_RESTRICT dsp_buf, int looping) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; char *dsp_data24 = voice->sample->data24; 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; fluid_real_t point; const fluid_real_t *FLUID_RESTRICT coeffs; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); /* 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 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); /* loop start */ } else { point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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_real_t *FLUID_RESTRICT dsp_buf, int looping) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; char *dsp_data24 = voice->sample->data24; 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; fluid_real_t start_point, end_point1, end_point2; const fluid_real_t *FLUID_RESTRICT coeffs; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); /* 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 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); /* last point in loop (wrap around) */ } else { start_index = voice->start; start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the point */ } /* get points off the end (loop start if looping, duplicate point if end) */ if(looping) { end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); end_point2 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1); } else { end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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_real_t *FLUID_RESTRICT dsp_buf, int looping) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; char *dsp_data24 = voice->sample->data24; 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; fluid_real_t start_points[3], end_points[3]; const fluid_real_t *FLUID_RESTRICT coeffs; /* 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); /* 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] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2); start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3); } else { start_index = voice->start; start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); end_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1); end_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 2); } else { end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * start_points[2] + coeffs[1] * start_points[1] + coeffs[2] * start_points[0] + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * start_points[1] + coeffs[1] * start_points[0] + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * start_points[0] + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + coeffs[6] * 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_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + coeffs[5] * end_points[0] + coeffs[6] * 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_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + coeffs[4] * end_points[0] + coeffs[5] * end_points[1] + coeffs[6] * 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] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2); start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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-2.2.5/src/rvoice/fluid_rvoice_dsp_tables.h000066400000000000000000000002521417326347500230250ustar00rootroot00000000000000 #ifndef _FLUID_RVOICE_DSP_TABLES_H #define _FLUID_RVOICE_DSP_TABLES_H #define FLUID_INTERP_MAX 256 #define SINC_INTERP_ORDER 7 /* 7th order constant */ #endif fluidsynth-2.2.5/src/rvoice/fluid_rvoice_event.c000066400000000000000000000140001417326347500220150ustar00rootroot00000000000000/* 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" static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *handler, const fluid_rvoice_event_t *src_event); static FLUID_INLINE void fluid_rvoice_event_dispatch(fluid_rvoice_event_t *event) { event->method(event->object, event->param); } /** * 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_int_real(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t 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.param[0].i = intparam; local_event.param[1].real = realparam; return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); } int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t method, void *object, fluid_rvoice_param_t param[MAX_EVENT_PARAMS]) { fluid_rvoice_event_t local_event; local_event.method = method; local_event.object = object; FLUID_MEMCPY(&local_event.param, param, sizeof(*param) * MAX_EVENT_PARAMS); return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); } int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t method, void *object, void *ptr) { fluid_rvoice_event_t local_event; local_event.method = method; local_event.object = object; local_event.param[0].ptr = ptr; 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 synth.polyphony!"); return FLUID_FAILED; // Buffer full... } FLUID_MEMCPY(event, src_event, sizeof(*event)); return FLUID_OK; } void fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler, fluid_rvoice_t *rvoice) { 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 queuesize, int finished_voices_size, int bufs, int fx_bufs, int fx_units, fluid_real_t sample_rate_max, fluid_real_t sample_rate, int extra_threads, int prio) { 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; fluid_atomic_int_set(&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, fx_units, sample_rate_max, sample_rate, eventhandler, extra_threads, prio); if(eventhandler->mixer == NULL) { goto error_recovery; } 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) { fluid_return_if_fail(handler != NULL); delete_fluid_rvoice_mixer(handler->mixer); delete_fluid_ringbuffer(handler->queue); delete_fluid_ringbuffer(handler->finished_voices); FLUID_FREE(handler); } fluidsynth-2.2.5/src/rvoice/fluid_rvoice_event.h000066400000000000000000000075701417326347500220400ustar00rootroot00000000000000/* 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" typedef struct _fluid_rvoice_event_t fluid_rvoice_event_t; struct _fluid_rvoice_event_t { fluid_rvoice_function_t method; void *object; fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; }; /* * Bridge between the renderer thread and the midi state thread. * fluid_rvoice_eventhandler_fetch_all() can be called in parallel * with fluid_rvoice_eventhandler_push/flush() */ struct _fluid_rvoice_eventhandler_t { fluid_ringbuffer_t *queue; /**< List of fluid_rvoice_event_t */ fluid_atomic_int_t 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 queuesize, int finished_voices_size, int bufs, int fx_bufs, int fx_units, fluid_real_t sample_rate_max, fluid_real_t sample_rate, int, int); 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 *); void fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler, fluid_rvoice_t *rvoice); 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_int_real(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t method, void *object, int intparam, fluid_real_t realparam); int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t method, void *object, void *ptr); int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t method, void *object, fluid_rvoice_param_t param[MAX_EVENT_PARAMS]); static FLUID_INLINE void fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_t *rvoice) { fluid_rvoice_eventhandler_push_ptr(handler, fluid_rvoice_mixer_add_voice, handler->mixer, rvoice); } #endif fluidsynth-2.2.5/src/rvoice/fluid_rvoice_mixer.c000066400000000000000000001522001417326347500220250ustar00rootroot00000000000000/* 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 "fluid_ladspa.h" #include "fluid_synth.h" // 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 */ #if ENABLE_MIXER_THREADS fluid_thread_t *thread; /**< Thread object */ fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */ #endif fluid_rvoice_t **finished_voices; /* List of voices who have finished */ int finished_voice_count; fluid_real_t *local_buf; int buf_count; int fx_buf_count; /** buffer to store the left part of a stereo channel to. * Specifically a two dimensional array, containing \c buf_count sample buffers * (i.e. for each synth.audio-groups), of which each contains * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples) * @note Each sample buffer is aligned to the FLUID_DEFAULT_ALIGNMENT * boundary provided that this pointer points to an aligned buffer. * So make sure to access the sample buffer by first aligning this * pointer using fluid_align_ptr() */ fluid_real_t *left_buf; /** dito, but for right part of a stereo channel */ fluid_real_t *right_buf; /** buffer to store the left part of a stereo effects channel to. * Specifically a two dimensional array, containing \c fx_buf_count buffers * (i.e. for each synth.effects-channels), of which each buffer contains * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples) */ 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 */ /* reverb shadow parameters here will be returned if queried */ double reverb_param[FLUID_REVERB_PARAM_LAST]; int reverb_on; /* reverb on/off */ fluid_chorus_t *chorus; /**< Chorus unit */ /* chorus shadow parameters here will be returned if queried */ double chorus_param[FLUID_CHORUS_PARAM_LAST]; int chorus_on; /* chorus on/off */ }; struct _fluid_rvoice_mixer_t { fluid_mixer_fx_t *fx; fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */ fluid_rvoice_eventhandler_t *eventhandler; 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 */ int fx_units; 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? */ #ifdef LADSPA fluid_ladspa_fx_t *ladspa_fx; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */ #endif #if ENABLE_MIXER_THREADS // int sleeping_threads; /**< Atomic: number of threads currently asleep */ // int active_threads; /**< Atomic: number of threads in the thread loop */ fluid_atomic_int_t threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */ fluid_atomic_int_t 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 }; #if ENABLE_MIXER_THREADS static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer); static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level); #endif static FLUID_INLINE void fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcount) { const int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units; int i, f; int dry_count = mixer->buffers.buf_count; /* dry buffers count */ int mix_fx_to_out = mixer->mix_fx_to_out; /* get mix_fx_to_out mode */ int dry_idx = 0; /* dry buffer index */ int buf_idx; /* buffer index */ int samp_idx; /* sample index in buffer */ int sample_count; /* sample count to process */ void (*reverb_process_func)(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); void (*chorus_process_func)(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); fluid_real_t *out_rev_l, *out_rev_r, *out_ch_l, *out_ch_r; // all dry unprocessed mono input is stored in the left channel fluid_real_t *in_rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); fluid_real_t *in_ch = in_rev; fluid_profile_ref_var(prof_ref); if(mix_fx_to_out) { // mix effects to first stereo channel out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); reverb_process_func = fluid_revmodel_processmix; chorus_process_func = fluid_chorus_processmix; } else { // replace effects into respective stereo effects channel out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT); reverb_process_func = fluid_revmodel_processreplace; chorus_process_func = fluid_chorus_processreplace; } if(mixer->with_reverb) { for(f = 0; f < mixer->fx_units; f++) { if(!mixer->fx[f].reverb_on) { continue; /* this reverb unit is disabled */ } buf_idx = f * fx_channels_per_unit + SYNTH_REVERB_CHANNEL; samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; sample_count = current_blockcount * FLUID_BUFSIZE; /* in mix mode, map fx out_rev at index f to a dry buffer at index dry_idx */ if(mix_fx_to_out) { /* dry buffer mapping, should be done more flexible in the future */ dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; } for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE) { reverb_process_func(mixer->fx[f].reverb, &in_rev[samp_idx], mix_fx_to_out ? &out_rev_l[dry_idx + i] : &out_rev_l[samp_idx], mix_fx_to_out ? &out_rev_r[dry_idx + i] : &out_rev_r[samp_idx]); } } fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref, 0, current_blockcount * FLUID_BUFSIZE); } if(mixer->with_chorus) { for(f = 0; f < mixer->fx_units; f++) { if(!mixer->fx[f].chorus_on) { continue; /* this chorus unit is disabled */ } buf_idx = f * fx_channels_per_unit + SYNTH_CHORUS_CHANNEL; samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; sample_count = current_blockcount * FLUID_BUFSIZE; /* in mix mode, map fx out_ch at index f to a dry buffer at index dry_idx */ if(mix_fx_to_out) { /* dry buffer mapping, should be done more flexible in the future */ dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; } for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE) { chorus_process_func(mixer->fx[f].chorus, &in_ch [samp_idx], mix_fx_to_out ? &out_ch_l[dry_idx + i] : &out_ch_l[samp_idx], mix_fx_to_out ? &out_ch_r[dry_idx + i] : &out_ch_r[samp_idx]); } } fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref, 0, current_blockcount * FLUID_BUFSIZE); } #ifdef LADSPA /* Run the signal through the LADSPA Fx unit. The buffers have already been * set up in fluid_rvoice_mixer_set_ladspa. */ if(mixer->ladspa_fx) { fluid_ladspa_run(mixer->ladspa_fx, current_blockcount, FLUID_BUFSIZE); fluid_check_fpe("LADSPA"); } #endif } /** * 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 *base_ptr; int i; const int fx_channels_per_unit = buffers->fx_buf_count / buffers->mixer->fx_units; const int offset = buffers->buf_count * 2; int with_reverb = buffers->mixer->with_reverb; int with_chorus = buffers->mixer->with_chorus; /* Set up the reverb and chorus buffers only when the effect is enabled or * when LADSPA is active. Nonexisting buffers are detected in the DSP loop. * Not sending the effect signals saves some time in that case. */ #ifdef LADSPA int with_ladspa = (buffers->mixer->ladspa_fx != NULL); with_reverb = (with_reverb | with_ladspa); with_chorus = (with_chorus | with_ladspa); #endif // all the dry, non-processed mono audio for effects is to be stored in the left buffers base_ptr = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); for(i = 0; i < buffers->mixer->fx_units; i++) { int fx_idx = i * fx_channels_per_unit; outbufs[offset + fx_idx + SYNTH_REVERB_CHANNEL] = (with_reverb) ? &base_ptr[(fx_idx + SYNTH_REVERB_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] : NULL; outbufs[offset + fx_idx + SYNTH_CHORUS_CHANNEL] = (with_chorus) ? &base_ptr[(fx_idx + SYNTH_CHORUS_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] : NULL; } /* 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. */ base_ptr = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT); for(i = 0; i < buffers->buf_count; i++) { outbufs[i * 2] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; } base_ptr = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT); for(i = 0; i < buffers->buf_count; i++) { outbufs[i * 2 + 1] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; } return offset + buffers->fx_buf_count; } 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]; } } } buffers->mixer->active_voices = av; fluid_rvoice_eventhandler_finished_voice_callback(buffers->mixer->eventhandler, v); } buffers->finished_voice_count = 0; } static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t *mixer) { #if 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 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 samples down from internal dsp_buf to output buffers * * @param buffers Destination buffer(s) * @param dsp_buf Mono sample source * @param start_block starting sample in dsp_buf * @param sample_count number of samples to mix following \c start_block * @param dest_bufs Array of buffers to mixdown to * @param dest_bufcount Length of dest_bufs (i.e count of buffers) */ static void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers, const fluid_real_t *FLUID_RESTRICT dsp_buf, int start_block, int sample_count, fluid_real_t **dest_bufs, int dest_bufcount) { /* buffers count to mixdown to */ int bufcount = buffers->count; int i, dsp_i; /* if there is nothing to mix, return immediately */ if(sample_count <= 0 || dest_bufcount <= 0) { return; } FLUID_ASSERT((uintptr_t)dsp_buf % FLUID_DEFAULT_ALIGNMENT == 0); FLUID_ASSERT((uintptr_t)(&dsp_buf[start_block * FLUID_BUFSIZE]) % FLUID_DEFAULT_ALIGNMENT == 0); /* mixdown for each buffer */ for(i = 0; i < bufcount; i++) { fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount); fluid_real_t target_amp = buffers->bufs[i].target_amp; fluid_real_t current_amp = buffers->bufs[i].current_amp; fluid_real_t amp_incr; if(buf == NULL || (current_amp == 0.0f && target_amp == 0.0f)) { continue; } amp_incr = (target_amp - current_amp) / FLUID_BUFSIZE; FLUID_ASSERT((uintptr_t)buf % FLUID_DEFAULT_ALIGNMENT == 0); /* Mixdown sample_count samples in the current buffer buf * * For the first FLUID_BUFSIZE samples, we linearly interpolate the buffers amplitude to * avoid clicks/pops when rapidly changing the channels panning (issue 768). * * We could have squashed this into one single loop by using an if clause within the loop body. * But it seems like having two separate loops is easier for compilers to understand, and therefore * auto-vectorizing the loops. */ if(sample_count < FLUID_BUFSIZE) { // scalar loop variant, the voice will have finished afterwards for(dsp_i = 0; dsp_i < sample_count; dsp_i++) { buf[start_block * FLUID_BUFSIZE + dsp_i] += current_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; current_amp += amp_incr; } } else { // here goes the vectorizable loop #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) for(dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i++) { // We cannot simply increment current_amp by amp_incr during every iteration, as this would create a dependency and prevent vectorization. buf[start_block * FLUID_BUFSIZE + dsp_i] += (current_amp + amp_incr * dsp_i) * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; } // we have reached the target_amp if(target_amp > 0) { /* Note, that this loop could be unrolled by FLUID_BUFSIZE elements */ #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) for(dsp_i = FLUID_BUFSIZE; dsp_i < sample_count; dsp_i++) { // Index by blocks (not by samples) to let the compiler know that we always start accessing // buf and dsp_buf at the FLUID_BUFSIZE*sizeof(fluid_real_t) byte boundary and never somewhere // in between. // A good compiler should understand: Aha, so I don't need to add a peel loop when vectorizing // this loop. Great. buf[start_block * FLUID_BUFSIZE + dsp_i] += target_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; } } } buffers->bufs[i].current_amp = target_amp; } } /** * 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. */ static FLUID_INLINE void fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers, fluid_rvoice_t *rvoice, fluid_real_t **dest_bufs, unsigned int dest_bufcount, fluid_real_t *src_buf, int blockcount) { int i, total_samples = 0, last_block_mixed = 0; for(i = 0; i < blockcount; i++) { /* render one block in src_buf */ int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]); if(s == -1) { /* the voice is silent, mix back all the previously rendered sound */ fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed, total_samples - (last_block_mixed * FLUID_BUFSIZE), dest_bufs, dest_bufcount); last_block_mixed = i + 1; /* future block start index to mix from */ total_samples += FLUID_BUFSIZE; /* accumulate samples count rendered */ } else { /* the voice wasn't quiet. Some samples have been rendered [0..FLUID_BUFSIZE] */ total_samples += s; if(s < FLUID_BUFSIZE) { /* voice has finished */ break; } } } /* Now mix the remaining blocks from last_block_mixed to total_sample */ fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed, total_samples - (last_block_mixed * FLUID_BUFSIZE), dest_bufs, dest_bufcount); if(total_samples < blockcount * FLUID_BUFSIZE) { /* voice has finished */ fluid_finish_rvoice(buffers, rvoice); } } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice) { int i; fluid_rvoice_mixer_t *mixer = obj; fluid_rvoice_t *voice = param[0].ptr; if(mixer->active_voices < mixer->polyphony) { mixer->rvoices[mixer->active_voices++] = voice; return; // success } /* 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; } if(mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED) { fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]); mixer->rvoices[i] = voice; return; // success } } /* This should never happen */ FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice"); return; } 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 */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony) { void *newptr; fluid_rvoice_mixer_t *handler = obj; int value = param[0].i; 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*/; } #if 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 blockcount) { 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_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT); 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, local_buf, blockcount); fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1, blockcount * FLUID_BUFSIZE); } } static FLUID_INLINE void fluid_mixer_buffers_zero(fluid_mixer_buffers_t *buffers, int current_blockcount) { int i, size = current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t); /* TODO: Optimize by only zero out the buffers we actually use later on. */ int buf_count = buffers->buf_count, fx_buf_count = buffers->fx_buf_count; fluid_real_t *FLUID_RESTRICT buf_l = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT); fluid_real_t *FLUID_RESTRICT buf_r = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT); for(i = 0; i < buf_count; i++) { FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); } buf_l = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); buf_r = fluid_align_ptr(buffers->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); for(i = 0; i < fx_buf_count; i++) { FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); } } static int fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *mixer) { static const int samplecount = FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT; buffers->mixer = mixer; buffers->buf_count = mixer->buffers.buf_count; buffers->fx_buf_count = mixer->buffers.fx_buf_count; /* Local mono voice buf */ buffers->local_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, samplecount, FLUID_DEFAULT_ALIGNMENT); /* Left and right audio buffers */ buffers->left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); buffers->right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); if((buffers->local_buf == NULL) || (buffers->left_buf == NULL) || (buffers->right_buf == NULL)) { FLUID_LOG(FLUID_ERR, "Out of memory"); return 0; } /* Effects audio buffers */ buffers->fx_left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); buffers->fx_right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); if((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == 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) */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate) { fluid_rvoice_mixer_t *mixer = obj; fluid_real_t samplerate = param[1].real; // because fluid_synth_update_mixer() puts real into arg2 int i; for(i = 0; i < mixer->fx_units; i++) { if(mixer->fx[i].chorus) { fluid_chorus_samplerate_change(mixer->fx[i].chorus, samplerate); } if(mixer->fx[i].reverb) { fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate); /* fluid_revmodel_samplerate_change() shouldn't fail if the reverb was created with sample_rate_max set to the maximum sample rate indicated in the settings. If this condition isn't respected, the reverb will continue to work but with lost of quality. */ } } #if LADSPA if(mixer->ladspa_fx != NULL) { fluid_ladspa_set_sample_rate(mixer->ladspa_fx, samplerate); } #endif } /** * @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, int fx_units, fluid_real_t sample_rate_max, fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *evthandler, int extra_threads, int prio) { int i; 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->eventhandler = evthandler; mixer->fx_units = fx_units; mixer->buffers.buf_count = buf_count; mixer->buffers.fx_buf_count = fx_buf_count * fx_units; /* allocate the reverb module */ mixer->fx = FLUID_ARRAY(fluid_mixer_fx_t, fx_units); if(mixer->fx == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } FLUID_MEMSET(mixer->fx, 0, fx_units * sizeof(*mixer->fx)); for(i = 0; i < fx_units; i++) { /* create reverb and chorus units */ mixer->fx[i].reverb = new_fluid_revmodel(sample_rate_max, sample_rate); mixer->fx[i].chorus = new_fluid_chorus(sample_rate); if(mixer->fx[i].reverb == NULL || mixer->fx[i].chorus == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } } if(!fluid_mixer_buffers_init(&mixer->buffers, mixer)) { goto error_recovery; } #if 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) { goto error_recovery; } if(fluid_rvoice_mixer_set_threads(mixer, extra_threads, prio) != FLUID_OK) { goto error_recovery; } #endif return mixer; error_recovery: delete_fluid_rvoice_mixer(mixer); return NULL; } static void fluid_mixer_buffers_free(fluid_mixer_buffers_t *buffers) { FLUID_FREE(buffers->finished_voices); /* free all the sample buffers */ FLUID_FREE(buffers->local_buf); FLUID_FREE(buffers->left_buf); FLUID_FREE(buffers->right_buf); FLUID_FREE(buffers->fx_left_buf); FLUID_FREE(buffers->fx_right_buf); } void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer) { int i; fluid_return_if_fail(mixer != NULL); #if ENABLE_MIXER_THREADS delete_rvoice_mixer_threads(mixer); 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); for(i = 0; i < mixer->fx_units; i++) { if(mixer->fx[i].reverb) { delete_fluid_revmodel(mixer->fx[i].reverb); } if(mixer->fx[i].chorus) { delete_fluid_chorus(mixer->fx[i].chorus); } } FLUID_FREE(mixer->fx); FLUID_FREE(mixer->rvoices); FLUID_FREE(mixer); } #ifdef LADSPA /** * Set a LADSPS fx instance to be used by the mixer and assign the mixer buffers * as LADSPA host buffers with sensible names */ void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer, fluid_ladspa_fx_t *ladspa_fx, int audio_groups) { mixer->ladspa_fx = ladspa_fx; if(ladspa_fx == NULL) { return; } else { fluid_real_t *main_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); fluid_real_t *main_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); fluid_real_t *rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); fluid_real_t *chor = rev; rev = &rev[SYNTH_REVERB_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; chor = &chor[SYNTH_CHORUS_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; fluid_ladspa_add_host_ports(ladspa_fx, "Main:L", audio_groups, main_l, FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); fluid_ladspa_add_host_ports(ladspa_fx, "Main:R", audio_groups, main_r, FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); fluid_ladspa_add_host_ports(ladspa_fx, "Reverb:Send", 1, rev, FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); fluid_ladspa_add_host_ports(ladspa_fx, "Chorus:Send", 1, chor, FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); } } #endif /** * set one or more reverb shadow parameters for one fx group. * These parameters will be returned if queried. * (see fluid_rvoice_mixer_reverb_get_param()) * * @param mixer that contains all fx units. * @param fx_group index of the fx group to which parameters must be set. * must be in the range [-1..mixer->fx_units[. If -1 the changes are applied to * all fx units. * @param set Flags indicating which parameters should be set (#fluid_revmodel_set_t) * @param values table of parameters values. */ void fluid_rvoice_mixer_set_reverb_full(const fluid_rvoice_mixer_t *mixer, int fx_group, int set, const double values[]) { fluid_mixer_fx_t *fx = mixer->fx; int nr_units = mixer->fx_units; if(fx_group >= 0) /* apply parameters to this fx group only */ { nr_units = fx_group + 1; } else /* apply parameters to all fx groups */ { fx_group = 0; } for(; fx_group < nr_units; fx_group++) { int param; for(param = 0; param < FLUID_REVERB_PARAM_LAST; param++) { if(set & FLUID_REVPARAM_TO_SETFLAG(param)) { fx[fx_group].reverb_param[param] = values[param]; } } } } /** * get one reverb shadow parameter for one fx group. * (see fluid_rvoice_mixer_set_reverb_full()) * * @param mixer that contains all fx group units. * @param fx_group index of the fx group to get parameter from. * must be in the range [0..mixer->fx_units[. * @param enum indicating the parameter to get. * FLUID_REVERB_ROOMSIZE, reverb room size value. * FLUID_REVERB_DAMP, reverb damping value. * FLUID_REVERB_WIDTH, reverb width value. * FLUID_REVERB_LEVEL, reverb level value. * @return value. */ double fluid_rvoice_mixer_reverb_get_param(const fluid_rvoice_mixer_t *mixer, int fx_group, int param) { return mixer->fx[fx_group].reverb_param[param]; } /** * set one or more chorus shadow parameters for one fx group. * These parameters will be returned if queried. * (see fluid_rvoice_mixer_chorus_get_param()) * * @param mixer that contains all fx units. * @param fx_group index of the fx group to which parameters must be set. * must be in the range [-1..mixer->fx_units[. If -1 the changes are applied * to all fx group. * Keep in mind, that the needed CPU time is proportional to 'nr'. * @param set Flags indicating which parameters to set (#fluid_chorus_set_t) * @param values table of pararameters. */ void fluid_rvoice_mixer_set_chorus_full(const fluid_rvoice_mixer_t *mixer, int fx_group, int set, const double values[]) { fluid_mixer_fx_t *fx = mixer->fx; int nr_units = mixer->fx_units; if(fx_group >= 0) /* apply parameters to this group fx only */ { nr_units = fx_group + 1; } else /* apply parameters to all fx units*/ { fx_group = 0; } for(; fx_group < nr_units; fx_group++) { int param; for(param = 0; param < FLUID_CHORUS_PARAM_LAST; param++) { if(set & FLUID_CHORPARAM_TO_SETFLAG(param)) { fx[fx_group].chorus_param[param] = values[param]; } } } } /** * get one chorus shadow parameter for one fx group. * (see fluid_rvoice_mixer_set_chorus_full()) * * @param mixer that contains all fx groups units. * @param fx_group index of the fx group to get parameter from. * must be in the range [0..mixer->fx_units[. * @param get Flags indicating which parameter to get (#fluid_chorus_set_t) * @return the parameter value (0.0 is returned if error) */ double fluid_rvoice_mixer_chorus_get_param(const fluid_rvoice_mixer_t *mixer, int fx_group, int param) { return mixer->fx[fx_group].chorus_param[param]; } /* @deprecated: use fluid_rvoice_mixer_reverb_enable instead */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled) { fluid_rvoice_mixer_t *mixer = obj; int on = param[0].i; mixer->with_reverb = on; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reverb_enable) { fluid_rvoice_mixer_t *mixer = obj; int fx_group = param[0].i; /* reverb fx group index */ int on = param[1].i; /* on/off */ int nr_units = mixer->fx_units; /* does on/off must be applied only to fx group at index fx_group ? */ if(fx_group >= 0) { mixer->fx[fx_group].reverb_on = on; } /* on/off must be applied to all fx groups */ else { for(fx_group = 0; fx_group < nr_units; fx_group++) { mixer->fx[fx_group].reverb_on = on; } } /* set with_reverb if at least one reverb unit is on */ for(fx_group = 0; fx_group < nr_units; fx_group++) { on = mixer->fx[fx_group].reverb_on; if(on) { break; } } mixer->with_reverb = on; } /* @deprecated: use fluid_rvoice_mixer_chorus_enable instead */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled) { fluid_rvoice_mixer_t *mixer = obj; int on = param[0].i; mixer->with_chorus = on; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_chorus_enable) { fluid_rvoice_mixer_t *mixer = obj; int fx_group = param[0].i; /* chorus fx group index */ int on = param[1].i; /* on/off */ int nr_units = mixer->fx_units; /* does on/off must be applied only to fx group at index fx_group ? */ if(fx_group >= 0) { mixer->fx[fx_group].chorus_on = on; } /* on/off must be applied to all fx groups */ else { for(fx_group = 0; fx_group < nr_units; fx_group++) { mixer->fx[fx_group].chorus_on = on; } } /* set with_chorus if at least one chorus unit is on */ for(fx_group = 0; fx_group < nr_units; fx_group++) { on = mixer->fx[fx_group].chorus_on; if(on) { break; } } mixer->with_chorus = on; } void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on) { mixer->mix_fx_to_out = on; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params) { fluid_rvoice_mixer_t *mixer = obj; int i = param[0].i; int set = param[1].i; int nr = param[2].i; fluid_real_t level = param[3].real; fluid_real_t speed = param[4].real; fluid_real_t depth_ms = param[5].real; int type = param[6].i; int nr_units = mixer->fx_units; /* does parameters must be applied only to fx group i ? */ if(i >= 0) { nr_units = i + 1; } else { i = 0; /* parameters must be applied to all fx groups */ } while(i < nr_units) { fluid_chorus_set(mixer->fx[i++].chorus, set, nr, level, speed, depth_ms, type); } } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params) { fluid_rvoice_mixer_t *mixer = obj; int i = param[0].i; /* fx group index */ int set = param[1].i; fluid_real_t roomsize = param[2].real; fluid_real_t damping = param[3].real; fluid_real_t width = param[4].real; fluid_real_t level = param[5].real; int nr_units = mixer->fx_units; /* does parameters change should be applied only to fx group i ? */ if(i >= 0) { nr_units = i + 1; /* parameters change must be applied to fx groups i */ } else { i = 0; /* parameters change must be applied to all fx groups */ } while(i < nr_units) { fluid_revmodel_set(mixer->fx[i++].reverb, set, roomsize, damping, width, level); } } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb) { fluid_rvoice_mixer_t *mixer = obj; int i; for(i = 0; i < mixer->fx_units; i++) { fluid_revmodel_reset(mixer->fx[i].reverb); } } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus) { fluid_rvoice_mixer_t *mixer = obj; int i; for(i = 0; i < mixer->fx_units; i++) { fluid_chorus_reset(mixer->fx[i].chorus); } } int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer, fluid_real_t **left, fluid_real_t **right) { *left = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); *right = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); 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 = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); *fx_right = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT); return mixer->buffers.fx_buf_count; } int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer) { return FLUID_MIXER_MAX_BUFFERS_DEFAULT; } #if WITH_PROFILING int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer) { return mixer->active_voices; } #endif #if 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 fluid_thread_return_t 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; int current_blockcount = 0; fluid_real_t *local_buf = fluid_align_ptr(buffers->local_buf, FLUID_DEFAULT_ALIGNMENT); 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) { // blockcount may have changed, since thread was put to sleep current_blockcount = mixer->current_blockcount; fluid_mixer_buffers_zero(buffers, current_blockcount); bufcount = fluid_mixer_buffers_prepare(buffers, bufs); hasValidData = 1; } // then render voice to buffers fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount, local_buf, current_blockcount); } } return FLUID_THREAD_RETURN_VALUE; } static void fluid_mixer_buffers_mix(fluid_mixer_buffers_t *dst, fluid_mixer_buffers_t *src, int current_blockcount) { int i, j; int scount = current_blockcount * FLUID_BUFSIZE; int minbuf; fluid_real_t *FLUID_RESTRICT base_src; fluid_real_t *FLUID_RESTRICT base_dst; minbuf = dst->buf_count; if(minbuf > src->buf_count) { minbuf = src->buf_count; } base_src = fluid_align_ptr(src->left_buf, FLUID_DEFAULT_ALIGNMENT); base_dst = fluid_align_ptr(dst->left_buf, FLUID_DEFAULT_ALIGNMENT); for(i = 0; i < minbuf; i++) { #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) for(j = 0; j < scount; j++) { int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; base_dst[dsp_i] += base_src[dsp_i]; } } base_src = fluid_align_ptr(src->right_buf, FLUID_DEFAULT_ALIGNMENT); base_dst = fluid_align_ptr(dst->right_buf, FLUID_DEFAULT_ALIGNMENT); for(i = 0; i < minbuf; i++) { #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) for(j = 0; j < scount; j++) { int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; base_dst[dsp_i] += base_src[dsp_i]; } } minbuf = dst->fx_buf_count; if(minbuf > src->fx_buf_count) { minbuf = src->fx_buf_count; } base_src = fluid_align_ptr(src->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); base_dst = fluid_align_ptr(dst->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); for(i = 0; i < minbuf; i++) { #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) for(j = 0; j < scount; j++) { int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; base_dst[dsp_i] += base_src[dsp_i]; } } base_src = fluid_align_ptr(src->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); base_dst = fluid_align_ptr(dst->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); for(i = 0; i < minbuf; i++) { #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) for(j = 0; j < scount; j++) { int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; base_dst[dsp_i] += base_src[dsp_i]; } } } /** * Go through all threads and see if someone is finished for mixing */ static int fluid_mixer_mix_in(fluid_rvoice_mixer_t *mixer, int extra_threads, int current_blockcount) { 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], current_blockcount); hasmixed = 1; break; } } } while(hasmixed); return result; } static void fluid_render_loop_multithread(fluid_rvoice_mixer_t *mixer, int current_blockcount) { int i, bufcount; fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT); 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, current_blockcount); 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, current_blockcount)) { // 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, local_buf, current_blockcount); fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1, current_blockcount * FLUID_BUFSIZE); //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", // current_blockcount, test, mixer->active_voices, waits); } static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer) { int i; // if no threads have been created yet (e.g. because a previous error prevented creation of threads // mutexes and condition variables), skip terminating threads if(mixer->thread_count != 0) { 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; } /** * 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 */ static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level) { char name[16]; int i; // Kill all existing threads first if(mixer->thread_count) { delete_rvoice_mixer_threads(mixer); } if(thread_count == 0) { return FLUID_OK; } // 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_FAILED; } 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_FAILED; } fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA); FLUID_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 FLUID_FAILED; } } return FLUID_OK; } #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; // Zero buffers fluid_mixer_buffers_zero(&mixer->buffers, blockcount); fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref, mixer->active_voices, blockcount * FLUID_BUFSIZE); #if ENABLE_MIXER_THREADS if(mixer->thread_count > 0) { fluid_render_loop_multithread(mixer, blockcount); } else #endif { fluid_render_loop_singlethread(mixer, blockcount); } fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref, mixer->active_voices, blockcount * FLUID_BUFSIZE); // Process reverb & chorus fluid_rvoice_mixer_process_fx(mixer, blockcount); // Call the callback and pack active voice array fluid_rvoice_mixer_process_finished_voices(mixer); return blockcount; } fluidsynth-2.2.5/src/rvoice/fluid_rvoice_mixer.h000066400000000000000000000065761417326347500220500ustar00rootroot00000000000000/* 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; 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); #if WITH_PROFILING int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer); #endif fluid_rvoice_mixer_t *new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, fluid_real_t sample_rate_max, fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *, int, int); void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *); void fluid_rvoice_mixer_set_reverb_full(const fluid_rvoice_mixer_t *mixer, int fx_group, int set, const double values[]); double fluid_rvoice_mixer_reverb_get_param(const fluid_rvoice_mixer_t *mixer, int fx_group, int param); void fluid_rvoice_mixer_set_chorus_full(const fluid_rvoice_mixer_t *mixer, int fx_group, int set, const double values[]); double fluid_rvoice_mixer_chorus_get_param(const fluid_rvoice_mixer_t *mixer, int fx_group, int param); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony); /* @deprecated */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled); /* @deprecated */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reverb_enable); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_chorus_enable); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus); void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on); #ifdef LADSPA void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer, fluid_ladspa_fx_t *ladspa_fx, int audio_groups); #endif #endif fluidsynth-2.2.5/src/sfloader/000077500000000000000000000000001417326347500163135ustar00rootroot00000000000000fluidsynth-2.2.5/src/sfloader/fluid_defsfont.c000066400000000000000000002044451417326347500214630ustar00rootroot00000000000000/* 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" #include "fluid_sfont.h" #include "fluid_sys.h" #include "fluid_synth.h" #include "fluid_samplecache.h" #include "fluid_chan.h" /* EMU8k/10k hardware applies this factor to initial attenuation generator values set at preset and * instrument level in a soundfont. We apply this factor when loading the generator values to stay * compatible as most existing soundfonts expect exactly this (strange, non-standard) behaviour. */ #define EMU_ATTENUATION_FACTOR (0.4f) /* Dynamic sample loading functions */ static int pin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); static int unpin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); static void unload_sample(fluid_sample_t *sample); static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan); static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason); static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone); static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx); /*************************************************************** * * SFONT LOADER */ /** * Creates a default soundfont2 loader that can be used with fluid_synth_add_sfloader(). * By default every synth instance has an initial default soundfont loader instance. * Calling this function is usually only necessary to load a soundfont from memory, by providing custom callback functions via fluid_sfloader_set_callbacks(). * * @param settings A settings instance obtained by new_fluid_settings() * @return A default soundfont2 loader struct */ fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings) { fluid_sfloader_t *loader; fluid_return_val_if_fail(settings != NULL, NULL); loader = new_fluid_sfloader(fluid_defsfloader_load, delete_fluid_sfloader); if(loader == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } fluid_sfloader_set_data(loader, settings); return loader; } 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(fluid_sfloader_get_data(loader)); if(defsfont == NULL) { return NULL; } sfont = new_fluid_sfont(fluid_defsfont_sfont_get_name, fluid_defsfont_sfont_get_preset, fluid_defsfont_sfont_iteration_start, fluid_defsfont_sfont_iteration_next, fluid_defsfont_sfont_delete); if(sfont == NULL) { delete_fluid_defsfont(defsfont); return NULL; } fluid_sfont_set_data(sfont, defsfont); defsfont->sfont = sfont; if(fluid_defsfont_load(defsfont, &loader->file_callbacks, filename) == FLUID_FAILED) { fluid_defsfont_sfont_delete(sfont); return NULL; } return sfont; } /*************************************************************** * * PUBLIC INTERFACE */ int fluid_defsfont_sfont_delete(fluid_sfont_t *sfont) { if(delete_fluid_defsfont(fluid_sfont_get_data(sfont)) != FLUID_OK) { return -1; } delete_fluid_sfont(sfont); return 0; } const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont) { return fluid_defsfont_get_name(fluid_sfont_get_data(sfont)); } fluid_preset_t * fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) { return fluid_defsfont_get_preset(fluid_sfont_get_data(sfont), bank, prenum); } void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont) { fluid_defsfont_iteration_start(fluid_sfont_get_data(sfont)); } fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont) { return fluid_defsfont_iteration_next(fluid_sfont_get_data(sfont)); } void fluid_defpreset_preset_delete(fluid_preset_t *preset) { fluid_defsfont_t *defsfont; fluid_defpreset_t *defpreset; defsfont = fluid_sfont_get_data(preset->sfont); defpreset = fluid_preset_get_data(preset); if(defsfont) { defsfont->preset = fluid_list_remove(defsfont->preset, defpreset); } delete_fluid_defpreset(defpreset); delete_fluid_preset(preset); } const char *fluid_defpreset_preset_get_name(fluid_preset_t *preset) { return fluid_defpreset_get_name(fluid_preset_get_data(preset)); } int fluid_defpreset_preset_get_banknum(fluid_preset_t *preset) { return fluid_defpreset_get_banknum(fluid_preset_get_data(preset)); } int fluid_defpreset_preset_get_num(fluid_preset_t *preset) { return fluid_defpreset_get_num(fluid_preset_get_data(preset)); } int fluid_defpreset_preset_noteon(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel) { return fluid_defpreset_noteon(fluid_preset_get_data(preset), synth, chan, key, vel); } /*************************************************************** * * SFONT */ /* * new_fluid_defsfont */ fluid_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings) { fluid_defsfont_t *defsfont; defsfont = FLUID_NEW(fluid_defsfont_t); if(defsfont == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(defsfont, 0, sizeof(*defsfont)); fluid_settings_getint(settings, "synth.lock-memory", &defsfont->mlock); fluid_settings_getint(settings, "synth.dynamic-sample-loading", &defsfont->dynamic_samples); return defsfont; } /* * delete_fluid_defsfont */ int delete_fluid_defsfont(fluid_defsfont_t *defsfont) { fluid_list_t *list; fluid_preset_t *preset; fluid_sample_t *sample; fluid_return_val_if_fail(defsfont != NULL, FLUID_OK); /* If we use dynamic sample loading, make sure we unpin any * pinned presets before removing this soundfont */ if(defsfont->dynamic_samples) { for(list = defsfont->preset; list; list = fluid_list_next(list)) { preset = (fluid_preset_t *)fluid_list_get(list); unpin_preset_samples(defsfont, preset); } } /* Check that no samples are currently used */ for(list = defsfont->sample; list; list = fluid_list_next(list)) { sample = (fluid_sample_t *) fluid_list_get(list); if(sample->refcount != 0) { return FLUID_FAILED; } } if(defsfont->filename != NULL) { FLUID_FREE(defsfont->filename); } for(list = defsfont->sample; list; list = fluid_list_next(list)) { sample = (fluid_sample_t *) fluid_list_get(list); /* If the sample data pointer is different to the sampledata chunk of * the soundfont, then the sample has been loaded individually (SF3) * and needs to be unloaded explicitly. This is safe even if using * dynamic sample loading, as the sample_unload mechanism sets * sample->data to NULL after unload. */ if ((sample->data != NULL) && (sample->data != defsfont->sampledata)) { fluid_samplecache_unload(sample->data); } delete_fluid_sample(sample); } if(defsfont->sample) { delete_fluid_list(defsfont->sample); } if(defsfont->sampledata != NULL) { fluid_samplecache_unload(defsfont->sampledata); } for(list = defsfont->preset; list; list = fluid_list_next(list)) { preset = (fluid_preset_t *)fluid_list_get(list); fluid_defpreset_preset_delete(preset); } delete_fluid_list(defsfont->preset); for(list = defsfont->inst; list; list = fluid_list_next(list)) { delete_fluid_inst(fluid_list_get(list)); } delete_fluid_list(defsfont->inst); FLUID_FREE(defsfont); return FLUID_OK; } /* * fluid_defsfont_get_name */ const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont) { return defsfont->filename; } /* Load sample data for a single sample from the Soundfont file. * Returns FLUID_OK on error, otherwise FLUID_FAILED */ int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample) { int num_samples; unsigned int source_end = sample->source_end; /* For uncompressed samples we want to include the 46 zero sample word area following each sample * in the Soundfont. Otherwise samples with loopend > end, which we have decided not to correct, would * be corrected after all in fluid_sample_sanitize_loop */ if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) { source_end += 46; /* Length of zero sample word after each sample, according to SF specs */ /* Safeguard against Soundfonts that are not quite valid and don't include 46 sample words after the * last sample */ if(source_end >= (defsfont->samplesize / sizeof(short))) { source_end = defsfont->samplesize / sizeof(short); } } num_samples = fluid_samplecache_load( sfdata, sample->source_start, source_end, sample->sampletype, defsfont->mlock, &sample->data, &sample->data24); if(num_samples < 0) { return FLUID_FAILED; } if(num_samples == 0) { sample->start = sample->end = 0; sample->loopstart = sample->loopend = 0; return FLUID_OK; } /* Ogg Vorbis samples already have loop pointers relative to the individual decompressed sample, * but SF2 samples are relative to sample chunk start, so they need to be adjusted */ if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) { sample->loopstart = sample->source_loopstart - sample->source_start; sample->loopend = sample->source_loopend - sample->source_start; } /* As we've just loaded an individual sample into it's own buffer, we need to adjust the start * and end pointers */ sample->start = 0; sample->end = num_samples - 1; return FLUID_OK; } /* Loads the sample data for all samples from the Soundfont file. For SF2 files, it loads the data in * one large block. For SF3 files, each compressed sample gets loaded individually. * Returns FLUID_OK on success, otherwise FLUID_FAILED */ int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata) { fluid_list_t *list; fluid_sample_t *sample; int sf3_file = (sfdata->version.major == 3); int sample_parsing_result = FLUID_OK; int invalid_loops_were_sanitized = FALSE; /* For SF2 files, we load the sample data in one large block */ if(!sf3_file) { int read_samples; int num_samples = sfdata->samplesize / sizeof(short); read_samples = fluid_samplecache_load(sfdata, 0, num_samples - 1, 0, defsfont->mlock, &defsfont->sampledata, &defsfont->sample24data); if(read_samples != num_samples) { FLUID_LOG(FLUID_ERR, "Attempted to read %d words of sample data, but got %d instead", num_samples, read_samples); return FLUID_FAILED; } } #pragma omp parallel #pragma omp single for(list = defsfont->sample; list; list = fluid_list_next(list)) { sample = fluid_list_get(list); if(sf3_file) { /* SF3 samples get loaded individually, as most (or all) of them are in Ogg Vorbis format * anyway */ #pragma omp task firstprivate(sample,sfdata,defsfont) shared(sample_parsing_result, invalid_loops_were_sanitized) default(none) { if(fluid_defsfont_load_sampledata(defsfont, sfdata, sample) == FLUID_FAILED) { #pragma omp critical { FLUID_LOG(FLUID_ERR, "Failed to load sample '%s'", sample->name); sample_parsing_result = FLUID_FAILED; } } else { int modified = fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); if(modified) { #pragma omp critical { invalid_loops_were_sanitized = TRUE; } } fluid_voice_optimize_sample(sample); } } } else { #pragma omp task firstprivate(sample, defsfont) shared(invalid_loops_were_sanitized) default(none) { int modified; /* Data pointers of SF2 samples point to large sample data block loaded above */ sample->data = defsfont->sampledata; sample->data24 = defsfont->sample24data; modified = fluid_sample_sanitize_loop(sample, defsfont->samplesize); if(modified) { #pragma omp critical { invalid_loops_were_sanitized = TRUE; } } fluid_voice_optimize_sample(sample); } } } if(invalid_loops_were_sanitized) { FLUID_LOG(FLUID_WARN, "Some invalid sample loops were sanitized! If you experience audible glitches, " "start fluidsynth in verbose mode for detailed information."); } return sample_parsing_result; } /* * fluid_defsfont_load */ int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *fcbs, const char *file) { SFData *sfdata; fluid_list_t *p; SFPreset *sfpreset; SFSample *sfsample; fluid_sample_t *sample; fluid_defpreset_t *defpreset = NULL; defsfont->filename = FLUID_STRDUP(file); if(defsfont->filename == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } defsfont->fcbs = fcbs; /* The actual loading is done in the sfont and sffile files */ sfdata = fluid_sffile_open(file, fcbs); if(sfdata == NULL) { /* error message already printed */ return FLUID_FAILED; } if(fluid_sffile_parse_presets(sfdata) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Couldn't parse presets from soundfont file"); goto err_exit; } /* Keep track of the position and size of the sample data because it's loaded separately (and might be unoaded/reloaded in future) */ defsfont->samplepos = sfdata->samplepos; defsfont->samplesize = sfdata->samplesize; defsfont->sample24pos = sfdata->sample24pos; defsfont->sample24size = sfdata->sample24size; /* Create all samples from sample headers */ p = sfdata->sample; while(p != NULL) { sfsample = (SFSample *)fluid_list_get(p); sample = new_fluid_sample(); if(sample == NULL) { goto err_exit; } if(fluid_sample_import_sfont(sample, sfsample, defsfont) == FLUID_OK) { fluid_defsfont_add_sample(defsfont, sample); } else { delete_fluid_sample(sample); sample = NULL; } /* Store reference to FluidSynth sample in SFSample for later IZone fixups */ sfsample->fluid_sample = sample; p = fluid_list_next(p); } /* If dynamic sample loading is disabled, load all samples in the Soundfont */ if(!defsfont->dynamic_samples) { if(fluid_defsfont_load_all_sampledata(defsfont, sfdata) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Unable to load all sample data"); goto err_exit; } } /* Load all the presets */ p = sfdata->preset; while(p != NULL) { sfpreset = (SFPreset *)fluid_list_get(p); defpreset = new_fluid_defpreset(); if(defpreset == NULL) { goto err_exit; } if(fluid_defpreset_import_sfont(defpreset, sfpreset, defsfont, sfdata) != FLUID_OK) { goto err_exit; } if(fluid_defsfont_add_preset(defsfont, defpreset) == FLUID_FAILED) { goto err_exit; } p = fluid_list_next(p); } fluid_sffile_close(sfdata); return FLUID_OK; err_exit: fluid_sffile_close(sfdata); delete_fluid_defpreset(defpreset); return FLUID_FAILED; } /* fluid_defsfont_add_sample * * Add a sample to the SoundFont */ int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample) { defsfont->sample = fluid_list_prepend(defsfont->sample, sample); return FLUID_OK; } /* fluid_defsfont_add_preset * * Add a preset to the SoundFont */ int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset) { fluid_preset_t *preset; preset = new_fluid_preset(defsfont->sfont, fluid_defpreset_preset_get_name, fluid_defpreset_preset_get_banknum, fluid_defpreset_preset_get_num, fluid_defpreset_preset_noteon, fluid_defpreset_preset_delete); if(preset == NULL) { return FLUID_FAILED; } if(defsfont->dynamic_samples) { preset->notify = dynamic_samples_preset_notify; } fluid_preset_set_data(preset, defpreset); defsfont->preset = fluid_list_append(defsfont->preset, preset); return FLUID_OK; } /* * fluid_defsfont_get_preset */ fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, int num) { fluid_preset_t *preset; fluid_list_t *list; for(list = defsfont->preset; list != NULL; list = fluid_list_next(list)) { preset = (fluid_preset_t *)fluid_list_get(list); if((fluid_preset_get_banknum(preset) == bank) && (fluid_preset_get_num(preset) == num)) { return preset; } } return NULL; } /* * fluid_defsfont_iteration_start */ void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont) { defsfont->preset_iter_cur = defsfont->preset; } /* * fluid_defsfont_iteration_next */ fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont) { fluid_preset_t *preset = (fluid_preset_t *)fluid_list_get(defsfont->preset_iter_cur); defsfont->preset_iter_cur = fluid_list_next(defsfont->preset_iter_cur); return preset; } /*************************************************************** * * PRESET */ /* * new_fluid_defpreset */ fluid_defpreset_t * new_fluid_defpreset(void) { fluid_defpreset_t *defpreset = FLUID_NEW(fluid_defpreset_t); if(defpreset == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } defpreset->next = NULL; defpreset->name[0] = 0; defpreset->bank = 0; defpreset->num = 0; defpreset->global_zone = NULL; defpreset->zone = NULL; defpreset->pinned = FALSE; return defpreset; } /* * delete_fluid_defpreset */ void delete_fluid_defpreset(fluid_defpreset_t *defpreset) { fluid_preset_zone_t *zone; fluid_return_if_fail(defpreset != NULL); delete_fluid_preset_zone(defpreset->global_zone); defpreset->global_zone = NULL; zone = defpreset->zone; while(zone != NULL) { defpreset->zone = zone->next; delete_fluid_preset_zone(zone); zone = defpreset->zone; } FLUID_FREE(defpreset); } int fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset) { return defpreset->bank; } int fluid_defpreset_get_num(fluid_defpreset_t *defpreset) { return defpreset->num; } const char * fluid_defpreset_get_name(fluid_defpreset_t *defpreset) { return defpreset->name; } /* * fluid_defpreset_next */ fluid_defpreset_t * fluid_defpreset_next(fluid_defpreset_t *defpreset) { return defpreset->next; } /* * Adds global and local modulators list to the voice. This is done in 2 steps: * - Step 1: Local modulators replace identic global modulators. * - Step 2: global + local modulators are added to the voice using mode. * * Instrument zone list (local/global) must be added using FLUID_VOICE_OVERWRITE. * Preset zone list (local/global) must be added using FLUID_VOICE_ADD. * * @param voice voice instance. * @param global_mod global list of modulators. * @param local_mod local list of modulators. * @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, */ static void fluid_defpreset_noteon_add_mod_to_voice(fluid_voice_t *voice, fluid_mod_t *global_mod, fluid_mod_t *local_mod, int mode) { fluid_mod_t *mod; /* list for 'sorting' global/local modulators */ fluid_mod_t *mod_list[FLUID_NUM_MOD]; int mod_list_count, i; /* identity_limit_count is the modulator upper limit number to handle with * existing identical modulators. * When identity_limit_count is below the actual number of modulators, this * will restrict identity check to this upper limit, * This is useful when we know by advance that there is no duplicate with * modulators at index above this limit. This avoid wasting cpu cycles at * noteon. */ int identity_limit_count; /* Step 1: Local modulators replace identic global modulators. */ /* local (instrument zone/preset zone), modulators: Put them all into a list. */ mod_list_count = 0; while(local_mod) { /* As modulators number in local_mod list was limited to FLUID_NUM_MOD at soundfont loading time (fluid_limit_mod_list()), here we don't need to check if mod_list is full. */ mod_list[mod_list_count++] = local_mod; local_mod = local_mod->next; } /* global (instrument zone/preset zone), modulators. * Replace modulators with the same definition in the global list: * (Instrument zone: SF 2.01 page 69, 'bullet' 8) * (Preset zone: SF 2.01 page 69, second-last bullet). * * mod_list contains local modulators. Now we know that there * is no global modulator identic to another global modulator (this has * been checked at soundfont loading time). So global modulators * are only checked against local modulators number. */ /* Restrict identity check to the number of local modulators */ identity_limit_count = mod_list_count; while(global_mod) { /* 'Identical' global modulators are ignored. * SF2.01 section 9.5.1 * page 69, 'bullet' 3 defines 'identical'. */ for(i = 0; i < identity_limit_count; i++) { if(fluid_mod_test_identity(global_mod, mod_list[i])) { break; } } /* Finally add the new modulator to the list. */ if(i >= identity_limit_count) { /* Although local_mod and global_mod lists was limited to FLUID_NUM_MOD at soundfont loading time, it is possible that local + global modulators exceeds FLUID_NUM_MOD. So, checks if mod_list_count reaches the limit. */ if(mod_list_count >= FLUID_NUM_MOD) { /* mod_list is full, we silently forget this modulator and next global modulators. When mod_list will be added to the voice, a warning will be displayed if the voice list is full. (see fluid_voice_add_mod_local()). */ break; } mod_list[mod_list_count++] = global_mod; } global_mod = global_mod->next; } /* Step 2: global + local modulators are added to the voice using mode. */ /* * mod_list contains local and global modulators, we know that: * - there is no global modulator identic to another global modulator, * - there is no local modulator identic to another local modulator, * So these local/global modulators are only checked against * actual number of voice modulators. */ /* Restrict identity check to the actual number of voice modulators */ /* Actual number of voice modulators : defaults + [instruments] */ identity_limit_count = voice->mod_count; for(i = 0; i < mod_list_count; i++) { mod = mod_list[i]; /* in mode FLUID_VOICE_OVERWRITE disabled instruments modulators CANNOT be skipped. */ /* in mode FLUID_VOICE_ADD disabled preset modulators can be skipped. */ if((mode == FLUID_VOICE_OVERWRITE) || (mod->amount != 0)) { /* Instrument modulators -supersede- existing (default) modulators. SF 2.01 page 69, 'bullet' 6 */ /* Preset modulators -add- to existing instrument modulators. SF2.01 page 70 first bullet on page */ fluid_voice_add_mod_local(voice, mod, mode, identity_limit_count); } } } /* * fluid_defpreset_noteon */ int fluid_defpreset_noteon(fluid_defpreset_t *defpreset, 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_voice_zone_t *voice_zone; fluid_list_t *list; fluid_voice_t *voice; int tuned_key; int i; /* For detuned channels it might be better to use another key for Soundfont sample selection * giving better approximations for the pitch than the original key. * Example: play key 60 on 6370 Hz => use tuned key 64 for sample selection * * This feature is only enabled for melodic channels. * For drum channels we always select Soundfont samples by key numbers. */ if(synth->channel[chan]->channel_type == CHANNEL_TYPE_MELODIC) { tuned_key = (int)(fluid_channel_get_key_pitch(synth->channel[chan], key) / 100.0f + 0.5f); } else { tuned_key = key; } global_preset_zone = fluid_defpreset_get_global_zone(defpreset); /* run thru all the zones of this preset */ preset_zone = fluid_defpreset_get_zone(defpreset); while(preset_zone != NULL) { /* check if the note falls into the key and velocity range of this preset */ if(fluid_zone_inside_range(&preset_zone->range, tuned_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 that could start a voice */ for(list = preset_zone->voice_zone; list != NULL; list = fluid_list_next(list)) { voice_zone = fluid_list_get(list); /* check if the instrument zone is ignored and the note falls into the key and velocity range of this instrument zone. An instrument zone must be ignored when its voice is already running played by a legato passage (see fluid_synth_noteon_monopoly_legato()) */ if(fluid_zone_inside_range(&voice_zone->range, tuned_key, vel)) { inst_zone = voice_zone->inst_zone; /* this is a good zone. allocate a new synthesis process and initialize it */ voice = fluid_synth_alloc_voice_LOCAL(synth, inst_zone->sample, chan, key, vel, &voice_zone->range); 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 */ /* Adds instrument zone modulators (global and local) to the voice.*/ fluid_defpreset_noteon_add_mod_to_voice(voice, /* global instrument modulators */ global_inst_zone ? global_inst_zone->mod : NULL, inst_zone->mod, /* local instrument modulators */ FLUID_VOICE_OVERWRITE); /* mode */ /* 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. However this check is not necessary when the soundfont loader has ignored invalid preset generators. Actually load_pgen()has ignored these invalid preset generators: GEN_STARTADDROFS, GEN_ENDADDROFS, GEN_STARTLOOPADDROFS, GEN_ENDLOOPADDROFS, GEN_STARTADDRCOARSEOFS,GEN_ENDADDRCOARSEOFS, GEN_STARTLOOPADDRCOARSEOFS, GEN_KEYNUM, GEN_VELOCITY, GEN_ENDLOOPADDRCOARSEOFS, GEN_SAMPLEMODE, GEN_EXCLUSIVECLASS,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. */ } } /* for all generators */ /* Adds preset zone modulators (global and local) to the voice.*/ fluid_defpreset_noteon_add_mod_to_voice(voice, /* global preset modulators */ global_preset_zone ? global_preset_zone->mod : NULL, preset_zone->mod, /* local preset modulators */ FLUID_VOICE_ADD); /* mode */ /* 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) */ } } } 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 *defpreset, fluid_preset_zone_t *zone) { defpreset->global_zone = zone; return FLUID_OK; } /* * fluid_defpreset_import_sfont */ int fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, SFPreset *sfpreset, fluid_defsfont_t *defsfont, SFData *sfdata) { 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(defpreset->name, sfpreset->name); } else { FLUID_SNPRINTF(defpreset->name, sizeof(defpreset->name), "Bank%d,Pre%d", sfpreset->bank, sfpreset->prenum); } defpreset->bank = sfpreset->bank; defpreset->num = sfpreset->prenum; p = sfpreset->zone; count = 0; while(p != NULL) { sfzone = (SFZone *)fluid_list_get(p); FLUID_SNPRINTF(zone_name, sizeof(zone_name), "pz:%s/%d", defpreset->name, count); zone = new_fluid_preset_zone(zone_name); if(zone == NULL) { return FLUID_FAILED; } if(fluid_preset_zone_import_sfont(zone, sfzone, defsfont, sfdata) != 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(defpreset, zone); } else if(fluid_defpreset_add_zone(defpreset, 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 *defpreset, fluid_preset_zone_t *zone) { if(defpreset->zone == NULL) { zone->next = NULL; defpreset->zone = zone; } else { zone->next = defpreset->zone; defpreset->zone = zone; } return FLUID_OK; } /* * fluid_defpreset_get_zone */ fluid_preset_zone_t * fluid_defpreset_get_zone(fluid_defpreset_t *defpreset) { return defpreset->zone; } /* * fluid_defpreset_get_global_zone */ fluid_preset_zone_t * fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset) { return defpreset->global_zone; } /*************************************************************** * * PRESET_ZONE */ /* * fluid_preset_zone_next */ fluid_preset_zone_t * fluid_preset_zone_next(fluid_preset_zone_t *zone) { return zone->next; } /* * new_fluid_preset_zone */ fluid_preset_zone_t * new_fluid_preset_zone(char *name) { 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; zone->voice_zone = NULL; zone->name = FLUID_STRDUP(name); if(zone->name == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_FREE(zone); return NULL; } zone->inst = NULL; zone->range.keylo = 0; zone->range.keyhi = 128; zone->range.vello = 0; zone->range.velhi = 128; zone->range.ignore = FALSE; /* 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_init(&zone->gen[0], NULL); zone->mod = NULL; /* list of modulators */ return zone; } /* * delete list of modulators. */ void delete_fluid_list_mod(fluid_mod_t *mod) { fluid_mod_t *tmp; while(mod) /* delete the modulators */ { tmp = mod; mod = mod->next; delete_fluid_mod(tmp); } } /* * delete_fluid_preset_zone */ void delete_fluid_preset_zone(fluid_preset_zone_t *zone) { fluid_list_t *list; fluid_return_if_fail(zone != NULL); delete_fluid_list_mod(zone->mod); for(list = zone->voice_zone; list != NULL; list = fluid_list_next(list)) { FLUID_FREE(fluid_list_get(list)); } delete_fluid_list(zone->voice_zone); FLUID_FREE(zone->name); FLUID_FREE(zone); } static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone) { fluid_inst_zone_t *inst_zone; fluid_sample_t *sample; fluid_voice_zone_t *voice_zone; fluid_zone_range_t *irange; fluid_zone_range_t *prange = &preset_zone->range; fluid_return_val_if_fail(preset_zone->inst != NULL, FLUID_FAILED); inst_zone = fluid_inst_get_zone(preset_zone->inst); while(inst_zone != NULL) { /* We only create voice ranges for zones that could actually start a voice, * i.e. that have a sample and don't point to ROM */ 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; } voice_zone = FLUID_NEW(fluid_voice_zone_t); if(voice_zone == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } voice_zone->inst_zone = inst_zone; irange = &inst_zone->range; voice_zone->range.keylo = (prange->keylo > irange->keylo) ? prange->keylo : irange->keylo; voice_zone->range.keyhi = (prange->keyhi < irange->keyhi) ? prange->keyhi : irange->keyhi; voice_zone->range.vello = (prange->vello > irange->vello) ? prange->vello : irange->vello; voice_zone->range.velhi = (prange->velhi < irange->velhi) ? prange->velhi : irange->velhi; voice_zone->range.ignore = FALSE; preset_zone->voice_zone = fluid_list_append(preset_zone->voice_zone, voice_zone); inst_zone = fluid_inst_zone_next(inst_zone); } return FLUID_OK; } /** * Checks if modulator mod is identic to another modulator in the list * (specs SF 2.0X 7.4, 7.8). * @param mod, modulator list. * @param name, if not NULL, pointer on a string displayed as warning. * @return TRUE if mod is identic to another modulator, FALSE otherwise. */ static int fluid_zone_is_mod_identic(fluid_mod_t *mod, char *name) { fluid_mod_t *next = mod->next; while(next) { /* is mod identic to next ? */ if(fluid_mod_test_identity(mod, next)) { if(name) { FLUID_LOG(FLUID_WARN, "Ignoring identic modulator %s", name); } return TRUE; } next = next->next; } return FALSE; } /** * Limits the number of modulators in a modulator list. * This is appropriate to internal synthesizer modulators tables * which have a fixed size (FLUID_NUM_MOD). * * @param zone_name, zone name * @param list_mod, address of pointer on modulator list. */ static void fluid_limit_mod_list(char *zone_name, fluid_mod_t **list_mod) { int mod_idx = 0; /* modulator index */ fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */ fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */ while(mod) { if((mod_idx + 1) > FLUID_NUM_MOD) { /* truncation of list_mod */ if(mod_idx) { prev_mod->next = NULL; } else { *list_mod = NULL; } delete_fluid_list_mod(mod); FLUID_LOG(FLUID_WARN, "%s, modulators count limited to %d", zone_name, FLUID_NUM_MOD); break; } mod_idx++; prev_mod = mod; mod = mod->next; } } /** * Checks and remove invalid modulators from a zone modulators list. * - checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1). * - checks identic modulators in the list (specs SF 2.01 7.4, 7.8). * @param zone_name, zone name. * @param list_mod, address of pointer on modulators list. */ static void fluid_zone_check_mod(char *zone_name, fluid_mod_t **list_mod) { fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */ fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */ int mod_idx = 0; /* modulator index */ while(mod) { char zone_mod_name[256]; fluid_mod_t *next = mod->next; /* prepare modulator name: zonename/#modulator */ FLUID_SNPRINTF(zone_mod_name, sizeof(zone_mod_name), "%s/mod%d", zone_name, mod_idx); /* has mod invalid sources ? */ if(!fluid_mod_check_sources(mod, zone_mod_name) /* or is mod identic to any following modulator ? */ || fluid_zone_is_mod_identic(mod, zone_mod_name)) { /* the modulator is useless so we remove it */ if(prev_mod) { prev_mod->next = next; } else { *list_mod = next; } delete_fluid_mod(mod); /* freeing */ } else { prev_mod = mod; } mod = next; mod_idx++; } /* limits the size of modulators list */ fluid_limit_mod_list(zone_name, list_mod); } /* * fluid_zone_gen_import_sfont * Imports generators from sfzone to gen and range. * @param gen, pointer on destination generators table. * @param range, pointer on destination range generators. * @param sfzone, pointer on soundfont zone generators. */ static void fluid_zone_gen_import_sfont(fluid_gen_t *gen, fluid_zone_range_t *range, SFZone *sfzone) { fluid_list_t *r; SFGen *sfgen; for(r = sfzone->gen; r != NULL;) { sfgen = (SFGen *)fluid_list_get(r); switch(sfgen->id) { case GEN_KEYRANGE: range->keylo = sfgen->amount.range.lo; range->keyhi = sfgen->amount.range.hi; break; case GEN_VELRANGE: range->vello = sfgen->amount.range.lo; range->velhi = sfgen->amount.range.hi; break; case GEN_ATTENUATION: /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at * preset and instrument level */ gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR; gen[sfgen->id].flags = GEN_SET; break; case GEN_INSTRUMENT: case GEN_SAMPLEID: gen[sfgen->id].val = (fluid_real_t) sfgen->amount.uword; gen[sfgen->id].flags = GEN_SET; break; default: gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; gen[sfgen->id].flags = GEN_SET; break; } r = fluid_list_next(r); } } /* * fluid_zone_mod_source_import_sfont * Imports source information from sf_source to src and flags. * @param src, pointer on destination modulator source. * @param flags, pointer on destination modulator flags. * @param sf_source, soundfont modulator source. * @return return TRUE if success, FALSE if source type is unknown. */ static int fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, unsigned short sf_source) { int type; unsigned char flags_dest; /* destination flags */ /* sources */ *src = sf_source & 127; /* index of source, seven-bit value, SF2.01 section 8.2, page 50 */ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ flags_dest = 0; if(sf_source & (1 << 7)) { flags_dest |= FLUID_MOD_CC; } else { flags_dest |= FLUID_MOD_GC; } /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ if(sf_source & (1 << 8)) { flags_dest |= FLUID_MOD_NEGATIVE; } else { flags_dest |= FLUID_MOD_POSITIVE; } /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ if(sf_source & (1 << 9)) { flags_dest |= FLUID_MOD_BIPOLAR; } else { flags_dest |= FLUID_MOD_UNIPOLAR; } /* modulator source types: SF2.01 section 8.2.1 page 52 */ type = sf_source >> 10; type &= 63; /* type is a 6-bit value */ if(type == 0) { flags_dest |= FLUID_MOD_LINEAR; } else if(type == 1) { flags_dest |= FLUID_MOD_CONCAVE; } else if(type == 2) { flags_dest |= FLUID_MOD_CONVEX; } else if(type == 3) { flags_dest |= FLUID_MOD_SWITCH; } else { *flags = flags_dest; /* This shouldn't happen - unknown type! */ return FALSE; } *flags = flags_dest; return TRUE; } /* * fluid_zone_mod_import_sfont * Imports modulators from sfzone to modulators list mod. * @param zone_name, zone name. * @param mod, address of pointer on modulators list to return. * @param sfzone, pointer on soundfont zone. * @return FLUID_OK if success, FLUID_FAILED otherwise. */ static int fluid_zone_mod_import_sfont(char *zone_name, fluid_mod_t **mod, SFZone *sfzone) { fluid_list_t *r; int count; /* Import the modulators (only SF2.1 and higher) */ for(count = 0, r = sfzone->mod; r != NULL; count++) { SFMod *mod_src = (SFMod *)fluid_list_get(r); fluid_mod_t *mod_dest = new_fluid_mod(); 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 *** */ if(!fluid_zone_mod_source_import_sfont(&mod_dest->src1, &mod_dest->flags1, mod_src->src)) { /* This shouldn't happen - unknown type! * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount = 0; } /* Note: When primary source input (src1) is set to General Controller 'No Controller', output will be forced to 0.0 at synthesis time (see fluid_mod_get_value()). That means that the minimum value of the modulator will be always 0.0. We need to force amount value to 0 to ensure a correct evaluation of the minimum value later (see fluid_voice_get_lower_boundary_for_attenuation()). */ if(((mod_dest->flags1 & FLUID_MOD_CC) == FLUID_MOD_GC) && (mod_dest->src1 == FLUID_MOD_NONE)) { mod_dest->amount = 0; } /* *** Dest *** */ mod_dest->dest = mod_src->dest; /* index of controlled generator */ /* *** Amount source *** */ if(!fluid_zone_mod_source_import_sfont(&mod_dest->src2, &mod_dest->flags2, mod_src->amtsrc)) { /* This shouldn't happen - unknown type! * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount = 0; } /* Note: When secondary source input (src2) is set to General Controller 'No Controller', output will be forced to +1.0 at synthesis time (see fluid_mod_get_value()). That means that this source will behave unipolar only. We need to force the unipolar flag to ensure to ensure a correct evaluation of the minimum value later (see fluid_voice_get_lower_boundary_for_attenuation()). */ if(((mod_dest->flags2 & FLUID_MOD_CC) == FLUID_MOD_GC) && (mod_dest->src2 == FLUID_MOD_NONE)) { mod_dest->flags2 &= ~FLUID_MOD_BIPOLAR; } /* *** 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) { *mod = mod_dest; } else { fluid_mod_t *last_mod = *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 */ /* checks and removes invalid modulators in modulators list*/ fluid_zone_check_mod(zone_name, mod); return FLUID_OK; } /* * fluid_preset_zone_import_sfont */ int fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata) { /* import the generators */ fluid_zone_gen_import_sfont(zone->gen, &zone->range, sfzone); if(zone->gen[GEN_INSTRUMENT].flags == GEN_SET) { int inst_idx = (int) zone->gen[GEN_INSTRUMENT].val; zone->inst = find_inst_by_idx(defsfont, inst_idx); if(zone->inst == NULL) { zone->inst = fluid_inst_import_sfont(inst_idx, defsfont, sfdata); } if(zone->inst == NULL) { FLUID_LOG(FLUID_ERR, "Preset zone %s: Invalid instrument reference", zone->name); return FLUID_FAILED; } if(fluid_preset_zone_create_voice_zones(zone) == FLUID_FAILED) { return FLUID_FAILED; } /* We don't need this generator anymore */ zone->gen[GEN_INSTRUMENT].flags = GEN_UNUSED; } /* Import the modulators (only SF2.1 and higher) */ return fluid_zone_mod_import_sfont(zone->name, &zone->mod, sfzone); } /* * fluid_preset_zone_get_inst */ fluid_inst_t * fluid_preset_zone_get_inst(fluid_preset_zone_t *zone) { return zone->inst; } /*************************************************************** * * 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 */ void delete_fluid_inst(fluid_inst_t *inst) { fluid_inst_zone_t *zone; fluid_return_if_fail(inst != NULL); delete_fluid_inst_zone(inst->global_zone); inst->global_zone = NULL; zone = inst->zone; while(zone != NULL) { inst->zone = zone->next; delete_fluid_inst_zone(zone); zone = inst->zone; } FLUID_FREE(inst); } /* * 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 */ fluid_inst_t * fluid_inst_import_sfont(int inst_idx, fluid_defsfont_t *defsfont, SFData *sfdata) { fluid_list_t *p; fluid_list_t *inst_list; fluid_inst_t *inst; SFZone *sfzone; SFInst *sfinst; fluid_inst_zone_t *inst_zone; char zone_name[256]; int count; for (inst_list = sfdata->inst; inst_list; inst_list = fluid_list_next(inst_list)) { sfinst = fluid_list_get(inst_list); if (sfinst->idx == inst_idx) { break; } } if (inst_list == NULL) { return NULL; } inst = (fluid_inst_t *) new_fluid_inst(); if(inst == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } inst->source_idx = sfinst->idx; 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 *)fluid_list_get(p); /* instrument zone name */ FLUID_SNPRINTF(zone_name, sizeof(zone_name), "iz:%s/%d", inst->name, count); inst_zone = new_fluid_inst_zone(zone_name); if(inst_zone == NULL) { return NULL; } if(fluid_inst_zone_import_sfont(inst_zone, sfzone, defsfont, sfdata) != FLUID_OK) { delete_fluid_inst_zone(inst_zone); return NULL; } if((count == 0) && (fluid_inst_zone_get_sample(inst_zone) == NULL)) { fluid_inst_set_global_zone(inst, inst_zone); } else if(fluid_inst_add_zone(inst, inst_zone) != FLUID_OK) { return NULL; } p = fluid_list_next(p); count++; } defsfont->inst = fluid_list_append(defsfont->inst, inst); return inst; } /* * 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) { 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; zone->name = FLUID_STRDUP(name); if(zone->name == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_FREE(zone); return NULL; } zone->sample = NULL; zone->range.keylo = 0; zone->range.keyhi = 128; zone->range.vello = 0; zone->range.velhi = 128; zone->range.ignore = FALSE; /* Flag the generators as unused. * This also sets the generator values to default, but they will be overwritten anyway, if used.*/ fluid_gen_init(&zone->gen[0], NULL); zone->mod = NULL; /* list of modulators */ return zone; } /* * delete_fluid_inst_zone */ void delete_fluid_inst_zone(fluid_inst_zone_t *zone) { fluid_return_if_fail(zone != NULL); delete_fluid_list_mod(zone->mod); FLUID_FREE(zone->name); FLUID_FREE(zone); } /* * 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 *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata) { /* import the generators */ fluid_zone_gen_import_sfont(inst_zone->gen, &inst_zone->range, sfzone); /* FIXME */ /* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */ /* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */ /* } */ if (inst_zone->gen[GEN_SAMPLEID].flags == GEN_SET) { fluid_list_t *list; SFSample *sfsample; int sample_idx = (int) inst_zone->gen[GEN_SAMPLEID].val; /* find the SFSample by index */ for(list = sfdata->sample; list; list = fluid_list_next(list)) { sfsample = fluid_list_get(list); if (sfsample->idx == sample_idx) { break; } } if (list == NULL) { FLUID_LOG(FLUID_ERR, "Instrument zone '%s': Invalid sample reference", inst_zone->name); return FLUID_FAILED; } inst_zone->sample = sfsample->fluid_sample; /* we don't need this generator anymore, mark it as unused */ inst_zone->gen[GEN_SAMPLEID].flags = GEN_UNUSED; } /* Import the modulators (only SF2.1 and higher) */ return fluid_zone_mod_import_sfont(inst_zone->name, &inst_zone->mod, sfzone); } /* * fluid_inst_zone_get_sample */ fluid_sample_t * fluid_inst_zone_get_sample(fluid_inst_zone_t *zone) { return zone->sample; } int fluid_zone_inside_range(fluid_zone_range_t *range, int key, int vel) { /* ignoreInstrumentZone is set in mono legato playing */ int ignore_zone = range->ignore; /* Reset the 'ignore' request */ range->ignore = FALSE; return !ignore_zone && ((range->keylo <= key) && (range->keyhi >= key) && (range->vello <= vel) && (range->velhi >= vel)); } /*************************************************************** * * SAMPLE */ /* * fluid_sample_in_rom */ int fluid_sample_in_rom(fluid_sample_t *sample) { return (sample->sampletype & FLUID_SAMPLETYPE_ROM); } /* * fluid_sample_import_sfont */ int fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont) { FLUID_STRCPY(sample->name, sfsample->name); sample->source_start = sfsample->start; sample->source_end = (sfsample->end > 0) ? sfsample->end - 1 : 0; /* marks last sample, contrary to SF spec. */ sample->source_loopstart = sfsample->loopstart; sample->source_loopend = sfsample->loopend; sample->start = sample->source_start; sample->end = sample->source_end; sample->loopstart = sample->source_loopstart; sample->loopend = sample->source_loopend; sample->samplerate = sfsample->samplerate; sample->origpitch = sfsample->origpitch; sample->pitchadj = sfsample->pitchadj; sample->sampletype = sfsample->sampletype; if(defsfont->dynamic_samples) { sample->notify = dynamic_samples_sample_notify; } if(fluid_sample_validate(sample, defsfont->samplesize) == FLUID_FAILED) { return FLUID_FAILED; } return FLUID_OK; } /* Called if a sample is no longer used by a voice. Used by dynamic sample loading * to unload a sample that is not used by any loaded presets anymore but couldn't * be unloaded straight away because it was still in use by a voice. */ static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason) { if(reason == FLUID_SAMPLE_DONE && sample->preset_count == 0) { unload_sample(sample); } return FLUID_OK; } /* Called if a preset has been selected for or unselected from a channel. Used by * dynamic sample loading to load and unload samples on demand. */ static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan) { fluid_defsfont_t *defsfont; if(reason == FLUID_PRESET_SELECTED) { FLUID_LOG(FLUID_DBG, "Selected preset '%s' on channel %d", fluid_preset_get_name(preset), chan); defsfont = fluid_sfont_get_data(preset->sfont); return load_preset_samples(defsfont, preset); } if(reason == FLUID_PRESET_UNSELECTED) { FLUID_LOG(FLUID_DBG, "Deselected preset '%s' from channel %d", fluid_preset_get_name(preset), chan); defsfont = fluid_sfont_get_data(preset->sfont); return unload_preset_samples(defsfont, preset); } if(reason == FLUID_PRESET_PIN) { defsfont = fluid_sfont_get_data(preset->sfont); return pin_preset_samples(defsfont, preset); } if(reason == FLUID_PRESET_UNPIN) { defsfont = fluid_sfont_get_data(preset->sfont); return unpin_preset_samples(defsfont, preset); } return FLUID_OK; } static int pin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) { fluid_defpreset_t *defpreset; defpreset = fluid_preset_get_data(preset); if (defpreset->pinned) { return FLUID_OK; } FLUID_LOG(FLUID_DBG, "Pinning preset '%s'", fluid_preset_get_name(preset)); if(load_preset_samples(defsfont, preset) == FLUID_FAILED) { return FLUID_FAILED; } defpreset->pinned = TRUE; return FLUID_OK; } static int unpin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) { fluid_defpreset_t *defpreset; defpreset = fluid_preset_get_data(preset); if (!defpreset->pinned) { return FLUID_OK; } FLUID_LOG(FLUID_DBG, "Unpinning preset '%s'", fluid_preset_get_name(preset)); if(unload_preset_samples(defsfont, preset) == FLUID_FAILED) { return FLUID_FAILED; } defpreset->pinned = FALSE; return FLUID_OK; } /* Walk through all samples used by the passed in preset and make sure that the * sample data is loaded for each sample. Used by dynamic sample loading. */ static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) { fluid_defpreset_t *defpreset; fluid_preset_zone_t *preset_zone; fluid_inst_t *inst; fluid_inst_zone_t *inst_zone; fluid_sample_t *sample; SFData *sffile = NULL; defpreset = fluid_preset_get_data(preset); preset_zone = fluid_defpreset_get_zone(defpreset); while(preset_zone != NULL) { inst = fluid_preset_zone_get_inst(preset_zone); inst_zone = fluid_inst_get_zone(inst); while(inst_zone != NULL) { sample = fluid_inst_zone_get_sample(inst_zone); if((sample != NULL) && (sample->start != sample->end)) { sample->preset_count++; /* If this is the first time this sample has been selected, * load the sampledata */ if(sample->preset_count == 1) { /* Make sure we have an open Soundfont file. Do this here * to avoid having to open the file if no loading is necessary * for a preset */ if(sffile == NULL) { sffile = fluid_sffile_open(defsfont->filename, defsfont->fcbs); if(sffile == NULL) { FLUID_LOG(FLUID_ERR, "Unable to open Soundfont file"); return FLUID_FAILED; } } if(fluid_defsfont_load_sampledata(defsfont, sffile, sample) == FLUID_OK) { fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); fluid_voice_optimize_sample(sample); } else { FLUID_LOG(FLUID_ERR, "Unable to load sample '%s', disabling", sample->name); sample->start = sample->end = 0; } } } inst_zone = fluid_inst_zone_next(inst_zone); } preset_zone = fluid_preset_zone_next(preset_zone); } if(sffile != NULL) { fluid_sffile_close(sffile); } return FLUID_OK; } /* Walk through all samples used by the passed in preset and unload the sample data * of each sample that is not used by any selected preset anymore. Used by dynamic * sample loading. */ static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) { fluid_defpreset_t *defpreset; fluid_preset_zone_t *preset_zone; fluid_inst_t *inst; fluid_inst_zone_t *inst_zone; fluid_sample_t *sample; defpreset = fluid_preset_get_data(preset); preset_zone = fluid_defpreset_get_zone(defpreset); while(preset_zone != NULL) { inst = fluid_preset_zone_get_inst(preset_zone); inst_zone = fluid_inst_get_zone(inst); while(inst_zone != NULL) { sample = fluid_inst_zone_get_sample(inst_zone); if((sample != NULL) && (sample->preset_count > 0)) { sample->preset_count--; /* If the sample is not used by any preset or used by a * sounding voice, unload it from the sample cache. If it's * still in use by a voice, dynamic_samples_sample_notify will * take care of unloading the sample as soon as the voice is * finished with it (but only on the next API call). */ if(sample->preset_count == 0 && sample->refcount == 0) { unload_sample(sample); } } inst_zone = fluid_inst_zone_next(inst_zone); } preset_zone = fluid_preset_zone_next(preset_zone); } return FLUID_OK; } /* Unload an unused sample from the samplecache */ static void unload_sample(fluid_sample_t *sample) { fluid_return_if_fail(sample != NULL); fluid_return_if_fail(sample->data != NULL); fluid_return_if_fail(sample->preset_count == 0); fluid_return_if_fail(sample->refcount == 0); FLUID_LOG(FLUID_DBG, "Unloading sample '%s'", sample->name); if(fluid_samplecache_unload(sample->data) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Unable to unload sample '%s'", sample->name); } else { sample->data = NULL; sample->data24 = NULL; } } static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx) { fluid_list_t *list; fluid_inst_t *inst; for(list = defsfont->inst; list != NULL; list = fluid_list_next(list)) { inst = fluid_list_get(list); if(inst->source_idx == idx) { return inst; } } return NULL; } fluidsynth-2.2.5/src/sfloader/fluid_defsfont.h000066400000000000000000000214511417326347500214620ustar00rootroot00000000000000/* 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_sffile.h" #include "fluid_list.h" #include "fluid_mod.h" #include "fluid_gen.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 /*************************************************************** * * 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; /**< Soundfont Instrument Zone */ typedef struct _fluid_voice_zone_t fluid_voice_zone_t; /* defines the velocity and key range for a zone */ struct _fluid_zone_range_t { int keylo; int keyhi; int vello; int velhi; unsigned char ignore; /* set to TRUE for legato playing to ignore this range zone */ }; /* Stored on a preset zone to keep track of the inst zones that could start a voice * and their combined preset zone/instument zone ranges */ struct _fluid_voice_zone_t { fluid_inst_zone_t *inst_zone; fluid_zone_range_t range; }; /* Public interface */ fluid_sfont_t *fluid_defsfloader_load(fluid_sfloader_t *loader, const char *filename); int fluid_defsfont_sfont_delete(fluid_sfont_t *sfont); const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont); fluid_preset_t *fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont); fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont); void fluid_defpreset_preset_delete(fluid_preset_t *preset); const 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); int fluid_zone_inside_range(fluid_zone_range_t *zone_range, int key, int vel); /* * fluid_defsfont_t */ struct _fluid_defsfont_t { const fluid_file_callbacks_t *fcbs; /* the file callbacks used to load this Soundfont */ 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 in bytes */ short *sampledata; /* the sample data, loaded in ram */ unsigned int sample24pos; /* position within sffd of the sm24 chunk, set to zero if no 24 bit sample support */ unsigned int sample24size; /* length within sffd of the sm24 chunk */ char *sample24data; /* if not NULL, the least significant byte of the 24bit sample data, loaded in ram */ fluid_sfont_t *sfont; /* pointer to parent sfont */ fluid_list_t *sample; /* the samples in this soundfont */ fluid_list_t *preset; /* the presets of this soundfont */ fluid_list_t *inst; /* the instruments of this soundfont */ int mlock; /* Should we try memlock (avoid swapping)? */ int dynamic_samples; /* Enables dynamic sample loading if set */ fluid_list_t *preset_iter_cur; /* the current preset in the iteration */ }; fluid_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings); int delete_fluid_defsfont(fluid_defsfont_t *defsfont); int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *file_callbacks, const char *file); const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont); fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, int prenum); void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont); fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont); int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample); int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata); int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample); int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset); /* * fluid_preset_t */ struct _fluid_defpreset_t { fluid_defpreset_t *next; 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 */ int pinned; /* preset samples pinned to sample cache? */ }; fluid_defpreset_t *new_fluid_defpreset(void); void delete_fluid_defpreset(fluid_defpreset_t *defpreset); fluid_defpreset_t *fluid_defpreset_next(fluid_defpreset_t *defpreset); int fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, SFPreset *sfpreset, fluid_defsfont_t *defsfont, SFData *sfdata); int fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); int fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); fluid_preset_zone_t *fluid_defpreset_get_zone(fluid_defpreset_t *defpreset); fluid_preset_zone_t *fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset); int fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset); int fluid_defpreset_get_num(fluid_defpreset_t *defpreset); const char *fluid_defpreset_get_name(fluid_defpreset_t *defpreset); int fluid_defpreset_noteon(fluid_defpreset_t *defpreset, 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; fluid_list_t *voice_zone; fluid_zone_range_t range; fluid_gen_t gen[GEN_LAST]; fluid_mod_t *mod; /* List of modulators */ }; fluid_preset_zone_t *new_fluid_preset_zone(char *name); void delete_fluid_list_mod(fluid_mod_t *mod); void delete_fluid_preset_zone(fluid_preset_zone_t *zone); fluid_preset_zone_t *fluid_preset_zone_next(fluid_preset_zone_t *zone); int fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defssfont, SFData *sfdata); fluid_inst_t *fluid_preset_zone_get_inst(fluid_preset_zone_t *zone); /* * fluid_inst_t */ struct _fluid_inst_t { char name[21]; int source_idx; /* Index of instrument in source Soundfont */ fluid_inst_zone_t *global_zone; fluid_inst_zone_t *zone; }; fluid_inst_t *new_fluid_inst(void); fluid_inst_t *fluid_inst_import_sfont(int inst_idx, fluid_defsfont_t *defsfont, SFData *sfdata); void delete_fluid_inst(fluid_inst_t *inst); int fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); int fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); 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; fluid_zone_range_t range; fluid_gen_t gen[GEN_LAST]; fluid_mod_t *mod; /* List of modulators */ }; fluid_inst_zone_t *new_fluid_inst_zone(char *name); void 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 *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata); fluid_sample_t *fluid_inst_zone_get_sample(fluid_inst_zone_t *zone); int fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont); int fluid_sample_in_rom(fluid_sample_t *sample); #endif /* _FLUID_SFONT_H */ fluidsynth-2.2.5/src/sfloader/fluid_instpatch.c000066400000000000000000000503631417326347500216460ustar00rootroot00000000000000 #include "fluid_instpatch.h" #include "fluid_list.h" #include "fluid_sfont.h" #include "fluid_sys.h" #include typedef struct _fluid_instpatch_font_t { char name[256]; IpatchDLS2 *dls; fluid_list_t *preset_list; /* the presets of this soundfont */ fluid_list_t *preset_iter_cur; /* the current preset in the iteration */ } fluid_instpatch_font_t; typedef struct _fluid_instpatch_preset_t { fluid_instpatch_font_t *parent_sfont; IpatchSF2VoiceCache *cache; /* pointer to name of the preset, duplicated from item, allocated by glib */ char *name; int bank; int prog; } fluid_instpatch_preset_t; // private struct for storing additional data for each instpatch voice typedef struct _instpatch_voice_user_data { /* pointer to the sample store that holds the PCM */ IpatchSampleStoreCache *sample_store; /* pointer to a preallocated fluid_sample_t that we can use during noteon for this voice */ fluid_sample_t *sample; } fluid_instpatch_voice_user_data_t; /* max voices per instrument (voices exceeding this will not sound) */ enum { MAX_INST_VOICES = 128, }; int fluid_instpatch_supports_multi_init(void) { guint major, minor, patch; ipatch_version(&major, &minor, &patch); /* libinstpatch <= 1.1.4 only supports calling ipatch_init() once */ return FLUID_VERSION_CHECK(major, minor, patch) > FLUID_VERSION_CHECK(1, 1, 4); } void fluid_instpatch_init(void) { ipatch_init(); } void fluid_instpatch_deinit(void) { ipatch_close(); } static int delete_fluid_instpatch(fluid_instpatch_font_t *pfont); static const char *fluid_instpatch_sfont_get_name(fluid_sfont_t *sfont); static fluid_preset_t *fluid_instpatch_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); /* sfloader callback to get the name of a preset */ static const char * fluid_instpatch_preset_get_name(fluid_preset_t *preset) { fluid_instpatch_preset_t *preset_data = fluid_preset_get_data(preset); return preset_data->name; } /* sfloader callback to get the bank number of a preset */ static int fluid_instpatch_preset_get_banknum(fluid_preset_t *preset) { fluid_instpatch_preset_t *preset_data = fluid_preset_get_data(preset); return preset_data->bank; } /* sfloader callback to get the preset number of a preset */ static int fluid_instpatch_preset_get_num(fluid_preset_t *preset) { fluid_instpatch_preset_t *preset_data = fluid_preset_get_data(preset); return preset_data->prog; } static void fluid_instpatch_iteration_start(fluid_sfont_t *sfont) { fluid_instpatch_font_t *pfont = fluid_sfont_get_data(sfont); pfont->preset_iter_cur = pfont->preset_list; } static fluid_preset_t *fluid_instpatch_iteration_next(fluid_sfont_t *sfont) { fluid_instpatch_font_t *pfont = fluid_sfont_get_data(sfont); fluid_preset_t *preset = fluid_list_get(pfont->preset_iter_cur); pfont->preset_iter_cur = fluid_list_next(pfont->preset_iter_cur); return preset; } /* sfloader callback for a noteon event */ static int fluid_instpatch_preset_noteon(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel) { /* voice index array */ guint16 voice_indices[MAX_INST_VOICES]; int sel_values[IPATCH_SF2_VOICE_CACHE_MAX_SEL_VALUES]; fluid_mod_t *fmod = g_alloca(fluid_mod_sizeof()); fluid_voice_t *flvoice; fluid_instpatch_preset_t *preset_data = fluid_preset_get_data(preset); int i, voice_count, voice_num, ret = FLUID_FAILED; GSList *p; /* lookup the voice cache that we've created on loading */ IpatchSF2VoiceCache *cache = preset_data->cache; /* loading and caching the instrument could have failed though */ if(FLUID_UNLIKELY(cache == NULL)) { return FLUID_FAILED; } g_object_ref(cache); for(i = 0; i < cache->sel_count; i++) { IpatchSF2VoiceSelInfo *sel_info = &cache->sel_info[i]; switch(sel_info->type) { case IPATCH_SF2_VOICE_SEL_NOTE: sel_values[i] = key; break; case IPATCH_SF2_VOICE_SEL_VELOCITY: sel_values[i] = vel; break; default: /* match any; NOTE: according to documentation this should be IPATCH_SF2_VOICE_SEL_WILDCARD */ sel_values[i] = -1; break; } } voice_count = ipatch_sf2_voice_cache_select(cache, sel_values, voice_indices, MAX_INST_VOICES); /* loop over matching voice indexes */ for(voice_num = 0; voice_num < voice_count; voice_num++) { IpatchSF2GenArray *gen_array; fluid_sample_t *fsample; IpatchSF2Voice *voice = IPATCH_SF2_VOICE_CACHE_GET_VOICE(cache, voice_indices[voice_num]); if(!voice->sample_store) { /* For ROM and other non-readable samples */ continue; } fsample = ((fluid_instpatch_voice_user_data_t *)voice->user_data)->sample; ret = fluid_sample_set_sound_data(fsample, ipatch_sample_store_cache_get_location(IPATCH_SAMPLE_STORE_CACHE(voice->sample_store)), NULL, voice->sample_size, voice->rate, FALSE ); if(FLUID_UNLIKELY(ret == FLUID_FAILED)) { FLUID_LOG(FLUID_ERR, "fluid_sample_set_sound_data() failed"); goto error_rec; } ret = fluid_sample_set_loop(fsample, voice->loop_start, voice->loop_end); if(FLUID_UNLIKELY(ret == FLUID_FAILED)) { FLUID_LOG(FLUID_ERR, "fluid_sample_set_loop() failed"); goto error_rec; } ret = fluid_sample_set_pitch(fsample, voice->root_note, voice->fine_tune); if(FLUID_UNLIKELY(ret == FLUID_FAILED)) { FLUID_LOG(FLUID_ERR, "fluid_sample_set_pitch() failed"); goto error_rec; } /* allocate the FluidSynth voice */ flvoice = fluid_synth_alloc_voice(synth, fsample, chan, key, vel); if(flvoice == NULL) { ret = FLUID_FAILED; goto error_rec; } /* set only those generator parameters that are set */ gen_array = &voice->gen_array; for(i = 0; i < IPATCH_SF2_GEN_COUNT; i++) { if(IPATCH_SF2_GEN_ARRAY_TEST_FLAG(gen_array, i)) { fluid_voice_gen_set(flvoice, i, (float)(gen_array->values[i].sword)); } } p = voice->mod_list; while(p) { static const unsigned int mod_mask = (IPATCH_SF2_MOD_MASK_DIRECTION | IPATCH_SF2_MOD_MASK_POLARITY | IPATCH_SF2_MOD_MASK_TYPE); IpatchSF2Mod *mod = p->data; fluid_mod_set_dest(fmod, mod->dest); fluid_mod_set_source1(fmod, mod->src & IPATCH_SF2_MOD_MASK_CONTROL, ((mod->src & mod_mask) >> IPATCH_SF2_MOD_SHIFT_DIRECTION) | ((mod->src & IPATCH_SF2_MOD_MASK_CC) ? FLUID_MOD_CC : 0)); fluid_mod_set_source2(fmod, mod->amtsrc & IPATCH_SF2_MOD_MASK_CONTROL, ((mod->amtsrc & mod_mask) >> IPATCH_SF2_MOD_SHIFT_DIRECTION) | ((mod->amtsrc & IPATCH_SF2_MOD_MASK_CC) ? FLUID_MOD_CC : 0)); fluid_mod_set_amount(fmod, mod->amount); fluid_voice_add_mod(flvoice, fmod, FLUID_VOICE_OVERWRITE); p = p->next; } fluid_synth_start_voice(synth, flvoice); /* sample store reference taken over by fsample structure */ } ret = FLUID_OK; error_rec: g_object_unref(cache); return ret; } /* sfloader callback to get a patch file name */ static const char * fluid_instpatch_sfont_get_name(fluid_sfont_t *sfont) { fluid_instpatch_font_t *sfont_data = fluid_sfont_get_data(sfont); return sfont_data->name; } static void delete_fluid_instpatch_preset(fluid_instpatch_preset_t *preset_data) { fluid_return_if_fail(preset_data != NULL); /* -- remove voice cache reference */ g_object_unref(preset_data->cache); g_free(preset_data->name); FLUID_FREE(preset_data); } static void fluid_instpatch_preset_free(fluid_preset_t *preset) { fluid_return_if_fail(preset != NULL); delete_fluid_instpatch_preset(fluid_preset_get_data(preset)); delete_fluid_preset(preset); } /* sfloader callback to get a preset (instrument) by bank and preset number */ static fluid_preset_t * fluid_instpatch_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) { fluid_instpatch_font_t *sfont_data = fluid_sfont_get_data(sfont); fluid_preset_t *preset; fluid_list_t *list; for(list = sfont_data->preset_list; list != NULL; list = fluid_list_next(list)) { preset = (fluid_preset_t *)fluid_list_get(list); if((fluid_preset_get_banknum(preset) == bank) && (fluid_preset_get_num(preset) == prenum)) { return preset; } } return NULL; } static fluid_instpatch_voice_user_data_t *new_fluid_instpatch_voice_user_data(IpatchSampleStoreCache *sample_store) { fluid_instpatch_voice_user_data_t *data = FLUID_NEW(fluid_instpatch_voice_user_data_t); fluid_sample_t *sample = new_fluid_sample(); if(data == NULL || sample == NULL) { FLUID_FREE(data); delete_fluid_sample(sample); FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } data->sample = sample; /* Keep sample store cached by doing a dummy open */ ipatch_sample_store_cache_open(sample_store); data->sample_store = sample_store; return data; } static void fluid_instpatch_on_voice_user_data_destroy(gpointer user_data) { fluid_instpatch_voice_user_data_t *data = user_data; delete_fluid_sample(data->sample); ipatch_sample_store_cache_close(data->sample_store); FLUID_FREE(data); } static IpatchSF2VoiceCache *convert_dls_to_sf2_instrument(fluid_instpatch_font_t *patchfont, IpatchDLS2Inst *item, const char **err) { static const char no_conv[] = "Unable to find a voice cache converter for this type"; static const char conv_fail[] = "Failed to convert DLS inst to SF2 voices"; static const char cache_fail[] = "Failed to cache DLS inst to SF2 voices"; static const char oom[] = "Out of memory"; IpatchConverter *conv; IpatchSF2VoiceCache *cache; int i, count; /* create SF2 voice cache converter */ conv = ipatch_create_converter(G_OBJECT_TYPE(item), IPATCH_TYPE_SF2_VOICE_CACHE); /* no SF2 voice cache converter for this item type? */ if(conv == NULL) { *err = no_conv; return NULL; } cache = ipatch_sf2_voice_cache_new(NULL, 0); if(cache == NULL) { *err = oom; g_object_unref(conv); return NULL; } /* do not use the default modulator list of libinstpatch, we manage our own list of default modulators */ ipatch_sf2_voice_cache_set_default_mods(cache, NULL); ipatch_converter_add_input(conv, G_OBJECT(item)); ipatch_converter_add_output(conv, G_OBJECT(cache)); if(!ipatch_converter_convert(conv, NULL)) { *err = conv_fail; g_object_unref(cache); g_object_unref(conv); return NULL; } g_object_unref (conv); conv = NULL; /* Use voice->user_data to close open cached stores */ cache->voice_user_data_destroy = fluid_instpatch_on_voice_user_data_destroy; /* loop over voices and load sample data into RAM */ count = cache->voices->len; for(i = 0; i < count; i++) { IpatchSF2Voice *voice = &g_array_index(cache->voices, IpatchSF2Voice, i); if(!ipatch_sf2_voice_cache_sample_data(voice, NULL)) { *err = cache_fail; g_object_unref(cache); return NULL; } if((voice->user_data = new_fluid_instpatch_voice_user_data(IPATCH_SAMPLE_STORE_CACHE(voice->sample_store))) == NULL) { *err = oom; g_object_unref(cache); return NULL; } } /* !! caller takes over cache reference */ return cache; } fluid_instpatch_font_t *new_fluid_instpatch(fluid_sfont_t *sfont, const fluid_file_callbacks_t *fcbs, const char *filename) { fluid_instpatch_font_t *patchfont = NULL; GError *err = NULL; IpatchDLSReader *reader = NULL; IpatchDLSFile *file = NULL; IpatchFileHandle *handle = NULL; fluid_return_val_if_fail(filename != NULL, NULL); if((patchfont = FLUID_NEW(fluid_instpatch_font_t)) == NULL) { return NULL; } FLUID_MEMSET(patchfont, 0, sizeof(*patchfont)); FLUID_STRNCPY(&patchfont->name[0], filename, sizeof(patchfont->name)); /* open a file, we get a reference */ if((file = ipatch_dls_file_new()) == NULL) { FLUID_FREE(patchfont); return NULL; } /* ipatch_file_open() references the file again */ if((handle = ipatch_file_open(IPATCH_FILE(file), filename, "r", &err)) == NULL) { FLUID_LOG(FLUID_ERR, "ipatch_file_open() failed with error: '%s'", ipatch_gerror_message(err)); g_object_unref(file); FLUID_FREE(patchfont); return NULL; } /* get rid of the reference we own, we dont need it any longer */ g_object_unref(file); file = NULL; /* open a reader, this gives us a reference */ if((reader = ipatch_dls_reader_new(handle)) == NULL) { ipatch_file_close(handle); FLUID_FREE(patchfont); return NULL; } patchfont->dls = ipatch_dls_reader_load(reader, &err); /* unref the reader directly afterwards, not needed any longer */ g_object_unref(reader); reader = NULL; if(patchfont->dls == NULL) { FLUID_LOG(FLUID_ERR, "ipatch_dls_reader_new() failed with error: '%s'", ipatch_gerror_message(err)); // reader has already been unrefed, i.e. no need to call ipatch_file_close() FLUID_FREE(patchfont); return NULL; } else { /* at this point everything is owned by the IpatchDLS2*, no need for custom cleanups any longer */ IpatchIter iter; IpatchDLS2Inst *inst; gboolean success = ipatch_container_init_iter(IPATCH_CONTAINER(patchfont->dls), &iter, IPATCH_TYPE_DLS2_INST); if(success == FALSE) { goto bad_luck; } inst = ipatch_dls2_inst_first(&iter); if(inst == NULL) { FLUID_LOG(FLUID_ERR, "A soundfont file was accepted by libinstpatch, but it doesn't contain a single instrument. Dropping the whole file."); goto bad_luck; } /* loop over instruments, convert to sf2 voices, create fluid_samples, cache all presets in a list */ do { fluid_preset_t *preset; IpatchSF2VoiceCache *cache; int bank, prog; const char *err = NULL; ipatch_dls2_inst_get_midi_locale(inst, &bank, &prog); if((cache = convert_dls_to_sf2_instrument(patchfont, inst, &err)) == NULL) { FLUID_LOG(FLUID_WARN, "Unable to use DLS instrument bank %d , prog %d : %s.", bank, prog, err); } else { int is_percussion = (ipatch_item_get_flags(inst) & IPATCH_DLS2_INST_PERCUSSION) != 0; fluid_instpatch_preset_t *preset_data = FLUID_NEW(fluid_instpatch_preset_t); if(preset_data == NULL) { g_object_unref(inst); g_object_unref(cache); FLUID_LOG(FLUID_ERR, "Out of memory"); goto bad_luck; } FLUID_MEMSET(preset_data, 0, sizeof(*preset_data)); preset_data->parent_sfont = patchfont; preset_data->cache = cache; /* save name, bank and preset for quick lookup */ preset_data->bank = is_percussion * 128 + bank; preset_data->prog = prog; g_object_get(inst, "name", &preset_data->name, NULL); preset = new_fluid_preset(sfont, fluid_instpatch_preset_get_name, fluid_instpatch_preset_get_banknum, fluid_instpatch_preset_get_num, fluid_instpatch_preset_noteon, fluid_instpatch_preset_free); if(preset == NULL) { delete_fluid_instpatch_preset(preset_data); FLUID_LOG(FLUID_ERR, "Out of memory"); goto bad_luck; } else { fluid_preset_set_data(preset, preset_data); patchfont->preset_list = fluid_list_append(patchfont->preset_list, preset); } } inst = ipatch_dls2_inst_next(&iter); } while(inst); return patchfont; } bad_luck: delete_fluid_instpatch(patchfont); return NULL; } static int delete_fluid_instpatch(fluid_instpatch_font_t *pfont) { guint16 voice_indices[MAX_INST_VOICES]; int sel_values[IPATCH_SF2_VOICE_CACHE_MAX_SEL_VALUES]; fluid_list_t *list; fluid_return_val_if_fail(pfont != NULL, FLUID_OK); /* loop through all fluid samples and return error if any sample is currently in use for rendering */ for(list = pfont->preset_list; list; list = fluid_list_next(list)) { fluid_instpatch_preset_t *preset_data = fluid_preset_get_data((fluid_preset_t *)fluid_list_get(list)); int i, voice_count; /* lookup the voice cache that we've created on loading */ IpatchSF2VoiceCache *cache = preset_data->cache; if(cache == NULL) { continue; } g_object_ref(cache); for(i = 0; i < cache->sel_count; i++) { sel_values[i] = -1; } voice_count = ipatch_sf2_voice_cache_select(cache, sel_values, voice_indices, MAX_INST_VOICES); for(i = 0; i < voice_count; i++) { IpatchSF2Voice *voice = IPATCH_SF2_VOICE_CACHE_GET_VOICE(cache, voice_indices[i]); fluid_sample_t *fsample = ((fluid_instpatch_voice_user_data_t *)voice->user_data)->sample; if(fsample->refcount != 0) { g_object_unref(cache); return FLUID_FAILED; } } g_object_unref(cache); } for(list = pfont->preset_list; list; list = fluid_list_next(list)) { fluid_preset_delete_internal((fluid_preset_t *)fluid_list_get(list)); } delete_fluid_list(pfont->preset_list); // also unrefs IpatchDLSFile and IpatchFileHandle g_object_unref(pfont->dls); FLUID_FREE(pfont); return FLUID_OK; } static int fluid_instpatch_sfont_delete(fluid_sfont_t *sfont) { int ret; fluid_return_val_if_fail(sfont != NULL, -1); if((ret = delete_fluid_instpatch(fluid_sfont_get_data(sfont))) == FLUID_OK) { delete_fluid_sfont(sfont); } return ret; } static fluid_sfont_t *fluid_instpatch_loader_load(fluid_sfloader_t *loader, const char *filename) { fluid_instpatch_font_t *patchfont = NULL; fluid_sfont_t *sfont = NULL; sfont = new_fluid_sfont(fluid_instpatch_sfont_get_name, fluid_instpatch_sfont_get_preset, fluid_instpatch_iteration_start, fluid_instpatch_iteration_next, fluid_instpatch_sfont_delete); if(sfont == NULL) { return NULL; } if((patchfont = new_fluid_instpatch(sfont, &loader->file_callbacks, filename)) == NULL) { delete_fluid_sfont(sfont); return NULL; } fluid_sfont_set_data(sfont, patchfont); return sfont; } fluid_sfloader_t *new_fluid_instpatch_loader(fluid_settings_t *settings) { fluid_sfloader_t *loader; fluid_return_val_if_fail(settings != NULL, NULL); loader = new_fluid_sfloader(fluid_instpatch_loader_load, delete_fluid_sfloader); if(loader == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } fluid_sfloader_set_data(loader, settings); return loader; } fluidsynth-2.2.5/src/sfloader/fluid_instpatch.h000066400000000000000000000005121417326347500216420ustar00rootroot00000000000000 #ifndef _FLUID_INSTPATCH_H #define _FLUID_INSTPATCH_H #include "fluid_sfont.h" #include "fluid_settings.h" void fluid_instpatch_init(void); void fluid_instpatch_deinit(void); fluid_sfloader_t *new_fluid_instpatch_loader(fluid_settings_t *settings); int fluid_instpatch_supports_multi_init(void); #endif // _FLUID_INSTPATCH_H fluidsynth-2.2.5/src/sfloader/fluid_samplecache.c000066400000000000000000000211141417326347500221060ustar00rootroot00000000000000/* 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 */ /* CACHED SAMPLE DATA LOADER * * This is a wrapper around fluid_sffile_read_sample_data that attempts to cache the read * data across all FluidSynth instances in a global (process-wide) list. */ #include "fluid_samplecache.h" #include "fluid_sys.h" #include "fluid_list.h" typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t; struct _fluid_samplecache_entry_t { /* The following members all form the cache key */ char *filename; time_t modification_time; unsigned int sf_samplepos; unsigned int sf_samplesize; unsigned int sf_sample24pos; unsigned int sf_sample24size; unsigned int sample_start; unsigned int sample_end; int sample_type; /* End of cache key members */ short *sample_data; char *sample_data24; int sample_count; int num_references; int mlocked; }; static fluid_list_t *samplecache_list = NULL; static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT; static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, time_t mtime); static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, time_t mtime); static void delete_samplecache_entry(fluid_samplecache_entry_t *entry); static int fluid_get_file_modification_time(char *filename, time_t *modification_time); /* PUBLIC INTERFACE */ int fluid_samplecache_load(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, int try_mlock, short **sample_data, char **sample_data24) { fluid_samplecache_entry_t *entry; int ret; time_t mtime; fluid_mutex_lock(samplecache_mutex); if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED) { mtime = 0; } entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime); if(entry == NULL) { fluid_mutex_unlock(samplecache_mutex); entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime); if(entry == NULL) { ret = -1; goto unlock_exit; } fluid_mutex_lock(samplecache_mutex); samplecache_list = fluid_list_prepend(samplecache_list, entry); } fluid_mutex_unlock(samplecache_mutex); if(try_mlock && !entry->mlocked) { /* Lock the memory to disable paging. It's okay if this fails. It * probably means that the user doesn't have the required permission. */ if(fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0) { if(entry->sample_data24 != NULL) { entry->mlocked = (fluid_mlock(entry->sample_data24, entry->sample_count) == 0); } else { entry->mlocked = TRUE; } if(!entry->mlocked) { fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); } } } entry->num_references++; *sample_data = entry->sample_data; *sample_data24 = entry->sample_data24; ret = entry->sample_count; unlock_exit: return ret; } int fluid_samplecache_unload(const short *sample_data) { fluid_list_t *entry_list; fluid_samplecache_entry_t *entry; int ret; fluid_mutex_lock(samplecache_mutex); entry_list = samplecache_list; while(entry_list) { entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); if(sample_data == entry->sample_data) { entry->num_references--; if(entry->num_references == 0) { if(entry->mlocked) { fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); if(entry->sample_data24 != NULL) { fluid_munlock(entry->sample_data24, entry->sample_count); } } samplecache_list = fluid_list_remove(samplecache_list, entry); delete_samplecache_entry(entry); } ret = FLUID_OK; goto unlock_exit; } entry_list = fluid_list_next(entry_list); } FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache."); ret = FLUID_FAILED; unlock_exit: fluid_mutex_unlock(samplecache_mutex); return ret; } /* Private functions */ static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, time_t mtime) { fluid_samplecache_entry_t *entry; entry = FLUID_NEW(fluid_samplecache_entry_t); if(entry == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(entry, 0, sizeof(*entry)); entry->filename = FLUID_STRDUP(sf->fname); if(entry->filename == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_exit; } entry->sf_samplepos = sf->samplepos; entry->sf_samplesize = sf->samplesize; entry->sf_sample24pos = sf->sample24pos; entry->sf_sample24size = sf->sample24size; entry->sample_start = sample_start; entry->sample_end = sample_end; entry->sample_type = sample_type; entry->modification_time = mtime; entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type, &entry->sample_data, &entry->sample_data24); if(entry->sample_count < 0) { goto error_exit; } return entry; error_exit: delete_samplecache_entry(entry); return NULL; } static void delete_samplecache_entry(fluid_samplecache_entry_t *entry) { fluid_return_if_fail(entry != NULL); FLUID_FREE(entry->filename); FLUID_FREE(entry->sample_data); FLUID_FREE(entry->sample_data24); FLUID_FREE(entry); } static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, time_t mtime) { fluid_list_t *entry_list; fluid_samplecache_entry_t *entry; entry_list = samplecache_list; while(entry_list) { entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); if((FLUID_STRCMP(sf->fname, entry->filename) == 0) && (mtime == entry->modification_time) && (sf->samplepos == entry->sf_samplepos) && (sf->samplesize == entry->sf_samplesize) && (sf->sample24pos == entry->sf_sample24pos) && (sf->sample24size == entry->sf_sample24size) && (sample_start == entry->sample_start) && (sample_end == entry->sample_end) && (sample_type == entry->sample_type)) { return entry; } entry_list = fluid_list_next(entry_list); } return NULL; } static int fluid_get_file_modification_time(char *filename, time_t *modification_time) { fluid_stat_buf_t buf; if(fluid_stat(filename, &buf)) { return FLUID_FAILED; } *modification_time = buf.st_mtime; return FLUID_OK; } /* Only used for tests */ int fluid_samplecache_count_entries(void) { fluid_list_t *entry; int count = 0; fluid_mutex_lock(samplecache_mutex); for(entry = samplecache_list; entry != NULL; entry = fluid_list_next(entry)) { count++; } fluid_mutex_unlock(samplecache_mutex); return count; } fluidsynth-2.2.5/src/sfloader/fluid_samplecache.h000066400000000000000000000024531417326347500221200ustar00rootroot00000000000000/* 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_SAMPLECACHE_H #define _FLUID_SAMPLECACHE_H #include "fluid_sfont.h" #include "fluid_sffile.h" int fluid_samplecache_load(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, int try_mlock, short **data, char **data24); int fluid_samplecache_unload(const short *sample_data); /* Only used for tests */ int fluid_samplecache_count_entries(void); #endif /* _FLUID_SAMPLECACHE_H */ fluidsynth-2.2.5/src/sfloader/fluid_sffile.c000066400000000000000000002023141417326347500211140ustar00rootroot00000000000000/* 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_sffile.h" #include "fluid_sfont.h" #include "fluid_sys.h" #if LIBSNDFILE_SUPPORT #include #endif #if LIBINSTPATCH_SUPPORT #include #endif /*=================================sfload.c======================== Borrowed from Smurf SoundFont Editor by Josh Green =================================================================*/ /* FOURCC definitions */ #define RIFF_FCC FLUID_FOURCC('R','I','F','F') #define LIST_FCC FLUID_FOURCC('L','I','S','T') #define SFBK_FCC FLUID_FOURCC('s','f','b','k') #define INFO_FCC FLUID_FOURCC('I','N','F','O') #define SDTA_FCC FLUID_FOURCC('s','d','t','a') #define PDTA_FCC FLUID_FOURCC('p','d','t','a') /* info/sample/preset */ #define IFIL_FCC FLUID_FOURCC('i','f','i','l') #define ISNG_FCC FLUID_FOURCC('i','s','n','g') #define INAM_FCC FLUID_FOURCC('I','N','A','M') #define IROM_FCC FLUID_FOURCC('i','r','o','m') /* info ids (1st byte of info strings) */ #define IVER_FCC FLUID_FOURCC('i','v','e','r') #define ICRD_FCC FLUID_FOURCC('I','C','R','D') #define IENG_FCC FLUID_FOURCC('I','E','N','G') #define IPRD_FCC FLUID_FOURCC('I','P','R','D') /* more info ids */ #define ICOP_FCC FLUID_FOURCC('I','C','O','P') #define ICMT_FCC FLUID_FOURCC('I','C','M','T') #define ISFT_FCC FLUID_FOURCC('I','S','F','T') /* and yet more info ids */ #define SNAM_FCC FLUID_FOURCC('s','n','a','m') #define SMPL_FCC FLUID_FOURCC('s','m','p','l') /* sample ids */ #define PHDR_FCC FLUID_FOURCC('p','h','d','r') #define PBAG_FCC FLUID_FOURCC('p','b','a','g') #define PMOD_FCC FLUID_FOURCC('p','m','o','d') #define PGEN_FCC FLUID_FOURCC('p','g','e','n') /* preset ids */ #define IHDR_FCC FLUID_FOURCC('i','n','s','t') #define IBAG_FCC FLUID_FOURCC('i','b','a','g') #define IMOD_FCC FLUID_FOURCC('i','m','o','d') #define IGEN_FCC FLUID_FOURCC('i','g','e','n') /* instrument ids */ #define SHDR_FCC FLUID_FOURCC('s','h','d','r') /* sample info */ #define SM24_FCC FLUID_FOURCC('s','m','2','4') /* Set when the FCC code is unknown */ #define UNKN_ID FLUID_N_ELEMENTS(idlist) /* * This declares a uint32_t array containing the SF2 chunk identifiers. */ static const uint32_t idlist[] = { RIFF_FCC, LIST_FCC, SFBK_FCC, INFO_FCC, SDTA_FCC, PDTA_FCC, IFIL_FCC, ISNG_FCC, INAM_FCC, IROM_FCC, IVER_FCC, ICRD_FCC, IENG_FCC, IPRD_FCC, ICOP_FCC, ICMT_FCC, ISFT_FCC, SNAM_FCC, SMPL_FCC, PHDR_FCC, PBAG_FCC, PMOD_FCC, PGEN_FCC, IHDR_FCC, IBAG_FCC, IMOD_FCC, IGEN_FCC, SHDR_FCC, SM24_FCC }; static const unsigned short invalid_inst_gen[] = { GEN_UNUSED1, GEN_UNUSED2, GEN_UNUSED3, GEN_UNUSED4, GEN_RESERVED1, GEN_RESERVED2, GEN_RESERVED3, GEN_INSTRUMENT, }; static const unsigned short invalid_preset_gen[] = { GEN_STARTADDROFS, GEN_ENDADDROFS, GEN_STARTLOOPADDROFS, GEN_ENDLOOPADDROFS, GEN_STARTADDRCOARSEOFS, GEN_ENDADDRCOARSEOFS, GEN_STARTLOOPADDRCOARSEOFS, GEN_KEYNUM, GEN_VELOCITY, GEN_ENDLOOPADDRCOARSEOFS, GEN_SAMPLEMODE, GEN_EXCLUSIVECLASS, GEN_OVERRIDEROOTKEY, GEN_SAMPLEID, }; /* sfont file chunk sizes */ #define SF_PHDR_SIZE (38) #define SF_BAG_SIZE (4) #define SF_MOD_SIZE (10) #define SF_GEN_SIZE (4) #define SF_IHDR_SIZE (22) #define SF_SHDR_SIZE (46) #define READCHUNK(sf, var) \ do \ { \ if (sf->fcbs->fread(var, 8, sf->sffd) == FLUID_FAILED) \ return FALSE; \ ((SFChunk *)(var))->size = FLUID_LE32TOH(((SFChunk *)(var))->size); \ } while (0) #define READD(sf, var) \ do \ { \ uint32_t _temp; \ if (sf->fcbs->fread(&_temp, 4, sf->sffd) == FLUID_FAILED) \ return FALSE; \ var = FLUID_LE32TOH(_temp); \ } while (0) #define READW(sf, var) \ do \ { \ uint16_t _temp; \ if (sf->fcbs->fread(&_temp, 2, sf->sffd) == FLUID_FAILED) \ return FALSE; \ var = FLUID_LE16TOH(_temp); \ } while (0) #define READID(sf, var) \ do \ { \ if (sf->fcbs->fread(var, 4, sf->sffd) == FLUID_FAILED) \ return FALSE; \ } while (0) #define READSTR(sf, var) \ do \ { \ if (sf->fcbs->fread(var, 20, sf->sffd) == FLUID_FAILED) \ return FALSE; \ (*var)[20] = '\0'; \ } while (0) #define READB(sf, var) \ do \ { \ if (sf->fcbs->fread(&var, 1, sf->sffd) == FLUID_FAILED) \ return FALSE; \ } while (0) #define FSKIP(sf, size) \ do \ { \ if (sf->fcbs->fseek(sf->sffd, size, SEEK_CUR) == FLUID_FAILED) \ return FALSE; \ } while (0) #define FSKIPW(sf) \ do \ { \ if (sf->fcbs->fseek(sf->sffd, 2, SEEK_CUR) == FLUID_FAILED) \ return FALSE; \ } while (0) /* removes and advances a fluid_list_t pointer */ #define SLADVREM(list, item) \ do \ { \ fluid_list_t *_temp = item; \ item = fluid_list_next(item); \ list = fluid_list_remove_link(list, _temp); \ delete1_fluid_list(_temp); \ } while (0) static int load_header(SFData *sf); static int load_body(SFData *sf); static int process_info(SFData *sf, int size); static int process_sdta(SFData *sf, unsigned int size); static int process_pdta(SFData *sf, int size); static int load_phdr(SFData *sf, unsigned int size); static int load_pbag(SFData *sf, int size); static int load_pmod(SFData *sf, int size); static int load_ihdr(SFData *sf, unsigned int size); static int load_ibag(SFData *sf, int size); static int load_imod(SFData *sf, int size); static int load_shdr(SFData *sf, unsigned int size); static int chunkid(uint32_t id); static int read_listchunk(SFData *sf, SFChunk *chunk); static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size); static int preset_compare_func(const void *a, const void *b); static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist); static int valid_inst_genid(unsigned short genid); static int valid_preset_genid(unsigned short genid); static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data); static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24); /** * Check if a file is a SoundFont file. * * @param filename Path to the file to check * @return TRUE if it could be a SF2, SF3 or DLS file, FALSE otherwise * * If fluidsynth was built with DLS support, this function will also identify DLS files. * * @note This function only checks whether header(s) in the RIFF chunk are present. * A call to fluid_synth_sfload() might still fail. */ int fluid_is_soundfont(const char *filename) { FILE *fp; uint32_t fcc; int retcode = FALSE; const char* err_msg; do { if((fp = fluid_file_open(filename, &err_msg)) == NULL) { FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): fopen() failed: '%s'", err_msg); return retcode; } if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) { FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): failed to read RIFF chunk id."); break; } if(fcc != RIFF_FCC) { FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): expected RIFF chunk id '0x%04X' but got '0x%04X'.", (unsigned int) RIFF_FCC, (unsigned int)fcc); break; } if(FLUID_FSEEK(fp, 4, SEEK_CUR)) { FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): cannot seek +4 bytes."); break; } if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) { FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): failed to read SFBK chunk id."); break; } retcode = (fcc == SFBK_FCC); if(retcode) { break; // seems to be SF2, stop here } #ifdef LIBINSTPATCH_SUPPORT else { IpatchFileHandle *fhandle = ipatch_file_identify_open(filename, NULL); if(fhandle != NULL) { retcode = (ipatch_file_identify(fhandle->file, NULL) == IPATCH_TYPE_DLS_FILE); ipatch_file_close(fhandle); } } #endif } while(0); FLUID_FCLOSE(fp); return retcode; } /* * Open a SoundFont file and parse it's contents into a SFData structure. * * @param fname filename * @param fcbs file callback structure * @return the partially parsed SoundFont as SFData structure or NULL on error */ SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs) { SFData *sf; fluid_long_long_t fsize = 0; if(!(sf = FLUID_NEW(SFData))) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(sf, 0, sizeof(SFData)); fluid_rec_mutex_init(sf->mtx); sf->fcbs = fcbs; if((sf->sffd = fcbs->fopen(fname)) == NULL) { FLUID_LOG(FLUID_ERR, "Unable to open file '%s'", fname); goto error_exit; } sf->fname = FLUID_STRDUP(fname); if(sf->fname == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_exit; } /* get size of file by seeking to end */ if(fcbs->fseek(sf->sffd, 0L, SEEK_END) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Seek to end of file failed"); goto error_exit; } if((fsize = fcbs->ftell(sf->sffd)) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Get end of file position failed"); goto error_exit; } sf->filesize = fsize; if(fcbs->fseek(sf->sffd, 0, SEEK_SET) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Rewind to start of file failed"); goto error_exit; } if(!load_header(sf)) { goto error_exit; } return sf; error_exit: fluid_sffile_close(sf); return NULL; } /* * Parse all preset information from the soundfont * * @return FLUID_OK on success, otherwise FLUID_FAILED */ int fluid_sffile_parse_presets(SFData *sf) { if(!load_body(sf)) { return FLUID_FAILED; } return FLUID_OK; } /* Load sample data from the soundfont file * * This function will always return the sample data in WAV format. If the sample_type specifies an * Ogg Vorbis compressed sample, it will be decompressed automatically before returning. * * @param sf SFData instance * @param sample_start index of first sample point in Soundfont sample chunk * @param sample_end index of last sample point in Soundfont sample chunk * @param sample_type type of the sample in Soundfont * @param data pointer to sample data pointer, will point to loaded sample data on success * @param data24 pointer to 24-bit sample data pointer if 24-bit data present, will point to loaded * 24-bit sample data on success or NULL if no 24-bit data is present in file * * @return The number of sample words in returned buffers or -1 on failure */ int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, short **data, char **data24) { int num_samples; if(sample_type & FLUID_SAMPLETYPE_OGG_VORBIS) { num_samples = fluid_sffile_read_vorbis(sf, sample_start, sample_end, data); } else { num_samples = fluid_sffile_read_wav(sf, sample_start, sample_end, data, data24); } return num_samples; } /* * Close a SoundFont file and free the SFData structure. * * @param sf pointer to SFData structure * @param fcbs file callback structure */ void fluid_sffile_close(SFData *sf) { fluid_list_t *entry; SFPreset *preset; SFInst *inst; fluid_rec_mutex_destroy(sf->mtx); if(sf->sffd) { sf->fcbs->fclose(sf->sffd); } FLUID_FREE(sf->fname); entry = sf->info; while(entry) { FLUID_FREE(fluid_list_get(entry)); entry = fluid_list_next(entry); } delete_fluid_list(sf->info); entry = sf->preset; while(entry) { preset = (SFPreset *)fluid_list_get(entry); delete_preset(preset); entry = fluid_list_next(entry); } delete_fluid_list(sf->preset); entry = sf->inst; while(entry) { inst = (SFInst *)fluid_list_get(entry); delete_inst(inst); entry = fluid_list_next(entry); } delete_fluid_list(sf->inst); entry = sf->sample; while(entry) { FLUID_FREE(fluid_list_get(entry)); entry = fluid_list_next(entry); } delete_fluid_list(sf->sample); FLUID_FREE(sf); } /* * Private functions */ /* sound font file load functions */ static int chunkid(uint32_t id) { unsigned int i; for(i = 0; i < FLUID_N_ELEMENTS(idlist); i++) { if(idlist[i] == id) { break; } } /* Return chunk id or UNKN_ID if not found */ return i; } static int load_header(SFData *sf) { SFChunk chunk; READCHUNK(sf, &chunk); /* load RIFF chunk */ if(chunk.id != RIFF_FCC) { /* error if not RIFF */ FLUID_LOG(FLUID_ERR, "Not a RIFF file"); return FALSE; } READID(sf, &chunk.id); /* load file ID */ if(chunk.id != SFBK_FCC) { /* error if not SFBK_ID */ FLUID_LOG(FLUID_ERR, "Not a SoundFont file"); return FALSE; } if(chunk.size != sf->filesize - 8) { FLUID_LOG(FLUID_ERR, "SoundFont file size mismatch"); return FALSE; } /* Process INFO block */ if(!read_listchunk(sf, &chunk)) { return FALSE; } if(chunk.id != INFO_FCC) { FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk"); return FALSE; } if(!process_info(sf, chunk.size)) { return FALSE; } /* Process sample chunk */ if(!read_listchunk(sf, &chunk)) { return FALSE; } if(chunk.id != SDTA_FCC) { FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk"); return FALSE; } if(!process_sdta(sf, chunk.size)) { return FALSE; } /* process HYDRA chunk */ if(!read_listchunk(sf, &chunk)) { return FALSE; } if(chunk.id != PDTA_FCC) { FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk"); return FALSE; } sf->hydrapos = sf->fcbs->ftell(sf->sffd); sf->hydrasize = chunk.size; return TRUE; } static int load_body(SFData *sf) { if(sf->fcbs->fseek(sf->sffd, sf->hydrapos, SEEK_SET) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Failed to seek to HYDRA position"); return FALSE; } if(!process_pdta(sf, sf->hydrasize)) { return FALSE; } /* sort preset list by bank, preset # */ sf->preset = fluid_list_sort(sf->preset, preset_compare_func); return TRUE; } static int read_listchunk(SFData *sf, SFChunk *chunk) { READCHUNK(sf, chunk); /* read list chunk */ if(chunk->id != LIST_FCC) /* error if ! list chunk */ { FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse"); return FALSE; } READID(sf, &chunk->id); /* read id string */ chunk->size -= 4; return TRUE; } static int process_info(SFData *sf, int size) { SFChunk chunk; union { char *chr; uint32_t *fcc; } item; unsigned short ver; while(size > 0) { READCHUNK(sf, &chunk); size -= 8; if(chunk.id == IFIL_FCC) { /* sound font version chunk? */ if(chunk.size != 4) { FLUID_LOG(FLUID_ERR, "Sound font version info chunk has invalid size"); return FALSE; } READW(sf, ver); sf->version.major = ver; READW(sf, ver); 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 FALSE; } 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 FALSE; #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 FALSE; } } else if(chunk.id == IVER_FCC) { /* ROM version chunk? */ if(chunk.size != 4) { FLUID_LOG(FLUID_ERR, "ROM version info chunk has invalid size"); return FALSE; } READW(sf, ver); sf->romver.major = ver; READW(sf, ver); sf->romver.minor = ver; } else if(chunkid(chunk.id) != UNKN_ID) { if((chunk.id != ICMT_FCC && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2)) { FLUID_LOG(FLUID_ERR, "INFO sub chunk %.4s has invalid chunk size of %d bytes", (char*)&chunk.id, chunk.size); return FALSE; } /* alloc for chunk fcc and da chunk */ if(!(item.fcc = FLUID_MALLOC(chunk.size + sizeof(uint32_t) + 1))) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } /* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */ sf->info = fluid_list_append(sf->info, item.fcc); /* save chunk fcc and update pointer to data value */ *item.fcc++ = chunk.id; if(sf->fcbs->fread(item.chr, chunk.size, sf->sffd) == FLUID_FAILED) { return FALSE; } /* force terminate info item */ item.chr[chunk.size] = '\0'; } else { FLUID_LOG(FLUID_ERR, "Invalid chunk id in INFO chunk"); return FALSE; } size -= chunk.size; } if(size < 0) { FLUID_LOG(FLUID_ERR, "INFO chunk size mismatch"); return FALSE; } return TRUE; } static int process_sdta(SFData *sf, unsigned int size) { SFChunk chunk; if(size == 0) { return TRUE; /* no sample data? */ } /* read sub chunk */ READCHUNK(sf, &chunk); size -= 8; if(chunk.id != SMPL_FCC) { FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead"); return FALSE; } /* 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) { FLUID_LOG(FLUID_ERR, "SDTA chunk size mismatch"); return FALSE; } /* sample data follows */ sf->samplepos = sf->fcbs->ftell(sf->sffd); /* used to check validity of sample headers */ sf->samplesize = chunk.size; FSKIP(sf, chunk.size); size -= chunk.size; if(sf->version.major >= 2 && sf->version.minor >= 4) { /* any chance to find another chunk here? */ if(size > 8) { /* read sub chunk */ READCHUNK(sf, &chunk); size -= 8; if(chunk.id == SM24_FCC) { int sm24size, sdtahalfsize; FLUID_LOG(FLUID_DBG, "Found SM24 chunk"); if(chunk.size > size) { FLUID_LOG(FLUID_WARN, "SM24 exceeds SDTA chunk, ignoring SM24"); goto ret; // no error } sdtahalfsize = sf->samplesize / 2; /* + 1 byte in the case that half the size of smpl chunk is an odd value */ sdtahalfsize += sdtahalfsize % 2; sm24size = chunk.size; if(sdtahalfsize != sm24size) { FLUID_LOG(FLUID_WARN, "SM24 not equal to half the size of SMPL chunk (0x%X != " "0x%X), ignoring SM24", sm24size, sdtahalfsize); goto ret; // no error } /* sample data24 follows */ sf->sample24pos = sf->fcbs->ftell(sf->sffd); sf->sample24size = sm24size; } } } ret: FSKIP(sf, size); return TRUE; } static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size) { READCHUNK(sf, chunk); *size -= 8; if(chunk->id != expid) { FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", (char*)&expid); return FALSE; } if(chunk->size % reclen) /* valid chunk size? */ { FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", (char*)&expid, reclen); return FALSE; } if((*size -= chunk->size) < 0) { FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", (char*)&expid); return FALSE; } return TRUE; } static int process_pdta(SFData *sf, int size) { SFChunk chunk; if(!pdtahelper(sf, PHDR_FCC, SF_PHDR_SIZE, &chunk, &size)) { return FALSE; } if(!load_phdr(sf, chunk.size)) { return FALSE; } if(!pdtahelper(sf, PBAG_FCC, SF_BAG_SIZE, &chunk, &size)) { return FALSE; } if(!load_pbag(sf, chunk.size)) { return FALSE; } if(!pdtahelper(sf, PMOD_FCC, SF_MOD_SIZE, &chunk, &size)) { return FALSE; } if(!load_pmod(sf, chunk.size)) { return FALSE; } if(!pdtahelper(sf, PGEN_FCC, SF_GEN_SIZE, &chunk, &size)) { return FALSE; } if(!load_pgen(sf, chunk.size)) { return FALSE; } if(!pdtahelper(sf, IHDR_FCC, SF_IHDR_SIZE, &chunk, &size)) { return FALSE; } if(!load_ihdr(sf, chunk.size)) { return FALSE; } if(!pdtahelper(sf, IBAG_FCC, SF_BAG_SIZE, &chunk, &size)) { return FALSE; } if(!load_ibag(sf, chunk.size)) { return FALSE; } if(!pdtahelper(sf, IMOD_FCC, SF_MOD_SIZE, &chunk, &size)) { return FALSE; } if(!load_imod(sf, chunk.size)) { return FALSE; } if(!pdtahelper(sf, IGEN_FCC, SF_GEN_SIZE, &chunk, &size)) { return FALSE; } if(!load_igen(sf, chunk.size)) { return FALSE; } if(!pdtahelper(sf, SHDR_FCC, SF_SHDR_SIZE, &chunk, &size)) { return FALSE; } if(!load_shdr(sf, chunk.size)) { return FALSE; } return TRUE; } /* preset header loader */ static int load_phdr(SFData *sf, unsigned int size) { unsigned int i; int i2; SFPreset *preset, *prev_preset = NULL; unsigned short pbag_idx, prev_pbag_idx = 0; if(size % SF_PHDR_SIZE || size == 0) { FLUID_LOG(FLUID_ERR, "Preset header chunk size is invalid"); return FALSE; } i = size / SF_PHDR_SIZE - 1; if(i == 0) { /* at least one preset + term record */ FLUID_LOG(FLUID_WARN, "File contains no presets"); FSKIP(sf, SF_PHDR_SIZE); return TRUE; } for(; i > 0; i--) { /* load all preset headers */ if((preset = FLUID_NEW(SFPreset)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } sf->preset = fluid_list_append(sf->preset, preset); preset->zone = NULL; /* In case of failure, fluid_sffile_close can cleanup */ READSTR(sf, &preset->name); /* possible read failure ^ */ READW(sf, preset->prenum); READW(sf, preset->bank); READW(sf, pbag_idx); FSKIP(sf, 4); /* library ignored */ FSKIP(sf, 4); /* genre ignored */ FSKIP(sf, 4); /* morphology ignored */ if(prev_preset) { /* not first preset? */ if(pbag_idx < prev_pbag_idx) { FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic"); return FALSE; } i2 = pbag_idx - prev_pbag_idx; while(i2--) { prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL); } } else if(pbag_idx > 0) /* 1st preset, warn if ofs >0 */ { FLUID_LOG(FLUID_WARN, "%d preset zones not referenced, discarding", pbag_idx); } prev_preset = preset; /* update preset ptr */ prev_pbag_idx = pbag_idx; } FSKIP(sf, 24); READW(sf, pbag_idx); /* Read terminal generator index */ FSKIP(sf, 12); if(pbag_idx < prev_pbag_idx) { FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic"); return FALSE; } i2 = pbag_idx - prev_pbag_idx; while(i2--) { prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL); } return TRUE; } /* preset bag loader */ static int load_pbag(SFData *sf, int size) { fluid_list_t *preset_list; fluid_list_t *zone_list; SFZone *z, *pz = NULL; unsigned short genndx, modndx; unsigned short pgenndx = 0, pmodndx = 0; unsigned short i; if(size % SF_BAG_SIZE || size == 0) /* size is multiple of SF_BAG_SIZE? */ { FLUID_LOG(FLUID_ERR, "Preset bag chunk size is invalid"); return FALSE; } preset_list = sf->preset; /* traverse through presets */ while(preset_list) { zone_list = ((SFPreset *)(preset_list->data))->zone; /* traverse preset's zones */ while(zone_list) { if((size -= SF_BAG_SIZE) < 0) { FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch"); return FALSE; } if((z = FLUID_NEW(SFZone)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } zone_list->data = z; z->gen = NULL; /* Init gen and mod before possible failure, */ z->mod = NULL; /* to ensure proper cleanup (fluid_sffile_close) */ READW(sf, genndx); /* possible read failure ^ */ READW(sf, modndx); if(pz) { /* if not first zone */ if(genndx < pgenndx) { FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic"); return FALSE; } if(modndx < pmodndx) { FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic"); return FALSE; } 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 */ zone_list = fluid_list_next(zone_list); } preset_list = fluid_list_next(preset_list); } size -= SF_BAG_SIZE; if(size != 0) { FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch"); return FALSE; } READW(sf, genndx); READW(sf, modndx); 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 TRUE; } if(genndx < pgenndx) { FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic"); return FALSE; } if(modndx < pmodndx) { FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic"); return FALSE; } 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 TRUE; } /* preset modulator loader */ static int load_pmod(SFData *sf, int size) { fluid_list_t *preset_list; fluid_list_t *zone_list; fluid_list_t *mod_list; SFMod *m; preset_list = sf->preset; while(preset_list) { /* traverse through all presets */ zone_list = ((SFPreset *)(preset_list->data))->zone; while(zone_list) { /* traverse this preset's zones */ mod_list = ((SFZone *)(zone_list->data))->mod; while(mod_list) { /* load zone's modulators */ if((size -= SF_MOD_SIZE) < 0) { FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch"); return FALSE; } if((m = FLUID_NEW(SFMod)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } mod_list->data = m; READW(sf, m->src); READW(sf, m->dest); READW(sf, m->amount); READW(sf, m->amtsrc); READW(sf, m->trans); mod_list = fluid_list_next(mod_list); } zone_list = fluid_list_next(zone_list); } preset_list = fluid_list_next(preset_list); } /* If there isn't even a terminal record Hmmm, the specs say there should be one, but.. */ if(size == 0) { return TRUE; } size -= SF_MOD_SIZE; if(size != 0) { FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch"); return FALSE; } FSKIP(sf, SF_MOD_SIZE); /* terminal mod */ return TRUE; } /* ------------------------------------------------------------------- * 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 * ------------------------------------------------------------------- */ int load_pgen(SFData *sf, int size) { fluid_list_t *dup; fluid_list_t *preset_list; fluid_list_t *zone_list; fluid_list_t *gen_list; SFZone *zone; SFGen *g; SFPreset *preset; SFGenAmount genval; unsigned short genid; int level, skip, drop, discarded; preset_list = sf->preset; while(preset_list) { preset = fluid_list_get(preset_list); /* traverse through all presets */ discarded = FALSE; zone_list = preset->zone; /* traverse preset's zones */ while(zone_list) { zone = fluid_list_get(zone_list); level = 0; gen_list = zone->gen; while(gen_list) { /* load zone's generators */ dup = NULL; skip = FALSE; drop = FALSE; if((size -= SF_GEN_SIZE) < 0) { FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); return FALSE; } READW(sf, genid); if(genid == GEN_KEYRANGE) { /* nothing precedes */ if(level == 0) { level = 1; READB(sf, genval.range.lo); READB(sf, genval.range.hi); } else { skip = TRUE; } } else if(genid == GEN_VELRANGE) { /* only KeyRange precedes */ if(level <= 1) { level = 2; READB(sf, genval.range.lo); READB(sf, genval.range.hi); } else { skip = TRUE; } } else if(genid == GEN_INSTRUMENT) { /* inst is last gen */ level = 3; READW(sf, genval.uword); } else { level = 2; if(valid_preset_genid(genid)) { /* generator valid? */ READW(sf, genval.sword); dup = find_gen_by_id(genid, zone->gen); } else { skip = TRUE; } } if(!skip) { if(!dup) { /* if gen ! dup alloc new */ if((g = FLUID_NEW(SFGen)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } gen_list->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(sf); } if(!drop) { gen_list = fluid_list_next(gen_list); /* next gen */ } else { SLADVREM(zone->gen, gen_list); /* drop place holder */ } /* GEN_INSTRUMENT should be the last generator */ if (level == 3) { break; } } /* generator loop */ /* Anything below level 3 means it's a global zone. The global zone * should always be the first zone in the list, so discard any * other global zones we encounter */ if(level < 3 && (zone_list != preset->zone)) { /* advance to next zone before deleting the current list element */ zone_list = fluid_list_next(zone_list); FLUID_LOG(FLUID_WARN, "Preset '%s': Discarding invalid global zone", preset->name); preset->zone = fluid_list_remove(preset->zone, zone); delete_zone(zone); /* we have already advanced the zone_list pointer, so continue with next zone */ continue; } /* All remaining generators are invalid and should be discarded * (because they come after an instrument generator) */ while(gen_list) { discarded = TRUE; if((size -= SF_GEN_SIZE) < 0) { FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); return FALSE; } FSKIP(sf, SF_GEN_SIZE); SLADVREM(zone->gen, gen_list); } zone_list = fluid_list_next(zone_list); } if(discarded) { FLUID_LOG(FLUID_WARN, "Preset '%s': Some invalid generators were discarded", preset->name); } preset_list = fluid_list_next(preset_list); } /* in case there isn't a terminal record */ if(size == 0) { return TRUE; } size -= SF_GEN_SIZE; if(size != 0) { FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); return FALSE; } FSKIP(sf, SF_GEN_SIZE); /* terminal gen */ return TRUE; } /* instrument header loader */ static int load_ihdr(SFData *sf, unsigned int size) { unsigned int i; int i2; SFInst *inst, *prev_inst = NULL; /* ptr to current & previous instrument */ unsigned short zndx, pzndx = 0; if(size % SF_IHDR_SIZE || size == 0) /* chunk size is valid? */ { FLUID_LOG(FLUID_ERR, "Instrument header has invalid size"); return FALSE; } size = size / SF_IHDR_SIZE - 1; if(size == 0) { /* at least one preset + term record */ FLUID_LOG(FLUID_WARN, "File contains no instruments"); FSKIP(sf, SF_IHDR_SIZE); return TRUE; } for(i = 0; i < size; i++) { /* load all instrument headers */ if((inst = FLUID_NEW(SFInst)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } sf->inst = fluid_list_append(sf->inst, inst); inst->zone = NULL; /* For proper cleanup if fail (fluid_sffile_close) */ inst->idx = i; READSTR(sf, &inst->name); /* Possible read failure ^ */ READW(sf, zndx); if(prev_inst) { /* not first instrument? */ if(zndx < pzndx) { FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic"); return FALSE; } i2 = zndx - pzndx; while(i2--) { prev_inst->zone = fluid_list_prepend(prev_inst->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; prev_inst = inst; /* update instrument ptr */ } FSKIP(sf, 20); READW(sf, zndx); if(zndx < pzndx) { FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic"); return FALSE; } i2 = zndx - pzndx; while(i2--) { prev_inst->zone = fluid_list_prepend(prev_inst->zone, NULL); } return TRUE; } /* instrument bag loader */ static int load_ibag(SFData *sf, int size) { fluid_list_t *inst_list; fluid_list_t *zone_list; SFZone *z, *pz = NULL; unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0; int i; if(size % SF_BAG_SIZE || size == 0) /* size is multiple of SF_BAG_SIZE? */ { FLUID_LOG(FLUID_ERR, "Instrument bag chunk size is invalid"); return FALSE; } inst_list = sf->inst; while(inst_list) { /* traverse through inst */ zone_list = ((SFInst *)(inst_list->data))->zone; while(zone_list) { /* load this inst's zones */ if((size -= SF_BAG_SIZE) < 0) { FLUID_LOG(FLUID_ERR, "Instrument bag chunk size mismatch"); return FALSE; } if((z = FLUID_NEW(SFZone)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } zone_list->data = z; z->gen = NULL; /* In case of failure, */ z->mod = NULL; /* fluid_sffile_close can clean up */ READW(sf, genndx); /* READW = possible read failure */ READW(sf, modndx); if(pz) { /* if not first zone */ if(genndx < pgenndx) { FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic"); return FALSE; } if(modndx < pmodndx) { FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic"); return FALSE; } 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; zone_list = fluid_list_next(zone_list); } inst_list = fluid_list_next(inst_list); } size -= SF_BAG_SIZE; if(size != 0) { FLUID_LOG(FLUID_ERR, "Instrument chunk size mismatch"); return FALSE; } READW(sf, genndx); READW(sf, modndx); 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 TRUE; } if(genndx < pgenndx) { FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic"); return FALSE; } if(modndx < pmodndx) { FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic"); return FALSE; } 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 TRUE; } /* instrument modulator loader */ static int load_imod(SFData *sf, int size) { fluid_list_t *inst_list; fluid_list_t *zone_list; fluid_list_t *mod_list; SFMod *m; inst_list = sf->inst; while(inst_list) { /* traverse through all inst */ zone_list = ((SFInst *)(inst_list->data))->zone; while(zone_list) { /* traverse this inst's zones */ mod_list = ((SFZone *)(zone_list->data))->mod; while(mod_list) { /* load zone's modulators */ if((size -= SF_MOD_SIZE) < 0) { FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch"); return FALSE; } if((m = FLUID_NEW(SFMod)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } mod_list->data = m; READW(sf, m->src); READW(sf, m->dest); READW(sf, m->amount); READW(sf, m->amtsrc); READW(sf, m->trans); mod_list = fluid_list_next(mod_list); } zone_list = fluid_list_next(zone_list); } inst_list = fluid_list_next(inst_list); } /* If there isn't even a terminal record Hmmm, the specs say there should be one, but.. */ if(size == 0) { return TRUE; } size -= SF_MOD_SIZE; if(size != 0) { FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch"); return FALSE; } FSKIP(sf, SF_MOD_SIZE); /* terminal mod */ return TRUE; } /* load instrument generators (see load_pgen for loading rules) */ int load_igen(SFData *sf, int size) { fluid_list_t *dup; fluid_list_t *inst_list; fluid_list_t *zone_list; fluid_list_t *gen_list; SFZone *zone; SFGen *g; SFInst *inst; SFGenAmount genval; unsigned short genid; int level, skip, drop, discarded; inst_list = sf->inst; /* traverse through all instruments */ while(inst_list) { inst = fluid_list_get(inst_list); discarded = FALSE; zone_list = inst->zone; /* traverse this instrument's zones */ while(zone_list) { zone = fluid_list_get(zone_list); level = 0; gen_list = zone->gen; while(gen_list) { /* load zone's generators */ dup = NULL; skip = FALSE; drop = FALSE; if((size -= SF_GEN_SIZE) < 0) { FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch"); return FALSE; } READW(sf, genid); if(genid == GEN_KEYRANGE) { /* nothing precedes */ if(level == 0) { level = 1; READB(sf, genval.range.lo); READB(sf, genval.range.hi); } else { skip = TRUE; } } else if(genid == GEN_VELRANGE) { /* only KeyRange precedes */ if(level <= 1) { level = 2; READB(sf, genval.range.lo); READB(sf, genval.range.hi); } else { skip = TRUE; } } else if(genid == GEN_SAMPLEID) { /* sample is last gen */ level = 3; READW(sf, genval.uword); } else { level = 2; if(valid_inst_genid(genid)) { /* gen valid? */ READW(sf, genval.sword); dup = find_gen_by_id(genid, zone->gen); } else { skip = TRUE; } } if(!skip) { if(!dup) { /* if gen ! dup alloc new */ if((g = FLUID_NEW(SFGen)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } gen_list->data = g; g->id = genid; } else { g = (SFGen *)(dup->data); drop = TRUE; } g->amount = genval; } else { /* skip this generator */ discarded = TRUE; drop = TRUE; FSKIPW(sf); } if(!drop) { gen_list = fluid_list_next(gen_list); /* next gen */ } else { SLADVREM(zone->gen, gen_list); } /* GEN_SAMPLEID should be last generator */ if (level == 3) { break; } } /* generator loop */ /* Anything below level 3 means it's a global zone. The global zone * should always be the first zone in the list, so discard any * other global zones we encounter */ if(level < 3 && (zone_list != inst->zone)) { /* advance to next zone before deleting the current list element */ zone_list = fluid_list_next(zone_list); FLUID_LOG(FLUID_WARN, "Instrument '%s': Discarding invalid global zone", inst->name); inst->zone = fluid_list_remove(inst->zone, zone); delete_zone(zone); /* we have already advanced the zone_list pointer, so continue with next zone */ continue; } /* All remaining generators must be invalid and should be discarded * (because they come after a sampleid generator) */ while(gen_list) { discarded = TRUE; if((size -= SF_GEN_SIZE) < 0) { FLUID_LOG(FLUID_ERR, "Instrument generator chunk size mismatch"); return FALSE; } FSKIP(sf, SF_GEN_SIZE); SLADVREM(zone->gen, gen_list); } zone_list = fluid_list_next(zone_list); /* next zone */ } if(discarded) { FLUID_LOG(FLUID_WARN, "Instrument '%s': Some invalid generators were discarded", inst->name); } inst_list = fluid_list_next(inst_list); } /* for those non-terminal record cases, grr! */ if(size == 0) { return TRUE; } size -= SF_GEN_SIZE; if(size != 0) { FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch"); return FALSE; } FSKIP(sf, SF_GEN_SIZE); /* terminal gen */ return TRUE; } /* sample header loader */ static int load_shdr(SFData *sf, unsigned int size) { unsigned int i; SFSample *p; if(size % SF_SHDR_SIZE || size == 0) /* size is multiple of SHDR size? */ { FLUID_LOG(FLUID_ERR, "Sample header has invalid size"); return FALSE; } size = size / SF_SHDR_SIZE - 1; if(size == 0) { /* at least one sample + term record? */ FLUID_LOG(FLUID_WARN, "File contains no samples"); FSKIP(sf, SF_SHDR_SIZE); return TRUE; } /* load all sample headers */ for(i = 0; i < size; i++) { if((p = FLUID_NEW(SFSample)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } p->idx = i; sf->sample = fluid_list_prepend(sf->sample, p); READSTR(sf, &p->name); READD(sf, p->start); READD(sf, p->end); READD(sf, p->loopstart); READD(sf, p->loopend); READD(sf, p->samplerate); READB(sf, p->origpitch); READB(sf, p->pitchadj); FSKIPW(sf); /* skip sample link */ READW(sf, p->sampletype); } FSKIP(sf, SF_SHDR_SIZE); /* skip terminal shdr */ return TRUE; } void delete_preset(SFPreset *preset) { fluid_list_t *entry; SFZone *zone; if(!preset) { return; } entry = preset->zone; while(entry) { zone = (SFZone *)fluid_list_get(entry); delete_zone(zone); entry = fluid_list_next(entry); } delete_fluid_list(preset->zone); FLUID_FREE(preset); } void delete_inst(SFInst *inst) { fluid_list_t *entry; SFZone *zone; if(!inst) { return; } entry = inst->zone; while(entry) { zone = (SFZone *)fluid_list_get(entry); delete_zone(zone); entry = fluid_list_next(entry); } delete_fluid_list(inst->zone); FLUID_FREE(inst); } /* Free all elements of a zone (Preset or Instrument) */ void delete_zone(SFZone *zone) { fluid_list_t *entry; if(!zone) { return; } entry = zone->gen; while(entry) { FLUID_FREE(fluid_list_get(entry)); entry = fluid_list_next(entry); } delete_fluid_list(zone->gen); entry = zone->mod; while(entry) { FLUID_FREE(fluid_list_get(entry)); entry = fluid_list_next(entry); } delete_fluid_list(zone->mod); FLUID_FREE(zone); } /* preset sort function, first by bank, then by preset # */ static int preset_compare_func(const void *a, const void *b) { int aval, bval; aval = (int)(((const SFPreset *)a)->bank) << 16 | ((const SFPreset *)a)->prenum; bval = (int)(((const SFPreset *)b)->bank) << 16 | ((const SFPreset *)b)->prenum; return (aval - bval); } /* Find a generator by its id in the passed in list. * * @return pointer to SFGen if found, otherwise NULL */ static fluid_list_t *find_gen_by_id(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 */ static int valid_inst_genid(unsigned short genid) { size_t i; /* OVERRIDEROOTKEY is the last official generator, everything * following it are generators internal to FluidSynth and will * never appear in a SoundFont file. */ if(genid > GEN_OVERRIDEROOTKEY) { return FALSE; } for(i = 0; i < FLUID_N_ELEMENTS(invalid_inst_gen); i++) { if (invalid_inst_gen[i] == genid) { return FALSE; } } return TRUE; } /* check validity of preset generator */ static int valid_preset_genid(unsigned short genid) { size_t i; if(!valid_inst_genid(genid)) { return FALSE; } for(i = 0; i < FLUID_N_ELEMENTS(invalid_preset_gen); i++) { if (invalid_preset_gen[i] == genid) { return FALSE; } } return TRUE; } static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24) { short *loaded_data = NULL; char *loaded_data24 = NULL; unsigned int num_samples; fluid_return_val_if_fail((end + 1) > start , -1); num_samples = (end + 1) - start; if((start * sizeof(short) > sf->samplesize) || (end * sizeof(short) > sf->samplesize)) { FLUID_LOG(FLUID_ERR, "Sample offsets exceed sample data chunk"); goto error_exit; } /* Load 16-bit sample data */ if(sf->fcbs->fseek(sf->sffd, sf->samplepos + (start * sizeof(short)), SEEK_SET) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Failed to seek to sample position"); goto error_exit; } loaded_data = FLUID_ARRAY(short, num_samples); if(loaded_data == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_exit; } if(sf->fcbs->fread(loaded_data, num_samples * sizeof(short), sf->sffd) == FLUID_FAILED) { #if FLUID_VERSION_CHECK(FLUIDSYNTH_VERSION_MAJOR, FLUIDSYNTH_VERSION_MINOR, FLUIDSYNTH_VERSION_MICRO) < FLUID_VERSION_CHECK(2,2,0) if((int)(num_samples * sizeof(short)) < 0) { FLUID_LOG(FLUID_INFO, "This SoundFont seems to be bigger than 2GB, which is not supported in this version of fluidsynth. " "You need to use at least fluidsynth 2.2.0"); } #endif FLUID_LOG(FLUID_ERR, "Failed to read sample data"); goto error_exit; } /* If this machine is big endian, byte swap the 16 bit samples */ if(FLUID_IS_BIG_ENDIAN) { unsigned int i; for(i = 0; i < num_samples; i++) { loaded_data[i] = FLUID_LE16TOH(loaded_data[i]); } } *data = loaded_data; /* Optionally load additional 8 bit sample data for 24-bit support. Any failures while loading * the 24-bit sample data will be logged as errors but won't prevent the sample reading to * fail, as sound output is still possible with the 16-bit sample data. */ if(sf->sample24pos) { if((start > sf->sample24size) || (end > sf->sample24size)) { FLUID_LOG(FLUID_ERR, "Sample offsets exceed 24-bit sample data chunk"); goto error24_exit; } if(sf->fcbs->fseek(sf->sffd, sf->sample24pos + start, SEEK_SET) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Failed to seek position for 24-bit sample data in data file"); goto error24_exit; } loaded_data24 = FLUID_ARRAY(char, num_samples); if(loaded_data24 == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory reading 24-bit sample data"); goto error24_exit; } if(sf->fcbs->fread(loaded_data24, num_samples, sf->sffd) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Failed to read 24-bit sample data"); goto error24_exit; } } *data24 = loaded_data24; return num_samples; error24_exit: FLUID_LOG(FLUID_WARN, "Ignoring 24-bit sample data, sound quality might suffer"); FLUID_FREE(loaded_data24); *data24 = NULL; return num_samples; error_exit: FLUID_FREE(loaded_data); FLUID_FREE(loaded_data24); return -1; } /* Ogg Vorbis loading and decompression */ #if LIBSNDFILE_SUPPORT /* Virtual file access routines to allow loading individually compressed * samples from the Soundfont sample data chunk using the file callbacks * passing in during opening of the file */ typedef struct _sfvio_data_t { SFData *sffile; sf_count_t start; /* start byte offset of compressed data */ sf_count_t end; /* end byte offset of compressed data */ sf_count_t offset; /* current virtual file offset from start byte offset */ } sfvio_data_t; static sf_count_t sfvio_get_filelen(void *user_data) { sfvio_data_t *data = user_data; return (data->end + 1) - data->start; } static sf_count_t sfvio_seek(sf_count_t offset, int whence, void *user_data) { sfvio_data_t *data = user_data; SFData *sf = data->sffile; sf_count_t new_offset; switch(whence) { case SEEK_SET: new_offset = offset; break; case SEEK_CUR: new_offset = data->offset + offset; break; case SEEK_END: new_offset = sfvio_get_filelen(user_data) + offset; break; default: goto fail; /* proper error handling not possible?? */ } new_offset += data->start; fluid_rec_mutex_lock(sf->mtx); if (data->start <= new_offset && new_offset <= data->end && sf->fcbs->fseek(sf->sffd, new_offset, SEEK_SET) != FLUID_FAILED) { data->offset = new_offset - data->start; } fluid_rec_mutex_unlock(sf->mtx); fail: return data->offset; } static sf_count_t sfvio_read(void *ptr, sf_count_t count, void *user_data) { sfvio_data_t *data = user_data; SFData *sf = data->sffile; sf_count_t remain; remain = sfvio_get_filelen(user_data) - data->offset; if(count > remain) { count = remain; } if(count == 0) { return count; } fluid_rec_mutex_lock(sf->mtx); if (sf->fcbs->fseek(sf->sffd, data->start + data->offset, SEEK_SET) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "This should never happen: fseek failed in sfvoid_read()"); count = 0; } else { if (sf->fcbs->fread(ptr, count, sf->sffd) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Failed to read compressed sample data"); count = 0; } } fluid_rec_mutex_unlock(sf->mtx); data->offset += count; return count; } static sf_count_t sfvio_tell(void *user_data) { sfvio_data_t *data = user_data; return data->offset; } /** * Read Ogg Vorbis compressed data from the Soundfont and decompress it, returning the number of samples * in the decompressed WAV. Only 16-bit mono samples are supported. * * Note that this function takes byte indices for start and end source data. The sample headers in SF3 * files use byte indices, so those pointers can be passed directly to this function. * * This function uses a virtual file structure in order to read the Ogg Vorbis * data from arbitrary locations in the source file. */ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data) { SNDFILE *sndfile; SF_INFO sfinfo; SF_VIRTUAL_IO sfvio = { sfvio_get_filelen, sfvio_seek, sfvio_read, NULL, sfvio_tell }; sfvio_data_t sfdata; short *wav_data = NULL; if((start_byte > sf->samplesize) || (end_byte > sf->samplesize)) { FLUID_LOG(FLUID_ERR, "Ogg Vorbis data offsets exceed sample data chunk"); return -1; } // Initialize file position indicator and SF_INFO structure sfdata.sffile = sf; sfdata.start = sf->samplepos + start_byte; sfdata.end = sf->samplepos + end_byte; sfdata.offset = -1; /* Seek to sfdata.start, the beginning of Ogg Vorbis data in Soundfont */ sfvio_seek(0, SEEK_SET, &sfdata); if (sfdata.offset != 0) { FLUID_LOG(FLUID_ERR, "Failed to seek to compressed sample position"); return -1; } FLUID_MEMSET(&sfinfo, 0, sizeof(sfinfo)); // Open sample as a virtual file sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, &sfdata); if(!sndfile) { FLUID_LOG(FLUID_ERR, "sf_open_virtual(): %s", sf_strerror(sndfile)); return -1; } // Empty sample if(sfinfo.frames <= 0 || sfinfo.channels <= 0) { FLUID_LOG(FLUID_DBG, "Empty decompressed sample"); *data = NULL; sf_close(sndfile); return 0; } // Mono sample if(sfinfo.channels != 1) { FLUID_LOG(FLUID_DBG, "Unsupported channel count %d in ogg sample", sfinfo.channels); goto error_exit; } if((sfinfo.format & SF_FORMAT_OGG) == 0) { FLUID_LOG(FLUID_WARN, "OGG sample is not OGG compressed, this is not officially supported"); } wav_data = FLUID_ARRAY(short, sfinfo.frames * sfinfo.channels); if(!wav_data) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_exit; } /* Automatically decompresses the Ogg Vorbis data to 16-bit PCM */ if(sf_readf_short(sndfile, wav_data, sfinfo.frames) < sfinfo.frames) { FLUID_LOG(FLUID_DBG, "Decompression failed!"); FLUID_LOG(FLUID_ERR, "sf_readf_short(): %s", sf_strerror(sndfile)); goto error_exit; } sf_close(sndfile); *data = wav_data; return sfinfo.frames; error_exit: FLUID_FREE(wav_data); sf_close(sndfile); return -1; } #else static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data) { return -1; } #endif fluidsynth-2.2.5/src/sfloader/fluid_sffile.h000066400000000000000000000141131417326347500211170ustar00rootroot00000000000000/* 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_SFFILE_H #define _FLUID_SFFILE_H #include "fluid_gen.h" #include "fluid_list.h" #include "fluid_mod.h" #include "fluidsynth.h" #include "fluid_sys.h" /* Sound Font structure defines */ /* Forward declarations */ typedef union _SFGenAmount SFGenAmount; typedef struct _SFVersion SFVersion; typedef struct _SFMod SFMod; typedef struct _SFGen SFGen; typedef struct _SFZone SFZone; typedef struct _SFSample SFSample; typedef struct _SFInst SFInst; typedef struct _SFPreset SFPreset; typedef struct _SFData SFData; typedef struct _SFChunk SFChunk; struct _SFVersion { /* version structure */ unsigned short major; unsigned short minor; }; 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 */ }; 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; }; struct _SFGen { /* Generator structure */ unsigned short id; /* generator ID */ SFGenAmount amount; /* generator value */ }; struct _SFZone { /* Sample/instrument zone structure */ fluid_list_t *gen; /* list of generators */ fluid_list_t *mod; /* list of modulators */ }; struct _SFSample { /* Sample structure */ char name[21]; /* Name of sample */ int idx; /* Index of this instrument in the Soundfont */ 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) */ }; struct _SFInst { /* Instrument structure */ char name[21]; /* Name of instrument */ int idx; /* Index of this instrument in the Soundfont */ fluid_list_t *zone; /* list of instrument zones */ }; struct _SFPreset { /* Preset structure */ char name[21]; /* preset name */ unsigned short prenum; /* preset number */ unsigned short bank; /* bank number */ fluid_list_t *zone; /* list of preset zones */ }; /* NOTE: sffd is also used to determine if sound font is new (NULL) */ struct _SFData { /* Sound font data structure */ SFVersion version; /* sound font version */ SFVersion romver; /* ROM version */ unsigned int filesize; unsigned int samplepos; /* position within sffd of the sample chunk */ unsigned int samplesize; /* length within sffd of the sample chunk */ unsigned int sample24pos; /* position within sffd of the sm24 chunk, set to zero if no 24 bit sample support */ unsigned int sample24size; /* length within sffd of the sm24 chunk */ unsigned int hydrapos; unsigned int hydrasize; char *fname; /* file name */ FILE *sffd; /* loaded sfont file descriptor */ const fluid_file_callbacks_t *fcbs; /* file callbacks used to read this file */ fluid_rec_mutex_t mtx; /* this mutex can be used to synchronize calls to fcbs when using multiple threads (e.g. SF3 loading) */ 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 */ }; /* functions */ /*-----------------------------------sffile.h----------------------------*/ /* File structures and routines (used to be in sffile.h) */ /* sfont file data structures */ struct _SFChunk { /* RIFF file chunk structure */ unsigned int id; /* chunk id */ unsigned int size; /* size of the following chunk */ }; /* Public functions */ SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs); void fluid_sffile_close(SFData *sf); int fluid_sffile_parse_presets(SFData *sf); int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, short **data, char **data24); /* extern only for unit test purposes */ int load_igen(SFData *sf, int size); int load_pgen(SFData *sf, int size); void delete_preset(SFPreset *preset); void delete_inst(SFInst *inst); void delete_zone(SFZone *zone); #endif /* _FLUID_SFFILE_H */ fluidsynth-2.2.5/src/sfloader/fluid_sfont.c000066400000000000000000000624651417326347500210100ustar00rootroot00000000000000/* 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_sfont.h" #include "fluid_sys.h" void *default_fopen(const char *path) { const char* msg; FILE* handle = fluid_file_open(path, &msg); if(handle == NULL) { FLUID_LOG(FLUID_ERR, "fluid_sfloader_load(): Failed to open '%s': %s", path, msg); } return handle; } int default_fclose(void *handle) { return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED; } fluid_long_long_t default_ftell(void *handle) { return FLUID_FTELL((FILE *)handle); } #ifdef WIN32 #define PRIi64 "%I64d" #else #define PRIi64 "%lld" #endif int safe_fread(void *buf, fluid_long_long_t count, void *fd) { if(FLUID_FREAD(buf, (size_t)count, 1, (FILE *)fd) != 1) { if(feof((FILE *)fd)) { FLUID_LOG(FLUID_ERR, "EOF while attempting to read " PRIi64 " bytes", count); } else { FLUID_LOG(FLUID_ERR, "File read failed"); } return FLUID_FAILED; } return FLUID_OK; } int safe_fseek(void *fd, fluid_long_long_t ofs, int whence) { if(FLUID_FSEEK((FILE *)fd, ofs, whence) != 0) { FLUID_LOG(FLUID_ERR, "File seek failed with offset = " PRIi64 " and whence = %d", ofs, whence); return FLUID_FAILED; } return FLUID_OK; } #undef PRIi64 /** * Creates a new SoundFont loader. * * @param load Pointer to a function that provides a #fluid_sfont_t (see #fluid_sfloader_load_t). * @param free Pointer to a function that destroys this instance (see #fluid_sfloader_free_t). * Unless any private data needs to be freed it is sufficient to set this to delete_fluid_sfloader(). * * @return the SoundFont loader instance on success, NULL otherwise. */ fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free) { fluid_sfloader_t *loader; fluid_return_val_if_fail(load != NULL, NULL); fluid_return_val_if_fail(free != NULL, NULL); loader = FLUID_NEW(fluid_sfloader_t); if(loader == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(loader, 0, sizeof(*loader)); loader->load = load; loader->free = free; fluid_sfloader_set_callbacks(loader, default_fopen, safe_fread, safe_fseek, default_ftell, default_fclose); return loader; } /** * Frees a SoundFont loader created with new_fluid_sfloader(). * * @param loader The SoundFont loader instance to free. */ void delete_fluid_sfloader(fluid_sfloader_t *loader) { fluid_return_if_fail(loader != NULL); FLUID_FREE(loader); } /** * Specify private data to be used by #fluid_sfloader_load_t. * * @param loader The SoundFont loader instance. * @param data The private data to store. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data) { fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); loader->data = data; return FLUID_OK; } /** * Obtain private data previously set with fluid_sfloader_set_data(). * * @param loader The SoundFont loader instance. * @return The private data or NULL if none explicitly set before. */ void *fluid_sfloader_get_data(fluid_sfloader_t *loader) { fluid_return_val_if_fail(loader != NULL, NULL); return loader->data; } /** * Set custom callbacks to be used upon soundfont loading. * * @param loader The SoundFont loader instance. * @param open A function implementing #fluid_sfloader_callback_open_t. * @param read A function implementing #fluid_sfloader_callback_read_t. * @param seek A function implementing #fluid_sfloader_callback_seek_t. * @param tell A function implementing #fluid_sfloader_callback_tell_t. * @param close A function implementing #fluid_sfloader_callback_close_t. * @return #FLUID_OK if the callbacks have been successfully set, #FLUID_FAILED otherwise. * * Useful for loading a soundfont from memory, see \a doc/fluidsynth_sfload_mem.c as an example. * */ int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, fluid_sfloader_callback_open_t open, fluid_sfloader_callback_read_t read, fluid_sfloader_callback_seek_t seek, fluid_sfloader_callback_tell_t tell, fluid_sfloader_callback_close_t close) { fluid_file_callbacks_t *cb; fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); fluid_return_val_if_fail(open != NULL, FLUID_FAILED); fluid_return_val_if_fail(read != NULL, FLUID_FAILED); fluid_return_val_if_fail(seek != NULL, FLUID_FAILED); fluid_return_val_if_fail(tell != NULL, FLUID_FAILED); fluid_return_val_if_fail(close != NULL, FLUID_FAILED); cb = &loader->file_callbacks; cb->fopen = open; cb->fread = read; cb->fseek = seek; cb->ftell = tell; cb->fclose = close; // NOTE: if we ever make the instpatch loader public, this may return FLUID_FAILED return FLUID_OK; } /** * Creates a new virtual SoundFont instance structure. * * @param get_name A function implementing #fluid_sfont_get_name_t. * @param get_preset A function implementing #fluid_sfont_get_preset_t. * @param iter_start A function implementing #fluid_sfont_iteration_start_t, or NULL if preset iteration not needed. * @param iter_next A function implementing #fluid_sfont_iteration_next_t, or NULL if preset iteration not needed. * @param free A function implementing #fluid_sfont_free_t. * @return The soundfont instance on success or NULL otherwise. */ fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, fluid_sfont_get_preset_t get_preset, fluid_sfont_iteration_start_t iter_start, fluid_sfont_iteration_next_t iter_next, fluid_sfont_free_t free) { fluid_sfont_t *sfont; fluid_return_val_if_fail(get_name != NULL, NULL); fluid_return_val_if_fail(get_preset != NULL, NULL); fluid_return_val_if_fail(free != NULL, NULL); sfont = FLUID_NEW(fluid_sfont_t); if(sfont == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(sfont, 0, sizeof(*sfont)); sfont->get_name = get_name; sfont->get_preset = get_preset; sfont->iteration_start = iter_start; sfont->iteration_next = iter_next; sfont->free = free; return sfont; } /** * Set private data to use with a SoundFont instance. * * @param sfont The SoundFont instance. * @param data The private data to store. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data) { fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); sfont->data = data; return FLUID_OK; } /** * Retrieve the private data of a SoundFont instance. * * @param sfont The SoundFont instance. * @return The private data or NULL if none explicitly set before. */ void *fluid_sfont_get_data(fluid_sfont_t *sfont) { fluid_return_val_if_fail(sfont != NULL, NULL); return sfont->data; } /** * Retrieve the unique ID of a SoundFont instance. * * @param sfont The SoundFont instance. * @return The SoundFont ID. */ int fluid_sfont_get_id(fluid_sfont_t *sfont) { return sfont->id; } /** * Retrieve the name of a SoundFont instance. * * @param sfont The SoundFont instance. * @return The name of the SoundFont. */ const char *fluid_sfont_get_name(fluid_sfont_t *sfont) { return sfont->get_name(sfont); } /** * Retrieve the preset assigned the a SoundFont instance for the given bank and preset number. * * @param sfont The SoundFont instance. * @param bank bank number of the preset * @param prenum program number of the preset * @return The preset instance or NULL if none found. */ fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) { return sfont->get_preset(sfont, bank, prenum); } /** * Starts / re-starts virtual preset iteration in a SoundFont. * * @param sfont Virtual SoundFont instance */ void fluid_sfont_iteration_start(fluid_sfont_t *sfont) { fluid_return_if_fail(sfont != NULL); fluid_return_if_fail(sfont->iteration_start != NULL); sfont->iteration_start(sfont); } /** * Virtual SoundFont preset iteration function. * * Returns preset information to the caller and advances the * internal iteration state to the next preset for subsequent calls. * @param sfont The SoundFont instance. * @return NULL when no more presets are available, otherwise the a pointer to the current preset */ fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont) { fluid_return_val_if_fail(sfont != NULL, NULL); fluid_return_val_if_fail(sfont->iteration_next != NULL, NULL); return sfont->iteration_next(sfont); } /** * Destroys a SoundFont instance created with new_fluid_sfont(). * * @param sfont The SoundFont instance to destroy. * @return Always returns 0. * * Implements #fluid_sfont_free_t. * */ int delete_fluid_sfont(fluid_sfont_t *sfont) { fluid_return_val_if_fail(sfont != NULL, 0); FLUID_FREE(sfont); return 0; } /** * Create a virtual SoundFont preset instance. * * @param parent_sfont The SoundFont instance this preset shall belong to * @param get_name A function implementing #fluid_preset_get_name_t * @param get_bank A function implementing #fluid_preset_get_banknum_t * @param get_num A function implementing #fluid_preset_get_num_t * @param noteon A function implementing #fluid_preset_noteon_t * @param free A function implementing #fluid_preset_free_t * @return The preset instance on success, NULL otherwise. */ fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont, fluid_preset_get_name_t get_name, fluid_preset_get_banknum_t get_bank, fluid_preset_get_num_t get_num, fluid_preset_noteon_t noteon, fluid_preset_free_t free) { fluid_preset_t *preset; fluid_return_val_if_fail(parent_sfont != NULL, NULL); fluid_return_val_if_fail(get_name != NULL, NULL); fluid_return_val_if_fail(get_bank != NULL, NULL); fluid_return_val_if_fail(get_num != NULL, NULL); fluid_return_val_if_fail(noteon != NULL, NULL); fluid_return_val_if_fail(free != NULL, NULL); preset = FLUID_NEW(fluid_preset_t); if(preset == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(preset, 0, sizeof(*preset)); preset->sfont = parent_sfont; preset->get_name = get_name; preset->get_banknum = get_bank; preset->get_num = get_num; preset->noteon = noteon; preset->free = free; return preset; } /** * Set private data to use with a SoundFont preset instance. * * @param preset The SoundFont preset instance. * @param data The private data to store. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_preset_set_data(fluid_preset_t *preset, void *data) { fluid_return_val_if_fail(preset != NULL, FLUID_FAILED); preset->data = data; return FLUID_OK; } /** * Retrieve the private data of a SoundFont preset instance. * * @param preset The SoundFont preset instance. * @return The private data or NULL if none explicitly set before. */ void *fluid_preset_get_data(fluid_preset_t *preset) { fluid_return_val_if_fail(preset != NULL, NULL); return preset->data; } /** * Retrieves the presets name by executing the \p get_name function * provided on its creation. * * @param preset The SoundFont preset instance. * @return Pointer to a NULL-terminated string containing the presets name. */ const char *fluid_preset_get_name(fluid_preset_t *preset) { return preset->get_name(preset); } /** * Retrieves the presets bank number by executing the \p get_bank function * provided on its creation. * * @param preset The SoundFont preset instance. * @return The bank number of \p preset. */ int fluid_preset_get_banknum(fluid_preset_t *preset) { return preset->get_banknum(preset); } /** * Retrieves the presets (instrument) number by executing the \p get_num function * provided on its creation. * * @param preset The SoundFont preset instance. * @return The number of \p preset. */ int fluid_preset_get_num(fluid_preset_t *preset) { return preset->get_num(preset); } /** * Retrieves the presets parent SoundFont instance. * * @param preset The SoundFont preset instance. * @return The parent SoundFont of \p preset. */ fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset) { return preset->sfont; } /** * Destroys a SoundFont preset instance created with new_fluid_preset(). * * @param preset The SoundFont preset instance to destroy. * * Implements #fluid_preset_free_t. * */ void delete_fluid_preset(fluid_preset_t *preset) { fluid_return_if_fail(preset != NULL); FLUID_FREE(preset); } /** * Create a new sample instance. * * @return The sample on success, NULL otherwise. */ 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; } FLUID_MEMSET(sample, 0, sizeof(*sample)); return sample; } /** * Destroy a sample instance previously created with new_fluid_sample(). * * @param sample The sample to destroy. */ void delete_fluid_sample(fluid_sample_t *sample) { fluid_return_if_fail(sample != NULL); if(sample->auto_free) { FLUID_FREE(sample->data); FLUID_FREE(sample->data24); } FLUID_FREE(sample); } /** * Returns the size of the fluid_sample_t structure. * * @return Size of fluid_sample_t in bytes * * Useful in low latency scenarios e.g. to allocate a pool of samples. * * @note It is recommend to zero initialize the memory before using the object. * * @warning Do NOT allocate samples on the stack and assign them to a voice! */ size_t fluid_sample_sizeof() { return sizeof(fluid_sample_t); } /** * Set the name of a SoundFont sample. * * @param sample SoundFont sample * @param name Name to assign to sample (20 chars in length + zero terminator) * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_sample_set_name(fluid_sample_t *sample, const char *name) { fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); FLUID_STRNCPY(sample->name, name, sizeof(sample->name)); return FLUID_OK; } /** * Assign sample data to a SoundFont sample. * * @param sample SoundFont sample * @param data Buffer containing 16 bit (mono-)audio sample data * @param data24 If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples * @param nbframes Number of samples in \a data * @param sample_rate Sampling rate of the sample data * @param copy_data TRUE to copy the sample data (and automatically free it upon delete_fluid_sample()), FALSE to use it directly (and not free it) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * @note If \a copy_data is FALSE, data should have 8 unused frames at start * and 8 unused frames at the end and \a nbframes should be >=48 */ int fluid_sample_set_sound_data(fluid_sample_t *sample, short *data, char *data24, unsigned int nbframes, unsigned int sample_rate, short copy_data ) { /* the number of samples before the start and after the end */ #define SAMPLE_LOOP_MARGIN 8U fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); fluid_return_val_if_fail(data != NULL, FLUID_FAILED); fluid_return_val_if_fail(nbframes != 0, FLUID_FAILED); /* in case we already have some data */ if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free) { FLUID_FREE(sample->data); FLUID_FREE(sample->data24); } sample->data = NULL; sample->data24 = NULL; if(copy_data) { unsigned int storedNbFrames; /* nbframes should be >= 48 (SoundFont specs) */ storedNbFrames = nbframes; if(storedNbFrames < 48) { storedNbFrames = 48; } storedNbFrames += 2 * SAMPLE_LOOP_MARGIN; sample->data = FLUID_ARRAY(short, storedNbFrames); if(sample->data == NULL) { goto error_rec; } FLUID_MEMSET(sample->data, 0, storedNbFrames * sizeof(short)); FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short)); if(data24 != NULL) { sample->data24 = FLUID_ARRAY(char, storedNbFrames); if(sample->data24 == NULL) { goto error_rec; } FLUID_MEMSET(sample->data24, 0, storedNbFrames); FLUID_MEMCPY(sample->data24 + SAMPLE_LOOP_MARGIN, data24, nbframes * sizeof(char)); } /* pointers */ /* all from the start of data */ sample->start = SAMPLE_LOOP_MARGIN; sample->end = SAMPLE_LOOP_MARGIN + nbframes - 1; } else { /* we cannot assure the SAMPLE_LOOP_MARGIN */ sample->data = data; sample->data24 = data24; sample->start = 0; sample->end = nbframes - 1; } sample->samplerate = sample_rate; sample->sampletype = FLUID_SAMPLETYPE_MONO; sample->auto_free = copy_data; return FLUID_OK; error_rec: FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_FREE(sample->data); FLUID_FREE(sample->data24); sample->data = NULL; sample->data24 = NULL; return FLUID_FAILED; #undef SAMPLE_LOOP_MARGIN } /** * Set the loop of a sample. * * @param sample SoundFont sample * @param loop_start Start sample index of the loop. * @param loop_end End index of the loop (must be a valid sample as it marks the last sample to be played). * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end) { fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); sample->loopstart = loop_start; sample->loopend = loop_end; return FLUID_OK; } /** * Set the pitch of a sample. * * @param sample SoundFont sample * @param root_key Root MIDI note of sample (0-127) * @param fine_tune Fine tune in cents * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune) { fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); fluid_return_val_if_fail(0 <= root_key && root_key <= 127, FLUID_FAILED); sample->origpitch = root_key; sample->pitchadj = fine_tune; return FLUID_OK; } /** * Validate parameters of a sample * */ int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size) { #define EXCLUSIVE_FLAGS (FLUID_SAMPLETYPE_MONO | FLUID_SAMPLETYPE_RIGHT | FLUID_SAMPLETYPE_LEFT) static const unsigned int supported_flags = EXCLUSIVE_FLAGS | FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_OGG_VORBIS | FLUID_SAMPLETYPE_ROM; /* ROM samples are unusable for us by definition */ if(sample->sampletype & FLUID_SAMPLETYPE_ROM) { FLUID_LOG(FLUID_WARN, "Sample '%s': ROM sample ignored", sample->name); return FLUID_FAILED; } if(sample->sampletype & ~supported_flags) { FLUID_LOG(FLUID_WARN, "Sample '%s' has unknown flags, possibly using an unsupported compression; sample ignored", sample->name); return FLUID_FAILED; } if((sample->sampletype & EXCLUSIVE_FLAGS) & ((sample->sampletype & EXCLUSIVE_FLAGS) - 1)) { FLUID_LOG(FLUID_INFO, "Sample '%s' should be either mono or left or right; using it anyway", sample->name); } if((sample->sampletype & FLUID_SAMPLETYPE_LINKED) && (sample->sampletype & EXCLUSIVE_FLAGS)) { FLUID_LOG(FLUID_INFO, "Linked sample '%s' should not be mono, left or right at the same time; using it anyway", sample->name); } if((sample->sampletype & EXCLUSIVE_FLAGS) == 0) { FLUID_LOG(FLUID_INFO, "Sample '%s' has no flags set, assuming mono", sample->name); sample->sampletype = FLUID_SAMPLETYPE_MONO; } /* Ogg vorbis compressed samples in the SF3 format use byte indices for * sample start and end pointers before decompression. Standard SF2 samples * use sample word indices for all pointers, so use half the buffer_size * for validation. */ if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) { if(buffer_size % 2) { FLUID_LOG(FLUID_WARN, "Sample '%s': invalid buffer size", sample->name); return FLUID_FAILED; } buffer_size /= 2; } if((sample->end > buffer_size) || (sample->start >= sample->end)) { FLUID_LOG(FLUID_WARN, "Sample '%s': invalid start/end file positions", sample->name); return FLUID_FAILED; } return FLUID_OK; #undef EXCLUSIVE_FLAGS } /* Check the sample loop pointers and optionally convert them to something * usable in case they are broken. Return a boolean indicating if the pointers * have been modified, so the user can be notified of possible audio glitches. */ int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size) { int modified = FALSE; unsigned int max_end = buffer_size / 2; /* In fluid_sample_t the sample end pointer points to the last sample, not * to the data word after the last sample. FIXME: why? */ unsigned int sample_end = sample->end + 1; if(sample->loopstart == sample->loopend) { /* Some SoundFonts disable loops by setting loopstart = loopend. While * technically invalid, we decided to accept those samples anyway. * Before fluidsynth 2.2.5 we've set those indices to zero, as this * change was believed to be inaudible. This turned out to be an * incorrect assumption, as the loop points may still be modified by * loop offset modulators afterwards. */ if(sample->loopstart != sample->start) { // Many soundfonts set loopstart == loopend == sample->start to disabled to loop. // Only report cases where it's not equal to the sample->start, to avoid spam. FLUID_LOG(FLUID_DBG, "Sample '%s': zero length loop detected: loopstart == loopend == '%d', sample start '%d', using it anyway", sample->name, sample->loopstart, sample->start); } } else if(sample->loopstart > sample->loopend) { unsigned int tmp; /* If loop start and end are reversed, try to swap them around and * continue validation */ FLUID_LOG(FLUID_DBG, "Sample '%s': reversed loop pointers '%d' - '%d', trying to fix", sample->name, sample->loopstart, sample->loopend); tmp = sample->loopstart; sample->loopstart = sample->loopend; sample->loopend = tmp; modified = TRUE; } /* The SoundFont 2.4 spec defines the loopstart index as the first sample * point of the loop while loopend is the first point AFTER the last sample * of the loop. However we cannot be sure whether any of loopend or 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 the pointers are within sample->start to max_end. Incorrect * soundfont shall preferably fail loudly. */ if((sample->loopstart < sample->start) || (sample->loopstart > max_end)) { FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop start '%d', setting to sample start '%d'", sample->name, sample->loopstart, sample->start); sample->loopstart = sample->start; modified = TRUE; } if((sample->loopend < sample->start) || (sample->loopend > max_end)) { FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop end '%d', setting to sample end '%d'", sample->name, sample->loopend, sample_end); sample->loopend = sample_end; modified = TRUE; } if((sample->loopstart > sample_end) || (sample->loopend > sample_end)) { FLUID_LOG(FLUID_DBG, "Sample '%s': loop range '%d - %d' after sample end '%d', using it anyway", sample->name, sample->loopstart, sample->loopend, sample_end); } return modified; } fluidsynth-2.2.5/src/sfloader/fluid_sfont.h000066400000000000000000000156231417326347500210070ustar00rootroot00000000000000/* 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 #include "fluidsynth.h" int fluid_sample_validate(fluid_sample_t *sample, unsigned int max_end); int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int max_end); /* * 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 fluid_sfont_delete_internal(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0) #define fluid_preset_delete_internal(_preset) \ { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }} #define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \ (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel) #define fluid_preset_notify(_preset,_reason,_chan) \ ( ((_preset) && (_preset)->notify) ? (*(_preset)->notify)(_preset,_reason,_chan) : FLUID_OK ) #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); /** * File callback structure to enable custom soundfont loading (e.g. from memory). */ struct _fluid_file_callbacks_t { fluid_sfloader_callback_open_t fopen; fluid_sfloader_callback_read_t fread; fluid_sfloader_callback_seek_t fseek; fluid_sfloader_callback_close_t fclose; fluid_sfloader_callback_tell_t ftell; }; /** * SoundFont loader structure. */ struct _fluid_sfloader_t { void *data; /**< User defined data pointer used by _fluid_sfloader_t::load() */ /** Callback structure specifying file operations used during soundfont loading to allow custom loading, such as from memory */ fluid_file_callbacks_t file_callbacks; fluid_sfloader_free_t free; fluid_sfloader_load_t load; }; /** * Virtual SoundFont instance structure. */ struct _fluid_sfont_t { void *data; /**< User defined data */ int id; /**< SoundFont ID */ int refcount; /**< SoundFont reference count (1 if no presets referencing it) */ int bankofs; /**< Bank offset */ fluid_sfont_free_t free; fluid_sfont_get_name_t get_name; fluid_sfont_get_preset_t get_preset; fluid_sfont_iteration_start_t iteration_start; fluid_sfont_iteration_next_t iteration_next; }; /** * Virtual SoundFont preset. */ struct _fluid_preset_t { void *data; /**< User supplied data */ fluid_sfont_t *sfont; /**< Parent virtual SoundFont */ fluid_preset_free_t free; fluid_preset_get_name_t get_name; fluid_preset_get_banknum_t get_banknum; fluid_preset_get_num_t get_num; fluid_preset_noteon_t noteon; /** * 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 */ /* The following four sample pointers store the original pointers from the Soundfont * file. They are never changed after loading and are used to re-create the * actual sample pointers after a sample has been unloaded and loaded again. The * actual sample pointers get modified during loading for SF3 (compressed) samples * and individually loaded SF2 samples. */ unsigned int source_start; unsigned int source_end; unsigned int source_loopstart; unsigned int source_loopend; 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; /**< Specifies the type of this sample as indicated by the #fluid_sample_type enum */ int auto_free; /**< TRUE if _fluid_sample_t::data and _fluid_sample_t::data24 should be freed upon sample destruction */ short *data; /**< Pointer to the sample's 16 bit PCM data */ char *data24; /**< If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples */ 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 */ int preset_count; /**< Count of selected presets using this sample (used for dynamic sample loading) */ /** * 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); }; #endif /* _PRIV_FLUID_SFONT_H */ fluidsynth-2.2.5/src/synth/000077500000000000000000000000001417326347500156615ustar00rootroot00000000000000fluidsynth-2.2.5/src/synth/fluid_chan.c000066400000000000000000000552661417326347500201370ustar00rootroot00000000000000/* 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 i, prognum, banknum; chan->sostenuto_orderid = 0; /*--- Init poly/mono modes variables --------------------------------------*/ chan->mode = 0; chan->mode_val = 0; /* monophonic list initialization */ for(i = 0; i < FLUID_CHANNEL_SIZE_MONOLIST; i++) { chan->monolist[i].next = i + 1; } chan->monolist[FLUID_CHANNEL_SIZE_MONOLIST - 1].next = 0; /* ending element chained to the 1st */ chan->i_last = chan->n_notes = 0; /* clears the list */ chan->i_first = chan->monolist[chan->i_last].next; /* first note index in the list */ fluid_channel_clear_prev_note(chan); /* Mark previous note invalid */ /*---*/ chan->key_mono_sustained = INVALID_NOTE; /* No previous mono note sustained */ chan->legatomode = FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER; /* Default mode */ chan->portamentomode = FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY; /* Default mode */ /*--- End of poly/mono initialization --------------------------------------*/ 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 https://www.midi.org/techspecs/rp15.php */ void fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off) { int i; 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; } 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 || i == BALANCE_MSB || i == BALANCE_LSB ) { continue; } fluid_channel_set_cc(chan, i, 0); } } else { for(i = 0; i < 128; i++) { fluid_channel_set_cc(chan, i, 0); } fluid_channel_clear_portamento(chan); /* Clear PTC receive */ chan->previous_cc_breath = 0;/* Reset previous breath */ } /* Reset polyphonic key pressure on all voices */ for(i = 0; i < 128; i++) { fluid_channel_set_key_pressure(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); /* Balance (MSB & LSB) */ fluid_channel_set_cc(chan, BALANCE_MSB, 64); fluid_channel_set_cc(chan, BALANCE_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 https://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 */ void delete_fluid_channel(fluid_channel_t *chan) { fluid_return_if_fail(chan != NULL); FLUID_FREE(chan); } 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_sfont_t *sfont; if(chan->preset == preset) { return FLUID_OK; } if(chan->preset) { sfont = chan->preset->sfont; sfont->refcount--; } fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); chan->preset = preset; if(preset) { sfont = preset->sfont; sfont->refcount++; } 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: https://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; } } /** * Compute the pitch for a key after applying Fluidsynth's tuning functionality * and channel coarse/fine tunings. * @param chan fluid_channel_t * @param key MIDI note number (0-127) * @return the pitch of the key */ fluid_real_t fluid_channel_get_key_pitch(fluid_channel_t *chan, int key) { if(chan->tuning) { return fluid_tuning_get_pitch(chan->tuning, key) + 100.0f * fluid_channel_get_gen(chan, GEN_COARSETUNE) + fluid_channel_get_gen(chan, GEN_FINETUNE); } else { return key * 100.0f; } } /** * Updates legato/ staccato playing state * The function is called: * - on noteon before adding a note into the monolist. * - on noteoff after removing a note out of the monolist. * @param chan fluid_channel_t. */ static void fluid_channel_update_legato_staccato_state(fluid_channel_t *chan) { /* Updates legato/ staccato playing state */ if(chan->n_notes) { chan->mode |= FLUID_CHANNEL_LEGATO_PLAYING; /* Legato state */ } else { chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ } } /** * Adds a note into the monophonic list. The function is part of the legato * detector. fluid_channel_add_monolist() is intended to be called by * fluid_synth_noteon_mono_LOCAL(). * * When a note is added at noteOn each element is use in the forward direction * and indexed by i_last variable. * * @param chan fluid_channel_t. * @param key MIDI note number (0-127). * @param vel MIDI velocity (0-127, 0=noteoff). * @param onenote. When 1 the function adds the note but the monophonic list * keeps only one note (used on noteOn poly). * Note: i_last index keeps a trace of the most recent note added. * prev_note keeps a trace of the note prior i_last note. * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing state. * * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). */ void fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel, unsigned char onenote) { unsigned char i_last = chan->i_last; /* Updates legato/ staccato playing state */ fluid_channel_update_legato_staccato_state(chan); if(chan->n_notes) { /* keeps trace of the note prior last note */ chan->prev_note = chan->monolist[i_last].note; } /* moves i_last forward before writing new note */ i_last = chan->monolist[i_last].next; chan->i_last = i_last; /* now ilast indexes the last note */ chan->monolist[i_last].note = key; /* we save note and velocity */ chan->monolist[i_last].vel = vel; if(onenote) { /* clears monolist before one note addition */ chan->i_first = i_last; chan->n_notes = 0; } if(chan->n_notes < FLUID_CHANNEL_SIZE_MONOLIST) { chan->n_notes++; /* updates n_notes */ } else { /* The end of buffer is reach. So circular motion for i_first */ /* i_first index is moved forward */ chan->i_first = chan->monolist[i_last].next; } } /** * Searching a note in the monophonic list. The function is part of the legato * detector. fluid_channel_search_monolist() is intended to be called by * fluid_synth_noteoff_mono_LOCAL(). * * The search starts from the first note in the list indexed by i_first * @param chan fluid_channel_t. * @param key MIDI note number (0-127) to search. * @param i_prev pointer on returned index of the note prior the note to search. * @return index of the note if find, FLUID_FAILED otherwise. * */ int fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev) { short n = chan->n_notes; /* number of notes in monophonic list */ short j, i = chan->i_first; /* searching starts from i_first included */ for(j = 0 ; j < n ; j++) { if(chan->monolist[i].note == key) { if(i == chan->i_first) { /* tracking index of the previous note (i_prev) */ for(j = chan->i_last ; n < FLUID_CHANNEL_SIZE_MONOLIST; n++) { j = chan->monolist[j].next; } * i_prev = j; /* returns index of the previous note */ } return i; /* returns index of the note to search */ } * i_prev = i; /* tracking index of the previous note (i_prev) */ i = chan->monolist[i].next; /* next element */ } return FLUID_FAILED; /* not found */ } /** * removes a note from the monophonic list. The function is part of * the legato detector. * fluid_channel_remove_monolist() is intended to be called by * fluid_synth_noteoff_mono_LOCAL(). * * When a note is removed at noteOff the element concerned is fast unlinked * and relinked after the i_last element. * * @param chan fluid_channel_t. * @param * i, index of the note to remove. If i is invalid or the list is * empty, the function do nothing and returns FLUID_FAILED. * @param * On input, i_prev is a pointer on index of the note previous i. * On output i_prev is a pointer on index of the note previous i if i is the last note * in the list,FLUID_FAILED otherwise. When the returned index is valid it means * a legato detection on noteoff. * * Note: the following variables in Channel keeps trace of the situation. * - i_last index keeps a trace of the most recent note played even if * the list is empty. * - prev_note keeps a trace of the note removed if it is i_last. * - FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing state. * * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). */ void fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev) { unsigned char i_last = chan->i_last; /* checks if index is valid */ if(i < 0 || i >= FLUID_CHANNEL_SIZE_MONOLIST || !chan->n_notes) { * i_prev = FLUID_FAILED; } /* The element is about to be removed and inserted between i_last and next */ /* Note: when i is egal to i_last or egal to i_first, removing/inserting isn't necessary */ if(i == i_last) { /* Removing/Inserting isn't necessary */ /* keeps trace of the note prior last note */ chan->prev_note = chan->monolist[i_last].note; /* moves i_last backward to the previous */ chan->i_last = *i_prev; /* i_last index is moved backward */ } else { /* i is before i_last */ if(i == chan->i_first) { /* Removing/inserting isn't necessary */ /* i_first index is moved forward to the next element*/ chan->i_first = chan->monolist[i].next; } else { /* i is between i_first and i_last */ /* Unlinks element i and inserts after i_last */ chan->monolist[* i_prev].next = chan->monolist[i].next; /* unlinks i */ /*inserts i after i_last */ chan->monolist[i].next = chan->monolist[i_last].next; chan->monolist[i_last].next = i; } * i_prev = FLUID_FAILED; } chan->n_notes--; /* updates the number of note in the list */ /* Updates legato/ staccato playing state */ fluid_channel_update_legato_staccato_state(chan); } /** * On noteOff on a polyphonic channel,the monophonic list is fully flushed. * * @param chan fluid_channel_t. * Note: i_last index keeps a trace of the most recent note played even if * the list is empty. * prev_note keeps a trace of the note i_last . * FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing. */ void fluid_channel_clear_monolist(fluid_channel_t *chan) { /* keeps trace off the most recent note played */ chan->prev_note = chan->monolist[chan->i_last].note; /* flushes the monolist */ chan->i_first = chan->monolist[chan->i_last].next; chan->n_notes = 0; /* Update legato/ sataccato playing state */ chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ } /** * On noteOn on a polyphonic channel,adds the note into the monophonic list * keeping only this note. * @param * chan fluid_channel_t. * key, vel, note and velocity added in the monolist * Note: i_last index keeps a trace of the most recent note inserted. * prev_note keeps a trace of the note prior i_last note. * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. */ void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel) { fluid_channel_add_monolist(chan, key, vel, 1); } /** * The function changes the state (Valid/Invalid) of the previous note played in * a staccato manner (fluid_channel_prev_note()). * When potamento mode 'each note' or 'staccato only' is selected, on next * noteOn a portamento will be started from the most recent note played * staccato. * It will be possible that it isn't appropriate. To give the musician the * possibility to choose a portamento from this note , prev_note will be forced * to invalid state on noteOff if portamento pedal is Off. * * The function is intended to be called when the following event occurs: * - On noteOff (in poly or mono mode), to mark prev_note invalid. * - On Portamento Off(in poly or mono mode), to mark prev_note invalid. * @param chan fluid_channel_t. */ void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan) { /* checks if the playing is staccato */ if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) { /* checks if portamento pedal is off */ if(! fluid_channel_portamento(chan)) { /* forces prev_note invalid */ fluid_channel_clear_prev_note(chan); } } /* else prev_note still remains valid for next fromkey portamento */ } /** * The function handles poly/mono commutation on legato pedal On/Off. * @param chan fluid_channel_t. * @param value, value of the CC legato. */ void fluid_channel_cc_legato(fluid_channel_t *chan, int value) { /* Special handling of the monophonic list */ if(!(chan->mode & FLUID_CHANNEL_POLY_OFF) && chan->n_notes) /* The monophonic list have notes */ { if(value < 64) /* legato is released */ { /* returns from monophonic to polyphonic with notes in the monophonic list */ /* The monophonic list is flushed keeping last note only Note: i_last index keeps a trace of the most recent note played. prev_note keeps a trace of the note i_last. FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. */ chan->i_first = chan->i_last; chan->n_notes = 1; } else /* legato is depressed */ { /* Inters in monophonic from polyphonic with note in monophonic list */ /* Stops the running note to remain coherent with Breath Sync mode */ if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && !fluid_channel_breath_msb(chan)) { fluid_synth_noteoff_monopoly(chan->synth, chan->channum, fluid_channel_last_note(chan), 1); } } } } /** * The function handles CC Breath On/Off detection. When a channel is in * Breath Sync mode and in monophonic playing, the breath controller allows * to trigger noteon/noteoff note when the musician starts to breath (noteon) and * stops to breath (noteoff). * @param chan fluid_channel_t. * @param value, value of the CC Breath.. */ void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value) { if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && fluid_channel_is_playing_mono(chan) && (chan->n_notes)) { /* The monophonic list isn't empty */ if((value > 0) && (chan->previous_cc_breath == 0)) { /* CC Breath On detection */ fluid_synth_noteon_mono_staccato(chan->synth, chan->channum, fluid_channel_last_note(chan), fluid_channel_last_vel(chan)); } else if((value == 0) && (chan->previous_cc_breath > 0)) { /* CC Breath Off detection */ fluid_synth_noteoff_monopoly(chan->synth, chan->channum, fluid_channel_last_note(chan), 1); } } chan->previous_cc_breath = value; } fluidsynth-2.2.5/src/synth/fluid_chan.h000066400000000000000000000322321417326347500201300ustar00rootroot00000000000000/* 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" /* The mononophonic list is part of the legato detector for monophonic mode */ /* see fluid_synth_monopoly.c about a description of the legato detector device */ /* Size of the monophonic list - 1 is the minimum. it allows playing legato passage of any number of notes on noteon only. - Size above 1 allows playing legato on noteon but also on noteOff. This allows the musician to play fast trills. This feature is particularly usful when the MIDI input device is a keyboard. Choosing a size of 10 is sufficient (because most musicians have only 10 fingers when playing a monophonic instrument). */ #define FLUID_CHANNEL_SIZE_MONOLIST 10 /* The monophonic list +------------------------------------------------+ | +----+ +----+ +----+ +----+ | | |note| |note| |note| |note| | +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ +----+ +----+ +----+ +----+ /|\ /|\ | | i_first i_last The monophonic list is a circular buffer of FLUID_CHANNEL_SIZE_MONOLIST elements. Each element is linked forward at initialisation time. - when a note is added at noteOn (see fluid_channel_add_monolist()) each element is use in the forward direction and indexed by i_last variable. - when a note is removed at noteOff (see fluid_channel_remove_monolist()), the element concerned is fast unlinked and relinked after the i_last element. The most recent note added is indexed by i_last. The most ancient note added is the first note indexed by i_first. i_first is moving in the forward direction in a circular manner. */ struct mononote { unsigned char next; /* next note */ unsigned char note; /* note */ unsigned char vel; /* velocity */ }; /* * 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_synth_t *synth; /**< Parent synthesizer instance */ int channum; /**< MIDI channel number */ /* Poly Mono variables see macro access description */ int mode; /**< Poly Mono mode */ int mode_val; /**< number of channel in basic channel group */ /* monophonic list - legato detector */ unsigned char i_first; /**< First note index */ unsigned char i_last; /**< most recent note index since the most recent add */ unsigned char prev_note; /**< previous note of the most recent add/remove */ unsigned char n_notes; /**< actual number of notes in the list */ struct mononote monolist[FLUID_CHANNEL_SIZE_MONOLIST]; /**< monophonic list */ unsigned char key_mono_sustained; /**< previous sustained monophonic note */ unsigned char previous_cc_breath; /**< Previous Breath */ enum fluid_channel_legato_mode legatomode; /**< legato mode */ enum fluid_channel_portamento_mode portamentomode; /**< portamento mode */ /*- End of Poly/mono variables description */ unsigned char cc[128]; /**< MIDI controller values from [0;127] */ unsigned char key_pressure[128]; /**< MIDI polyphonic key pressure from [0;127] */ /* Drum channel flag, CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM. */ enum fluid_midi_channel_type channel_type; enum fluid_interp interp_method; /**< Interpolation method (enum fluid_interp) */ unsigned char channel_pressure; /**< MIDI channel pressure from [0;127] */ unsigned char pitch_wheel_sensitivity; /**< Current pitch wheel sensitivity */ short pitch_bend; /**< Current pitch bend value */ /* 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 tuning_bank; /**< Current tuning bank number */ int tuning_prog; /**< Current tuning program number */ fluid_tuning_t *tuning; /**< Micro tuning */ fluid_preset_t *preset; /**< Selected preset */ int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ /* NRPN system */ enum fluid_gen_type nrpn_select; /* Generator ID of SoundFont NRPN message */ char 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]; }; 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); void 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); 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); fluid_real_t fluid_channel_get_key_pitch(fluid_channel_t *chan, int key); #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, key) \ ((chan)->key_pressure[key]) #define fluid_channel_set_key_pressure(chan, key, val) \ ((chan)->key_pressure[key] = (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_portamentotime(_c) \ ((_c)->cc[PORTAMENTO_TIME_MSB] * 128 + (_c)->cc[PORTAMENTO_TIME_LSB]) #define fluid_channel_portamento(_c) ((_c)->cc[PORTAMENTO_SWITCH] >= 64) #define fluid_channel_breath_msb(_c) ((_c)->cc[BREATH_MSB] > 0) #define fluid_channel_clear_portamento(_c) ((_c)->cc[PORTAMENTO_CTRL] = INVALID_NOTE) #define fluid_channel_legato(_c) ((_c)->cc[LEGATO_SWITCH] >= 64) #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) { (_c)->gen[_n] = _v; } #define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n]) #define fluid_channel_get_min_note_length_ticks(chan) \ ((chan)->synth->min_note_length_ticks) /* Macros interface to poly/mono mode variables */ #define MASK_BASICCHANINFOS (FLUID_CHANNEL_MODE_MASK|FLUID_CHANNEL_BASIC|FLUID_CHANNEL_ENABLED) /* Set the basic channel infos for a MIDI basic channel */ #define fluid_channel_set_basic_channel_info(chan,Infos) \ (chan->mode = (chan->mode & ~MASK_BASICCHANINFOS) | (Infos & MASK_BASICCHANINFOS)) /* Reset the basic channel infos for a MIDI basic channel */ #define fluid_channel_reset_basic_channel_info(chan) (chan->mode &= ~MASK_BASICCHANINFOS) /* Macros interface to breath variables */ #define FLUID_CHANNEL_BREATH_MASK (FLUID_CHANNEL_BREATH_POLY|FLUID_CHANNEL_BREATH_MONO|FLUID_CHANNEL_BREATH_SYNC) /* Set the breath infos for a MIDI channel */ #define fluid_channel_set_breath_info(chan,BreathInfos) \ (chan->mode = (chan->mode & ~FLUID_CHANNEL_BREATH_MASK) | (BreathInfos & FLUID_CHANNEL_BREATH_MASK)) /* Get the breath infos for a MIDI channel */ #define fluid_channel_get_breath_info(chan) (chan->mode & FLUID_CHANNEL_BREATH_MASK) /* Returns true when channel is mono or legato is on */ #define fluid_channel_is_playing_mono(chan) ((chan->mode & FLUID_CHANNEL_POLY_OFF) ||\ fluid_channel_legato(chan)) /* Macros interface to monophonic list variables */ #define INVALID_NOTE (255) /* Returns true when a note is a valid note */ #define fluid_channel_is_valid_note(n) (n != INVALID_NOTE) /* Marks prev_note as invalid. */ #define fluid_channel_clear_prev_note(chan) (chan->prev_note = INVALID_NOTE) /* Returns the most recent note from i_last entry of the monophonic list */ #define fluid_channel_last_note(chan) (chan->monolist[chan->i_last].note) /* Returns the most recent velocity from i_last entry of the monophonic list */ #define fluid_channel_last_vel(chan) (chan->monolist[chan->i_last].vel) /* prev_note is used to determine fromkey_portamento as well as fromkey_legato (see fluid_synth_get_fromkey_portamento_legato()). prev_note is updated on noteOn/noteOff mono by the legato detector as this: - On noteOn mono, before adding a new note into the monolist,the most recent note in the list (i.e at i_last position) is kept in prev_note. - Similarly, on noteOff mono , before removing a note out of the monolist, the most recent note (i.e those at i_last position) is kept in prev_note. */ #define fluid_channel_prev_note(chan) (chan->prev_note) /* Interface to poly/mono mode variables */ enum fluid_channel_mode_flags_internal { FLUID_CHANNEL_BASIC = 0x04, /**< if flag set the corresponding midi channel is a basic channel */ FLUID_CHANNEL_ENABLED = 0x08, /**< if flag set the corresponding midi channel is enabled, else disabled, i.e. channel ignores any MIDI messages */ /* FLUID_CHANNEL_LEGATO_PLAYING bit of channel mode keeps trace of the legato /staccato state playing. FLUID_CHANNEL_LEGATO_PLAYING bit is updated on noteOn/noteOff mono by the legato detector: - On noteOn, before inserting a new note into the monolist. - On noteOff, after removing a note out of the monolist. - On noteOn, this state is used by fluid_synth_noteon_mono_LOCAL() to play the current note legato or staccato. - On noteOff, this state is used by fluid_synth_noteoff_mono_LOCAL() to play the current noteOff legato with the most recent note. */ /* bit7, 1: means legato playing , 0: means staccato playing */ FLUID_CHANNEL_LEGATO_PLAYING = 0x80 }; /* End of interface to monophonic list variables */ void fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel, unsigned char onenote); int fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev); void fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev); void fluid_channel_clear_monolist(fluid_channel_t *chan); void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel); void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan); void fluid_channel_cc_legato(fluid_channel_t *chan, int value); void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value); #endif /* _FLUID_CHAN_H */ fluidsynth-2.2.5/src/synth/fluid_event.c000066400000000000000000000430611417326347500203350ustar00rootroot00000000000000/* 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.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; evt->id = -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) { fluid_return_if_fail(evt != NULL); 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; } void fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id) { evt->id = id; } /** * 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) * @note Since fluidsynth 2.2.2, this function will give you a #FLUID_SEQ_NOTEOFF when * called with @p vel being zero. */ void fluid_event_noteon(fluid_event_t *evt, int channel, short key, short vel) { if(vel == 0) { fluid_event_noteoff(evt, channel, key); return; } 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. * * Before fluidsynth 2.2.0, this event type was naively implemented when used in conjunction with fluid_sequencer_register_fluidsynth(), * because it simply enqueued a fluid_event_noteon() and fluid_event_noteoff(). * A handling for overlapping notes was not implemented. Starting with 2.2.0, this changes: If a fluid_event_note() is already playing, * while another fluid_event_note() arrives on the same @c channel and @c key, the earlier event will be canceled. * @param evt Sequencer event structure * @param channel MIDI channel number * @param key MIDI note number (0-127) * @param vel MIDI velocity value (1-127) * @param duration Duration of note in the time scale used by the sequencer * * @note The application should decide whether to use only Notes with duration, or separate NoteOn and NoteOff events. * @warning Calling this function with @p vel or @p duration being zero results in undefined behavior! */ 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, int 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 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, int value) { evt->type = FLUID_SEQ_PITCHWHEELSENS; 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, int 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, int 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, int 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, int 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, int 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, int 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, int 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 scale change event. * Useful for scheduling tempo changes. * @param evt Sequencer event structure * @param new_scale The new time scale to apply to the sequencer, see fluid_sequencer_set_time_scale() * @since 2.2.0 */ void fluid_event_scale(fluid_event_t *evt, double new_scale) { evt->type = FLUID_SEQ_SCALE; evt->scale = new_scale; } /** * 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, int 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 polyphonic aftertouch event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param key MIDI note number (0-127) * @param val Aftertouch amount (0-127) * @since 2.0.0 */ void fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, int val) { evt->type = FLUID_SEQ_KEYPRESSURE; evt->channel = channel; if(key < 0) { key = 0; } if(key > 127) { key = 127; } if(val < 0) { val = 0; } if(val > 127) { val = 127; } evt->key = key; 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; } /** * @internal * Get the time field from a sequencer event structure. * @param evt Sequencer event structure * @return Time value */ fluid_note_id_t fluid_event_get_id(fluid_event_t *evt) { return evt->id; } /** * 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_PITCHWHEELSENS, #FLUID_SEQ_MODULATION, #FLUID_SEQ_SUSTAIN, * #FLUID_SEQ_CONTROLCHANGE, #FLUID_SEQ_PAN, #FLUID_SEQ_VOLUME, * #FLUID_SEQ_REVERBSEND, #FLUID_SEQ_CHORUSSEND. */ int 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. */ int 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; } /** * Gets time scale field from a sequencer event structure. * @param evt Sequencer event structure * @return SoundFont identifier value. * * Used by the #FLUID_SEQ_SCALE event type. */ double fluid_event_get_scale(fluid_event_t *evt) { return evt->scale; } fluidsynth-2.2.5/src/synth/fluid_event.h000066400000000000000000000032711417326347500203410ustar00rootroot00000000000000/* 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" #ifdef __cplusplus extern "C" { #endif typedef int fluid_note_id_t; /* 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; int value; fluid_note_id_t id; int pitch; unsigned int duration; double scale; void *data; }; unsigned int fluid_event_get_time(fluid_event_t *evt); void fluid_event_set_time(fluid_event_t *evt, unsigned int time); fluid_note_id_t fluid_event_get_id(fluid_event_t *evt); void fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id); void fluid_event_clear(fluid_event_t *evt); #ifdef __cplusplus } #endif #endif /* _FLUID_EVENT_PRIV_H */ fluidsynth-2.2.5/src/synth/fluid_gen.c000066400000000000000000000157541417326347500177750ustar00rootroot00000000000000/* 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" #define _GEN(_name) GEN_ ## _name, #_name /* See SFSpec21 $8.1.3 */ static const fluid_gen_info_t fluid_gen_info[] = { /* number/name init nrpn-scale min max def */ { _GEN(STARTADDROFS), 1, 1, 0.0f, 1e10f, 0.0f }, { _GEN(ENDADDROFS), 1, 1, -1e10f, 0.0f, 0.0f }, { _GEN(STARTLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f }, { _GEN(ENDLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f }, { _GEN(STARTADDRCOARSEOFS), 0, 1, 0.0f, 1e10f, 0.0f }, { _GEN(MODLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, { _GEN(VIBLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, { _GEN(MODENVTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, { _GEN(FILTERFC), 1, 2, 1500.0f, 13500.0f, 13500.0f }, { _GEN(FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f }, { _GEN(MODLFOTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f }, { _GEN(MODENVTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f }, { _GEN(ENDADDRCOARSEOFS), 0, 1, -1e10f, 0.0f, 0.0f }, { _GEN(MODLFOTOVOL), 1, 1, -960.0f, 960.0f, 0.0f }, { _GEN(UNUSED1), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(CHORUSSEND), 1, 1, 0.0f, 1000.0f, 0.0f }, { _GEN(REVERBSEND), 1, 1, 0.0f, 1000.0f, 0.0f }, { _GEN(PAN), 1, 1, -500.0f, 500.0f, 0.0f }, { _GEN(UNUSED2), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(UNUSED3), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(UNUSED4), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(MODLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, { _GEN(MODLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f }, { _GEN(VIBLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, { _GEN(VIBLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f }, { _GEN(MODENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, { _GEN(MODENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f }, { _GEN(MODENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f }, { _GEN(MODENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f }, { _GEN(MODENVSUSTAIN), 0, 1, 0.0f, 1000.0f, 0.0f }, { _GEN(MODENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f }, { _GEN(KEYTOMODENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f }, { _GEN(KEYTOMODENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f }, { _GEN(VOLENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, { _GEN(VOLENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f }, { _GEN(VOLENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f }, { _GEN(VOLENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f }, { _GEN(VOLENVSUSTAIN), 0, 1, 0.0f, 1440.0f, 0.0f }, { _GEN(VOLENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f }, { _GEN(KEYTOVOLENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f }, { _GEN(KEYTOVOLENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f }, { _GEN(INSTRUMENT), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(RESERVED1), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(KEYRANGE), 0, 0, 0.0f, 127.0f, 0.0f }, { _GEN(VELRANGE), 0, 0, 0.0f, 127.0f, 0.0f }, { _GEN(STARTLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f }, { _GEN(KEYNUM), 1, 0, 0.0f, 127.0f, -1.0f }, { _GEN(VELOCITY), 1, 1, 0.0f, 127.0f, -1.0f }, { _GEN(ATTENUATION), 1, 1, 0.0f, 1440.0f, 0.0f }, { _GEN(RESERVED2), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(ENDLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f }, { _GEN(COARSETUNE), 0, 1, -120.0f, 120.0f, 0.0f }, { _GEN(FINETUNE), 0, 1, -99.0f, 99.0f, 0.0f }, { _GEN(SAMPLEID), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(SAMPLEMODE), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(RESERVED3), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(SCALETUNE), 0, 1, 0.0f, 1200.0f, 100.0f }, { _GEN(EXCLUSIVECLASS), 0, 0, 0.0f, 0.0f, 0.0f }, { _GEN(OVERRIDEROOTKEY), 1, 0, 0.0f, 127.0f, -1.0f }, { _GEN(PITCH), 1, 0, 0.0f, 127.0f, 0.0f }, { _GEN(CUSTOM_BALANCE), 1, 0, -960.0f, 960.0f, 0.0f }, { _GEN(CUSTOM_FILTERFC), 1, 2, 0.0f, 22050.0f, 0.0f }, { _GEN(CUSTOM_FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f } }; /* fluid_gen_init * * Set an array of generators to their initial value */ void fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel) { int i; for(i = 0; i < GEN_LAST; i++) { gen[i].flags = GEN_UNUSED; gen[i].mod = 0.0; gen[i].nrpn = (channel == NULL) ? 0.0 : fluid_channel_get_gen(channel, i); gen[i].val = fluid_gen_info[i].def; } } 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) { data = data - 8192; fluid_clip(data, -8192, 8192); return (fluid_real_t)(data * fluid_gen_info[gen].nrpn_scale); } const char *fluid_gen_name(int gen) { return fluid_gen_info[gen].name; } fluidsynth-2.2.5/src/synth/fluid_gen.h000066400000000000000000000043051417326347500177700ustar00rootroot00000000000000/* 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 *name; char init; /* Does the generator need to be initialized (not used) */ char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */ float min; /* The minimum value */ float max; /* The maximum value */ float def; /* The default value (cfr. fluid_gen_init()) */ } fluid_gen_info_t; /* * 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 */ }; #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); void fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel); const char *fluid_gen_name(int gen); #endif /* _FLUID_GEN_H */ fluidsynth-2.2.5/src/synth/fluid_mod.c000066400000000000000000000610001417326347500177640ustar00rootroot00000000000000/* 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" /** * Clone the modulators destination, sources, flags and amount. * * @param mod the modulator to store the copy to * @param src the source modulator to retrieve the information from * * @note The \c next member of \c mod will be left unchanged. */ void fluid_mod_clone(fluid_mod_t *mod, const 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 The modulator instance * @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 The modulator instance * @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 The modulator instance * @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 The modulator instance * @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 The modulator instance * @return The primary source value (#fluid_mod_src or a MIDI CC controller value). */ int fluid_mod_get_source1(const fluid_mod_t *mod) { return mod->src1; } /** * Get primary source flags from a modulator. * * @param mod The modulator instance * @return The primary source flags (#fluid_mod_flags). */ int fluid_mod_get_flags1(const fluid_mod_t *mod) { return mod->flags1; } /** * Get the secondary source value from a modulator. * * @param mod The modulator instance * @return The secondary source value (#fluid_mod_src or a MIDI CC controller value). */ int fluid_mod_get_source2(const fluid_mod_t *mod) { return mod->src2; } /** * Get secondary source flags from a modulator. * * @param mod The modulator instance * @return The secondary source flags (#fluid_mod_flags). */ int fluid_mod_get_flags2(const fluid_mod_t *mod) { return mod->flags2; } /** * Get destination effect from a modulator. * * @param mod The modulator instance * @return Destination generator (#fluid_gen_type) */ int fluid_mod_get_dest(const fluid_mod_t *mod) { return mod->dest; } /** * Get the scale amount from a modulator. * * @param mod The modulator instance * @return Scale amount */ double fluid_mod_get_amount(const 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_voice_t *voice ) { const fluid_channel_t *chan = voice->channel; fluid_real_t val; if(mod_flags & FLUID_MOD_CC) { /* From MIDI Recommended Practice (RP-036) Default Pan Formula: * "Since MIDI controller values range from 0 to 127, the exact center * of the range, 63.5, cannot be represented. Therefore, the effective * range for CC#10 is modified to be 1 to 127, and values 0 and 1 both * pan hard left. The recommended method is to subtract 1 from the * value of CC#10, and saturate the result to be non-negative." * * We treat the balance control in exactly the same way, as the same * problem applies here as well. */ if(mod_src == PAN_MSB || mod_src == BALANCE_MSB) { *range = 126; val = fluid_channel_get_cc(chan, mod_src) - 1; if(val < 0) { val = 0; } } else { 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, voice->key); 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] */ 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; /* * MIDI CCs only have a resolution of 7 bits. The closer val_norm gets to 1, * the less will be the resulting change of the sinus. When using this sin() * for scaling the cutoff frequency, there will be no audible difference between * MIDI CCs 118 to 127. To avoid this waste of CCs multiply with 0.87 * (at least for unipolar) which makes sin() never get to 1.0 but to 0.98 which * is close enough. */ case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* custom sin(x) */ val = FLUID_SIN((FLUID_M_PI / 2.0f * 0.87f) * val_norm); break; case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* custom */ val = FLUID_SIN((FLUID_M_PI / 2.0f * 0.87f) * (1.0f - val_norm)); break; case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* custom */ val = (val_norm > 0.5f) ? FLUID_SIN(FLUID_M_PI * (val_norm - 0.5f)) : -FLUID_SIN(FLUID_M_PI * (0.5f - val_norm)); break; case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* custom */ val = (val_norm > 0.5f) ? -FLUID_SIN(FLUID_M_PI * (val_norm - 0.5f)) : FLUID_SIN(FLUID_M_PI * (0.5f - val_norm)); break; default: FLUID_LOG(FLUID_ERR, "Unknown modulator type '%d', disabling modulator.", mod_flags); val = 0.0f; break; } return val; } /* * fluid_mod_get_value. * Computes and return modulator output following SF2.01 * (See SoundFont Modulator Controller Model Chapter 9.5). * * Output = Transform(Amount * Map(primary source input) * Map(secondary source input)) * * Notes: * 1)fluid_mod_get_value, ignores the Transform operator. The result is: * * Output = Amount * Map(primary source input) * Map(secondary source input) * * 2)When primary source input (src1) is set to General Controller 'No Controller', * output is forced to 0. * * 3)When secondary source input (src2) is set to General Controller 'No Controller', * output is forced to +1.0 */ fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) { extern fluid_mod_t default_vel2filter_mod; fluid_real_t v1 = 0.0, v2 = 1.0; fluid_real_t range1 = 127.0, range2 = 127.0; /* '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(fluid_mod_test_identity(mod, &default_vel2filter_mod)) { // 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, voice); /* transform the input value */ v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1); } /* When primary source input (src1) is set to General Controller 'No Controller', output is forced to 0.0 */ else { return 0.0; } /* 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, voice); /* transform the second input value */ v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2); } /* When secondary source input (src2) is set to General Controller 'No Controller', output is forced to +1.0 */ else { v2 = 1.0f; } /* 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 * new_fluid_mod() { 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 delete_fluid_mod(fluid_mod_t *mod) { FLUID_FREE(mod); } /** * Returns the size of the fluid_mod_t structure. * * @return Size of fluid_mod_t in bytes * * Useful in low latency scenarios e.g. to allocate a modulator on the stack. */ size_t fluid_mod_sizeof() { return sizeof(fluid_mod_t); } /** * Checks if modulator with source 1 other than CC is FLUID_MOD_NONE. * * @param mod, modulator. * @return TRUE if modulator source 1 other than cc is FLUID_MOD_NONE, FALSE otherwise. */ static int fluid_mod_is_src1_none(const fluid_mod_t *mod) { return(((mod->flags1 & FLUID_MOD_CC) == 0) && (mod->src1 == FLUID_MOD_NONE)); } /** * Checks if modulators source other than CC source is invalid. * * @param mod, modulator. * @param src1_select, source input selection to check. * 1 to check src1 source. * 0 to check src2 source. * @return FALSE if selected modulator source other than cc is invalid, TRUE otherwise. * * (specs SF 2.01 7.4, 7.8, 8.2.1) */ static int fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select) { unsigned char flags, src; if(src1_select) { flags = mod->flags1; src = mod->src1; } else { flags = mod->flags2; src = mod->src2; } return(((flags & FLUID_MOD_CC) != 0) /* src is a CC */ /* SF2.01 section 8.2.1: Constant value */ || ((src == FLUID_MOD_NONE) || (src == FLUID_MOD_VELOCITY) /* Note-on velocity */ || (src == FLUID_MOD_KEY) /* Note-on key number */ || (src == FLUID_MOD_KEYPRESSURE) /* Poly pressure */ || (src == FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */ || (src == FLUID_MOD_PITCHWHEEL) /* Pitch wheel */ || (src == FLUID_MOD_PITCHWHEELSENS) /* Pitch wheel sensitivity */ )); } /** * Checks if modulator CC source is invalid (specs SF 2.01 7.4, 7.8, 8.2.1). * * @param mod, modulator. * @src1_select, source input selection: * 1 to check src1 source or * 0 to check src2 source. * @return FALSE if selected modulator's source CC is invalid, TRUE otherwise. */ static int fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select) { unsigned char flags, src; if(src1_select) { flags = mod->flags1; src = mod->src1; } else { flags = mod->flags2; src = mod->src2; } return(((flags & FLUID_MOD_CC) == 0) /* src is non CC */ || ((src != BANK_SELECT_MSB) && (src != BANK_SELECT_LSB) && (src != DATA_ENTRY_MSB) && (src != DATA_ENTRY_LSB) /* is src not NRPN_LSB, NRPN_MSB, RPN_LSB, RPN_MSB */ && ((src < NRPN_LSB) || (RPN_MSB < src)) /* is src not ALL_SOUND_OFF, ALL_CTRL_OFF, LOCAL_CONTROL, ALL_NOTES_OFF ? */ /* is src not OMNI_OFF, OMNI_ON, POLY_OFF, POLY_ON ? */ && (src < ALL_SOUND_OFF) /* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) However, as long fluidsynth will use only CC 7 bits resolution, it is safe to ignore these SF recommendations on CC receive. See explanations in fluid_synth_cc_LOCAL() */ /* uncomment next line to forbid CC lsb */ /* && ((src < 32) || (63 < src)) */ )); } /** * Checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1) * * @param mod, modulator. * @param name,if not NULL, pointer on a string displayed as a warning. * @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise. */ int fluid_mod_check_sources(const fluid_mod_t *mod, char *name) { static const char invalid_non_cc_src[] = "Invalid modulator, using non-CC source %s.src%d=%d"; static const char invalid_cc_src[] = "Invalid modulator, using CC source %s.src%d=%d"; static const char src1_is_none[] = "Modulator with source 1 none %s.src1=%d"; /* checks valid non cc sources */ if(!fluid_mod_check_non_cc_source(mod, 1)) /* check src1 */ { if(name) { FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 1, mod->src1); } return FALSE; } /* When src1 is non CC source FLUID_MOD_NONE, the modulator is valid but the output of this modulator will be forced to 0 at synthesis time. Also this modulator cannot be used to overwrite a default modulator (as there is no default modulator with src1 source equal to FLUID_MOD_NONE). Consequently it is useful to return FALSE to indicate this modulator being useless. It will be removed later with others invalid modulators. */ if(fluid_mod_is_src1_none(mod)) { if(name) { FLUID_LOG(FLUID_WARN, src1_is_none, name, mod->src1); } return FALSE; } if(!fluid_mod_check_non_cc_source(mod, 0)) /* check src2 */ { if(name) { FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 2, mod->src2); } return FALSE; } /* checks valid cc sources */ if(!fluid_mod_check_cc_source(mod, 1)) /* check src1 */ { if(name) { FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 1, mod->src1); } return FALSE; } if(!fluid_mod_check_cc_source(mod, 0)) /* check src2 */ { if(name) { FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 2, mod->src2); } return FALSE; } return TRUE; } /** * Checks if two modulators are identical in sources, flags and destination. * * @param mod1 First modulator * @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(const fluid_mod_t *mod1, const 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; } /** * Check if the modulator has the given source. * * @param mod The modulator instance * @param cc Boolean value indicating if ctrl is a CC controller or not * @param ctrl The source to check for (if \c cc == FALSE : a value of type #fluid_mod_src, else the value of the MIDI CC to check for) * * @return TRUE if the modulator has the given source, FALSE otherwise. */ int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl) { return ( ( ((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)) ) ); } /** * Check if the modulator has the given destination. * * @param mod The modulator instance * @param gen The destination generator of type #fluid_gen_type to check for * @return TRUE if the modulator has the given destination, FALSE otherwise. */ int fluid_mod_has_dest(const fluid_mod_t *mod, int gen) { return mod->dest == gen; } /* debug function: Prints the contents of a modulator */ #ifdef DEBUG 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_CUSTOM_FILTERQ: printf("custom-Q"); break; case GEN_CUSTOM_FILTERFC: printf("custom-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_CUSTOM_BALANCE: printf("balance"); 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); }; #endif fluidsynth-2.2.5/src/synth/fluid_mod.h000066400000000000000000000036441417326347500200030ustar00rootroot00000000000000/* 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" /* * Modulator structure. See SoundFont 2.04 PDF section 8.2. */ 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; }; fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice); int fluid_mod_check_sources(const fluid_mod_t *mod, char *name); #ifdef DEBUG void fluid_dump_modulator(fluid_mod_t *mod); #endif #endif /* _FLUID_MOD_H */ fluidsynth-2.2.5/src/synth/fluid_synth.c000066400000000000000000010571561417326347500203740ustar00rootroot00000000000000/* 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_synth.h" #include "fluid_sys.h" #include "fluid_chan.h" #include "fluid_tuning.h" #include "fluid_settings.h" #include "fluid_sfont.h" #include "fluid_defsfont.h" #include "fluid_instpatch.h" #ifdef TRAP_ON_FPE #define _GNU_SOURCE #include /* seems to not be declared in fenv.h */ extern int feenableexcept(int excepts); #endif #define FLUID_API_RETURN(return_value) \ do { fluid_synth_api_exit(synth); \ return return_value; } while (0) #define FLUID_API_RETURN_IF_CHAN_DISABLED(return_value) \ do { if (FLUID_LIKELY(synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED)) \ {} \ else \ { FLUID_API_RETURN(return_value); } \ } while (0) #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_API_RETURN(fail_value); \ } \ static void fluid_synth_init(void); static void fluid_synth_api_enter(fluid_synth_t *synth); static void fluid_synth_api_exit(fluid_synth_t *synth); 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_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_sysex_gs_dt1(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_sysex_xg(fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int avail_response, int *handled, int dryrun); 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_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key); 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 int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group, int param, double *value); static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group, int param, double *value); static fluid_preset_t * fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum, int banknum, int prognum); static fluid_preset_t * fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname, int banknum, int prognum); static void fluid_synth_update_presets(fluid_synth_t *synth); static void fluid_synth_update_gain_LOCAL(fluid_synth_t *synth); static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony); static void init_dither(void); static FLUID_INLINE int16_t round_clip_to_i16(float x); static int fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount); static fluid_voice_t *fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth); static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth, fluid_voice_t *new_voice); static int fluid_synth_sfunload_callback(void *data, unsigned int msec); 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); static void fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id); static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels); /* Callback handlers for real-time settings */ static void fluid_synth_handle_gain(void *data, const char *name, double value); static void fluid_synth_handle_polyphony(void *data, const char *name, int value); static void fluid_synth_handle_device_id(void *data, const char *name, int value); static void fluid_synth_handle_overflow(void *data, const char *name, double value); static void fluid_synth_handle_important_channels(void *data, const char *name, const char *value); static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value); static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value); static void fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan); static int fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val); static void fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val); /*************************************************************** * * GLOBAL */ /* has the synth module been initialized? */ /* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */ static fluid_atomic_int_t fluid_synth_initialized = {0}; /* 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. */ static fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */ /*not static */ fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */ static fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */ static fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */ static fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */ static fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */ static fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */ static fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */ static fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */ static fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */ static fluid_mod_t custom_balance_mod; /* Non-standard modulator */ /* custom_breath2att_modulator is not a default modulator specified in SF it is intended to replace default_vel2att_mod on demand using API fluid_set_breath_mode() or command shell setbreathmode. */ static fluid_mod_t custom_breath2att_mod; /* reverb presets */ static const 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 }, }; /*************************************************************** * * INITIALIZATION & UTILITIES */ void fluid_synth_settings(fluid_settings_t *settings) { fluid_settings_register_int(settings, "synth.verbose", 0, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_int(settings, "synth.reverb.active", 1, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_num(settings, "synth.reverb.room-size", FLUID_REVERB_DEFAULT_ROOMSIZE, 0.0f, 1.0f, 0); fluid_settings_register_num(settings, "synth.reverb.damp", FLUID_REVERB_DEFAULT_DAMP, 0.0f, 1.0f, 0); fluid_settings_register_num(settings, "synth.reverb.width", FLUID_REVERB_DEFAULT_WIDTH, 0.0f, 100.0f, 0); fluid_settings_register_num(settings, "synth.reverb.level", FLUID_REVERB_DEFAULT_LEVEL, 0.0f, 1.0f, 0); fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_int(settings, "synth.chorus.nr", FLUID_CHORUS_DEFAULT_N, 0, 99, 0); fluid_settings_register_num(settings, "synth.chorus.level", FLUID_CHORUS_DEFAULT_LEVEL, 0.0f, 10.0f, 0); fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.1f, 5.0f, 0); fluid_settings_register_num(settings, "synth.chorus.depth", FLUID_CHORUS_DEFAULT_DEPTH, 0.0f, 256.0f, 0); fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_int(settings, "synth.lock-memory", 1, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_str(settings, "midi.portname", "", 0); #ifdef DEFAULT_SOUNDFONT fluid_settings_register_str(settings, "synth.default-soundfont", DEFAULT_SOUNDFONT, 0); #endif fluid_settings_register_int(settings, "synth.polyphony", 256, 1, 65535, 0); fluid_settings_register_int(settings, "synth.midi-channels", 16, 16, 256, 0); fluid_settings_register_num(settings, "synth.gain", 0.2f, 0.0f, 10.0f, 0); fluid_settings_register_int(settings, "synth.audio-channels", 1, 1, 128, 0); fluid_settings_register_int(settings, "synth.audio-groups", 1, 1, 128, 0); fluid_settings_register_int(settings, "synth.effects-channels", 2, 2, 2, 0); fluid_settings_register_int(settings, "synth.effects-groups", 1, 1, 128, 0); fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0); fluid_settings_register_int(settings, "synth.device-id", 0, 0, 126, 0); #ifdef ENABLE_MIXER_THREADS fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0); #else fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0); #endif fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0); fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_num(settings, "synth.overflow.percussion", 4000, -10000, 10000, 0); fluid_settings_register_num(settings, "synth.overflow.sustained", -1000, -10000, 10000, 0); fluid_settings_register_num(settings, "synth.overflow.released", -2000, -10000, 10000, 0); fluid_settings_register_num(settings, "synth.overflow.age", 1000, -10000, 10000, 0); fluid_settings_register_num(settings, "synth.overflow.volume", 500, -10000, 10000, 0); fluid_settings_register_num(settings, "synth.overflow.important", 5000, -50000, 50000, 0); fluid_settings_register_str(settings, "synth.overflow.important-channels", "", 0); fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0); 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"); fluid_settings_register_int(settings, "synth.dynamic-sample-loading", 0, 0, 1, FLUID_HINT_TOGGLED); } /** * 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; } /* * void fluid_synth_init * * Does all the initialization for this module. */ static void fluid_synth_init(void) { #ifdef TRAP_ON_FPE /* Turn on floating point exception traps */ feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_INVALID); #endif init_dither(); /* custom_breath2att_mod is not a default modulator specified in SF2.01. it is intended to replace default_vel2att_mod on demand using API fluid_set_breath_mode() or command shell setbreathmode. */ fluid_mod_set_source1(&custom_breath2att_mod, /* The modulator we are programming here */ BREATH_MSB, /* Source. breath MSB corresponds to 2. */ FLUID_MOD_CC /* 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(&custom_breath2att_mod, 0, 0); /* No 2nd source */ fluid_mod_set_dest(&custom_breath2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ fluid_mod_set_amount(&custom_breath2att_mod, FLUID_PEAK_ATTENUATION); /* Modulation amount: 960 */ /* 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, FLUID_PEAK_ATTENUATION); /* 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, MODULATION_MSB, /* 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, VOLUME_MSB, /* 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, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ /* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */ fluid_mod_set_source1(&default_pan_mod, PAN_MSB, /* 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 says: "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, EXPRESSION_MSB, /* 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, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ /* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */ fluid_mod_set_source1(&default_reverb_mod, EFFECTS_DEPTH1, /* 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, EFFECTS_DEPTH3, /* 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 ... */ /* Initial Pitch is not a "standard" generator, because it isn't mentioned in the list of generators in the SF2 specifications. That's why destination Initial Pitch is replaced here by fine tune generator. */ 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 */ ); /* Also see the comment in gen.h about GEN_PITCH */ fluid_mod_set_dest(&default_pitch_bend_mod, GEN_FINETUNE); /* Destination: Fine Tune */ fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */ /* Non-standard MIDI continuous controller 8 to channel stereo balance */ fluid_mod_set_source1(&custom_balance_mod, BALANCE_MSB, /* Index=8 */ FLUID_MOD_CC /* CC=1 */ | FLUID_MOD_CONCAVE /* type=1 */ | FLUID_MOD_BIPOLAR /* P=1 */ | FLUID_MOD_POSITIVE /* D=0 */ ); fluid_mod_set_source2(&custom_balance_mod, 0, 0); fluid_mod_set_dest(&custom_balance_mod, GEN_CUSTOM_BALANCE); /* Destination: stereo balance */ /* Amount: 96 dB of attenuation (on the opposite channel) */ fluid_mod_set_amount(&custom_balance_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ #if defined(LIBINSTPATCH_SUPPORT) /* defer libinstpatch init to fluid_instpatch.c to avoid #include "libinstpatch.h" */ if(!fluid_instpatch_supports_multi_init()) { fluid_instpatch_init(); } #endif } static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t *synth) { return fluid_atomic_int_get(&synth->ticks_since_start); } static FLUID_INLINE void fluid_synth_add_ticks(fluid_synth_t *synth, int val) { fluid_atomic_int_add(&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 */ static 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; } fluid_sample_timer_reset(synth, result); result->data = data; result->callback = callback; result->next = synth->sample_timers; synth->sample_timers = result; return result; } void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer) { fluid_sample_timer_t **ptr; fluid_return_if_fail(synth != NULL); fluid_return_if_fail(timer != NULL); ptr = &synth->sample_timers; while(*ptr) { if(*ptr == timer) { *ptr = timer->next; FLUID_FREE(timer); return; } ptr = &((*ptr)->next); } } void fluid_sample_timer_reset(fluid_synth_t *synth, fluid_sample_timer_t *timer) { timer->starttick = fluid_synth_get_ticks(synth); timer->isfinished = 0; } /*************************************************************** * * FLUID SYNTH */ static FLUID_INLINE void fluid_synth_update_mixer(fluid_synth_t *synth, fluid_rvoice_function_t 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_int_real(synth->eventhandler, method, synth->eventhandler->mixer, intparam, realparam); } static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL(fluid_synth_t *synth) { int i; fluid_settings_getint(synth->settings, "synth.min-note-length", &i); return (unsigned int)(i * synth->sample_rate / 1000.0f); } /** * Create new FluidSynth instance. * @param settings Configuration parameters to use (used directly). * @return New FluidSynth instance or NULL on error * * @note The @p settings parameter is used directly, but the synth does not take ownership of it. * Hence, the caller is responsible for freeing it, when no longer needed. * Further note that you may modify FluidSettings of the * @p settings instance. However, only those FluidSettings marked as 'realtime' will * affect the synth immediately. See the \ref fluidsettings for more details. * * @warning The @p settings object should only be used by a single synth at a time. I.e. creating * multiple synth instances with a single @p settings object causes undefined behavior. Once the * "single synth" has been deleted, you may use the @p settings object again for another synth. */ fluid_synth_t * new_fluid_synth(fluid_settings_t *settings) { fluid_synth_t *synth; fluid_sfloader_t *loader; char *important_channels; int i, prio_level = 0; int with_ladspa = 0; double sample_rate_min, sample_rate_max; /* initialize all the conversion tables and other stuff */ if(fluid_atomic_int_compare_and_exchange(&fluid_synth_initialized, 0, 1)) { 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)); #if defined(LIBINSTPATCH_SUPPORT) if(fluid_instpatch_supports_multi_init()) { fluid_instpatch_init(); } #endif 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.polyphony", &synth->polyphony); fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate); fluid_settings_getnum_range(settings, "synth.sample-rate", &sample_rate_min, &sample_rate_max); 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_getint(settings, "synth.effects-groups", &synth->effects_groups); fluid_settings_getnum_float(settings, "synth.gain", &synth->gain); fluid_settings_getint(settings, "synth.device-id", &synth->device_id); fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores); fluid_settings_getnum_float(settings, "synth.overflow.percussion", &synth->overflow.percussion); fluid_settings_getnum_float(settings, "synth.overflow.released", &synth->overflow.released); fluid_settings_getnum_float(settings, "synth.overflow.sustained", &synth->overflow.sustained); fluid_settings_getnum_float(settings, "synth.overflow.volume", &synth->overflow.volume); fluid_settings_getnum_float(settings, "synth.overflow.age", &synth->overflow.age); fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important); /* register the callbacks */ fluid_settings_callback_num(settings, "synth.gain", fluid_synth_handle_gain, synth); fluid_settings_callback_int(settings, "synth.polyphony", fluid_synth_handle_polyphony, synth); fluid_settings_callback_int(settings, "synth.device-id", fluid_synth_handle_device_id, synth); fluid_settings_callback_num(settings, "synth.overflow.percussion", fluid_synth_handle_overflow, synth); fluid_settings_callback_num(settings, "synth.overflow.sustained", fluid_synth_handle_overflow, synth); fluid_settings_callback_num(settings, "synth.overflow.released", fluid_synth_handle_overflow, synth); fluid_settings_callback_num(settings, "synth.overflow.age", fluid_synth_handle_overflow, synth); fluid_settings_callback_num(settings, "synth.overflow.volume", fluid_synth_handle_overflow, synth); fluid_settings_callback_num(settings, "synth.overflow.important", fluid_synth_handle_overflow, synth); fluid_settings_callback_str(settings, "synth.overflow.important-channels", fluid_synth_handle_important_channels, synth); fluid_settings_callback_num(settings, "synth.reverb.room-size", fluid_synth_handle_reverb_chorus_num, synth); fluid_settings_callback_num(settings, "synth.reverb.damp", fluid_synth_handle_reverb_chorus_num, synth); fluid_settings_callback_num(settings, "synth.reverb.width", fluid_synth_handle_reverb_chorus_num, synth); fluid_settings_callback_num(settings, "synth.reverb.level", fluid_synth_handle_reverb_chorus_num, synth); fluid_settings_callback_int(settings, "synth.reverb.active", fluid_synth_handle_reverb_chorus_int, synth); fluid_settings_callback_int(settings, "synth.chorus.active", fluid_synth_handle_reverb_chorus_int, synth); fluid_settings_callback_int(settings, "synth.chorus.nr", fluid_synth_handle_reverb_chorus_int, synth); fluid_settings_callback_num(settings, "synth.chorus.level", fluid_synth_handle_reverb_chorus_num, synth); fluid_settings_callback_num(settings, "synth.chorus.depth", fluid_synth_handle_reverb_chorus_num, synth); fluid_settings_callback_num(settings, "synth.chorus.speed", fluid_synth_handle_reverb_chorus_num, 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; } /* number of buffers rendered by the mixer is determined by synth->audio_groups. audio from MIDI channel is rendered, mapped and mixed in these buffers. Typically synth->audio_channels is only used by audio driver and should be set to the same value that synth->audio_groups. In some situation using LADSPA, it is best to diminish audio-channels so that the driver will be able to pass the audio to audio devices in the case these devices have a limited number of audio channels. audio-channels must not be greater then audio-groups, otherwise these audio output above audio-groups will not be rendered by the mixeur. */ if(synth->audio_channels > synth->audio_groups) { synth->audio_channels = synth->audio_groups; fluid_settings_setint(settings, "synth.audio-channels", synth->audio_channels); FLUID_LOG(FLUID_WARN, "Requested audio-channels to high. " "Limiting this setting to audio-groups."); } if(fluid_settings_dupstr(settings, "synth.overflow.important-channels", &important_channels) == FLUID_OK) { if(fluid_synth_set_important_channels(synth, important_channels) != FLUID_OK) { FLUID_LOG(FLUID_WARN, "Failed to set overflow important channels"); } FLUID_FREE(important_channels); } /* as soon as the synth is created it starts playing. */ synth->state = FLUID_SYNTH_PLAYING; synth->fromkey_portamento = INVALID_NOTE; /* disable portamento */ fluid_atomic_int_set(&synth->ticks_since_start, 0); synth->tuning = NULL; fluid_private_init(synth->tuning_iter); /* Initialize multi-core variables if multiple cores enabled */ if(synth->cores > 1) { fluid_settings_getint(synth->settings, "audio.realtime-prio", &prio_level); } /* Allocate event queue for rvoice mixer */ /* In an overflow situation, a new voice takes about 50 spaces in the queue! */ synth->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony * 64, synth->polyphony, synth->audio_groups, synth->effects_channels, synth->effects_groups, (fluid_real_t)sample_rate_max, synth->sample_rate, synth->cores - 1, prio_level); if(synth->eventhandler == NULL) { goto error_recovery; } /* Setup the list of default modulators. * Needs to happen after eventhandler has been set up, as fluid_synth_enter_api is called in the process */ synth->default_mod = NULL; fluid_synth_add_default_mod(synth, &default_vel2att_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &default_vel2filter_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &default_at2viblfo_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &default_mod2viblfo_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &default_att_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &default_pan_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &default_expr_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &default_reverb_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &default_chorus_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &default_pitch_bend_mod, FLUID_SYNTH_ADD); fluid_synth_add_default_mod(synth, &custom_balance_mod, FLUID_SYNTH_ADD); /* Create and initialize the Fx unit.*/ fluid_settings_getint(settings, "synth.ladspa.active", &with_ladspa); if(with_ladspa) { #ifdef LADSPA synth->ladspa_fx = new_fluid_ladspa_fx(synth->sample_rate, FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE); if(synth->ladspa_fx == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->ladspa_fx, synth->audio_groups); #else /* LADSPA */ FLUID_LOG(FLUID_WARN, "FluidSynth has not been compiled with LADSPA support"); #endif /* LADSPA */ } /* allocate and add the dls sfont loader */ #ifdef LIBINSTPATCH_SUPPORT loader = new_fluid_instpatch_loader(settings); if(loader == NULL) { FLUID_LOG(FLUID_WARN, "Failed to create the instpatch SoundFont loader"); } else { fluid_synth_add_sfloader(synth, loader); } #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; } FLUID_MEMSET(synth->channel, 0, synth->midi_channels * sizeof(*synth->channel)); 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; } FLUID_MEMSET(synth->voice, 0, synth->nvoice * sizeof(*synth->voice)); for(i = 0; i < synth->nvoice; i++) { synth->voice[i] = new_fluid_voice(synth->eventhandler, synth->sample_rate); if(synth->voice[i] == NULL) { goto error_recovery; } } /* sets a default basic channel */ /* Sets one basic channel: basic channel 0, mode 0 (Omni On - Poly) */ /* (i.e all channels are polyphonic) */ /* Must be called after channel objects allocation */ fluid_synth_set_basic_channel_LOCAL(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, synth->midi_channels); synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, synth->polyphony, 0.0f); fluid_synth_reverb_on(synth, -1, synth->with_reverb); fluid_synth_chorus_on(synth, -1, synth->with_chorus); synth->cur = FLUID_BUFSIZE; synth->curmax = 0; synth->dither_index = 0; { double values[FLUID_REVERB_PARAM_LAST]; fluid_settings_getnum(settings, "synth.reverb.room-size", &values[FLUID_REVERB_ROOMSIZE]); fluid_settings_getnum(settings, "synth.reverb.damp", &values[FLUID_REVERB_DAMP]); fluid_settings_getnum(settings, "synth.reverb.width", &values[FLUID_REVERB_WIDTH]); fluid_settings_getnum(settings, "synth.reverb.level", &values[FLUID_REVERB_LEVEL]); fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); } { double values[FLUID_CHORUS_PARAM_LAST]; fluid_settings_getint(settings, "synth.chorus.nr", &i); values[FLUID_CHORUS_NR] = (double)i; fluid_settings_getnum(settings, "synth.chorus.level", &values[FLUID_CHORUS_LEVEL]); fluid_settings_getnum(settings, "synth.chorus.speed", &values[FLUID_CHORUS_SPEED]); fluid_settings_getnum(settings, "synth.chorus.depth", &values[FLUID_CHORUS_DEPTH]); values[FLUID_CHORUS_TYPE] = (double)FLUID_CHORUS_DEFAULT_TYPE; fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values); } synth->bank_select = FLUID_BANK_STYLE_GS; if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gm")) { synth->bank_select = FLUID_BANK_STYLE_GM; } else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gs")) { synth->bank_select = FLUID_BANK_STYLE_GS; } else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "xg")) { synth->bank_select = FLUID_BANK_STYLE_XG; } else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "mma")) { 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 * * @note Other users of a synthesizer instance, such as audio and MIDI drivers, * should be deleted prior to freeing the FluidSynth instance. */ void delete_fluid_synth(fluid_synth_t *synth) { int i, k; fluid_list_t *list; fluid_sfont_t *sfont; fluid_sfloader_t *loader; fluid_return_if_fail(synth != NULL); fluid_profiling_print(); /* unregister all real-time settings callback, to avoid a use-after-free when changing those settings after * this synth has been deleted*/ fluid_settings_callback_num(synth->settings, "synth.gain", NULL, NULL); fluid_settings_callback_int(synth->settings, "synth.polyphony", NULL, NULL); fluid_settings_callback_int(synth->settings, "synth.device-id", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.overflow.percussion", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.overflow.sustained", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.overflow.released", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.overflow.age", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.overflow.volume", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.overflow.important", NULL, NULL); fluid_settings_callback_str(synth->settings, "synth.overflow.important-channels", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.reverb.room-size", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.reverb.damp", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.reverb.width", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.reverb.level", NULL, NULL); fluid_settings_callback_int(synth->settings, "synth.reverb.active", NULL, NULL); fluid_settings_callback_int(synth->settings, "synth.chorus.active", NULL, NULL); fluid_settings_callback_int(synth->settings, "synth.chorus.nr", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.chorus.level", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.chorus.depth", NULL, NULL); fluid_settings_callback_num(synth->settings, "synth.chorus.speed", NULL, NULL); /* turn off all voices, needed to unload SoundFont data */ if(synth->voice != NULL) { for(i = 0; i < synth->nvoice; i++) { fluid_voice_t *voice = synth->voice[i]; if(!voice) { continue; } /* WARNING: A this point we must ensure that the reference counter of any soundfont sample owned by any rvoice belonging to the voice are correctly decremented. This is the contrary part to to fluid_voice_init() where the sample's reference counter is incremented. */ fluid_voice_unlock_rvoice(voice); fluid_voice_overflow_rvoice_finished(voice); 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); } } } delete_fluid_rvoice_eventhandler(synth->eventhandler); /* delete all the SoundFonts */ for(list = synth->sfont; list; list = fluid_list_next(list)) { sfont = fluid_list_get(list); fluid_sfont_delete_internal(sfont); } delete_fluid_list(synth->sfont); /* 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); /* wait for and delete all the lazy sfont unloading timers */ for(list = synth->fonts_to_be_unloaded; list; list = fluid_list_next(list)) { fluid_timer_t* timer = fluid_list_get(list); // explicitly join to wait for the unload really to happen fluid_timer_join(timer); // delete_fluid_timer alone would stop the timer, even if it had not unloaded the soundfont yet delete_fluid_timer(timer); } delete_fluid_list(synth->fonts_to_be_unloaded); if(synth->channel != NULL) { for(i = 0; i < synth->midi_channels; i++) { delete_fluid_channel(synth->channel[i]); } FLUID_FREE(synth->channel); } if(synth->voice != NULL) { for(i = 0; i < synth->nvoice; i++) { 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++) { 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 effects unit */ delete_fluid_ladspa_fx(synth->ladspa_fx); #endif /* delete all default modulators */ delete_fluid_list_mod(synth->default_mod); FLUID_FREE(synth->overflow.important_channels); fluid_rec_mutex_destroy(synth->mutex); FLUID_FREE(synth); #if defined(LIBINSTPATCH_SUPPORT) if(fluid_instpatch_supports_multi_init()) { fluid_instpatch_deinit(); } #endif } /** * 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. * @deprecated This function is not thread-safe and does not work with multiple synths. * It has been deprecated. It may return "" in a future release and will eventually be removed. */ const char * fluid_synth_error(fluid_synth_t *synth) { return ""; } /** * Send a note-on event to a FluidSynth object. * * This function will take care of proper legato playing. If a note on channel @p chan is * already playing at the given key @p key, it will be released (even if it is sustained). * In other words, overlapping notes are not allowed. * @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); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(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]; /* makes 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(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ { /* play the noteOn in monophonic */ return fluid_synth_noteon_mono_LOCAL(synth, chan, key, vel); } else { /* channel is poly and legato CC is Off) */ /* plays the noteOn in polyphonic */ /* Sets the note at first position in monophonic list */ /* In the case where the musician intends to inter the channel in monophonic (by depressing the CC legato on), the next noteOn mono could be played legato with the previous note poly (if the musician choose this). */ fluid_channel_set_onenote_monolist(channel, (unsigned char) key, (unsigned char) vel); /* 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); /* a noteon poly is passed to fluid_synth_noteon_monopoly_legato(). This allows an opportunity to get this note played legato with a previous note if a CC PTC have been received before this noteon. This behavior is a MIDI specification (see FluidPolymono-0004.pdf chapter 4.3-a ,3.4.11 for details). */ return fluid_synth_noteon_monopoly_legato(synth, chan, INVALID_NOTE, key, vel); } } /** * Sends 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); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(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) { int status; fluid_channel_t *channel = synth->channel[chan]; if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ { /* play the noteOff in monophonic */ status = fluid_synth_noteoff_mono_LOCAL(synth, chan, key); } else { /* channel is poly and legato CC is Off) */ /* removes the note from the monophonic list */ if(channel->n_notes && key == fluid_channel_last_note(channel)) { fluid_channel_clear_monolist(channel); } status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); } /* Changes the state (Valid/Invalid) of the most recent note played in a staccato manner */ fluid_channel_invalid_prev_note_staccato(channel); return status; } /* Damps 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_channel_t *channel = synth->channel[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)) { if(voice->key == channel->key_mono_sustained) { /* key_mono_sustained is a possible mono note sustainted (by sustain or sostenuto pedal). It must be marked released (INVALID_NOTE) here because it is released only by sustain pedal */ channel->key_mono_sustained = INVALID_NOTE; } fluid_voice_release(voice); } } return FLUID_OK; } /* Damps 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_channel_t *channel = synth->channel[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)) { if(voice->key == channel->key_mono_sustained) { /* key_mono_sustained is a possible mono note sustainted (by sustain or sostenuto pedal). It must be marked released (INVALID_NOTE) here because it is released only by sostenuto pedal */ channel->key_mono_sustained = INVALID_NOTE; } fluid_voice_release(voice); } } return FLUID_OK; } /** * Adds the specified modulator \c mod as default modulator to the synth. \c mod will * take effect for any subsequently created voice. * @param synth FluidSynth instance * @param mod Modulator info (values copied, passed in object can be freed immediately afterwards) * @param mode Determines how to handle an existing identical modulator (#fluid_synth_add_mod) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * @note Not realtime safe (due to internal memory allocation) and therefore should not be called * from synthesis context at the risk of stalling audio output. */ int fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode) { fluid_mod_t *default_mod; fluid_mod_t *last_mod = NULL; fluid_mod_t *new_mod; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(mod != NULL, FLUID_FAILED); fluid_return_val_if_fail((mode == FLUID_SYNTH_ADD) || (mode == FLUID_SYNTH_OVERWRITE) , FLUID_FAILED); /* Checks if modulators sources are valid */ if(!fluid_mod_check_sources(mod, "api fluid_synth_add_default_mod mod")) { return FLUID_FAILED; } fluid_synth_api_enter(synth); default_mod = synth->default_mod; while(default_mod != NULL) { if(fluid_mod_test_identity(default_mod, mod)) { if(mode == FLUID_SYNTH_ADD) { default_mod->amount += mod->amount; } else // mode == FLUID_SYNTH_OVERWRITE { default_mod->amount = mod->amount; } FLUID_API_RETURN(FLUID_OK); } last_mod = default_mod; default_mod = default_mod->next; } /* Add a new modulator (no existing modulator to add / overwrite). */ new_mod = new_fluid_mod(); if(new_mod == NULL) { FLUID_API_RETURN(FLUID_FAILED); } fluid_mod_clone(new_mod, mod); new_mod->next = NULL; if(last_mod == NULL) { synth->default_mod = new_mod; } else { last_mod->next = new_mod; } FLUID_API_RETURN(FLUID_OK); } /** * Removes the specified modulator \c mod from the synth's default modulator list. * fluid_mod_test_identity() will be used to test modulator matching. * @param synth synth instance * @param mod The modulator to remove * @return #FLUID_OK if a matching modulator was found and successfully removed, #FLUID_FAILED otherwise * * @note Not realtime safe (due to internal memory freeing) and therefore should not be called * from synthesis context at the risk of stalling audio output. */ int fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod) { fluid_mod_t *default_mod; fluid_mod_t *last_mod; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(mod != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); last_mod = default_mod = synth->default_mod; while(default_mod != NULL) { if(fluid_mod_test_identity(default_mod, mod)) { if(synth->default_mod == default_mod) { synth->default_mod = default_mod->next; } else { last_mod->next = default_mod->next; } delete_fluid_mod(default_mod); FLUID_API_RETURN(FLUID_OK); } last_mod = default_mod; default_mod = default_mod->next; } FLUID_API_RETURN(FLUID_FAILED); } /** * 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 * @note This function supports MIDI Global Controllers which will be sent to * all channels of the basic channel if this basic channel is in mode OmniOff/Mono. * This is accomplished by sending the CC one MIDI channel below the basic * channel of the receiver. * Examples: let a synthesizer with 16 MIDI channels: * - Let a basic channel 7 in mode 3 (Omni Off, Mono). If MIDI channel 6 is disabled it * could be used as CC global for all channels belonging to basic channel 7. * - Let a basic channel 0 in mode 3. If MIDI channel 15 is disabled it could be used * as CC global for all channels belonging to basic channel 0. */ int fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val) { int result = FLUID_FAILED; fluid_channel_t *channel; 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); channel = synth->channel[chan]; if(channel->mode & FLUID_CHANNEL_ENABLED) { /* chan is enabled */ if(synth->verbose) { FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); } fluid_channel_set_cc(channel, num, val); result = fluid_synth_cc_LOCAL(synth, chan, num); } else /* chan is disabled so it is a candidate for global channel */ { /* looks for next basic channel */ int n_chan = synth->midi_channels; /* MIDI Channels number */ int basicchan ; if(chan < n_chan - 1) { basicchan = chan + 1; /* next channel */ } else { basicchan = 0; /* wrap to 0 */ } channel = synth->channel[basicchan]; /* Channel must be a basicchan in mode OMNIOFF_MONO */ if((channel->mode & FLUID_CHANNEL_BASIC) && ((channel->mode & FLUID_CHANNEL_MODE_MASK) == FLUID_CHANNEL_MODE_OMNIOFF_MONO)) { /* sends cc to all channels in this basic channel */ int i, nbr = channel->mode_val; for(i = basicchan; i < basicchan + nbr; i++) { if(synth->verbose) { FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", i, num, val); } fluid_channel_set_cc(synth->channel[i], num, val); result = fluid_synth_cc_LOCAL(synth, i, num); } } /* The channel chan is not a valid 'global channel' */ else { result = FLUID_FAILED; } } FLUID_API_RETURN(result); } /* Local synthesis thread variant of MIDI CC set function. Most of CC are allowed to modulate but not all. A comment describes if CC num isn't allowed to modulate. Following explanations should help to understand both MIDI specifications and Soundfont specifications in regard to MIDI specs. MIDI specs: CC LSB (32 to 63) are LSB contributions to CC MSB (0 to 31). It's up to the synthesizer to decide to take LSB values into account or not. Actually Fluidsynth doesn't use CC LSB value inside fluid_voice_update_param() (once fluid_voice_modulate() has been triggered). This is because actually fluidsynth needs only 7 bits resolution (and not 14 bits) from these CCs. So fluidsynth is using only 7 bit MSB (except for portamento time). In regard to MIDI specs Fluidsynth behaves correctly. Soundfont specs 2.01 - 8.2.1: To deal correctly with MIDI CC (regardless if any synth will use CC MSB alone (7 bit) or both CCs MSB,LSB (14 bits) during synthesis), SF specs recommend not making use of CC LSB (i.e only CC MSB) in modulator sources to trigger modulation (i.e modulators with CC LSB connected to sources inputs should be ignored). These specifics are particularly suited for synths that use 14 bits CCs. In this case, the MIDI transmitter sends CC LSB first followed by CC MSB. The MIDI synth receives both CC LSB and CC MSB but only CC MSB will trigger the modulation. This will produce correct synthesis parameters update from a correct 14 bits CC. If in SF specs, modulator sources with CC LSB had been accepted, both CC LSB and CC MSB will triggers 2 modulations. This leads to incorrect synthesis parameters update followed by correct synthesis parameters update. However, as long as fluidsynth will use only CC 7 bits resolution, it is safe to ignore these SF recommendations on CC receive. */ static int fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) { fluid_channel_t *chan = synth->channel[channum]; int nrpn_select; int value; value = fluid_channel_get_cc(chan, num); switch(num) { case LOCAL_CONTROL: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ break; /* CC omnioff, omnion, mono, poly */ /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ case POLY_OFF: case POLY_ON: case OMNI_OFF: case OMNI_ON: /* allowed only if channum is a basic channel */ if(chan->mode & FLUID_CHANNEL_BASIC) { /* Construction of new_mode from current channel mode and this CC mode */ int new_mode = chan->mode & FLUID_CHANNEL_MODE_MASK; switch(num) { case POLY_OFF: new_mode |= FLUID_CHANNEL_POLY_OFF; break; case POLY_ON: new_mode &= ~FLUID_CHANNEL_POLY_OFF; break; case OMNI_OFF: new_mode |= FLUID_CHANNEL_OMNI_OFF; break; case OMNI_ON: new_mode &= ~FLUID_CHANNEL_OMNI_OFF; break; default: /* should never happen */ return FLUID_FAILED; } /* MIDI specs: if value is 0 it means all channels from channum to next basic channel minus 1 (if any) or to MIDI channel count minus 1. However, if value is > 0 (e.g. 4), the group of channels will be be limited to 4. value is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY as this mode implies a group of only one channel. */ /* Checks value range and changes this existing basic channel group */ value = fluid_synth_check_next_basic_channel(synth, channum, new_mode, value); if(value != FLUID_FAILED) { /* reset the current basic channel before changing it */ fluid_synth_reset_basic_channel_LOCAL(synth, channum, chan->mode_val); fluid_synth_set_basic_channel_LOCAL(synth, channum, new_mode, value); break; /* FLUID_OK */ } } return FLUID_FAILED; case LEGATO_SWITCH: /* not allowed to modulate */ /* handles Poly/mono commutation on Legato pedal On/Off.*/ fluid_channel_cc_legato(chan, value); break; case PORTAMENTO_SWITCH: /* not allowed to modulate */ /* Special handling of the monophonic list */ /* Invalids the most recent note played in a staccato manner */ fluid_channel_invalid_prev_note_staccato(chan); break; case SUSTAIN_SWITCH: /* not allowed to modulate */ /* 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: /* not allowed to modulate */ /* 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: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_channel_set_bank_msb(chan, value & 0x7F); break; case BANK_SELECT_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_channel_set_bank_lsb(chan, value & 0x7F); break; case ALL_NOTES_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_synth_all_notes_off_LOCAL(synth, channum); break; case ALL_SOUND_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_synth_all_sounds_off_LOCAL(synth, channum); break; case ALL_CTRL_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_channel_init_ctrl(chan, 1); fluid_synth_modulate_voices_all_LOCAL(synth, channum); break; case DATA_ENTRY_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ break; case DATA_ENTRY_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ { int data = (value << 7) + fluid_channel_get_cc(chan, DATA_ENTRY_LSB); 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); } 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, (float)(data - 8192) * (100.0f / 8192.0f)); 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); 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: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_channel_set_cc(chan, NRPN_LSB, 0); chan->nrpn_select = 0; chan->nrpn_active = 1; break; case NRPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ if(fluid_channel_get_cc(chan, NRPN_MSB) == 120) { 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: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ case RPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ chan->nrpn_active = 0; break; case BREATH_MSB: /* handles CC Breath On/Off noteOn/noteOff mode */ fluid_channel_cc_breath_note_on_off(chan, value); /* fall-through */ default: /* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) */ /* However, as long fluidsynth will use only CC 7 bits resolution, it is safe to ignore these SF recommendations on CC receive. See explanations above */ /* if (! (32 <= num && num <= 63)) */ { return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num); } } return FLUID_OK; } /** * 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); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); *pval = fluid_channel_get_cc(synth->channel[chan], num); FLUID_API_RETURN(FLUID_OK); } /* * Handler for synth.device-id setting. */ static void fluid_synth_handle_device_id(void *data, const char *name, int value) { fluid_synth_t *synth = (fluid_synth_t *)data; fluid_return_if_fail(synth != NULL); fluid_synth_api_enter(synth); synth->device_id = value; fluid_synth_api_exit(synth); } /** * 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 * GS DT1 messages: 0xF0 0x41 0x42 0x12 [ADDRESS (3 bytes)] [DATA] 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); } /* GM or GM2 system on */ if(data[0] == MIDI_SYSEX_UNIV_NON_REALTIME && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) && data[2] == MIDI_SYSEX_GM_ID) { if(handled) { *handled = TRUE; } if(!dryrun && (data[3] == MIDI_SYSEX_GM_ON || data[3] == MIDI_SYSEX_GM2_ON)) { int result; synth->bank_select = FLUID_BANK_STYLE_GM; fluid_synth_api_enter(synth); result = fluid_synth_system_reset_LOCAL(synth); FLUID_API_RETURN(result); } return FLUID_OK; } /* GS DT1 message */ if(data[0] == MIDI_SYSEX_MANUF_ROLAND && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) && data[2] == MIDI_SYSEX_GS_ID && data[3] == MIDI_SYSEX_GS_DT1) { int result; fluid_synth_api_enter(synth); result = fluid_synth_sysex_gs_dt1(synth, data, len, response, response_len, avail_response, handled, dryrun); FLUID_API_RETURN(result); } /* XG message */ if(data[0] == MIDI_SYSEX_MANUF_YAMAHA && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) && data[2] == MIDI_SYSEX_XG_ID) { int result; fluid_synth_api_enter(synth); result = fluid_synth_sysex_xg(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]={0}; int note, frac, frac2; uint8_t 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; /* copy 16 ASCII characters (potentially not null terminated) to the sysex buffer */ FLUID_MEMCPY(resptr, name, 16); resptr += 16; for(i = 0; i < 128; i++) { 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; } /* Handler for GS DT1 messages */ static int fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int avail_response, int *handled, int dryrun) { int addr; int len_data; int checksum = 0, i; if(len < 9) // at least one byte of data should be transmitted { FLUID_LOG(FLUID_INFO, "SysEx DT1: message too short, dropping it."); return FLUID_FAILED; } len_data = len - 8; addr = (data[4] << 16) | (data[5] << 8) | data[6]; for (i = 4; i < len - 1; ++i) { checksum += data[i]; } checksum = 0x80 - (checksum & 0x7F); if (checksum != data[len - 1]) { FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping message on addr 0x%x due to incorrect checksum 0x%x. Correct checksum: 0x%x", addr, (int)data[len - 1], checksum); return FLUID_FAILED; } if (addr == 0x40007F) // Mode set { if (len_data > 1 || (data[7] != 0 && data[7] != 0x7f)) { FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid mode set message"); return FLUID_FAILED; } if (handled) { *handled = TRUE; } if (!dryrun) { if (data[7] == 0) { synth->bank_select = FLUID_BANK_STYLE_GS; } else { synth->bank_select = FLUID_BANK_STYLE_GM; } return fluid_synth_system_reset_LOCAL(synth); } return FLUID_OK; } if (synth->bank_select != FLUID_BANK_STYLE_GS) { return FLUID_OK; // Silently ignore all other messages } if ((addr & 0xFFF0FF) == 0x401015) // Use for rhythm part { if (len_data > 1 || data[7] > 0x02) { FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid rhythm part message"); return FLUID_FAILED; } if (handled) { *handled = TRUE; } if (!dryrun) { int chan = (addr >> 8) & 0x0F; //See the Patch Part parameters section in SC-88Pro/8850 owner's manual chan = chan >= 0x0a ? chan : (chan == 0 ? 9 : chan - 1); synth->channel[chan]->channel_type = data[7] == 0x00 ? CHANNEL_TYPE_MELODIC : CHANNEL_TYPE_DRUM; FLUID_LOG(FLUID_DBG, "SysEx DT1: setting MIDI channel %d to type %d", chan, (int)synth->channel[chan]->channel_type); //Roland synths seem to "remember" the last instrument a channel //used in the selected mode. This behavior is not replicated here. fluid_synth_program_change(synth, chan, 0); } return FLUID_OK; } //silently ignore return FLUID_OK; } /* Handler for XG messages */ static int fluid_synth_sysex_xg(fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int avail_response, int *handled, int dryrun) { int addr; int len_data; if(len < 7) // at least one byte of data should be transmitted { return FLUID_FAILED; } len_data = len - 6; addr = (data[3] << 16) | (data[4] << 8) | data[5]; if (addr == 0x00007E // Reset || addr == 0x00007F) // Reset to factory { if (len_data > 1 || data[6] != 0) { return FLUID_FAILED; } if (handled) { *handled = TRUE; } if (!dryrun) { synth->bank_select = FLUID_BANK_STYLE_XG; return fluid_synth_system_reset_LOCAL(synth); } return FLUID_OK; } /* No other messages handled yet if (synth->bank_select != FLUID_BANK_STYLE_XG) { return FLUID_OK; // Silently ignore all other messages }*/ //silently ignore return FLUID_OK; } /** * Turn off all voices that are playing on the given MIDI channel, by putting 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 { /* Allowed (even for channel disabled) as chan = -1 selects all channels */ 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 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 voices on the given 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 { /* Allowed (even for channel disabled) as chan = -1 selects all channels */ 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, resets * controllers and restores initial basic channel configuration. * @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) { int i; fluid_synth_all_sounds_off_LOCAL(synth, -1); for(i = 0; i < synth->midi_channels; i++) { fluid_channel_reset(synth->channel[i]); } /* Basic channel 0, Mode Omni On Poly */ fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, synth->midi_channels); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 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); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(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 polyphonic key pressure controller value. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI key number (0-127) * @param val MIDI key pressure value (0-127) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 2.0.0 */ int fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val) { int result; fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); if(synth->verbose) { FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val); } fluid_channel_set_key_pressure(synth->channel[chan], key, val); result = fluid_synth_update_key_pressure_LOCAL(synth, chan, key); FLUID_API_RETURN(result); } /* Updates key pressure from within synthesis thread */ static int fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key) { fluid_voice_t *voice; int i; int result = FLUID_OK; for(i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if(voice->chan == chan && voice->key == key) { result = fluid_voice_modulate(voice, 0, FLUID_MOD_KEYPRESSURE); if(result != FLUID_OK) { return result; } } } return result; } /** * 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); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(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) { int result; fluid_return_val_if_fail(ppitch_bend != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); *ppitch_bend = fluid_channel_get_pitch_bend(synth->channel[chan]); result = FLUID_OK; FLUID_API_RETURN(result); } /** * 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); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(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) { int result; fluid_return_val_if_fail(pval != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); *pval = fluid_channel_get_pitch_wheel_sensitivity(synth->channel[chan]); result = FLUID_OK; FLUID_API_RETURN(result); } /** * 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. */ static fluid_preset_t * fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum, int banknum, int prognum) { fluid_sfont_t *sfont; fluid_list_t *list; /* 128 indicates an "unset" operation" */ if(prognum == FLUID_UNSET_PROGRAM) { return NULL; } for(list = synth->sfont; list; list = fluid_list_next(list)) { sfont = fluid_list_get(list); if(fluid_sfont_get_id(sfont) == sfontnum) { return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); } } return NULL; } /* Get a preset by SoundFont name, bank and program. * Returns preset pointer or NULL. */ static fluid_preset_t * fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname, int banknum, int prognum) { fluid_sfont_t *sfont; fluid_list_t *list; for(list = synth->sfont; list; list = fluid_list_next(list)) { sfont = fluid_list_get(list); if(FLUID_STRCMP(fluid_sfont_get_name(sfont), sfontname) == 0) { return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); } } return NULL; } /* Find a preset by bank and program numbers. * Returns preset pointer or NULL. */ fluid_preset_t * fluid_synth_find_preset(fluid_synth_t *synth, int banknum, int prognum) { fluid_preset_t *preset; fluid_sfont_t *sfont; fluid_list_t *list; for(list = synth->sfont; list; list = fluid_list_next(list)) { sfont = fluid_list_get(list); preset = fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); if(preset) { return preset; } } return NULL; } /** * 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 */ /* 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_FAILED; fluid_return_val_if_fail(prognum >= 0 && prognum <= 128, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(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, int bank) { int result; fluid_return_val_if_fail(bank <= 16383, FLUID_FAILED); fluid_return_val_if_fail(bank >= 0, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1); result = FLUID_OK; FLUID_API_RETURN(result); } /** * 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, int sfont_id) { int result; FLUID_API_ENTRY_CHAN(FLUID_FAILED); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); result = FLUID_OK; FLUID_API_RETURN(result); } /** * 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) { FLUID_API_ENTRY_CHAN(FLUID_FAILED); FLUID_API_RETURN(fluid_synth_program_change(synth, chan, FLUID_UNSET_PROGRAM)); } /** * 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, int *sfont_id, int *bank_num, int *preset_num) { int result; 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); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); channel = synth->channel[chan]; fluid_channel_get_sfont_bank_prog(channel, sfont_id, bank_num, 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; } result = FLUID_OK; FLUID_API_RETURN(result); } /** * 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, int sfont_id, int bank_num, int preset_num) { fluid_preset_t *preset = NULL; fluid_channel_t *channel; int result; fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); channel = synth->channel[chan]; 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); } /** * Pins all samples of the given preset. * * @param synth FluidSynth instance * @param sfont_id ID of a loaded SoundFont * @param bank_num MIDI bank number * @param preset_num MIDI program number * @return #FLUID_OK if the preset was found, pinned and loaded * into memory successfully. #FLUID_FAILED otherwise. Note that #FLUID_OK * is returned, even if synth.dynamic-sample-loading is disabled or * the preset doesn't support dynamic-sample-loading. * * This function will attempt to pin all samples of the given preset and * load them into memory, if they are currently unloaded. "To pin" in this * context means preventing them from being unloaded by an upcoming channel * prog change. * * @note This function is only useful if \ref settings_synth_dynamic-sample-loading is enabled. * By default, dynamic-sample-loading is disabled and all samples are kept in memory. * Furthermore, this is only useful for presets which support dynamic-sample-loading (currently, * only preset loaded with the default soundfont loader do). * * @since 2.2.0 */ int fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num) { int ret; fluid_preset_t *preset; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); fluid_synth_api_enter(synth); 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); } ret = fluid_preset_notify(preset, FLUID_PRESET_PIN, -1); // channel unused for pinning messages FLUID_API_RETURN(ret); } /** * Unpin all samples of the given preset. * * @param synth FluidSynth instance * @param sfont_id ID of a loaded SoundFont * @param bank_num MIDI bank number * @param preset_num MIDI program number * @return #FLUID_OK if preset was found, #FLUID_FAILED otherwise * * This function undoes the effect of fluid_synth_pin_preset(). If the preset is * not currently used, its samples will be unloaded. * * @note Only useful for presets loaded with the default soundfont loader and * only if \ref settings_synth_dynamic-sample-loading is enabled. * * @since 2.2.0 */ int fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num) { int ret; fluid_preset_t *preset; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); fluid_synth_api_enter(synth); 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); } ret = fluid_preset_notify(preset, FLUID_PRESET_UNPIN, -1); // channel unused for pinning messages FLUID_API_RETURN(ret); } /** * 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, int bank_num, 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); /* Allowed only on MIDI channel enabled */ FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); channel = synth->channel[chan]; 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); } } static void fluid_synth_set_sample_rate_LOCAL(fluid_synth_t *synth, float sample_rate) { int i; fluid_clip(sample_rate, 8000.0f, 96000.0f); synth->sample_rate = sample_rate; synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); for(i = 0; i < synth->polyphony; i++) { fluid_voice_set_output_rate(synth->voice[i], sample_rate); } } /** * Set up an event to change the sample-rate of the synth during the next rendering call. * @warning This function is broken-by-design! Don't use it! Instead, specify the sample-rate when creating the synth. * @deprecated As of fluidsynth 2.1.0 this function has been deprecated. * Changing the sample-rate is generally not considered to be a real-time use-case, as it always produces some audible artifact ("click", "pop") on the dry sound and effects (because LFOs for chorus and reverb need to be reinitialized). * The sample-rate change may also require memory allocation deep down in the effect units. * However, this memory allocation may fail and there is no way for the caller to know that, because the actual change of the sample-rate is executed during rendering. * This function cannot (must not) do the sample-rate change itself, otherwise the synth needs to be locked down, causing rendering to block. * Esp. do not use this function if this @p synth instance is used by an audio driver, because the audio driver cannot be notified by this sample-rate change. * Long story short: don't use it. * @code{.cpp} fluid_synth_t* synth; // assume initialized // [...] // sample-rate change needed? Delete the audio driver, if any. delete_fluid_audio_driver(adriver); // then delete the synth delete_fluid_synth(synth); // update the sample-rate fluid_settings_setnum(settings, "synth.sample-rate", 22050.0); // and re-create objects synth = new_fluid_synth(settings); adriver = new_fluid_audio_driver(settings, synth); * @endcode * @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) { fluid_return_if_fail(synth != NULL); fluid_synth_api_enter(synth); fluid_synth_set_sample_rate_LOCAL(synth, sample_rate); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate, 0, synth->sample_rate); fluid_synth_api_exit(synth); } // internal sample rate change function for the jack driver // executes immediately, therefore, make sure no rendering call is running! void fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate) { fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; fluid_return_if_fail(synth != NULL); fluid_synth_api_enter(synth); fluid_synth_set_sample_rate_LOCAL(synth, sample_rate); param[0].i = 0; param[1].real = synth->sample_rate; fluid_rvoice_mixer_set_samplerate(synth->eventhandler->mixer, param); fluid_synth_api_exit(synth); } /* Handler for synth.gain setting. */ static void fluid_synth_handle_gain(void *data, const char *name, double value) { fluid_synth_t *synth = (fluid_synth_t *)data; fluid_synth_set_gain(synth, (float) value); } /** * 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 void fluid_synth_handle_polyphony(void *data, const char *name, int value) { fluid_synth_t *synth = (fluid_synth_t *)data; fluid_synth_set_polyphony(synth, value); } /** * 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->eventhandler, synth->sample_rate); if(synth->voice[i] == NULL) { return FLUID_FAILED; } fluid_voice_set_custom_filter(synth->voice[i], synth->custom_filter_type, synth->custom_filter_flags); } 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); } /** * @brief Get current number of active voices. * * I.e. the no. of voices that have been * started and have not yet finished. Unless called from synthesis context, * this number does not necessarily have to be equal to the number of voices * currently processed by the DSP loop, see below. * @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. Otherwise every call to this function may return different * voice counts as it may change after any (concurrent) call to fluid_synth_write_*() made by * e.g. an audio driver or the applications audio rendering thread. */ 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 (multi-channel rendering). * * @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_FAILED otherwise * * First effect channel used by reverb, second for chorus. * * @note Should only be called from synthesis thread. * * @deprecated fluid_synth_nwrite_float() is deprecated and will be removed in a future release. * It may continue to work or it may return #FLUID_FAILED in the future. Consider using the more * powerful and flexible fluid_synth_process(). * * Usage example: * @code{.cpp} 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; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(left != NULL, FLUID_FAILED); fluid_return_val_if_fail(right != NULL, FLUID_FAILED); fluid_return_val_if_fail(len >= 0, FLUID_FAILED); fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below /* 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 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); FLUID_MEMCPY(right[i], &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); #else //WITH_FLOAT int j; for(j = 0; j < num; j++) { left[i][j] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; right[i][j] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + 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 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); } if(fx_right != NULL) { FLUID_MEMCPY(fx_right[i], &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + 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 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; } } if(fx_right != NULL) { for(j = 0; j < num; j++) { fx_right[i][j] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + 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 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); FLUID_MEMCPY(right[i] + count, &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); #else //WITH_FLOAT int j; for(j = 0; j < num; j++) { left[i][j + count] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; right[i][j + count] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + 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 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); } if(fx_right != NULL) { FLUID_MEMCPY(fx_right[i] + count, &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], 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 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; } } if(fx_right != NULL) { for(j = 0; j < num; j++) { fx_right[i][j + count] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; } } #endif //WITH_FLOAT } count += num; } synth->cur = num; time = fluid_utime() - time; cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); fluid_atomic_float_set(&synth->cpu_load, cpu_load); return FLUID_OK; } /** * mixes the samples of \p in to \p out * * @param out the output sample buffer to mix to * @param ooff sample offset in \p out * @param in the rvoice_mixer input sample buffer to mix from * @param ioff sample offset in \p in * @param buf_idx the sample buffer index of \p in to mix from * @param num number of samples to mix */ static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out, int ooff, const fluid_real_t *FLUID_RESTRICT in, int ioff, int buf_idx, int num) { if(out != NULL) { int j; for(j = 0; j < num; j++) { out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff]; } } } /** * Synthesize floating point audio to stereo audio channels * (implements the default interface #fluid_audio_func_t). * * @param synth FluidSynth instance * * @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx. * Zero value is permitted, the function does nothing and return FLUID_OK. * * @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo) * and in the range 0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). * Note that zero value is valid and allows to skip mixing effects in all fx output buffers. * * @param fx Array of buffers to store effects audio to. Buffers may * alias with buffers of \c out. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer. * * @param nout Count of arrays in \c out. Must be a multiple of 2 * (because of stereo) and in the range 0 <= nout/2 <= fluid_synth_count_audio_channels(). * Note that zero value is valid and allows to skip mixing dry audio in all out output buffers. * * @param out Array of buffers to store (dry) audio to. Buffers may * alias with buffers of \c fx. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer. * * @return #FLUID_OK on success, * #FLUID_FAILED otherwise, * - fx == NULL while nfx > 0, or out == NULL while nout > 0. * - \c nfx or \c nout not multiple of 2. * - len < 0. * - \c nfx or \c nout exceed the range explained above. * * Synthesize and mix audio to a given number of planar audio buffers. * Therefore pass nout = N*2 float buffers to \p out in order to render * the synthesized audio to \p N stereo channels. Each float buffer must be * able to hold \p len elements. * * \p out contains an array of planar buffers for normal, dry, stereo * audio (alternating left and right). Like: @code{.cpp} out[0] = left_buffer_audio_channel_0 out[1] = right_buffer_audio_channel_0 out[2] = left_buffer_audio_channel_1 out[3] = right_buffer_audio_channel_1 ... out[ (i * 2 + 0) % nout ] = left_buffer_audio_channel_i out[ (i * 2 + 1) % nout ] = right_buffer_audio_channel_i @endcode * * for zero-based channel index \p i. * The buffer layout of \p fx used for storing effects * like reverb and chorus looks similar: @code{.cpp} fx[0] = left_buffer_channel_of_reverb_unit_0 fx[1] = right_buffer_channel_of_reverb_unit_0 fx[2] = left_buffer_channel_of_chorus_unit_0 fx[3] = right_buffer_channel_of_chorus_unit_0 fx[4] = left_buffer_channel_of_reverb_unit_1 fx[5] = right_buffer_channel_of_reverb_unit_1 fx[6] = left_buffer_channel_of_chorus_unit_1 fx[7] = right_buffer_channel_of_chorus_unit_1 fx[8] = left_buffer_channel_of_reverb_unit_2 ... fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 0) % nfx ] = left_buffer_for_effect_channel_j_of_unit_k fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ] = right_buffer_for_effect_channel_j_of_unit_k @endcode * where 0 <= k < fluid_synth_count_effects_groups() is a zero-based index denoting the effects unit and * 0 <= j < fluid_synth_count_effects_channels() is a zero-based index denoting the effect channel within * unit \p k. * * Any playing voice is assigned to audio channels based on the MIDI channel it's playing on: Let \p chan be the * zero-based MIDI channel index an arbitrary voice is playing on. To determine the audio channel and effects unit it is * going to be rendered to use: * * i = chan % fluid_synth_count_audio_groups() * * k = chan % fluid_synth_count_effects_groups() * * @parblock * @note The owner of the sample buffers must zero them out before calling this * function, because any synthesized audio is mixed (i.e. added) to the buffers. * E.g. if fluid_synth_process() is called from a custom audio driver process function * (see new_fluid_audio_driver2()), the audio driver takes care of zeroing the buffers. * @endparblock * * @parblock * @note No matter how many buffers you pass in, fluid_synth_process() * will always render all audio channels to the * buffers in \c out and all effects channels to the * buffers in \c fx, provided that nout > 0 and nfx > 0 respectively. If * nout/2 < fluid_synth_count_audio_channels() it will wrap around. Same * is true for effects audio if nfx/2 < (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). * See usage examples below. * @endparblock * * @parblock * @note Should only be called from synthesis thread. * @endparblock */ int fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], int nout, float *out[]) { return fluid_synth_process_LOCAL(synth, len, nfx, fx, nout, out, fluid_synth_render_blocks); } /* declared public (instead of static) for testing purpose */ int fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)) { fluid_real_t *left_in, *fx_left_in; fluid_real_t *right_in, *fx_right_in; int nfxchan, nfxunits, naudchan; double time = fluid_utime(); int i, f, num, count, buffered_blocks; float cpu_load; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); /* fx NULL while nfx > 0 is invalid */ fluid_return_val_if_fail((fx != NULL) || (nfx == 0), FLUID_FAILED); /* nfx must be multiple of 2. Note that 0 value is valid and allows to skip mixing in fx output buffers */ fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED); /* out NULL while nout > 0 is invalid */ fluid_return_val_if_fail((out != NULL) || (nout == 0), FLUID_FAILED); /* nout must be multiple of 2. Note that 0 value is valid and allows to skip mixing in out output buffers */ fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED); /* check len value. Note that 0 value is valid, the function does nothing and returns FLUID_OK. */ fluid_return_val_if_fail(len >= 0, FLUID_FAILED); fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below nfxchan = synth->effects_channels; nfxunits = synth->effects_groups; naudchan = synth->audio_channels; fluid_return_val_if_fail(0 <= nfx / 2 && nfx / 2 <= nfxchan * nfxunits, FLUID_FAILED); fluid_return_val_if_fail(0 <= nout / 2 && nout / 2 <= naudchan, FLUID_FAILED); /* get internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); /* get internal mixer audio effect buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); /* Conversely to fluid_synth_write_float(),fluid_synth_write_s16() (which handle only one stereo output) we don't want rendered audio effect mixed in internal audio dry buffers. FALSE instructs the mixer that internal audio effects will be mixed in respective internal audio effects buffers. */ fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, FALSE); /* First, take what's still available in the buffer */ count = 0; /* synth->cur indicates if available samples are still in internal mixer buffer */ num = synth->cur; buffered_blocks = (synth->cur + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; if(synth->cur < buffered_blocks * FLUID_BUFSIZE) { /* yes, available sample are in internal mixer buffer */ int available = (buffered_blocks * FLUID_BUFSIZE) - synth->cur; num = (available > len) ? len : available; /* mixing dry samples (or skip if requested by the caller) */ if(nout != 0) { for(i = 0; i < naudchan; i++) { /* mix num left samples from input mixer buffer (left_in) at input offset synth->cur to output buffer (out_buf) at offset 0 */ float *out_buf = out[(i * 2) % nout]; fluid_synth_mix_single_buffer(out_buf, 0, left_in, synth->cur, i, num); /* mix num right samples from input mixer buffer (right_in) at input offset synth->cur to output buffer (out_buf) at offset 0 */ out_buf = out[(i * 2 + 1) % nout]; fluid_synth_mix_single_buffer(out_buf, 0, right_in, synth->cur, i, num); } } /* mixing effects samples (or skip if requested by the caller) */ if(nfx != 0) { // loop over all effects units for(f = 0; f < nfxunits; f++) { // write out all effects (i.e. reverb and chorus) for(i = 0; i < nfxchan; i++) { int buf_idx = f * nfxchan + i; /* mix num left samples from input mixer buffer (fx_left_in) at input offset synth->cur to output buffer (out_buf) at offset 0 */ float *out_buf = fx[(buf_idx * 2) % nfx]; fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num); /* mix num right samples from input mixer buffer (fx_right_in) at input offset synth->cur to output buffer (out_buf) at offset 0 */ out_buf = fx[(buf_idx * 2 + 1) % nfx]; fluid_synth_mix_single_buffer(out_buf, 0, fx_right_in, synth->cur, buf_idx, num); } } } count += num; num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ } /* Then, render blocks and copy till we have 'len' samples */ while(count < len) { /* always render full bloc multiple of FLUID_BUFSIZE */ int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; /* render audio (dry and effect) to respective internal dry and effect buffers */ int blockcount = block_render_func(synth, blocksleft); num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE; /* mixing dry samples (or skip if requested by the caller) */ if(nout != 0) { for(i = 0; i < naudchan; i++) { /* mix num left samples from input mixer buffer (left_in) at input offset 0 to output buffer (out_buf) at offset count */ float *out_buf = out[(i * 2) % nout]; fluid_synth_mix_single_buffer(out_buf, count, left_in, 0, i, num); /* mix num right samples from input mixer buffer (right_in) at input offset 0 to output buffer (out_buf) at offset count */ out_buf = out[(i * 2 + 1) % nout]; fluid_synth_mix_single_buffer(out_buf, count, right_in, 0, i, num); } } /* mixing effects samples (or skip if requested by the caller) */ if(nfx != 0) { // loop over all effects units for(f = 0; f < nfxunits; f++) { // write out all effects (i.e. reverb and chorus) for(i = 0; i < nfxchan; i++) { int buf_idx = f * nfxchan + i; /* mix num left samples from input mixer buffer (fx_left_in) at input offset 0 to output buffer (out_buf) at offset count */ float *out_buf = fx[(buf_idx * 2) % nfx]; fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num); /* mix num right samples from input mixer buffer (fx_right_in) at input offset 0 to output buffer (out_buf) at offset count */ out_buf = fx[(buf_idx * 2 + 1) % nfx]; fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num); } } } count += num; } synth->cur = num; time = fluid_utime() - time; cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); fluid_atomic_float_set(&synth->cpu_load, cpu_load); return FLUID_OK; } /** * Synthesize a block of floating point audio samples to audio buffers. * @param synth FluidSynth instance * @param len Count of audio frames to synthesize * @param lout Array of floats 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 floats 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_FAILED 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. */ int fluid_synth_write_float(fluid_synth_t *synth, int len, void *lout, int loff, int lincr, void *rout, int roff, int rincr) { void *channels_out[2] = {lout, rout}; int channels_off[2] = {loff, roff }; int channels_incr[2] = {lincr, rincr }; return fluid_synth_write_float_channels(synth, len, 2, channels_out, channels_off, channels_incr); } /** * Synthesize a block of float audio samples channels to audio buffers. * The function is convenient for audio driver to render multiple stereo * channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels). * * @param synth FluidSynth instance. * @param len Count of audio frames to synthesize. * @param channels_count Count of channels in a frame. * must be multiple of 2 and channel_count/2 must not exceed the number * of internal mixer buffers (synth->audio_groups) * @param channels_out Array of channels_count pointers on 16 bit words to * store sample channels. Modified on return. * @param channels_off Array of channels_count offset index to add to respective pointer * in channels_out for first sample. * @param channels_incr Array of channels_count increment between consecutive * samples channels. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. * * Useful for storing: * - interleaved channels in a unique buffer. * - non interleaved channels in an unique buffer (or in distinct buffers). * * Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn) * in a unique buffer: * { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,... * sn:c1, sn:c2, sn:c3, sn:c4 }. * * @note Should only be called from synthesis thread. * @note Reverb and Chorus are mixed to \c lout resp. \c rout. */ int fluid_synth_write_float_channels(fluid_synth_t *synth, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]) { return fluid_synth_write_float_channels_LOCAL(synth, len, channels_count, channels_out, channels_off, channels_incr, fluid_synth_render_blocks); } int fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[], int (*block_render_func)(fluid_synth_t *, int)) { float **chan_out = (float **)channels_out; int n, cur, size; /* pointers on first input mixer buffer */ fluid_real_t *left_in; fluid_real_t *right_in; int bufs_in_count; /* number of stereo input buffers */ int i; /* start average cpu load probe */ double time = fluid_utime(); float cpu_load; /* start profiling duration probe (if profiling is enabled) */ fluid_profile_ref_var(prof_ref); /* check parameters */ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(len >= 0, FLUID_FAILED); fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below /* check for valid channel_count: must be multiple of 2 and channel_count/2 must not exceed the number of internal mixer buffers (synth->audio_groups) */ fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */ && channels_count >= 2, FLUID_FAILED); bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */ fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED); fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED); fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED); fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED); /* initialize output channels buffers on first sample position */ i = channels_count; do { i--; chan_out[i] += channels_off[i]; } while(i); /* Conversely to fluid_synth_process(), we want rendered audio effect mixed in internal audio dry buffers. TRUE instructs the mixer that internal audio effects will be mixed in internal audio dry buffers. */ fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE); /* get first internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); size = len; /* synth->cur indicates if available samples are still in internal mixer buffer */ cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */ do { /* fill up the buffers as needed */ if(cur >= synth->curmax) { /* render audio (dry and effect) to internal dry buffers */ /* always render full blocs multiple of FLUID_BUFSIZE */ int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft); /* get first internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); cur = 0; } /* calculate amount of available samples */ n = synth->curmax - cur; /* keep track of emitted samples */ if(n > size) { n = size; } size -= n; /* update pointers to current position */ left_in += cur + n; right_in += cur + n; /* set final cursor position */ cur += n; /* reverse index */ n = 0 - n; do { i = bufs_in_count; do { /* input sample index in stereo buffer i */ int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n; int c = i << 1; /* channel index c to write */ /* write left input sample to channel sample */ *chan_out[c] = (float) left_in[in_idx]; /* write right input sample to next channel sample */ *chan_out[c+1] = (float) right_in[in_idx]; /* advance output pointers */ chan_out[c] += channels_incr[c]; chan_out[c+1] += channels_incr[c+1]; } while(i); } while(++n < 0); } while(size); synth->cur = cur; /* save current sample position. It will be used on next call */ /* save average cpu load, use by API for real time cpu load meter */ time = fluid_utime() - time; cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); fluid_atomic_float_set(&synth->cpu_load, cpu_load); /* stop duration probe and save performance measurement (if profiling is enabled) */ fluid_profile_write(FLUID_PROF_WRITE, prof_ref, fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), len); return FLUID_OK; } /* for testing purpose */ int fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, void *lout, int loff, int lincr, void *rout, int roff, int rincr, int (*block_render_func)(fluid_synth_t *, int) ) { void *channels_out[2] = {lout, rout}; int channels_off[2] = {loff, roff }; int channels_incr[2] = {lincr, rincr }; return fluid_synth_write_float_channels_LOCAL(synth, len, 2, channels_out, channels_off, channels_incr, block_render_func); } #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 FLUID_INLINE int16_t round_clip_to_i16(float x) { long i; if(x >= 0.0f) { i = (long)(x + 0.5f); if(FLUID_UNLIKELY(i > 32767)) { i = 32767; } } else { i = (long)(x - 0.5f); if(FLUID_UNLIKELY(i < -32768)) { i = -32768; } } return (int16_t)i; } /** * 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_FAILED 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) { void *channels_out[2] = {lout, rout}; int channels_off[2] = {loff, roff }; int channels_incr[2] = {lincr, rincr }; return fluid_synth_write_s16_channels(synth, len, 2, channels_out, channels_off, channels_incr); } /** * Synthesize a block of 16 bit audio samples channels to audio buffers. * The function is convenient for audio driver to render multiple stereo * channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels). * * @param synth FluidSynth instance. * @param len Count of audio frames to synthesize. * @param channels_count Count of channels in a frame. * must be multiple of 2 and channel_count/2 must not exceed the number * of internal mixer buffers (synth->audio_groups) * @param channels_out Array of channels_count pointers on 16 bit words to * store sample channels. Modified on return. * @param channels_off Array of channels_count offset index to add to respective pointer * in channels_out for first sample. * @param channels_incr Array of channels_count increment between consecutive * samples channels. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. * * Useful for storing: * - interleaved channels in a unique buffer. * - non interleaved channels in an unique buffer (or in distinct buffers). * * Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn) * in a unique buffer: * { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4, .... * sn:c1, sn:c2, sn:c3, sn:c4 }. * * @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_channels(fluid_synth_t *synth, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]) { int16_t **chan_out = (int16_t **)channels_out; int di, n, cur, size; /* pointers on first input mixer buffer */ fluid_real_t *left_in; fluid_real_t *right_in; int bufs_in_count; /* number of stereo input buffers */ int i; /* start average cpu load probe */ double time = fluid_utime(); float cpu_load; /* start profiling duration probe (if profiling is enabled) */ fluid_profile_ref_var(prof_ref); /* check parameters */ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(len >= 0, FLUID_FAILED); fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below /* check for valid channel_count: must be multiple of 2 and channel_count/2 must not exceed the number of internal mixer buffers (synth->audio_groups) */ fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */ && channels_count >= 2, FLUID_FAILED); bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */ fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED); fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED); fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED); fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED); /* initialize output channels buffers on first sample position */ i = channels_count; do { i--; chan_out[i] += channels_off[i]; } while(i); /* Conversely to fluid_synth_process(), we want rendered audio effect mixed in internal audio dry buffers. TRUE instructs the mixer that internal audio effects will be mixed in internal audio dry buffers. */ fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE); /* get first internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); size = len; /* synth->cur indicates if available samples are still in internal mixer buffer */ cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */ di = synth->dither_index; do { /* fill up the buffers as needed */ if(cur >= synth->curmax) { /* render audio (dry and effect) to internal dry buffers */ /* always render full blocs multiple of FLUID_BUFSIZE */ int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); /* get first internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); cur = 0; } /* calculate amount of available samples */ n = synth->curmax - cur; /* keep track of emitted samples */ if(n > size) { n = size; } size -= n; /* update pointers to current position */ left_in += cur + n; right_in += cur + n; /* set final cursor position */ cur += n; /* reverse index */ n = 0 - n; do { i = bufs_in_count; do { /* input sample index in stereo buffer i */ int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n; int c = i << 1; /* channel index c to write */ /* write left input sample to channel sample */ *chan_out[c] = round_clip_to_i16(left_in[in_idx] * 32766.0f + rand_table[0][di]); /* write right input sample to next channel sample */ *chan_out[c+1] = round_clip_to_i16(right_in[in_idx] * 32766.0f + rand_table[1][di]); /* advance output pointers */ chan_out[c] += channels_incr[c]; chan_out[c+1] += channels_incr[c+1]; } while(i); if(++di >= DITHER_SIZE) { di = 0; } } while(++n < 0); } while(size); synth->cur = cur; /* save current sample position. It will be used on next call */ synth->dither_index = di; /* keep dither buffer continuous */ /* save average cpu load, used by API for real time cpu load meter */ time = fluid_utime() - time; cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); fluid_atomic_float_set(&synth->cpu_load, cpu_load); /* stop duration probe and save performance measurement (if profiling is enabled) */ fluid_profile_write(FLUID_PROF_WRITE, prof_ref, fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), len); return FLUID_OK; } /** * 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, const float *lin, const float *rin, void *lout, int loff, int lincr, void *rout, int roff, int rincr) { int i, j, k; int16_t *left_out = lout; int16_t *right_out = rout; int di = *dither_index; fluid_profile_ref_var(prof_ref); for(i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) { left_out[j] = round_clip_to_i16(lin[i] * 32766.0f + rand_table[0][di]); right_out[k] = round_clip_to_i16(rin[i] * 32766.0f + rand_table[1][di]); if(++di >= DITHER_SIZE) { di = 0; } } *dither_index = di; /* keep dither buffer continuous */ fluid_profile(FLUID_PROF_WRITE, prof_ref, 0, len); } 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) { /* Unlock the overflow_rvoice of the voice. Decrement the reference count of the sample owned by this rvoice. */ fluid_voice_overflow_rvoice_finished(synth->voice[j]); /* Decrement synth active voice count. Must not be incorporated in fluid_voice_overflow_rvoice_finished() because fluid_voice_overflow_rvoice_finished() is called also at synth destruction and in this case the variable should be accessed via voice->channel->synth->active_voice_count. And for certain voices which are not playing, the field voice->channel is NULL. */ synth->active_voice_count--; break; } } } } /** * 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, fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), blockcount * FLUID_BUFSIZE); return blockcount; } /* * Handler for synth.reverb.* and synth.chorus.* double settings. */ static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value) { fluid_synth_t *synth = (fluid_synth_t *)data; fluid_return_if_fail(synth != NULL); if(FLUID_STRCMP(name, "synth.reverb.room-size") == 0) { fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, value); } else if(FLUID_STRCMP(name, "synth.reverb.damp") == 0) { fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, value); } else if(FLUID_STRCMP(name, "synth.reverb.width") == 0) { fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, value); } else if(FLUID_STRCMP(name, "synth.reverb.level") == 0) { fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, value); } else if(FLUID_STRCMP(name, "synth.chorus.depth") == 0) { fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, value); } else if(FLUID_STRCMP(name, "synth.chorus.speed") == 0) { fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, value); } else if(FLUID_STRCMP(name, "synth.chorus.level") == 0) { fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, value); } } /* * Handler for synth.reverb.* and synth.chorus.* integer settings. */ static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value) { fluid_synth_t *synth = (fluid_synth_t *)data; fluid_return_if_fail(synth != NULL); if(FLUID_STRCMP(name, "synth.reverb.active") == 0) { fluid_synth_reverb_on(synth, -1, value); } else if(FLUID_STRCMP(name, "synth.chorus.active") == 0) { fluid_synth_chorus_on(synth, -1, value); } else if(FLUID_STRCMP(name, "synth.chorus.nr") == 0) { fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, (double)value); } } /* * Handler for synth.overflow.* settings. */ static void fluid_synth_handle_overflow(void *data, const char *name, double value) { fluid_synth_t *synth = (fluid_synth_t *)data; fluid_return_if_fail(synth != NULL); fluid_synth_api_enter(synth); if(FLUID_STRCMP(name, "synth.overflow.percussion") == 0) { synth->overflow.percussion = value; } else if(FLUID_STRCMP(name, "synth.overflow.released") == 0) { synth->overflow.released = value; } else if(FLUID_STRCMP(name, "synth.overflow.sustained") == 0) { synth->overflow.sustained = value; } else if(FLUID_STRCMP(name, "synth.overflow.volume") == 0) { synth->overflow.volume = value; } else if(FLUID_STRCMP(name, "synth.overflow.age") == 0) { synth->overflow.age = value; } else if(FLUID_STRCMP(name, "synth.overflow.important") == 0) { synth->overflow.important = value; } fluid_synth_api_exit(synth); } /* Selects a voice for killing. */ static fluid_voice_t * fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth) { int i; float best_prio = OVERFLOW_PRIO_CANNOT_KILL - 1; float 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) { fluid_return_val_if_fail(sample != NULL, NULL); fluid_return_val_if_fail(sample->data != NULL, NULL); FLUID_API_ENTRY_CHAN(NULL); FLUID_API_RETURN(fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, NULL)); } fluid_voice_t * fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range) { int i, k; fluid_voice_t *voice = NULL; fluid_channel_t *channel = NULL; unsigned int ticks; /* 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); 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); } channel = synth->channel[chan]; if(fluid_voice_init(voice, sample, zone_range, channel, key, vel, synth->storeid, ticks, synth->gain) != FLUID_OK) { FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); return NULL; } /* add the default modulators to the synthesis process. */ /* custom_breath2att_modulator is not a default modulator specified in SF it is intended to replace default_vel2att_mod for this channel on demand using API fluid_synth_set_breath_mode() or shell command setbreathmode for this channel. */ { int mono = fluid_channel_is_playing_mono(channel); fluid_mod_t *default_mod = synth->default_mod; while(default_mod != NULL) { if( /* See if default_mod is the velocity_to_attenuation modulator */ fluid_mod_test_identity(default_mod, &default_vel2att_mod) && // See if a replacement by custom_breath2att_modulator has been demanded // for this channel ((!mono && (channel->mode & FLUID_CHANNEL_BREATH_POLY)) || (mono && (channel->mode & FLUID_CHANNEL_BREATH_MONO))) ) { // Replacement of default_vel2att modulator by custom_breath2att_modulator fluid_voice_add_mod_local(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT, 0); } else { fluid_voice_add_mod_local(voice, default_mod, FLUID_VOICE_DEFAULT, 0); } // Next default modulator to add to the voice default_mod = default_mod->next; } } 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 = fluid_voice_gen_value(new_voice, GEN_EXCLUSIVECLASS); 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++) { fluid_voice_t *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) && fluid_voice_gen_value(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 */ 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) { fluid_return_if_fail(synth != NULL); fluid_return_if_fail(loader != NULL); fluid_synth_api_enter(synth); /* Test if sfont is already loaded */ if(synth->sfont == NULL) { 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 * * @note Since FluidSynth 2.2.0 @c filename is treated as an UTF8 encoded string on Windows. FluidSynth will convert it * to wide-char internally and then pass it to _wfopen(). Before FluidSynth 2.2.0, @c filename was treated as ANSI string * on Windows. All other platforms directly pass it to fopen() without any conversion (usually, UTF8 is accepted). */ int fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets) { fluid_sfont_t *sfont; fluid_list_t *list; fluid_sfloader_t *loader; 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); sfont_id = synth->sfont_id; if(++sfont_id != FLUID_FAILED) { /* 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->refcount++; synth->sfont_id = sfont->id = sfont_id; synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */ /* reset the presets for all channels if requested */ if(reset_presets) { fluid_synth_program_reset(synth); } FLUID_API_RETURN(sfont_id); } } } FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); FLUID_API_RETURN(FLUID_FAILED); } /** * Schedule a SoundFont for unloading. * * If the SoundFont isn't used anymore by any playing voices, it will be unloaded immediately. * * If any samples of the given SoundFont are still required by active voices, * the SoundFont will be unloaded in a lazy manner, once those voices have finished synthesizing. * If you call delete_fluid_synth(), all voices will be destroyed and the SoundFont * will be unloaded in any case. * Once this function returned, fluid_synth_sfcount() and similar functions will behave as if * the SoundFont has already been unloaded, even though the lazy-unloading is still pending. * * @note This lazy-unloading mechanism was broken between FluidSynth 1.1.4 and 2.1.5 . As a * consequence, SoundFonts scheduled for lazy-unloading may be never freed under certain * conditions. Calling delete_fluid_synth() does not recover this situation either. * * @param synth FluidSynth instance * @param id ID of SoundFont to unload * @param reset_presets TRUE to re-assign presets for all MIDI channels * @return #FLUID_OK if the given @p id was found, #FLUID_FAILED otherwise. */ int fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets) { fluid_sfont_t *sfont = 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; list; list = fluid_list_next(list)) { sfont = fluid_list_get(list); if(fluid_sfont_get_id(sfont) == id) { synth->sfont = fluid_list_remove(synth->sfont, sfont); 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 list's reference to SoundFont */ fluid_synth_sfont_unref(synth, 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_return_if_fail(sfont != NULL); /* Shouldn't happen, programming error if so */ sfont->refcount--; /* -- Remove the sfont list's reference */ if(sfont->refcount == 0) /* No more references? - Attempt delete */ { if(fluid_sfont_delete_internal(sfont) == 0) /* SoundFont loader can block SoundFont unload */ { FLUID_LOG(FLUID_DBG, "Unloaded SoundFont"); } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */ else { fluid_timer_t* timer = new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, FALSE, FALSE); synth->fonts_to_be_unloaded = fluid_list_prepend(synth->fonts_to_be_unloaded, timer); } } } /* 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_t *sfont = data; if(fluid_sfont_delete_internal(sfont) == 0) { 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, int id) { char *filename = NULL; fluid_sfont_t *sfont; fluid_sfloader_t *loader; fluid_list_t *list; int index, ret = FLUID_FAILED; 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, index = 0; list; list = fluid_list_next(list), index++) { sfont = fluid_list_get(list); if(fluid_sfont_get_id(sfont) == id) { break; } } if(!list) { FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); goto exit; } /* keep a copy of the SoundFont's filename */ filename = FLUID_STRDUP(fluid_sfont_get_name(sfont)); if(filename == NULL || fluid_synth_sfunload(synth, id, FALSE) != FLUID_OK) { goto exit; } /* 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->refcount++; synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont); /* insert the sfont at the same index */ /* reset the presets for all channels */ fluid_synth_update_presets(synth); ret = id; goto exit; } } FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); exit: FLUID_FREE(filename); FLUID_API_RETURN(ret); } /** * 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) { 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_id = synth->sfont_id; if(++sfont_id != FLUID_FAILED) { synth->sfont_id = sfont->id = sfont_id; synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */ /* 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 * @return #FLUID_OK if \c sfont successfully removed, #FLUID_FAILED otherwise * * 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. */ int fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont) { fluid_sfont_t *sfont_tmp; fluid_list_t *list; int ret = FLUID_FAILED; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); /* remove the SoundFont from the list */ for(list = synth->sfont; list; list = fluid_list_next(list)) { sfont_tmp = fluid_list_get(list); if(sfont_tmp == sfont) { synth->sfont = fluid_list_remove(synth->sfont, sfont_tmp); ret = FLUID_OK; break; } } /* reset the presets for all channels */ fluid_synth_program_reset(synth); FLUID_API_RETURN(ret); } /** * 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); 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, num); if(list) { sfont = fluid_list_get(list); } 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, 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; list; list = fluid_list_next(list)) { sfont = fluid_list_get(list); 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; list; list = fluid_list_next(list)) { sfont = fluid_list_get(list); 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 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 chorus, FALSE to disable * @deprecated Use fluid_synth_reverb_on() instead. */ void fluid_synth_set_reverb_on(fluid_synth_t *synth, int on) { fluid_return_if_fail(synth != NULL); fluid_synth_api_enter(synth); synth->with_reverb = (on != 0); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled, on != 0, 0.0f); fluid_synth_api_exit(synth); } /** * Enable or disable reverb on one fx group unit. * @param synth FluidSynth instance * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all fx groups. * @param on TRUE to enable reverb, FALSE to disable * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on) { int ret; fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); if(fx_group < -1 || fx_group >= synth->effects_groups) { FLUID_API_RETURN(FLUID_FAILED); } if(fx_group < 0 ) { synth->with_reverb = (on != 0); } param[0].i = fx_group; param[1].i = on; ret = fluid_rvoice_eventhandler_push(synth->eventhandler, fluid_rvoice_mixer_reverb_enable, synth->eventhandler->mixer, param); FLUID_API_RETURN(ret); } /** * 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, unsigned int num) { double values[FLUID_REVERB_PARAM_LAST]; fluid_return_val_if_fail( num < FLUID_N_ELEMENTS(revmodel_preset), FLUID_FAILED ); values[FLUID_REVERB_ROOMSIZE] = revmodel_preset[num].roomsize; values[FLUID_REVERB_DAMP] = revmodel_preset[num].damp; values[FLUID_REVERB_WIDTH] = revmodel_preset[num].width; values[FLUID_REVERB_LEVEL] = revmodel_preset[num].level; fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); return FLUID_OK; } /** * Set reverb parameters to all groups. * * @param synth FluidSynth instance * @param roomsize Reverb room size value (0.0-1.0) * @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 * @deprecated Use the individual reverb setter functions in new code instead. */ int fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, double damping, double width, double level) { double values[FLUID_REVERB_PARAM_LAST]; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); values[FLUID_REVERB_ROOMSIZE] = roomsize; values[FLUID_REVERB_DAMP] = damping; values[FLUID_REVERB_WIDTH] = width; values[FLUID_REVERB_LEVEL] = level; return fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); } /** * Set reverb roomsize of all groups. * * @param synth FluidSynth instance * @param roomsize Reverb room size value (0.0-1.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use fluid_synth_set_reverb_group_roomsize() in new code instead. */ int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize) { return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, roomsize); } /** * Set reverb damping of all groups. * * @param synth FluidSynth instance * @param damping Reverb damping value (0.0-1.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use fluid_synth_set_reverb_group_damp() in new code instead. */ int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping) { return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, damping); } /** * Set reverb width of all groups. * * @param synth FluidSynth instance * @param width Reverb width value (0.0-100.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use fluid_synth_set_reverb_group_width() in new code instead. */ int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width) { return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, width); } /** * Set reverb level of all groups. * * @param synth FluidSynth instance * @param level Reverb level value (0.0-1.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use fluid_synth_set_reverb_group_level() in new code instead. */ int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level) { return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, level); } /** * Set reverb roomsize to one or all fx groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all fx groups. * @param roomsize roomsize value to set. Must be in the range indicated by * synth.reverb.room-size setting. * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double roomsize) { return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize); } /** * Set reverb damp to one or all fx groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all fx groups. * @param damping damping value to set. Must be in the range indicated by * synth.reverb.damp setting. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group, double damping) { return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_DAMP, damping); } /** * Set reverb width to one or all fx groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all fx groups. * @param width width value to set. Must be in the range indicated by * synth.reverb.width setting. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group, double width) { return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_WIDTH, width); } /** * Set reverb level to one or all fx groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all fx groups. * @param level output level to set. Must be in the range indicated by * synth.reverb.level setting. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group, double level) { return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_LEVEL, level); } /** * Set one reverb parameter to one fx groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all fx groups. * @param enum indicating the parameter to set (#fluid_reverb_param). * FLUID_REVERB_ROOMSIZE, roomsize Reverb room size value (0.0-1.0) * FLUID_REVERB_DAMP, reverb damping value (0.0-1.0) * FLUID_REVERB_WIDTH, reverb width value (0.0-100.0) * FLUID_REVERB_LEVEL, reverb level value (0.0-1.0) * @param value, parameter value * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_reverb_set_param(fluid_synth_t *synth, int fx_group, int param, double value) { int ret; double values[FLUID_REVERB_PARAM_LAST] = {0.0}; static const char *name[FLUID_REVERB_PARAM_LAST] = { "synth.reverb.room-size", "synth.reverb.damp", "synth.reverb.width", "synth.reverb.level" }; double min; /* minimum value */ double max; /* maximum value */ /* check parameters */ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED); fluid_synth_api_enter(synth); if(fx_group < -1 || fx_group >= synth->effects_groups) { FLUID_API_RETURN(FLUID_FAILED); } /* check if reverb value is in max min range */ fluid_settings_getnum_range(synth->settings, name[param], &min, &max); if(value < min || value > max) { FLUID_API_RETURN(FLUID_FAILED); } /* set the value */ values[param] = value; ret = fluid_synth_set_reverb_full(synth, fx_group, FLUID_REVPARAM_TO_SETFLAG(param), values); FLUID_API_RETURN(ret); } int fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set, const double values[]) { fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; /* if non of the flags is set, fail */ fluid_return_val_if_fail(set & FLUID_REVMODEL_SET_ALL, FLUID_FAILED); /* fx group shadow values are set here so that they will be returned if queried */ fluid_rvoice_mixer_set_reverb_full(synth->eventhandler->mixer, fx_group, set, values); /* Synth shadow values are set here so that they will be returned if queried */ if (fx_group < 0) { int i; for(i = 0; i < FLUID_REVERB_PARAM_LAST; i++) { if(set & FLUID_REVPARAM_TO_SETFLAG(i)) { synth->reverb_param[i] = values[i]; } } } param[0].i = fx_group; param[1].i = set; param[2].real = values[FLUID_REVERB_ROOMSIZE]; param[3].real = values[FLUID_REVERB_DAMP]; param[4].real = values[FLUID_REVERB_WIDTH]; param[5].real = values[FLUID_REVERB_LEVEL]; /* finally enqueue an rvoice event to the mixer to actual update reverb */ return fluid_rvoice_eventhandler_push(synth->eventhandler, fluid_rvoice_mixer_set_reverb_params, synth->eventhandler->mixer, param); } /** * Get reverb room size of all fx groups. * @param synth FluidSynth instance * @return Reverb room size (0.0-1.2) * @deprecated Use fluid_synth_get_reverb_group_roomsize() in new code instead. */ double fluid_synth_get_reverb_roomsize(fluid_synth_t *synth) { double roomsize = 0.0; fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_ROOMSIZE, &roomsize); return roomsize; } /** * Get reverb damping of all fx groups. * @param synth FluidSynth instance * @return Reverb damping value (0.0-1.0) * @deprecated Use fluid_synth_get_reverb_group_damp() in new code instead. */ double fluid_synth_get_reverb_damp(fluid_synth_t *synth) { double damp = 0.0; fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_DAMP, &damp); return damp; } /** * Get reverb level of all fx groups. * @param synth FluidSynth instance * @return Reverb level value (0.0-1.0) * @deprecated Use fluid_synth_get_reverb_group_level() in new code instead. */ double fluid_synth_get_reverb_level(fluid_synth_t *synth) { double level = 0.0; fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_LEVEL, &level); return level; } /** * Get reverb width of all fx groups. * @param synth FluidSynth instance * @return Reverb width value (0.0-100.0) * @deprecated Use fluid_synth_get_reverb_group_width() in new code instead. */ double fluid_synth_get_reverb_width(fluid_synth_t *synth) { double width = 0.0; fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_WIDTH, &width); return width; } /** * get reverb roomsize of one or all groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter common to all fx groups is fetched. * @param roomsize valid pointer on the value to return. * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double *roomsize) { return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize); } /** * get reverb damp of one or all groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter common to all fx groups is fetched. * @param damping valid pointer on the value to return. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group, double *damping) { return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_DAMP, damping); } /** * get reverb width of one or all groups * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter common to all fx groups is fetched. * @param width valid pointer on the value to return. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group, double *width) { return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_WIDTH, width); } /** * get reverb level of one or all groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter common to all fx groups is fetched. * @param level valid pointer on the value to return. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group, double *level) { return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_LEVEL, level); } /** * Get one reverb parameter value of one fx groups. * @param synth FluidSynth instance * @param fx_group index of the fx group to get parameter value from. * Must be in the range -1 to synth->effects_groups-1. If -1 get the * parameter common to all fx groups. * @param enum indicating the parameter to get (#fluid_reverb_param). * FLUID_REVERB_ROOMSIZE, reverb room size value. * FLUID_REVERB_DAMP, reverb damping value. * FLUID_REVERB_WIDTH, reverb width value. * FLUID_REVERB_LEVEL, reverb level value. * @param value pointer on the value to return. * @return FLUID_OK if success, FLUID_FAILED otherwise. */ static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group, int param, double *value) { fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED); fluid_return_val_if_fail(value != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); if(fx_group < -1 || fx_group >= synth->effects_groups) { FLUID_API_RETURN(FLUID_FAILED); } if (fx_group < 0) { /* return reverb param common to all fx groups */ *value = synth->reverb_param[param]; } else { /* return reverb param of fx group at index fx_group */ *value = fluid_rvoice_mixer_reverb_get_param(synth->eventhandler->mixer, fx_group, param); } FLUID_API_RETURN(FLUID_OK); } /** * Enable or disable all chorus groups. * @param synth FluidSynth instance * @param on TRUE to enable chorus, FALSE to disable * @deprecated Use fluid_synth_chorus_on() in new code instead. */ void fluid_synth_set_chorus_on(fluid_synth_t *synth, int on) { fluid_return_if_fail(synth != NULL); fluid_synth_api_enter(synth); 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); } /** * Enable or disable chorus on one or all groups. * @param synth FluidSynth instance * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all fx groups. * @param on TRUE to enable chorus, FALSE to disable * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on) { int ret; fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); if(fx_group < -1 || fx_group >= synth->effects_groups) { FLUID_API_RETURN(FLUID_FAILED); } if(fx_group < 0 ) { synth->with_chorus = (on != 0); } param[0].i = fx_group; param[1].i = on; ret = fluid_rvoice_eventhandler_push(synth->eventhandler, fluid_rvoice_mixer_chorus_enable, synth->eventhandler->mixer, param); FLUID_API_RETURN(ret); } /** * Set chorus parameters to all fx groups. * Keep in mind, that the needed CPU time is proportional to 'nr'. * @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.1-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) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use the individual chorus setter functions in new code instead. * * Keep in mind, that the needed CPU time is proportional to 'nr'. */ int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, double speed, double depth_ms, int type) { double values[FLUID_CHORUS_PARAM_LAST]; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); values[FLUID_CHORUS_NR] = nr; values[FLUID_CHORUS_LEVEL] = level; values[FLUID_CHORUS_SPEED] = speed; values[FLUID_CHORUS_DEPTH] = depth_ms; values[FLUID_CHORUS_TYPE] = type; return fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values); } /** * Set the chorus voice count of all groups. * * @param synth FluidSynth instance * @param nr Chorus voice count (0-99, CPU time consumption proportional to * this value) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use fluid_synth_set_chorus_group_nr() in new code instead. */ int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr) { return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, nr); } /** * Set the chorus level of all groups. * * @param synth FluidSynth instance * @param level Chorus level (0.0-10.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use fluid_synth_set_chorus_group_level() in new code instead. */ int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level) { return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, level); } /** * Set the chorus speed of all groups. * * @param synth FluidSynth instance * @param speed Chorus speed in Hz (0.1-5.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use fluid_synth_set_chorus_group_level() in new code instead. */ int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed) { return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, speed); } /** * Set the chorus depth of all groups. * * @param synth FluidSynth instance * @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) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use fluid_synth_set_chorus_group_depth() in new code instead. */ int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms) { return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, depth_ms); } /** * Set the chorus type of all groups. * * @param synth FluidSynth instance * @param type Chorus waveform type (#fluid_chorus_mod) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Use fluid_synth_set_chorus_group_type() in new code instead. */ int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type) { return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_TYPE, type); } /** * Set chorus voice count nr to one or all chorus groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all groups. * @param nr Voice count to set. Must be in the range indicated by \setting{synth_chorus_nr} * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr) { return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_NR, (double)nr); } /** * Set chorus output level to one or all chorus groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all groups. * @param level Output level to set. Must be in the range indicated by \setting{synth_chorus_level} * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level) { return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_LEVEL, level); } /** * Set chorus lfo speed to one or all chorus groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all groups. * @param speed Lfo speed to set. Must be in the range indicated by \setting{synth_chorus_speed} * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed) { return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_SPEED, speed); } /** * Set chorus lfo depth to one or all chorus groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all groups. * @param depth_ms lfo depth to set. Must be in the range indicated by \setting{synth_chorus_depth} * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms) { return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms); } /** * Set chorus lfo waveform type to one or all chorus groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all groups. * @param type Lfo waveform type to set. (#fluid_chorus_mod) * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type) { return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_TYPE, (double)type); } /** * Set one chorus parameter to one fx groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter will be applied to all groups. * @param enum indicating the parameter to set (#fluid_chorus_param). * FLUID_CHORUS_NR, chorus voice count (0-99, CPU time consumption proportional to * this value). * FLUID_CHORUS_LEVEL, chorus level (0.0-10.0). * FLUID_CHORUS_SPEED, chorus speed in Hz (0.1-5.0). * FLUID_CHORUS_DEPTH, chorus depth (max value depends on synth sample-rate, * 0.0-21.0 is safe for sample-rate values up to 96KHz). * FLUID_CHORUS_TYPE, chorus waveform type (#fluid_chorus_mod) * @param value, parameter value * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, int param, double value) { int ret; double values[FLUID_CHORUS_PARAM_LAST] = {0.0}; /* setting name (except lfo waveform type) */ static const char *name[FLUID_CHORUS_PARAM_LAST-1] = { "synth.chorus.nr", "synth.chorus.level", "synth.chorus.speed", "synth.chorus.depth" }; /* check parameters */ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED); fluid_synth_api_enter(synth); if(fx_group < -1 || fx_group >= synth->effects_groups) { FLUID_API_RETURN(FLUID_FAILED); } /* check if chorus value is in max min range */ if(param == FLUID_CHORUS_TYPE || param == FLUID_CHORUS_NR) /* integer value */ { int min = FLUID_CHORUS_MOD_SINE; int max = FLUID_CHORUS_MOD_TRIANGLE; if(param == FLUID_CHORUS_NR) { fluid_settings_getint_range(synth->settings, name[param], &min, &max); } if((int)value < min || (int)value > max) { FLUID_API_RETURN(FLUID_FAILED); } } else /* float value */ { double min; double max; fluid_settings_getnum_range(synth->settings, name[param], &min, &max); if(value < min || value > max) { FLUID_API_RETURN(FLUID_FAILED); } } /* set the value */ values[param] = value; ret = fluid_synth_set_chorus_full(synth, fx_group, FLUID_CHORPARAM_TO_SETFLAG(param), values); FLUID_API_RETURN(ret); } int fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set, const double values[]) { fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; /* if non of the flags is set, fail */ fluid_return_val_if_fail(set & FLUID_CHORUS_SET_ALL, FLUID_FAILED); /* fx group shadow values are set here so that they will be returned if queried */ fluid_rvoice_mixer_set_chorus_full(synth->eventhandler->mixer, fx_group, set, values); /* Synth shadow values are set here so that they will be returned if queried */ if (fx_group < 0) { int i; for(i = 0; i < FLUID_CHORUS_PARAM_LAST; i++) { if(set & FLUID_CHORPARAM_TO_SETFLAG(i)) { synth->chorus_param[i] = values[i]; } } } param[0].i = fx_group; param[1].i = set; param[2].i = (int)values[FLUID_CHORUS_NR]; param[3].real = values[FLUID_CHORUS_LEVEL]; param[4].real = values[FLUID_CHORUS_SPEED]; param[5].real = values[FLUID_CHORUS_DEPTH]; param[6].i = (int)values[FLUID_CHORUS_TYPE]; return fluid_rvoice_eventhandler_push(synth->eventhandler, fluid_rvoice_mixer_set_chorus_params, synth->eventhandler->mixer, param); } /** * Get chorus voice number (delay line count) value of all fx groups. * @param synth FluidSynth instance * @return Chorus voice count * @deprecated Use fluid_synth_get_chorus_group_nr() in new code instead. */ int fluid_synth_get_chorus_nr(fluid_synth_t *synth) { double nr = 0.0; fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_NR, &nr); return (int)nr; } /** * Get chorus level of all fx groups. * @param synth FluidSynth instance * @return Chorus level value * @deprecated Use fluid_synth_get_chorus_group_level() in new code instead. */ double fluid_synth_get_chorus_level(fluid_synth_t *synth) { double level = 0.0; fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_LEVEL, &level); return level; } /** * Get chorus speed in Hz of all fx groups. * @param synth FluidSynth instance * @return Chorus speed in Hz * @deprecated Use fluid_synth_get_chorus_group_speed() in new code instead. */ double fluid_synth_get_chorus_speed(fluid_synth_t *synth) { double speed = 0.0; fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_SPEED, &speed); return speed; } /** * Get chorus depth of all fx groups. * @param synth FluidSynth instance * @return Chorus depth * @deprecated Use fluid_synth_get_chorus_group_depth() in new code instead. */ double fluid_synth_get_chorus_depth(fluid_synth_t *synth) { double depth = 0.0; fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_DEPTH, &depth); return depth; } /** * Get chorus waveform type of all fx groups. * @param synth FluidSynth instance * @return Chorus waveform type (#fluid_chorus_mod) * @deprecated Use fluid_synth_get_chorus_group_type() in new code instead. */ int fluid_synth_get_chorus_type(fluid_synth_t *synth) { double type = 0.0; fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_TYPE, &type); return (int)type; } /** * Get chorus count nr of one or all fx groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group from which to fetch the chorus voice count. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter common to all fx groups is fetched. * @param nr valid pointer on value to return. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr) { double num_nr = 0.0; int status; status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_NR, &num_nr); *nr = (int)num_nr; return status; } /** * Get chorus output level of one or all fx groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group from which chorus level to fetch. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter common to all fx groups is fetched. * @param level valid pointer on value to return. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level) { return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_LEVEL, level); } /** * Get chorus waveform lfo speed of one or all fx groups. * @param synth FluidSynth instance. * @param fx_group Index of the fx group from which lfo speed to fetch. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter common to all fx groups is fetched. * @param speed valid pointer on value to return. * @return #FLUID_OK on success, #FLUID_FAILED otherwise. */ int fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed) { return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_SPEED, speed); } /** * Get chorus lfo depth of one or all fx groups. * @param synth FluidSynth instance * @param fx_group Index of the fx group from which lfo depth to fetch. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter common to all fx groups is fetched. * @param depth_ms valid pointer on value to return. * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms) { return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms); } /** * Get chorus waveform type of one or all fx groups. * @param synth FluidSynth instance * @param fx_group Index of the fx group from which to fetch the waveform type. * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the * parameter common to all fx groups is fetched. * @param type valid pointer on waveform type to return (#fluid_chorus_mod) * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type) { double num_type = 0.0; int status; status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_TYPE, &num_type); *type = (int)num_type; return status; } /** * Get chorus parameter value of one or all fx groups. * @param synth FluidSynth instance * @param fx_group index of the fx group * @param enum indicating the parameter to get. * FLUID_CHORUS_NR, chorus voice count. * FLUID_CHORUS_LEVEL, chorus level. * FLUID_CHORUS_SPEED, chorus speed. * FLUID_CHORUS_DEPTH, chorus depth. * FLUID_CHORUS_TYPE, chorus waveform type. * @param value pointer on the value to return. * @return FLUID_OK if success, FLUID_FAILED otherwise. */ static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group, int param, double *value) { fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED); fluid_return_val_if_fail(value != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); if(fx_group < -1 || fx_group >= synth->effects_groups) { FLUID_API_RETURN(FLUID_FAILED); } if (fx_group < 0) { /* return chorus param common to all fx groups */ *value = synth->chorus_param[param]; } else { /* return chorus param of fx group at index group */ *value = fluid_rvoice_mixer_chorus_get_param(synth->eventhandler->mixer, fx_group, param); } FLUID_API_RETURN(FLUID_OK); } /* * 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... */ void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan, int key) { int i; fluid_voice_t *voice; /* storeid is a parameter for fluid_voice_init() */ synth->storeid = synth->noteid++; /* for "monophonic playing" key is the previous sustained note if it exists (0 to 127) or INVALID_NOTE otherwise */ if(key == INVALID_NOTE) { return; } 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 except if pedaling (sostenuto or sustain) is active */ fluid_voice_noteoff(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 total number of allocated effects units. * * This is the same number as initially provided by the setting \setting{synth_effects-groups}. * @param synth FluidSynth instance * @return Count of allocated effects units */ int fluid_synth_count_effects_groups(fluid_synth_t *synth) { int result; fluid_return_val_if_fail(synth != NULL, 0); fluid_synth_api_enter(synth); result = synth->effects_groups; 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. * @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); } /** * 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); } /** * 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 (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) { FLUID_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; } /** * Apply an offset to a SoundFont generator on a MIDI channel. * * This function allows to set an offset for the specified destination generator in real-time. * The offset will be applied immediately to all voices that are currently and subsequently playing * on the given MIDI channel. This functionality works equivalent to using NRPN MIDI messages to * manipulate synthesis parameters. See SoundFont spec, paragraph 8.1.3, for details on SoundFont * generator parameters and valid ranges, as well as paragraph 9.6 for details on NRPN messages. * @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 value (in native units of the generator) to assign to the MIDI channel * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ 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); 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) { fluid_voice_t *voice; int i; fluid_channel_set_gen(synth->channel[chan], param, value); 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); } } } /** * Retrieve the generator NRPN offset assigned to a MIDI channel. * * The value returned is in native units of the generator. By default, the offset is zero. * @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 NRPN generator offset value assigned to the 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); } /** * 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 KEY_PRESSURE: return fluid_synth_key_pressure(synth, chan, fluid_midi_event_get_key(event), fluid_midi_event_get_value(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); case MIDI_TEXT: case MIDI_LYRIC: case MIDI_SET_TEMPO: return FLUID_OK; } return FLUID_FAILED; } /** * Create and start voices using an arbitrary preset and a MIDI note on event. * * Using this function is only supported when the setting @c synth.dynamic-sample-loading is false! * @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, dynamic_samples; 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); fluid_settings_getint(fluid_synth_get_settings(synth), "synth.dynamic-sample-loading", &dynamic_samples); if(dynamic_samples) { // The preset might not be currently used, thus its sample data may not be loaded. // This guard is to avoid a NULL deref in rvoice_write(). FLUID_LOG(FLUID_ERR, "Calling fluid_synth_start() while synth.dynamic-sample-loading is enabled is not supported."); // Although we would be able to select the preset (and load it's samples) we have no way to // unselect the preset again in fluid_synth_stop(). Also dynamic sample loading was intended // to be used only when presets have been selected on a MIDI channel. // Note that even if the preset is currently selected on a channel, it could be unselected at // any time. And we would end up with a NULL sample->data again, because we are not referencing // the preset here. Thus failure is our only option. result = FLUID_FAILED; } else { 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 * @return #FLUID_OK if the offset was set successfully, #FLUID_FAILED otherwise */ int fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset) { fluid_sfont_t *sfont; fluid_list_t *list; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); for(list = synth->sfont; list; list = fluid_list_next(list)) { sfont = fluid_list_get(list); if(fluid_sfont_get_id(sfont) == sfont_id) { sfont->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_t *sfont; fluid_list_t *list; int offset = 0; fluid_return_val_if_fail(synth != NULL, 0); fluid_synth_api_enter(synth); for(list = synth->sfont; list; list = fluid_list_next(list)) { sfont = fluid_list_get(list); if(fluid_sfont_get_id(sfont) == sfont_id) { offset = sfont->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); } /** * Return the LADSPA effects instance used by FluidSynth * * @param synth FluidSynth instance * @return pointer to LADSPA fx or NULL if compiled without LADSPA support or LADSPA is not active */ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth) { fluid_return_val_if_fail(synth != NULL, NULL); return synth->ladspa_fx; } /** * Configure a general-purpose IIR biquad filter. * * @param synth FluidSynth instance * @param type Type of the IIR filter to use (see #fluid_iir_filter_type) * @param flags Additional flags to customize this filter or zero to stay with the default (see #fluid_iir_filter_flags) * @return #FLUID_OK if the settings have been successfully applied, otherwise #FLUID_FAILED * * This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard. * By default this filter is off (#FLUID_IIR_DISABLED). */ int fluid_synth_set_custom_filter(fluid_synth_t *synth, int type, int flags) { int i; fluid_voice_t *voice; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(type >= FLUID_IIR_DISABLED && type < FLUID_IIR_LAST, FLUID_FAILED); fluid_synth_api_enter(synth); synth->custom_filter_type = type; synth->custom_filter_flags = flags; for(i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; fluid_voice_set_custom_filter(voice, type, flags); } FLUID_API_RETURN(FLUID_OK); } /** * Set the important channels for voice overflow priority calculation. * * @param synth FluidSynth instance * @param channels comma-separated list of channel numbers * @return #FLUID_OK on success, otherwise #FLUID_FAILED */ static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels) { int i; int retval = FLUID_FAILED; int *values = NULL; int num_values; fluid_overflow_prio_t *scores; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); scores = &synth->overflow; if(scores->num_important_channels < synth->midi_channels) { scores->important_channels = FLUID_REALLOC(scores->important_channels, sizeof(*scores->important_channels) * synth->midi_channels); if(scores->important_channels == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto exit; } scores->num_important_channels = synth->midi_channels; } FLUID_MEMSET(scores->important_channels, FALSE, sizeof(*scores->important_channels) * scores->num_important_channels); if(channels != NULL) { values = FLUID_ARRAY(int, synth->midi_channels); if(values == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto exit; } /* Every channel given in the comma-separated list of channel numbers * is set to TRUE, i.e. flagging it as "important". Channel numbers are * 1-based. */ num_values = fluid_settings_split_csv(channels, values, synth->midi_channels); for(i = 0; i < num_values; i++) { if(values[i] > 0 && values[i] <= synth->midi_channels) { scores->important_channels[values[i] - 1] = TRUE; } } } retval = FLUID_OK; exit: FLUID_FREE(values); return retval; } /* * Handler for synth.overflow.important-channels setting. */ static void fluid_synth_handle_important_channels(void *data, const char *name, const char *value) { fluid_synth_t *synth = (fluid_synth_t *)data; fluid_synth_api_enter(synth); fluid_synth_set_important_channels(synth, value); fluid_synth_api_exit(synth); } /* API legato mode *********************************************************/ /** * Sets the legato mode of a channel. * * @param synth the synth instance. * @param chan MIDI channel number (0 to MIDI channel count - 1). * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. * * @return * - #FLUID_OK on success. * - #FLUID_FAILED * - \a synth is NULL. * - \a chan is outside MIDI channel count. * - \a legatomode is invalid. */ int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode) { /* checks parameters first */ fluid_return_val_if_fail(legatomode >= 0, FLUID_FAILED); fluid_return_val_if_fail(legatomode < FLUID_CHANNEL_LEGATO_MODE_LAST, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /**/ synth->channel[chan]->legatomode = legatomode; /**/ FLUID_API_RETURN(FLUID_OK); } /** * Gets the legato mode of a channel. * * @param synth the synth instance. * @param chan MIDI channel number (0 to MIDI channel count - 1). * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. * * @return * - #FLUID_OK on success. * - #FLUID_FAILED * - \a synth is NULL. * - \a chan is outside MIDI channel count. * - \a legatomode is NULL. */ int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode) { /* checks parameters first */ fluid_return_val_if_fail(legatomode != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /**/ * legatomode = synth->channel[chan]->legatomode; /**/ FLUID_API_RETURN(FLUID_OK); } /* API portamento mode *********************************************************/ /** * Sets the portamento mode of a channel. * * @param synth the synth instance. * @param chan MIDI channel number (0 to MIDI channel count - 1). * @param portamentomode The portamento mode as indicated by #fluid_channel_portamento_mode. * @return * - #FLUID_OK on success. * - #FLUID_FAILED * - \a synth is NULL. * - \a chan is outside MIDI channel count. * - \a portamentomode is invalid. */ int fluid_synth_set_portamento_mode(fluid_synth_t *synth, int chan, int portamentomode) { /* checks parameters first */ fluid_return_val_if_fail(portamentomode >= 0, FLUID_FAILED); fluid_return_val_if_fail(portamentomode < FLUID_CHANNEL_PORTAMENTO_MODE_LAST, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /**/ synth->channel[chan]->portamentomode = portamentomode; /**/ FLUID_API_RETURN(FLUID_OK); } /** * Gets the portamento mode of a channel. * * @param synth the synth instance. * @param chan MIDI channel number (0 to MIDI channel count - 1). * @param portamentomode Pointer to the portamento mode as indicated by #fluid_channel_portamento_mode. * @return * - #FLUID_OK on success. * - #FLUID_FAILED * - \a synth is NULL. * - \a chan is outside MIDI channel count. * - \a portamentomode is NULL. */ int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan, int *portamentomode) { /* checks parameters first */ fluid_return_val_if_fail(portamentomode != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /**/ * portamentomode = synth->channel[chan]->portamentomode; /**/ FLUID_API_RETURN(FLUID_OK); } /* API breath mode *********************************************************/ /** * Sets the breath mode of a channel. * * @param synth the synth instance. * @param chan MIDI channel number (0 to MIDI channel count - 1). * @param breathmode The breath mode as indicated by #fluid_channel_breath_flags. * * @return * - #FLUID_OK on success. * - #FLUID_FAILED * - \a synth is NULL. * - \a chan is outside MIDI channel count. */ int fluid_synth_set_breath_mode(fluid_synth_t *synth, int chan, int breathmode) { /* checks parameters first */ FLUID_API_ENTRY_CHAN(FLUID_FAILED); /**/ fluid_channel_set_breath_info(synth->channel[chan], breathmode); /**/ FLUID_API_RETURN(FLUID_OK); } /** * Gets the breath mode of a channel. * * @param synth the synth instance. * @param chan MIDI channel number (0 to MIDI channel count - 1). * @param breathmode Pointer to the returned breath mode as indicated by #fluid_channel_breath_flags. * * @return * - #FLUID_OK on success. * - #FLUID_FAILED * - \a synth is NULL. * - \a chan is outside MIDI channel count. * - \a breathmode is NULL. */ int fluid_synth_get_breath_mode(fluid_synth_t *synth, int chan, int *breathmode) { /* checks parameters first */ fluid_return_val_if_fail(breathmode != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /**/ * breathmode = fluid_channel_get_breath_info(synth->channel[chan]); /**/ FLUID_API_RETURN(FLUID_OK); } /** API Poly/mono mode ******************************************************/ /* * Resets a basic channel group of MIDI channels. * @param synth the synth instance. * @param chan the beginning channel of the group. * @param nbr_chan the number of channel in the group. */ static void fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan) { int i; for(i = chan; i < chan + nbr_chan; i++) { fluid_channel_reset_basic_channel_info(synth->channel[i]); synth->channel[i]->mode_val = 0; } } /** * Disables and unassigns all channels from a basic channel group. * * @param synth The synth instance. * @param chan The basic channel of the group to reset or -1 to reset all channels. * @note By default (i.e. on creation after new_fluid_synth() and after fluid_synth_system_reset()) * a synth instance has one basic channel at channel 0 in mode #FLUID_CHANNEL_MODE_OMNION_POLY. * All other channels belong to this basic channel group. Make sure to call this function before * setting any custom basic channel setup. * * @return * - #FLUID_OK on success. * - #FLUID_FAILED * - \a synth is NULL. * - \a chan is outside MIDI channel count. * - \a chan isn't a basic channel. */ int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan) { int nbr_chan; /* checks parameters first */ if(chan < 0) { fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); /* The range is all MIDI channels from 0 to MIDI channel count -1 */ chan = 0; /* beginning chan */ nbr_chan = synth->midi_channels; /* MIDI Channels number */ } else { FLUID_API_ENTRY_CHAN(FLUID_FAILED); /* checks if chan is a basic channel */ if(!(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC)) { FLUID_API_RETURN(FLUID_FAILED); } /* The range is all MIDI channels in the group from chan */ nbr_chan = synth->channel[chan]->mode_val; /* nbr of channels in the group */ } /* resets the range of MIDI channels */ fluid_synth_reset_basic_channel_LOCAL(synth, chan, nbr_chan); FLUID_API_RETURN(FLUID_OK); } /** * Checks if a new basic channel group overlaps the next basic channel group. * * On success the function returns the possible number of channel for this * new basic channel group. * The function fails if the new group overlaps the next basic channel group. * * @param see fluid_synth_set_basic_channel. * @return * - On success, the effective number of channels for this new basic channel group, * #FLUID_FAILED otherwise. * - #FLUID_FAILED * - \a val has a number of channels overlapping next basic channel group or been * above MIDI channel count. */ static int fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val) { int i, n_chan = synth->midi_channels; /* MIDI Channels count */ int real_val = val; /* real number of channels in the group */ /* adjusts val range */ if(mode == FLUID_CHANNEL_MODE_OMNIOFF_POLY) { real_val = 1; /* mode poly omnioff implies a group of only one channel.*/ } else if(val == 0) { /* mode poly omnion (0), mono omnion (1), mono omni off (3) */ /* value 0 means all possible channels from basicchan to MIDI channel count -1.*/ real_val = n_chan - basicchan; } /* checks if val range is above MIDI channel count */ else if(basicchan + val > n_chan) { return FLUID_FAILED; } /* checks if this basic channel group overlaps next basic channel group */ for(i = basicchan + 1; i < basicchan + real_val; i++) { if(synth->channel[i]->mode & FLUID_CHANNEL_BASIC) { /* A value of 0 for val means all possible channels from basicchan to to the next basic channel -1 (if any). When i reaches the next basic channel group, real_val will be limited if it is possible */ if(val == 0) { /* limitation of real_val */ real_val = i - basicchan; break; } /* overlap with the next basic channel group */ return FLUID_FAILED; } } return real_val; } /** * Sets a new basic channel group only. The function doesn't allow to change an * existing basic channel. * * The function fails if any channel overlaps any existing basic channel group. * To make room if necessary, basic channel groups can be cleared using * fluid_synth_reset_basic_channel(). * * @param synth the synth instance. * @param chan the basic Channel number (0 to MIDI channel count-1). * @param mode the MIDI mode to use for chan (see #fluid_basic_channel_modes). * @param val number of channels in the group. * @note \a val is only relevant for mode #FLUID_CHANNEL_MODE_OMNION_POLY, * #FLUID_CHANNEL_MODE_OMNION_MONO and #FLUID_CHANNEL_MODE_OMNIOFF_MONO. A value * of 0 means all possible channels from \a chan to to next basic channel minus 1 (if any) * or to MIDI channel count minus 1. Val is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY * as this mode implies a group of only one channel. * @return * - #FLUID_OK on success. * - #FLUID_FAILED * - \a synth is NULL. * - \a chan is outside MIDI channel count. * - \a mode is invalid. * - \a val has a number of channels overlapping another basic channel group or been * above MIDI channel count. * - When the function fails, any existing basic channels aren't modified. */ int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val) { /* check parameters */ fluid_return_val_if_fail(mode >= 0, FLUID_FAILED); fluid_return_val_if_fail(mode < FLUID_CHANNEL_MODE_LAST, FLUID_FAILED); fluid_return_val_if_fail(val >= 0, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); /**/ if(val > 0 && chan + val > synth->midi_channels) { FLUID_API_RETURN(FLUID_FAILED); } /* Checks if there is an overlap with the next basic channel */ val = fluid_synth_check_next_basic_channel(synth, chan, mode, val); if(val == FLUID_FAILED || synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) { /* overlap with the next or previous channel group */ FLUID_LOG(FLUID_INFO, "basic channel %d overlaps another group", chan); FLUID_API_RETURN(FLUID_FAILED); } /* sets a new basic channel group */ fluid_synth_set_basic_channel_LOCAL(synth, chan, mode, val); /**/ FLUID_API_RETURN(FLUID_OK); } /* * Local version of fluid_synth_set_basic_channel(), called internally: * - by fluid_synth_set_basic_channel() to set a new basic channel group. * - during creation new_fluid_synth() or on CC reset to set a default basic channel group. * - on CC ominoff, CC omnion, CC poly , CC mono to change an existing basic channel group. * * @param see fluid_synth_set_basic_channel() */ static void fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val) { int i; /* sets the basic channel group */ for(i = basicchan; i < basicchan + val; i++) { int new_mode = mode; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */ int new_val; /* MIDI specs: when mode is changed, channel must receive ALL_NOTES_OFF */ fluid_synth_all_notes_off_LOCAL(synth, i); if(i == basicchan) { new_mode |= FLUID_CHANNEL_BASIC; /* First channel in the group */ new_val = val; /* number of channels in the group */ } else { new_val = 0; /* val is 0 for other channel than basic channel */ } /* Channel is enabled */ new_mode |= FLUID_CHANNEL_ENABLED; /* Now new_mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not and enabled */ fluid_channel_set_basic_channel_info(synth->channel[i], new_mode); synth->channel[i]->mode_val = new_val; } } /** * Searches a previous basic channel starting from chan. * * @param synth the synth instance. * @param chan starting index of the search (including chan). * @return index of the basic channel if found , FLUID_FAILED otherwise. */ static int fluid_synth_get_previous_basic_channel(fluid_synth_t *synth, int chan) { for(; chan >= 0; chan--) { /* searches previous basic channel */ if(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC) { /* chan is the previous basic channel */ return chan; } } return FLUID_FAILED; } /** * Returns poly mono mode information of any MIDI channel. * * @param synth the synth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param basic_chan_out Buffer to store the basic channel \a chan belongs to or #FLUID_FAILED if \a chan is disabled. * @param mode_out Buffer to store the mode of \a chan (see #fluid_basic_channel_modes) or #FLUID_FAILED if \a chan is disabled. * @param val_out Buffer to store the total number of channels in this basic channel group or #FLUID_FAILED if \a chan is disabled. * @note If any of \a basic_chan_out, \a mode_out, \a val_out pointer is NULL * the corresponding information isn't returned. * * @return * - #FLUID_OK on success. * - #FLUID_FAILED * - \a synth is NULL. * - \a chan is outside MIDI channel count. */ int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan, int *basic_chan_out, int *mode_out, int *val_out) { int basic_chan = FLUID_FAILED; int mode = FLUID_FAILED; int val = FLUID_FAILED; /* checks parameters first */ FLUID_API_ENTRY_CHAN(FLUID_FAILED); if((synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) && /* chan is enabled , we search the basic channel chan belongs to */ (basic_chan = fluid_synth_get_previous_basic_channel(synth, chan)) != FLUID_FAILED) { mode = synth->channel[chan]->mode & FLUID_CHANNEL_MODE_MASK; val = synth->channel[basic_chan]->mode_val; } /* returns the information if they are requested */ if(basic_chan_out) { * basic_chan_out = basic_chan; } if(mode_out) { * mode_out = mode; } if(val_out) { * val_out = val; } FLUID_API_RETURN(FLUID_OK); } fluidsynth-2.2.5/src/synth/fluid_synth.h000066400000000000000000000306671417326347500203760ustar00rootroot00000000000000/* 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 */ #include "fluid_sys.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_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 */ #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 */ #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 */ /*************************************************************** * * 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 /* * 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_fx - same instance copied in rendering thread. Synchronising handled internally. * */ 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? */ 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 effects_groups; /**< the number of effects units (>= 1) */ int state; /**< the synthesizer state */ fluid_atomic_uint_t 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; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */ fluid_list_t *fonts_to_be_unloaded; /**< list of timers that try to unload a soundfont */ float gain; /**< master gain */ fluid_channel_t **channel; /**< the channels */ 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; int fromkey_portamento; /**< fromkey portamento */ fluid_rvoice_eventhandler_t *eventhandler; /**< Shadow of reverb parameter: roomsize, damping, width, level */ double reverb_param[FLUID_REVERB_PARAM_LAST]; /**< Shadow of chorus parameter: chorus number, level, speed, depth, type */ double chorus_param[FLUID_CHORUS_PARAM_LAST]; 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) */ fluid_atomic_float_t 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_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) */ fluid_mod_t *default_mod; /**< the (dynamic) list of default modulators */ fluid_ladspa_fx_t *ladspa_fx; /**< Effects unit for LADSPA support */ enum fluid_iir_filter_type custom_filter_type; /**< filter type of the user-defined filter currently used for all voices */ enum fluid_iir_filter_flags custom_filter_flags; /**< filter type of the user-defined filter currently used for all voices */ }; /** * 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); typedef int (*fluid_audio_channels_callback_t)(fluid_synth_t *synth, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]); int fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[], int (*block_render_func)(fluid_synth_t *, int)); int fluid_synth_write_s16_channels(fluid_synth_t *synth, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]); int fluid_synth_write_float_channels(fluid_synth_t *synth, int len, int channels_count, void *channels_out[], int channels_off[], int channels_incr[]); fluid_preset_t *fluid_synth_find_preset(fluid_synth_t *synth, int banknum, int prognum); void fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont); void fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const 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, unsigned int num); int fluid_synth_reverb_set_param(fluid_synth_t *synth, int fx_group, int param, double value); int fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set, const double values[]); int fluid_synth_reset_chorus(fluid_synth_t *synth); int fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, int param, double value); int fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set, const double values[]); fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data); void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer); void fluid_sample_timer_reset(fluid_synth_t *synth, fluid_sample_timer_t *timer); void fluid_synth_process_event_queue(fluid_synth_t *synth); int fluid_synth_set_gen2(fluid_synth_t *synth, int chan, int param, float value, int absolute, int normalized); int fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)); int fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, void *lout, int loff, int lincr, void *rout, int roff, int rincr, int (*block_render_func)(fluid_synth_t *, int)); /* * misc */ void fluid_synth_settings(fluid_settings_t *settings); void fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate); /* extern declared in fluid_synth_monopoly.c */ int fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel); int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan, int key, int vel); int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key); int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, int fromkey, int tokey, int vel); int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, char Mono); fluid_voice_t * fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range); void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan, int key); #endif /* _FLUID_SYNTH_H */ fluidsynth-2.2.5/src/synth/fluid_synth_monopoly.c000066400000000000000000000766071417326347500223310ustar00rootroot00000000000000/* 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_synth.h" #include "fluid_chan.h" #include "fluid_defsfont.h" /****************************************************************************** The legato detector is composed as this, variables: - monolist: monophonic list variable. - prev_note: to store the most recent note before adding on noteon or before removing on noteoff. - FLUID_CHANNEL_LEGATO_PLAYING: legato/staccato state bit that informs on legato or staccato playing. functions: - fluid_channel_add_monolist(), for inserting a new note. - fluid_channel_search_monolist(), for searching the position of a note into the list. - fluid_channel_remove_monolist(), for removing a note from the list. The monophonic list +------------------------------------------------+ | +----+ +----+ +----+ +----+ | | |note| |note| |note| |note| | +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ +----+ +----+ +----+ +----+ /|\ /|\ | | i_first i_last The list allows an easy automatic detection of a legato passage when it is played on a MIDI keyboard input device. It is useful also when the input device is an ewi (electronic wind instrument) or evi (electronic valve instrument) and these instruments are unable to send MIDI CC legato on/off. The list memorizes the notes in playing order. - (a) On noteOn n2, if a previous note n1 exists, there is a legato detection with n1 (with or without portamento from n1 to n2 See note below). - (b) On noteOff of the running note n2, if a previous note n1 exists, there is a legato detection from n2 to n1, allowing fast trills playing (with or without portamento from n2 to n1. See note below). Notes in the list are inserted to the end of the list that works like a circular buffer.The features are: 1) It is always possible to play an infinite legato passage in direct order (n1_On,n2_On,n3_On,....). 2) Playing legato in the reverse order (n10_Off, n9_Off,,...) helps in fast trills playing as the list memorizes 10 most recent notes. 3) Playing an infinite lagato passage in ascendant or descendant order, without playing trills is always possible using the usual way like this: First we begin with an ascendant passage, n1On, (n2On,n1Off), (n3On,n2Off) , (n4On,n3Off), then we continue with a descendant passage (n3On,n4off), (n2On,n3off), (n1On,n2off), n1Off...and so on Each MIDI channel have a legato detector. Note: Portamento is a feature independent of the legato detector. So portamento isn't part of the lagato detector. However portamento (when enabled) is triggered at noteOn (like legato). Like in legato situation it is usual to have a portamento from a note 'fromkey' to another note 'tokey'. Portamento fromkey note choice is determined at noteOn by fluid_synth_get_fromkey_portamento_legato() (see below). More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). ******************************************************************************/ /***************************************************************************** Portamento related functions in Poly or Mono mode ******************************************************************************/ /** * fluid_synth_get_fromkey_portamento_legato returns two information: * - fromkey note for portamento. * - fromkey note for legato. * +-----> fromkey_portamento * ______|________ * portamento modes >------->| | * | get_fromkey | * Porta.on/off >------------------------->|_______________| * (PTC) | * +-----> fromkey_legato * * The functions is intended to be call on noteOn mono * see fluid_synth_noteon_mono_staccato(), fluid_synth_noteon_monopoly_legato() * ------- * 1)The function determines if a portamento must occur on next noteOn. * The value returned is 'fromkey portamento' which is the pitchstart key * of a portamento, as function of PTC or (default_fromkey, prev_note) both * if Portamento On. By order of precedence the result is: * 1.1) PTC have precedence over Portamento On. * If CC PTC has been received, its value supersedes and any * portamento pedal On, default_fromkey,prev_note or portamento mode. * 1.2) Otherwise ,when Portamento On the function takes the following value: * - default_fromkey if valid * - otherwise prev_note(prev_note is the note prior the most recent * note played). * Then portamento mode is applied to validate the value chosen. * Where portamento mode is: * - each note, a portamento occurs on each note. * - legato only, portamento only on notes played legato. * - staccato only, portamento only on notes played staccato. * 1.3) Otherwise, portamento is off,INVALID_NOTE is returned (portamento is disabled). * ------ * 2)The function determines if a legato playing must occur on next noteOn. * 'fromkey legato note' is returned as a function of default_fromkey, PTC, * current mono/poly mode,actual 'staccato/legato' playing state and prev_note. * By order of precedence the result is: * 2.1) If valid, default_fromkey have precedence over any others values. * 2.2) Otherwise if CC PTC has been received its value is returned. * 2.3) Otherwise fromkey legato is determined from the mono/poly mode, * the actual 'staccato/legato' playing state (FLUID_CHANNEL_LEGATO_PLAYING) and prev_note * as this: * - in (poly/Mono) staccato , INVALID_NOTE is returned. * - in poly legato , actually we don't want playing legato. So * INVALID_NOTE is returned. * - in mono legato , prev_note is returned. * * On input * @param chan fluid_channel_t. * @param defaultFromkey, the default 'fromkey portamento' note or 'fromkey legato' * note (see description above). * * @return * 1)'fromkey portamento' is returned in fluid_synth_t.fromkey_portamento. * If valid,it means that portamento is enabled . * * 2)The 'fromkey legato' note is returned. * * Notes about usage: * The function is intended to be called when the following event occurs: * - On noteOn (Poly or Mono) after insertion in the monophonic list. * - On noteOff(mono legato playing). In this case, default_fromkey must be valid. * * Typical calling usage: * - In poly, default_fromkey must be INVALID_NOTE. * - In mono staccato playing,default_fromkey must be INVALID_NOTE. * - In mono legato playing,default_fromkey must be valid. */ static char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t *chan, int default_fromkey) { unsigned char ptc = fluid_channel_get_cc(chan, PORTAMENTO_CTRL); if(fluid_channel_is_valid_note(ptc)) { /* CC PTC has been received */ fluid_channel_clear_portamento(chan); /* clears the CC PTC receive */ chan->synth->fromkey_portamento = ptc;/* returns fromkey portamento */ /* returns fromkey legato */ if(!fluid_channel_is_valid_note(default_fromkey)) { default_fromkey = ptc; } } else { /* determines and returns fromkey portamento */ unsigned char fromkey_portamento = INVALID_NOTE; if(fluid_channel_portamento(chan)) { /* Portamento when Portamento pedal is On */ /* 'fromkey portamento'is determined from the portamento mode and the most recent note played (prev_note)*/ enum fluid_channel_portamento_mode portamentomode = chan->portamentomode; if(fluid_channel_is_valid_note(default_fromkey)) { fromkey_portamento = default_fromkey; /* on each note */ } else { fromkey_portamento = fluid_channel_prev_note(chan); /* on each note */ } if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY) { /* Mode portamento:legato only */ if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) { fromkey_portamento = INVALID_NOTE; } } else if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY) { /* Mode portamento:staccato only */ if(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING) { fromkey_portamento = INVALID_NOTE; } } /* else Mode portamento: on each note (staccato/legato) */ } /* Returns fromkey portamento */ chan->synth->fromkey_portamento = fromkey_portamento; /* Determines and returns fromkey legato */ if(!fluid_channel_is_valid_note(default_fromkey)) { /* in staccato (poly/Mono) returns INVALID_NOTE */ /* In mono mode legato playing returns the note prior most recent note played */ if(fluid_channel_is_playing_mono(chan) && (chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) { default_fromkey = fluid_channel_prev_note(chan); /* note prior last note */ } /* In poly mode legato playing, actually we don't want playing legato. So returns INVALID_NOTE */ } } return default_fromkey; /* Returns legato fromkey */ } /***************************************************************************** noteon - noteoff functions in Mono mode ******************************************************************************/ /* * noteon - noteoff on a channel in "monophonic playing". * * A channel needs to be played monophonic if this channel has been set in * monophonic mode by basic channel API.(see fluid_synth_polymono.c). * A channel needs also to be played monophonic if it has been set in * polyphonic mode and legato pedal is On during the playing. * When a channel is in "monophonic playing" state, only one note at a time can be * played in a staccato or legato manner (with or without portamento). * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). * _______________ * ________________ | noteon | * | legato detector| O-->| mono_staccato |--*-> preset_noteon * noteon_mono ->| (add_monolist) |--O-- |_______________| | (with or without) * LOCAL |________________| O /|\ | (portamento) * /|\ set_onenote | | fromkey | * | | | portamento| * noteOn poly >---*------------------* | | * | | | * | _____ |________ | * portamento modes >--- | ->| | | * | | get_fromkey | | * Porta.on/off >--------------------- | ->|_______________| | * (PTC) | | | * | fromkey | fromkey | * | legato | portamento| * | _____\|/_______ | * *-->| noteon |--/ * | | monopoly | * | | legato |----> voices * legato modes >------- | ->|_______________| triggering * | (with or without) * | (portamento) * | * | * noteOff poly >---*----------------- | ---------+ * | clear | | * _\|/_____________ | | * | legato detector | O | * noteoff_mono->|(search_monolist)|-O-- _____\|/_______ * LOCAL |(remove_monolist)| O-->| noteoff | * |_________________| | monopoly |----> noteoff * Sust.on/off >------------------------->|_______________| * Sost.on/off ------------------------------------------------------------------------------*/ /** * Plays a noteon event for a Synth instance in "monophonic playing" state. * Please see the description above about "monophonic playing". * _______________ * ________________ | noteon | * | legato detector| O-->| mono_staccato |--->preset_noteon * noteon_mono ->| (add_monolist) |--O-- |_______________| * LOCAL |________________| O * | * | * | * | * | * | * | * | * | * | _______________ * | | noteon | * +-->| monopoly | * | legato |---> voices * |_______________| triggering * * The function uses the legato detector (see above) to determine if the note must * be played staccato or legato. * * @param synth 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). * @return FLUID_OK on success, FLUID_FAILED otherwise. */ int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan, int key, int vel) { fluid_channel_t *channel = synth->channel[chan]; /* Adds the note into the monophonic list */ fluid_channel_add_monolist(channel, key, vel, 0); /* in Breath Sync mode, the noteon triggering is postponed until the musician starts blowing in the breath controller */ if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || fluid_channel_breath_msb(channel)) { /* legato/staccato playing detection */ if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) { /* legato playing */ /* legato from prev_note to key */ /* the voices from prev_note key number are to be used to play key number */ /* fromkey must be valid */ return fluid_synth_noteon_monopoly_legato(synth, chan, fluid_channel_prev_note(channel), key, vel); } else { /* staccato playing */ return fluid_synth_noteon_mono_staccato(synth, chan, key, vel); } } else { return FLUID_OK; } } /** * Plays a noteoff event for a Synth instance in "monophonic playing" state. * Please see the description above about "monophonic playing" * * _______________ * | noteon | * +-->| monopoly | * | | legato |----> voices * | |_______________| triggering * | (with or without) * | (portamento) * | * | * | * | * | * | * _________________ | * | legato detector | O * noteoff_mono->|(search_monolist)|-O-- _______________ * LOCAL |(remove_monolist)| O-->| noteoff | * |_________________| | monopoly |----> noteoff * |_______________| * * The function uses the legato detector (see above) to determine if the noteoff must * be played staccato or legato. * * @param synth 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. */ int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key) { int status; int i, i_prev; fluid_channel_t *channel = synth->channel[chan]; /* searching the note in the monophonic list */ i = fluid_channel_search_monolist(channel, key, &i_prev); if(i >= 0) { /* the note is in the monophonic list */ /* Removes the note from the monophonic list */ fluid_channel_remove_monolist(channel, i, &i_prev); /* in Breath Sync mode, the noteoff triggering is done if the musician is blowing in the breath controller */ if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || fluid_channel_breath_msb(channel)) { /* legato playing detection */ if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) { /* the list contains others notes */ if(i_prev >= 0) { /* legato playing detection on noteoff */ /* legato from key to i_prev key */ /* the voices from key number are to be used to play i_prev key number. */ status = fluid_synth_noteon_monopoly_legato(synth, chan, key, channel->monolist[i_prev].note, channel->monolist[i_prev].vel); } /* else the note doesn't need to be played off */ else { status = FLUID_OK; } } else { /* the monophonic list is empty */ /* plays the monophonic note noteoff and eventually held by sustain/sostenuto */ status = fluid_synth_noteoff_monopoly(synth, chan, key, 1); } } else { status = FLUID_OK; } } else { /* the note is not found in the list so the note was played On when the channel was in polyphonic playing */ /* plays the noteoff as for polyphonic */ status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); } return status; } /*---------------------------------------------------------------------------- staccato playing -----------------------------------------------------------------------------*/ /** * Plays noteon for a monophonic note in staccato manner. * Please see the description above about "monophonic playing". * _______________ * | noteon | * noteon_mono >------------------------>| mono_staccato |----> preset_noteon * |_______________| (with or without) * LOCAL /|\ (portamento) * | fromkey * | portamento * | * | * ______|________ * portamento modes >----->| | * | get_fromkey | * Porta.on/off >----------------------->|_______________| * Portamento * (PTC) * * We are in staccato situation (where no previous note have been depressed). * Before the note been passed to fluid_preset_noteon(), the function must determine * the from_key_portamento parameter used by fluid_preset_noteon(). * * from_key_portamento is returned by fluid_synth_get_fromkey_portamento_legato() function. * fromkey_portamento is set to valid/invalid key value depending of the portamento * modes (see portamento mode API) , CC portamento On/Off , and CC portamento control * (PTC). * * @param synth 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). * @return FLUID_OK on success, FLUID_FAILED otherwise. */ int fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel) { fluid_channel_t *channel = synth->channel[chan]; /* Before playing a new note, if a previous monophonic note is currently sustained it needs to be released */ fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, channel->key_mono_sustained); /* Get possible 'fromkey portamento' */ fluid_synth_get_fromkey_portamento_legato(channel, INVALID_NOTE); /* The note needs to be played by voices allocation */ return fluid_preset_noteon(channel->preset, synth, chan, key, vel); } /** * Plays noteoff for a polyphonic or monophonic note * Please see the description above about "monophonic playing". * * * noteOff poly >---------------------------------+ * | * | * | * noteoff_mono _____\|/_______ * LOCAL >------------------------->| noteoff | * | monopoly |----> noteoff * Sust.on/off >------------------------->|_______________| * Sost.on/off * * The function has the same behaviour when the noteoff is poly of mono, except * that for mono noteoff, if any pedal (sustain or sostenuto ) is depressed, the * key is memorized. This is necessary when the next mono note will be played * staccato, as any current mono note currently sustained will need to be released * (see fluid_synth_noteon_mono_staccato()). * Note also that for a monophonic legato passage, the function is called only when * the last noteoff of the passage occurs. That means that if sustain or sostenuto * is depressed, only the last note of a legato passage will be sustained. * * @param synth instance. * @param chan MIDI channel number (0 to MIDI channel count - 1). * @param key MIDI note number (0-127). * @param Mono, 1 noteoff on monophonic note. * 0 noteoff on polyphonic note. * @return FLUID_OK on success, FLUID_FAILED otherwise. * * Note: On return, on monophonic, possible sustained note is memorized in * key_mono_sustained. Memorization is done here on noteOff. */ int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, char Mono) { int status = FLUID_FAILED; fluid_voice_t *voice; int i; fluid_channel_t *channel = synth->channel[chan]; /* Key_sustained is prepared to return no note sustained (INVALID_NOTE) */ if(Mono) { channel->key_mono_sustained = INVALID_NOTE; /* no mono note sustained */ } /* noteoff for all voices with same chan and same key */ 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); /* noteoff on monophonic note */ /* Key memorization if the note is sustained */ if(Mono && (fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice))) { channel->key_mono_sustained = key; } status = FLUID_OK; } /* if voice on */ } /* for all voices */ return status; } /*---------------------------------------------------------------------------- legato playing -----------------------------------------------------------------------------*/ /** * Plays noteon for a monophonic note played legato. * Please see the description above about "monophonic playing". * * * _______________ * portamento modes >----->| | * | get_fromkey | * Porta.on/off >----------------------->|_______________| * Portamento | * (PTC) | +-->preset_noteon * fromkey | fromkey | (with or without) * legato | portamento| (portamento) * _____\|/_______ | * | noteon |--+ * noteon_mono >------------------------>| monopoly | * LOCAL | legato |----->voices * |_______________| triggering * /|\ (with or without) * | (portamento) * legato modes >-----------------+ * * We are in legato situation (where a previous note has been depressed). * The function must determine the from_key_portamento and from_key_legato parameters * used respectively by fluid_preset_noteon() function and voices triggering functions. * * from_key_portamento and from_key_legato are returned by * fluid_synth_get_fromkey_portamento_legato() function. * fromkey_portamento is set to valid/invalid key value depending of the portamento * modes (see portamento mode API), CC portamento On/Off, and CC portamento control * (PTC). * Then, depending of the legato modes (see legato mode API), the function will call * the appropriate triggering functions. * @param synth instance. * @param chan MIDI channel number (0 to MIDI channel count - 1). * @param fromkey MIDI note number (0-127). * @param tokey MIDI note number (0-127). * @param vel MIDI velocity (0-127). * @return FLUID_OK on success, FLUID_FAILED otherwise. * * Note: The voices with key 'fromkey' are to be used to play key 'tokey'. * The function is able to play legato through Preset Zone(s) (PZ) and * Instrument Zone(s) (IZ) as far as possible. * When key tokey is outside the current Instrument Zone, Preset Zone, * current 'fromkey' voices are released. If necessary new voices * are restarted when tokey enters inside new Instrument(s) Zones,Preset Zone(s). * More information in FluidPolyMono-0004.pdf chapter 4.7 (Appendices). */ int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, int fromkey, int tokey, int vel) { fluid_channel_t *channel = synth->channel[chan]; enum fluid_channel_legato_mode legatomode = channel->legatomode; fluid_voice_t *voice; int i ; /* Gets possible 'fromkey portamento' and possible 'fromkey legato' note */ fromkey = fluid_synth_get_fromkey_portamento_legato(channel, fromkey); if(fluid_channel_is_valid_note(fromkey)) { for(i = 0; i < synth->polyphony; i++) { /* searching fromkey voices: only those who don't have 'note off' */ voice = synth->voice[i]; if(fluid_voice_is_on(voice) && fluid_voice_get_channel(voice) == chan && fluid_voice_get_key(voice) == fromkey) { fluid_zone_range_t *zone_range = voice->zone_range; /* Ignores voice when there is no instrument zone (i.e no zone_range). Otherwise checks if tokey is inside the range of the running voice */ if(zone_range && fluid_zone_inside_range(zone_range, tokey, vel)) { switch(legatomode) { case FLUID_CHANNEL_LEGATO_MODE_RETRIGGER: /* mode 0 */ fluid_voice_release(voice); /* normal release */ break; case FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER: /* mode 1 */ /* Skip in attack section */ fluid_voice_update_multi_retrigger_attack(voice, tokey, vel); /* Starts portamento if enabled */ if(fluid_channel_is_valid_note(synth->fromkey_portamento)) { /* Sends portamento parameters to the voice dsp */ fluid_voice_update_portamento(voice, synth->fromkey_portamento, tokey); } /* The voice is now used to play tokey in legato manner */ /* Marks this Instrument Zone to be ignored during next fluid_preset_noteon() */ zone_range->ignore = TRUE; break; default: /* Invalid mode: this should never happen */ FLUID_LOG(FLUID_WARN, "Failed to execute legato mode: %d", legatomode); return FLUID_FAILED; } } else { /* tokey note is outside the voice range, so the voice is released */ fluid_voice_release(voice); } } } } /* May be,tokey will enter in new others Insrument Zone(s),Preset Zone(s), in this case it needs to be played by voices allocation */ return fluid_preset_noteon(channel->preset, synth, chan, tokey, vel); } fluidsynth-2.2.5/src/synth/fluid_tuning.c000066400000000000000000000104151417326347500205150ustar00rootroot00000000000000/* 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 "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; } FLUID_MEMSET(tuning, 0, sizeof(fluid_tuning_t)); if(fluid_tuning_set_name(tuning, name) != FLUID_OK) { delete_fluid_tuning(tuning); return NULL; } tuning->bank = bank; tuning->prog = prog; for(i = 0; i < 128; i++) { tuning->pitch[i] = i * 100.0; } fluid_atomic_int_set(&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; } FLUID_MEMSET(new_tuning, 0, sizeof(fluid_tuning_t)); if(fluid_tuning_set_name(new_tuning, tuning->name) != FLUID_OK) { delete_fluid_tuning(new_tuning); return NULL; } new_tuning->bank = tuning->bank; new_tuning->prog = tuning->prog; for(i = 0; i < 128; i++) { new_tuning->pitch[i] = tuning->pitch[i]; } fluid_atomic_int_set(&new_tuning->refcount, 1); /* Start with a refcount of 1 */ return new_tuning; } void delete_fluid_tuning(fluid_tuning_t *tuning) { fluid_return_if_fail(tuning != NULL); 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; } } int fluid_tuning_set_name(fluid_tuning_t *tuning, const char *name) { if(tuning->name != NULL) { FLUID_FREE(tuning->name); tuning->name = NULL; } if(name != NULL) { tuning->name = FLUID_STRDUP(name); if(tuning->name == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } } return FLUID_OK; } char *fluid_tuning_get_name(fluid_tuning_t *tuning) { return tuning->name; } 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-2.2.5/src/synth/fluid_tuning.h000066400000000000000000000042641417326347500205270ustar00rootroot00000000000000/* 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: https://www.midi.org/about-midi/tuning.htm https://www.midi.org/about-midi/tuning-scale.htm https://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 */ fluid_atomic_int_t 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); int fluid_tuning_set_name(fluid_tuning_t *tuning, const 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-2.2.5/src/synth/fluid_voice.c000066400000000000000000002135201417326347500203200ustar00rootroot00000000000000/* 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" #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" #include "fluid_defsfont.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 const int32_t INT24_MAX = (1 << (16 + 8 - 1)); 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 { \ fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ fluid_rvoice_eventhandler_push(voice->eventhandler, proc, voice->rvoice, param); \ } while (0) #define UPDATE_RVOICE_GENERIC_R1(proc, obj, rarg) \ do { \ fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ param[0].real = rarg; \ fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ } while (0) #define UPDATE_RVOICE_GENERIC_I1(proc, obj, iarg) \ do { \ fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ param[0].i = iarg; \ fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ } while (0) #define UPDATE_RVOICE_GENERIC_I2(proc, obj, iarg1, iarg2) \ do { \ fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ param[0].i = iarg1; \ param[1].i = iarg2; \ fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ } while (0) #define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \ do { \ fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ param[0].i = iarg; \ param[1].real = rarg; \ fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ } while (0) #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_BUFFERS_AMP(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 FLUID_INLINE void fluid_voice_update_volenv(fluid_voice_t *voice, int enqueue, 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_rvoice_param_t param[MAX_EVENT_PARAMS]; param[0].i = section; param[1].i = count; param[2].real = coeff; param[3].real = increment; param[4].real = min; param[5].real = max; if(enqueue) { fluid_rvoice_eventhandler_push(voice->eventhandler, fluid_adsr_env_set_data, &voice->rvoice->envlfo.volenv, param); } else { fluid_adsr_env_set_data(&voice->rvoice->envlfo.volenv, param); } } static FLUID_INLINE void fluid_voice_update_modenv(fluid_voice_t *voice, int enqueue, 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_rvoice_param_t param[MAX_EVENT_PARAMS]; param[0].i = section; param[1].i = count; param[2].real = coeff; param[3].real = increment; param[4].real = min; param[5].real = max; if(enqueue) { fluid_rvoice_eventhandler_push(voice->eventhandler, fluid_adsr_env_set_data, &voice->rvoice->envlfo.modenv, param); } else { fluid_adsr_env_set_data(&voice->rvoice->envlfo.modenv, param); } } static FLUID_INLINE void fluid_voice_sample_unref(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; voice->overflow_sample = voice->sample; } static void fluid_voice_initialize_rvoice(fluid_voice_t *voice, fluid_real_t output_rate) { fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; 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, FALSE, FLUID_VOICE_ENVSUSTAIN, 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVFINISHED, 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN, 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVFINISHED, 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); param[0].i = FLUID_IIR_LOWPASS; param[1].i = 0; fluid_iir_filter_init(&voice->rvoice->resonant_filter, param); param[0].i = FLUID_IIR_DISABLED; fluid_iir_filter_init(&voice->rvoice->resonant_custom_filter, param); param[0].real = output_rate; fluid_rvoice_set_output_rate(voice->rvoice, param); } /* * new_fluid_voice */ fluid_voice_t * new_fluid_voice(fluid_rvoice_eventhandler_t *handler, 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->can_access_rvoice = TRUE; voice->can_access_overflow_rvoice = TRUE; 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"); delete_fluid_voice(voice); return NULL; } voice->status = FLUID_VOICE_CLEAN; voice->chan = NO_CHANNEL; voice->key = 0; voice->vel = 0; voice->eventhandler = handler; voice->channel = NULL; voice->sample = NULL; voice->overflow_sample = NULL; voice->output_rate = output_rate; /* Initialize both the rvoice and overflow_rvoice */ fluid_voice_initialize_rvoice(voice, output_rate); fluid_voice_swap_rvoice(voice); fluid_voice_initialize_rvoice(voice, output_rate); return voice; } /* * delete_fluid_voice */ void delete_fluid_voice(fluid_voice_t *voice) { fluid_return_if_fail(voice != NULL); if(!voice->can_access_rvoice || !voice->can_access_overflow_rvoice) { FLUID_LOG(FLUID_WARN, "Deleting voice %u which has locked rvoices!", voice->id); } FLUID_FREE(voice->overflow_rvoice); FLUID_FREE(voice->rvoice); FLUID_FREE(voice); } /* fluid_voice_init * * Initialize the synthesis process * inst_zone, the Instrument Zone contains the sample, Keyrange,Velrange * of the voice. * When playing legato (n1,n2) in mono mode, n2 will use n1 voices * as far as n2 still enters in Keyrange,Velrange of n1. */ int fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample, fluid_zone_range_t *inst_zone_range, 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->zone_range = inst_zone_range; /* Instrument zone range for legato */ 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->has_noteoff = 0; UPDATE_RVOICE0(fluid_rvoice_reset); /* We increment the reference count of the sample to indicate that this sample is about to be owned by the rvoice. This will prevent the unloading of the soundfont while this rvoice is playing. */ fluid_sample_incr_ref(sample); fluid_rvoice_eventhandler_push_ptr(voice->eventhandler, fluid_rvoice_set_sample, voice->rvoice, sample); 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.0000001f) { voice->synth_gain = 0.0000001f; } UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain); /* Set up buffer mapping, should be done more flexible in the future. */ i = 2 * channel->synth->audio_groups; i += (voice->chan % channel->synth->effects_groups) * channel->synth->effects_channels; UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 2, i + SYNTH_REVERB_CHANNEL); UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 3, i + SYNTH_CHORUS_CHANNEL); i = 2 * (voice->chan % channel->synth->audio_groups); UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 0, i); UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 1, i + 1); return FLUID_OK; } /** * Update sample rate. * * @note If the voice is active, it will be turned off. */ void 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_GENERIC_R1(fluid_rvoice_set_output_rate, voice->rvoice, value); UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->overflow_rvoice, value); } /** * 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(const fluid_voice_t *voice, int num) { return (fluid_real_t)(voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn); } /* * 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); #ifdef WITH_PROFILING voice->ref = fluid_profile_ref(); #endif voice->status = FLUID_VOICE_ON; /* Increment voice count */ voice->channel->synth->active_voice_count++; } /** * Calculate the amplitude of a voice. * * @param gain The gain value in the range [0.0 ; 1.0] * @return An amplitude used by rvoice_mixer's buffers */ static FLUID_INLINE fluid_real_t fluid_voice_calculate_gain_amplitude(const fluid_voice_t *voice, fluid_real_t gain) { /* we use 24bit samples in fluid_rvoice_dsp. in order to normalize float * samples to [0.0;1.0] divide samples by the max. value of an int24 and * amplify them with the gain */ return gain * voice->synth_gain / (INT24_MAX * 1.0f); } /* Useful to return the nominal pitch of a key */ /* The nominal pitch is dependent of voice->root_pitch,tuning, and GEN_SCALETUNE generator. This is useful to set the value of GEN_PITCH generator on noteOn. This is useful to get the beginning/ending pitch for portamento. */ fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key) { fluid_tuning_t *tuning; fluid_real_t x, pitch; /* Now the nominal pitch of the key is returned. * 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 fluid_voice_gen_value(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)); pitch = voice->gen[GEN_SCALETUNE].val / 100.0f * (fluid_tuning_get_pitch(tuning, key) - x) + x; } else { pitch = voice->gen[GEN_SCALETUNE].val * (key - voice->root_pitch / 100.0f) + voice->root_pitch; } return pitch; } void fluid_voice_calculate_gen_pitch(fluid_voice_t *voice) { voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice)); } /* * fluid_voice_calculate_runtime_synthesis_parameters * * 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; unsigned int n; static int const list_of_generators_to_initialize[] = { 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, /* --- */ GEN_CUSTOM_BALANCE, /* --- */ GEN_CUSTOM_FILTERFC, /* --- */ GEN_CUSTOM_FILTERQ /* --- */ }; /* 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_init(). */ 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); 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(n = 0; n < FLUID_N_ELEMENTS(list_of_generators_to_initialize); n++) { fluid_voice_update_param(voice, list_of_generators_to_initialize[n]); } /* Start portamento if enabled */ { /* fromkey note comes from "GetFromKeyPortamentoLegato()" detector. When fromkey is set to ValidNote , portamento is started */ /* Return fromkey portamento */ int fromkey = voice->channel->synth->fromkey_portamento; if(fluid_channel_is_valid_note(fromkey)) { /* Send portamento parameters to the voice dsp */ fluid_voice_update_portamento(voice, fromkey, fluid_voice_get_actual_key(voice)); } } /* 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 keysteps; 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. */ keysteps = 60.0f - fluid_channel_get_key_pitch(voice->channel, fluid_voice_get_actual_key(voice)) / 100.0f; timecents = fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * keysteps; /* Range checking */ if(is_decay) { /* SF 2.01 section 8.1.3 # 28, 36 */ if(timecents > 8000.f) { timecents = 8000.f; } } else { /* SF 2.01 section 8.1.3 # 27, 35 */ if(timecents > 5000.f) { timecents = 5000.f; } /* SF 2.01 section 8.1.2 # 27, 35: * The most negative number indicates no hold time */ if(timecents <= -32768.f) { return 0; } } /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */ if(timecents < -12000.f) { timecents = -12000.f; } 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.5f); 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. fluid_voice_gen_value(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) * * Calling this function is only necessary after changing a generator of an already playing voice. */ void fluid_voice_update_param(fluid_voice_t *voice, int gen) { unsigned int count, z; fluid_real_t x = fluid_voice_gen_value(voice, gen); switch(gen) { case GEN_PAN: case GEN_CUSTOM_BALANCE: /* range checking is done in the fluid_pan and fluid_balance functions */ voice->pan = fluid_voice_gen_value(voice, GEN_PAN); voice->balance = fluid_voice_gen_value(voice, GEN_CUSTOM_BALANCE); /* left amp */ UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, fluid_voice_calculate_gain_amplitude(voice, fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1))); /* right amp */ UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, fluid_voice_calculate_gain_amplitude(voice, fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0))); break; case GEN_ATTENUATION: voice->attenuation = x; /* 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.f, 1440.f); 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 = (fluid_voice_gen_value(voice, GEN_PITCH) + 100.0f * fluid_voice_gen_value(voice, GEN_COARSETUNE) + fluid_voice_gen_value(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 = x / 1000.0f; fluid_clip(voice->reverb_send, 0.f, 1.f); UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send)); break; case GEN_CHORUSSEND: /* The generator unit is 'tenths of a percent'. */ voice->chorus_send = x / 1000.0f; fluid_clip(voice->chorus_send, 0.f, 1.f); UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send)); 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_real(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_real(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] */ UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_filter, x); break; case GEN_FILTERQ: UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_filter, x); break; /* same as the two above, only for the custom filter */ case GEN_CUSTOM_FILTERFC: UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_custom_filter, x); break; case GEN_CUSTOM_FILTERQ: UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_custom_filter, x); break; case GEN_MODLFOTOPITCH: fluid_clip(x, -12000.f, 12000.f); UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x); break; case GEN_MODLFOTOVOL: fluid_clip(x, -960.f, 960.f); UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x); break; case GEN_MODLFOTOFILTERFC: fluid_clip(x, -12000.f, 12000.f); UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x); break; case 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 */ 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 */ 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: 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: fluid_clip(x, -12000.f, 12000.f); 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 = fluid_voice_gen_value(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, use * fluid_voice_get_actual_velocity() to get the value of this generator * if active. */ #if 0 x = fluid_voice_gen_value(voice, GEN_VELOCITY); if(x > 0) { voice->vel = x; } #endif break; case GEN_MODENVTOPITCH: fluid_clip(x, -12000.f, 12000.f); UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x); break; case 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.f, 12000.f); 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) { fluid_real_t start_fine = fluid_voice_gen_value(voice, GEN_STARTADDROFS); fluid_real_t start_coar = fluid_voice_gen_value(voice, GEN_STARTADDRCOARSEOFS); z = voice->sample->start + (int)start_fine + 32768 * (int)start_coar; 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) { fluid_real_t end_fine = fluid_voice_gen_value(voice, GEN_ENDADDROFS); fluid_real_t end_coar = fluid_voice_gen_value(voice, GEN_ENDADDRCOARSEOFS); z = voice->sample->end + (int)end_fine + 32768 * (int)end_coar; 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) { fluid_real_t lstart_fine = fluid_voice_gen_value(voice, GEN_STARTLOOPADDROFS); fluid_real_t lstart_coar = fluid_voice_gen_value(voice, GEN_STARTLOOPADDRCOARSEOFS); z = voice->sample->loopstart + (int)lstart_fine + 32768 * (int)lstart_coar; 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) { fluid_real_t lend_fine = fluid_voice_gen_value(voice, GEN_ENDLOOPADDROFS); fluid_real_t lend_coar = fluid_voice_gen_value(voice, GEN_ENDLOOPADDRCOARSEOFS); z = voice->sample->loopend + (int)lend_fine + 32768 * (int)lend_coar; 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 */ fluid_clip(x, -12000.0f, 5000.0f); count = NUM_BUFFERS_DELAY(x); fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDELAY, count, 0.0f, 0.0f, -1.0f, 1.0f); break; case GEN_VOLENVATTACK: /* SF2.01 section 8.1.3 # 34 */ fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_BUFFERS_ATTACK(x); fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVATTACK, count, 1.0f, 1.0f / count, -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, TRUE, 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 */ x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_VOLENVSUSTAIN); fluid_clip(x, 0.0f, 1.0f); count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */ fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDECAY, count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f); break; case GEN_VOLENVRELEASE: /* SF2.01 section 8.1.3 # 38 */ fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f); count = 1 + NUM_BUFFERS_RELEASE(x); fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVRELEASE, count, 1.0f, -1.0f / count, 0.0f, 1.0f); break; /* Modulation envelope */ case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */ fluid_clip(x, -12000.0f, 5000.0f); fluid_voice_update_modenv(voice, TRUE, 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 */ fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_BUFFERS_ATTACK(x); fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVATTACK, count, 1.0f, 1.0f / count, -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, TRUE, 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 */ x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_MODENVSUSTAIN); fluid_clip(x, 0.0f, 1.0f); fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDECAY, count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f); break; case GEN_MODENVRELEASE: /* SF 2.01 section 8.1.3 # 30 */ fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_BUFFERS_RELEASE(x); fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVRELEASE, count, 1.0f, -1.0f / count, 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 continuous control and a channel control (pitch bend, ...) * @param ctrl the control number: * when >=0, only modulators's destination having ctrl as source are updated. * when -1, all modulators's destination are updated (regardless of ctrl). * * In this implementation, I want to make sure that all controllers * are event based: the parameter values of the DSP algorithm should * 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 two steps: * * - step 1: first, we look for all the modulators that have the changed * controller as a source. This will yield a generator that will be changed * because of the controller event. * * - step 2: For this generator, calculate its new value. This is the * sum of its original value plus the values of all the attached modulators. * The generator flag is set to indicate the parameters must be updated. * This avoid the risk to call 'fluid_voice_update_param' several * times for the same generator if several modulators have that generator as * destination. So every changed generators are updated only once. */ /* bit table for each generator being updated. The bits are packed in variables Each variable have NBR_BIT_BY_VAR bits represented by NBR_BIT_BY_VAR_LN2. The size of the table is the number of variables: SIZE_UPDATED_GEN_BIT. Note: In this implementation NBR_BIT_BY_VAR_LN2 is set to 5 (convenient for 32 bits cpu) but this could be set to 6 for 64 bits cpu. */ #define NBR_BIT_BY_VAR_LN2 5 /* for 32 bits variables */ #define NBR_BIT_BY_VAR (1 << NBR_BIT_BY_VAR_LN2) #define NBR_BIT_BY_VAR_ANDMASK (NBR_BIT_BY_VAR - 1) #define SIZE_UPDATED_GEN_BIT ((GEN_LAST + NBR_BIT_BY_VAR_ANDMASK) / NBR_BIT_BY_VAR) #define is_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] & (1 << (gen & NBR_BIT_BY_VAR_ANDMASK))) #define set_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] |= (1 << (gen & NBR_BIT_BY_VAR_ANDMASK))) int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) { int i, k; fluid_mod_t *mod; uint32_t gen; fluid_real_t modval; /* Clears registered bits table of updated generators */ uint32_t updated_gen_bit[SIZE_UPDATED_GEN_BIT] = {0}; /* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */ for(i = 0; i < voice->mod_count; i++) { mod = &voice->mod[i]; /* step 1: find all the modulators that have the changed controller as input source. When ctrl is -1 all modulators destination are updated */ if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl)) { gen = fluid_mod_get_dest(mod); /* Skip if this generator has already been updated */ if(!is_gen_updated(updated_gen_bit, gen)) { modval = 0.0; /* step 2: for every attached modulator, calculate the modulation * value for the generator gen */ for(k = 0; k < voice->mod_count; k++) { if(fluid_mod_has_dest(&voice->mod[k], gen)) { modval += fluid_mod_get_value(&voice->mod[k], voice); } } fluid_gen_set_mod(&voice->gen[gen], modval); /* now recalculate the parameter values that are derived from the generator */ fluid_voice_update_param(voice, gen); /* set the bit that indicates this generator is updated */ set_gen_updated(updated_gen_bit, gen); } } } return FLUID_OK; } /** * Update all the modulators. * * This function is called after a ALL_CTRL_OFF MIDI message has been received (CC 121). * All destinations of all modulators will be updated. */ int fluid_voice_modulate_all(fluid_voice_t *voice) { return fluid_voice_modulate(voice, 0, -1); } /* legato update functions --------------------------------------------------*/ /* Updates voice portamento parameters * * @voice voice the synthesis voice * @fromkey the beginning pitch of portamento. * @tokey the ending pitch of portamento. * * The function calculates pitch offset and increment, then these parameters * are send to the dsp. */ void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey) { fluid_channel_t *channel = voice->channel; /* calculates pitch offset */ fluid_real_t PitchBeg = fluid_voice_calculate_pitch(voice, fromkey); fluid_real_t PitchEnd = fluid_voice_calculate_pitch(voice, tokey); fluid_real_t pitchoffset = PitchBeg - PitchEnd; /* Calculates increment countinc */ /* Increment is function of PortamentoTime (ms)*/ unsigned int countinc = (unsigned int)(((fluid_real_t)voice->output_rate * 0.001f * (fluid_real_t)fluid_channel_portamentotime(channel)) / (fluid_real_t)FLUID_BUFSIZE + 0.5f); /* Send portamento parameters to the voice dsp */ UPDATE_RVOICE_GENERIC_IR(fluid_rvoice_set_portamento, voice->rvoice, countinc, pitchoffset); } /*---------------------------------------------------------------*/ /*legato mode 1: multi_retrigger * * Modulates all generators dependent of key,vel. * Forces the voice envelopes in the attack section (legato mode 1). * * @voice voice the synthesis voice * @tokey the new key to be applied to this voice. * @vel the new velocity to be applied to this voice. */ void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice, int tokey, int vel) { voice->key = tokey; /* new note */ voice->vel = vel; /* new velocity */ /* Updates generators dependent of velocity */ /* Modulates GEN_ATTENUATION (and others ) before calling fluid_rvoice_multi_retrigger_attack().*/ fluid_voice_modulate(voice, FALSE, FLUID_MOD_VELOCITY); /* Updates generator dependent of voice->key */ fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD); fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY); fluid_voice_update_param(voice, GEN_KEYTOVOLENVHOLD); fluid_voice_update_param(voice, GEN_KEYTOVOLENVDECAY); /* Updates pitch generator */ fluid_voice_calculate_gen_pitch(voice); fluid_voice_update_param(voice, GEN_PITCH); /* updates adsr generator */ UPDATE_RVOICE0(fluid_rvoice_multi_retrigger_attack); } /** end of legato update functions */ /* 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 occurred } /* * fluid_voice_noteoff * * Sending a noteoff event will advance the envelopes to section 5 (release). * The function is convenient for polyphonic or monophonic note */ void fluid_voice_noteoff(fluid_voice_t *voice) { fluid_channel_t *channel; fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref, 0, 0); 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); } } /* * 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; } /* * Unlock the overflow rvoice of the voice. * Decrement the reference count of the sample owned by this rvoice. * * Called by fluid_synth when the overflow rvoice has finished by itself. * Must be called also explicitly at synth destruction to ensure that * the soundfont be unloaded successfully. */ void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice) { voice->can_access_overflow_rvoice = 1; /* Decrement the reference count of the sample to indicate that this sample isn't owned by the rvoice anymore */ fluid_voice_sample_unref(&voice->overflow_sample); } /* * 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, 0, 0); voice->chan = NO_CHANNEL; /* Decrement the reference count of the sample, to indicate that this sample isn't owned by the rvoice anymore. */ fluid_voice_sample_unref(&voice->sample); voice->status = FLUID_VOICE_OFF; voice->has_noteoff = 1; /* Decrement voice count */ voice->channel->synth->active_voice_count--; } /** * Adds a modulator to the voice if the modulator has valid sources. * * @param voice Voice instance. * @param mod Modulator info (copied). * @param mode Determines how to handle an existing identical modulator. * #FLUID_VOICE_ADD to add (offset) the modulator amounts, * #FLUID_VOICE_OVERWRITE to replace the modulator, * #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should * exist so don't check. */ void fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode) { /* Ignore the modulator if its sources inputs are invalid */ if(fluid_mod_check_sources(mod, "api fluid_voice_add_mod mod")) { fluid_voice_add_mod_local(voice, mod, mode, FLUID_NUM_MOD); } } /** * Adds a modulator to the voice. * local version of fluid_voice_add_mod function. Called at noteon time. * @param voice, mod, mode, same as for fluid_voice_add_mod() (see above). * @param check_limit_count is the modulator number limit to handle with existing * identical modulator(i.e mode FLUID_VOICE_OVERWRITE, FLUID_VOICE_ADD). * - When FLUID_NUM_MOD, all the voices modulators (since the previous call) * are checked for identity. * - When check_count_limit is below the actual number of voices modulators * (voice->mod_count), this will restrict identity check to this number, * This is useful when we know by advance that there is no duplicate with * modulators at index above this limit. This avoid wasting cpu cycles at noteon. */ void fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count) { int i; /* check_limit_count cannot be above voice->mod_count */ if(check_limit_count > voice->mod_count) { check_limit_count = voice->mod_count; } if(mode == FLUID_VOICE_ADD) { /* if identical modulator exists, add them */ for(i = 0; i < check_limit_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 < check_limit_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 pointers to voices 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 internally which are * 'recycled' and never deallocated. * * However, 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. * * Like fluid_voice_is_on() this will return TRUE once a call to * fluid_synth_start_voice() has been made. Contrary to fluid_voice_is_on(), * this might also return TRUE after the voice received a noteoff event, as it may * still be playing in release phase, or because it has been sustained or * sostenuto'ed. * * @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 in ON state as soon as a call to fluid_synth_start_voice() has been made * (which is typically done in a fluid_preset_t's noteon function). * A voice stays ON as long as it has not 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); } /** * Return the MIDI channel the voice is playing on. * * @param voice Voice instance * @return The channel assigned to this voice * * @note The result of this function is only valid if the voice is playing. * * @since 1.1.7 */ int fluid_voice_get_channel(const fluid_voice_t *voice) { return voice->chan; } /** * Return the effective MIDI key of the playing voice. * * @param voice Voice instance * @return The MIDI key this voice is playing at * * If the voice was started from an instrument which uses a fixed key generator, it returns that. * Otherwise returns the same value as \c fluid_voice_get_key. * * @note The result of this function is only valid if the voice is playing. * * @since 1.1.7 */ int fluid_voice_get_actual_key(const fluid_voice_t *voice) { fluid_real_t x = fluid_voice_gen_value(voice, GEN_KEYNUM); if(x >= 0) { return (int)x; } else { return fluid_voice_get_key(voice); } } /** * Return the MIDI key from the starting noteon event. * * @param voice Voice instance * @return The MIDI key of the noteon event that originally turned on this voice * * @note The result of this function is only valid if the voice is playing. * * @since 1.1.7 */ int fluid_voice_get_key(const fluid_voice_t *voice) { return voice->key; } /** * Return the effective MIDI velocity of the playing voice. * * @param voice Voice instance * @return The MIDI velocity this voice is playing at * * If the voice was started from an instrument which uses a fixed velocity generator, it returns that. * Otherwise it returns the same value as \c fluid_voice_get_velocity. * * @note The result of this function is only valid if the voice is playing. * * @since 1.1.7 */ int fluid_voice_get_actual_velocity(const fluid_voice_t *voice) { fluid_real_t x = fluid_voice_gen_value(voice, GEN_VELOCITY); if(x > 0) { return (int)x; } else { return fluid_voice_get_velocity(voice); } } /** * Return the MIDI velocity from the starting noteon event. * * @param voice Voice instance * @return The MIDI velocity which originally turned on this voice * * @note The result of this function is only valid if the voice is playing. * * @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_start, after * all modulators have been run on the voice once. Also, * voice->attenuation has to be initialized. * (see fluid_voice_calculate_runtime_synthesis_parameters()) */ static fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice) { 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) || (mod->src1 == FLUID_MOD_CHANNELPRESSURE) || (mod->src1 == FLUID_MOD_KEYPRESSURE) || (mod->src1 == FLUID_MOD_PITCHWHEEL) || (mod->src2 == FLUID_MOD_CHANNELPRESSURE) || (mod->src2 == FLUID_MOD_KEYPRESSURE) || (mod->src2 == FLUID_MOD_PITCHWHEEL))) { fluid_real_t current_val = fluid_mod_get_value(mod, voice); /* min_val is the possible minimum value for this modulator. it depends of 3 things : 1)the minimum values of src1,src2 (i.e -1 if mapping is bipolar or 0 if mapping is unipolar). 2)the sign of amount. 3)absolute value of amount. When at least one source mapping is bipolar: min_val is -|amount| regardless the sign of amount. When both sources mapping are unipolar: min_val is -|amount|, if amount is negative. min_val is 0, if amount is positive */ fluid_real_t min_val = fabs(mod->amount); /* Can this modulator produce a negative contribution? */ if((mod->flags1 & FLUID_MOD_BIPOLAR) || (mod->flags2 & FLUID_MOD_BIPOLAR) || (mod->amount < 0)) { min_val = -min_val; /* min_val = - |amount|*/ } else { /* No negative value possible. But still, the minimum contribution is 0. */ min_val = 0; } /* For example: * - current_val=100 * - min_val=-4000 * - possible reduction contribution of this modulator = current_val - min_val = 4100 */ if(current_val > min_val) { possible_att_reduction_cB += (current_val - min_val); } } } 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) { voice->gen[gen].nrpn = nrpn_value; voice->gen[gen].flags = GEN_SET; fluid_voice_update_param(voice, gen); return FLUID_OK; } int fluid_voice_set_gain(fluid_voice_t *voice, fluid_real_t gain) { fluid_real_t left, right, reverb, chorus; /* avoid division by zero*/ if(gain < 0.0000001f) { gain = 0.0000001f; } voice->synth_gain = gain; left = fluid_voice_calculate_gain_amplitude(voice, fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1)); right = fluid_voice_calculate_gain_amplitude(voice, fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0)); reverb = fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send); chorus = fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send); UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain); UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, left); UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, right); UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, reverb); UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, 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) { int32_t peak_max = 0; int32_t peak_min = 0; int32_t peak; fluid_real_t normalized_amplitude_during_loop; double result; unsigned int i; /* ignore disabled samples */ if(s->start == s->end) { return (FLUID_OK); } if(!s->amplitude_that_reaches_noise_floor_is_valid) /* Only once */ { /* Scan the loop */ for(i = s->loopstart; i < s->loopend; i++) { int32_t val = fluid_rvoice_get_sample(s->data, s->data24, 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) / (INT24_MAX * 1.0f); 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; } float fluid_voice_get_overflow_prio(fluid_voice_t *voice, fluid_overflow_prio_t *score, unsigned int cur_time) { float this_voice_prio = 0; int channel; /* 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.1f) { a = 0.1f; // Avoid div by zero } this_voice_prio += score->volume / a; } /* Check if this voice is on an important channel. If so, then add the * score for important channels */ channel = fluid_voice_get_channel(voice); if(channel < score->num_important_channels && score->important_channels[channel]) { this_voice_prio += score->important; } return this_voice_prio; } void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags) { UPDATE_RVOICE_GENERIC_I2(fluid_iir_filter_init, &voice->rvoice->resonant_custom_filter, type, flags); } fluidsynth-2.2.5/src/synth/fluid_voice.h000066400000000000000000000157641417326347500203370ustar00rootroot00000000000000/* 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_rvoice_event.h" #define NO_CHANNEL 0xff typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t; struct _fluid_overflow_prio_t { float percussion; /**< Is this voice on the drum channel? Then add this score */ float released; /**< Is this voice in release stage? Then add this score (usually negative) */ float sustained; /**< Is this voice sustained? Then add this score (usually negative) */ float volume; /**< Multiply current (or future) volume (a value between 0 and 1) */ float age; /**< This score will be divided by the number of seconds the voice has lasted */ float important; /**< This score will be added to all important channels */ char *important_channels; /**< "important" flags indexed by MIDI channel number */ int num_important_channels; /**< Number of elements in the important_channels array */ }; 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_rvoice_eventhandler_t *eventhandler; fluid_zone_range_t *zone_range; /* instrument zone range*/ fluid_sample_t *sample; /* Pointer to sample (dupe in rvoice) */ fluid_sample_t *overflow_sample; /* Pointer to sample (dupe in overflow_rvoice) */ unsigned int start_time; int mod_count; fluid_mod_t mod[FLUID_NUM_MOD]; fluid_gen_t gen[GEN_LAST]; /* basic parameters */ fluid_real_t output_rate; /* the sample rate of the synthesizer (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; /* balance */ fluid_real_t balance; /* reverb */ fluid_real_t reverb_send; /* chorus */ fluid_real_t chorus_send; /* rvoice control */ fluid_rvoice_t *rvoice; fluid_rvoice_t *overflow_rvoice; /* Used temporarily and only in overflow situations */ char can_access_rvoice; /* False if rvoice is being rendered in separate thread */ char can_access_overflow_rvoice; /* False if overflow_rvoice is being rendered in separate thread */ char has_noteoff; /* Flag set when noteoff has been sent */ #ifdef WITH_PROFILING /* for debugging */ double ref; #endif }; fluid_voice_t *new_fluid_voice(fluid_rvoice_eventhandler_t *handler, fluid_real_t output_rate); void 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_init(fluid_voice_t *voice, fluid_sample_t *sample, fluid_zone_range_t *inst_zone_range, 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); /** Set the gain. */ int fluid_voice_set_gain(fluid_voice_t *voice, fluid_real_t gain); void 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); /** legato modes */ /* force in the attack section for legato mode multi_retrigger: 1 */ void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice, int tokey, int vel); /* Update portamento parameter */ void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey); void fluid_voice_release(fluid_voice_t *voice); void fluid_voice_noteoff(fluid_voice_t *voice); void fluid_voice_off(fluid_voice_t *voice); void fluid_voice_stop(fluid_voice_t *voice); void fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count); void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice); int fluid_voice_kill_excl(fluid_voice_t *voice); float 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 void fluid_voice_lock_rvoice(fluid_voice_t *voice) { voice->can_access_rvoice = 0; } /** * 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) fluid_real_t fluid_voice_gen_value(const fluid_voice_t *voice, int num); void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags); #endif /* _FLUID_VOICE_H */ fluidsynth-2.2.5/src/utils/000077500000000000000000000000001417326347500156545ustar00rootroot00000000000000fluidsynth-2.2.5/src/utils/fluid_conv.c000066400000000000000000000163121417326347500201530ustar00rootroot00000000000000/* 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" #include "fluid_sys.h" #include "fluid_conv_tables.inc.h" /* * Converts absolute cents to Hertz * * As per sfspec section 9.3: * * ABSOLUTE CENTS - An absolute logarithmic measure of frequency based on a * reference of MIDI key number scaled by 100. * A cent is 1/1200 of an octave [which is the twelve hundredth root of two], * and value 6900 is 440 Hz (A-440). * * Implemented below basically is the following: * 440 * 2^((cents-6900)/1200) * = 440 * 2^((int)((cents-6900)/1200)) * 2^(((int)cents-6900)%1200)) * = 2^((int)((cents-6900)/1200)) * (440 * 2^(((int)cents-6900)%1200))) * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * This second factor is stored in the lookup table. * * The first factor can be implemented with a fast shift when the exponent * is always an int. This is the case when using 440/2^6 Hz rather than 440Hz * reference. */ fluid_real_t fluid_ct2hz_real(fluid_real_t cents) { if(FLUID_UNLIKELY(cents < 0)) { return (fluid_real_t) 1.0; } else { unsigned int mult, fac, rem; unsigned int icents = (unsigned int)cents; icents += 300u; // don't use stdlib div() here, it turned out have poor performance fac = icents / 1200u; rem = icents % 1200u; // Think of "mult" as the factor that we multiply (440/2^6)Hz with, // or in other words mult is the "first factor" of the above // functions comment. // // Assuming sizeof(uint)==4 this will give us a maximum range of // 32 * 1200cents - 300cents == 38100 cents == 29,527,900,160 Hz // which is much more than ever needed. For bigger values, just // safely wrap around (the & is just a replacement for the quick // modulo operation % 32). mult = 1u << (fac & (sizeof(mult)*8u - 1u)); // don't use ldexp() either (poor performance) return mult * fluid_ct2hz_tab[rem]; } } /* * 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 1440, 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_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.f) { tc = (fluid_real_t) -12000.0f; } if(tc > 5000.0f) { tc = (fluid_real_t) 5000.0f; } return FLUID_POW(2.f, tc / 1200.f); } /* * 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.f) { return (fluid_real_t) 0.f; }; if(tc < -12000.f) { tc = (fluid_real_t) -12000.f; }; if(tc > 8000.f) { tc = (fluid_real_t) 8000.f; }; return FLUID_POW(2.f, tc / 1200.f); } /* * fluid_tc2sec */ fluid_real_t fluid_tc2sec(fluid_real_t tc) { /* No range checking here! */ return FLUID_POW(2.f, tc / 1200.f); } /* * 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.f) { return (fluid_real_t) 0.f; }; if(tc < -12000.f) { tc = (fluid_real_t) -12000.f; }; if(tc > 8000.f) { tc = (fluid_real_t) 8000.f; }; return FLUID_POW(2.f, tc / 1200.f); } /* * fluid_act2hz * * Convert from absolute cents to Hertz * * The inverse operation, converting from Hertz to cents, was unused and implemented as * fluid_hz2ct(fluid_real_t f) { return 6900.f + (1200.f / FLUID_M_LN2) * FLUID_LOGF(f / 440.0f)); } */ fluid_real_t fluid_act2hz(fluid_real_t c) { return 8.176f * FLUID_POW(2.f, c / 1200.f); } /* * fluid_pan */ fluid_real_t fluid_pan(fluid_real_t c, int left) { if(left) { c = -c; } if(c <= -500.f) { return (fluid_real_t) 0.f; } else if(c >= 500.f) { return (fluid_real_t) 1.f; } else { return fluid_pan_tab[(int)(c) + 500]; } } /* * Return the amount of attenuation based on the balance for the specified * channel. If balance is negative (turned toward left channel, only the right * channel is attenuated. If balance is positive, only the left channel is * attenuated. * * @params balance left/right balance, range [-960;960] in absolute centibels * @return amount of attenuation [0.0;1.0] */ fluid_real_t fluid_balance(fluid_real_t balance, int left) { /* This is the most common case */ if(balance == 0.f) { return 1.0f; } if((left && balance < 0.f) || (!left && balance > 0.f)) { return 1.0f; } if(balance < 0.f) { balance = -balance; } return fluid_cb2amp(balance); } /* * fluid_concave */ fluid_real_t fluid_concave(fluid_real_t val) { if(val < 0.f) { return 0.f; } else if(val >= (fluid_real_t)FLUID_VEL_CB_SIZE) { return 1.f; } return fluid_concave_tab[(int) val]; } /* * fluid_convex */ fluid_real_t fluid_convex(fluid_real_t val) { if(val < 0.f) { return 0.f; } else if(val >= (fluid_real_t)FLUID_VEL_CB_SIZE) { return 1.f; } return fluid_convex_tab[(int) val]; } fluidsynth-2.2.5/src/utils/fluid_conv.h000066400000000000000000000030341417326347500201550ustar00rootroot00000000000000/* 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" #include "utils/fluid_conv_tables.h" fluid_real_t fluid_ct2hz_real(fluid_real_t cents); fluid_real_t fluid_ct2hz(fluid_real_t cents); fluid_real_t fluid_cb2amp(fluid_real_t cb); 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_pan(fluid_real_t c, int left); fluid_real_t fluid_balance(fluid_real_t balance, int left); fluid_real_t fluid_concave(fluid_real_t val); fluid_real_t fluid_convex(fluid_real_t val); #endif /* _FLUID_CONV_H */ fluidsynth-2.2.5/src/utils/fluid_conv_tables.h000066400000000000000000000032701417326347500215110ustar00rootroot00000000000000 #ifndef _FLUID_CONV_TABLES_H #define _FLUID_CONV_TABLES_H /* Attenuation range in centibels. Attenuation range is the dynamic range of the volume envelope generator from 0 to the end of attack segment. fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation. However the spec makes no distinction between 16 or 24 bit synths, so use 96 dB here. Note about usefulness of 24 bits: 1)Even fluidsynth is a 24 bit synth, this format is only relevant if the sample format coming from the soundfont is 24 bits and the audio sample format chosen by the application (audio.sample.format) is not 16 bits. 2)When the sample soundfont is 16 bits, the internal 24 bits number have 16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db) even if this sample is produced by the audio driver using an audio sample format compatible for a 24 bit DAC. 3)When the audio sample format settings is 16 bits (audio.sample.format), the audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB even if the initial sample comes from a 24 bits soundfont. In both cases (2) or (3), the real dynamic range is only 96 dB. Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3): - for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB). - for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB). */ #define FLUID_PEAK_ATTENUATION 960.0f #define FLUID_CENTS_HZ_SIZE 1200 #define FLUID_VEL_CB_SIZE 128 #define FLUID_CB_AMP_SIZE 1441 #define FLUID_PAN_SIZE 1002 #endif fluidsynth-2.2.5/src/utils/fluid_hash.c000066400000000000000000001153001417326347500201260ustar00rootroot00000000000000/* 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 "fluid_sys.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 unsigned int 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 = FLUID_N_ELEMENTS(primes); static 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 precedes 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 FLUID_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 FLUID_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; fluid_atomic_int_set(&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); if(hashtable->nodes == NULL) { delete_fluid_hashtable(hashtable); FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(hashtable->nodes, 0, hashtable->size * sizeof(*hashtable->nodes)); 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(fluid_atomic_int_get(&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(fluid_atomic_int_get(&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(fluid_atomic_int_get(&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(fluid_atomic_int_get(&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; } #if 0 /** * 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. **/ static 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); } #endif /** * 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 FLUID_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_t 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-2.2.5/src/utils/fluid_hash.h000066400000000000000000000117701417326347500201410ustar00rootroot00000000000000/* 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; fluid_atomic_int_t 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-2.2.5/src/utils/fluid_list.c000066400000000000000000000134421417326347500201620ustar00rootroot00000000000000/* 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_sys.h" #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; fluid_return_if_fail(list != NULL); while(list) { next = list->next; FLUID_FREE(list); list = next; } } void delete1_fluid_list(fluid_list_t *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(const void *a, const void *b) { if(a && b) { return FLUID_STRCMP(a, b); } if(!a && !b) { return 0; } if(a) { return -1; } return 1; } int fluid_list_idx(fluid_list_t *list, void *data) { int i = 0; while(list) { if (list->data == data) { return i; } list = list->next; } return -1; } fluidsynth-2.2.5/src/utils/fluid_list.h000066400000000000000000000043301417326347500201630ustar00rootroot00000000000000/* 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)(const void *a, const 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_idx(fluid_list_t *list, void *data); int fluid_list_size(fluid_list_t *list); #define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL) #define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL) int fluid_list_str_compare_func(const void *a, const void *b); #endif /* _FLUID_LIST_H */ fluidsynth-2.2.5/src/utils/fluid_ringbuffer.c000066400000000000000000000052331417326347500213370ustar00rootroot00000000000000/* 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 "fluid_sys.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, size_t 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_LOG(FLUID_ERR, "Out of memory"); delete_fluid_ringbuffer(queue); 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; fluid_atomic_int_set(&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_return_if_fail(queue != NULL); FLUID_FREE(queue->array); FLUID_FREE(queue); } fluidsynth-2.2.5/src/utils/fluid_ringbuffer.h000066400000000000000000000106121417326347500213410ustar00rootroot00000000000000/* 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 */ fluid_atomic_int_t count; /**< Current count of elements */ int in; /**< Index in queue to store next pushed element */ int out; /**< Index in queue of next popped element */ size_t elementsize; /**< Size of each element */ void *userdata; }; typedef struct _fluid_ringbuffer_t fluid_ringbuffer_t; fluid_ringbuffer_t *new_fluid_ringbuffer(int count, size_t elementsize); void delete_fluid_ringbuffer(fluid_ringbuffer_t *queue); /** * Get pointer to next input array element in queue. * @param queue Lockless queue instance * @param offset 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-2.2.5/src/utils/fluid_settings.c000066400000000000000000001506121417326347500210500ustar00rootroot00000000000000/* 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" #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" /* 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 { char *value; char *def; int hints; fluid_list_t *options; fluid_str_update_t update; void *data; } fluid_str_setting_t; typedef struct { double value; double def; double min; double max; int hints; fluid_num_update_t update; void *data; } fluid_num_setting_t; typedef struct { int value; int def; int min; int max; int hints; fluid_int_update_t update; void *data; } fluid_int_setting_t; typedef struct { fluid_hashtable_t *hashtable; } fluid_set_setting_t; typedef struct { int type; /**< fluid_types_enum */ union { fluid_str_setting_t str; fluid_num_setting_t num; fluid_int_setting_t i; fluid_set_setting_t set; }; } fluid_setting_node_t; static fluid_setting_node_t * new_fluid_str_setting(const char *value, const char *def, int hints) { fluid_setting_node_t *node; fluid_str_setting_t *str; node = FLUID_NEW(fluid_setting_node_t); if(!node) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } node->type = FLUID_STR_TYPE; str = &node->str; str->value = value ? FLUID_STRDUP(value) : NULL; str->def = def ? FLUID_STRDUP(def) : NULL; str->hints = hints; str->options = NULL; str->update = NULL; str->data = NULL; return node; } static void delete_fluid_str_setting(fluid_setting_node_t *node) { fluid_return_if_fail(node != NULL); FLUID_ASSERT(node->type == FLUID_STR_TYPE); FLUID_FREE(node->str.value); FLUID_FREE(node->str.def); if(node->str.options) { fluid_list_t *list = node->str.options; while(list) { FLUID_FREE(list->data); list = fluid_list_next(list); } delete_fluid_list(node->str.options); } FLUID_FREE(node); } static fluid_setting_node_t * new_fluid_num_setting(double min, double max, double def, int hints) { fluid_setting_node_t *node; fluid_num_setting_t *num; node = FLUID_NEW(fluid_setting_node_t); if(!node) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } node->type = FLUID_NUM_TYPE; num = &node->num; num->value = def; num->def = def; num->min = min; num->max = max; num->hints = hints; num->update = NULL; num->data = NULL; return node; } static void delete_fluid_num_setting(fluid_setting_node_t *node) { fluid_return_if_fail(node != NULL); FLUID_ASSERT(node->type == FLUID_NUM_TYPE); FLUID_FREE(node); } static fluid_setting_node_t * new_fluid_int_setting(int min, int max, int def, int hints) { fluid_setting_node_t *node; fluid_int_setting_t *i; node = FLUID_NEW(fluid_setting_node_t); if(!node) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } node->type = FLUID_INT_TYPE; i = &node->i; i->value = def; i->def = def; i->min = min; i->max = max; i->hints = hints; i->update = NULL; i->data = NULL; return node; } static void delete_fluid_int_setting(fluid_setting_node_t *node) { fluid_return_if_fail(node != NULL); FLUID_ASSERT(node->type == FLUID_INT_TYPE); FLUID_FREE(node); } static fluid_setting_node_t * new_fluid_set_setting(void) { fluid_setting_node_t *node; fluid_set_setting_t *set; node = FLUID_NEW(fluid_setting_node_t); if(!node) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } node->type = FLUID_SET_TYPE; set = &node->set; set->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, fluid_settings_key_destroy_func, fluid_settings_value_destroy_func); if(!set->hashtable) { FLUID_FREE(node); return NULL; } return node; } static void delete_fluid_set_setting(fluid_setting_node_t *node) { fluid_return_if_fail(node != NULL); FLUID_ASSERT(node->type == FLUID_SET_TYPE); delete_fluid_hashtable(node->set.hashtable); FLUID_FREE(node); } /** * 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(node); break; case FLUID_INT_TYPE: delete_fluid_int_setting(node); break; case FLUID_STR_TYPE: delete_fluid_str_setting(node); break; case FLUID_SET_TYPE: delete_fluid_set_setting(node); 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(FLUID_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 #FLUID_OK if the node exists, #FLUID_FAILED 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 FLUID_FAILED; } for(n = 0; n < ntokens; n++) { node = fluid_hashtable_lookup(table, tokens[n]); if(!node) { return FLUID_FAILED; } table = (node->type == FLUID_SET_TYPE) ? node->set.hashtable : NULL; } if(value) { *value = node; } return FLUID_OK; } /** * 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 #FLUID_OK if the value has been set, #FLUID_FAILED otherwise */ static int fluid_settings_set(fluid_settings_t *settings, const char *name, fluid_setting_node_t *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); if(num == 0) { return FLUID_FAILED; } num--; for(n = 0; n < num; n++) { node = fluid_hashtable_lookup(table, tokens[n]); if(node) { if(node->type == FLUID_SET_TYPE) { table = node->set.hashtable; } else { /* path ends prematurely */ FLUID_LOG(FLUID_ERR, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name); return FLUID_FAILED; } } else { /* create a new node */ fluid_setting_node_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 FLUID_FAILED; } fluid_hashtable_insert(table, dupname, setnode); table = setnode->set.hashtable; } } dupname = FLUID_STRDUP(tokens[num]); if(!dupname) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } fluid_hashtable_insert(table, dupname, value); return FLUID_OK; } /** * Registers a new string value for the specified setting. * * @param settings a settings object * @param name the setting's name * @param def the default value for the setting * @param hints the hints for the setting * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise */ int fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) != FLUID_OK) { node = new_fluid_str_setting(def, def, hints); retval = fluid_settings_set(settings, name, node); if(retval != FLUID_OK) { delete_fluid_str_setting(node); } } else { /* if variable already exists, don't change its value. */ if(node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = &node->str; FLUID_FREE(setting->def); setting->def = def ? FLUID_STRDUP(def) : NULL; setting->hints = hints; retval = FLUID_OK; } else { FLUID_LOG(FLUID_ERR, "Failed to register string setting '%s' as it already exists with a different type", name); } } fluid_rec_mutex_unlock(settings->mutex); return retval; } /** * Registers a new float value for the specified setting. * * @param settings a settings object * @param name the setting's name * @param def the default value for the setting * @param min the smallest allowed value for the setting * @param max the largest allowed value for the setting * @param hints the hints for the setting * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise */ int fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def, double min, double max, int hints) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); /* 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) != FLUID_OK) { /* insert a new setting */ node = new_fluid_num_setting(min, max, def, hints); retval = fluid_settings_set(settings, name, node); if(retval != FLUID_OK) { delete_fluid_num_setting(node); } } else { if(node->type == FLUID_NUM_TYPE) { /* update the existing setting but don't change its value */ fluid_num_setting_t *setting = &node->num; setting->min = min; setting->max = max; setting->def = def; setting->hints = hints; retval = FLUID_OK; } else { /* type mismatch */ FLUID_LOG(FLUID_ERR, "Failed to register numeric setting '%s' as it already exists with a different type", name); } } fluid_rec_mutex_unlock(settings->mutex); return retval; } /** * Registers a new integer value for the specified setting. * * @param settings a settings object * @param name the setting's name * @param def the default value for the setting * @param min the smallest allowed value for the setting * @param max the largest allowed value for the setting * @param hints the hints for the setting * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise */ int fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def, int min, int max, int hints) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); /* 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) != FLUID_OK) { /* insert a new setting */ node = new_fluid_int_setting(min, max, def, hints); retval = fluid_settings_set(settings, name, node); if(retval != FLUID_OK) { delete_fluid_int_setting(node); } } else { if(node->type == FLUID_INT_TYPE) { /* update the existing setting but don't change its value */ fluid_int_setting_t *setting = &node->i; setting->min = min; setting->max = max; setting->def = def; setting->hints = hints; retval = FLUID_OK; } else { /* type mismatch */ FLUID_LOG(FLUID_ERR, "Failed to register int setting '%s' as it already exists with a different type", name); } } fluid_rec_mutex_unlock(settings->mutex); return retval; } /** * Registers a callback for the specified string setting. * * @param settings a settings object * @param name the setting's name * @param callback an update function for the setting * @param data user supplied data passed to the update function * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise */ int fluid_settings_callback_str(fluid_settings_t *settings, const char *name, fluid_str_update_t callback, void *data) { fluid_setting_node_t *node; fluid_str_setting_t *setting; fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); fluid_rec_mutex_lock(settings->mutex); if((fluid_settings_get(settings, name, &node) != FLUID_OK) || node->type != FLUID_STR_TYPE) { fluid_rec_mutex_unlock(settings->mutex); return FLUID_FAILED; } setting = &node->str; setting->update = callback; setting->data = data; fluid_rec_mutex_unlock(settings->mutex); return FLUID_OK; } /** * Registers a callback for the specified numeric setting. * * @param settings a settings object * @param name the setting's name * @param callback an update function for the setting * @param data user supplied data passed to the update function * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise */ int fluid_settings_callback_num(fluid_settings_t *settings, const char *name, fluid_num_update_t callback, void *data) { fluid_setting_node_t *node; fluid_num_setting_t *setting; fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); fluid_rec_mutex_lock(settings->mutex); if((fluid_settings_get(settings, name, &node) != FLUID_OK) || node->type != FLUID_NUM_TYPE) { fluid_rec_mutex_unlock(settings->mutex); return FLUID_FAILED; } setting = &node->num; setting->update = callback; setting->data = data; fluid_rec_mutex_unlock(settings->mutex); return FLUID_OK; } /** * Registers a callback for the specified int setting. * * @param settings a settings object * @param name the setting's name * @param callback an update function for the setting * @param data user supplied data passed to the update function * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise */ int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, fluid_int_update_t callback, void *data) { fluid_setting_node_t *node; fluid_int_setting_t *setting; fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); fluid_rec_mutex_lock(settings->mutex); if((fluid_settings_get(settings, name, &node) != FLUID_OK) || node->type != FLUID_INT_TYPE) { fluid_rec_mutex_unlock(settings->mutex); return FLUID_FAILED; } setting = &node->i; setting->update = callback; setting->data = data; fluid_rec_mutex_unlock(settings->mutex); return FLUID_OK; } void* fluid_settings_get_user_data(fluid_settings_t * settings, const char *name) { fluid_setting_node_t *node; void* 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) == FLUID_OK) { if(node->type == FLUID_NUM_TYPE) { fluid_num_setting_t *setting = &node->num; retval = setting->data; } else if(node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = &node->str; retval = setting->data; } else if(node->type == FLUID_INT_TYPE) { fluid_int_setting_t *setting = &node->i; retval = setting->data; } } 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 (see #fluid_types_enum), 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_NO_TYPE; fluid_return_val_if_fail(settings != NULL, type); fluid_return_val_if_fail(name != NULL, type); fluid_return_val_if_fail(name[0] != '\0', type); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK) { type = node->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 * @param hints set to the hints associated to the setting if it exists * @return #FLUID_OK if hints associated to the named setting exist, #FLUID_FAILED otherwise */ int fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *hints) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK) { if(node->type == FLUID_NUM_TYPE) { fluid_num_setting_t *setting = &node->num; *hints = setting->hints; retval = FLUID_OK; } else if(node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = &node->str; *hints = setting->hints; retval = FLUID_OK; } else if(node->type == FLUID_INT_TYPE) { fluid_int_setting_t *setting = &node->i; *hints = setting->hints; retval = FLUID_OK; } } fluid_rec_mutex_unlock(settings->mutex); return retval; } /** * Ask whether the setting is changeable in real-time. * * @param settings a settings object * @param name a setting's name * @return TRUE if the setting is changeable in real-time, FALSE otherwise * * @note Before using this function, make sure the @p settings object has already been used to create * a synthesizer, a MIDI driver, an audio driver, a MIDI player, or a command handler (depending on * which settings you want to query). */ int fluid_settings_is_realtime(fluid_settings_t *settings, const char *name) { 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) == FLUID_OK) { if(node->type == FLUID_NUM_TYPE) { fluid_num_setting_t *setting = &node->num; isrealtime = setting->update != NULL; } else if(node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = &node->str; isrealtime = setting->update != NULL; } else if(node->type == FLUID_INT_TYPE) { fluid_int_setting_t *setting = &node->i; 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 #FLUID_OK if the value has been set, #FLUID_FAILED otherwise */ int fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str) { fluid_setting_node_t *node; fluid_str_setting_t *setting; char *new_value = NULL; fluid_str_update_t callback = NULL; void *data = NULL; fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); fluid_rec_mutex_lock(settings->mutex); if((fluid_settings_get(settings, name, &node) != FLUID_OK) || (node->type != FLUID_STR_TYPE)) { FLUID_LOG(FLUID_ERR, "Unknown string setting '%s'", name); goto error_recovery; } setting = &node->str; if(setting->value) { FLUID_FREE(setting->value); } if(str) { new_value = FLUID_STRDUP(str); if(new_value == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } } setting->value = new_value; callback = setting->update; data = setting->data; /* Release the mutex before calling the update callback, to avoid * possible deadlocks with FluidSynths API lock */ fluid_rec_mutex_unlock(settings->mutex); if(callback) { (*callback)(data, name, new_value); } return FLUID_OK; error_recovery: fluid_rec_mutex_unlock(settings->mutex); return FLUID_FAILED; } /** * Copy the value of a string setting into the provided buffer (thread safe) * * @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 #FLUID_OK if the value exists, #FLUID_FAILED otherwise * * @note A size of 256 should be more than sufficient for the string buffer. * * @since 1.1.0 */ int fluid_settings_copystr(fluid_settings_t *settings, const char *name, char *str, int len) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(str != NULL, retval); fluid_return_val_if_fail(len > 0, retval); str[0] = 0; fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK) { if(node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = &node->str; if(setting->value) { FLUID_STRNCPY(str, setting->value, len); } retval = FLUID_OK; } else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ { fluid_int_setting_t *setting = &node->i; if(setting->hints & FLUID_HINT_TOGGLED) { FLUID_STRNCPY(str, setting->value ? "yes" : "no", len); retval = FLUID_OK; } } } 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 #FLUID_OK if the value exists and was successfully duplicated, #FLUID_FAILED otherwise * * Like fluid_settings_copystr() but allocates a new copy of the string. Caller * owns the string and should free it with fluid_free() when done using it. * * @since 1.1.0 */ int fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(str != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK) { if(node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = &node->str; if(setting->value) { *str = FLUID_STRDUP(setting->value); if(!*str) { FLUID_LOG(FLUID_ERR, "Out of memory"); } } if(!setting->value || *str) { retval = FLUID_OK; /* Don't set to FLUID_OK if out of memory */ } } else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ { fluid_int_setting_t *setting = &node->i; 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 = FLUID_OK; /* Don't set to FLUID_OK if out of memory */ } } } } 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 TRUE if the value exists and is equal to \c s, FALSE otherwise */ int fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *s) { fluid_setting_node_t *node; int retval = FALSE; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(s != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK) { if(node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = &node->str; 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 = &node->i; 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. * * @param settings a settings object * @param name a setting's name * @param def the default string value of the setting if it exists * @return FLUID_OK if a default value exists, FLUID_FAILED otherwise * * @note The returned string is not owned by the caller and should not be modified or freed. */ int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def) { fluid_setting_node_t *node; char *retval = NULL; fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK) { if(node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = &node->str; retval = setting->def; } else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ { fluid_int_setting_t *setting = &node->i; if(setting->hints & FLUID_HINT_TOGGLED) { retval = setting->def ? "yes" : "no"; } } } *def = retval; fluid_rec_mutex_unlock(settings->mutex); return retval != NULL ? FLUID_OK : FLUID_FAILED; } /** * 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 #FLUID_OK if the setting exists and option was added, #FLUID_FAILED 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 = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(s != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK && (node->type == FLUID_STR_TYPE)) { fluid_str_setting_t *setting = &node->str; char *copy = FLUID_STRDUP(s); setting->options = fluid_list_append(setting->options, copy); setting->hints |= FLUID_HINT_OPTIONLIST; retval = FLUID_OK; } 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 #FLUID_OK if the setting exists and option was removed, #FLUID_FAILED otherwise */ int fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(s != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK && (node->type == FLUID_STR_TYPE)) { fluid_str_setting_t *setting = &node->str; 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 = FLUID_OK; 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 #FLUID_OK if the value has been set, #FLUID_FAILED otherwise */ int fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val) { fluid_setting_node_t *node; fluid_num_setting_t *setting; fluid_num_update_t callback = NULL; void *data = NULL; fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); fluid_rec_mutex_lock(settings->mutex); if((fluid_settings_get(settings, name, &node) != FLUID_OK) || (node->type != FLUID_NUM_TYPE)) { FLUID_LOG(FLUID_ERR, "Unknown numeric setting '%s'", name); goto error_recovery; } setting = &node->num; if(val < setting->min || val > setting->max) { FLUID_LOG(FLUID_ERR, "requested set value for '%s' out of range", name); goto error_recovery; } setting->value = val; callback = setting->update; data = setting->data; /* Release the mutex before calling the update callback, to avoid * possible deadlocks with FluidSynths API lock */ fluid_rec_mutex_unlock(settings->mutex); if(callback) { (*callback)(data, name, val); } return FLUID_OK; error_recovery: fluid_rec_mutex_unlock(settings->mutex); return FLUID_FAILED; } /** * 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 #FLUID_OK if the value exists, #FLUID_FAILED otherwise */ int fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(val != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK && (node->type == FLUID_NUM_TYPE)) { fluid_num_setting_t *setting = &node->num; *val = setting->value; retval = FLUID_OK; } fluid_rec_mutex_unlock(settings->mutex); return retval; } /** * float-typed wrapper for fluid_settings_getnum * * @param settings a settings object * @param name a setting's name * @param val variable pointer to receive the setting's float value * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise */ int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val) { double tmp; if(fluid_settings_getnum(settings, name, &tmp) == FLUID_OK) { *val = tmp; return FLUID_OK; } return FLUID_FAILED; } /** * 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 * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise */ int fluid_settings_getnum_range(fluid_settings_t *settings, const char *name, double *min, double *max) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(min != NULL, retval); fluid_return_val_if_fail(max != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK && (node->type == FLUID_NUM_TYPE)) { fluid_num_setting_t *setting = &node->num; *min = setting->min; *max = setting->max; retval = FLUID_OK; } fluid_rec_mutex_unlock(settings->mutex); return retval; } /** * Get the default value of a named numeric (double) setting * * @param settings a settings object * @param name a setting's name * @param val set to the default value if the named setting exists * @return #FLUID_OK if the default value of the named setting exists, #FLUID_FAILED otherwise */ int fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(val != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK && (node->type == FLUID_NUM_TYPE)) { fluid_num_setting_t *setting = &node->num; *val = setting->def; retval = FLUID_OK; } 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 #FLUID_OK if the value has been set, #FLUID_FAILED otherwise */ int fluid_settings_setint(fluid_settings_t *settings, const char *name, int val) { fluid_setting_node_t *node; fluid_int_setting_t *setting; fluid_int_update_t callback = NULL; void *data = NULL; fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); fluid_rec_mutex_lock(settings->mutex); if((fluid_settings_get(settings, name, &node) != FLUID_OK) || (node->type != FLUID_INT_TYPE)) { FLUID_LOG(FLUID_ERR, "Unknown integer parameter '%s'", name); goto error_recovery; } setting = &node->i; if(val < setting->min || val > setting->max) { FLUID_LOG(FLUID_ERR, "requested set value for setting '%s' out of range", name); goto error_recovery; } setting->value = val; callback = setting->update; data = setting->data; /* Release the mutex before calling the update callback, to avoid * possible deadlocks with FluidSynths API lock */ fluid_rec_mutex_unlock(settings->mutex); if(callback) { (*callback)(data, name, val); } return FLUID_OK; error_recovery: fluid_rec_mutex_unlock(settings->mutex); return FLUID_FAILED; } /** * 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 #FLUID_OK if the value exists, #FLUID_FAILED otherwise */ int fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(val != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK && (node->type == FLUID_INT_TYPE)) { fluid_int_setting_t *setting = &node->i; *val = setting->value; retval = FLUID_OK; } 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 * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise */ int fluid_settings_getint_range(fluid_settings_t *settings, const char *name, int *min, int *max) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(min != NULL, retval); fluid_return_val_if_fail(max != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK && (node->type == FLUID_INT_TYPE)) { fluid_int_setting_t *setting = &node->i; *min = setting->min; *max = setting->max; retval = FLUID_OK; } fluid_rec_mutex_unlock(settings->mutex); return retval; } /** * Get the default value of an integer setting. * * @param settings a settings object * @param name a setting's name * @param val set to the setting's default integer value if it exists * @return #FLUID_OK if the setting's default integer value exists, #FLUID_FAILED otherwise */ int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val) { fluid_setting_node_t *node; int retval = FLUID_FAILED; fluid_return_val_if_fail(settings != NULL, retval); fluid_return_val_if_fail(name != NULL, retval); fluid_return_val_if_fail(name[0] != '\0', retval); fluid_return_val_if_fail(val != NULL, retval); fluid_rec_mutex_lock(settings->mutex); if(fluid_settings_get(settings, name, &node) == FLUID_OK && (node->type == FLUID_INT_TYPE)) { fluid_int_setting_t *setting = &node->i; *val = setting->def; retval = FLUID_OK; } 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 \p 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) != FLUID_OK || node->type != FLUID_STR_TYPE) { fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ return; } setting = &node->str; /* 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, name, (const 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) == FLUID_OK && node->type == FLUID_STR_TYPE) { count = fluid_list_size(node->str.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 \p name or not a string setting). Free the string when finished with it by using fluid_free(). * * @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_list_t *p, *newlist = NULL; size_t 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) != FLUID_OK || node->type != FLUID_STR_TYPE) { fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ return (NULL); } /* Duplicate option list, count options and get total string length */ for(p = node->str.options, count = 0, len = 0; p; p = p->next) { option = fluid_list_get(p); if(option) { newlist = fluid_list_append(newlist, option); len += FLUID_STRLEN(option); count++; } } if(count > 1) { len += (count - 1) * FLUID_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; size_t pathlen; char *s; pathlen = FLUID_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(node->set.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 \p 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; 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) { if(fluid_settings_get(settings, (const char *)(p->data), &node) == FLUID_OK && node) { (*func)(data, (const 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 */ } /** * Split a comma-separated list of integers and fill the passed * in buffer with the parsed values. * * @param str the comma-separated string to split * @param buf user-supplied buffer to hold the parsed numbers * @param buf_len length of user-supplied buffer * @return number of parsed values or -1 on failure */ int fluid_settings_split_csv(const char *str, int *buf, int buf_len) { char *s; char *tok; char *tokstr; int n = 0; s = tokstr = FLUID_STRDUP(str); if(s == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return -1; } while((tok = fluid_strtok(&tokstr, ",")) && n < buf_len) { buf[n++] = atoi(tok); } FLUID_FREE(s); return n; } fluidsynth-2.2.5/src/utils/fluid_settings.h000066400000000000000000000047311417326347500210550ustar00rootroot00000000000000/* 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 int fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s); int fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s); typedef void (*fluid_str_update_t)(void *data, const char *name, const char *value); int fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints); int fluid_settings_callback_str(fluid_settings_t *settings, const char *name, fluid_str_update_t fun, void *data); typedef void (*fluid_num_update_t)(void *data, const char *name, double value); int fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def, double min, double max, int hints); int fluid_settings_callback_num(fluid_settings_t *settings, const char *name, fluid_num_update_t fun, void *data); /* Type specific wrapper for fluid_settings_getnum */ int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val); typedef void (*fluid_int_update_t)(void *data, const char *name, int value); int fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def, int min, int max, int hints); int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, fluid_int_update_t fun, void *data); int fluid_settings_split_csv(const char *str, int *buf, int buf_len); void* fluid_settings_get_user_data(fluid_settings_t * settings, const char *name); #endif /* _FLUID_SETTINGS_H */ fluidsynth-2.2.5/src/utils/fluid_sys.c000066400000000000000000001444261417326347500200340ustar00rootroot00000000000000/* 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 #if HAVE_PTHREAD_H && !defined(WIN32) // Do not include pthread on windows. It includes winsock.h, which collides with ws2tcpip.h from fluid_sys.h // It isn't need on Windows anyway. #include #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 */ #ifdef _WIN32 #define FLUID_SOCKET_FLAG 0x40000000 #else #define FLUID_SOCKET_FLAG 0x00000000 #define SOCKET_ERROR -1 #define INVALID_SOCKET -1 #endif /* 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; // Pointer to a function to be executed by the timer. // This field is set to NULL once the timer is finished to indicate completion. // This allows for timed waits, rather than waiting forever as fluid_timer_join() does. fluid_timer_callback_t callback; void *data; fluid_thread_t *thread; 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 fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL] = { fluid_default_log_function, fluid_default_log_function, fluid_default_log_function, fluid_default_log_function, #ifdef DEBUG fluid_default_log_function #else NULL #endif }; static void *fluid_log_user_data[LAST_LOG_LEVEL] = { NULL }; static const char fluid_libname[] = "fluidsynth"; /** * 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, const char *message, void *data) { FILE *out; #if defined(WIN32) out = stdout; #else out = stderr; #endif 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: FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message); break; default: FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); break; } fflush(out); } /** * 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, ...) { if((level >= 0) && (level < LAST_LOG_LEVEL)) { fluid_log_function_t fun = fluid_log_function[level]; if(fun != NULL) { char errbuf[1024]; va_list args; va_start(args, fmt); FLUID_VSNPRINTF(errbuf, sizeof(errbuf), fmt, args); va_end(args); (*fun)(level, errbuf, fluid_log_user_data[level]); } } return FLUID_FAILED; } void* fluid_alloc(size_t len) { void* ptr = malloc(len); #if defined(DEBUG) && !defined(_MSC_VER) // garbage initialize allocated memory for debug builds to ease reproducing // bugs like 44453ff23281b3318abbe432fda90888c373022b . // // MSVC++ already garbage initializes allocated memory by itself (debug-heap). // // 0xCC because // * it makes pointers reliably crash when dereferencing them, // * floating points are still some valid but insanely huge negative number, and // * if for whatever reason this allocated memory is executed, it'll trigger // INT3 (...at least on x86) if(ptr != NULL) { memset(ptr, 0xCC, len); } #endif return ptr; } /** * Open a file with a UTF-8 string, even in Windows * @param filename The name of the file to open * @param mode The mode to open the file in */ FILE *fluid_fopen(const char *filename, const char *mode) { #if defined(WIN32) wchar_t *wpath = NULL, *wmode = NULL; FILE *file = NULL; int length; if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, NULL, 0)) == 0) { FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for filename '%s'. Error was: '%s'", filename, fluid_get_windows_error()); errno = EINVAL; goto error_recovery; } wpath = FLUID_MALLOC(length * sizeof(wchar_t)); if (wpath == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory."); errno = EINVAL; goto error_recovery; } MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, wpath, length); if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, NULL, 0)) == 0) { FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for mode '%s'. Error was: '%s'", mode, fluid_get_windows_error()); errno = EINVAL; goto error_recovery; } wmode = FLUID_MALLOC(length * sizeof(wchar_t)); if (wmode == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory."); errno = EINVAL; goto error_recovery; } MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, wmode, length); file = _wfopen(wpath, wmode); error_recovery: FLUID_FREE(wpath); FLUID_FREE(wmode); return file; #else return fopen(filename, mode); #endif } /** * Wrapper for free() that satisfies at least C90 requirements. * * @param ptr Pointer to memory region that should be freed * * @note Only use this function when the API documentation explicitly says so. Otherwise use * adequate \c delete_fluid_* functions. * * @warning Calling ::free() on memory that is advised to be freed with fluid_free() results in undefined behaviour! * (cf.: "Potential Errors Passing CRT Objects Across DLL Boundaries" found in MS Docs) * * @since 2.0.7 */ void fluid_free(void* ptr) { free(ptr); } /** * 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 gets 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; } /** * Suspend the execution of the current thread for the specified amount of time. * @param milliseconds to wait. */ void fluid_msleep(unsigned int msecs) { g_usleep(msecs * 1000); } /** * Get time in milliseconds to be used in relative timing operations. * @return Monotonic time in milliseconds. */ unsigned int fluid_curtime(void) { float now; static float initial_time = 0; if(initial_time == 0) { initial_time = (float)fluid_utime(); } now = (float)fluid_utime(); return (unsigned int)((now - initial_time) / 1000.0f); } /** * Get time in microseconds to be used in relative timing operations. * @return time in microseconds. * Note: When used for profiling we need high precision clock given * by g_get_monotonic_time()if available (glib version >= 2.53.3). * If glib version is too old and in the case of Windows the function * uses high precision performance counter instead of g_getmonotic_time(). */ double fluid_utime(void) { double utime; #if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 28 /* use high precision monotonic clock if available (g_monotonic_time(). * For Windows, if this clock is actually implemented as low prec. clock * (i.e. in case glib is too old), high precision performance counter are * used instead. * see: https://bugzilla.gnome.org/show_bug.cgi?id=783340 */ #if defined(WITH_PROFILING) && defined(WIN32) &&\ /* glib < 2.53.3 */\ (GLIB_MINOR_VERSION <= 53 && (GLIB_MINOR_VERSION < 53 || GLIB_MICRO_VERSION < 3)) /* use high precision performance counter. */ static LARGE_INTEGER freq_cache = {0, 0}; /* Performance Frequency */ LARGE_INTEGER perf_cpt; if(! freq_cache.QuadPart) { QueryPerformanceFrequency(&freq_cache); /* Frequency value */ } QueryPerformanceCounter(&perf_cpt); /* Counter value */ utime = perf_cpt.QuadPart * 1000000.0 / freq_cache.QuadPart; /* time in micros */ #else utime = g_get_monotonic_time(); #endif #else /* fallback to less precise clock */ GTimeVal timeval; g_get_current_time(&timeval); utime = (timeval.tv_sec * 1000000.0 + timeval.tv_usec); #endif return utime; } #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. https://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 /* Profiling interface between profiling command shell and audio rendering API (FluidProfile_0004.pdf- 3.2.2). Macros are in defined in fluid_sys.h. */ /* ----------------------------------------------------------------------------- Shell task side | Profiling interface | Audio task side ----------------------------------------------------------------------------- profiling | Internal | | | Audio command <---> |<-- profling -->| Data |<--macros -->| <--> rendering shell | API | | | API */ /* default parameters for shell command "prof_start" in fluid_sys.c */ unsigned short fluid_profile_notes = 0; /* number of generated notes */ /* preset bank:0 prog:16 (organ) */ unsigned char fluid_profile_bank = FLUID_PROFILE_DEFAULT_BANK; unsigned char fluid_profile_prog = FLUID_PROFILE_DEFAULT_PROG; /* print mode */ unsigned char fluid_profile_print = FLUID_PROFILE_DEFAULT_PRINT; /* number of measures */ unsigned short fluid_profile_n_prof = FLUID_PROFILE_DEFAULT_N_PROF; /* measure duration in ms */ unsigned short fluid_profile_dur = FLUID_PROFILE_DEFAULT_DURATION; /* lock between multiple-shell */ fluid_atomic_int_t fluid_profile_lock = 0; /**/ /*---------------------------------------------- Profiling Data -----------------------------------------------*/ unsigned char fluid_profile_status = PROFILE_STOP; /* command and status */ unsigned int fluid_profile_end_ticks = 0; /* ending position (in ticks) */ fluid_profile_data_t fluid_profile_data[] = /* Data duration */ { {"synth_write_* ------------>", 1e10, 0.0, 0.0, 0, 0, 0}, {"synth_one_block ---------->", 1e10, 0.0, 0.0, 0, 0, 0}, {"synth_one_block:clear ---->", 1e10, 0.0, 0.0, 0, 0, 0}, {"synth_one_block:one voice->", 1e10, 0.0, 0.0, 0, 0, 0}, {"synth_one_block:all voices>", 1e10, 0.0, 0.0, 0, 0, 0}, {"synth_one_block:reverb --->", 1e10, 0.0, 0.0, 0, 0, 0}, {"synth_one_block:chorus --->", 1e10, 0.0, 0.0, 0, 0, 0}, {"voice:note --------------->", 1e10, 0.0, 0.0, 0, 0, 0}, {"voice:release ------------>", 1e10, 0.0, 0.0, 0, 0, 0} }; /*---------------------------------------------- Internal profiling API -----------------------------------------------*/ /* logging profiling data (used on synthesizer instance deletion) */ 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_PROFILE_NBR; 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); } } } /* Macro that returns cpu load in percent (%) * @dur: duration (micro second). * @sample_rate: sample_rate used in audio driver (Hz). * @n_amples: number of samples collected during 'dur' duration. */ #define fluid_profile_load(dur,sample_rate,n_samples) \ (dur * sample_rate / n_samples / 10000.0) /* prints cpu loads only * * @param sample_rate the sample rate of audio output. * @param out output stream device. * * ------------------------------------------------------------------------------ * Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices * ------------------------------------------------------------------------------ * nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices * -------|---------|---------|----------|---------|---------|------------------- * 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612 */ static void fluid_profiling_print_load(double sample_rate, fluid_ostream_t out) { unsigned int n_voices; /* voices number */ static const char max_voices_not_available[] = " not available"; const char *pmax_voices; char max_voices_available[20]; /* First computes data to be printed */ double total, voices, reverb, chorus, all_voices, voice; /* voices number */ n_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ? fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_voices / fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count : 0; /* total load (%) */ total = fluid_profile_data[FLUID_PROF_WRITE].count ? fluid_profile_load(fluid_profile_data[FLUID_PROF_WRITE].total, sample_rate, fluid_profile_data[FLUID_PROF_WRITE].n_samples) : 0; /* reverb load (%) */ reverb = fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].count ? fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].total, sample_rate, fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].n_samples) : 0; /* chorus load (%) */ chorus = fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].count ? fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].total, sample_rate, fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].n_samples) : 0; /* total voices load: total - reverb - chorus (%) */ voices = total - reverb - chorus; /* One voice load (%): all_voices / n_voices. */ all_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ? fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].total, sample_rate, fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_samples) : 0; voice = n_voices ? all_voices / n_voices : 0; /* estimated maximum voices number */ if(voice > 0) { FLUID_SNPRINTF(max_voices_available, sizeof(max_voices_available), "%17d", (unsigned int)((100.0 - reverb - chorus) / voice)); pmax_voices = max_voices_available; } else { pmax_voices = max_voices_not_available; } /* Now prints data */ fluid_ostream_printf(out, " ------------------------------------------------------------------------------\n"); fluid_ostream_printf(out, " Cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond) and maximum voices\n", sample_rate, 1000000.0 / sample_rate); fluid_ostream_printf(out, " ------------------------------------------------------------------------------\n"); fluid_ostream_printf(out, " nVoices| total(%%)|voices(%%)| reverb(%%)|chorus(%%)| voice(%%)|estimated maxVoices\n"); fluid_ostream_printf(out, " -------|---------|---------|----------|---------|---------|-------------------\n"); fluid_ostream_printf(out, "%8d|%9.3f|%9.3f|%10.3f|%9.3f|%9.3f|%s\n", n_voices, total, voices, reverb, chorus, voice, pmax_voices); } /* * prints profiling data (used by profile shell command: prof_start). * The function is an internal profiling API between the "profile" command * prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). * * @param sample_rate the sample rate of audio output. * @param out output stream device. * * When print mode is 1, the function prints all the information (see below). * When print mode is 0, the function prints only the cpu loads. * * ------------------------------------------------------------------------------ * Duration(microsecond) and cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) * ------------------------------------------------------------------------------ * Code under profiling |Voices| Duration (microsecond) | Load(%) * | nbr| min| avg| max| * ---------------------------|------|--------------------------------|---------- * synth_write_* ------------>| 250| 3.91| 2188.82| 3275.00| 41.544 * synth_one_block ---------->| 250| 1150.70| 2273.56| 3241.47| 41.100 * synth_one_block:clear ---->| 250| 3.07| 4.62| 61.18| 0.084 * synth_one_block:one voice->| 1| 4.19| 9.02| 1044.27| 0.163 * synth_one_block:all voices>| 250| 1138.41| 2259.11| 3217.73| 40.839 * synth_one_block:reverb --->| no profiling available * synth_one_block:chorus --->| no profiling available * voice:note --------------->| no profiling available * voice:release ------------>| no profiling available * ------------------------------------------------------------------------------ * Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices * ------------------------------------------------------------------------------ * nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices * -------|---------|---------|----------|---------|---------|------------------- * 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612 */ void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out) { int i; if(fluid_profile_print) { /* print all details: Duration(microsecond) and cpu loads(%) */ fluid_ostream_printf(out, " ------------------------------------------------------------------------------\n"); fluid_ostream_printf(out, " Duration(microsecond) and cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond)\n", sample_rate, 1000000.0 / sample_rate); fluid_ostream_printf(out, " ------------------------------------------------------------------------------\n"); fluid_ostream_printf(out, " Code under profiling |Voices| Duration (microsecond) | Load(%%)\n"); fluid_ostream_printf(out, " | nbr| min| avg| max|\n"); fluid_ostream_printf(out, " ---------------------------|------|--------------------------------|----------\n"); for(i = 0; i < FLUID_PROFILE_NBR; i++) { unsigned int count = fluid_profile_data[i].count; if(count > 0) { /* data are available */ if(FLUID_PROF_WRITE <= i && i <= FLUID_PROF_ONE_BLOCK_CHORUS) { double load = fluid_profile_load(fluid_profile_data[i].total, sample_rate, fluid_profile_data[i].n_samples); fluid_ostream_printf(out, " %s|%6d|%10.2f|%10.2f|%10.2f|%8.3f\n", fluid_profile_data[i].description, /* code under profiling */ fluid_profile_data[i].n_voices / count, /* voices number */ fluid_profile_data[i].min, /* minimum duration */ fluid_profile_data[i].total / count, /* average duration */ fluid_profile_data[i].max, /* maximum duration */ load); /* cpu load */ } else { /* note and release duration */ fluid_ostream_printf(out, " %s|%6d|%10.0f|%10.0f|%10.0f|\n", fluid_profile_data[i].description, /* code under profiling */ fluid_profile_data[i].n_voices / count, fluid_profile_data[i].min, /* minimum duration */ fluid_profile_data[i].total / count, /* average duration */ fluid_profile_data[i].max); /* maximum duration */ } } else { /* data aren't available */ fluid_ostream_printf(out, " %s| no profiling available\n", fluid_profile_data[i].description); } } } /* prints cpu loads only */ fluid_profiling_print_load(sample_rate, out);/* prints cpu loads */ } /* Returns true if the user cancels the current profiling measurement. Actually this is implemented using the key. To add this functionality: 1) Adds #define FLUID_PROFILE_CANCEL in fluid_sys.h. 2) Adds the necessary code inside fluid_profile_is_cancel(). When FLUID_PROFILE_CANCEL is not defined, the function return FALSE. */ int fluid_profile_is_cancel_req(void) { #ifdef FLUID_PROFILE_CANCEL #if defined(WIN32) /* Windows specific stuff */ /* Profile cancellation is supported for Windows */ /* returns TRUE if key is depressed */ return(GetAsyncKeyState(VK_RETURN) & 0x1); #elif defined(__OS2__) /* OS/2 specific stuff */ /* Profile cancellation isn't yet supported for OS2 */ /* For OS2, replaces the following line with the function that returns true when the keyboard key is depressed */ return FALSE; /* default value */ #else /* POSIX stuff */ /* Profile cancellation is supported for Linux */ /* returns true is is depressed */ { /* Here select() is used to poll the standard input to see if an input is ready. As the standard input is usually buffered, the user needs to depress to set the input to a "ready" state. */ struct timeval tv; fd_set fds; /* just one fds need to be polled */ tv.tv_sec = 0; /* Setting both values to 0, means a 0 timeout */ tv.tv_usec = 0; FD_ZERO(&fds); /* reset fds */ FD_SET(STDIN_FILENO, &fds); /* sets fds to poll standard input only */ select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); /* polling */ return (FD_ISSET(0, &fds)); /* returns true if standard input is ready */ } #endif /* OS stuff */ #else /* FLUID_PROFILE_CANCEL not defined */ return FALSE; /* default value */ #endif /* FLUID_PROFILE_CANCEL */ } /** * Returns status used in shell command "prof_start". * The function is an internal profiling API between the "profile" command * prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). * * @return status * - PROFILE_READY profiling data are ready. * - PROFILE_RUNNING, profiling data are still under acquisition. * - PROFILE_CANCELED, acquisition has been cancelled by the user. * - PROFILE_STOP, no acquisition in progress. * * When status is PROFILE_RUNNING, the caller can do passive waiting, or other * work before recalling the function later. */ int fluid_profile_get_status(void) { /* Checks if user has requested to cancel the current measurement */ /* Cancellation must have precedence over other status */ if(fluid_profile_is_cancel_req()) { fluid_profile_start_stop(0, 0); /* stops the measurement */ return PROFILE_CANCELED; } switch(fluid_profile_status) { case PROFILE_READY: return PROFILE_READY; /* profiling data are ready */ case PROFILE_START: return PROFILE_RUNNING;/* profiling data are under acquisition */ default: return PROFILE_STOP; } } /** * Starts or stops profiling measurement. * The function is an internal profiling API between the "profile" command * prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). * * @param end_tick end position of the measure (in ticks). * - If end_tick is greater then 0, the function starts a measure if a measure * isn't running. If a measure is already running, the function does nothing * and returns. * - If end_tick is 0, the function stops a measure. * @param clear_data, * - If clear_data is 0, the function clears fluid_profile_data before starting * a measure, otherwise, the data from the started measure will be accumulated * within fluid_profile_data. */ void fluid_profile_start_stop(unsigned int end_ticks, short clear_data) { if(end_ticks) /* This is a "start" request */ { /* Checks if a measure is already running */ if(fluid_profile_status != PROFILE_START) { short i; fluid_profile_end_ticks = end_ticks; /* Clears profile data */ if(clear_data == 0) { for(i = 0; i < FLUID_PROFILE_NBR; i++) { fluid_profile_data[i].min = 1e10;/* min sets to max value */ fluid_profile_data[i].max = 0; /* maximum sets to min value */ fluid_profile_data[i].total = 0; /* total duration microsecond */ fluid_profile_data[i].count = 0; /* data count */ fluid_profile_data[i].n_voices = 0; /* voices number */ fluid_profile_data[i].n_samples = 0;/* audio samples number */ } } fluid_profile_status = PROFILE_START; /* starts profiling */ } /* else do nothing when profiling is already started */ } else /* This is a "stop" request */ { /* forces the current running profile (if any) to stop */ fluid_profile_status = PROFILE_STOP; /* stops profiling */ } } #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 = NULL; GError *err = NULL; g_return_val_if_fail(func != NULL, NULL); #if OLD_GLIB_THREAD_API /* Make sure g_thread_init has been called. * 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 } else { #if NEW_GLIB_THREAD_API thread = g_thread_try_new(name, (GThreadFunc)func, data, &err); #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); FLUID_FREE(info); 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; } static fluid_thread_return_t 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) { fluid_msleep(delay); } } FLUID_LOG(FLUID_DBG, "Timer thread finished"); timer->callback = NULL; if(timer->auto_destroy) { FLUID_FREE(timer); } return FLUID_THREAD_RETURN_VALUE; } 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; } void delete_fluid_timer(fluid_timer_t *timer) { int auto_destroy; fluid_return_if_fail(timer != NULL); 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); } } 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; } int fluid_timer_is_running(const fluid_timer_t *timer) { // for unit test usage only return timer->callback != NULL; } long fluid_timer_get_interval(const fluid_timer_t * timer) { // for unit test usage only return timer->msec; } /*************************************************************** * * 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; } FLUID_SNPRINTF(buf, len, "%s", line); buf[len - 1] = 0; if(buf[0] != '\0') { add_history(buf); } 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 & FLUID_SOCKET_FLAG)) { // usually read() is supposed to return '\n' as last valid character of the user input // when compiled with compatibility for WinXP however, read() may return 0 (EOF) rather than '\n' // this would cause the shell to exit early n = read(in, &c, 1); if(n == -1) { return -1; } } else { #ifdef NETWORK_SUPPORT n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0); if(n == SOCKET_ERROR) #endif { return -1; } } #endif if(n == 0) { *buf = 0; // return 1 if read from stdin, else 0, to fix early exit of shell return (in == STDIN_FILENO); } 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, const char *format, ...) { char buf[4096]; va_list args; int len; va_start(args, format); len = FLUID_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, FLUID_STRLEN(buf)); #else { int retval; /* Handle write differently depending on if its a socket or file descriptor */ if(!(out & FLUID_SOCKET_FLAG)) { return write(out, buf, (unsigned int)FLUID_STRLEN(buf)); } #ifdef NETWORK_SUPPORT /* Socket */ retval = send(out & ~FLUID_SOCKET_FLAG, buf, (int)FLUID_STRLEN(buf), 0); return retval != SOCKET_ERROR ? retval : -1; #else return -1; #endif } #endif } #ifdef NETWORK_SUPPORT int fluid_server_socket_join(fluid_server_socket_t *server_socket) { return fluid_thread_join(server_socket->thread); } static int fluid_socket_init(void) { #ifdef _WIN32 WSADATA wsaData; int res = WSAStartup(MAKEWORD(2, 2), &wsaData); if(res != 0) { FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", res); return FLUID_FAILED; } #endif return FLUID_OK; } static void fluid_socket_cleanup(void) { #ifdef _WIN32 WSACleanup(); #endif } static int fluid_socket_get_error(void) { #ifdef _WIN32 return (int)WSAGetLastError(); #else return errno; #endif } fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock) { return sock | FLUID_SOCKET_FLAG; } fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock) { return sock | FLUID_SOCKET_FLAG; } void fluid_socket_close(fluid_socket_t sock) { if(sock != INVALID_SOCKET) { #ifdef _WIN32 closesocket(sock); #else close(sock); #endif } } static fluid_thread_return_t 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; #else struct sockaddr_in addr; #endif #ifdef HAVE_INETNTOP #ifdef IPV6_SUPPORT char straddr[INET6_ADDRSTRLEN]; #else char straddr[INET_ADDRSTRLEN]; #endif /* IPV6_SUPPORT */ #endif /* HAVE_INETNTOP */ 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: %d", fluid_socket_get_error()); } server_socket->cont = 0; return FLUID_THREAD_RETURN_VALUE; } 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 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"); return FLUID_THREAD_RETURN_VALUE; } 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; fluid_return_val_if_fail(func != NULL, NULL); if(fluid_socket_init() != FLUID_OK) { 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: %d", fluid_socket_get_error()); fluid_socket_cleanup(); return NULL; } FLUID_MEMSET(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons((uint16_t)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: %d", fluid_socket_get_error()); fluid_socket_cleanup(); return NULL; } FLUID_MEMSET(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons((uint16_t)port); addr.sin_addr.s_addr = htonl(INADDR_ANY); #endif if(bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) { FLUID_LOG(FLUID_ERR, "Failed to bind server socket: %d", fluid_socket_get_error()); fluid_socket_close(sock); fluid_socket_cleanup(); return NULL; } if(listen(sock, SOMAXCONN) == SOCKET_ERROR) { FLUID_LOG(FLUID_ERR, "Failed to listen on server socket: %d", fluid_socket_get_error()); fluid_socket_close(sock); fluid_socket_cleanup(); 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); fluid_socket_cleanup(); 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); fluid_socket_cleanup(); return NULL; } return server_socket; } void delete_fluid_server_socket(fluid_server_socket_t *server_socket) { fluid_return_if_fail(server_socket != NULL); 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); // Should be called the same number of times as fluid_socket_init() fluid_socket_cleanup(); } #endif // NETWORK_SUPPORT FILE* fluid_file_open(const char* path, const char** errMsg) { static const char ErrExist[] = "File does not exist."; static const char ErrRegular[] = "File is not regular, refusing to open it."; static const char ErrNull[] = "File does not exists or insufficient permissions to open it."; FILE* handle = NULL; if(!g_file_test(path, G_FILE_TEST_EXISTS)) { if(errMsg != NULL) { *errMsg = ErrExist; } } else if(!g_file_test(path, G_FILE_TEST_IS_REGULAR)) { if(errMsg != NULL) { *errMsg = ErrRegular; } } else if((handle = FLUID_FOPEN(path, "rb")) == NULL) { if(errMsg != NULL) { *errMsg = ErrNull; } } return handle; } fluid_long_long_t fluid_file_tell(FILE* f) { #ifdef WIN32 // On Windows, long is only a 32 bit integer. Thus ftell() does not support to handle files >2GiB. // We should use _ftelli64() in this case, however its availability depends on MS CRT and might not be // available on WindowsXP, Win98, etc. // // The web recommends to fallback to _telli64() in this case. However, it's return value differs from // _ftelli64() on Win10: https://github.com/FluidSynth/fluidsynth/pull/629#issuecomment-602238436 // // Thus, we use fgetpos(). fpos_t pos; if(fgetpos(f, &pos) != 0) { return (fluid_long_long_t)-1L; } return pos; #else return ftell(f); #endif } #ifdef WIN32 // not thread-safe! char* fluid_get_windows_error(void) { static TCHAR err[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), err, sizeof(err)/sizeof(err[0]), NULL); #ifdef _UNICODE static char ascii_err[sizeof(err)]; WideCharToMultiByte(CP_UTF8, 0, err, -1, ascii_err, sizeof(ascii_err)/sizeof(ascii_err[0]), 0, 0); return ascii_err; #else return err; #endif } #endif fluidsynth-2.2.5/src/utils/fluid_sys.h000066400000000000000000000567341417326347500200450ustar00rootroot00000000000000/* 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 */ /* * @file fluid_sys.h * * 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 * * fluidsynth's wrapper OSAL so to say; include it in .c files, be careful to include * it in fluidsynth's private header files (see comment in fluid_coreaudio.c) */ #ifndef _FLUID_SYS_H #define _FLUID_SYS_H #include "fluidsynth_priv.h" #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_OPENMP #include #endif #if HAVE_IO_H #include // _open(), _close(), read(), write() on windows #endif #if HAVE_SIGNAL_H #include #endif /** Integer types */ #if HAVE_STDINT_H #include #else /* Assume GLIB types */ typedef gint8 int8_t; typedef guint8 uint8_t; typedef gint16 int16_t; typedef guint16 uint16_t; typedef gint32 int32_t; typedef guint32 uint32_t; typedef gint64 int64_t; typedef guint64 uint64_t; typedef guintptr uintptr_t; typedef gintptr intptr_t; #endif #if defined(WIN32) && HAVE_WINDOWS_H #include #include /* Provides also socklen_t */ /* WIN32 special defines */ #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #ifdef _MSC_VER #pragma warning(disable : 4244) #pragma warning(disable : 4101) #pragma warning(disable : 4305) #pragma warning(disable : 4996) #endif #endif /* Darwin special defines (taken from config_macosx.h) */ #ifdef DARWIN # define MACINTOSH # define __Types__ #endif #ifdef LADSPA #include #endif #include /** * 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") #ifdef WIN32 char* fluid_get_windows_error(void); #endif #define FLUID_INLINE inline #define FLUID_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) /* Integer<->pointer conversion */ #define FLUID_POINTER_TO_UINT(x) ((unsigned int)(uintptr_t)(x)) #define FLUID_UINT_TO_POINTER(x) ((void *)(uintptr_t)(x)) #define FLUID_POINTER_TO_INT(x) ((signed int)(intptr_t)(x)) #define FLUID_INT_TO_POINTER(x) ((void *)(intptr_t)(x)) /* Endian detection */ #define FLUID_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN) #define FLUID_LE32TOH(x) GINT32_FROM_LE(x) #define FLUID_LE16TOH(x) GINT16_FROM_LE(x) #if FLUID_IS_BIG_ENDIAN #define FLUID_FOURCC(_a, _b, _c, _d) \ (uint32_t)(((uint32_t)(_a) << 24) | ((uint32_t)(_b) << 16) | ((uint32_t)(_c) << 8) | (uint32_t)(_d)) #else #define FLUID_FOURCC(_a, _b, _c, _d) \ (uint32_t)(((uint32_t)(_d) << 24) | ((uint32_t)(_c) << 16) | ((uint32_t)(_b) << 8) | (uint32_t)(_a)) #endif /* * Utility functions */ char *fluid_strtok(char **str, char *delim); #if defined(__OS2__) #define INCL_DOS #include /* Define socklen_t if not provided */ #if !HAVE_SOCKLEN_T typedef int socklen_t; #endif #endif /** Time functions */ 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); void delete_fluid_timer(fluid_timer_t *timer); int fluid_timer_join(fluid_timer_t *timer); int fluid_timer_stop(fluid_timer_t *timer); int fluid_timer_is_running(const fluid_timer_t *timer); long fluid_timer_get_interval(const fluid_timer_t * timer); // Macros to use for pre-processor if statements to test which Glib thread API we have (pre or post 2.32) #define NEW_GLIB_THREAD_API GLIB_CHECK_VERSION(2,32,0) #define OLD_GLIB_THREAD_API !GLIB_CHECK_VERSION(2,32,0) /* 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) { fluid_return_if_fail(m != NULL); 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) { fluid_return_if_fail(cond != NULL); 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) do { \ if (!g_thread_supported ()) g_thread_init (NULL); \ g_static_mutex_init (&(_m)); \ } while(0) /* 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) do { \ if (!g_thread_supported ()) g_thread_init (NULL); \ g_static_rec_mutex_init (&(_m)); \ } while(0) /* 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) do { \ if (!g_thread_supported ()) g_thread_init (NULL); \ g_static_private_init (&(_priv)); \ } while(0) #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(fluid_atomic_float_t *fptr, float val) { int32_t ival; memcpy(&ival, &val, 4); fluid_atomic_int_set((fluid_atomic_int_t *)fptr, ival); } static FLUID_INLINE float fluid_atomic_float_get(fluid_atomic_float_t *fptr) { int32_t ival; float fval; ival = fluid_atomic_int_get((fluid_atomic_int_t *)fptr); memcpy(&fval, &ival, 4); return fval; } /* Threads */ /* other thread implementations might change this for their needs */ typedef void *fluid_thread_return_t; /* static return value for thread functions which requires a return value */ #define FLUID_THREAD_RETURN_VALUE (NULL) typedef GThread fluid_thread_t; typedef fluid_thread_return_t (*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); /* Dynamic Module Loading, currently only used by LADSPA subsystem */ #ifdef LADSPA typedef GModule fluid_module_t; #define fluid_module_open(_name) g_module_open((_name), G_MODULE_BIND_LOCAL) #define fluid_module_close(_mod) g_module_close(_mod) #define fluid_module_error() g_module_error() #define fluid_module_name(_mod) g_module_name(_mod) #define fluid_module_symbol(_mod, _name, _ptr) g_module_symbol((_mod), (_name), (_ptr)) #endif /* LADSPA */ /* Sockets and I/O */ 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, const char *format, ...); #if defined(WIN32) typedef SOCKET fluid_socket_t; #else typedef int fluid_socket_t; #endif /* The function should return 0 if no error occurred, 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); void 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); /* File access */ #define fluid_stat(_filename, _statbuf) g_stat((_filename), (_statbuf)) #if !GLIB_CHECK_VERSION(2, 26, 0) /* GStatBuf has not been introduced yet, manually typedef to what they had at that time: * https://github.com/GNOME/glib/blob/e7763678b56e3be073cc55d707a6e92fc2055ee0/glib/gstdio.h#L98-L115 */ #if defined(WIN32) || HAVE_WINDOWS_H // somehow reliably mock G_OS_WIN32?? // Any effort from our side to reliably mock GStatBuf on Windows is in vain. E.g. glib-2.16 is broken as it uses struct stat rather than struct _stat32 on Win x86. // Disable it (the user has been warned by cmake). #undef fluid_stat #define fluid_stat(_filename, _statbuf) (-1) typedef struct _fluid_stat_buf_t{int st_mtime;} fluid_stat_buf_t; #else /* posix, OS/2, etc. */ typedef struct stat fluid_stat_buf_t; #endif #else typedef GStatBuf fluid_stat_buf_t; #endif FILE* fluid_file_open(const char* filename, const char** errMsg); fluid_long_long_t fluid_file_tell(FILE* f); /* Profiling */ #if WITH_PROFILING /** profiling interface between Profiling command shell and Audio rendering API (FluidProfile_0004.pdf- 3.2.2) */ /* ----------------------------------------------------------------------------- Shell task side | Profiling interface | Audio task side ----------------------------------------------------------------------------- profiling | Internal | | | Audio command <---> |<-- profling -->| Data |<--macros -->| <--> rendering shell | API | | | API */ /* default parameters for shell command "prof_start" in fluid_sys.c */ #define FLUID_PROFILE_DEFAULT_BANK 0 /* default bank */ #define FLUID_PROFILE_DEFAULT_PROG 16 /* default prog (organ) */ #define FLUID_PROFILE_FIRST_KEY 12 /* first key generated */ #define FLUID_PROFILE_LAST_KEY 108 /* last key generated */ #define FLUID_PROFILE_DEFAULT_VEL 64 /* default note velocity */ #define FLUID_PROFILE_VOICE_ATTEN -0.04f /* gain attenuation per voice (dB) */ #define FLUID_PROFILE_DEFAULT_PRINT 0 /* default print mode */ #define FLUID_PROFILE_DEFAULT_N_PROF 1 /* default number of measures */ #define FLUID_PROFILE_DEFAULT_DURATION 500 /* default duration (ms) */ extern unsigned short fluid_profile_notes; /* number of generated notes */ extern unsigned char fluid_profile_bank; /* bank,prog preset used by */ extern unsigned char fluid_profile_prog; /* generated notes */ extern unsigned char fluid_profile_print; /* print mode */ extern unsigned short fluid_profile_n_prof;/* number of measures */ extern unsigned short fluid_profile_dur; /* measure duration in ms */ extern fluid_atomic_int_t fluid_profile_lock ; /* lock between multiple shell */ /**/ /*---------------------------------------------- Internal profiling API (in fluid_sys.c) -----------------------------------------------*/ /* Starts a profiling measure used in shell command "prof_start" */ void fluid_profile_start_stop(unsigned int end_ticks, short clear_data); /* Returns status used in shell command "prof_start" */ int fluid_profile_get_status(void); /* Prints profiling data used in shell command "prof_start" */ void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out); /* Returns True if profiling cancellation has been requested */ int fluid_profile_is_cancel_req(void); /* For OS that implement key for profile cancellation: 1) Adds #define FLUID_PROFILE_CANCEL 2) Adds the necessary code inside fluid_profile_is_cancel() see fluid_sys.c */ #if defined(WIN32) /* Profile cancellation is supported for Windows */ #define FLUID_PROFILE_CANCEL #elif defined(__OS2__) /* OS/2 specific stuff */ /* Profile cancellation isn't yet supported for OS2 */ #else /* POSIX stuff */ #define FLUID_PROFILE_CANCEL /* Profile cancellation is supported for linux */ #include /* STDIN_FILENO */ #include /* select() */ #endif /* posix */ /* logging profiling data (used on synthesizer instance deletion) */ void fluid_profiling_print(void); /*---------------------------------------------- Profiling Data (in fluid_sys.c) -----------------------------------------------*/ /** Profiling data. Keep track of min/avg/max values to profile a piece of code. */ typedef struct _fluid_profile_data_t { const char *description; /* name of the piece of code under profiling */ double min, max, total; /* duration (microsecond) */ unsigned int count; /* total count */ unsigned int n_voices; /* voices number */ unsigned int n_samples; /* audio samples number */ } fluid_profile_data_t; enum { /* commands/status (profiling interface) */ PROFILE_STOP, /* command to stop a profiling measure */ PROFILE_START, /* command to start a profile measure */ PROFILE_READY, /* status to signal that a profiling measure has finished and ready to be printed */ /*- State returned by fluid_profile_get_status() -*/ /* between profiling commands and internal profiling API */ PROFILE_RUNNING, /* a profiling measure is running */ PROFILE_CANCELED,/* a profiling measure has been canceled */ }; /* Data interface */ extern unsigned char fluid_profile_status ; /* command and status */ extern unsigned int fluid_profile_end_ticks; /* ending position (in ticks) */ extern fluid_profile_data_t fluid_profile_data[]; /* Profiling data */ /*---------------------------------------------- Probes macros -----------------------------------------------*/ /** Macro to obtain a time reference 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() /** * Profile identifier 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_PROFILE_NBR /* number of profile probes */ }; /** Those macros are used to calculate the min/avg/max. Needs a profile number, a time reference, the voices and samples number. */ /* local macro : acquiere data */ #define fluid_profile_data(_num, _ref, voices, samples)\ {\ 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++;\ fluid_profile_data[_num].n_voices += voices;\ fluid_profile_data[_num].n_samples += samples;\ _ref = _now;\ } /** Macro to collect data, called from inner functions inside audio rendering API */ #define fluid_profile(_num, _ref, voices, samples)\ {\ if ( fluid_profile_status == PROFILE_START)\ { /* acquires data */\ fluid_profile_data(_num, _ref, voices, samples)\ }\ } /** Macro to collect data, called from audio rendering API (fluid_write_xxxx()). This macro control profiling ending position (in ticks). */ #define fluid_profile_write(_num, _ref, voices, samples)\ {\ if (fluid_profile_status == PROFILE_START)\ {\ /* acquires data first: must be done before checking that profile is finished to ensure at least one valid data sample. */\ fluid_profile_data(_num, _ref, voices, samples)\ if (fluid_synth_get_ticks(synth) >= fluid_profile_end_ticks)\ {\ /* profiling is finished */\ fluid_profile_status = PROFILE_READY;\ }\ }\ } #else /* No profiling */ #define fluid_profiling_print() #define fluid_profile_ref() 0 #define fluid_profile_ref_var(name) #define fluid_profile(_num,_ref,voices, samples) #define fluid_profile_write(_num,_ref, voices, samples) #endif /* WITH_PROFILING */ /** 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() unsigned int fluid_check_fpe_i386(char *explanation_in_case_of_fpe); void fluid_clear_fpe_i386(void); #else #define fluid_check_fpe(expl) #define fluid_clear_fpe() #endif /* System control */ void fluid_msleep(unsigned int msecs); /** * Advances the given \c ptr to the next \c alignment byte boundary. * Make sure you've allocated an extra of \c alignment bytes to avoid a buffer overflow. * * @note \c alignment must be a power of two * @return Returned pointer is guaranteed to be aligned to \c alignment boundary and in range \f[ ptr <= returned_ptr < ptr + alignment \f]. */ static FLUID_INLINE void *fluid_align_ptr(const void *ptr, unsigned int alignment) { uintptr_t ptr_int = (uintptr_t)ptr; unsigned int offset = ptr_int & (alignment - 1); unsigned int add = (alignment - offset) & (alignment - 1); // advance the pointer to the next alignment boundary ptr_int += add; /* assert alignment is power of two */ FLUID_ASSERT(!(alignment == 0) && !(alignment & (alignment - 1))); return (void *)ptr_int; } #define FLUID_DEFAULT_ALIGNMENT (64U) #endif /* _FLUID_SYS_H */ fluidsynth-2.2.5/src/utils/fluidsynth_priv.h000066400000000000000000000225661417326347500212710ustar00rootroot00000000000000/* 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 */ /* * @file fluidsynth_priv.h * * lightweight part of fluid_sys.h, containing forward declarations of fluidsynth's private types and private macros * * include this one file in fluidsynth's private header files */ #ifndef _FLUIDSYNTH_PRIV_H #define _FLUIDSYNTH_PRIV_H #include #include "config.h" #if HAVE_STDLIB_H #include // malloc, free #endif #if HAVE_STDIO_H #include // printf #endif #if HAVE_STRING_H #include #endif #include "fluidsynth.h" /*************************************************************** * * BASIC TYPES */ #if defined(WITH_FLOAT) typedef float fluid_real_t; #else typedef double fluid_real_t; #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 /** Atomic types */ typedef int fluid_atomic_int_t; typedef unsigned int fluid_atomic_uint_t; typedef float fluid_atomic_float_t; /*************************************************************** * * 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; typedef struct _fluid_zone_range_t fluid_zone_range_t; typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t; /* Declare rvoice related typedefs here instead of fluid_rvoice.h, as it's needed * in fluid_lfo.c and fluid_adsr.c as well */ typedef union _fluid_rvoice_param_t { void *ptr; int i; fluid_real_t real; } fluid_rvoice_param_t; enum { MAX_EVENT_PARAMS = 7 }; /**< Maximum number of #fluid_rvoice_param_t to be passed to an #fluid_rvoice_function_t */ typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]); /* Macro for declaring an rvoice event function (#fluid_rvoice_function_t). The functions may only access * those params that were previously set in fluid_voice.c */ #define DECLARE_FLUID_RVOICE_FUNCTION(name) void name(void* obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]) /*************************************************************** * * CONSTANTS */ #define FLUID_BUFSIZE 64 /**< FluidSynth internal buffer size (in samples) */ #define FLUID_MIXER_MAX_BUFFERS_DEFAULT (8192/FLUID_BUFSIZE) /**< Number of buffers that can be processed in one rendering run */ #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 */ #define FLUID_NUM_MOD 64 /**< Maximum number of modulators in a voice */ /*************************************************************** * * SYSTEM INTERFACE */ /* Math constants */ #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 #endif #ifndef M_LN2 #define M_LN2 0.69314718055994530941723212145818 #endif #ifndef M_LN10 #define M_LN10 2.3025850929940456840179914546844 #endif #define FLUID_M_PI ((fluid_real_t)M_PI) #define FLUID_M_LN2 ((fluid_real_t)M_LN2) #define FLUID_M_LN10 ((fluid_real_t)M_LN10) /* Math functions */ #if defined WITH_FLOAT && defined HAVE_SINF #define FLUID_SIN sinf #else #define FLUID_SIN (fluid_real_t)sin #endif #if defined WITH_FLOAT && defined HAVE_COSF #define FLUID_COS cosf #else #define FLUID_COS (fluid_real_t)cos #endif #if defined WITH_FLOAT && defined HAVE_FABSF #define FLUID_FABS fabsf #else #define FLUID_FABS (fluid_real_t)fabs #endif #if defined WITH_FLOAT && defined HAVE_POWF #define FLUID_POW powf #else #define FLUID_POW (fluid_real_t)pow #endif #if defined WITH_FLOAT && defined HAVE_SQRTF #define FLUID_SQRT sqrtf #else #define FLUID_SQRT (fluid_real_t)sqrt #endif #if defined WITH_FLOAT && defined HAVE_LOGF #define FLUID_LOGF logf #else #define FLUID_LOGF (fluid_real_t)log #endif /* Memory allocation */ #define FLUID_MALLOC(_n) fluid_alloc(_n) #define FLUID_REALLOC(_p,_n) realloc(_p,_n) #define FLUID_FREE(_p) fluid_free(_p) #define FLUID_NEW(_t) (_t*)FLUID_MALLOC(sizeof(_t)) #define FLUID_ARRAY_ALIGNED(_t,_n,_a) (_t*)FLUID_MALLOC((_n)*sizeof(_t) + ((unsigned int)_a - 1u)) #define FLUID_ARRAY(_t,_n) FLUID_ARRAY_ALIGNED(_t,_n,1u) void* fluid_alloc(size_t len); /* File access */ #define FLUID_FOPEN(_f,_m) fluid_fopen(_f,_m) #define FLUID_FCLOSE(_f) fclose(_f) #define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f) FILE *fluid_fopen(const char *filename, const char *mode); #ifdef WIN32 #define FLUID_FSEEK(_f,_n,_set) _fseeki64(_f,_n,_set) #else #define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set) #endif #define FLUID_FTELL(_f) fluid_file_tell(_f) /* Memory functions */ #define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n) #define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n) /* String functions */ #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_STRTOL(_s,_e,_b) strtol(_s,_e,_b) #define FLUID_STRNCPY(_dst,_src,_n) \ do { strncpy(_dst,_src,_n-1); \ (_dst)[(_n)-1]='\0'; \ }while(0) #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 #if (defined(WIN32) && _MSC_VER < 1900) || defined(MINGW32) /* need to make sure we use a C99 compliant implementation of (v)snprintf(), * i.e. not microsofts non compliant extension _snprintf() as it doesn't * reliably null-terminate the buffer */ #define FLUID_SNPRINTF g_snprintf #else #define FLUID_SNPRINTF snprintf #endif #if (defined(WIN32) && _MSC_VER < 1500) || defined(MINGW32) #define FLUID_VSNPRINTF g_vsnprintf #else #define FLUID_VSNPRINTF vsnprintf #endif #if defined(WIN32) && !defined(MINGW32) #define FLUID_STRCASECMP _stricmp #else #define FLUID_STRCASECMP strcasecmp #endif #if defined(WIN32) && !defined(MINGW32) #define FLUID_STRNCASECMP _strnicmp #else #define FLUID_STRNCASECMP strncasecmp #endif #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 /* People who want to reduce the size of the may do this by entirely * removing the logging system. This will cause all log messages to * be discarded at compile time, allowing to save about 80 KiB for * the compiled binary. */ #if 0 #define FLUID_LOG (void)sizeof #else #define FLUID_LOG fluid_log #endif #if defined(DEBUG) && !defined(NDEBUG) #define FLUID_ASSERT(a) g_assert(a) #else #define FLUID_ASSERT(a) #endif #define FLUID_LIKELY G_LIKELY #define FLUID_UNLIKELY G_UNLIKELY /* Misc */ #if defined(__INTEL_COMPILER) #define FLUID_RESTRICT restrict #elif defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #define FLUID_RESTRICT __restrict__ #elif defined(_MSC_VER) && _MSC_VER >= 1400 #define FLUID_RESTRICT __restrict #else #warning "Dont know how this compiler handles restrict pointers, refuse to use them." #define FLUID_RESTRICT #endif #define FLUID_N_ELEMENTS(struct) (sizeof (struct) / sizeof (struct[0])) #define FLUID_MEMBER_SIZE(struct, member) ( sizeof (((struct *)0)->member) ) #define fluid_return_if_fail(cond) \ if(cond) \ ; \ else \ return #define fluid_return_val_if_fail(cond, val) \ fluid_return_if_fail(cond) (val) #endif /* _FLUIDSYNTH_PRIV_H */ fluidsynth-2.2.5/test-android/000077500000000000000000000000001417326347500163225ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/.gitignore000066400000000000000000000004771417326347500203220ustar00rootroot00000000000000*.iml .gradle /local.properties /.idea/caches /.idea/libraries /.idea/modules.xml /.idea/workspace.xml /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store /build /captures .externalNativeBuild .cxx local.properties .idea app/src/main/cpp/include/ app/src/main/cpp/tests/ app/src/main/jniLibs/ test-names.txt fluidsynth-2.2.5/test-android/README.md000066400000000000000000000076401417326347500176100ustar00rootroot00000000000000# Android test runner It is meant to be an Android app that runs those fluidsynth tests under `../test` directory. It is not immediately doable because everything is based on ctest where each source has `main()` function that cannot be more than one within a shared library. Therefore, we generate the modified test sources into this standalone Android tester app. Also, since those tests have to access to libfluidsynth non-public implementation, we have to build the entire native test libraries (for each ABI), not with `libfluidsynth.so`. The application was based on Android Studio 4.2 (when it was created). ## Building Here is a brief task list to generate, build, and run Android tests: - run `./convert-tests.sh` to generate runnable tests. - run `build-scripts/download.sh` to download fluidsynth dependency archives to build from sources as Android dependencies. - run `build-scripts/build-all-archs.sh` to build fluidsynth native libraries for Android. - copy `build-scripts/build-artifacts/lib` contents into `app/src/main/jniLibs`. - run `./gradlew connectedCheck` to build and run Android tests. Detailed explanation follows. (1) First of all, you have to generate the modified test sources as well as the native test runner from `../test` directory. `./convert-tests.sh` does this work for you. It is a simple sed script that expects various preconditions that those existing ctests meet at the moment when this script was created (e.g. test source filename can be used to construct a valid C function name, it must have `int main()` literally, it must be compilable among with other sources, etc.). This also overwrites `app/src/main/cpp/run_all_tests.c` You are supposed to run this every time the set of test files get updated. On CI builds it has to be run every time. This also applies when you want to try re-enabling those failing tests, adding exceptional tests that this converter cannot cover, or adding more failing tests. (2) Once you are done with generating tests, then the next step is to download all fluidsynth runtime dependencies. This can be done by `./download.sh`. It is based on (but not a complete copy of) [Azure DevOps CI build setup](https://github.com/FluidSynth/fluidsynth/blob/master/.azure/azure-pipelines-android.yml). You have to do once until the list of dependencies changes. On Azure DevOps this step is therefore already handled (when this notes were written). (3) Once you are done with downloading all those dependencies, the next step is to build fluidsynth for Android. Locally it can be achieved by `build-scripts/build-all-archs.sh`. It is (again) based on the Azure DevOps setup and therefore it is already handled there (when this notes were written). It will end up with `build-scripts/build-artifacts/` that contains a `include` directory and `lib/*` directories for each ABI, on local builds. (Azure DevOps builds it is `$(Build.ArtifactStagingDirectory)/lib/*`.) Once you have finished building fluidsynth for Android. there will be `$(topdir)/build_(ABI)` directories. While you want to build the Android tester app, you cannot remove them because those intermediate files (OBJ files) are referenced by this app's `CMakeLists.txt`. (4) The `lib` part from the above has to be copied into `app/src/main/jniLibs`. There should be `armeabi-v7a`, `arm64-v8a`, `x86`, and `x86_64` subdirectories. (5) After all the steps above are done, the Android tester app is ready to build and run. You can either open this directory as a project on Android Studio, or run `./gradlew build` to build the app, or run `./gradlew connectedCheck` to build and run the tests on a connected Android target (emulator or device). You can also run the tests by simply launching the MainActivity as it run there before showing the UI. Note that those tests have to run on an Android target otherwise it does not make sense. `./gradlew build` or `./gradlew check` runs "test" in the project, but it does not mean they run on an Android target. fluidsynth-2.2.5/test-android/app/000077500000000000000000000000001417326347500171025ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/.gitignore000066400000000000000000000000061417326347500210660ustar00rootroot00000000000000/buildfluidsynth-2.2.5/test-android/app/build.gradle000066400000000000000000000033121417326347500213600ustar00rootroot00000000000000plugins { id 'com.android.application' id 'kotlin-android' } android { compileSdkVersion 30 buildToolsVersion "30.0.3" defaultConfig { applicationId "org.fluidsynth.fluidsynth_tests" minSdkVersion 24 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { cppFlags '' arguments '-DANDROID_STL=c++_shared' } if (project.hasProperty('customAbiFilters')) ndk.abiFilters "$customAbiFilters" } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } externalNativeBuild { cmake { path file('src/main/cpp/CMakeLists.txt') version '3.16.0+' } } buildFeatures { viewBinding true } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.5.0' implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' } fluidsynth-2.2.5/test-android/app/proguard-rules.pro000066400000000000000000000013561417326347500226040ustar00rootroot00000000000000# Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFilefluidsynth-2.2.5/test-android/app/src/000077500000000000000000000000001417326347500176715ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/androidTest/000077500000000000000000000000001417326347500221515ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/androidTest/java/000077500000000000000000000000001417326347500230725ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/androidTest/java/org/000077500000000000000000000000001417326347500236615ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/androidTest/java/org/fluidsynth/000077500000000000000000000000001417326347500260525ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/androidTest/java/org/fluidsynth/fluidsynth_tests/000077500000000000000000000000001417326347500314655ustar00rootroot00000000000000ExampleInstrumentedTest.kt000066400000000000000000000014011417326347500365770ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/androidTest/java/org/fluidsynth/fluidsynth_testspackage org.fluidsynth.fluidsynth_tests import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 import org.fluidsynth.fluidsynthtests.TestRunner import org.junit.Test import org.junit.runner.RunWith import org.junit.Assert.* /** * Instrumented test, which will execute on an Android device. * * See [testing documentation](http://d.android.com/tools/testing). */ @RunWith(AndroidJUnit4::class) class ExampleInstrumentedTest { @Test fun useAppContext() { // Context of the app under test. val appContext = InstrumentationRegistry.getInstrumentation().targetContext assertEquals("org.fluidsynth.fluidsynth_tests", appContext.packageName) TestRunner.runTests() } }fluidsynth-2.2.5/test-android/app/src/main/000077500000000000000000000000001417326347500206155ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/AndroidManifest.xml000066400000000000000000000013771417326347500244160ustar00rootroot00000000000000 fluidsynth-2.2.5/test-android/app/src/main/cpp/000077500000000000000000000000001417326347500213775ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/cpp/CMakeLists.txt000066400000000000000000000075461417326347500241530ustar00rootroot00000000000000# For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.10.2) # Declares and names the project. project("fluidsynth_tests") set (TEST_SRC_DIR ../../../../../test) # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. AUX_SOURCE_DIRECTORY(tests TEST_SRC_FILES) FILE(GLOB LINKED_OBJ_FILES ../../../../../build_${CMAKE_ANDROID_ARCH_ABI}/src/CMakeFiles/libfluidsynth-OBJ.dir/**/*.o) message("-----------------------TEST_SRC_FILES---------------------------") message("${TEST_SRC_FILES}") message("-----------------------LINKED_OBJ_FILES---------------------------") message("${LINKED_OBJ_FILES}") add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). ${LINKED_OBJ_FILES} tests/test_preset_pinning.c tests/test_seq_event_queue_remove.c tests/test_seq_scale.c tests/test_jack_obtaining_synth.c tests/test_settings_unregister_callback.c tests/test_pointer_alignment.c tests/test_sfont_zone.c tests/test_utf8_open.c tests/test_seqbind_unregister.c tests/test_sf3_sfont_loading.c tests/test_sample_cache.c tests/test_synth_process.c tests/test_ct2hz.c tests/test_seq_evt_order.c tests/test_snprintf.c tests/test_seq_event_queue_sort.c tests/test_sfont_loading.c tests/test_preset_sample_loading.c tests/test_sfont_unloading.c tests/test_sample_rate_change.c tests/test_sample_validate.c tests/test_synth_chorus_reverb.c tests/test_bug_635.c run_all_tests.c native-lib.cpp ) target_include_directories(native-lib PRIVATE ../../../../../build_${CMAKE_ANDROID_ARCH_ABI} ../../../../build-scripts/build-artifacts/include ../../../../build-scripts/android-build-root/${CMAKE_ANDROID_ARCH_ABI}/opt/android/include ../../../../build-scripts/android-build-root/${CMAKE_ANDROID_ARCH_ABI}/opt/android/include/glib-2.0 ../../../../build-scripts/android-build-root/${CMAKE_ANDROID_ARCH_ABI}/opt/android/lib/glib-2.0/include ../../../../../src ../../../../../src/bindings ../../../../../src/drivers ../../../../../src/midi ../../../../../src/rvoice ../../../../../src/sfloader ../../../../../src/synth ../../../../../src/utils ../../../../../test ) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_directories(native-lib PRIVATE ../jniLibs/${CMAKE_ANDROID_ARCH_ABI}) target_link_libraries(native-lib ${log-lib} c++_shared gobject-2.0 glib-2.0 sndfile instpatch-1.0 OpenSLES oboe ) fluidsynth-2.2.5/test-android/app/src/main/cpp/native-lib.cpp000066400000000000000000000004111417326347500241310ustar00rootroot00000000000000#include #include #include extern "C" int run_all_fluidsynth_tests(); extern "C" JNIEXPORT void JNICALL Java_org_fluidsynth_fluidsynthtests_TestRunner_00024Companion_runTests(JNIEnv* env, jobject thiz) { run_all_fluidsynth_tests(); } fluidsynth-2.2.5/test-android/app/src/main/java/000077500000000000000000000000001417326347500215365ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/java/org/000077500000000000000000000000001417326347500223255ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/java/org/fluidsynth/000077500000000000000000000000001417326347500245165ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/java/org/fluidsynth/fluidsynthtests/000077500000000000000000000000001417326347500277725ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/java/org/fluidsynth/fluidsynthtests/MainActivity.kt000066400000000000000000000012301417326347500327270ustar00rootroot00000000000000package org.fluidsynth.fluidsynthtests import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import org.fluidsynth.fluidsynthtests.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) TestRunner.runTests() // Example of a call to a native method binding.sampleText.text = "If this shows up then there was no reported test failure!" } }fluidsynth-2.2.5/test-android/app/src/main/java/org/fluidsynth/fluidsynthtests/TestRunner.kt000066400000000000000000000002771417326347500324510ustar00rootroot00000000000000package org.fluidsynth.fluidsynthtests class TestRunner { companion object { external fun runTests() init { System.loadLibrary("native-lib") } } }fluidsynth-2.2.5/test-android/app/src/main/res/000077500000000000000000000000001417326347500214065ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/drawable-v24/000077500000000000000000000000001417326347500236005ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml000066400000000000000000000032461417326347500310350ustar00rootroot00000000000000 fluidsynth-2.2.5/test-android/app/src/main/res/drawable/000077500000000000000000000000001417326347500231675ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/drawable/ic_launcher_background.xml000066400000000000000000000127461417326347500303760ustar00rootroot00000000000000 fluidsynth-2.2.5/test-android/app/src/main/res/layout/000077500000000000000000000000001417326347500227235ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/layout/activity_main.xml000066400000000000000000000014621417326347500263100ustar00rootroot00000000000000 fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-anydpi-v26/000077500000000000000000000000001417326347500245665ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml000066400000000000000000000004201417326347500275600ustar00rootroot00000000000000 fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml000066400000000000000000000004201417326347500307670ustar00rootroot00000000000000 fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-hdpi/000077500000000000000000000000001417326347500236135ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-hdpi/ic_launcher.png000066400000000000000000000070111417326347500265740ustar00rootroot00000000000000PNG  IHDRHHUG IDATx\iPTWM2IլUdɟT\Qf-$q 5tI\0:q QY}_D@TILD֙{H=A)^թny}~߹=Fc=x:WHDBH8 裝>&YhMr2$%oNRPR dyWgʋe.dy9>xQ,ݖ ۓ/UYHH_bNhTQFhD P>ù5Ap'qUmHsȽ퐾d?N\'ӷZ L Xʃt7l[0sȋHqcڻ1g1ױS5= b܇yl4޹E۞[ui7 |ԳX3qz,e͑~.'sտmr#"I-z߯%_rpb7ZQtWaBl.kh`/&m*fE~N"o]EW\޻{Iw?%!/I9Ѳ|wo [%>S[WpM6!HI>haV[|oGHq|G.۬CCQS듄-Al ) F9g#qB&1xR[{ذ|_b\8=iS2qξVAsb^+Yb6k8|޼o= qֵ>mH?ܹg7owb(Y:\zS 8`+] w\}5̸4|OWb b^/إ[[hXea±Xp67tJV90;6RGڔʕˍЖBޥK ܽo^5Wl(Ҡ 8e>J젟&}  ݲƸm5a!׊vg4XT><{mK.}ygZۢ0k62l6P H(5ZC'ʎ]έ>R̐]}@#]m "c@:R>v;"EXqa!oO{ P|0XAgs Wd l Ĭ1j\I_Snls19gGĔG8 "0S EA;4e^gwaqh5}$19B}$řpb$NJ34 GK3܊)TEJI$Exb"DHcrt9l8܎ ~()up:m^8JaGIGA%VDPA{s a}xG13C>PdBA(=+ N`YhI0;' `8䄈Pj@u& 0 Z8 up, S1$oWlƜD8E< Be4U1P[ mqji(A#k`~t ,"plfi`yb=,3}a ~:{N8rtX&'6 Q\Bd(Ԉ9mq'ý<ve:ؕ`_.¡UYc>Cs˕+Mv ZV?h^V齬z'd| :?eՓyY(}N\V{L1,H\b*BUu&Y1tM p s" s2*%%uRH9ǽW`L k0ЊiټI $o)Wy'PRN )H Y]0Iq8!W<\B`Lјrdп4VR.sT,PnQE8X 9} j.fx$.ػ R.sTp%s\#P̪x(BBBMtn8 ĉD {rX΃e}*/TܕX')/ b+k@1`Mb`>gnN Tq99‘f ;d3aϙOp.0\/+uq(..epk%#UpmXX3j1.8DaqG9 ÅXX%[+KLur% (}!'YBBFĜ(Jà(#k w]AR'^F¾p.J>JhLYǑr>ǵ<CSVQ)f$PcL3VV!Ι-)`RDFPe\pQQ9!O.ZJ@Mrl@fPƧ:nqre(s(!*"e|}RA MN[oȆ/gPs`~!|p?L"qKU,UNUAU)s48-8fI':R,X'n~q?ΆTD,To[99W/naf.Ӓ@aYaj.uajQ`Z(AiY@\px{+>Cf>ǣ!Ep8!s1?O5haźZqЀAO1&VY(85'iMs«mCzcʼ!U_ ~oԇBY8ؕѮ\KOBxXPC{Tj")!GQ!#aIx6ncC, Mq"ՑH_W/ś Zks9^~a;ɞwN3ݷ<4~nwYcs龕iV=N%i.%xݺ Dn%ixMq,%6nWٮiGz'3wx^cLM|ozX^}u#'_g1 +w 0FxEuXMM> W-!]gGo@w̩]G/]ų{`Qͷ3y*'ǨN[}ٵe6?M ݍVpӨ\L"BS+Et̰Lp1єBXy:Nu0X݁(iW†U'.&Tl4F@#i 6TncS9wicte{MڲtzXb5%*~ƻ 偰nR]o%$-nqfv=.@o(J>W d4**D5$׹n@ͨl"dD%?*#/Fʹ7.[$ DNXB\ȴ"#S5LTJ:¥?,sr5V2$-C\{e:te<Wa?Vw?9M&x ;LY'A ƣ&Wq7.)Kb v1boHKb$,y.w$6GIϰ$`tobe0-:n.r5`OP 6>-%X[pH_ۨ}6waiֹοÂ4ts܏ H21Qn= O$qA &p8ZYfU}RȤ<T ε?v6\dKv/}Gr3Y|{^8%a僼/xϓy*wڦpF._9S;~ 6~ƏE3-|gV (B`!3Wsܷ>'3Ẁo޻ eUnA70*:Od#Jm5뙵S9wk]XѺt3IޜL[hץ{Ơ3wx\[͍ϴ I11&~o([hfe_H~@31S ٷ`BEhs~NgP[ fjg`^[KzG{1ˇ 0VH!S__w(z$칞Q7?S {5u;H׿Z<t/:X@2&4JvKnDy7ì/`j6 Yp|D"4 UnЫ10r ̯*^%zf4ZơKL^* ˠY;w,\󕙇1l'j~p$҈Gi.~sjPdC.\0j:?KC-" ĺ2%e6:o[~ƞ]JR' K/җ&esuVUyH>5 }۸Tp\̨J vRi#*!;4aƛeS%@Q4cVo=s>N0H9~uJbܘCYyr&7 d5|;|{.Ud2j ́P?-w]X`R> ɹ*}ZC9A&A…A?\O{֣dWEO3ARo4jк*!-hsi+ H&p[N3 z )VɛIϮS%u_4+7`N砛sA7`?u&/~S+AC@"ϊ~v^+z?X`P A3gb_qCn"ٖk'i8 *Td$(Md]hbkօ̂r & G+i$oٙH  WG0 ! ptsA7M`OF2>zd@zHC!Vp(|r b)O/( 愁NL8s9@(%߳bϨo4'/H;E}޲kwګYQ- wC0{+8o0;Yy'ؤ˴I@6Js%2³;@?t=Dd=1MFQ[L;N~G{b/ȂCVc>8$'c K G(iQ& 0(/*z[!FwT6{3 ]{(ԣWա}LIArx紗2@p(0@yEhAͺf'O'ߤo>V+mCAsG$jP<2dsK Д4As2<>ɄC1 w4`>6rIr/ jo` wf`RԩW(FI̡ Y/ A7CcIŀd :%g~sҨ)&m]ߔ,unа;xmz\MqCx[MHCcI}Vr G7]hXsE/Ŷdҷ%# 5U{bM0H4/EWafr08ǴpžPN a9bҜX5c7n=yyG0!SvE8yo;殣6 6Fw‘l=_=*v^-ndwd4 p['-.2$v@{gIQmNB@b@ڙDhqB5"̊]E?M0Wۑt ]8?;aXeo>O}C$U"V8ПП oP/)P4fH+Ud/*$(h14Eʠh!DW?&MWВaγ`5| J:*}9#Pg1S rJɂVoǬIbIxR"],] Ԣ׏e7eϫq3r%%Jz?b.-?ݷ>t xʨ'sYe_xú UƄ8+-ؿ.Q~3T9QPjGA'K)˔`rGkӂ6ǡhQ4ޛpKL&o跃ȍXҪ?,U՗Ȳ(}u;o~w? Q`X*Ce7ljBU;5E EKhړ>1S6iu[9IENDB`fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-mdpi/000077500000000000000000000000001417326347500236205ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-mdpi/ic_launcher.png000066400000000000000000000051141417326347500266030ustar00rootroot00000000000000PNG  IHDR00W IDATxY{PT4LgZ;8L봵a]<j/ޏuy-o3mv26*,A^ tvf}=|l2/}: Y0C,dY3tB>bɤȎI~({I; 1 :š=sHyXd A}7YuP0bɤȎI~cZԽNyKNto:BOq?_c=Z>je܌ : Ovd?N0Pq""_dBݩ`ydG4x^C8aǚ؍= }^LLp'ʎ2yZ=C sRLM̿.glKh {C"Mh mGq?h+EV~߷oܿ }lF8]U.Sf]v]/Wkڳ6wmXKeK8TB_n=Rr{71.K;FZhcrG w=X-DVJhB^@T ikΛh.3 C0Bw׌&45b1CgiG4n͸sM=f!*Ҫx%Z iQbUp<|>VbAv=.66n5Ok!=KR3J7`ƙ p?[İn_l[E4U87$qҶ0iTv9yJ׃rjY'u:FlJ7&&!Y /bq>Uq̀&)\L䤍J6gH}1N'`q%x,Od<"JT-Q"4gt.4DQ̈Oj N&!2zQbD4"!yMX+*$̯# GWe}2Nu|xoYfײM|Pީl+c^˗.oOذI#?B". Oߨ xx,kb )OE2+=oR^ՉQ6| _6%"~;7BSqޗ@e?w\g_Hq-*E F^*]:Vŗ 5, V5Zùh6s3*pD.FɹVr5q[m3.2yS[O @Ҳmjx3@ىtNeTQܜ:eݛRL:q6mT9|=۔OvTy"/)vxaq#KM:\nJ2[ C tEVߦt;|kpzPJ4qV߲D*)J,'XdZj$6:_eSVn-*񎾩|&L\ઘcU tNAx#h :)&uVqy,.{WE r|i0`]:IENDB`fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png000066400000000000000000000064741417326347500300240ustar00rootroot00000000000000PNG  IHDR00W IDATxZ XSWVSvS?;V[AT`VAV!@!!l"Vk;]ZkUTE1\B6֥-*rK:/y{s޹1` O:g_mƒ{ӝg9ݤ\fZ[`w OSPSld;WP;e7fBlN~XML>ݠid6I{]J Ϡ`X@!5d\+k=^)ZѝOfhQ:fi pWmO0x 726S88k-bҊfipj/;"70 0'fe ]khma ,va <>g7lPFhJg|MfeyDuѣu֣ ա/Af:ѯfk''QkلtxLo6]N&VhOtڅ:niԤaex V4%R(ʖauV 1c%nq"mx5TyS ˻Gް|wz]V(|s&aI23YYz+߽ Fey<W' K_v>71Ke 4>=Sh-d^/CR(Ȕ:jm(~VK\Iś:eS{g9MAH < \ QuPd4VK)$qT4驍^;~Fk+v} veXכazE}V!ޠL"ѢʮYV^/41>ZY&|(DW |:sۋ9e8l&-x5.Z(N~_wHmE!SʟFhȸWpX6omN~}AշoYU9o'ԫy̴8]mͭ-}1kǼ%ܞ.Tr[itf[ Z.;Q)yҏpvS A|i= < [{>nMI~mJ-pNcf;ނݔߦ@|+=c֤^ ~Y{ ։=ɮK(a g`uDz\ *6t(p=" {krpKd O!Y hp/ rp%L.#ERHDOV!D R xh< *DVggU2xU'[urV g!}l+_ǾZ }o[0c'B~ }1๟H^,xԃR9$C]>HL꾜H D@xL )w3O#<@4]:_|"| CG4ۍ5?b0azɲ'G8.}(m<P8,A8G(m&P#JAI%S8ŶQ2WSUY'Ld' t},GA,؀G{byAs#$ѭ窄j1.8AvF<۩S#C]H DS! q0&,<&QB)<&GuI‚PJf&[yB~bs2Ϯ?GC~6GF*OdzА".?[m<@P(6E+$OK YǥL4SF*t|LZmR!&>ȚMៈG)"#]dզP+' OiMX %u6Xऍ`"6[̿2?Mj"G<35%̘Bt95do #aHHT$K`[L,72sZ| 6t3Ou,\'0HǛGz2ϤMX/2d |R8ƹ"[l Qv/н>cxe%1$]KwZ4_W'7|k7<%\)7x^5jۊ𤗄QVOS?̳V xP|w5=nEĘ?Y]X8k:ǾIENDB`fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-xhdpi/000077500000000000000000000000001417326347500240035ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-xhdpi/ic_launcher.png000066400000000000000000000114761417326347500267760ustar00rootroot00000000000000PNG  IHDR``w8IDATx] XTG6d{'{D4.A5ƍf_E5*$fD@UvMV5*t@yTw;62}}UΩ2D(BP"E(B@c _pEQ<^_R=7 qΕGnKu |ҴUXزhkGpykkG6dۗ:m>; ˆnlP3Aji3hus&n3Q*lqn#}݃׎lȶ/uRt |vS6\5Lk3AUy}DNEЯ iPaun .K1kGpixȆlR'MgG}>q(8  H Fn^yCd)`\QݑIo0[9|ѯțT^}\@3ӊo}eS0zˇQ}BP稰;m (gKiC?um6xj,mlpu.68 />; )^;Bg^;!۾Im5Q_Ou"7^Ssl-΀GKOkӈ†})5F+jj# MgG}>7-[BpH? k S0&Y:?-$.٭l6k~8l#嶋?AIg3|oq-J:Ҟ uZc;۹%N\ߥks.߿lC1竓lȖ&1zyoqʷ0lchnEe m4 mtMA0> ?_8RE!`ۼF.Nw'>ᇮ{pn%;ȞyI W~j y-;eFk_I TGڽjwEH4 jt?=-齁6-A5ԝ\<3U6-jS;/> =z5*P[&=`۾]Z.s*Hl(}+ Ws#q ;%t-\׺JyS_׫,Z4)c^6)7\|9roq:1!Wł?_`Qݽ mk߱l&_GkSK,|uwD60kd[|{ӢE@_q 5dBWwֈ#YPd8mwbY`qk׎_'aΠg l*=Д/?cO|7۫2Xذ}p @?vQ7A%0z3޻S#ẅPp3p 7gQY`iN-!)w)V N}c9g~h\3÷ D9 M@{aO]t{kJ7[3`Bz\?؂~f|zCk + / eي؆Yfs{z 08dG|)H#_d+i\X 4i0>m觻1W.XkFINPcfxywX' W;pӴZ0`hrOlfl,WЫ,NW{08 2=`WLq(B`vJAh <l/BBh^>J"l?H< <Ҫ$TpBc9ÐP_֝# ԩ(D` vS e0 t,{᪠o)\ G:Ч qask`w+`ٮ|@Xeexf= άqI/JϭO2B`Z.kbnz=,OXR_+6TňU'_g? ph5X%̱0'O$l" H 0'v Q͏|χ"8* C_1^ FN$U JFRCxCĜ1B΀zU~<3L0z'܎(Bx'cd9'I[ gL$qSV~,#zT/F޸z3?-Ç%1`fi6"sy.e;aFa0'EYܡzXCy掊A$/Ɂ`NQ_pE)n`֫Q_p*v\_#j@ ʶqOX#7B28h;0&-Ο̒, W۱h\. ivN'-P,'Cpki{, K>HpO^T".'W&vFm/Pŗy^ dPO. 1SgdDg]y3\Gp P@ P!XN')b- Y6a\zgä0)y8OMNQKU"Ű('#wg)O; ٞlk(%YSIю|Q Vj'.Qb4d0 Ϯ\>8pʈt xda+e3dFHD,Ȏ|vhX/ERX\\ a ֖/!ʷ²m TnOgpWr1Nu ʆx[p(={p<7Px3\^իQ_pm FKnWΊ'U-k#aJxp=-:9CKJ2\L17|&@q4f |q5Kkcqp1 Ǽ(n򕡦(75BVqO2mU?HzzeO_Ǚd17On P̛( d&Wɭ0Юq>/lѽY>K>) ›Rb<'0F)@I` b4bp*%FCi p)\Y| $G|/=ʯx1#ͨT0QxB]ǣv—ROpPc3@^ʨ?C y*v|J_R{Q6ƍ(@p"HW_pMr|r;IP P|vWKwgJhC2clqD0&wWV~#V3ul;7#$-"xR||||+{+_P-@\7C(TS>K>\*( O(iab'崹F0gm1`9#t;(@7"ޣmc Ì>ozv)*_^F_=EU3m?`Z$,>(Œ qQ@i0;^Y*\*B12"HQF {X;[bi `V'_BPA RyQoo?j 'o43%W65Ϸ^Up((/SO, d"df~ L?qlJ&I,?Afp@n 6ees zDi/1἗l*m#I&"[f9%~WԺ1[{sHS/rwJ/зW[>cmک/'ڽ=4bx? -C{^[3w W޵Qէl>k=qʰ?'č9U"߽6_!8+qBGJI!UiQ~m;okWg[zWJ9_w‸`(yas(/W~+KJ. BP"E(b9i}xIENDB`fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png000066400000000000000000000164601417326347500302030ustar00rootroot00000000000000PNG  IHDR``w8IDATx]XTFƎlc!шl(ƠؒX)AK D1 Hbccys޻,ݝ{Ϝ9ܻ**^^ _ hB`GaB՚pAc(GJj-_x,w<bp$kxZhSy#hNg}us)~e "ƇRCavKlra6Nm}3 ^2_cb ^x~Ve,9\xu/D! Bf,ںӬ0$O0+5e`Y> 1x,s+xf3}T!\/f"[hQ|vr#rE>2XaK!6AV;ۤ H@HztI+3av6B`Y<sTX 'kg:9}HCl>1)F$($Q^I~`nXG%I^{\omK`Ez} P] eUnD<źbqoG&UxZ_p`-6Myzy>s.06Rv!8ϙl 'yaI-`:IK?{%L#%ܐh(6Ikk&'۽Jbc MlT b^A*gyj"\[]^?rm'gnuxpA$_i{'770s2-TUO+ιHqPNPAPz 㰬x?/ ]1|c]N9 |ow]1|c]N|c[CFciy4=MOcr ǗJԼ[l]1|c]N|c[s׃ ᡖMߍw${m>%*b|m8Hۊm6 ( wTq^7]$tNs'h%up^DbtovS3fy.]Ŋ%)tp+t̼r=#L@1IFD{DAs/:h&:%\5M9_,.#^ Dj{0yJ܄2G{[@&Ձ]ILgl/ @9"U;ɰ_5">fYl{ߏ#i= 0D"S@#n'rXcƒ=mJ|G)/)!'G-=Ҿ xFׇpx~,jRg፰bm|]jrfus{WC.-kp83ݤ L7&mi eh>EVy?ySX̓yDzS|Ѻ`ۊmƶ*Cx .:h. #֭nI|jlKBk ')`MQn D?̓+NJ3Aٕi$8dvA[^_4i~&E͟4@3B]:a ˹譱-EKu'{5 ҞA;`"9kc.@gzˆ_MG%J,z* _"ʩ#S*J'9;Gact=*?~rCໂ20$Mr^8@KtGAO&20QF\v, / ~B v{a<J_?_?/xI$"=11gw^?Aܳ,*P0]/w;kⵕ0CGnՏT&/ilqޯ gd=sj&# Iy]%ui:Y}_x-&^@W6wr̭!K`L3 ԗ`$]_xmzX_|' o癤gۯ_WKI XSLĢ陛@*@[ؗEE WNCwТx)x^ ȗc6`[v>&HLto#>nE]h^Ee/@@ uTl+·#2 qeq˷̛F [OOmLiyIvQu:N߬4児SXmջw{$=!.wj  F]J>㹧WFj#:6݋Vv`@>9wQ>.(0w_XgCm% 0.~{h</s(St~ uZTS5aoyBbvdLY`ʋ-}v6{RqAR|8: _>QX>xFy 06M4e D}緃%Al,qAQ/(;Y[vjł cik)9Y@X *@sԕep3/C3$8K)]T1N/s ~^<@/K)&2)e&b2/+#n>Wi;"\֓"^dždD'\=Xv'q@A]PgëqYY0=B Bcrr]`e?_ bco?-8/Jr#nhKQԐ &kA-:bZUT^](~v^c:} a ¿H'qFFڂV dL\D:lճSWyJJ]&h;"p1}s;!a&D $L5NE]pTDʵJ* qH.8C^I03n̉'dK 9*-NM8pp܆r]}q]$hIMG3w8T3] +=1CveD txbV0F5 -.Ȱ6zLv 2?9(Af;-K|5TgNu!"Xʹ@fd5>mYޖGYNODĽ Џv˕hĬ2G$9%I/sQȱ$a;_}č0&΍ `.T5sMH8zp X c ɟSBk+$7ы4@߿'b<| r^=#/[`:-|GtqyO%m@|!Qlr4?.>A8r+źHw~;OOr(0GE@F!],aNݣ+xO88"_,%o^䏼d \XO'8)$HNx˪V3OFNqb1C-?G+5]=`x bvfdC>yNGc^Uyc.rH!;q. 7لx̎kJzsr͎NF\#9Ꮎ 65;U\xgXze$W3;ΠI̎fi~fύ_ Ѿ~O7jČ OA>{*y^S.yY/hv%nE> AB k:'mzѠh䰧a浹YQGݾh-q<E>h\xH8ᎉqEI/$1HvB>~⸈Fs~gطcJHͺ)-.h`\;!]1hv0*ʋ|T~^,Nڢsn ,JHNqjsvSF_d ." ub9SSuB}خ-ջmDydH@{;1&܎I4 vθ`UّvRbL'u90rJJ7zLFjڲKk[r>W8&ǿB). mـjM"<@*[мHK "WSL9q=]BԎf *ԉw4 ~>z:dO@?ͽb=Bx7PM$ba 74m:dyAl_vHh[rB-lQNq6v;, cX`nQF1ob֤}`N|{dd vaV6c7,XzF(M֮}/ q;$קݗn{Zk'gӖM/88/5G8'( an?Lp{Ћ AP(G~0/!`ЏqpV?΅.#b"1<#0 >Aj% hbGgD94mT9`>M4I!-¹>AΆ%ÍqkhbmnvzŹvŔ2m>6ֹhvpy/I^Y^W#XqI+̓dDanF^ۙ x%p|%1=G?vV鄋ͶA~!#a`FؿA#fz}5!jEu0 1ɞH&0@"\ \$t5Ҟϴo%9jI6G?[jh>Բ|RGCm:+/Y| SQs;eE&^IS^|6Y괍ֿA}{]ᓡlӻN`#q4p?Bh$E^aVhE<q-7U㐾Nj}IN]C#_RtZSs{ۍrbiV7V "TTnN@FF&œ2#^Vwp% SFB}Ϭ YsM664@9NP߿EZfl%Q]ylsICؑ +lkv4(D㍮xW,!>e{ܵqWmm[݌?VOέpm.,$6-4zN<{ȀI<2'AҞ#Ks5J"lx|$>ѱB+QHL4;-eI_M~hwcv{jr*][wjE'8?f48`!.Enxplf%3 w~ ռ`.@#nMFhd""ν=. a957}i]I^}$șO2қb`ӦM5z.7agaU7`aQhNv!]~ )Nuj B IiVq ~2/dvb}RMmC cnZ] 8[Ю'4utUtvm]Uy`I 0*aڍ@ L`~FL01U4Q{G~{Xvuפn%z7lPՊcc%->нjsnT @bFvS6̂F'(6asGE8D;3qyGE): ]Q:>-BzMs\E0>}zO\XI GsUQKm g~aam\jxn:25{&4SS!6cjTU {y`GHde E|)~wX9ZujUEU=kb>fN<Aћ8eӝtD{Up-e[@xDi̚&,>o,Av'k^IENDB`fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-xxhdpi/000077500000000000000000000000001417326347500241735ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png000066400000000000000000000173451417326347500271670ustar00rootroot00000000000000PNG  IHDRFIDATxXUGڀIQd7>&$Ƙ( w+("H/J#Ec5{^)Z&&o\{N jwΙs;jh / / / / / / / \8>}H24/ݬo\_d}MekJWt=S7M﵇Ae{3*=W~o BzcMAU:T .Z]yP]y6Bܫޠ:~VUH}{鈑$~Q)W${/Q?WR F]*r(DQ2 ̫ATe k˫w8%d`{0 ?F7I)Xo?xl7l?lGUc<! ]aJǸ 3L̬ .1hyFZCbc]p#6Garxx܎x}A )n~#iy/Mw,5_mާ}^*r]F&r&\!|e;c^S }Ij yH)Wc.9Ē|Yܗyz៾^.sDȱ H[Ġ* ǻԾ5wЛا9 ϧW~U`Ve06 TTO}QHGkҝi,꾆[Q晦u`p#gt-N8\;a=a$im^>liHTEo06| Ww_1錄{%`Hj;$1q;,QyX)@>+1PX\| 8!fOy8 h}X>Iu~^vJ4; si=@º@d21tB}' 'h~}N'Ck}|Ǒo{wq=˯u@v8a'䴳U@ Ę i~XO~ s1Ʉڨj[}wxJw|H'jA-jߓ™saX*a;ש@'rZ/\nx%zcgETТsu+n ^@Kȥ* Z]H_N/h &$2J?nas".P;ɉ!%f> Y_DiDR[gwPZ8. < K҃@/Ή2.,j>H{@fuS<֦EEZ$XGB@)iq-TeF"{ri&Vhځ6g S`m0Ne$>9>t@y/Jat3|cESax#I4a l?V2!K?u3,aL*-sz@sҋ4ʗ6=`ˢ`YLK 0RL/'@X5$,BY 9ª00N 4$[b!F aUv 9 ? i@Jy%H*.~eVXUW2 0<z`Ac!Ƨߚ0lSʠ0j0>''(,Ǭ͔ ʙ505%#u09|~%pq:C@(b,Z0+[JyBV Mh'Q^ k!Q;P -0@:i)Z*y"P\3DȃLk-(ɮMy0L$eT"3AL : %$Rg-E˃\$E)8yeK3CZ J\G$DmC:4H']aqw¶WrƟfL88N` λ„s'xqQIE%B NI;azL(Jn0IpodiJ gI^0-%{dOx1Roa#(&7H/#[ flf"~0+fü+ .04=t);"V Eg< ^J  \%Q>x}сp)c4^SgĶ&a } !"!L1N$$IR# ) ~,#L`U2YVdّ:o7[t5(`_(AY[+G`YFp^XWbR/:~ŧ`q[w3rLLPN3NÈS,:F!ka(rƂqVipDM 6yъqi=#1idF'x{oHc$SJnE#, \F&iNt=C :YJ6NB0&m}ggd΀-< c[4i}>ZX:g֫4i}{V'nDUա9zP2ŬBsQn{&J#"r'L@9^<8Yf1Dʃ)I`J[#V,fi.Os+.s4 dKZ d>1愍hy@e# tD:ʃLm-y@-R]*l"иKY Xg0bCV Dʃ,hR !kj2L bCa(OH<0<T@rjhy,Fd,SY mɤ׋ !d'Z;5R=ΉĬ7H0Hh@T7(`BDR-|"Uo^CF0XKYީ_I("Ѣx?Q@?,FɌ%)?[XBQ_,²L,d ,f`2#`+raE@>8?N "Hcry+6Ȏʗ`Z,Pd*(cvRVRIB <ȼfWp>N,2os+wx]vLd#z\'M:T8= ZC.Z2. D[P$Fwm4gX5G`K(Iv-!sdf&XGSz4GF9=W#2J-hhl%yJ6V>C[I+2M_ 6@abu <(*%gtXdnx;I<3a5HH#!]T<\YO-0&|\MID3= a]!庈F*#4˼`7]σO [EH3#s(QJ>,*D5 3j{<1 kM҉.%PLɘC(ؔ #@Dw >0 4wKaoZ%n5(Aeؓ!Pu<(P|9J%Q1 $JɼP;k{>HϼΫaα(ck[zD6RqatF/n7JN/ Vg|C"П}hз+f(g tfG (UΎXUgwJ gyEA>7a_;zA{LLq'R2yD\$KVDdI<3s@E$>YJ>,. gl%zȵ ]Aյ{BV-ihy÷ qTWr-OK5'[_!7"jrLj >Ĩ#FʼkjR,ڡm1J歰;0&)~GARL ,G$RƦ(v)SKSqCSDtFCtTSThg(^u[{ JgX\0B7^YVx(T϶pŒ,|l5|4շWKO"p!~?뀁6-Ymj)&gfU`P!Ց1qšs4x;vg'3r GOg*a晙2+~Xyely002HI ]s JJ`]58W}Y(SRi# mLIp}h}&B_0䡹o}#4ڳ*wQF9>x|-&n&#&xD?xI6ʗ'ʧƹ";m+yؒ2\卑\\^{1uKNRq?!sڎnW| r֎qξ9c(x) Fn܁fB%au>G!L//d>}|J~kG4SÎxw`w=YL̤ Oo?e|{|ld' fSIMCb1&Xqc_>}}$_ѧ(0aEo~B >}CW>|AJs~Kx뿑Sa{,r;"bDŇOX߼)UZ"\r81pl>G}<%RG8*T79jE#E?o^j_ݮ(+Kn?8 Zx^x^x^x^xW,v-\;IENDB`fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png000066400000000000000000000271411417326347500303710ustar00rootroot00000000000000PNG  IHDRF.(IDATxTTG(^co ƆRE;^bE!`Z -F%{Gb'K>{Λ,}-,?[fUQQ>CP>>sU88UI_Gߣ||y Cj2@KGL ghbKy5{޾gWtWuT kKU T%Fjxz630k{[ ir+eDψeS.5wx5ZxM6‒)ƙ|gdl:ޖ2m%v?[O 32ÿ>N=ANk5oC~ބۀmQ¤`=oq-f5!=O5n@ǣݟm!4?,?e$^!k&fקﹶ6aی}(Q`kqQ;iiFi R>v&?v4 l=muʝ^kMhI[`_ d北8鏼aߔ !8hpmȌAn _lj+|Y&7'o냌=7ʇqxl`G`9O΃]Ys3wP۟gYc._c`.ux ^SAcyx/J nIthӈ,$?,>rl'O&N{x-[D=/\k>4Ѡ;^)אON ϰ4o%Xf}:l̝0H{ ڷĝ|Af#g YT=Սf[$R@s[*]\f\-^l[Jbm) ѵ",[7J\Ճ_49-8f >8G a[.cElP P~WWI sb.)R5dg~V1|q[xQJoXwtclcȜ}U[嬢Emo2>e7d#!}ùu8G+TGd><^E *= v)J~ì琛 YxAv`{]G־?<!'Q'Sk`.&8eikTںmY@n/.ǜ,:w?YWZ $ }k첲Ș9w$>h~t~wz6n$1wGB劁'_\@ao B:^t[= :8NvQU][`FEe*+I< Ì*@oi/ا0jVr3ιfyBU3R  Ϗ=OY/{* @SWCʻD/a*| iY_%s^[a}t PJgӬעk,>`_aKgݷOs/SJLW9q_ ZMPc ,W$@36mƶc/ HJK_YnƘB'phZz1$s1}դ@HpikD'K7T8,Nc񚊴ʎsLXͭ6 vca":[~r e|N$8wfqDWރ;Cpq]߷?oEߟƀm$}CvvܦA7k%؍7U)} 3uQ@Lm|O>Sn}O!@>#Z=|<"I ~OW_,;ݚ(0(ՋBʿk)(@9X+du2 =@yٗ4(!ٗ<7zB!P0_SzM`B@ѪYft$i;[lnc6v9$jIؾ׈ߌ{)$ "u*}E'2G #0P_l"WШUNMhfQQs+}x{]Aqo?>GǠBx"@eD=F 3MӪti\w_d}߱3i*-6l8s:Xoi| ?110ren6ZՑ$7T[ᝌo?(0M/^89@}Ö=k;M=KHT1j|546<לxpL$hOnvu㔞S= )x xZvsrSx%,!1)H/C{zFU'&Gy| ?9cvʈ0)l5'KO] Hl=XQj4P  b lHEg&;ӾYˈρI+8nI3!:8 Lny(,b橆Y/Tw3G_r[hq5=h5Pqܳ|h䘼 pg?Y)PхP1׬^*@]ZK}~m!xPWR+PQ=}F]p.*~3(:$lt-4^’Q/ Vjv[nгx`s,- 4G9l|@d'Au>xtO[g?C,Qyd󠩾Ii<Qe;~$L<~/+F}H0O&հN+ZրVI4הq%ߝQUC<5Xy2ZkɆ]=|VlهDU7;+MuacIJ**_\q9OJS孄'$t.,Šn{i9@\,h(V暵sQIʻlͣS(Vm=0!ʋh L$y5ޯ*uf5dlxgF66zx+k0w.i=* $ GEü 0/~Sl. 6F&$h&Ѣr+ Ȧ+A?V$0XZ"h~B@&Z.VcV~Sn~_^A_baW.f!H _2&xADG0*m=c<>k80Rby~}:v- _RsOg+)<:K@?̖t×BtKzpq[~Z+\IK;+-NjǭX4H%Uv4?!@NR턆|{ 98jU[=<6߽.ynj㹾q!HK'?uuu8$4. q!tA:WgiSH` ^Ƿҿc~;sߕ̷q]G1awqy\wXGܵ,H@js^e Р[an? ZC3ϯɯ`|Eck‚ufPuy K5/b=x0<'D)J\~bH^a~FOEIvD *mp\%d1] ,B`ޥ<E:HD[YRqE`QZD]`&W"p^$s ectTk`ЮAg@Vщ+  ^ +PYJ8@y|kA˓ A8k3ϡ: ^``~z-iY^>"?/Ղ`?XVs 9ğShmʊ57?6`ad MT9YlD,^PrL []8qՒ_8%oii.rZvi' o)I9 WwQ]-O9٧ݟn9ǽ4&\7ԨpyJ: ϝ]p8&#%2 "jycHs"DC,ͩ0@ajgg@ZaaG?sO6Zד9x.XvxsY pKz]!8D '@'H0H(b`H<".oɤMC蘃mP6=s[i鉬ܜ @|0Ҧ>ZvmO}+*:@K?؎"`E ^c X$ wF. fZ+[a W<Q\4GhjN%N8s\ ; k9N ݕH@xe,k` 2s(p"&9q>;~zTK>>5-ԽĔZ!]};a]IlnkDQp'Q$<"0 KS JD* <$;8sY',[j%]I@A]mly~n$PC qQ~ Ap Z, P<>$9ekobHޖ)Ь%1B'zٻ ",iytO-%O)@E$gE =+*(@IxX=7JNydp~͖ ;gnV^q{g8=Vb)g[ hzؓ,hy$ Pr VC$ $X~ )VT2b0@1#:Pu:A͎͌hOڭ8P7]YfjG._ ZZ*(ԇJ#GbxW"@)Z )^a%ds[MѵE~/9Υ2tkRY.hMR?*<Z'~Z-hL4X )x0ȟ..t~#&bUT*TTԯ sO駁2yĻ3'(@a $m%u|$(vC>Q.Ã2J\V)@zNޓq qsQt]Źz*O[Az;3D(_LT"!bY !e@$p8@x' K< ؀Ys0h) !/h1w)RN8@ D`av ^VN*G)ڑS#Wvx7:qC-xN +8%)i%dX|}̺~UpsPl<$@넫ԍ۷֛~iVs\,{f@v3wkyx07|=A[`Ne2@b ybgJ ޗҳ PY$пiHEYP~NfjpٕVNWq'+sNc”_EZOg;_,Ө]3 6'!)-ͦ@ֺj&s,-[_q0,fc¸"w Nѿ¢@O~CŅSE`B$SLZ走$a8'>0)v-7LC{PNSP(_ kץS/'򃩗.sv?OW6PM5|S6ex&﴿עgA8ǰ)jOE53֚j&S2J^Ib"N$~>K^Cq'Zg7fC&nO&ʹ3k .фh/bu6ғtÖr _JA?ҖާNeKoɧh{ vWwxgZ@wE-Kp2<ٶ8g\؜TWdx nuہjpwkԮ~=sq1 3!5HDKG "*  <呥p0Ȗ"qǯ`3ptpQf oXUW[e F-굮-y^zuۧDntQ^D$ŘkyG'Qd$L,51$Ebx-~8BƶLX~@Ә&ߘRUG'Cn,k?+변j#j>lR?LEmO#| XgK&X]u\(%1d$Dpx\Mv+˲zƮ~ET* D|p DO~j~PlWmP .\<׉I(* $vcwdl[T+z6U?ٲ1ͲQJNJ3>X)*.v[Fv}N@#3|s@7єNri%x3f'Hxt`:$잃GIe{|u&ߎ-dcUswF5ES Ac^ 9φ-|] pO{$Ik Q %xp;8Hqn=?gr{9La=2pz1-:MR֨dWoXqP6v#ݧ_veQIl߂L!*fאDE;a8O: 6Z}[BmX9v7}8ϜjƢHaEK:uUlHt{S|C&Ocjٗ4_MY}z&!H  OCE!k]׏OhkaAb 8uHI_8-c֨3uY!4>"J1֛H&}0Iؼm~ONfԦ_ԸKۡֆZfl;Cs'őHlپ>Lf{A)Ld,(M:8Jtɿut׶鱽N8`yiZEϵO/|ub-ocԔ|Ū>gŏ/fUcUVU8KK3k׬ X_TXF[b0PGp~⦂Pi0 _eC֨w፴ۍ"? :.ޟ EV'j-\ġTu%8RA$Sbx.#PWY4]&Bb[.q~¬ :̞j:يIh Z%8 Sm֐XښYl;QׂEAԍ7_ùNٵZk7c!۵T^bVؚJ]?`5a h[0Ղ{m1Pka '%4(qxTلd_Y&{((B*W?gwc$IENDB`fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-xxxhdpi/000077500000000000000000000000001417326347500243635ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png000066400000000000000000000246341417326347500273560ustar00rootroot00000000000000PNG  IHDRRl)cIDATx\TWM1&&don4V v( **  tXb޻(T1a9PT`qs><νsnyk|||||||||||||G3C)X2o;o*\:ݚsjB:Xձy+|Mj.LjsM\f\O&n3YhA'pz"它RVuOBB9աTzؒ?pn͆+={Aͦ#+0&fUmpjkpvN:8~=z퀩ASA\XDlKWܢjv7̮ aVӠz#XPf5ڬj2:`U n#V-~]sy Y4.׃zUɟ >WWojk=~׎/̬ŝb/ncVkaMͪ&.3.;V5q6m]Vʿ&P̊ϝYC=VSP7UoaLFcVma|/302YM56̸Xmu~P! 3NH^i-=@Byo Fa`WFMkI̹doc+՜v~-8`McVkaMͪ&.3.;V5q6mq2z̎N LtcJ->02w! Pbr^2D 0CSOHQ(j_N.A!x  "ӡ%0.9dI޿aӫH\.& $?T3ݡs ! k=$ POE2ցfII{K t\&鴊=ri6YӪiNo޿kqen\`_X=L|9qf%Xo据 {t t>X0 YL}\ᘵO%] {ͼe>mp kiHMu6kn?.i /1kZ%f:t>? T1qEWEe. `3z,fMU.   n8Gz^p~ 6 . ltmΤu5[ať}J`P WBXXZYM Uȍ80YMK`Íxά&šXUM\f\v\jmۊUMf6d7 Im쪐:K}%Bi_#ؒ= [dbV3Z J0}5 cVkaMͪ&.3.;V5q6mŪ&fdY&$)}HSa:= *`x'q:ٻrt2_J;|X kbmV5qqqn+V51C%6\? p\. p\. p\. p\. p\. p\.%"4v>[ ~|))un칕Bz{_[gT.+TޅL_d岪۷wATOuwk_ YOkkZ [++2߬V^>}mۊUMfonv7WYQJ"fd^Y ?+0 ^ѱF5Fþ`͕HfkaMI7t#q[,Y_ !uB*L9FWx1.pr6si\ ~='BK{4iPV_{/VkaMcȜxyY\v\'nFX afWB8K j$\zxK΍{p+F/r2`~B}ϸJ %2w8SNeeg4+ N&L"{E5[~Cy2~-#̯쾙:mx&2ec ~ @+ %Ƕ&2i=tܮvUs.he1ǝtnG<Өv[0< <'6ks@=k$_ /m6H}dn]~ۜ͆ N^aV/-thե@\t9#9OW`Oͅ1c /fkI^揔 XTE;M(^ -m6ȉlJo6ȰPKu)0-m5_N$phJ )6Ao>A;2X/xо`ݏ7(S-b2eA&3a9+rh|Ns;O¤HGzF5{]mqg)<aI{ V2=u{ =N6 X={?M;~647 éĚBDsY.3|}!f ?%N%oF-Vȏ3<}~3}ah̰TGRrD>_y0Gp2L2 $S6+캚27?ǢT?;KAaTe4aR;8l /cQ1g/;iЀkr `&C~o}"OIˇ 2 j$ g̴5+Ւ\ÏWuEkTN\%~\Om!j7/ $+X ќ"Hx>UH8FؿIq/ 0X@c%F IRܙ$ K$x3³”*|`t.+iXk^71>U(0 XCKrYFwH0b"pD$6FAcf2"D"0"jXPuF~o :'DNZET I@la\ XfmҼa`- JHR#HYAF c3` m()04I Klg u ?cKhfWB8KDG:κv]R:]>+j3!zJ60?f =?*BQvb,"O՘0j0hX{~?qA&qƭ'bhu#~`baEre()@ 9Pfq b*NfIKX-8.;aiVzX-"c*F0H. "lY~:L\h٭ . >J{aRI/ ;lJ:`u1\[YO T-V^yA %"ADWI"Ɉ@ysXRWR&4t  J S,OӛarjnU#|)) ooZCqLK”Uax;k)?h=xvC0X9S##m`a/ l1x $:yL`2YFS=CӔk", $ok"@ Nu> ,@%SN 0?2$^%S2`#EPM֥3+.Ƶ$c<2ߗ0qjh~$ǗЕ0Ǘ$T<5,V|8E=N0.A SP?AH `~*@ ;t<_&OQ".S#Ջ0G-sQED!QVLOpd~Ih~*@ / E Љ )s.mnЉ`~ S`iw I/ C7дa[GDPQ д}D" @M؇;$7AeΉD-n)~PG^zarT /d²rLcVӨz3r6ڷuCqX}cÄ8g0K/T#R Nŀ8+" V]+m`A$l"N U4{q+i/6褯 3TdI8lȼ_bl;,/09墢@+erF20K3JB(SlQo]Rgy)D(k[fA8y+j\ʝ0-'_X(` >{I~"`,`PJZE2R,v-8s,B(H0HWPB`IH~(Ej`w+KOmNolș`rfJI)95=Z >Gx *Dm`^&R.`Q,*$vU.XUuPiv~;?f7^{o[3|@qVXqiҐa 71Q Kk3!76~Ni0LI+0Lm0/tWHpLIT# j ROXwii0=]b Ld!0#[y`u~>3ח2+OE)ğW2`XCnp-evguA99`S HzAY(TTUn'l_[gz !K}|x6cI`Jê}X}d/f C\x+krZ[BgYR/* LwX^F`N8@x-@:;ae>"̈ߥ0k`ٌ1G~QSz_VEN06əR5Sr60j`\/f s%4 ?TPY(@9@@tK4+6j`, ʬ}xJF TQpYe\`PY57A/ ͬ})WAALO8+ Px^ӫyBHpc><Q<n`. 0`~AW":ﺽ7 s IЋr?TgAt7f in`Q-jɃZYx\Z#sE~;| ω pK[y؇´S>e3 ?~G9WCW?[@j7PŸq4/T(@Jf j`G,X @* }$cЬ}ś4{Z!@v,). n`ʴ6< L?ˬX$pM?V Aozzx߸%JGš A'ଚ zx% J3 8M|0,/IW#}Ht|kuS^ׯą H  p{ètALwf #r]`z/agVeNA%+lZXD9؟QWEoB~oOY=a`-uQr# ? / zxW4IS9LFNfcb/aio{%r6㭽-9PM? ^Td ҬykT YQ{57A1;Z }WދŒaִH8"ˆG{4g]@ܽNO 9@n /S>U{|/302u%o IHo`*VA4t*2%oeF0?]!B攄#/9-Y֕ᰠl (t(R(S,JOu 9?N{03=HzJ f 3g ^<[?B~k煏9022z[å}J+J|DK_$/+}F)V1iV]p5Zi\կYTN^Et+ WSN]lHZ rJSjx8{fҽtlroqZ:iE*#g8/ Rbm.\6J:k)xìudY#Pg̒ŌזӠvl_&?Ӿ}5c> lQ7Ku^[Stajպ6 z['atq3X]C4֗~͆6^"[e%03 F&_`BSaBPɹ͠6nIE"g(|UL/ uUTCT R?x Ʀ°#77f Y-4)?7jqA|xW޺1_o+cn?~lq=~i Ec--aPhŷ&jXa]-RU_q\E +ݹHq\w2;%إ."+zYJo2E=)],ث>1T_H〱bŐ.(].8w=ӎy7UI'i4Ўd' ?ɒ)7+tx7wd+믿OLiY 1aK_$>ξ槤f ?55& t3gOh5L&tj_O'6u$9+LiԞf3B!١G1[ZoxM@zW]/ڽdt(AIĜ;tŲ̆׵96\ЧTVv_2CםItiϐQSف,jjN ޞdfCȧbf>lW]G^sod'3p\s^j𙘍OĬ| f)%xM1Yy[k[iQhiR1oYyy~c ?^Oe!~).4CoxDxUMG=/\[">>>>>>>>,aWIENDB`fluidsynth-2.2.5/test-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png000066400000000000000000000402721417326347500305610ustar00rootroot00000000000000PNG  IHDRRl@IDATx]XW%&bFĆ ({L"v@W X޻A:X {F) ‚3wdw{w--Bhkp[ywʀK\R<ɵy.)mxvr߳Kdn]<~A#9 *Q⡖QGܼ+_ҤwlC.I[̒=O%{]=Ϸ >dwٿy~6{xN<7^ku_2xGAWBs`#Om処7lH+N]usp#˶z?gH+8|ύk5={I[5t`xMELUxEz$C.13g6A<*f${t'$FMy,?ώ(mmE#$IX 4K.)H'}?dq \Q_G<ǒNI%~n_@! uBHŶg٣F1B '*o-BQL{#IeP0ұODHM&7`\4vQE fizn@&2'F?f1uwf7\^1BNu"{&sў)>ľ=D:xtFQ':t"G_uM﹃qZ2.izsa $~CW(6:"0qopbҙ@'-}?zD|OK8fv GL)'+;(Yu ]16IpÐ+r)N/yȉ2u7y # +t Q*bo9u$K&0DPmpri]8:qO`z ]1dP_" @:t6ۨ? S8ؗ/|c[^v(Q_^ FzRޖA7 x$iQBw}f&%NSeC.>^QpMI4>&~.QQF>b)trD}]B{1}D(H u #BsſB̤~`ň fUEPfю֞dR1mʘ&NRu @׺a$"t]Gh{pFersyezjg[w()0|v\sg6"n : GT 7yߧ@vv6K"jQ,f= {e}nl @2 _@or#Boj^~꣭Oœߠ x;zU0HNر^sj3H{'}̠+_2H-}}}}yk65\ݞc9h?eH*-ˡyá7Q pPB' z^xn^K+N&3?A[cksUσ/y׶^-ZƵVq' 2ӛ\mmǾ>ƾ>/̹s@E"[lE&܋{=&9~/$1}>@^dV,brmmNpǐyu]t!D~C8&ЧLISV|uFlW QI$G(K`s~tgKg-Ư ljKfCwϪ %y GoXuo}x,I["mR 5v ڟLYFԑ oqB⿱}iWWd_|?Rו\|AZ93]hBe55h` K 0?%`dglEvb`qb F"ԓ^ýOfQ.5 \ PEض(pi3,~vAIgZ]p3 ~7۰MH18{_Cפ/:푴f |~~xJ2RJ'B u^CO" D UeE~'aS]:wY#WY grOmh~eqU#. g8#BD]Y| ˆ% xHm;b^y1 )qbdp.'h fg ?d`(uC>xw_^(L{K'C^БuD2Cyq&<(ȣ[3!.$tcm3ؓ|7:ׁ+@h_Svf>QFvH?}ׇ^#6:LS^A'v6Sn.["XϫNk)av9/e-thʖud k% /> 3eEŅ'Olh(rPlAI).*ÝQ%_ClC%vN7q 9N8[r eg~p6f`Te`jOVe}R:d ~#Ӏ4T?r>W{Oy% dSv21_.ggu'mS.rTDXaՄ\97'rlu4LqN6Ɩ`:BF9@Vi*i/ÜǻhX k5`v`6SV?m{LLX*0~t=j[UAMZGT5ѿlkߑ =*Nˊ'^Ni"`Իd}~UtqptR&qnaO5GkYU6>5ȇyFdcAh`>W IEu\cyi`\e ksu\9:.ېaUo]''FD{nt<K2N8Y{߄ס<7o$/ 2>5 [cksup 9\7Ϙ6QAYG@qK?s0w6q§`@AϽŅRW0!FطҢ'+lfWI:ìXjʣy-hBF0LK p$kVCm7#Y.+'-8T|i `-Y%EқGF" L.,Sxk;rAsKM]/3`ǥ8 ^Ԁ!QV^Y\%J,ϙ!U*œ^KJ|ɀ[oG3iΐ2eTjh_c~}Xհh,W55UX1@ no?׏L0>0r/P"4`r1!pq d $j,+UZm2\o1bAPF"mL<>i]0!>Bͮ{{&AS.lc 3X̔l{Zkz]i `YNP5*ngJf&`P%Kb3>\~T"?ɠy;x0AށM`v]fK6\fM J-aLNqN0"J֥@ae!hNX{Čћ$?9p$M5q5k|CC 4ف@o4M.j=_# L_'OSa]wYƮ|EP08?,wWZ }@[@4ioOv~활'ѻ)e~y3^750 |QE^P/qJ@~ۭTM;˘}F}"?"YboI~D v Aq9Tw}@ԛ?P.j++tjp 0OCoh]a 󜊢,l[xg1?/YC@|sv L6&ᆘ: l;$ҭS ~&`Xɭڛ(@ 0fDPps)nw9rBpg-ë |4aS<:Y"4%d sզ~j[,:} q/e;N, ֮0)B88 _"ܗ`X&ERel 0Z9 lӠ @w.N5Y8 c8[l\"4|`~&fEK`RžWW^kb_Y QmGt`YqVD\%Es7B=D_8{`F{#VVVeA#:4|-S,:\$V ,w:|ۂEj=cݾ;Rq\IB|\Mˠ>Q@etb$, F x[,ա%"J.d@gNQ9*<`kF Í &[|㱹ʣkJ3K𮬯Cyw //e;E ``qa.amUPA=/ef9E0'{EBP \"!DQ<̇NW"`32 Ze9\VYjm4h->%_{E&l5"r_7<>㑻5 u* #{83S&݋L_E2PlI(;!b[ a9 :q;#w] j5P4֚s=IE~)>X 80  ZN,a T>7ιԈr>}f*Ǿho!^Kx %|kbے7Ug9 OP(kʛCɞ9",w)Ukc {o3jL2%iu="J ;_9&?+qr3$Evg"gY(NDGtn20!m7xӋ)B:?|!k\b+"S\Un&@$ُkU5$6T*ȿv(7p`Ny!R\# o'\FF*`~xؘ .! 8; D6K#-2Xs(lL8vÂ- yU!(Iq%LU›#aǗq nmWd`N"l} u. Sg`n)k{i6+_x`yGXz _}MnzR*6@#*1p@a/'$BL~ĸ &DR '!"hz M1&節,hա;jPQ"]@-Ȇל@~%8l%8NذT qė Np# uby(Y!.i7 'mJpVe_ҪPYN) 0ZM&7"PR, @G2S|H~.'?Rx fa `tw.8 "Jc;(t GYV(/pX$8? R.CZ*E~*0$+G~%`T_Faނ=P''9y ?s@p?wܰ^?9Z43fM ~2w(3_"T8KPpS\vHN˜=P'@b:pӻl'jf͟j%3b9֭7NB,RAi B~i-8>PSQB~NCVA '?R,TW=PHx4a|ӉrNJ-L8j ޡQ{tp/̡/S&?#y0TB ;OR1W fC\ @"bQmwNJڪ!5~kV2d -*֮fP3ְ@ ['@G~s8ד(*uI ?o XE*E.F* ߈fv!'YnVeUnZ f1fPaly_*툵#?rsK-KBH5{v\T&?8choX_.+UzuQi:=/7rNjXAM[K3Nckj12vj>t=; `Yi8& ώtz,/Rg&`>_VgW3XxԳ< 88\\uZ e )`+f0qy!)fR!bY^: 6¬еR`"_ 097z)f<h 1:&c`CYx׶Hq}+łaݝSp&mv)nI qwRٙmG~p#3ծb닋bZU3܀ŗm)eM}TՓ=2VoUσpg ; S Fwg|`хw pdp)\z E.09Wwa 8o NOqMR\b+"C )FyE0lEI!%xK~pEKeaK˜G7o\H>0*/! +yk?,M< aLR\* @o-d1t_mM8؂rIج'Wn:²M~n1r7͏ ;DGnGmChY\">łO| BG_}5DŽbMa52="]90{ho*4jCrίZ ahCT`KV9 .:$! B'E.(_Fj ?RL~bX"ړ \'?c:\>#̖@WҪV~M@S%f9䨚&p tH< :s..V qė pWF>*ca3)bsgrNi]{VS3\Ir:lgg9d0Ur? pD_"@&DkRl0{NNV76KPPopvX6<dGJt~\]Ԯ8r$35DO%OF N~*3< !>RL~*PWe 0 7&7u6\z'|a%rB ٪%j@hÝɈK3-;aF*PKKB~8Wp#L#Y$v-09蟜=Zџ,2p0hZ>W? erBm"Y @~F0-hT0" {/&W iCn|ig߉\SwWzFm$|HK|jMgsMc֣fJ`64чOH# @ s[PXKp'5i{~Z4/}_ zW TӪU^- ΄x1RXl  !"?}4'j!? XTQ0F1n_&?~:d⳷XbY_B6S䶿Y{p-nh2e}FS qQ Fn031a6 @hsuuDB_*0.jGsu!QMxkm Usc"?"\_Z9Z1a;i)C\`CNЁO=- PC}%|\uy<I~n>!kSZUPqܿ!\U \v5}3oi u*BncA~9`fu{%TюDĺM4}pw.ql SZ7cVdql>TEhl}B>bD~QA1-N.,@ u{A`V!$^!8ܑVȓ>\7:NG.NIr~4bϝ)ʄꕫ]nƀ#*T`6;QEFj ?@ .ZENS%$ O~v4Yw r\rȥz,*GSivŴB n&`DN.`Zȏ#bWD~ FVϚ>lPLunK9T.j[KLZ-{<TH| VJ4S\^)fG\v8K'8q q+uWH6rOk6GMr>b)TBqk%~4I\!$2YQ"51*_p׶(@!M6bxv?9qo:> w˙>e4m ii5ЮUwn2:MN@EP6F/` ў fDnmb.C~ ?]EPML!rڴ0zT Md]VlKBTo!BsRp3pbp[*ci{:?Ҧn?>o,,1\KtlvNe2Ȑ @*/7`¼ TI~  TD׷J ܑ;f;2y #h`&F}1pѢڸ޾)q)dP$Rb‹zP dEhU.L\'3@CV{c5Plȓk-x((u12tI z")O{z%/s"1ZrԵ*}k2+t z]K&^?".XKqK6#y0ԈUpU+`L~6 :#B8,W8AP)L8, nlIq$|4_(HJÇ#9Ǻ-`{{8Y+hENhתܒgkjvm ct6U_\]oh-U1'5s86En=<%K1a mrsKCJ ϸ{39rSDbj)bJ`C: `͝tɴkau`7Kqk f'n6L+t_:`U֕kVnnSR\Fh^#,,KH> [_d:EO /`XqJs,$ML`jV6nm*qw{;{p&;zhfh@5h1/HT 2"h2􏩧KfI^B^Z~q\BWt &˅,l3-K "H pHHx$0b]؃9R9J*Np1 ,@Hg ݣ\!UX)zP1}3m!bk3oszoJwE2|kT"=F8YB0Np^r.:hU 8 ^ ?lon r\\~:h f7}nli#~zT(r=q["Z"~iTj ?Fy @ lzCacv3MAm~=kW"> 4Ԁ6^-&=l1Lu Is `Br_"+IU8+@!OWxrG%nk1ɭ\S_Qx;TAĔQVՒE!Z)ѕd F|A~*U0@ q~Rn¿? }Jxr!M4|H|eua )u 171O3Pq"Z bP6?WMƾdlJmXG~MODЎGf|Ƨ T -)~`T v s8sxm7]Lih ~l<!%e<'^"Z[iikS4n>SouQ|E!\͞Uu{pK"VQ @OJ[qVZmh_2Nɟc̅Hi+ l[No]Q|H٠`x7YKVpQKݞ|hUtk?{ ѡQ?3whf'Ņ:hH|D- ڌG v6=E)r YF媋ZHp}MOF}cجa?>úl~N˪y!XRu6fӸ[JGf"zX t*RU+t4 ͖ ]77tؾ[ɯDDy6) k@1[R5ڕPS%Cw?h ~ ]q[,pF@u{.;Nj iJ1uSBY#'ʶ>bÜ?}WE.G$"(Ǧ֔8ZZ8G㌦7wmaǠI,*.p3ة]RG"krcfr/RK]7%g4Ez*}(_E#DY[Ddi- ʖz;'e`EZdތPK%KP)6v2'|KhjdF9=֑6l_#=" &<cA.:-HS{Ʌ"@8s*0Z fq':=7{x-Cx>44g3y}1ң2KaFߠb{v6Я?䖫G;l&!L-5hvq ݈ ~*8q ̞xsMc@; vRo-mcdleGپhIąJA3zDE@3VklL|Ãkji"U6`T$!K~/0K}bf3Q?1ሏm]I[~5^BhQ?pAS2fQJة{+O.;ؑ zpbPF\]TJ3neT(%o?>1us:B0vnKLJ7;hkӪMy>C8Y>4b͢V:=1'vN|OwZ RQU @b Ipm3pSPA k ˯'}*;v>5wZN#km.(p))N͍J$dP{? zF]PD81T+E_K cJ9H,ufޮIY}Fi^MĹ*K|Ơˮ6f۴/#_̳Y=?BDLLPF-.h׳wLH7 @r0*#l!/A*2bzE:~2!&N6Kz:c9w 2lqFvT0O#;9;{8khH U $3#T:T ;5UO+Frb8ޑID JQp ^"ݠ!>?.Slbd!sKF~\ dH?:]}PӺ.޾ hߎL>no#~MO1X{9F4hT0#) jfk;Ow<9^\="wK^S}l򙅟cgvxz(?O>wׇvwG0smS}ŽK{Ff!1#:@d;2:W嘤.CN7^CS<;iγ(E`!a9_7!=t+17mjokrG3F_w,x=?So!ȦxY7I6f=*ևA 9vk^~_yN0aO5SL]2Ng?=rfN3 ~dۤH/lܺÙGtVN3vEuI8V_u:Nh9{;v\3>p{=YLJ߃hrLW n,`ox&  I?'Id6HXJ=nB 9bC}UU[{A{ý~0-oTN3qn̜mQm\̅OBC1M̚IkW y#kujr6lovf;haWEFSIF'4렋:j4EOO3ќ)_ryծ"×yR F{.9"!|ǬPfc؂A03!*JJ|Qp0`Iک|*fUu i3y)w7lYݮME4op^[5W枛FzcV$5 k"|A03ҸwS00Lѡb8p$rs +#%v|%v-Fx>d3ȓh/FuYA2+P5v2')ш7eMf,[lEg "-+1 0{wiq+\-s7cՔv#^_3ȓ^ţb 6kV51~`#K,IDg9)s4bِF=߳9^[$xV Y KcY%fuYrBPwe[=W-kUa]7+"HzD P$,+f fluidsynth-2.2.5/test-android/app/src/main/res/values/000077500000000000000000000000001417326347500227055ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/main/res/values/colors.xml000066400000000000000000000005721417326347500247340ustar00rootroot00000000000000 #FFBB86FC #FF6200EE #FF3700B3 #FF03DAC5 #FF018786 #FF000000 #FFFFFFFF fluidsynth-2.2.5/test-android/app/src/main/res/values/strings.xml000066400000000000000000000001161417326347500251160ustar00rootroot00000000000000 fluidsynth-tests fluidsynth-2.2.5/test-android/app/src/main/res/values/themes.xml000066400000000000000000000015051417326347500247150ustar00rootroot00000000000000 fluidsynth-2.2.5/test-android/app/src/test/000077500000000000000000000000001417326347500206505ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/test/java/000077500000000000000000000000001417326347500215715ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/test/java/org/000077500000000000000000000000001417326347500223605ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/test/java/org/fluidsynth/000077500000000000000000000000001417326347500245515ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/test/java/org/fluidsynth/fluidsynth_tests/000077500000000000000000000000001417326347500301645ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/app/src/test/java/org/fluidsynth/fluidsynth_tests/ExampleUnitTest.kt000066400000000000000000000005431417326347500336210ustar00rootroot00000000000000package org.fluidsynth.fluidsynth_tests import org.junit.Test import org.junit.Assert.* /** * Example local unit test, which will execute on the development machine (host). * * See [testing documentation](http://d.android.com/tools/testing). */ class ExampleUnitTest { @Test fun addition_isCorrect() { assertEquals(4, 2 + 2) } }fluidsynth-2.2.5/test-android/build-scripts/000077500000000000000000000000001417326347500211065ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/build-scripts/.gitignore000066400000000000000000000000711417326347500230740ustar00rootroot00000000000000android-build-root/ archives/ build-artifacts/ wget-log fluidsynth-2.2.5/test-android/build-scripts/build-all-archs.sh000077500000000000000000000015571417326347500244200ustar00rootroot00000000000000#!/bin/bash source ./build-env.sh mkdir -p $DEV # build ANDROID_ABI_CMAKE=armeabi-v7a ./extract.sh || exit 1 ARCH='arm' ANDROID_ARCH='armv7a' ANDROID_ABI_CMAKE='armeabi-v7a' ANDROID_TARGET_ABI='eabi' AUTOTOOLS_TARGET="$ARCH-none-linux-android$ANDROID_TARGET_ABI" ./build.sh || exit 1 ANDROID_ABI_CMAKE=arm64-v8a ./extract.sh || exit 1 ARCH='aarch64' ANDROID_ARCH='aarch64' ANDROID_ABI_CMAKE='arm64-v8a' ANDROID_TARGET_ABI='' AUTOTOOLS_TARGET="$ARCH-none-linux-android" ./build.sh || exit 1 ANDROID_ABI_CMAKE=x86 ./extract.sh || exit 1 ARCH='i686' ANDROID_ARCH='i686' ANDROID_ABI_CMAKE='x86' ANDROID_TARGET_ABI='' AUTOTOOLS_TARGET="$ARCH-pc-linux-android" ./build.sh || exit 1 ANDROID_ABI_CMAKE=x86_64 ./extract.sh || exit 1 ARCH='x86_64' ANDROID_ARCH='x86_64' ANDROID_ABI_CMAKE='x86_64' ANDROID_TARGET_ABI='' AUTOTOOLS_TARGET="$ARCH-pc-linux-android" ./build.sh || exit 1 fluidsynth-2.2.5/test-android/build-scripts/build-call-cmake.sh000077500000000000000000000033001417326347500245270ustar00rootroot00000000000000#!/bin/bash source ./build-env.sh if [ -z "$parameters_installCommand" ] ; then parameters_installCommand="make install" fi if [ -z "$parameters_workDir" ] ; then parameters_workDir=$DEV fi source ./build-env.sh #set -ex pushd $parameters_sourceDir mkdir -p build_$ANDROID_ABI_CMAKE pushd build_$ANDROID_ABI_CMAKE # Invoke cmake in the most correctest way I've could find while try and erroring: # # The biggest pain point is that CMake does not seem to respect our existing cross compilation CFLAGS and LDFLAGS. # Hence we are passing them manually, once via Android flags and once for "Required" flags. The latter is necessary # to let cmake correctly probe for any existing header, function, library, etc. # Watch out: Sometimes the flags are passed as ;-limited list! cmake -G "Unix Makefiles" \ -DCMAKE_MAKE_PROGRAM=make \ -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DANDROID_NATIVE_API_LEVEL=$ANDROID_API \ -DANDROID_ABI=$ANDROID_ABI_CMAKE \ -DANDROID_TOOLCHAIN=$CC \ -DANDROID_NDK=$NDK \ -DANDROID_COMPILER_FLAGS="${CFLAGS// /;}" \ -DANDROID_LINKER_FLAGS="${LDFLAGS// /;}" \ -DANDROID_STL="c++_shared" \ -DCMAKE_REQUIRED_FLAGS="${CFLAGS}" \ -DCMAKE_REQUIRED_LINK_OPTIONS="${LDFLAGS// /;}" \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DCMAKE_STAGING_PREFIX=${PREFIX} \ -DBUILD_SHARED_LIBS=1 \ -DLIB_SUFFIX="" \ $parameters_cmakeArgs .. #-DCMAKE_VERBOSE_MAKEFILE=1 \ make -j$((`nproc`+1)) || (popd && popd && exit 1) $parameters_installCommand popd popd fluidsynth-2.2.5/test-android/build-scripts/build-env.sh000077500000000000000000000055451417326347500233430ustar00rootroot00000000000000#!/bin/bash export ICONV_VERSION=1.16 # Use recent master libffi, because 3.3 is broken=checking host system type... Invalid configuration `arm-none-linux-eabi=machine `arm-none-linux not recognized export FFI_VERSION=dd5bd03075149d7cf8441875c1a344e8beb57dde export GETTEXT_VERSION=0.21 #need to switch to meson build system to use a more recent version export GLIB_VERSION=2.58 export GLIB_EXTRAVERSION=3 export OBOE_VERSION=1.5.0 export SNDFILE_VERSION=1.0.31 export INSTPATCH_VERSION=1.1.6 export VORBIS_VERSION=1.3.7 export OGG_VERSION=1.3.4 export OPUS_VERSION=1.3.1 # flac 1.3.3 is completely broken=pkgconfig is incorrectly installed, compilation failure, etc.; use recent master instead export FLAC_VERSION=27c615706cedd252a206dd77e3910dfa395dcc49 export SCRIPTSDIR=$PWD export DEV=$PWD/android-build-root/$ANDROID_ABI_CMAKE export ARCHIVE_DIR=$PWD/archives export DIST=$PWD/build-artifacts # This is a symlink pointing to the real Android NDK # Must be the same as $ANDROID_NDK_HOME see: # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md if [ -z "$NDK" ]; then export NDK=~/Android/Sdk/ndk/21.3.6528147 fi # All the built binaries, libs and their headers will be installed here export PREFIX=$DEV/opt/android # The path of standalone NDK toolchain # Refer to https://developer.android.com/ndk/guides/standalone_toolchain.html export NDK_TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64/ # Dont mix up .pc files from your host and build target export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig # setting PKG_CONFIG_PATH alone does not seem to be enough to avoid mixing up with the host, also set PKG_CONFIG_LIBDIR export PKG_CONFIG_LIBDIR=$PKG_CONFIG_PATH # Set Android target API level # when compiling with clang use at least 28 as this makes sure that android provides the posix_spawn functions, so the compilation of gettext will (should) work out of the box # its probably a bug of gettext, if posix_spawn is not available it replaces it with its own implementation. Autotools of gettext set HAVE_POSIX_SPAWN==0 (which is correct) but for some reason REPLACE_POSIX_SPAWN==0 (which is wrong, as it should be 1). # # NOTE=API 24 is required because it provides fseeko() and ftello() required by libflac export ANDROID_API=24 # Tell configure what flags Android requires. # Turn Wimplicit-function-declaration into errors. Else autotools will be fooled when checking for available functions (that in fact are NOT available) and compilation will fail later on. # Also disable clangs integrated assembler, as the hand written assembly of libffi is not recognized by it, cf. https://crbug.com/801303 export CFLAGS="-fPIE -fPIC -I$PREFIX/include --sysroot=$NDK_TOOLCHAIN/sysroot -I$NDK_TOOLCHAIN/sysroot/usr/include -Werror=implicit-function-declaration" export CXXFLAGS=$CFLAGS export CPPFLAGS=$CXXFLAGS export ARTIFACT_NAME=fluidsynth-android$ANDROID_API fluidsynth-2.2.5/test-android/build-scripts/build.sh000077500000000000000000000321621417326347500225500ustar00rootroot00000000000000#!/bin/bash set -e source ./build-env.sh # set environment variables # The cross-compile toolchain we use export ANDROID_TARGET=$ARCH-linux-android$ANDROID_TARGET_ABI #echo "##vso[task.setvariable variable=ANDROID_TARGET]$ANDROID_TARGET" export ANDROID_TARGET_API=$ANDROID_ARCH-linux-android$ANDROID_TARGET_ABI$ANDROID_API #echo "##vso[task.setvariable variable=ANDROID_TARGET_API]$ANDROID_TARGET_API" # Add the standalone toolchain to the search path. # FIXME: env. path should be at last; it depends on host glib tools to build some tests. export PATH=$PATH:$PREFIX/bin:$PREFIX/lib:$PREFIX/include:$NDK_TOOLCHAIN/bin #echo "##vso[task.setvariable variable=PATH]$PATH" export LIBPATH0=$PREFIX/lib export LIBPATH1=$NDK_TOOLCHAIN/sysroot/usr/lib export LIBPATH2=$NDK_TOOLCHAIN/sysroot/usr/lib/$ARCH-linux-android$ANDROID_TARGET_ABI/$ANDROID_API export LIBPATH3=$NDK_TOOLCHAIN/sysroot/usr/lib/$ARCH-linux-android$ANDROID_TARGET_ABI export LDFLAGS="-pie -Wl,-rpath-link=$LIBPATH1 -L$LIBPATH1 -Wl,-rpath-link=$LIBPATH2 -L$LIBPATH2 -Wl,-rpath-link=$LIBPATH3 -L$LIBPATH3 -Wl,-rpath-link=$LIBPATH0 -L$LIBPATH0" #echo "##vso[task.setvariable variable=LDFLAGS]$LDFLAGS" # Tell configure what tools to use. export AR=$ANDROID_TARGET-ar #echo "##vso[task.setvariable variable=AR]$AR" export AS=$ANDROID_TARGET_API-clang #echo "##vso[task.setvariable variable=AS]$AS" export CC=$ANDROID_TARGET_API-clang #echo "##vso[task.setvariable variable=CC]$CC" export CXX=$ANDROID_TARGET_API-clang++ #echo "##vso[task.setvariable variable=CXX]$CXX" export LD=ld.lld #echo "##vso[task.setvariable variable=LD]$LD" export STRIP=$ANDROID_TARGET-strip #echo "##vso[task.setvariable variable=STRIP]$STRIP" export RANLIB=$ANDROID_TARGET-ranlib #echo "##vso[task.setvariable variable=RANLIB]$RANLIB" # libiconv echo "Building libiconv..." pushd $DEV/libiconv-$ICONV_VERSION ./configure \ --host=$AUTOTOOLS_TARGET \ --prefix=$PREFIX \ --libdir=$LIBPATH0 \ --disable-rpath \ --enable-static \ --disable-shared \ --with-pic \ --disable-maintainer-mode \ --disable-silent-rules \ --disable-gtk-doc \ --disable-introspection \ --disable-nls make -j$((`nproc`+1)) || exit 1 make install || exit 1 popd # libffi echo "Building libffi..." pushd $DEV/libffi-$FFI_VERSION NOCONFIGURE=true autoreconf -v -i # install headers into the conventional ${PREFIX}/include rather than ${PREFIX}/lib/libffi-3.2.1/include. #sed -e '/^includesdir/ s/$(libdir).*$/$(includedir)/' -i include/Makefile.in #sed -e '/^includedir/ s/=.*$/=@includedir@/' -e 's/^Cflags: -I${includedir}/Cflags:/' -i libffi.pc.in LDFLAGS="$LDFLAGS -Wl,-soname,libffi.so" ./configure --host=$AUTOTOOLS_TARGET --prefix=$PREFIX --enable-static --disable-shared --libdir=$LIBPATH0 make -j$((`nproc`+1)) || exit 1 make install || exit 1 popd # gettext echo "Building gettext..." set -ex pushd $DEV/gettext-$GETTEXT_VERSION ./configure \ --host=x86_64-pc-linux \ --target=$AUTOTOOLS_TARGET \ --prefix=$PREFIX \ --libdir=$LIBPATH0 \ --disable-rpath \ --disable-libasprintf \ --disable-java \ --disable-native-java \ --disable-openmp \ --disable-curses \ --enable-static \ --disable-shared \ --with-pic \ --disable-maintainer-mode \ --disable-silent-rules \ --disable-gtk-doc \ --disable-introspection make -j$((`nproc`+1)) make install popd # glib echo "Building glib..." set -ex pushd $DEV/glib-$GLIB_VERSION.$GLIB_EXTRAVERSION cat << EOF > android.cache glib_cv_long_long_format=ll glib_cv_stack_grows=no glib_cv_sane_realloc=yes glib_cv_have_strlcpy=no glib_cv_va_val_copy=yes glib_cv_rtldglobal_broken=no glib_cv_uscore=no glib_cv_monotonic_clock=no ac_cv_func_nonposix_getpwuid_r=no ac_cv_func_posix_getpwuid_r=no ac_cv_func_posix_getgrgid_r=no glib_cv_use_pid_surrogate=yes ac_cv_func_printf_unix98=no ac_cv_func_vsnprintf_c99=yes ac_cv_func_realloc_0_nonnull=yes ac_cv_func_realloc_works=yes EOF # Unfortunately, libffi is not linked against libgobject when compiling for aarch64, leading to the following error: # # /bin/bash ../libtool --tag=CC --mode=link aarch64-linux-android23-clang -Wall -Wstrict-prototypes -Wno-bad-function-cast -Werror=declaration-after-statement -Werror=missing-prototypes -Werror=implicit-function-declaration -Werror=pointer-arith -Werror=init-self -Werror=format=2 -Werror=missing-include-dirs -fPIE -fPIC -I/home/vsts/work/1/s/android-build-root/opt/android/include --sysroot=/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot -I/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/include -Werror=implicit-function-declaration -fno-integrated-as -fno-strict-aliasing -pie -Wl,-rpath-link=-I/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/lib -L/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/lib -L/home/vsts/work/1/s/android-build-root/opt/android/lib -L/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//lib -o gobject-query gobject-query.o ./libgobject-2.0.la ../glib/libglib-2.0.la -lintl -liconv # libtool: link: aarch64-linux-android23-clang -Wall -Wstrict-prototypes -Wno-bad-function-cast -Werror=declaration-after-statement -Werror=missing-prototypes -Werror=implicit-function-declaration -Werror=pointer-arith -Werror=init-self -Werror=format=2 -Werror=missing-include-dirs -fPIE -fPIC -I/home/vsts/work/1/s/android-build-root/opt/android/include --sysroot=/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot -I/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/include -Werror=implicit-function-declaration -fno-integrated-as -fno-strict-aliasing -pie -Wl,-rpath-link=-I/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/lib -o .libs/gobject-query gobject-query.o -L/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/lib -L/home/vsts/work/1/s/android-build-root/opt/android/lib -L/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//lib ./.libs/libgobject-2.0.so ../glib/.libs/libglib-2.0.so /home/vsts/work/1/s/android-build-root/opt/android/lib/libintl.so /home/vsts/work/1/s/android-build-root/opt/android/lib/libiconv.so -pthread -L/home/vsts/work/1/s/android-build-root/opt/android/lib # /usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64//bin/../lib/gcc/aarch64-linux-android/4.9.x/../../../../aarch64-linux-android/bin/ld: warning: libffi.so, needed by ./.libs/libgobject-2.0.so, not found (try using -rpath or -rpath-link) # ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_sint32@LIBFFI_BASE_8.0' # ./.libs/libgobject-2.0.so: undefined reference to `ffi_prep_cif@LIBFFI_BASE_8.0' # # So, just add it to LDFLAGS to make sure it's always linked. # libz.so is also missing... #if [ "$ARCH" == "aarch64" ] ; then #FFILIB=`pkg-config --libs libffi` #echo $FFILIB #export LDFLAGS="$LDFLAGS $FFILIB -lz" #unset FFILIB ; #fi chmod a-x android.cache NOCONFIGURE=true ./autogen.sh ./configure \ --host=$ANDROID_TARGET \ --prefix=$PREFIX \ --libdir=$LIBPATH0 \ --disable-dependency-tracking \ --cache-file=android.cache \ --enable-included-printf \ --with-pcre=no \ --enable-libmount=no \ --enable-xattr=no \ --with-libiconv=gnu \ --disable-static \ --enable-shared \ --with-pic \ --disable-maintainer-mode \ --disable-silent-rules make -j$((`nproc`+1)) || exit 1 make install || exit 1 popd # ogg echo "Building libogg..." parameters_cmakeArgs="-DINSTALL_DOCS=0" parameters_sourceDir=$DEV/libogg-$OGG_VERSION parameters_workDir= parameters_condition= parameters_installCommand= bash ./build-call-cmake.sh ls -la $DEV/libogg-$OGG_VERSION/build_$ANDROID_ABI_CMAKE/CMakeFiles/ || exit 1 # vorbis echo "Building libvorbis..." parameters_cmakeArgs= parameters_sourceDir=$DEV/libvorbis-$VORBIS_VERSION parameters_workDir= parameters_condition= parameters_installCommand= bash ./build-call-cmake.sh ls -la $DEV/libvorbis-$VORBIS_VERSION/build_$ANDROID_ABI_CMAKE/CMakeFiles/ || exit 1 # flac echo "Building libFLAC..." parameters_cmakeArgs="-DCMAKE_C_STANDARD=99 -DCMAKE_C_STANDARD_REQUIRED=1 -DWITH_ASM=0 -DBUILD_CXXLIBS=0 -DBUILD_PROGRAMS=0 -DBUILD_EXAMPLES=0 -DBUILD_DOCS=0 -DINSTALL_MANPAGES=0" parameters_sourceDir=$DEV/flac-$FLAC_VERSION parameters_workDir= parameters_condition= parameters_installCommand= bash ./build-call-cmake.sh ls -la $DEV/flac-$FLAC_VERSION/build_$ANDROID_ABI_CMAKE/CMakeFiles/ || exit 1 # opus echo "Building libopus..." parameters_cmakeArgs="-DBUILD_PROGRAMS=0 -DOPUS_MAY_HAVE_NEON=1 -DCMAKE_C_STANDARD=99 -DCMAKE_C_STANDARD_REQUIRED=1" parameters_sourceDir=$DEV/opus-$OPUS_VERSION parameters_workDir= parameters_condition= parameters_installCommand= bash ./build-call-cmake.sh ls -la $DEV/opus-${OPUS_VERSION}/build_$ANDROID_ABI_CMAKE/CMakeFiles/ || exit 1 # sndfile echo "Building libsndfile..." parameters_cmakeArgs="-DBUILD_PROGRAMS=0 -DBUILD_EXAMPLES=0" parameters_sourceDir=$DEV/libsndfile-$SNDFILE_VERSION parameters_workDir= parameters_condition= parameters_installCommand= bash ./build-call-cmake.sh ls -la $DEV/libsndfile-$SNDFILE_VERSION/build_$ANDROID_ABI_CMAKE/CMakeFiles/ || exit 1 # oboe echo "Building oboe..." parameters_cmakeArgs= parameters_sourceDir=$DEV/oboe-$OBOE_VERSION parameters_workDir= parameters_condition= parameters_installCommand= bash ./build-call-cmake.sh || exit 1 # parameters_installCommand didn't work, ending up to copy liboboe.so into $PREFIX/include. Replacing it with the direct commands here. cp $DEV/oboe-$OBOE_VERSION/build_$ANDROID_ABI_CMAKE/liboboe.so $PREFIX/lib cp -ur $DEV/oboe-$OBOE_VERSION/include/oboe $PREFIX/include set -ex # create a custom pkgconfig file for oboe to allow fluidsynth to find it cat << EOF > $PKG_CONFIG_PATH/oboe-1.0.pc prefix=${PREFIX} exec_prefix=\${prefix} libdir=\${prefix}/lib includedir=\${prefix}/include Name: Oboe Description: Oboe library Version: ${OBOE_VERSION} Libs: -L\${libdir} -loboe -landroid -llog Cflags: -I\${includedir} EOF cat $PKG_CONFIG_PATH/oboe-1.0.pc || exit 1 # instpatch echo "Building libinstpatch..." parameters_cmakeArgs= parameters_sourceDir=$DEV/libinstpatch-$INSTPATCH_VERSION parameters_workDir= parameters_condition= parameters_installCommand= bash ./build-call-cmake.sh || exit 1 # fluidsynth # build echo "Building fluidsynth..." # FIXME: On arm64 it fails to build fluidsynth executable due to a bunch of library resolution failures... # To avoid the entire build failures, we ignore the # exit coode here and go on with fake executable file. # It is not runnable on Android anyways. parameters_cmakeArgs="-Denable-opensles=1 -Denable-floats=1 -Denable-oboe=1 -Denable-dbus=0 -Denable-oss=0" parameters_sourceDir=../.. parameters_workDir= parameters_condition= parameters_installCommand='echo success' bash ./build-call-cmake.sh || echo "Failed to build fluidsynth, but it is expected. We continue build..." && touch ../../build_$ANDROID_ABI_CMAKE/src/fluidsynth # TBD: test (there should be a complete Android project that installs apk, launches on android device, loads native tests there through JNI as a library, and run them, automatically.) # install set -ex pushd ../../build_$ANDROID_ABI_CMAKE make install popd # fluidsynth-assetloader echo "Building fluidsynth-assetloader..." parameters_cmakeArgs= parameters_sourceDir=../../../ parameters_workDir=doc/android/fluidsynth-assetloader parameters_condition= parameters_installCommand= bash ./build-call-cmake.sh # dist mkdir -p $DIST/lib/$ANDROID_ABI_CMAKE echo "Entering $DIST/lib/$ANDROID_ABI_CMAKE ..." pushd $DIST/lib/$ANDROID_ABI_CMAKE cp -LR $PREFIX/lib/* . ls -Rg . rm -rf *.dll *.alias gettext/ libtextstyle.* *.a *.la rm -f *.so.* mkdir -p $DIST/include pushd $DIST/include cp -a $PREFIX/include/fluidsynth* . popd popd echo "dist $ANDROID_ABI_CMAKE done." fluidsynth-2.2.5/test-android/build-scripts/download.sh000077500000000000000000000024541417326347500232610ustar00rootroot00000000000000#!/bin/bash source ./build-env.sh mkdir -p archives wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz wget -O libffi-${FFI_VERSION}.tar.gz https://github.com/libffi/libffi/archive/${FFI_VERSION}.tar.gz wget http://ftp.gnu.org/pub/gnu/gettext/gettext-${GETTEXT_VERSION}.tar.gz wget http://ftp.gnome.org/pub/gnome/sources/glib/${GLIB_VERSION}/glib-${GLIB_VERSION}.${GLIB_EXTRAVERSION}.tar.xz wget -O oboe-${OBOE_VERSION}.tar.gz https://github.com/google/oboe/archive/${OBOE_VERSION}.tar.gz wget https://github.com/libsndfile/libsndfile/releases/download/${SNDFILE_VERSION}/libsndfile-${SNDFILE_VERSION}.tar.bz2 wget -O libinstpatch-${INSTPATCH_VERSION}.tar.gz https://github.com/swami/libinstpatch/archive/refs/tags/v${INSTPATCH_VERSION}.tar.gz wget https://github.com/xiph/vorbis/releases/download/v${VORBIS_VERSION}/libvorbis-${VORBIS_VERSION}.tar.gz wget https://github.com/xiph/ogg/releases/download/v${OGG_VERSION}/libogg-${OGG_VERSION}.tar.gz wget -O flac-${FLAC_VERSION}.tar.gz https://github.com/xiph/flac/archive/${FLAC_VERSION}.tar.gz wget -O opus-${OPUS_VERSION}.tar.gz https://github.com/xiph/opus/archive/refs/tags/v${OPUS_VERSION}.tar.gz mv *.tar.gz *.tar.xz *.tar.bz2 $ARCHIVE_DIR fluidsynth-2.2.5/test-android/build-scripts/extract.sh000077500000000000000000000013651417326347500231240ustar00rootroot00000000000000#!/bin/bash source ./build-env.sh mkdir -p $DEV pushd $DEV tar zxf $ARCHIVE_DIR/libiconv-${ICONV_VERSION}.tar.gz tar zxf $ARCHIVE_DIR/libffi-${FFI_VERSION}.tar.gz tar zxf $ARCHIVE_DIR/gettext-${GETTEXT_VERSION}.tar.gz tar xf $ARCHIVE_DIR/glib-${GLIB_VERSION}.${GLIB_EXTRAVERSION}.tar.xz tar zxf $ARCHIVE_DIR/oboe-${OBOE_VERSION}.tar.gz tar jxf $ARCHIVE_DIR/libsndfile-${SNDFILE_VERSION}.tar.bz2 tar zxf $ARCHIVE_DIR/libinstpatch-${INSTPATCH_VERSION}.tar.gz tar zxf $ARCHIVE_DIR/libvorbis-${VORBIS_VERSION}.tar.gz tar zxf $ARCHIVE_DIR/libogg-${OGG_VERSION}.tar.gz tar xf $ARCHIVE_DIR/flac-${FLAC_VERSION}.tar.gz tar xf $ARCHIVE_DIR/opus-${OPUS_VERSION}.tar.gz popd fluidsynth-2.2.5/test-android/build.gradle000066400000000000000000000012141417326347500205770ustar00rootroot00000000000000// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = "1.5.10" repositories { google() mavenCentral() } dependencies { classpath "com.android.tools.build:gradle:4.2.1" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() mavenCentral() } } task clean(type: Delete) { delete rootProject.buildDir }fluidsynth-2.2.5/test-android/convert-tests.sh000066400000000000000000000022661417326347500215040ustar00rootroot00000000000000#!/bin/sh DISABLED_TESTS=(\ preset_pinning \ utf8_open \ sf3_sfont_loading \ sample_cache \ sfont_loading \ preset_sample_loading \ sfont_unloading \ sample_rate_change \ bug_635 \ ) rm -f test-names.txt mkdir -p app/src/main/cpp/tests/ for f in `grep -lR "int main(void)" ../test/ | sort` ; do export TESTMAINNAME=`echo $f | sed -e "s/\.\.\/test\/test_\(.*\).c$/\1/"` echo $TESTMAINNAME >> test-names.txt export OUTPUTFILE=app/src/main/cpp/tests/test_${TESTMAINNAME}.c sed -e "s/int main(void)/int "$TESTMAINNAME"_main(void)/" $f > $OUTPUTFILE ; done RUN_ALL_TESTS=app/src/main/cpp/run_all_tests.c rm -f $RUN_ALL_TESTS while IFS= read -r line; do echo "int "$line"_main();" >> $RUN_ALL_TESTS ; done < test-names.txt echo "int run_all_fluidsynth_tests() {" >> $RUN_ALL_TESTS echo " int ret = 0; " >> $RUN_ALL_TESTS while IFS= read -r line; do if [[ " ${DISABLED_TESTS[@]} " =~ " ${line} " ]]; then echo " //ret += "$line"_main();" >> $RUN_ALL_TESTS ; else echo " ret += "$line"_main();" >> $RUN_ALL_TESTS ; fi done < test-names.txt echo " return ret;" >> $RUN_ALL_TESTS echo "}" >> $RUN_ALL_TESTS fluidsynth-2.2.5/test-android/gradle.properties000066400000000000000000000021061417326347500216750ustar00rootroot00000000000000# Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=officialfluidsynth-2.2.5/test-android/gradle/000077500000000000000000000000001417326347500175605ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/gradle/wrapper/000077500000000000000000000000001417326347500212405ustar00rootroot00000000000000fluidsynth-2.2.5/test-android/gradle/wrapper/gradle-wrapper.jar000066400000000000000000001520711417326347500246600ustar00rootroot00000000000000PKA META-INF/PKA(M?TMETA-INF/MANIFEST.MFMLK-. K-*ϳR03-IM+I, dZ)%bµrrPKAorg/PKA org/gradle/PKAorg/gradle/wrapper/PKAzZ -org/gradle/wrapper/BootstrapMainStarter.classVYwV˖#DgH !bSJiIh @c5žv2$ڗSz?B;Wopsٿ;#g^O2Bx[aKXpA .X%˒ e\U &Ẅ)XpCƻHXp+|y/wPe#+!ǼC(H8=' f Ni[.יuM]׉JYU_U-kDJb)*$ Y⁥nn2+q4M%U3Ҷj̚/񣀣ѵ]UHۖff]f&4:V< ./EQY@˽Z}4le'uTJj1jN\_IձrvHz|;$󗘽a X,py$9ؘM/yfLq[YikQQ)Ke#,p)OżTGd`3¢r,[Yh.-(8Q3 ^:J=G]ܓ+(`* ,q_l0zFqjq8~XlC`5eOGX@.]O|Ϩ^Yb >Ǩr-? BJC|Mݸ(+o|z^)Vd;04z.bvD`".g3rJD3V+?~0?{g7y}^@O4V%\IZ$S$9T.٬qf ]}UA!  + ԏ{e n]*d0}Nε0mJ-kмYqZNeex45yz7n#4b$wѦ9z'e42l$ X)L#jSCŁv)I@EBM@ mg0}y[vvDc:t`րC܏c*2&H+?'9TOˈ@+WH?TA XR*;C]$ݝ']AL &w#,G\7 xϣ;NZr?a\VmˈãFX&1 cqBJCl"[F$k'UBmk$u : Hd VcO1 bᥩgsSxgL 'x* #_/2a/O0REϝ [&H| qs6!0~tMu4YO?G'N";8IPKAhdf#org/gradle/wrapper/Download$1.class}M 0h5Z+v/ ׆p!.Mˎµqd#c-l̈0b\7pK^\dNPKA&!org/gradle/wrapper/Download.classY `u=OFVH`b- FZ$`l#ia5#r[7C4Mܴq>Nb'NZ_ N-n7m~6iM?i~Zm}}{Ͻ).HກaTgdYټ,W/_/ienxbWyu[ߓ˿/???13y E}Y_Kk[7AknU3rN_3_RͿd|]6)/mG7to؇t|_&*HT8""$4]蚨S.juajx& Q/t]M5ѤhD.uFj"n&]lͺb.nV6MlĭM^J@xs/-fYSzc b9& ]'[?a2v@mCcOT ܙlrJ5?o{Cjq5gSs;e931K:3\6yLrހ奒#;J8TR Yxʤ,/X~fe6Ge0};^t=%l;Lw0܄$dέ oRq.s MNzSijs3ޔ-0.`Kc"͛Ma&|dpʹݬw< 5S9U2϶vMtStiCi )v~ QѧBN4C.IeGwc#x xw*kb)L_gW}51`C)HŞ\mR/3 M511$T E D ^.هM1"kb'Ę&b8)M1!LqJ,)ބMik3wlβ@P*mCO  葓'G*ADg{Cms~t) D}7ZHQ2E+GQdFtEGS6ӃdK'+G|_&Rʩ'Cl~ ϙbRrJ$LaiE>41cYLi,&69q>RmE\9tʋ*M8HbN: 0W/+6UBQ9{\k2$Z/Ltis?ZDori1^H9dϳIWM:z1Ϯd:~4wC"ʹ&щaهrAޕrFnOg 8.SCn84\4[6mLq{{{:ں=Wk _dHJNb^:ielK%(-&ZB XU[2Jo#mi+⽱ZF0K#Ojayɍ R;HziUٝ&}A RYuemi\,gNILIJ IcgX8. EI))JLL6,M\z]oˎPR$0 !l9g? s;%caD`?,_ |+k".]hAik/mQUl){k{}w%HUzRXh^\mn[DU[UTu^E-`[U- VJb-nC؄J]l.YJ:#zusM띊o@꼁,֞\4kx&{_Uߜh樇}:{1!10hGcD02'qO8k~ԟ#k 8/ q|saR75+x~;U/.B!5(gdCxj)$G^'pb/*Q>T GPx?>@,M/\bp#|T8W7a(ٞ@ p#=H p=|,aE] O#*g_,*S3s2BW/Qy=%ΆBCJrO0lO(<٨+XCB1| u#9G IR%̈Ad% rݸ^R?YU"Ta^H?PKAN/ݡ1org/gradle/wrapper/DownloadProgressListener.classu @E+jDEкe$B~҇)̘֢"n~<0y&\8VfsE1a< 2y`+Χ0TxLnQD1Aj,IX(D8gQkY?0&/׌##Ѕ>v~PKA~0^ 3org/gradle/wrapper/ExclusiveFileAccessManager.classV[s~>Y5685D2؂@*$FT sqB҇xVKOIKڤmzJچrӛ"әf&τ\3nMgzQ|;}{WG8:¹< 5Lj8#) _ӑ9yA_R'u5\T% /R=(4U qYV%:`+:ᴣ:Q7tPO@u}WqMu )SoD)H=喽J$m׹Uw45.^A>PoeIKeٷ1#Y"Y lrTg]+̭MTI#=+I2qvŗt\fdjlݖt=^DE'/;JK[֑a4^/5Rp~00Fkho <NY^!OҼWoi557Y6].p0՝zYHj$%=T2/iG I'E%)4Pĸ9~!9ӻlX~glDiNk0K17k^&gj_ nhwpR- Va-a~n7!"^ 3ܓά~ENY^nIgkI-UzZ&H`/c!MVY׈/]ˮvڵM35ϩ l]/u6;7`v]-%#kD͑>H6 9=#\rb Z0͠)nC id 0] :zUW q ! wӔiS;?Dl`LJh{?T>9s܃&<1#5|_~nDRb\_H ,">"wq@GΡ;0N 6O躅Cj:} nn{t/r K2- v:\G/Ug}[qx[{\"h\2l  >{*yR 4v.ebT.ٻ+`9=34HTM| )׹L"hgOE7s-6ep_̴kH="sEVb˔z5UVMmm|G0Z(=ç8}'Ei Q}(8!v]wq&2{ 4zmOgoG#|gmH"aAG5=R+ȃɹws??~XªQx)I)`^F\F ṂzQFRhMK K [A*_ɮoANϖvӟtp854˰ZsM0ݍ+e錞K{zahӱa{jr⿅ >4fڦ?(06 %L7k}8e*)v0 DqZ5*>F]m4xqNuj}g'-mZ0Zjw䜦[b!ڋ3)UD0A\>yIA$Rf MxfFӴ*e]ӫxwԯ x wuH𘗽P`{!!}%nx/ q}Jhͮ0,މ=q@{,Qzii“G7 !8CH3 `_[(`+8$U)<$4OZd4}/z@:CYׅ"D "Vv I (&%꿮)[|SW/9s ,n%BrUv/PKAj 4*org/gradle/wrapper/GradleWrapperMain.classX|?}ll0" A/l؄Ա dlcbHBE$Nfu$It&3I'I[ٍδN+{;˲t?޷^Ï 6؆UlKRLi%\+xUrj=~kNEĪToRf5-*㭒vC}_ ǻ$*= ޫbާb(xP><~|@E>" 5)a?xDc ΪxV|Tq >S*IgWEyz2/LHZR-WGb kmQ-6F^y%1Wk<ǂ<2*5[qU<((#p.<.m {9Dev4vG *Z_x;jsH0de[׽΅ .y:öOνyu/r. ]T9x8S;'e, Wá2JRilSƶh8iūt--إb֩p!Ů8?r/>ЊΜߒsS$7&drM-2yayyIR ³Ϥ @t̰Ʊ;Wh[":xG0zxr]CvYcN/s >gh[Ry.ϴS|l=1Ѹ)/@nE]6}y1xuss(c;6b7f CX 5Cy{y +I!!kqAR7p82D1B,WN㓘i.) OeJx~l`P(BWHpB)K$ 1@k$[Bhj(Yy7O"(7K[OV56Napl ˹VLbBSX5O`.rZźw!u-ݼ] E# ;тt5@lAI\۰w/Vƕ8vww#|6џe@_2 9nDX{a"$}~3ɥRP( ^q8i7_on5uh[낚@3b@'ElZMTQ(U]yUNvMο3՝]_@XxY_s% l(dדǮ6ǎtt{W56f)Y\eCEd [&pYh e^,Gmx~$3κʴO3&+( lA`Sg?V[=Y0xsi9]_]f0K;DYtn|f]:ۙ;"MigS;]=1vK<4i zY}g}%je]cFz* lLL5L&z9L_1w1uw3mČ^ݪ _I ΂ hqk;@Y{A͢_z39E ,e! $w wܷ+0H0}uSJ`IPKAXs"org/gradle/wrapper/IDownload.classE 0  ^b AP^26J;t>;ɗ|{z~+%5O&WΔ(a_4[gR#!XbQVg={}1AYCX'R5c/J$S@pP\mKulPKA6"org/gradle/wrapper/Install$1.classW{W~O2fZp[hF, P mC@Bcu;l&3,jVjTw%mW7ly9||;9y߯` [ C22LɐO"3Nj(&DAd%pJ"eMb$> /H|T8#&g8"yGEc|':I|JO'pAğI\`BO%<>#KIBg5<+ ]g v}*=h٦Š)㌑ܬw+tN*Ö򬩊oθg9lFD-:'b3DO7zl3 =e'yY0cFc0m8" U٪]-zc{.R\v^ozSQSf}er̔59)1ód]Fe PGl\6)kl㾣tBu{[gJՏVm"ogG|p=Q1Ӧ/wLPv:k 2l4[n)g1mx+=,R}ZLnd`;p.o$fYWn:e%Ҳ1~ţ}癎bHcn˛aJZַ {n>d.[t<n_v;q`A|C&TXJ.xAǷmVq{Zwd=u|}~ C ?2.)Ķ cyQ g~_ᲆWtq:aN5Ẏߊ߁^=lڦ/NH*&kx]:~?h?U*.w&xKl~R,'Zk~bdd< w8=+jF6E_^$-`y+$1cҷķï/ݦ,AÄ"{zZkspyǭrs%q*v74cf]x+pkm4ruggI,6z2 Jvm_:ڶ'W-.(C,U 1ǶޮCgl >FFǻ[R4+AG]-ao߰[+J۪M<:ҽϬRrf("S}V`rn Mt(7X7vGoZ3snK)vWfWru<+"l}5VĻ8o<2f0 fGZt@1y*s,:2}dRYD3,bT|UhPp^z0 Hw"N/g|h*Z >wTST*UqgrOEa}h:9=<KŮᝊ=.7ARq%DS ϡg^yU*x7ihQwo9 L3wyqcs\;]!}k9#MN"rd$yT ɢF]aߤ߱a&^J2xX ᠪuU/z 2y1N0'Y4dySTZ=GVaR$CWA(apL2*zc5^(%QJƻB_7ՠ̻8ǃ U4/PKA @$) org/gradle/wrapper/Install.classY |?'ͰB#v4( ! a1 $w%ΝKVVZuwUT4*uZgk}}o{o)fnnnAofs}yoO=KD q@E4y҄Kx'~.D!(c)d"MarQni҄) VU!Z^^Z(5Өy .MA^+!:I>[By<7ISͼE nI[C/Tx{Zo3_q4 !j6Cۂ 3. NyFEuh͗Jc) NnƖ)w)n^Y'^y]!>\+Im .'F)\߰vS-74ڴq]04]ҫzŶx quxf=.W1Nbє:;-=5{,۰A F=hB0X.ݺU cmQx_u<0u%`6FU*Иiصf2;m!ozcSz#nݮӂ:V7SGB{ڨgRreOG_]Ѿ3)YZ2VjTb`CNQyDj7<FmD`ԅTӼᐭ_ymq hZθn,c F0ɴa7TXqadRPMmLf0~FdGܜ#zuŠLG@ 8˨뭄dU)3RžFNceoEΊ8XZn?{JMk1~%b1=(S"axLn1ؗt+L$uY1OR$S~ylbhO!_fB9\#n[}_8vj$GdruY焔)}Qx*fd1dP.J|Ec!:=ݗJL&$Ui WZfbi 3Pc _v U~m˴By'">*?Qi/ݣ߃U)v'kKR/sbz֥26Èk [1S[1m46߳ܞu[ ,4XOxHL#*=5)Z53 X#%nAt6FvZif[-D1-PShPZԤ!;ctmW8 dZ@3#GU>ȏ)Oq?YkYzN*?%oLj3)} `,kiZaaC=\i Fϫ1U;H4PSUރbA!д՚ʯ˴ٟ^1%Rш(z}Г3MNp:"p0أm@I$TU O-eoT~E Y;¿P=A/!KT|̿u#i)9{H[?T7|ʿۑ['ꑐGOCuܗ3R46s ^js$%ouƞRy֎ӎL vN+envgNX9z,2 SqIp7W(8S.2p`~v`^z `r۪Fh趔?paE ɤɹ%̷k/K K=qr@xt9nJűuCrYC)3q> t8i9Čm{<)L%e0.cƥHQTɑ=I_ۖLDS!4rCe?EdCdn[rd]I}yr/#v=J\ "8wDTqrQW%7N-ǒن4S6&ݴ:ǚKV9o'VN(fPQVy8p/ZMJ_IoP±ɹ :9FIeFtt d-7TZ2X䀦D' as6H[ JPi0G0ZT7=\9z$UP@,Yhhw>=T 9i v"9XA zu꧁NµE+~X`^Gʖ@/>FKOKipur^E)0tįopb >xG$<鬂@5)'ț?yt$[ԛxJr`t 3x4W>[[hE5pVAjE8$C[vT fCfu |s+m!t6 'HP9=KG~p+eM1j⽜ܞCcyzM>c ,䊃TZ^r^̓tNkm 8e ;N: N50Vcy9q4OkAjnu2?@$S`E̷&".~ڲyEhv+^ lG3r"#qlQ{`{a<bD} B̷!{W#GLE<(:ޏqXbcp< v=%Xjl?/+งfѫb,z^kh'ހOwy{ӁL?u:V@˳Cd̏@c=%{aZ(}|OE7~Hmc-V@`2! &˶bf'`)Gu\^u._Q= Eɬ'Ϡ؀9RV-8\3;5PKDfv G:XYI4't8pb k "ޫpd Zz8I:GKy=#I'32/ (zvv O No6λ p޴a aQ-sNAsn㩨PKAy0Vorg/gradle/wrapper/Logger.classoPǿ*1pL EWo1&d  f&Ki Xs9* OHfp+ۨ&Q,D];Bܕp A`w 32 I^m{]⯧>wީ}N6Ǫc;8ƄԵ]u`r壣FQs5mP힩TT-]鹎a-PDս +TYb|Tuz|<[(N$&cbXC3RWu:+mf=mdϞ:?2RlDLBZB3{/AQHGB< 2og\+j/>M\> {JQV \ZWHcHXŠ\_)Gt } РF If:B~@R &~ 6H#J2b&J9򼉖=W-ĝPKAbi+&org/gradle/wrapper/PathAssembler.classV_.HZYm,c;`HeL!mM++NB눕WzM+=Һ}֑O{]K&A{7̛p'>ENs2|^m_1$R_/1UeFߌ[1|/G(''_V}?Ppl'Mǵ͵k,Zֲ {;(8uy~lrzjuiaj~lnjuaq>;sYA|~SuY0Dr\rbPpdn~#[%ǰ6E7c2E&"^RО\V(vtڴa/kEC e6el Kv!ܲrٰ3s1ƨ6iѱ~hple)fBEwt-pl}-ʹt ;ƌ.DF4v0l\U`6콃8[d^+AC-<|xp`LqJ^Mw;7kۮTg۵q c<ֺQ8 &66K7-װ屣 %K΂-,gY\"Me]AFǙ!hTHʃ n&+}_ xTqQ+&C 𴆧0!i^佖5w4<Wx, [ÏE')hFs 闸;p+0Y#*~kO.&eBr(⯂hGV؝XiwT)VMl0U 4Fw w񚆪D|Wu67@ 5cƋWa3ɛhx]0]֞db0Dz*"[QH`fnj<ƽJf9#+~[w:()Pni!gn Ca1 XVk{Qp'dlX ෯PrPXR.=d$$ΉdeRxcgq.ݒы:pH!vkLf[BE/:Mm56}^ndgUt_`# I?g}r{l꽜T ʫ%1@BxJ08ݘH_C[?Byv`I$% &0/=oUʥjϤ5D.v_"RCWZG8t8Tq΋zPPdWRH ];pzd(8JFO!MC°t=ۘ"=~^f.x&.S] 5 RxR|2\85iD4%wÖmԱ x8OKm)tlA8n ^^4O2SӴ#㳞 : =W 'KqhJOkalq8Ub4^"/YwI%F&d t<:{`.w$-AGX'^dW{hB4z}9Xeu{d$ւ {̠T_FVPKAdzߒ= 0org/gradle/wrapper/SystemPropertiesHandler.classV[S[U6I8!ʽ4H[[R/H%*$%^!&sIoZ_go8ԗ0Ȍ}GN"dk}{N?o}Ť)0uL+#o`VN"Rms }h¤Q9.H(MT,Ixx+Xk>ƻR'A#CuZlyz"Ls}-D(؆ 4MYfLgEKeu 4k{ڠy0hvk].Yq-نn'id,;JFJmZ:ۡnѷm3IS8(kX# ӬcBZ뼆cږ 1Y"L0#daz'2swH-t9ZXQuE#nk%?>YnMCyE.LV×.91a({7'\mƀ 1#ajNfgs}☾zT<(Z2I b\Q TZF" CnQrėcl%ήSMfԆn \mNfo6=[]rU&IRS&dH# Y/F戠]3お/𥂯T|oOR-X@-mn+6Pyds 9vDx)+#tLbGbT$5\/۳^*x;@"kdPbYNֶ|xXfMًeǠ*$LTaS?zeZ*g?e̪$"OfBX:]w"5Yr%.G5\k p82z ϯ%ZBG>yH?e}GR-0A 0Q\,HCԭ57GxCC!5FFGrh׷u-zPDZ5}?ΡёxT~wg9t?z=~9]g=9V9z Gq9?1q4#E;Yxq䱕 _Yg9Q'-aش> aWI\b>t.awCtGzm2)p+G%qg\k,s0K0Q,Nwڵ|_F^{_PKA ^F-org/gradle/wrapper/WrapperConfiguration.classmOAփ>"!3 U1U H|Cp#]5Si"1e[733\_(K`6 q?<7yBi~yVb޼[_2DO O[Bxko 2;d4q.wXs $.{{yxsx>;Շx0Ga|O>gـϱeċ"ƗWC_g3! c}I7M, eq5oIh9ܛO zG%DNΜL[: aI/Zj:J*CB53uh>zh|=~ zSZDN.FA3-]+JXi^\g'+RGhKF'$Q1-}99eٜyT t=w ZCMIzNjnhvŪsV˔,YOk V'HQsTSgdSL8ZP6X-$Htٓ=g3Z8`Msie ֋H%1u b[ly (%3S Gv ("墩Yi9:3lʓa=O3toP!,P4kYE;UW,!! ɉR/U.2 S *{f 䣃mH mwR(ańm [1C>/9@U)vq[UڜPeϹEjj)wG+1_*QIg$Oې͔φi"U"y[QS[_]2䂷F%jm!V4My/0e\\qxͭ0 IǦh<:ID7o,nf "Rl` NpC ; S۹QVZQ*xŘ').) ~Ě$R5, 3JoN4'6FIOYLyT/"4[Ϙ* fM>}mrY(nN3>15ӭ_)g_oW%lU̱|qz`c!Oc=PG)?+ 'Ec!UCFT$|nMGf/>k٨ufP^[YO|ܮcK5<1UT1_UME?Zο[e5nx[LN.z c;߹ONQh tfZ"[vs?*_bͮxη k ZŵhD22܋af&g( [#F$~_/{Xɮ%|nq1i@ D:l#^Et^Ctz==z#"ܠi-f4/!}'QO+eFEe-М܄} rBHy4FKX<( %XYOG#'ѕt?. hg.^F#-%eWwvYBHhBem` Cߢ/42YPa9{s?p# 5ϕBy9yEx\Ye ZpՆaEjw I7|ZU c:tiw]HaٲzQdu;BrQfظ&DV\%6# ǹƙun]s̷X3ػe9H%ҲIk.=,ȍ-1ak^1蔕)9(id0^}y_7бp:%`i6t*㰊jBL F vUWm_ImuוD I8H7Լ&%DDՄ3qONB :ACvsπb0\8y 3CEw&T*kY+$@B!K͡5IYF6VANwh`+&XEKH,έQFV# J#hRq +dAy ~#TN*)oްOOSxΑ 86'??'k<ų@zV`PKAy0L ;org/gradle/cli/AbstractPropertiesCommandLineConverter.classV[WUN2a`rԄ IєR Mik$d&L0 ]w_ۗQ\KWuIH&Ҕ%Ys}g?!>񉄋H厄Uܕ{K3 #;ul c[|b_J!7ØWˎ"1 4]W6jTѬjA5( qYTSt)p#kDTJ5QhtMhoFM5mMVjUK쪡S5 eDhfk0fC%QQr"g^&I]VZb*ZPr$iж"\<50jy6ZΒn"#iLdC+LF*ur52TѶRkZ )+>m2a*CQl#6zDE U4vryw0oQTE<洲nux|6zNjnԢzY ^:^79n{QN^dIvew' 9ؓQƾMȨBaFw@M lu{ ]ʈbعJwHrv2̼j<^.T߆ڰCzs.k%ꕊ uRj9M:d|",}@\xQ ک7X9|ue0 ޷. |4lk;ɼIi ߮?+&Dw^z BuN2#iZk"pOag$7J(Bڌ"cܯ!16Cfkt[¤q0~xȱc3B( p1YITةQcBW~ͿDcD y f@]tӻLA^%6uV18(c]3 2gy=ݴoa̝̩ kqv>PKA'H g)org/gradle/cli/CommandLineConverter.classQMK@}ԯ'"4 FM)HQ ޷lI7ݔ6MBya=t$S l)8A {Oyb :˄3I5' JXdT"qx{a/4OR1=Q615 ڹ6ƇEWbRh{'qj]4 {wǪSC- 表V%:m7rG4gĔpWBWc1d||ՠ'2œl!_9| z]VpgnNn|q_KoEJTkZv0F٘7uV<'hd 2ƵWpy_ABNgD&x9tN;P=cW٭LXw¼Q9q=@!_!g}@N4 d $];s):XIf(}M ia%`cv'zhf#Y Y eI:Y^^u/s:Dd )&x\"9~I~Xkc-d^V$N^jcMW9,X$?/|W S 9ݢ&y vԭLaS9|ᶟTyW.8qX[7xJqqt-r|q9Dw !6noDMTݍME/7{Ƨh4\p:~\$ƭ|^6'_Di^Z,jמ 9Zo:PKA2_e(org/gradle/cli/CommandLineParser$1.classA 0EhZ v庈kCPEv-iIp.<S\p>?fxCDlnmMJ]k'iu#0BWՔ!f,By@wZ͕t!BI]#HI9|g|{ -|PKA:< ;org/gradle/cli/CommandLineParser$AfterFirstSubCommand.classVNQζ-P*+^D"ZP.RZH-e el| *񟉏C,̜9ߙ=? e iKh,lȇa1">׌I1.⁈ >LJHc)FV zӆY ^T|Q-E_Ok%Lk$lx.96U6.lseHW+}Y2t^Y9%+VdjNRI%QWC'P_RS C,Ŵۖf"޹aOo(o9g^HVK=&{x 8q$23v̼: V сK~.31<1eaՑn'@i*?:wljS)bu֔S3M[V/YsU=197vS=i {,LjLR)4*1ܱlqL@\P-!]PJ%UB{wxv36B]^_<+yyEς˷{|NC|D@{]rE#H q9L$cA9/T#FAw4fb?qq5B0GUnwj$J"PٲwJdhgt,Blv'\tN KnpqGw pY ߘa6S. 8fsa;jE1qe^\$IWBQDO}HK`+`h+PKAxڤ)3org/gradle/cli/CommandLineParser$AfterOptions.classmOP( @ 1|Dd^\:ݭGBD?xnWGe[ 4=s{m@KTLjHc$]Ŵ$f5SQT1ϐ:Ւ-,ђ̚ǫeV|ʽU]9YbHݱew*3$W*sҰ[߶g|!`ɭp=[1 콗=yۮF"<93Hu~Z7 CW-7.j٢4Ő^M&6B5܌J>հՁ!V|mlnkSM ;/Iu0Š9*2g z-[cgVEYE/4 ZD6%cTq+Ef9Q2LdOtwaDW!ΏC^ mM-.`^>=^ή#PFi=ERgd#i @э/H쓪Hardn#S@ I,4BKI|lRE YiEG; aϧ#J,4][\/r^k$~H AّTYkѐw|dnp#aIJmRx`900@^gi!*ǵ!\!&>1u68 Fta"oZTRkZRSkfynVCr^ ݾp}&1owr3 Қ1fYwr,YQky( UiitaOahZMm44-ytD' T=> ðimղ Ol48`qn,ZtF5U-ݨ-3Ի|jUt]ۙDrM ~tHUh8,\L$Yt=MZ4 ~1EiU(MDvaf NaL(Ðϸ^KR@ :izoiAF0اTyFiM&T)5*ɒJu^(%n(^Ua,F~D4As?J, |xbhÇkllvmq gDՖl \.RjX y2]x.³x%/KEx%¢JxMkki&Q6вMU5Xj Էf>n*ZMhFO5]ݨj_%PZ" qELjYDq$R`يJCҮ L8~vOI$ە>>ikvͲioF;iI۫uB޹YݭYe ?stn8_}Qȍ|nff_#.cHgjnR%l7Jbbj>FK`z]&DsenCo1UTfjWf1iGL 4TNƬjS1+vC Q)pRV O+;./lTl3ȰUsWr2#2"ehࡑ&򰌇k X4- :f(c5n[sBbgxkRF5e4QFX2!P}aRv z_m&h?^աr"3T+vVRCJe|ݏcav5Jޠ8(E=X_BѧXA E/`N!qDFeEw!]9!,:wdֈq)x$| CJ8!c|"SFsG2akTiV_5) KBI[>2bؿƐaPb1+.:hbuOV;kBf:5Nm7wu Tμn_ )`= ]+ :KPnV5 "+$j A-џnO%Zuxp)u `dשXDe5.k[uʶ=: F}Y}2:z$b,m'\25rb{u+2ߝ AM]%׼.O}e̤RVt+?Ma̭L*Y,6ɗ[M#ٻE$=d3'HT{q/ڛ?aqU 7:9_jwΗ:nHiUB<s!(@Mzya1FP0ppRfK8hlq$z@@G8Q.ZY֏V|gƾ{_@݂[>J$<XxrWp[ 4.AØfnӼzs=>F@I#?q '`7۰q yO@0JQF1O`3`~{ɉgu!cNvy]<#MQP4\#2򑔓b @b.jYQXZ]]Aa\LK|Q*We(5͖8R];)0]⫈CZy?iòNqsķ|?P's@<_qVsw"pj&Rmcb#n'g ;=A;fNl(/B&8R_iĩIQX]J]T™uy"n@dl D=N^PPHE4Zp-}Lw~̧N_2xPKA9K<org/gradle/cli/CommandLineParser$MissingOptionArgState.classmOPw(Ld !·'`0W̚o 7i|!~?ܶ &9c?Pª:($IQM)̪☏x#X`~/tMqd0 aʊ]tlm[ŊhpQZ AV(ʪ%,C9 85gH6Lot2Ru0潵(Z zXR,zR)e&EV=^=In+M(u߱ @lvAt#q*1r3@m&Dj67} O_ȇ9u+1LąA_wvR2vfVh@eXp2& b{0s3̟ 0}NXwlS˞zat Sx ^, J#j$i 0iOH3g ?'5B (y`$X`s$u!t9jBo-{mp}mIyF)  @a ' [$W0 &r~n1ق&[9fp-D}=DQOW 쫏3Ma*<$hа#4*louJiIh8%1+aVU'tPKA=org/gradle/cli/CommandLineParser$OptionAwareParserState.classUnP=7qqI(mXRҔ&6.,RT"F˧D%>BMMH g9;s?} $1AGN>MisM4 0ThP1b^C *UuNl9&Cf)x6u*>30k 1{pB!c*6ٔ [5S<5"#e*"e<|[@sbgn%`&S3!a,_/yNX4 O\4e}Iof hZme祂:NcI0N鸁q:nbEŪ53,*oseq!uzwF0*WHS$ h&{2!d> b#1ReIXcPjȞ:OR#B00/Q1c3]Z{xb]..c"PU2>!JTCdW"suWz2C!unjd`sRWFpS9)'rVYpE_f [$dPKAJ7org/gradle/cli/CommandLineParser$OptionComparator.classUmOP~Q6|Aq 2@h,`2C2yIג# _FgQ]lRLsys=_BA3 ţ2HWȶ cQƒ',cEX&CgY67yuA*9&@vgo=4CfX>TJ;1dv}%qh mn2<$bqZ5-SY\#lðE׆n2&Z; *AG#2q:-0p_6Gfv].z`#T͸w`|Se3,KQmg][un%)zt-6Z:Mfcu  21\A-GhAdl4{dxfL>?2;cOD RG^T.P`AF9z,uuu̫mcP5}ʳ, -r 4T|jd^@Ԩe[n%nvt>E# 1 ӳG(7 MkH- ?Y eH|B9՚tƥ('f?0~.aT!V&˸C| ):=ew?n,ċ9fGL2 IVia3.2M!]%yu7hQ ,$MON PKA#t8org/gradle/cli/CommandLineParser$OptionParserState.classR]KA=YvMMw)F)n_$"h@ <6IfVf'%?O>w6 ivΝ=w _|dŦmTҜ1d{mC);BNDfQkikɄaXq2ЯܵRB7"$(XAy/A7A#>;:|(F ͹=U&KcEZP|yq- S+·\U|HQS$dú݄31TMZ@wťK) sAChU0^]<. uʓGa(M"%z.업cg"W=d1fIٔd}F6,O).a9Ţ,se,~Bٿŋ#H9 0a1 Z|ea%wA~刡vTWabH:arYXb;_Ů@S,븉 Vq*1/E_mkǮE SgFo mgGÈQމF\nrj^v;Б5Ts!G?.CU8ˊ)Ӛ!zsmRuX2#M+&~Bf6)K+qy% }\[HPKAG;~U=org/gradle/cli/CommandLineParser$OptionStringComparator.classTOAfvۅP-"Vh)BJIFL0$zʤ Yvɛ{&ƳͶR1{7o뷏jc6lX(Xab^/-,mE eT` fߗ #u筣={.yF~C"P:@ HN(YE "?`HGmdiҞ|~pF;6rɣj Z[hr/}ǯz`v¡GX;=A@'Bڞ8̏c"Ob^JOuE#%㱐C]!ͫN X|{lc쇒^O Xrn~tjYVB9faPJkOSvq;3̡P^}3ʩrhIV_ u6>?#T'` 5z31-P:j췚GBL{ݿϽALx ۋV,0L_"W`n 7V i'9(^LX&3A)*LAڜehf90k F$p؄5ߛ̶Q hȇ,I[3,=Nq&2|RÊisF wYn PKA]4n?org/gradle/cli/CommandLineParser$UnknownOptionParserState.classURA==$a2@(""UZ`$SdL__\H*~+7nܸ_= B%q}q[A :0A0iDq5k rCw˺& ezabq [vJ%nWL[F ! ɕgY6rkk X>O 6E=}9A{gN)Ҧ>曖.^]S`{jHO~噎-pe}^^rJI=R7٠cSޒ{2iOcinی1^#r޲t1b7?4yvo=)L^)aFZ֜[c9*z'̼n$T BAa F&00 mGd6a4j3 C'/n{._VƵձ+1DX UR%H}4+.L_c=:"L32}F7>L`g%.dJ\)+QLgq.:K^dI׵{""¾j4e8 ț#u`@Ts䥓?|pP{fr mEhQr!"XOٯUVH/.\ 鋘6?9R?SͩUÕ@FcH:U1-NcjXc4Uy<blfq=PKA\oY(&org/gradle/cli/CommandLineParser.classYy`Tչ}Nn.! DH${%B 5J"$70g&B\^ںj5!Ҿ֭>[^u-ť]lO;3I.&s9oL^虣˓؃mxGyxWotw:{ ':ב?k||<>RcZ.M:f/ ~G@Q= yjP)j%? Sd&:J.OQwRQf,1[=J Jq曓'sePbur&u,hRc^*2MuBGکJMt@yWV, NYKt9UNSae,Wzu䊀Tz jF3t) 3Y(d&^ըcfgl&]Αsu i&lelyjU-HĊ ؛E#X(ҽ.׌hL֙q)"P"t5HPpgm>?7q67kM Km<] &(j [gMhdɸ<dn30MLh*ӔC]ԗ@s7Wd5%Zn6c"k&6=ߛJhJ T4 "Ih唔Ǻ-h# k&kb 7lE8L[_UIH' /'"CV f轴,q~fie;UE0YX:#b"rVZ1Ej C Գ'l\bxϊ$b1^V\ѾJ@W Yb쳁;[ęeg)M|azgy@`]ӷdA![f{W>h,]4:숻 X*șH}(ܳ7P=[եb2{Aku\gGPqHmNҨO\R͎+/Yhlb(:Qepk/R"+dszk/Iqj7&S/6BgnC._OdZ4i7C:yXA)nMz ~4hא3$"Q+ՆĄiY=ᡙm:UWpծJCbC.~C.ˈekIU5\.W(Or\Elնq {Z fjzX,3j wu\kur!VA.33r!7U|r|N[ UIqIm \`R%w%fY `$mq!wȝ);ku&39Ico5%wg 5"? 9xH1Kr&|ِưڐUyK㥑sАHԐURjGkLxqb0Uk3AO{VmS71qL/^X_Xevw[:k_Wn~ȐU5ԯ3#h,f䍤V:xŒ%= <"jMU;&)8 wD# 3=fB=XS,Tg5N5!)EN*۹򝽗5)Y]<.OЩ{3&$>|BǻZO&/ނ%2(>Υ}t~<&/$w}ǧw)1Btq.1A*N ;_J]V~*xNܯ;c;ėi`J] ~Yei ǯ9{xu a`_fuvu%Rת2n_gcR"A8>Q͍Ǔ9WTړNQiavkÔ.M^<"uAҤ&&wtDru '=;/U~Ӿ]e@"Ėm%ؗ;=MRxž;c)D#ؔ)3~n;{nvn s 3AU-:YR_3{ᏁN7iY5C[X(W/s$kl6w|*!M[:\0zUL `='*{4|]8M~&ɋOkvR#C_I<4k쨠uZV0pܜ75C71ӊbVgZվ~Lѻrn|֞?^j,nq|BޚokCV6y漃|RK2LN% Us8VT6ciA~0!J PC_^Σ)ṣrFU T7_]nQrYa,U|Sv"H=-\8PT7?҉RDE--*O;@1h ۰mY;w;v)\n|x8NKfK[##c",;O *+p܄<<_~yʫ+wV寸A>99,O(^~܇L4jf8šx||_.q!aRv{7ssrIQ7@hgu|! O(vB%n@-pUq;E"jOG\cz(,i dۇXU,kVA РU){Χ)E|YmޤDL OL,xsYAn9w SbKdFXYawHceKʛLw#|Ot|y9 /xQ%d?^[<KxU^!l.'pWl.~ߣ|ߎ9(sͱ Ϗz8ن ~̵aBc7aEk)^SI* ^3s9~D Z\ut[sK}6Hp /J!QEޑ,噮AtJ􌥹KqumBL[.e*oT~^%oa&7oP.szg_X=}zlhQU6p~ Rڽb絗nIڏhoa|UMoױ1}Ҍ?Yrs6zLd+G0{p$`ߓۀRygcan*lXNjyqs\1%rsԫ2s}+8~xǫ9^#?M#^?PKA[xn&org/gradle/cli/ParsedCommandLine.classWWFDUǎ&U8e9$x&M)qIpZ k"Ogȉ[JKЅ}34 &?7oW;3Kָ1/39;|g??0O 3Qq)ģ"Nݩ&I\x<#YKF\e'Nj2/%Ўd|9nq|E_~Uh&N~5X~]|Cq@ěxK|K<-ofMrdyֶtȝ:XZ2 ծZ_GeI5.ȔiZ(i%}qժh sqQ5 Sr,9UfVfSƝ-KZJiWlKEsgJ6nI)5K=_FHLMxf5 MBzʜWKgTKko&BM !}:ciRvt*݌x&H;b/&|f $t4Ұ\Qq8@ecKgEY+l_PKUƍ¤ t2VeWvjKj:p HWVZ9٠+tt˺,Z=6kћիn 7k'u;aJڼ÷8d%gG\M Dբz5膹UOd״IBA<&\K:@CfiĬYch&i6 ]'ZY`>p+o(8Y:nY XGQWwe|W}?%d735V?6FeTs q83x[S/W&SwE>F)L+5VeF'1Gy݌`RͫW5OޞJo0LA E.ȸ:V61'!unQúwTK7rXr̐[1E0w$DϞCόm  kV%8py'ЃZT1жO~0͢˚QX|l%>ow l (t((TyN,x0^*QpKWj'DG \! ׇ6!$>!À40d)w[@bbu1NbkP$LװMUWlV Gn"{$ G^FHbp,>"]vqvοVw΄1(|v3A?w2{10]/~ ^&IC9$Egd$}uvNrg5&<gSF2#SӤXLBi ,A|;"AmȜ2BXiL8 G:m$x0$aOIkZPKAWgradle-cli-classpath.properties+(JM.)**+MPKAbuild-receipt.properties+K-*+MJ-53PK>PKA META-INF/PKA(M?T)META-INF/MANIFEST.MFPKAorg/PKA org/gradle/PKAorg/gradle/wrapper/PKAzZ -org/gradle/wrapper/BootstrapMainStarter.classPKAhdf#Vorg/gradle/wrapper/Download$1.classPKAo:s@4:org/gradle/wrapper/Download$ProxyAuthenticator.classPKA&! org/gradle/wrapper/Download.classPKAN/ݡ1org/gradle/wrapper/DownloadProgressListener.classPKA~0^ 3org/gradle/wrapper/ExclusiveFileAccessManager.classPKAz\Q-org/gradle/wrapper/GradleUserHomeLookup.classPKAj 4*/"org/gradle/wrapper/GradleWrapperMain.classPKAXs"-org/gradle/wrapper/IDownload.classPKA6".org/gradle/wrapper/Install$1.classPKA @$) 5org/gradle/wrapper/Install.classPKAy0V}Horg/gradle/wrapper/Logger.classPKAj jV8Korg/gradle/wrapper/PathAssembler$LocalDistribution.classPKAbi+&Morg/gradle/wrapper/PathAssembler.classPKAdzߒ= 0FTorg/gradle/wrapper/SystemPropertiesHandler.classPKA ^F-&Yorg/gradle/wrapper/WrapperConfiguration.classPKA(\org/gradle/wrapper/WrapperExecutor.classPKA_#egradle-wrapper-classpath.propertiesPKAneorg/gradle/cli/PKA<S1eorg/gradle/cli/AbstractCommandLineConverter.classPKAy0L ;(horg/gradle/cli/AbstractPropertiesCommandLineConverter.classPKA# GK1lorg/gradle/cli/CommandLineArgumentException.classPKA'H g)cnorg/gradle/cli/CommandLineConverter.classPKA7&oorg/gradle/cli/CommandLineOption.classPKA2_e(uorg/gradle/cli/CommandLineParser$1.classPKA:< ;vorg/gradle/cli/CommandLineParser$AfterFirstSubCommand.classPKAxڤ)3Kzorg/gradle/cli/CommandLineParser$AfterOptions.classPKAS6| <@}org/gradle/cli/CommandLineParser$BeforeFirstSubCommand.classPKA =Forg/gradle/cli/CommandLineParser$CaseInsensitiveStringComparator.classPKAo\9=%org/gradle/cli/CommandLineParser$KnownOptionParserState.classPKA9K<_org/gradle/cli/CommandLineParser$MissingOptionArgState.classPKA=]org/gradle/cli/CommandLineParser$OptionAwareParserState.classPKAJ7Qorg/gradle/cli/CommandLineParser$OptionComparator.classPKA#t8gorg/gradle/cli/CommandLineParser$OptionParserState.classPKA tB3dorg/gradle/cli/CommandLineParser$OptionString.classPKAG;~U=Iorg/gradle/cli/CommandLineParser$OptionStringComparator.classPKAYM2"org/gradle/cli/CommandLineParser$ParserState.classPKA]4n?yorg/gradle/cli/CommandLineParser$UnknownOptionParserState.classPKA\oY(&org/gradle/cli/CommandLineParser.classPKA[xn&org/gradle/cli/ParsedCommandLine.classPKA ,morg/gradle/cli/ParsedCommandLineOption.classPKAA5l| :sorg/gradle/cli/ProjectPropertiesCommandLineConverter.classPKA;|9Gorg/gradle/cli/SystemPropertiesCommandLineConverter.classPKAWgradle-cli-classpath.propertiesPKA>kbuild-receipt.propertiesPK22_fluidsynth-2.2.5/test-android/gradle/wrapper/gradle-wrapper.properties000066400000000000000000000003501417326347500262700ustar00rootroot00000000000000#Thu May 27 19:17:18 JST 2021 distributionBase=GRADLE_USER_HOME distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME fluidsynth-2.2.5/test-android/gradlew000077500000000000000000000122601417326347500176760ustar00rootroot00000000000000#!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" fluidsynth-2.2.5/test-android/gradlew.bat000066400000000000000000000043241417326347500204420ustar00rootroot00000000000000@if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega fluidsynth-2.2.5/test-android/settings.gradle000066400000000000000000000000651417326347500213430ustar00rootroot00000000000000rootProject.name = "fluidsynth-tests" include ':app' fluidsynth-2.2.5/test/000077500000000000000000000000001417326347500147045ustar00rootroot00000000000000fluidsynth-2.2.5/test/CMakeLists.txt000066400000000000000000000023671417326347500174540ustar00rootroot00000000000000 ENABLE_TESTING() include ( FluidUnitTest ) # first define the test target, used by the macros below add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -C $ --output-on-failure) ## add unit tests here ## ADD_FLUID_TEST(test_sample_cache) ADD_FLUID_TEST(test_sfont_loading) ADD_FLUID_TEST(test_sample_rate_change) ADD_FLUID_TEST(test_preset_sample_loading) ADD_FLUID_TEST(test_preset_pinning) ADD_FLUID_TEST(test_bug_635) ADD_FLUID_TEST(test_settings_unregister_callback) ADD_FLUID_TEST(test_pointer_alignment) ADD_FLUID_TEST(test_seqbind_unregister) ADD_FLUID_TEST(test_synth_chorus_reverb) ADD_FLUID_TEST(test_snprintf) ADD_FLUID_TEST(test_synth_process) ADD_FLUID_TEST(test_ct2hz) ADD_FLUID_TEST(test_sample_validate) ADD_FLUID_TEST(test_sfont_unloading) ADD_FLUID_TEST(test_sfont_zone) ADD_FLUID_TEST(test_seq_event_queue_sort) ADD_FLUID_TEST(test_seq_scale) ADD_FLUID_TEST(test_seq_evt_order) ADD_FLUID_TEST(test_seq_event_queue_remove) ADD_FLUID_TEST(test_jack_obtaining_synth) ADD_FLUID_TEST(test_utf8_open) ADD_FLUID_TEST_UTIL(dump_sfont) ADD_FLUID_SF_DUMP_TEST(VintageDreamsWaves-v2.sf2) if ( LIBSNDFILE_HASVORBIS ) ADD_FLUID_TEST(test_sf3_sfont_loading) ADD_FLUID_SF_DUMP_TEST(VintageDreamsWaves-v2.sf3) endif ( LIBSNDFILE_HASVORBIS ) fluidsynth-2.2.5/test/README.md000066400000000000000000000020561417326347500161660ustar00rootroot00000000000000 This directory contains small executables to verify fluidsynths correct behaviour, i.e. unit tests. #### Do *not* blindly use the tests as template for your application! Although some tests might serve as educational demonstration of how to use certain parts of fluidsynth, they are **not** intended to do so! It is most likely that those tests will consist of many hacky parts that are necessary to test fluidsynth (e.g. including fluidsynth's private headers to access internal data types and functions). For user applications this programming style is strongly discouraged! Keep referring to the documentation and code examples listed in the [API documentation](https://www.fluidsynth.org/api/). #### Developers To add a unit test just duplicate an existing one, give it a unique name and update the CMakeLists.txt by * adding a call to `ADD_FLUID_TEST()` and * a dependency to the custom `check` target. Execute the tests via `make check`. Unit tests should use the `VintageDreamsWaves-v2.sf2` as test soundfont. Use the `TEST_SOUNDFONT` macro to access it. fluidsynth-2.2.5/test/dump_sfont.c000066400000000000000000000227261417326347500172370ustar00rootroot00000000000000#include "test.h" #include "fluidsynth.h" #include "fluid_sfont.h" #include "fluid_defsfont.h" #include "fluid_sys.h" static void dump_sample(fluid_sample_t *sample); static void dump_gens(const fluid_gen_t gen[]); static void dump_mod(const fluid_mod_t *mod); static void dump_preset_zone(fluid_preset_zone_t *zone); static void dump_preset(fluid_preset_t *preset); static void dump_inst_zone(fluid_inst_zone_t *zone); static void dump_inst(fluid_inst_t *inst); static void dump_defsfont(fluid_defsfont_t *defsfont); static int inst_compare_func(const void *a, const void *b); static fluid_list_t *collect_preset_insts(fluid_preset_t *preset, fluid_list_t *inst_list); #define FMT_BUFSIZE (4096) static int indent_level = 0; static FILE *output = NULL; static void fmt(const char *format, ...); static void indent(void); static void outdent(void); int main(int argc, char **argv) { int ret = FLUID_FAILED; int id; fluid_sfont_t *sfont; fluid_defsfont_t *defsfont; fluid_settings_t *settings; fluid_synth_t *synth; const char *adrivers[1] = { NULL }; if (argc < 2) { fprintf(stderr, "Usage:\n"); fprintf(stderr, " dump_sfont [output_file]\n"); return FLUID_FAILED; } fluid_audio_driver_register(adrivers); settings = new_fluid_settings(); if (settings == NULL) { return FLUID_FAILED; } synth = new_fluid_synth(settings); if (synth == NULL) { goto EXIT; } id = fluid_synth_sfload(synth, argv[1], 1); if (id < 0) { goto EXIT; } sfont = fluid_synth_get_sfont_by_id(synth, id); if (sfont == NULL) { goto EXIT; } if (sfont->free != &fluid_defsfont_sfont_delete) { fprintf(stderr, "This tool only supports SoundFonts loaded by the default loader\n"); goto EXIT; } defsfont = (fluid_defsfont_t *)fluid_sfont_get_data(sfont); if (defsfont == NULL) { goto EXIT; } if (argc < 3) { output = stdout; } else { output = fopen(argv[2], "w"); if (output == NULL) { fprintf(stderr, "Unable to open output file %s", argv[2]); goto EXIT; } } dump_defsfont(defsfont); ret = FLUID_OK; EXIT: if (output && output != stdout) { fclose(output); } delete_fluid_synth(synth); delete_fluid_settings(settings); return ret; } static void dump_sample(fluid_sample_t *sample) { fmt("name: %s", sample->name); fmt("source_start: %u", sample->source_start); fmt("source_end: %u", sample->source_end); fmt("source_loopstart: %u", sample->source_loopstart); fmt("source_loopend: %u", sample->source_loopend); fmt("start: %u", sample->start); fmt("end: %u", sample->end); fmt("loopstart: %u", sample->loopstart); fmt("loopend: %u", sample->loopend); fmt("samplerate: %u", sample->samplerate); fmt("origpitch: %u", sample->origpitch); fmt("pitchadj: %u", sample->pitchadj); fmt("sampletype: %u", sample->sampletype); } static void dump_gens(const fluid_gen_t gen[]) { int i; /* only dump generators if at least one is set */ for (i = 0; i < GEN_LAST; i++) { if (gen[i].flags) { break; } } if (i == GEN_LAST) { return; } fmt("generators:"); indent(); for (i = 0; i < GEN_LAST; i++) { if (gen[i].flags) { fmt("%s: %.2f", fluid_gen_name(i), gen[i].val); } } outdent(); } static void dump_mod(const fluid_mod_t *mod) { fmt("dest: %s", fluid_gen_name(mod->dest)); fmt("src1: %u", mod->src1); fmt("flags1: %u", mod->flags1); fmt("src2: %u", mod->src2); fmt("flags2: %u", mod->flags2); fmt("amount: %.2f", mod->amount); } static void dump_preset_zone(fluid_preset_zone_t *zone) { int i; fluid_mod_t *mod; fmt("name: %s", zone->name); if (zone->inst) { fmt("instrument: %s (index %d)", zone->inst->name, zone->inst->source_idx); } fmt("key_range: %d - %d", zone->range.keylo, zone->range.keyhi); fmt("vel_range: %d - %d", zone->range.vello, zone->range.velhi); dump_gens(zone->gen); if (zone->mod) { fmt("modulators:"); for (i = 0, mod = zone->mod; mod; mod = mod->next, i++) { fmt("- modulator: %d", i); indent(); dump_mod(mod); outdent(); } } } static void dump_preset(fluid_preset_t *preset) { int i; fluid_preset_zone_t *zone; fluid_defpreset_t *defpreset = fluid_preset_get_data(preset); if (defpreset == NULL) { return; } fmt("name: %s", defpreset->name); fmt("bank: %u", defpreset->bank); fmt("num: %u", defpreset->num); if (defpreset->global_zone) { fmt("global_zone:"); indent(); dump_preset_zone(defpreset->global_zone); outdent(); } fmt("zones:"); for (i = 0, zone = defpreset->zone; zone; zone = fluid_preset_zone_next(zone), i++) { fmt("- zone: %d", i); if (zone == NULL) { continue; } indent(); dump_preset_zone(zone); outdent(); } } static void dump_inst_zone(fluid_inst_zone_t *zone) { int i; fluid_mod_t *mod; fmt("name: %s", zone->name); if (zone->sample) { fmt("sample: %s", zone->sample->name); } fmt("key_range: %d - %d", zone->range.keylo, zone->range.keyhi); fmt("vel_range: %d - %d", zone->range.vello, zone->range.velhi); dump_gens(zone->gen); if (zone->mod) { fmt("modulators:"); for (i = 0, mod = zone->mod; mod; mod = mod->next, i++) { fmt("- modulator: %d", i); indent(); dump_mod(mod); outdent(); } } } static void dump_inst(fluid_inst_t *inst) { int i; fluid_inst_zone_t *zone; fmt("name: %s", inst->name); if (inst->global_zone) { fmt("global_zone:"); indent(); dump_inst_zone(inst->global_zone); outdent(); } fmt("zones:"); for (i = 0, zone = inst->zone; zone; zone = fluid_inst_zone_next(zone), i++) { fmt("- zone: %d", i); if (zone == NULL) { continue; } indent(); dump_inst_zone(zone); outdent(); } } static int inst_compare_func(const void *a, const void *b) { const fluid_inst_t *inst_a = a; const fluid_inst_t *inst_b = b; return inst_a->source_idx - inst_b->source_idx; } static fluid_list_t *collect_preset_insts(fluid_preset_t *preset, fluid_list_t *inst_list) { fluid_preset_zone_t *zone; fluid_defpreset_t *defpreset = fluid_preset_get_data(preset); if (defpreset == NULL) { return inst_list; } if (defpreset->global_zone && defpreset->global_zone->inst && fluid_list_idx(inst_list, defpreset->global_zone->inst) == -1) { inst_list = fluid_list_prepend(inst_list, defpreset->global_zone->inst); } for (zone = defpreset->zone; zone; zone = fluid_preset_zone_next(zone)) { if (zone->inst && (fluid_list_idx(inst_list, zone->inst) == -1)) { inst_list = fluid_list_prepend(inst_list, zone->inst); } } return inst_list; } static void dump_defsfont(fluid_defsfont_t *defsfont) { int i; fluid_list_t *list; fluid_sample_t *sample; fluid_preset_t *preset; fluid_inst_t *inst; fluid_list_t *inst_list = NULL; fmt("samplepos: %u", defsfont->samplepos); fmt("samplesize: %u", defsfont->samplesize); fmt("sample24pos: %u", defsfont->sample24pos); fmt("sample24size: %u", defsfont->sample24size); fmt("presets:"); for (i = 0, list = defsfont->preset; list; list = fluid_list_next(list), i++) { preset = (fluid_preset_t *)fluid_list_get(list); fmt("- preset: %d", i); if (preset == NULL) { continue; } indent(); dump_preset(preset); outdent(); fmt(""); inst_list = collect_preset_insts(preset, inst_list); } inst_list = fluid_list_sort(inst_list, inst_compare_func); fmt("instruments:"); for (list = inst_list; list; list = fluid_list_next(list)) { inst = (fluid_inst_t *)fluid_list_get(list); fmt("- instrument: %d", inst->source_idx); indent(); dump_inst(inst); outdent(); fmt(""); } delete_fluid_list(inst_list); fmt("samples:"); for (i = 0, list = defsfont->sample; list; list = fluid_list_next(list), i++) { sample = (fluid_sample_t *)fluid_list_get(list); fmt("- sample: %d", i); if (sample == NULL) { continue; } indent(); dump_sample(sample); outdent(); fmt(""); } } static void fmt(const char *format, ...) { char buf[FMT_BUFSIZE]; va_list args; int len; int i; va_start(args, format); len = FLUID_VSNPRINTF(buf, FMT_BUFSIZE, format, args); va_end(args); if (len < 0) { FLUID_LOG(FLUID_ERR, "max buffer size exceeded"); return; } buf[FMT_BUFSIZE - 1] = '\0'; for (i = 0; i < indent_level; i++) { fprintf(output, " "); } fwrite(buf, 1, FLUID_STRLEN(buf), output); fprintf(output, "\n"); } static void indent(void) { indent_level += 1; } static void outdent(void) { if (indent_level > 0) { indent_level -= 1; } } fluidsynth-2.2.5/test/test.h000066400000000000000000000021711417326347500160350ustar00rootroot00000000000000 #pragma once #include "config.h" #include #include /* * We call abort() because on Linux this sends a signal to the process and causes the debugger to break. * * MSVC++ runtime opens a dialog when abort() is called, saying that abort() has been called and you can * click "Retry" to make the debugger break. * When executed by CI build however, the dialog causes the unit tests to be stuck forever. * Thus suppress the dialog on windows. * MinGW however requires explicit linking against MSVCRT >= 8.0 for _set_abort_behavior(). * It's not worth the hassle to implement this with cmake... */ #if defined(NO_GUI) && defined(MINGW32) #define TEST_ABORT exit(EXIT_FAILURE); #elif defined(NO_GUI) && defined(WIN32) #define TEST_ABORT _set_abort_behavior(0, _WRITE_ABORT_MSG); abort() #else #define TEST_ABORT abort() #endif #define TEST_ASSERT(COND) do { if (!(COND)) { fprintf(stderr, __FILE__ ":%d assertion (%s) failed\n", __LINE__, #COND); TEST_ABORT; } } while (0) /* macro to test whether a fluidsynth function succeeded or not */ #define TEST_SUCCESS(FLUID_FUNCT) TEST_ASSERT((FLUID_FUNCT) != FLUID_FAILED) fluidsynth-2.2.5/test/test_bug_635.c000066400000000000000000000041721417326347500172650ustar00rootroot00000000000000#include "test.h" #include "fluidsynth.h" #include "sfloader/fluid_sfont.h" #include "sfloader/fluid_defsfont.h" #include "utils/fluid_sys.h" #include "utils/fluid_list.h" // load our sf2 and sf3 test soundfonts, with and without dynamic sample loading int main(void) { int id[2], sfcount, i; fluid_synth_t *synth; fluid_settings_t *settings = new_fluid_settings(); fluid_settings_setint(settings, "synth.dynamic-sample-loading", 1); synth = new_fluid_synth(settings); id[0] = fluid_synth_sfload(synth, TEST_SOUNDFONT, 0); sfcount = fluid_synth_sfcount(synth); TEST_ASSERT(id[0] != FLUID_FAILED); TEST_ASSERT(sfcount == 1); for(i = 0; i < sfcount; i++) { fluid_preset_t *preset; fluid_list_t *list; fluid_sfont_t *sfont = fluid_synth_get_sfont_by_id(synth, id[i]); fluid_defsfont_t *defsfont = fluid_sfont_get_data(sfont); /* Make sure we have the right number of presets */ fluid_sfont_iteration_start(sfont); while((preset = fluid_sfont_iteration_next(sfont)) != NULL) { // Try to start a voice with a preset that has not been selected on any channel (bug #635) // ignore return value check on fluid_synth_start fluid_synth_start(synth, 0, preset, 0, 1, 60, 127); // fluidsynth < 2.1.3 would crash here TEST_SUCCESS(fluid_synth_process(synth, 1024, 0, NULL, 0, NULL)); fluid_synth_stop(synth, 0); } for(list = defsfont->sample; list; list = fluid_list_next(list)) { fluid_sample_t *sample = fluid_list_get(list); fluid_voice_t *v; v = fluid_synth_alloc_voice(synth, sample, 0, 60, 127); fluid_synth_start_voice(synth, v); // fluidsynth < 2.1.3 would crash here TEST_SUCCESS(fluid_synth_process(synth, 1024, 0, NULL, 0, NULL)); // make sure the voice was NULL, no need for fluid_synth_stop() here TEST_ASSERT(v == NULL); } } /* teardown */ delete_fluid_synth(synth); delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_ct2hz.c000066400000000000000000000024531417326347500171450ustar00rootroot00000000000000 #include "test.h" #include "utils/fluid_conv.h" #include "utils/fluid_sys.h" // this test makes sure FLUID_SNPRINTF uses a proper C99 compliant implementation int float_eq(fluid_real_t x, fluid_real_t y) { static const float EPS = 1e-5; FLUID_LOG(FLUID_INFO, "Comparing %.9f and %.9f", x, y); return FLUID_FABS(x-y) < EPS; } int main(void) { // 440 * 2^((x-6900)/1200) where x is the cent value given to ct2hz() TEST_ASSERT(float_eq(fluid_ct2hz_real(38099), 2.9510849101059895e10)); TEST_ASSERT(float_eq(fluid_ct2hz_real(13500), 19912.12696)); TEST_ASSERT(float_eq(fluid_ct2hz_real(12900), 14080)); TEST_ASSERT(float_eq(fluid_ct2hz_real(12899), 14071.86942)); TEST_ASSERT(float_eq(fluid_ct2hz_real(12700), 12543.85395)); TEST_ASSERT(float_eq(fluid_ct2hz_real(6900), 440)); TEST_ASSERT(float_eq(fluid_ct2hz_real(5700), 220)); TEST_ASSERT(float_eq(fluid_ct2hz_real(4500), 110)); TEST_ASSERT(float_eq(fluid_ct2hz_real(901), 13.75794461)); TEST_ASSERT(float_eq(fluid_ct2hz_real(900), 13.75)); TEST_ASSERT(float_eq(fluid_ct2hz_real(899), 13.74205998)); TEST_ASSERT(float_eq(fluid_ct2hz_real(1), 8.180522806)); TEST_ASSERT(float_eq(fluid_ct2hz_real(0), 8.175798916)); // often referred to as Absolute zero in the SF2 spec return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_jack_obtaining_synth.c000066400000000000000000000021311417326347500222730ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" #include "fluid_adriver.h" // The jack driver may need the synth instance to adjust the sample-rate in case it mismatches with // the sample-rate of the jack driver. However, new_fluid_audio_driver2() does not receive a synth pointer. // Thus looking up the synth instance must be done via the settings object. int main(void) { #if JACK_SUPPORT fluid_synth_t *obtained_synth; fluid_synth_t *expected_synth; fluid_settings_t *settings = new_fluid_settings(); TEST_ASSERT(settings != NULL); expected_synth = new_fluid_synth(settings); TEST_ASSERT(expected_synth != NULL); TEST_SUCCESS(fluid_jack_obtain_synth(settings, &obtained_synth)); TEST_ASSERT(obtained_synth == expected_synth); delete_fluid_synth(obtained_synth); delete_fluid_settings(settings); obtained_synth = expected_synth = NULL; settings = new_fluid_settings(); TEST_ASSERT(settings != NULL); TEST_ASSERT(fluid_jack_obtain_synth(settings, &obtained_synth) == FLUID_FAILED); delete_fluid_settings(settings); #endif return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_pointer_alignment.c000066400000000000000000000013501417326347500216240ustar00rootroot00000000000000 #include "test.h" #include "utils/fluid_sys.h" // test for fluid_align_ptr() int main(void) { unsigned int align; uintptr_t ptr, aligned_ptr; for(align = 32; align <= 4 * 1024u; align <<= 1) { for(ptr = 0; ptr <= (align << 10); ptr++) { char *tmp = fluid_align_ptr((char *)ptr, align); aligned_ptr = (uintptr_t)tmp; // pointer must be aligned properly TEST_ASSERT(aligned_ptr % align == 0); // aligned pointer must not be smaller than ptr TEST_ASSERT(aligned_ptr >= ptr); // aligned pointer must not be bigger than alignment TEST_ASSERT(aligned_ptr < ptr + align); } } return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_preset_pinning.c000066400000000000000000000162111417326347500211340ustar00rootroot00000000000000#include "test.h" #include "fluidsynth.h" #include "sfloader/fluid_sfont.h" #include "sfloader/fluid_defsfont.h" #include "sfloader/fluid_samplecache.h" #include "utils/fluid_sys.h" #include "utils/fluid_list.h" static int count_loaded_samples(fluid_synth_t *synth, int sfont_id); /* Test the preset pinning and unpinning feature */ int main(void) { int id; fluid_synth_t *synth; fluid_sfont_t *sfont; fluid_defsfont_t *defsfont; /* Setup */ fluid_settings_t *settings = new_fluid_settings(); /* Test that pinning and unpinning has no effect and throws no errors * when dynamic-sample-loading is disabled */ fluid_settings_setint(settings, "synth.dynamic-sample-loading", 0); synth = new_fluid_synth(settings); id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 0); TEST_ASSERT(count_loaded_samples(synth, id) == 123); TEST_ASSERT(fluid_samplecache_count_entries() == 1); /* Attempt to pin and unpin an existing preset should succeed (but have no effect) */ TEST_ASSERT(fluid_synth_pin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 123); TEST_ASSERT(fluid_synth_unpin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 123); /* Attempt to pin and unpin a non-existent preset should fail */ TEST_ASSERT(fluid_synth_pin_preset(synth, id, 42, 42) == FLUID_FAILED); TEST_ASSERT(count_loaded_samples(synth, id) == 123); TEST_ASSERT(fluid_synth_unpin_preset(synth, id, 42, 42) == FLUID_FAILED); TEST_ASSERT(count_loaded_samples(synth, id) == 123); delete_fluid_synth(synth); /* Test pinning and unpinning with dyamic-sample loading enabled */ fluid_settings_setint(settings, "synth.dynamic-sample-loading", 1); synth = new_fluid_synth(settings); id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 0); TEST_ASSERT(count_loaded_samples(synth, id) == 0); TEST_ASSERT(fluid_samplecache_count_entries() == 0); /* For the following tests, preset 42 (Lead Synth 2) consists of 4 samples, * preset 40 (Aluminum Plate) consists of 1 sample */ /* Simple pin and unpin */ TEST_ASSERT(fluid_synth_pin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); TEST_ASSERT(fluid_synth_unpin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 0); TEST_ASSERT(fluid_samplecache_count_entries() == 0); /* Pinning and unpinning twice should have exactly the same effect */ TEST_ASSERT(fluid_synth_pin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); TEST_ASSERT(fluid_synth_pin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); TEST_ASSERT(fluid_synth_unpin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 0); TEST_ASSERT(fluid_samplecache_count_entries() == 0); TEST_ASSERT(fluid_synth_unpin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 0); TEST_ASSERT(fluid_samplecache_count_entries() == 0); /* Pin a single preset, select both presets, unselect both, ensure the pinned * is still in memory and gone after unpinning */ TEST_ASSERT(fluid_synth_pin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); TEST_ASSERT(fluid_synth_program_select(synth, 0, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); TEST_ASSERT(fluid_synth_program_select(synth, 1, id, 0, 40) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 5); TEST_ASSERT(fluid_samplecache_count_entries() == 5); TEST_ASSERT(fluid_synth_unset_program(synth, 0) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 5); TEST_ASSERT(fluid_samplecache_count_entries() == 5); TEST_ASSERT(fluid_synth_unset_program(synth, 1) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); TEST_ASSERT(fluid_synth_unpin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 0); TEST_ASSERT(fluid_samplecache_count_entries() == 0); /* Unpinning an unpinned and selected preset should not remove it from memory */ TEST_ASSERT(fluid_synth_program_select(synth, 0, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); TEST_ASSERT(fluid_synth_unpin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); TEST_ASSERT(fluid_synth_unset_program(synth, 0) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 0); TEST_ASSERT(fluid_samplecache_count_entries() == 0); /* Test unload of soundfont with pinned sample automatically unpins * the sample and removes all samples from cache */ TEST_ASSERT(fluid_synth_pin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); TEST_ASSERT(fluid_synth_sfunload(synth, id, 0) == FLUID_OK); TEST_ASSERT(fluid_samplecache_count_entries() == 0); /* Ensure that failures during load of preset results in an * error returned by pinning function */ id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 0); // hack the soundfont filename so the next load won't find the file anymore sfont = fluid_synth_get_sfont_by_id(synth, id); defsfont = fluid_sfont_get_data(sfont); defsfont->filename[0]++; TEST_ASSERT(fluid_synth_pin_preset(synth, id, 0, 42) == FLUID_FAILED); defsfont->filename[0]--; TEST_ASSERT(fluid_synth_sfunload(synth, id, 0) == FLUID_OK); /* Test that deleting the synth with a pinned preset also leaves no * samples in cache */ id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 0); TEST_ASSERT(fluid_synth_pin_preset(synth, id, 0, 42) == FLUID_OK); TEST_ASSERT(count_loaded_samples(synth, id) == 4); TEST_ASSERT(fluid_samplecache_count_entries() == 4); delete_fluid_synth(synth); TEST_ASSERT(fluid_samplecache_count_entries() == 0); /* Tear down */ delete_fluid_settings(settings); return EXIT_SUCCESS; } static int count_loaded_samples(fluid_synth_t *synth, int sfont_id) { fluid_list_t *list; int count = 0; fluid_sfont_t *sfont = fluid_synth_get_sfont_by_id(synth, sfont_id); fluid_defsfont_t *defsfont = fluid_sfont_get_data(sfont); for(list = defsfont->sample; list; list = fluid_list_next(list)) { fluid_sample_t *sample = fluid_list_get(list); if(sample->data != NULL) { count++; } } FLUID_LOG(FLUID_INFO, "Loaded samples on sfont %d: %d\n", sfont_id, count); return count; } fluidsynth-2.2.5/test/test_preset_sample_loading.c000066400000000000000000000055361417326347500224600ustar00rootroot00000000000000#include "test.h" #include "fluidsynth.h" #include "sfloader/fluid_sfont.h" #include "sfloader/fluid_defsfont.h" #include "utils/fluid_sys.h" #include "utils/fluid_list.h" // load our sf2 and sf3 test soundfonts, with and without dynamic sample loading int main(void) { int id[2], sfcount, dyn_sample, i; /* setup */ fluid_settings_t *settings = new_fluid_settings(); for(dyn_sample = 0; dyn_sample <= 1; dyn_sample++) { fluid_synth_t *synth; fluid_settings_setint(settings, "synth.dynamic-sample-loading", dyn_sample); synth = new_fluid_synth(settings); id[0] = fluid_synth_sfload(synth, TEST_SOUNDFONT, 0); id[1] = FLUID_FAILED;//fluid_synth_sfload(synth, TEST_SOUNDFONT_SF3, 0); sfcount = fluid_synth_sfcount(synth); TEST_ASSERT(id[0] != FLUID_FAILED); #if 0 TEST_ASSERT(id[1] != FLUID_FAILED); TEST_ASSERT(sfcount == 2); #else TEST_ASSERT(id[1] == FLUID_FAILED); TEST_ASSERT(sfcount == 1); #endif for(i = 0; i < sfcount; i++) { fluid_preset_t *preset; fluid_list_t *list; fluid_preset_t *prev_preset = NULL; fluid_sample_t *prev_sample = NULL; int count = 0; fluid_sfont_t *sfont = fluid_synth_get_sfont_by_id(synth, id[i]); fluid_defsfont_t *defsfont = fluid_sfont_get_data(sfont); /* Make sure we have the right number of presets */ fluid_sfont_iteration_start(sfont); while((preset = fluid_sfont_iteration_next(sfont)) != NULL) { count++; if(preset->notify != NULL) { preset->notify(preset, FLUID_PRESET_SELECTED, 0); } /* make sure we actually got a different preset */ TEST_ASSERT(preset != prev_preset); prev_preset = preset; } /* VintageDreams has 136 presets */ TEST_ASSERT(count == 136); /* Make sure we have the right number of samples */ count = 0; for(list = defsfont->sample; list; list = fluid_list_next(list)) { fluid_sample_t *sample = fluid_list_get(list); if(sample->data != NULL) { count++; } TEST_ASSERT(sample->amplitude_that_reaches_noise_floor_is_valid); /* Make sure we actually got a different sample */ TEST_ASSERT(sample != prev_sample); prev_sample = sample; } /* VintageDreams has 123 valid samples (one is a ROM sample and ignored) */ TEST_ASSERT(count == 123); } /* teardown */ delete_fluid_synth(synth); } delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_sample_cache.c000066400000000000000000000024021417326347500205110ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" // use local fluidsynth header #include "utils/fluid_sys.h" // this test aims to make sure that sample data used by multiple synths is not freed // once unloaded by its parent synth int main(void) { enum { FRAMES = 1024 }; float buf[FRAMES * 2]; fluid_settings_t *settings = new_fluid_settings(); fluid_synth_t *synth1 = new_fluid_synth(settings), *synth2 = new_fluid_synth(settings); TEST_ASSERT(settings != NULL); TEST_ASSERT(synth1 != NULL); TEST_ASSERT(synth2 != NULL); // load a sfont to synth1 TEST_SUCCESS(fluid_synth_sfload(synth1, TEST_SOUNDFONT, 1)); // load the same font to synth2 and start a note TEST_SUCCESS(fluid_synth_sfload(synth2, TEST_SOUNDFONT, 1)); TEST_SUCCESS(fluid_synth_noteon(synth2, 0, 60, 127)); // render some audio TEST_SUCCESS(fluid_synth_write_float(synth2, FRAMES, buf, 0, 2, buf, 1, 2)); // delete the synth that owns the soundfont delete_fluid_synth(synth1); // render again with the unloaded sfont and hope no segfault happens TEST_SUCCESS(fluid_synth_write_float(synth2, FRAMES, buf, 0, 2, buf, 1, 2)); delete_fluid_synth(synth2); delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_sample_rate_change.c000066400000000000000000000036511417326347500217150ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" #include "synth/fluid_synth.h" #include "synth/fluid_voice.h" #include "rvoice/fluid_rvoice.h" #include "utils/fluid_sys.h" static void verify_sample_rate(fluid_synth_t *synth, int expected_srate) { int i; TEST_ASSERT(synth->sample_rate == expected_srate); for(i = 0; i < synth->polyphony; i++) { TEST_ASSERT(synth->voice[i]->output_rate == expected_srate); TEST_ASSERT(synth->voice[i]->rvoice->dsp.output_rate == expected_srate); TEST_ASSERT(synth->voice[i]->overflow_rvoice->dsp.output_rate == expected_srate); } // TODO check fx, rvoice_mixer et. al.? } // this test should make sure that sample rate changed are handled correctly int main(void) { int polyphony; double sample_rate; fluid_settings_t *settings = new_fluid_settings(); fluid_synth_t *synth = new_fluid_synth(settings); TEST_ASSERT(settings != NULL); TEST_ASSERT(synth != NULL); TEST_SUCCESS(fluid_settings_getint(settings, "synth.polyphony", &polyphony)); TEST_ASSERT(polyphony == synth->polyphony); TEST_SUCCESS(fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate)); verify_sample_rate(synth, sample_rate); TEST_SUCCESS(fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); // play some notes to start some voices TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); TEST_SUCCESS(fluid_synth_noteon(synth, 2, 71, 127)); TEST_SUCCESS(fluid_synth_noteon(synth, 3, 82, 127)); verify_sample_rate(synth, sample_rate); // set srate to minimum allowed sample_rate = 8000; fluid_synth_set_sample_rate(synth, sample_rate); // manually dispatch rvoice events to get samples rates updated (this simulates the render thread) fluid_synth_process_event_queue(synth); verify_sample_rate(synth, sample_rate); delete_fluid_synth(synth); delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_sample_validate.c000066400000000000000000000071461417326347500212510ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" #include "sfloader/fluid_sfont.h" #include "utils/fluid_sys.h" // this tests ensures that samples with invalid SfSampleType flag combinations are rejected int main(void) { fluid_sample_t* sample = new_fluid_sample(); sample->start = 0; sample->end = 1; /// valid flags { sample->sampletype = FLUID_SAMPLETYPE_OGG_VORBIS; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype = FLUID_SAMPLETYPE_LINKED; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype = FLUID_SAMPLETYPE_MONO; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype = FLUID_SAMPLETYPE_RIGHT; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype = FLUID_SAMPLETYPE_LEFT; TEST_SUCCESS(fluid_sample_validate(sample, 2)); } /// valid, but unsupported linked sample flags { sample->sampletype = FLUID_SAMPLETYPE_LINKED; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype = FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_OGG_VORBIS; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype = FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_MONO; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype = FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_RIGHT; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype = FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_LEFT; TEST_SUCCESS(fluid_sample_validate(sample, 2)); } /// valid, but rejected ROM sample flags { sample->sampletype = FLUID_SAMPLETYPE_ROM; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); sample->sampletype = FLUID_SAMPLETYPE_ROM | FLUID_SAMPLETYPE_OGG_VORBIS; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); sample->sampletype = FLUID_SAMPLETYPE_ROM | FLUID_SAMPLETYPE_LINKED; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); sample->sampletype = FLUID_SAMPLETYPE_ROM | FLUID_SAMPLETYPE_MONO; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); sample->sampletype = FLUID_SAMPLETYPE_ROM | FLUID_SAMPLETYPE_RIGHT; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); sample->sampletype = FLUID_SAMPLETYPE_ROM | FLUID_SAMPLETYPE_LEFT; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); } /// invalid flag combinations { // no flags set? technically illegal, but handle it as mono sample->sampletype = 0; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype = FLUID_SAMPLETYPE_MONO | FLUID_SAMPLETYPE_RIGHT; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype |= FLUID_SAMPLETYPE_LEFT; TEST_SUCCESS(fluid_sample_validate(sample, 2)); sample->sampletype |= FLUID_SAMPLETYPE_LINKED; TEST_SUCCESS(fluid_sample_validate(sample, 2)); } // unknown flags must be rejected (this seems to be implicitly required by SF3) { sample->sampletype = 0x20; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); sample->sampletype |= 0x40; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); sample->sampletype = 0x80; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); sample->sampletype <<= 1; TEST_ASSERT(fluid_sample_validate(sample, 2) == FLUID_FAILED); } delete_fluid_sample(sample); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_seq_event_queue_remove.c000066400000000000000000000046411417326347500226660ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" // use local fluidsynth header static int callback_triggered = 0; void callback_remove_events(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) { callback_triggered = 1; } void test_remove_events(fluid_sequencer_t *seq, fluid_event_t *evt) { // silently creates a fluid_seqbind_t unsigned int i; int seqid = fluid_sequencer_register_client(seq, "remove event test", callback_remove_events, NULL); TEST_SUCCESS(seqid); fluid_event_set_source(evt, -1); fluid_event_set_dest(evt, seqid); fluid_event_control_change(evt, 0, 1, 127); for(i = 0; i < 10000; i++) { TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 0)); } fluid_sequencer_remove_events(seq, -1, seqid, -1); fluid_sequencer_process(seq, i + fluid_sequencer_get_tick(seq)); TEST_ASSERT(!callback_triggered); for(i = 0; i < 10000; i++) { fluid_event_control_change(evt, 0, 1, 127); TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 0)); fluid_event_noteon(evt, 0, 64, 127); TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 0)); } fluid_sequencer_remove_events(seq, -1, seqid, FLUID_SEQ_CONTROLCHANGE); fluid_sequencer_process(seq, i + fluid_sequencer_get_tick(seq)); TEST_ASSERT(callback_triggered); callback_triggered = 0; for(i = 0; i < 10000; i++) { fluid_event_program_change(evt, 0, 0); TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 0)); fluid_event_noteon(evt, 0, 64, 127); TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 0)); } fluid_sequencer_remove_events(seq, -1, -1, -1); fluid_sequencer_process(seq, i + fluid_sequencer_get_tick(seq)); TEST_ASSERT(!callback_triggered); fluid_sequencer_unregister_client(seq, seqid); } // simple test to ensure that manually unregistering and deleting the internal fluid_seqbind_t works without crashing int main(void) { fluid_event_t *evt; fluid_sequencer_t *seq = new_fluid_sequencer2(0 /*i.e. use sample timer*/); TEST_ASSERT(seq != NULL); TEST_ASSERT(fluid_sequencer_get_use_system_timer(seq) == 0); evt = new_fluid_event(); TEST_ASSERT(evt != NULL); test_remove_events(seq, evt); // client should be removed, deleting the seq should not free the struct again delete_fluid_event(evt); delete_fluid_sequencer(seq); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_seq_event_queue_sort.c000066400000000000000000000075011417326347500223560ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" // use local fluidsynth header #include "fluid_event.h" #include "fluidsynth_priv.h" #include static short order = 0; void callback_stable_sort(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) { static const int expected_type_order[] = { FLUID_SEQ_NOTEOFF, FLUID_SEQ_NOTEON, FLUID_SEQ_SYSTEMRESET, FLUID_SEQ_UNREGISTERING /* technically, FLUID_SEQ_NOTEOFF and FLUID_SEQ_NOTEON are to follow, but we've already unregistered */ }; TEST_ASSERT(fluid_event_get_type(event) == expected_type_order[order++]); } void test_order_same_tick(fluid_sequencer_t *seq, fluid_event_t *evt) { // silently creates a fluid_seqbind_t int i, seqid = fluid_sequencer_register_client(seq, "test order at same tick", callback_stable_sort, NULL); TEST_SUCCESS(seqid); TEST_ASSERT(fluid_sequencer_count_clients(seq) == 1); fluid_event_set_source(evt, -1); fluid_event_set_dest(evt, seqid); for(i = 1; i <= 2; i++) { fluid_event_noteoff(evt, 0, 64); TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 1)); fluid_event_noteon(evt, 0, 64, 127); TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 1)); } fluid_event_system_reset(evt); TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, 2, 1)); fluid_event_unregistering(evt); TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, 2, 1)); fluid_sequencer_process(seq, 1); TEST_ASSERT(order == 2); fluid_sequencer_process(seq, 2); TEST_ASSERT(order == 4); fluid_sequencer_unregister_client(seq, seqid); TEST_ASSERT(fluid_sequencer_count_clients(seq) == 0); } static unsigned int prev_time, done = FALSE; void callback_correct_order(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) { if(done) { TEST_ASSERT(fluid_event_get_type(event) == FLUID_SEQ_UNREGISTERING); } else { TEST_ASSERT(fluid_event_get_type(event) == FLUID_SEQ_CONTROLCHANGE); } TEST_ASSERT(prev_time <= fluid_event_get_time(event) && fluid_event_get_time(event) <= prev_time + 1); prev_time = fluid_event_get_time(event); } void test_correct_order(fluid_sequencer_t *seq, fluid_event_t *evt) { // silently creates a fluid_seqbind_t unsigned int i, offset; int seqid = fluid_sequencer_register_client(seq, "correct order test", callback_correct_order, NULL); TEST_SUCCESS(seqid); fluid_event_set_source(evt, -1); fluid_event_set_dest(evt, seqid); fluid_event_control_change(evt, 0, 1, 127); for(i = 0; i < 10000; i++) { TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, 10000 - i, 0)); } for(; i <= 10000 + 20000; i++) { TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 0)); } for(; i < 80000; i++) { TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, 80000 - (i - 10000 - 20000), 0)); } for(; i < 200000; i++) { TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 0)); } offset = prev_time = fluid_sequencer_get_tick(seq); fluid_sequencer_process(seq, i + offset); TEST_ASSERT(prev_time == (i - 1) + offset); done = TRUE; fluid_sequencer_unregister_client(seq, seqid); } // simple test to ensure that manually unregistering and deleting the internal fluid_seqbind_t works without crashing int main(void) { fluid_event_t *evt; fluid_sequencer_t *seq = new_fluid_sequencer2(0 /*i.e. use sample timer*/); TEST_ASSERT(seq != NULL); TEST_ASSERT(fluid_sequencer_get_use_system_timer(seq) == 0); evt = new_fluid_event(); TEST_ASSERT(evt != NULL); test_order_same_tick(seq, evt); test_correct_order(seq, evt); // client should be removed, deleting the seq should not free the struct again delete_fluid_event(evt); delete_fluid_sequencer(seq); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_seq_evt_order.c000066400000000000000000000042441417326347500207540ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" // use local fluidsynth header #include "fluid_seq_queue.h" // simple test to ensure that manually unregistering and deleting the internal fluid_seqbind_t works without crashing int main(void) { fluid_event_t* evt1 = new_fluid_event(); fluid_event_t* evt2 = new_fluid_event(); fluid_event_set_time(evt1, 1); fluid_event_set_time(evt2, 1); // Note that event_compare() returns !leftIsBeforeRight TEST_ASSERT( !event_compare_for_test(evt1, evt1)); TEST_ASSERT( !event_compare_for_test(evt2, evt2)); fluid_event_bank_select(evt1, 0, 0); fluid_event_program_change(evt2, 0, 0); TEST_ASSERT( !event_compare_for_test(evt1, evt2)); TEST_ASSERT(!!event_compare_for_test(evt2, evt1)); fluid_event_note(evt1, 0, 0, 0, 1); TEST_ASSERT(!!event_compare_for_test(evt1, evt2)); TEST_ASSERT( !event_compare_for_test(evt2, evt1)); fluid_event_noteon(evt1, 0, 0, 60); fluid_event_noteoff(evt2, 0, 0); TEST_ASSERT(!!event_compare_for_test(evt1, evt2)); TEST_ASSERT( !event_compare_for_test(evt2, evt1)); // make sure noteons with vel=0 are handled like noteoffs fluid_event_noteon(evt1, 0, 0, 60); fluid_event_noteon(evt2, 0, 0, 0); TEST_ASSERT(!!event_compare_for_test(evt1, evt2)); TEST_ASSERT( !event_compare_for_test(evt2, evt1)); // two noteoffs fluid_event_noteon(evt1, 0, 0, 0); fluid_event_noteoff(evt2, 0, 0); TEST_ASSERT( !event_compare_for_test(evt1, evt2)); TEST_ASSERT( !event_compare_for_test(evt2, evt1)); fluid_event_unregistering(evt1); fluid_event_system_reset(evt2); TEST_ASSERT(!!event_compare_for_test(evt1, evt2)); TEST_ASSERT( !event_compare_for_test(evt2, evt1)); fluid_event_unregistering(evt1); fluid_event_pan(evt2, 0, 0); TEST_ASSERT( !event_compare_for_test(evt1, evt2)); TEST_ASSERT(!!event_compare_for_test(evt2, evt1)); fluid_event_modulation(evt1, 0, 0); fluid_event_pan(evt2, 0, 0); TEST_ASSERT( !event_compare_for_test(evt1, evt2)); TEST_ASSERT( !event_compare_for_test(evt2, evt1)); delete_fluid_event(evt1); delete_fluid_event(evt2); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_seq_scale.c000066400000000000000000000124761417326347500200600ustar00rootroot00000000000000#include "test.h" #include "fluidsynth.h" #include "utils/fluid_sys.h" #include "synth/fluid_event.h" static fluid_sequencer_t *sequencer; static short synthSeqID, mySeqID; static void sendnoteon(int chan, short key, short velocity, unsigned int time) { 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, velocity); TEST_SUCCESS(fluid_sequencer_send_at(sequencer, evt, time, 0)); delete_fluid_event(evt); } static void sendpc(int chan, short val, unsigned int time) { fluid_event_t *evt = new_fluid_event(); fluid_event_set_source(evt, -1); fluid_event_set_dest(evt, synthSeqID); fluid_event_program_change(evt, chan, val); TEST_SUCCESS(fluid_sequencer_send_at(sequencer, evt, time, 0)); delete_fluid_event(evt); } static void schedule_next_callback(unsigned int time) { fluid_event_t *evt = new_fluid_event(); fluid_event_set_source(evt, -1); fluid_event_set_dest(evt, mySeqID); fluid_event_timer(evt, NULL); TEST_SUCCESS(fluid_sequencer_send_at(sequencer, evt, time, 0)); delete_fluid_event(evt); } static const unsigned int expected_callback_times[] = { 0, 0, 120, 120, 240, 240, 360, 360, 480, 720, 840, 840, 960, 960, 1080, 1080, 1200, 1440, 1560, 1560, 1680, 1680, 1800, 1800, 1920, 2160, 2280, 2280, 2400, 2400, 2520, 2520, 2640, 2880, 3000, 3000, 3120, 3120, 3240, 3240, 3360, 18080 }; static void fake_synth_callback(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) { static int callback_idx = 0; TEST_ASSERT(time == expected_callback_times[callback_idx++]); FLUID_LOG(FLUID_INFO, "synth callback time: %u %u", time, fluid_event_get_time(event)); } static void seq_callback(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) { static int phase = 0; switch(phase) { case 0: sendpc(0, 47, 0); fluid_sequencer_set_time_scale(seq, 320); sendnoteon(0, 47, 60, 0); sendnoteon(0, 47, 0, 120); sendnoteon(0, 47, 90, 120); sendnoteon(0, 47, 0, 240); sendnoteon(0, 47, 110, 240); sendnoteon(0, 47, 0, 360); sendnoteon(0, 47, 127, 360); sendnoteon(0, 47, 0, 480); schedule_next_callback(480 + 240); break; case 1: fluid_sequencer_set_time_scale(seq, 1280 / 2); sendnoteon(0, 47, 60, 0); sendnoteon(0, 47, 0, 120); sendnoteon(0, 47, 80, 120); sendnoteon(0, 47, 0, 240); sendnoteon(0, 47, 90, 240); sendnoteon(0, 47, 0, 360); sendnoteon(0, 47, 100, 360); sendnoteon(0, 47, 0, 480); schedule_next_callback(480 + 240); break; case 2: fluid_sequencer_set_time_scale(seq, 800); sendnoteon(0, 47, 60, 0); sendnoteon(0, 47, 0, 120); sendnoteon(0, 47, 80, 120); sendnoteon(0, 47, 0, 240); sendnoteon(0, 47, 90, 240); sendnoteon(0, 47, 0, 360); sendnoteon(0, 47, 100, 360); sendnoteon(0, 47, 0, 480); schedule_next_callback(480 + 240); break; case 3: fluid_sequencer_set_time_scale(seq, 1000); sendnoteon(0, 47, 60, 0); sendnoteon(0, 47, 0, 120); sendnoteon(0, 47, 80, 120); sendnoteon(0, 47, 0, 240); sendnoteon(0, 47, 90, 240); sendnoteon(0, 47, 0, 360); sendnoteon(0, 47, 100, 360); sendnoteon(0, 47, 0, 480); schedule_next_callback(480 + 240); break; case 4: fluid_sequencer_set_time_scale(seq, 320 / 2); sendnoteon(0, 47, 60, 0); sendnoteon(0, 47, 0, 120); sendnoteon(0, 47, 80, 120); sendnoteon(0, 47, 0, 240); sendnoteon(0, 47, 90, 240); sendnoteon(0, 47, 0, 360); sendnoteon(0, 47, 100, 360); sendnoteon(0, 47, 0, 480); break; case 5: // this is the unregistering event, ignore break; default: TEST_ASSERT(0); } phase++; } // for debug, uncomment below to hear the notes // #define SOUND int main(void) { int i; #ifdef SOUND fluid_settings_t *settings = new_fluid_settings(); fluid_settings_setstr(settings, "audio.driver", "alsa"); fluid_synth_t *synth = new_fluid_synth(settings); TEST_SUCCESS(fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); #endif sequencer = new_fluid_sequencer2(0); TEST_ASSERT(sequencer != NULL); #ifdef SOUND synthSeqID = fluid_sequencer_register_fluidsynth(sequencer, synth); #else // register fake synth as first destination synthSeqID = fluid_sequencer_register_client(sequencer, "fake synth", fake_synth_callback, NULL); #endif TEST_SUCCESS(synthSeqID); // register myself as scheduling destination mySeqID = fluid_sequencer_register_client(sequencer, "me", seq_callback, NULL); TEST_SUCCESS(mySeqID); // initialize our absolute date schedule_next_callback(0); #ifdef SOUND fluid_audio_driver_t *adriver = new_fluid_audio_driver(settings, synth); fluid_msleep(100000); delete_fluid_audio_driver(adriver); delete_fluid_synth(synth); delete_fluid_settings(settings); #else for(i = 0; i < 100000; i++) { fluid_sequencer_process(sequencer, i); } #endif delete_fluid_sequencer(sequencer); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_seqbind_unregister.c000066400000000000000000000037201417326347500220050ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" // use local fluidsynth header static void test_unregister_with_event(fluid_synth_t *synth) { fluid_sequencer_t *seq = new_fluid_sequencer2(0 /*i.e. use sample timer*/); // silently creates a fluid_seqbind_t int seqid = fluid_sequencer_register_fluidsynth(seq, synth); fluid_event_t *evt = new_fluid_event(); fluid_event_set_source(evt, -1); fluid_event_set_dest(evt, seqid); // manually free the fluid_seqbind_t fluid_event_unregistering(evt); fluid_sequencer_send_now(seq, evt); // client should be removed, deleting the seq should not free the struct again delete_fluid_event(evt); delete_fluid_sequencer(seq); } static void test_unregister_with_client_unregister(fluid_synth_t *synth) { fluid_sequencer_t *seq = new_fluid_sequencer2(0 /*i.e. use sample timer*/); // silently creates a fluid_seqbind_t int seqid = fluid_sequencer_register_fluidsynth(seq, synth); fluid_sequencer_unregister_client(seq, seqid); // client should be removed, deleting the seq should not free the struct again delete_fluid_sequencer(seq); } static void test_unregister_without_unregister(fluid_synth_t *synth) { fluid_sequencer_t *seq = new_fluid_sequencer2(0 /*i.e. use sample timer*/); // silently creates a fluid_seqbind_t fluid_sequencer_register_fluidsynth(seq, synth); // client should be removed delete_fluid_sequencer(seq); } // test to ensure that unregistering and deleting the internal fluid_seqbind_t works without double-free // // valgrind should be used to ensure that there are no memory leaks int main(void) { fluid_settings_t *settings = new_fluid_settings(); fluid_synth_t *synth = new_fluid_synth(settings); test_unregister_with_event(synth); test_unregister_with_client_unregister(synth); test_unregister_without_unregister(synth); delete_fluid_synth(synth); delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_settings_unregister_callback.c000066400000000000000000000055001417326347500240320ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" #include "fluidsynth_priv.h" #include "fluid_synth.h" #include static fluid_list_t* realtime_int_settings = NULL; static fluid_list_t* realtime_str_settings = NULL; static fluid_list_t* realtime_num_settings = NULL; void iter_func (void *data, const char *name, int type) { if(fluid_settings_is_realtime(data, name)) { switch(type) { case FLUID_INT_TYPE: realtime_int_settings = fluid_list_prepend(realtime_int_settings, FLUID_STRDUP(name)); break; case FLUID_STR_TYPE: realtime_str_settings = fluid_list_prepend(realtime_str_settings, FLUID_STRDUP(name)); break; case FLUID_NUM_TYPE: realtime_num_settings = fluid_list_prepend(realtime_num_settings, FLUID_STRDUP(name)); break; case FLUID_SET_TYPE: break; default: TEST_ASSERT(FALSE); } } } // this test should make sure that sample rate changed are handled correctly int main(void) { fluid_list_t* list; fluid_player_t* player; fluid_synth_t *synth; fluid_settings_t *settings = new_fluid_settings(); TEST_ASSERT(settings != NULL); synth = new_fluid_synth(settings); TEST_ASSERT(synth != NULL); player = new_fluid_player(synth); TEST_ASSERT(player != NULL); // see which of the objects above has registered a realtime setting fluid_settings_foreach(settings, settings, iter_func); // delete the objects delete_fluid_player(player); delete_fluid_synth(synth); // and now, start making changes to those realtime settings // Anything below fluidsynth 2.1.5 will crash for(list = realtime_int_settings; list; list = fluid_list_next(list)) { int min, max; char* name = fluid_list_get(list); TEST_SUCCESS(fluid_settings_getint_range(settings, name, &min, &max)); TEST_SUCCESS(fluid_settings_setint(settings, name, min)); FLUID_FREE(name); } delete_fluid_list(realtime_int_settings); for(list = realtime_num_settings; list; list = fluid_list_next(list)) { double min, max; char* name = fluid_list_get(list); TEST_SUCCESS(fluid_settings_getnum_range(settings, name, &min, &max)); TEST_SUCCESS(fluid_settings_setnum(settings, name, min)); FLUID_FREE(name); } delete_fluid_list(realtime_num_settings); for(list = realtime_str_settings; list; list = fluid_list_next(list)) { char* name = fluid_list_get(list); TEST_SUCCESS(fluid_settings_setstr(settings, name, "ABCDEFG")); FLUID_FREE(name); } delete_fluid_list(realtime_str_settings); delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_sf3_sfont_loading.c000066400000000000000000000036671417326347500215240ustar00rootroot00000000000000#include "test.h" #include "fluidsynth.h" #include "sfloader/fluid_sfont.h" #include "utils/fluid_sys.h" int main(void) { int id; fluid_sfont_t *sfont; fluid_settings_t *settings = new_fluid_settings(); fluid_synth_t *synth = new_fluid_synth(settings); TEST_ASSERT(settings != NULL); TEST_ASSERT(synth != NULL); // no sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 0); FLUID_LOG(FLUID_INFO, "Attempt to open %s", TEST_SOUNDFONT_SF3); // load a sfont to synth TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT_SF3, 1)); // one sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 1); sfont = fluid_synth_get_sfont_by_id(synth, id); TEST_ASSERT(sfont != NULL); // this is still the same filename as we've put in TEST_ASSERT(FLUID_STRCMP(TEST_SOUNDFONT_SF3, fluid_sfont_get_name(sfont)) == 0); TEST_ASSERT(fluid_sfont_get_id(sfont) == id); // still the same id? TEST_ASSERT(fluid_synth_sfreload(synth, id) == id); // one sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 1); sfont = fluid_synth_get_sfont_by_id(synth, id); TEST_ASSERT(sfont != NULL); // still the same filename? TEST_ASSERT(FLUID_STRCMP(TEST_SOUNDFONT_SF3, fluid_sfont_get_name(sfont)) == 0); // correct id stored? TEST_ASSERT(fluid_sfont_get_id(sfont) == id); // remove the sfont without deleting TEST_SUCCESS(fluid_synth_remove_sfont(synth, sfont)); // no sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 0); // re-add the sfont without deleting TEST_SUCCESS(id = fluid_synth_add_sfont(synth, sfont)); // one sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 1); // destroy the sfont TEST_SUCCESS(fluid_synth_sfunload(synth, id, 0)); // no sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 0); delete_fluid_synth(synth); delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_sfont_loading.c000066400000000000000000000045731417326347500207460ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" #include "sfloader/fluid_sfont.h" #include "sfloader/fluid_defsfont.h" #include "utils/fluid_sys.h" // this tests the soundfont loading API of the synth. // might be expanded to test the soundfont loader as well... int main(void) { int id; fluid_sfont_t *sfont; fluid_defsfont_t *defsfont; fluid_settings_t *settings = new_fluid_settings(); fluid_synth_t *synth = new_fluid_synth(settings); TEST_ASSERT(settings != NULL); TEST_ASSERT(synth != NULL); // no sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 0); TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); // load a sfont to synth TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); // one sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 1); TEST_ASSERT((sfont = fluid_synth_get_sfont_by_id(synth, id)) != NULL); // this is still the same filename as we've put in TEST_ASSERT(FLUID_STRCMP(TEST_SOUNDFONT, fluid_sfont_get_name(sfont)) == 0); TEST_ASSERT(fluid_sfont_get_id(sfont) == id); // still the same id? TEST_ASSERT(fluid_synth_sfreload(synth, id) == id); // one sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 1); TEST_ASSERT((sfont = fluid_synth_get_sfont_by_id(synth, id)) != NULL); // still the same filename? TEST_ASSERT(FLUID_STRCMP(TEST_SOUNDFONT, fluid_sfont_get_name(sfont)) == 0); // correct id stored? TEST_ASSERT(fluid_sfont_get_id(sfont) == id); // remove the sfont without deleting TEST_SUCCESS(fluid_synth_remove_sfont(synth, sfont)); // no sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 0); // re-add the sfont without deleting TEST_SUCCESS(id = fluid_synth_add_sfont(synth, sfont)); // one sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 1); // count the number of presets, instruments, samples defsfont = fluid_sfont_get_data(sfont); TEST_ASSERT(fluid_list_size(defsfont->preset) == 136); TEST_ASSERT(fluid_list_size(defsfont->sample) == 124-1); // SineWave ROM sample ignored TEST_ASSERT(fluid_list_size(defsfont->inst) == 238); // destroy the sfont TEST_SUCCESS(fluid_synth_sfunload(synth, id, 0)); // no sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 0); delete_fluid_synth(synth); delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_sfont_unloading.c000066400000000000000000000212441417326347500213030ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" #include "synth/fluid_synth.h" #include "utils/fluid_sys.h" void wait_and_free(fluid_synth_t* synth, int id, const char* calling_func) { int i; fluid_list_t *list, *list_orig; list_orig = list = synth->fonts_to_be_unloaded; synth->fonts_to_be_unloaded = NULL; delete_fluid_synth(synth); for(; list; list = fluid_list_next(list)) { fluid_timer_t* timer = fluid_list_get(list); FLUID_LOG(FLUID_INFO, "%s(): Start waiting for soundfont %d to unload", calling_func, id); for(i = 0; fluid_timer_is_running(timer) && i < 5; i++) { /* timer still running, wait a bit */ fluid_msleep(fluid_timer_get_interval(timer)); } // In worst case we've waited 5*timer_interval, i.e. soundfont should be really unloaded by now. TEST_ASSERT(!fluid_timer_is_running(timer)); delete_fluid_timer(timer); FLUID_LOG(FLUID_INFO, "%s(): End waiting for soundfont %d to unload", calling_func, id); } delete_fluid_list(list_orig); } static void test_without_rendering(fluid_settings_t* settings) { int id; fluid_synth_t *synth = new_fluid_synth(settings); TEST_ASSERT(synth != NULL); TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); // load a sfont to synth TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); // one sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 1); TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); TEST_SUCCESS(fluid_synth_sfunload(synth, id, 1)); TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 60)); // there must be one font scheduled for lazy unloading TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); wait_and_free(synth, id, __func__); } // this should work fine after applying JJCs fix a4ac56502fec5f0c20a60187d965c94ba1dc81c2 static void test_after_polyphony_exceeded(fluid_settings_t* settings) { int id; fluid_synth_t *synth = new_fluid_synth(settings); TEST_ASSERT(synth != NULL); TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); // load a sfont to synth TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); // one sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 1); TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): note on C4, voice count=%d", fluid_synth_get_active_voice_count(synth)); // need to render a bit to make synth->ticks_since_start advance, to make the previous voice "killable" TEST_SUCCESS(fluid_synth_process(synth, 2048, 0, NULL, 0, NULL)); // polyphony exceeded - killing the killable voice from above TEST_SUCCESS(fluid_synth_noteon(synth, 0, 61, 127)); // need to render again, to make the synth thread assign rvoice->dsp.sample, so that sample_unref() later really unrefs TEST_SUCCESS(fluid_synth_process(synth, 2048, 0, NULL, 0, NULL)); FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): note on C#4, voice count=%d", fluid_synth_get_active_voice_count(synth)); FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): unload sounfont"); TEST_SUCCESS(fluid_synth_sfunload(synth, id, 1)); TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 61)); // need to render yet again, to make the synth thread release the rvoice so it can be reclaimed by // fluid_synth_check_finished_voices() // need to render may more samples this time, so the voice makes it pass the release phase... TEST_SUCCESS(fluid_synth_process(synth, 204800, 0, NULL, 0, NULL)); // make any API call to execute fluid_synth_check_finished_voices() FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): note off C#4, voice count=%d", fluid_synth_get_active_voice_count(synth)); // there must be one font scheduled for lazy unloading TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); wait_and_free(synth, id, __func__); } static void test_default_polyphony(fluid_settings_t* settings, int with_rendering) { enum { BUFSIZE = 128 }; fluid_voice_t* buf[BUFSIZE]; int id; fluid_synth_t *synth = new_fluid_synth(settings); TEST_ASSERT(synth != NULL); TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); // load a sfont to synth TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); // one sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 1); TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); if(with_rendering) { TEST_SUCCESS(fluid_synth_process(synth, fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); } TEST_SUCCESS(fluid_synth_noteon(synth, 0, 61, 127)); fluid_synth_get_voicelist(synth, buf, BUFSIZE, -1); TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); if(with_rendering) { // make the synth thread assign rvoice->dsp.sample TEST_SUCCESS(fluid_synth_process(synth, 2 * fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); } TEST_ASSERT(synth->fonts_to_be_unloaded == NULL); TEST_SUCCESS(fluid_synth_sfunload(synth, id, 1)); // now, there must be one font scheduled for lazy unloading TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); // noteoff the second note and render something TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 61)); if(with_rendering) { TEST_SUCCESS(fluid_synth_process(synth, fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); } // still 4 because key 61 is playing in release phase now TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); // must be still running TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); // noteoff the first note and render something TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 60)); if(with_rendering) { TEST_SUCCESS(fluid_synth_process(synth, fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); } // still 4 because keys 60 + 61 are playing in release phase now TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); // must be still running TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); if(with_rendering) { // render enough, to make the synth thread release the rvoice so it can be reclaimed by // fluid_synth_check_finished_voices() TEST_SUCCESS(fluid_synth_process(synth, 2048000, 0, NULL, 0, NULL)); // this API call should reclaim the rvoices and call fluid_voice_stop() TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 0); } TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); if(with_rendering) { // We want to see that the timer thread unloads the soundfont before we call delete_fluid_synth(). // Wait to give the timer thread a chance to unload and finish. fluid_msleep(10 * fluid_timer_get_interval(fluid_list_get(synth->fonts_to_be_unloaded))); TEST_ASSERT(!fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); } else { TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); } wait_and_free(synth, id, __func__); } // this tests the soundfont loading API of the synth. // might be expanded to test the soundfont loader as well... int main(void) { fluid_settings_t *settings = new_fluid_settings(); TEST_ASSERT(settings != NULL); FLUID_LOG(FLUID_INFO, "Begin test_default_polyphony() with rendering"); test_default_polyphony(settings, TRUE); FLUID_LOG(FLUID_INFO, "End test_default_polyphony()\n"); FLUID_LOG(FLUID_INFO, "Begin test_default_polyphony() without rendering"); test_default_polyphony(settings, FALSE); FLUID_LOG(FLUID_INFO, "End test_default_polyphony()\n"); fluid_settings_setint(settings, "synth.polyphony", 2); FLUID_LOG(FLUID_INFO, "Begin test_after_polyphony_exceeded()"); test_after_polyphony_exceeded(settings); FLUID_LOG(FLUID_INFO, "End test_after_polyphony_exceeded()\n"); FLUID_LOG(FLUID_INFO, "Begin test_without_rendering()"); test_without_rendering(settings); FLUID_LOG(FLUID_INFO, "End test_without_rendering()"); delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_sfont_zone.c000066400000000000000000000362031417326347500202770ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" #include "sfloader/fluid_sfont.h" #include "sfloader/fluid_defsfont.h" #include "sfloader/fluid_sffile.h" #include "utils/fluid_sys.h" #define SET_BUF2(START, SIZE) \ do \ { \ file_buf = START; \ file_end = (START) + (SIZE); \ } while (0) #define SET_BUF(BUF) SET_BUF2(BUF, FLUID_N_ELEMENTS(BUF)) #define UNSET_BUF \ do \ { \ file_buf = NULL; \ file_end = NULL; \ } while (0) typedef struct { // pointer to the start of the file_buf const unsigned char *start; // actual size of the buffer unsigned int size; // expected end address of the buffer const unsigned char *end; } buf_t; static const unsigned char *file_buf = NULL; static const unsigned char *file_end = NULL; static int test_reader(void *buf, fluid_long_long_t count, void *h) { if (file_buf + count > file_end) { return FLUID_FAILED; } FLUID_MEMCPY(buf, file_buf, count); file_buf += count; return FLUID_OK; } static int test_seek(void *handle, fluid_long_long_t offset, int origin) { if (origin == SEEK_CUR) { file_buf += offset; if (file_buf > file_end) { return FLUID_FAILED; } return FLUID_OK; } // shouldn't happen? TEST_ASSERT(0); } static const fluid_file_callbacks_t fcb = { NULL, &test_reader, &test_seek, NULL, NULL }; static SFZone* new_test_zone(fluid_list_t** parent_list, int gen_count) { int i; SFZone *zone = FLUID_NEW(SFZone); TEST_ASSERT(zone != NULL); FLUID_MEMSET(zone, 0, sizeof(*zone)); for (i = 0; i < gen_count; i++) { zone->gen = fluid_list_prepend(zone->gen, NULL); } if(parent_list != NULL) { *parent_list = fluid_list_append(*parent_list, zone); } return zone; } // test the good case first: one zone, with two generators and one terminal generator static void good_test_1zone_2gen_1termgen(int (*load_func)(SFData *sf, int size), SFData* sf, SFZone *zone) { const SFGen *gen; static const unsigned char buf[] = { GEN_KEYRANGE, 0, 60, 127, GEN_VELRANGE, 0, 60, 127, 0, 0, 0, 0 }; SET_BUF(buf); TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf))); gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); TEST_ASSERT(gen != NULL); TEST_ASSERT(gen->id == GEN_KEYRANGE); TEST_ASSERT(gen->amount.range.lo == 60); TEST_ASSERT(gen->amount.range.hi == 127); gen = fluid_list_get(fluid_list_nth(zone->gen, 1)); TEST_ASSERT(gen != NULL); TEST_ASSERT(gen->id == GEN_VELRANGE); TEST_ASSERT(gen->amount.range.lo == 60); TEST_ASSERT(gen->amount.range.hi == 127); TEST_ASSERT(file_buf == buf + sizeof(buf)); UNSET_BUF; } // bad case: too few generators in buffer, triggering a chunk size mismatch static void bad_test_too_short_gen_buffer(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone) { const unsigned char final_gen = (load_func == &load_pgen) ? GEN_INSTRUMENT : GEN_SAMPLEID; SFGen *gen; unsigned int i; static const unsigned char buf1[] = { GEN_KEYRANGE, 0, 0 }; static const unsigned char buf2[] = { GEN_KEYRANGE, 0 }; static const unsigned char buf3[] = { GEN_KEYRANGE }; static const unsigned char buf8[] = { GEN_VELRANGE, 0, 0 }; static const unsigned char buf9[] = { GEN_VELRANGE, 0 }; static const unsigned char buf10[] = { GEN_VELRANGE }; static const unsigned char buf4[] = { GEN_VELRANGE, 0, 0, 127, GEN_COARSETUNE, 0, 4 }; static const unsigned char buf5[] = { GEN_VELRANGE, 0, 0, 127, GEN_COARSETUNE, 0 }; static const unsigned char buf6[] = { GEN_VELRANGE, 0, 0, 127, GEN_COARSETUNE }; const unsigned char buf11[] = { GEN_VELRANGE, 0, 0, 127, final_gen, 0, 4 }; const unsigned char buf12[] = { GEN_VELRANGE, 0, 0, 127, final_gen, 0 }; const unsigned char buf13[] = { GEN_VELRANGE, 0, 0, 127, final_gen }; static const unsigned char buf7[] = { GEN_KEYRANGE, 0, 60, 127, GEN_OVERRIDEROOTKEY }; static const buf_t buf_with_one_gen[] = { { buf1, sizeof(buf1), buf1 + sizeof(buf1) }, { buf2, sizeof(buf2),buf2 + sizeof(buf2) }, { buf3, sizeof(buf3), buf3 }, { buf8, sizeof(buf8), buf8 + sizeof(buf8) }, { buf9, sizeof(buf9), buf9 + sizeof(buf9) }, { buf10, sizeof(buf10), buf10 } }; const buf_t buf_with_two_gen[] = { { buf4, sizeof(buf4), buf4 + sizeof(buf4) -1 }, { buf5, sizeof(buf5), buf5 + sizeof(buf5) }, { buf6, sizeof(buf6), buf6 + sizeof(buf6) - 1 }, { buf11, sizeof(buf11), buf11 + sizeof(buf11) - 1 }, { buf12, sizeof(buf12), buf12 + sizeof(buf12) }, { buf13, sizeof(buf13), buf13 + sizeof(buf13) -1} }; for (i = 0; i < FLUID_N_ELEMENTS(buf_with_one_gen); i++) { SET_BUF2(buf_with_one_gen[i].start, buf_with_one_gen[i].size); TEST_ASSERT(load_func(sf, 8 /* pretend that our input buffer is big enough, to make it fail in the fcbs later */) == FALSE); gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); TEST_ASSERT(gen == NULL); TEST_ASSERT(file_buf == buf_with_one_gen[i].end); UNSET_BUF; } for (i = 0; i < FLUID_N_ELEMENTS(buf_with_two_gen); i++) { SET_BUF2(buf_with_two_gen[i].start, buf_with_two_gen[i].size); TEST_ASSERT(load_func(sf, 8) == FALSE); gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); TEST_ASSERT(gen != NULL); FLUID_FREE(gen); zone->gen->data = NULL; gen = fluid_list_get(fluid_list_nth(zone->gen, 1)); TEST_ASSERT(gen == NULL); TEST_ASSERT(file_buf == buf_with_two_gen[i].end); UNSET_BUF; } SET_BUF(buf7); TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf7)) == FALSE); gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); TEST_ASSERT(gen != NULL); TEST_ASSERT(gen->id == GEN_KEYRANGE); TEST_ASSERT(gen->amount.range.lo == 60); TEST_ASSERT(gen->amount.range.hi == 127); TEST_ASSERT(file_buf == buf7 + sizeof(buf7) - 1); UNSET_BUF; } // bad case: one zone, with two similar generators static void bad_test_duplicate_gen(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone) { const SFGen *gen; static const unsigned char buf[] = { GEN_COARSETUNE, 0, 5, 0, GEN_COARSETUNE, 0, 10, 0 }; SET_BUF(buf); TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf))); gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); TEST_ASSERT(gen != NULL); TEST_ASSERT(gen->id == GEN_COARSETUNE); TEST_ASSERT(gen->amount.sword == 10); gen = fluid_list_get(fluid_list_nth(zone->gen, 1)); TEST_ASSERT(gen == NULL); TEST_ASSERT(file_buf == buf + sizeof(buf)); UNSET_BUF; } // bad case: with one zone, generators in wrong order static void bad_test_gen_wrong_order(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone) { const SFGen *gen; static const unsigned char buf[] = { GEN_VELRANGE, 0, 60, 127, GEN_KEYRANGE, 0, 60, 127, GEN_INSTRUMENT, 0, 0xDD, 0xDD }; SET_BUF(buf); TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf))); gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); TEST_ASSERT(gen != NULL); TEST_ASSERT(gen->id == GEN_VELRANGE); TEST_ASSERT(gen->amount.range.lo == 60); TEST_ASSERT(gen->amount.range.hi == 127); gen = fluid_list_get(fluid_list_nth(zone->gen, 1)); if (load_func == &load_igen) { TEST_ASSERT(gen == NULL); } else { TEST_ASSERT(gen != NULL); TEST_ASSERT(gen->id == GEN_INSTRUMENT); TEST_ASSERT(gen->amount.uword == 0xDDDDu); } gen = fluid_list_get(fluid_list_nth(zone->gen, 2)); TEST_ASSERT(gen == NULL); TEST_ASSERT(file_buf == buf + sizeof(buf)); UNSET_BUF; } // This test-case is derived from the invalid SoundFont provided in #808 static void bad_test_issue_808(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone1) { const SFGen *gen; static const unsigned char buf[] = { // zone 1 GEN_REVERBSEND, 0, 50, 0, GEN_VOLENVRELEASE, 0, 0xCE, 0xF9, // zone 2 GEN_KEYRANGE, 0, 0, 35, GEN_OVERRIDEROOTKEY, 0, 43, 0, GEN_STARTADDRCOARSEOFS, 0, 0, 0, GEN_SAMPLEMODE, 0, 1, 0, GEN_STARTADDROFS, 0, 0, 0 }; SET_BUF(buf); TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf))); gen = fluid_list_get(fluid_list_nth(zone1->gen, 0)); TEST_ASSERT(gen != NULL); TEST_ASSERT(gen->id == GEN_REVERBSEND); TEST_ASSERT(gen->amount.uword == 50); gen = fluid_list_get(fluid_list_nth(zone1->gen, 1)); TEST_ASSERT(gen != NULL); TEST_ASSERT(gen->id == GEN_VOLENVRELEASE); TEST_ASSERT(gen->amount.uword == 0xF9CE); gen = fluid_list_get(fluid_list_nth(zone1->gen, 2)); TEST_ASSERT(gen == NULL); TEST_ASSERT(file_buf == buf + sizeof(buf)); UNSET_BUF; } // This test-case has a single zone which has additional generators after the final generator, while some of them are incomplete and others still have an extra (maybe incomplete) terminal gen. static void bad_test_additional_gens_after_final_gen(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone1) { unsigned int i; SFGen *gen; const unsigned char final_gen = (load_func == &load_pgen) ? GEN_INSTRUMENT : GEN_SAMPLEID; const unsigned char buf1[] = { // zone 1 GEN_KEYRANGE, 0, 60, 127, GEN_UNUSED1, 0, 0xFF, 0xFF, final_gen, 0, 0xDD, 0xDD, GEN_KEYRANGE, 0, 0, 35, GEN_OVERRIDEROOTKEY, 0, 43, 0, 0, 0, 0, 0 // terminal generator }; const unsigned char buf2[] = { // zone 1 GEN_KEYRANGE, 0, 60, 127, GEN_UNUSED1, 0, 0xFF, 0xFF, final_gen, 0, 0xDD, 0xDD, GEN_KEYRANGE, 0, 0, 35, GEN_OVERRIDEROOTKEY, 0, 43, 0, 0, 0, 0 // incomplete terminal generator }; const unsigned char buf3[] = { // zone 1 GEN_KEYRANGE, 0, 60, 127, GEN_UNUSED1, 0, 0xFF, 0xFF, final_gen, 0, 0xDD, 0xDD, GEN_KEYRANGE, 0, 0, 35, GEN_OVERRIDEROOTKEY, 0, 43 }; const unsigned char buf4[] = { // zone 1 GEN_KEYRANGE, 0, 60, 127, GEN_UNUSED1, 0, 0xFF, 0xFF, final_gen, 0, 0xDD, 0xDD, GEN_KEYRANGE, 0, 0, 35, GEN_OVERRIDEROOTKEY, 0 }; const buf_t buf[] = { { buf1, sizeof(buf1), buf1 + sizeof(buf1) }, { buf2, sizeof(buf2), buf2 + sizeof(buf2) - 3 }, { buf3, sizeof(buf3), buf3 + sizeof(buf3) - 3 }, { buf4, sizeof(buf4), buf4 + sizeof(buf4) - 2 }, }; // the first test case should return true, all others false int expected_ret_val = TRUE; for (i = 0; i < FLUID_N_ELEMENTS(buf); i++) { SET_BUF2(buf[i].start, buf[i].size); TEST_ASSERT(load_func(sf, buf[i].size) == expected_ret_val); expected_ret_val = FALSE; gen = fluid_list_get(fluid_list_nth(zone1->gen, 0)); TEST_ASSERT(gen != NULL); TEST_ASSERT(gen->id == GEN_KEYRANGE); TEST_ASSERT(gen->amount.range.lo == 60); TEST_ASSERT(gen->amount.range.hi == 127); // delete this generator FLUID_FREE(gen); zone1->gen->data = NULL; gen = fluid_list_get(fluid_list_nth(zone1->gen, 1)); TEST_ASSERT(gen != NULL); if (load_func == &load_igen) { TEST_ASSERT(gen->id == GEN_SAMPLEID); } else { TEST_ASSERT(gen->id == GEN_INSTRUMENT); } TEST_ASSERT(gen->amount.uword == 0xDDDDu); // delete this generator FLUID_FREE(gen); zone1->gen->data = NULL; gen = fluid_list_get(fluid_list_nth(zone1->gen, 2)); TEST_ASSERT(gen == NULL); gen = fluid_list_get(fluid_list_nth(zone1->gen, 3)); TEST_ASSERT(gen == NULL); gen = fluid_list_get(fluid_list_nth(zone1->gen, 4)); TEST_ASSERT(gen == NULL); TEST_ASSERT(file_buf == buf[i].end); UNSET_BUF; // The test cases above expect zone1 to be pre-populated with 5 generators delete_fluid_list(zone1->gen); zone1->gen = NULL; zone1->gen = fluid_list_prepend(zone1->gen, NULL); zone1->gen = fluid_list_prepend(zone1->gen, NULL); zone1->gen = fluid_list_prepend(zone1->gen, NULL); zone1->gen = fluid_list_prepend(zone1->gen, NULL); zone1->gen = fluid_list_prepend(zone1->gen, NULL); } } int main(void) { // prepare a soundfont that has one preset and one instrument, with up to 2 zones SFZone *zone1; SFData *sf = FLUID_NEW(SFData); SFPreset *preset = FLUID_NEW(SFPreset); SFInst *inst = FLUID_NEW(SFInst); TEST_ASSERT(sf != NULL); FLUID_MEMSET(sf, 0, sizeof(*sf)); TEST_ASSERT(preset != NULL); FLUID_MEMSET(preset, 0, sizeof(*preset)); TEST_ASSERT(inst != NULL); FLUID_MEMSET(inst, 0, sizeof(*inst)); sf->fcbs = &fcb; sf->preset = fluid_list_append(sf->preset, preset); sf->inst = fluid_list_append(sf->inst, inst); // Calls the given test function for 1 zone once for preset and once for inst case. #define TEST_CASE_1(TEST_FUNC, GEN_COUNT) \ do \ { \ zone1 = new_test_zone(&preset->zone, GEN_COUNT); \ TEST_FUNC(&load_pgen, sf, zone1); \ delete_zone(zone1); \ delete_fluid_list(preset->zone); \ preset->zone = NULL; \ \ zone1 = new_test_zone(&inst->zone, GEN_COUNT); \ TEST_FUNC(&load_igen, sf, zone1); \ delete_zone(zone1); \ delete_fluid_list(inst->zone); \ inst->zone = NULL; \ } while (0) TEST_CASE_1(good_test_1zone_2gen_1termgen, 2); TEST_CASE_1(good_test_1zone_2gen_1termgen, 3); TEST_CASE_1(bad_test_too_short_gen_buffer, 2); TEST_CASE_1(bad_test_duplicate_gen, 2); TEST_CASE_1(bad_test_gen_wrong_order, 3); TEST_CASE_1(bad_test_additional_gens_after_final_gen, 5); zone1 = new_test_zone(&preset->zone, 2); (void)new_test_zone(&preset->zone, 5); bad_test_issue_808(&load_pgen, sf, zone1); // zone 2 was dropped TEST_ASSERT(preset->zone->next == NULL); delete_zone(zone1); // zone2 already deleted delete_fluid_list(preset->zone); preset->zone = NULL; zone1 = new_test_zone(&inst->zone, 2); (void)new_test_zone(&inst->zone, 5); bad_test_issue_808(&load_igen, sf, zone1); // zone 2 was dropped TEST_ASSERT(inst->zone->next == NULL); delete_zone(zone1); // zone2 already deleted delete_fluid_list(inst->zone); inst->zone = NULL; delete_inst(inst); delete_preset(preset); delete_fluid_list(sf->inst); delete_fluid_list(sf->preset); // we cannot call fluid_sffile_close here, because it would destroy the mutex which is not initialized FLUID_FREE(sf); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_snprintf.c000066400000000000000000000007441417326347500177570ustar00rootroot00000000000000 #include "test.h" #include "utils/fluid_sys.h" // this test makes sure FLUID_SNPRINTF uses a proper C99 compliant implementation int main(void) { char buf[2 + 1]; int ret = FLUID_SNPRINTF(buf, sizeof(buf), "99"); TEST_ASSERT(ret == 2); TEST_ASSERT(buf[2] == '\0'); ret = FLUID_SNPRINTF(buf, sizeof(buf), "999"); TEST_ASSERT(ret == 3); // output truncated, buffer must be NULL terminated! TEST_ASSERT(buf[2] == '\0'); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_synth_chorus_reverb.c000066400000000000000000000234631417326347500222140ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" // this test should make sure that effects change (reverb, chorus) are handled correctly int main(void) { fluid_synth_t *synth; fluid_settings_t *settings = new_fluid_settings(); double value; int int_value; TEST_ASSERT(settings != NULL); /* set 2 group of effects */ TEST_SUCCESS(fluid_settings_setint(settings, "synth.effects-groups", 2)); /* set values for all reverb group */ TEST_SUCCESS(fluid_settings_setnum(settings, "synth.reverb.room-size", 0.1)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.reverb.damp", 0.2)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.reverb.width", 0.3)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.reverb.level", 0.4)); /* set values for all chorus group */ TEST_SUCCESS(fluid_settings_setint(settings, "synth.chorus.nr", 99)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.chorus.level", 0.5)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.chorus.speed", 0.6)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.chorus.depth", 0.7)); synth = new_fluid_synth(settings); TEST_ASSERT(synth != NULL); /* check that the synth is initialized with the correct values (for all reverb group) */ TEST_ASSERT(fluid_synth_get_reverb_group_roomsize(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.1); TEST_ASSERT(fluid_synth_get_reverb_group_damp(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.2); TEST_ASSERT(fluid_synth_get_reverb_group_width(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.3); TEST_ASSERT(fluid_synth_get_reverb_group_level(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.4); TEST_ASSERT(fluid_synth_get_chorus_group_nr(synth, -1, &int_value) == FLUID_OK); TEST_ASSERT(int_value == 99); TEST_ASSERT(fluid_synth_get_chorus_group_level(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.5); TEST_ASSERT(fluid_synth_get_chorus_group_speed(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.6); TEST_ASSERT(fluid_synth_get_chorus_group_depth(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.7); // update the realtime settings for all reverb group and all chorus group afterward TEST_SUCCESS(fluid_settings_setnum(settings, "synth.reverb.room-size", 0.11)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.reverb.damp", 0.22)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.reverb.width", 0.33)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.reverb.level", 0.44)); TEST_SUCCESS(fluid_settings_setint(settings, "synth.chorus.nr", 11)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.chorus.level", 0.55)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.chorus.speed", 0.66)); TEST_SUCCESS(fluid_settings_setnum(settings, "synth.chorus.depth", 0.77)); // check that the realtime settings correctly update the values in the synth TEST_ASSERT(fluid_synth_get_reverb_group_roomsize(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.11); TEST_ASSERT(fluid_synth_get_reverb_group_damp(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.22); TEST_ASSERT(fluid_synth_get_reverb_group_width(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.33); TEST_ASSERT(fluid_synth_get_reverb_group_level(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.44); TEST_ASSERT(fluid_synth_get_chorus_group_nr(synth, -1, &int_value) == FLUID_OK); TEST_ASSERT(int_value == 11); TEST_ASSERT(fluid_synth_get_chorus_group_level(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.55); TEST_ASSERT(fluid_synth_get_chorus_group_speed(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.66); TEST_ASSERT(fluid_synth_get_chorus_group_depth(synth, -1, &value) == FLUID_OK); TEST_ASSERT(value == 0.77); /* Set/get that the synth is initialized with the correct values for one group only calling fx set/get API */ // test reverb invalid parameters range // room size valid range: 0.0..1.0 TEST_ASSERT(fluid_synth_set_reverb_group_roomsize(synth, 0, 1.1) == FLUID_FAILED); TEST_ASSERT(fluid_synth_set_reverb_group_roomsize(synth, 0, -0.1) == FLUID_FAILED); // damp valid range: 0.0..1.0 TEST_ASSERT(fluid_synth_set_reverb_group_damp(synth, 0, 1.1) == FLUID_FAILED); TEST_ASSERT(fluid_synth_set_reverb_group_damp(synth, 0, -0.1) == FLUID_FAILED); // width valid range: 0.0..100.0 TEST_ASSERT(fluid_synth_set_reverb_group_width(synth, 0, 100.1) == FLUID_FAILED); TEST_ASSERT(fluid_synth_set_reverb_group_width(synth, 0, -0.1) == FLUID_FAILED); // level valid range: 0.0..1.0 TEST_ASSERT(fluid_synth_set_reverb_group_level(synth, 0, 1.1) == FLUID_FAILED); TEST_ASSERT(fluid_synth_set_reverb_group_level(synth, 0, -0.1) == FLUID_FAILED); // test chorus invalid parameters range // number of chorus block valid range: 0..99 TEST_ASSERT(fluid_synth_set_chorus_group_nr(synth, 1, 100) == FLUID_FAILED); TEST_ASSERT(fluid_synth_set_chorus_group_nr(synth, 1, -1) == FLUID_FAILED); // level valid range: 0..10 TEST_ASSERT(fluid_synth_set_chorus_group_level(synth, 0, 10.1) == FLUID_FAILED); TEST_ASSERT(fluid_synth_set_chorus_group_level(synth, 0, -0.1) == FLUID_FAILED); // lfo speed valid range: 0.1..5.0 TEST_ASSERT(fluid_synth_set_chorus_group_speed(synth, 0, 5.1) == FLUID_FAILED); TEST_ASSERT(fluid_synth_set_chorus_group_speed(synth, 0, 0.09) == FLUID_FAILED); // lfo depth valid range: 0..256 TEST_ASSERT(fluid_synth_set_chorus_group_depth(synth, 0, 256.1) == FLUID_FAILED); TEST_ASSERT(fluid_synth_set_chorus_group_depth(synth, 0, -0.1) == FLUID_FAILED); // lfo wafeform type valid range: 0..1 TEST_ASSERT(fluid_synth_set_chorus_group_type(synth, 0, 2) == FLUID_FAILED); TEST_ASSERT(fluid_synth_set_chorus_group_type(synth, 0, -1) == FLUID_FAILED); // set a value and check if we get the same value to reverb group 0 TEST_ASSERT(fluid_synth_set_reverb_group_roomsize(synth, 0, 0.1110) == FLUID_OK); TEST_ASSERT(fluid_synth_set_reverb_group_damp(synth, 0, 0.2220) == FLUID_OK); TEST_ASSERT(fluid_synth_set_reverb_group_width(synth, 0, 0.3330) == FLUID_OK); TEST_ASSERT(fluid_synth_set_reverb_group_level(synth, 0, 0.4440) == FLUID_OK); TEST_ASSERT(fluid_synth_get_reverb_group_roomsize(synth, 0, &value) == FLUID_OK); TEST_ASSERT(value == 0.1110); TEST_ASSERT(fluid_synth_get_reverb_group_damp(synth, 0, &value) == FLUID_OK); TEST_ASSERT(value == 0.2220); TEST_ASSERT(fluid_synth_get_reverb_group_width(synth, 0, &value) == FLUID_OK); TEST_ASSERT(value == 0.3330); TEST_ASSERT(fluid_synth_get_reverb_group_level(synth, 0, &value) == FLUID_OK); TEST_ASSERT(value == 0.4440); // set a value and check if we get the same value to reverb group 1 TEST_ASSERT(fluid_synth_set_reverb_group_roomsize(synth, 1, 0.1111) == FLUID_OK); TEST_ASSERT(fluid_synth_set_reverb_group_damp(synth, 1, 0.2221) == FLUID_OK); TEST_ASSERT(fluid_synth_set_reverb_group_width(synth, 1, 0.3331) == FLUID_OK); TEST_ASSERT(fluid_synth_set_reverb_group_level(synth, 1, 0.4441) == FLUID_OK); TEST_ASSERT(fluid_synth_get_reverb_group_roomsize(synth, 1, &value) == FLUID_OK); TEST_ASSERT(value == 0.1111); TEST_ASSERT(fluid_synth_get_reverb_group_damp(synth, 1, &value) == FLUID_OK); TEST_ASSERT(value == 0.2221); TEST_ASSERT(fluid_synth_get_reverb_group_width(synth, 1, &value) == FLUID_OK); TEST_ASSERT(value == 0.3331); TEST_ASSERT(fluid_synth_get_reverb_group_level(synth, 1, &value) == FLUID_OK); TEST_ASSERT(value == 0.4441); // set a value and check if we get the same value to chorus group 0 TEST_ASSERT(fluid_synth_set_chorus_group_nr(synth, 0, 20) == FLUID_OK); TEST_ASSERT(fluid_synth_set_chorus_group_level(synth, 0, 0.5550) == FLUID_OK); TEST_ASSERT(fluid_synth_set_chorus_group_speed(synth, 0, 0.6660) == FLUID_OK); TEST_ASSERT(fluid_synth_set_chorus_group_depth(synth, 0, 0.7770) == FLUID_OK); TEST_ASSERT(fluid_synth_set_chorus_group_type(synth, 0, 0) == FLUID_OK); TEST_ASSERT(fluid_synth_get_chorus_group_nr(synth, 0, &int_value) == FLUID_OK); TEST_ASSERT(int_value == 20); TEST_ASSERT(fluid_synth_get_chorus_group_level(synth, 0, &value) == FLUID_OK); TEST_ASSERT(value == 0.5550); TEST_ASSERT(fluid_synth_get_chorus_group_speed(synth, 0, &value) == FLUID_OK); TEST_ASSERT(value == 0.6660); TEST_ASSERT(fluid_synth_get_chorus_group_depth(synth, 0, &value) == FLUID_OK); TEST_ASSERT(value == 0.7770); TEST_ASSERT(fluid_synth_get_chorus_group_type(synth, 0, &int_value) == FLUID_OK); TEST_ASSERT(int_value == 0); // set a value and check if we get the same value to chorus group 1 TEST_ASSERT(fluid_synth_set_chorus_group_nr(synth, 1, 21) == FLUID_OK); TEST_ASSERT(fluid_synth_set_chorus_group_level(synth, 1, 0.5551) == FLUID_OK); TEST_ASSERT(fluid_synth_set_chorus_group_speed(synth, 1, 0.6661) == FLUID_OK); TEST_ASSERT(fluid_synth_set_chorus_group_depth(synth, 1, 0.7771) == FLUID_OK); TEST_ASSERT(fluid_synth_set_chorus_group_type(synth, 1, 1) == FLUID_OK); TEST_ASSERT(fluid_synth_get_chorus_group_nr(synth, 1, &int_value) == FLUID_OK); TEST_ASSERT(int_value == 21); TEST_ASSERT(fluid_synth_get_chorus_group_level(synth, 1, &value) == FLUID_OK); TEST_ASSERT(value == 0.5551); TEST_ASSERT(fluid_synth_get_chorus_group_speed(synth, 1, &value) == FLUID_OK); TEST_ASSERT(value == 0.6661); TEST_ASSERT(fluid_synth_get_chorus_group_depth(synth, 1, &value) == FLUID_OK); TEST_ASSERT(value == 0.7771); TEST_ASSERT(fluid_synth_get_chorus_group_type(synth, 1, &int_value) == FLUID_OK); TEST_ASSERT(int_value == 1); delete_fluid_synth(synth); delete_fluid_settings(settings); return EXIT_SUCCESS; } fluidsynth-2.2.5/test/test_synth_process.c000066400000000000000000000072101417326347500210120ustar00rootroot00000000000000 #include "test.h" #include "fluidsynth.h" #include "fluidsynth_priv.h" #include "fluid_synth.h" #include // static const int CHANNELS=16; enum { SAMPLES=1024 }; static int smpl; int render_one_mock(fluid_synth_t *synth, int blocks) { fluid_real_t *left_in, *fx_left_in; fluid_real_t *right_in, *fx_right_in; int i, j; int naudchan = fluid_synth_count_audio_channels(synth); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); for(i = 0; i < naudchan; i++) { for(j = 0; j < blocks * FLUID_BUFSIZE; j++) { int idx = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; right_in[idx] = left_in[idx] = (float)smpl++; } } return blocks; } int process_and_check(fluid_synth_t* synth, int number_of_samples, int offset) { int i; float left[SAMPLES], right[SAMPLES]; float *dry[1 * 2]; dry[0] = left; dry[1] = right; FLUID_MEMSET(left, 0, sizeof(left)); FLUID_MEMSET(right, 0, sizeof(right)); TEST_SUCCESS(fluid_synth_process_LOCAL(synth, number_of_samples, 0, NULL, 2, dry, render_one_mock)); for(i=0; i