mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-10 15:01:40 +00:00
Merge branch 'master' into linked-modulators
This commit is contained in:
commit
d234f70ab5
30 changed files with 1232 additions and 2401 deletions
24
.azure-pipelines-mac.yml
Normal file
24
.azure-pipelines-mac.yml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# C/C++ with GCC
|
||||||
|
# Build your C/C++ project with GCC using make.
|
||||||
|
# Add steps that publish test results, save build artifacts, deploy, and more:
|
||||||
|
# https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: macOS
|
||||||
|
pool:
|
||||||
|
vmImage: 'macOS-10.14'
|
||||||
|
steps:
|
||||||
|
- script: |
|
||||||
|
brew update
|
||||||
|
brew install glib gobject-introspection libsndfile pkg-config jack dbus-glib pulseaudio portaudio sdl2
|
||||||
|
displayName: 'Prerequisites'
|
||||||
|
- script: |
|
||||||
|
mkdir build && cd build
|
||||||
|
export PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig"
|
||||||
|
cmake -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 ..
|
||||||
|
make
|
||||||
|
displayName: 'Compile fluidsynth'
|
||||||
|
- script: |
|
||||||
|
cd build || exit -1
|
||||||
|
make check || exit -1
|
||||||
|
displayName: 'Execute Unittests'
|
|
@ -4,25 +4,6 @@
|
||||||
# https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc
|
# https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: macOS
|
|
||||||
pool:
|
|
||||||
vmImage: 'macOS-10.14'
|
|
||||||
steps:
|
|
||||||
- script: |
|
|
||||||
brew update
|
|
||||||
brew install glib gobject-introspection libsndfile pkg-config jack dbus-glib pulseaudio portaudio sdl2
|
|
||||||
displayName: 'Prerequisites'
|
|
||||||
- script: |
|
|
||||||
mkdir build && cd build
|
|
||||||
export PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig"
|
|
||||||
cmake -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 ..
|
|
||||||
make
|
|
||||||
displayName: 'Compile fluidsynth'
|
|
||||||
- script: |
|
|
||||||
cd build || exit -1
|
|
||||||
make check || exit -1
|
|
||||||
displayName: 'Execute Unittests'
|
|
||||||
|
|
||||||
- job: Windows
|
- job: Windows
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
@ -92,7 +73,7 @@ jobs:
|
||||||
del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll
|
del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll
|
||||||
del $(Build.ArtifactStagingDirectory)\lib\libinstpatch*.lib
|
del $(Build.ArtifactStagingDirectory)\lib\libinstpatch*.lib
|
||||||
del $(Build.ArtifactStagingDirectory)\lib\pkgconfig\libinstpatch*.pc
|
del $(Build.ArtifactStagingDirectory)\lib\pkgconfig\libinstpatch*.pc
|
||||||
rd $(Build.ArtifactStagingDirectory)\include\libinstpatch-0 /s /q
|
rd $(Build.ArtifactStagingDirectory)\include\libinstpatch-1 /s /q
|
||||||
displayName: 'Copy Artifacts'
|
displayName: 'Copy Artifacts'
|
||||||
- task: PublishBuildArtifacts@1
|
- task: PublishBuildArtifacts@1
|
||||||
inputs:
|
inputs:
|
||||||
|
|
55
.circleci/config.yml
Normal file
55
.circleci/config.yml
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
version: 2.1
|
||||||
|
orbs:
|
||||||
|
android: circleci/android@0.2.0
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
working_directory: ~/code
|
||||||
|
docker:
|
||||||
|
- image: circleci/android:api-29
|
||||||
|
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: Link NDK for Cerbero
|
||||||
|
command: |
|
||||||
|
mkdir -p /home/circleci/android-sdk-linux
|
||||||
|
ln -s /opt/android/android-ndk-r18b /home/circleci/android-sdk-linux/ndk-bundle
|
||||||
|
- 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 gtk-doc-tools libxv-dev libx11-dev libpulse-dev
|
||||||
|
python3-dev texinfo gettext build-essential pkg-config doxygen curl libxext-dev
|
||||||
|
libxi-dev x11proto-record-dev libxrender-dev libgl1-mesa-dev libxfixes-dev
|
||||||
|
libxdamage-dev libxcomposite-dev libasound2-dev libxml-simple-perl dpkg-dev
|
||||||
|
debhelper build-essential devscripts fakeroot transfig gperf libdbus-glib-1-dev
|
||||||
|
wget glib-networking libxtst-dev libxrandr-dev libglu1-mesa-dev libegl1-mesa-dev
|
||||||
|
git subversion xutils-dev intltool ccache python3-setuptools autogen maven make
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Prepare FluidSynth Android
|
||||||
|
working_directory: doc/android
|
||||||
|
command: |
|
||||||
|
make -f Makefile.android prepare
|
||||||
|
- run:
|
||||||
|
name: Build FluidSynth Android
|
||||||
|
working_directory: doc/android
|
||||||
|
command: |
|
||||||
|
make -f Makefile.android
|
||||||
|
- run:
|
||||||
|
name: Show directory contents
|
||||||
|
working_directory: doc/android
|
||||||
|
command: |
|
||||||
|
ls -R
|
||||||
|
- run:
|
||||||
|
name: Zip FluidSnyth 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
|
12
.cirrus.yml
12
.cirrus.yml
|
@ -1,10 +1,14 @@
|
||||||
freebsd_instance:
|
|
||||||
image: freebsd-12-0-release-amd64
|
|
||||||
|
|
||||||
task:
|
task:
|
||||||
|
name: FreeBSD
|
||||||
|
freebsd_instance:
|
||||||
|
matrix:
|
||||||
|
# There isn't a stable 13.0 image yet (2019-09)
|
||||||
|
image_family: freebsd-13-0-snap
|
||||||
|
image_family: freebsd-12-0
|
||||||
|
image_family: freebsd-10-4
|
||||||
|
|
||||||
install_script: pwd && ls -la && pkg install -y cmake glib alsa-lib ladspa portaudio pulseaudio pkgconf sdl2
|
install_script: pwd && ls -la && 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 -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
|
compile_script: pwd && ls -la && mkdir $HOME/fluidsynth_install/ && mkdir build && cd build && cmake -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
17
.travis.yml
17
.travis.yml
|
@ -34,26 +34,29 @@ env:
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- env:
|
- arch: arm64
|
||||||
|
env:
|
||||||
- MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && sudo apt-get install gcc-7"
|
- MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && sudo apt-get install gcc-7"
|
||||||
|
|
||||||
- env:
|
- env:
|
||||||
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8 && sudo apt-get install gcc-8"
|
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8 && sudo apt-get install gcc-8"
|
||||||
- CMAKE_FLAGS="-Denable-debug=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold"
|
- CMAKE_FLAGS="-Denable-debug=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold"
|
||||||
|
|
||||||
- os: linux
|
- env:
|
||||||
env:
|
|
||||||
- MATRIX_EVAL="CC=clang-7 && CXX=clang++-7 && sudo apt-get install clang-7"
|
- MATRIX_EVAL="CC=clang-7 && CXX=clang++-7 && sudo apt-get install clang-7"
|
||||||
|
|
||||||
- os: linux
|
- env:
|
||||||
env:
|
|
||||||
- MATRIX_EVAL="CC=clang-8 && CXX=clang++-8 && sudo rm -f /usr/local/clang-7.0.0/bin/clang-tidy && sudo ln -s /usr/bin/clang-tidy-8 /usr/bin/clang-tidy && sudo apt-get install clang-8 clang-tidy-8"
|
- MATRIX_EVAL="CC=clang-8 && CXX=clang++-8 && sudo rm -f /usr/local/clang-7.0.0/bin/clang-tidy && sudo ln -s /usr/bin/clang-tidy-8 /usr/bin/clang-tidy && sudo apt-get install clang-8 clang-tidy-8"
|
||||||
- CMAKE_FLAGS="-Denable-profiling=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold"
|
- CMAKE_FLAGS="-Denable-profiling=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold"
|
||||||
|
|
||||||
|
- os: linux-ppc64le
|
||||||
|
env:
|
||||||
|
- CMAKE_FLAGS=""
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- eval "${MATRIX_EVAL}"
|
- eval "${MATRIX_EVAL}"
|
||||||
- which clang-tidy
|
- which clang-tidy || true
|
||||||
- ls -la `which clang-tidy`
|
- ls -la `which clang-tidy` || true
|
||||||
- echo $PATH
|
- echo $PATH
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
|
|
@ -185,7 +185,7 @@ if ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_
|
||||||
endif ( NOT APPLE AND NOT OS2 )
|
endif ( NOT APPLE AND NOT OS2 )
|
||||||
|
|
||||||
# define some warning flags
|
# 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" )
|
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" )
|
||||||
|
|
||||||
# prepend to build type specific flags, to allow users to override
|
# prepend to build type specific flags, to allow users to override
|
||||||
set ( CMAKE_C_FLAGS_DEBUG "-g ${CMAKE_C_FLAGS_DEBUG}" )
|
set ( CMAKE_C_FLAGS_DEBUG "-g ${CMAKE_C_FLAGS_DEBUG}" )
|
||||||
|
@ -568,7 +568,7 @@ else(NOT enable-pkgconfig)
|
||||||
|
|
||||||
unset ( LIBINSTPATCH_SUPPORT CACHE )
|
unset ( LIBINSTPATCH_SUPPORT CACHE )
|
||||||
if ( enable-libinstpatch )
|
if ( enable-libinstpatch )
|
||||||
pkg_check_modules ( LIBINSTPATCH libinstpatch-1.0 )
|
pkg_check_modules ( LIBINSTPATCH libinstpatch-1.0>=1.1.0 )
|
||||||
set ( LIBINSTPATCH_SUPPORT ${LIBINSTPATCH_FOUND} )
|
set ( LIBINSTPATCH_SUPPORT ${LIBINSTPATCH_FOUND} )
|
||||||
endif ( enable-libinstpatch )
|
endif ( enable-libinstpatch )
|
||||||
|
|
||||||
|
|
2
NEWS
2
NEWS
|
@ -1,2 +0,0 @@
|
||||||
Check the web site at http://www.fluidsynth.org for the latest news.
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
This file is obsolete and may be removed in the future. For tips building fluidsynth on MacOSX please see our wiki for the latest information:
|
|
||||||
|
|
||||||
https://github.com/FluidSynth/fluidsynth/wiki/BuildingWithCMake#building-on-os-x
|
|
129
README.md
129
README.md
|
@ -1,32 +1,53 @@
|
||||||
|
|
||||||
# FluidSynth
|
# FluidSynth
|
||||||
|
|
||||||
| Build Status | glib < 2.30 | glib >= 2.30 |
|
| Build Status | glib < 2.30 | glib >= 2.30 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Linux | n.a. | [![Build Status Travis](https://travis-ci.org/FluidSynth/fluidsynth.svg?branch=master)](https://travis-ci.org/FluidSynth/fluidsynth/branches) |
|
| <img src="https://www.kernel.org/theme/images/logos/tux.png" height="30" alt=""> **Linux** | n.a. | [![Build Status Travis](https://travis-ci.org/FluidSynth/fluidsynth.svg?branch=master)](https://travis-ci.org/FluidSynth/fluidsynth/branches) |
|
||||||
| FreeBSD | n.a. | [![Build Status](https://api.cirrus-ci.com/github/FluidSynth/fluidsynth.svg?branch=master)](https://cirrus-ci.com/github/FluidSynth/fluidsynth) |
|
| <img src="https://www.theinquirer.net/w-images/866eae81-b13b-47b5-8180-929943e9dc21/0/daemonhammerfreebsd-580x358.jpg" height="25" alt=""> **FreeBSD** | n.a. | [![Build Status](https://api.cirrus-ci.com/github/FluidSynth/fluidsynth.svg?branch=master)](https://cirrus-ci.com/github/FluidSynth/fluidsynth) |
|
||||||
| Windows/MacOSX | [![Build Status](https://dev.azure.com/tommbrt/tommbrt/_apis/build/status/FluidSynth.fluidsynth?branchName=master)](https://dev.azure.com/tommbrt/tommbrt/_build/latest?definitionId=3&branchName=master) | [![Build status](https://ci.appveyor.com/api/projects/status/anbmtebt5uk4q1it/branch/master?svg=true)](https://ci.appveyor.com/project/derselbst/fluidsynth-g2ouw/branch/master) |
|
| <img src="https://www.microsoft.com/windows/favicon.ico" height="25" alt=""> **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) | [![Build status](https://ci.appveyor.com/api/projects/status/anbmtebt5uk4q1it/branch/master?svg=true)](https://ci.appveyor.com/project/derselbst/fluidsynth-g2ouw/branch/master) |
|
||||||
|
| <img src="https://www.apple.com/favicon.ico" height="30" alt=""> **MacOSX** | n.a. | [![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) |
|
||||||
|
| <img src="https://www.android.com/favicon.ico" height="30" alt=""> **Android** | n.a. | [![CircleCI](https://circleci.com/gh/FluidSynth/fluidsynth/tree/master.svg?style=shield)](https://circleci.com/gh/FluidSynth/fluidsynth) |
|
||||||
|
|
||||||
### FluidSynth is a software real-time synthesizer based on the Soundfont 2 specifications.
|
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
[![OHLOH Project Stats](https://www.openhub.net/p/fluidsynth/widgets/project_thin_badge?format=gif)](https://www.openhub.net/p/fluidsynth)
|
[![OHLOH Project Stats](https://www.openhub.net/p/fluidsynth/widgets/project_thin_badge?format=gif)](https://www.openhub.net/p/fluidsynth)
|
||||||
|
|
||||||
FluidSynth reads and handles MIDI events from the MIDI input
|
## Documentation
|
||||||
device. It is the software analogue of a MIDI synthesizer. FluidSynth
|
|
||||||
can also play midifiles using a Soundfont.
|
|
||||||
|
|
||||||
|
The central place for documentation and further links is our **wiki** here at GitHub:
|
||||||
|
|
||||||
## Information on the web
|
**https://github.com/FluidSynth/fluidsynth/wiki**
|
||||||
|
|
||||||
The place to look if you are looking for the latest information on
|
If you are missing parts of the documentation, let us know by writing to our mailing list.
|
||||||
FluidSynth is the web site at http://www.fluidsynth.org/.
|
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.
|
||||||
|
|
||||||
For documentation, please [see the links below](#documentation).
|
Latest information about FluidSynth is also available on the web site at http://www.fluidsynth.org/.
|
||||||
|
|
||||||
For information on how to build FluidSynth from source, please [see our wiki page](https://github.com/FluidSynth/fluidsynth/wiki/BuildingWithCMake).
|
## 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).
|
||||||
|
|
||||||
## Why did we do it
|
## 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, http://www.fluidsynth.org
|
||||||
|
|
||||||
|
- FluidSynth's wiki, https://github.com/FluidSynth/fluidsynth/wiki
|
||||||
|
|
||||||
|
- FluidSynth's API documentation, http://www.fluidsynth.org/api/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historical background
|
||||||
|
|
||||||
|
### Why did we do it
|
||||||
|
|
||||||
The synthesizer grew out of a project, started by Samuel Bianchini and
|
The synthesizer grew out of a project, started by Samuel Bianchini and
|
||||||
Peter Hanappe, and later joined by Johnathan Lee, that aimed at
|
Peter Hanappe, and later joined by Johnathan Lee, that aimed at
|
||||||
|
@ -35,7 +56,7 @@ developing a networked multi-user game.
|
||||||
Sound (and music) was considered a very important part of the game. In
|
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
|
addition, users had to be able to extend the game with their own
|
||||||
sounds and images. Johnathan Lee proposed to use the Soundfont
|
sounds and images. Johnathan Lee proposed to use the Soundfont
|
||||||
standard combined with an intelligent use of midifiles. The arguments
|
standard combined with intelligent use of midifiles. The arguments
|
||||||
were:
|
were:
|
||||||
|
|
||||||
- Wavetable synthesis is low on CPU usage, it is intuitive and it can
|
- Wavetable synthesis is low on CPU usage, it is intuitive and it can
|
||||||
|
@ -58,89 +79,21 @@ In order to make Soundfonts available on all platforms (Linux, Mac,
|
||||||
and Windows) and for all sound cards, we needed a software Soundfont
|
and Windows) and for all sound cards, we needed a software Soundfont
|
||||||
synthesizer. That is why we developed FluidSynth.
|
synthesizer. That is why we developed FluidSynth.
|
||||||
|
|
||||||
|
### Design decisions
|
||||||
|
|
||||||
## Design decisions
|
|
||||||
|
|
||||||
The synthesizer was designed to be as self-contained as possible for
|
The synthesizer was designed to be as self-contained as possible for
|
||||||
several reasons:
|
several reasons:
|
||||||
|
|
||||||
- It had to be multi-platform (Linux, MacOS, Win32). It was therefore
|
- It had to be multi-platform (Linux, macOS, Win32). It was therefore
|
||||||
important that the code didn't rely on any platform specific
|
important that the code didn't rely on any platform-specific
|
||||||
library.
|
library.
|
||||||
|
|
||||||
- It had to be easy to integrate the synthesizer modules in various
|
- It had to be easy to integrate the synthesizer modules in various
|
||||||
environements, as a plugin or as a dynamically loadable object. I
|
environments, as a plugin or as a dynamically loadable object. I
|
||||||
wanted to make the synthesizer available as a plugin (jMax, LADSPA,
|
wanted to make the synthesizer available as a plugin (jMax, LADSPA,
|
||||||
Xmms, WinAmp, Director, ...); develop language bindings (Python,
|
Xmms, WinAmp, Director, ...); develop language bindings (Python,
|
||||||
Java, Perl, ...); and integrate it into (game) frameworks (Crystal
|
Java, Perl, ...); and integrate it into (game) frameworks (Crystal
|
||||||
Space, SDL, ...). For these reasons I've decided it would be easiest
|
Space, SDL, ...). For these reasons I've decided it would be easiest
|
||||||
if the project stayed very focussed on it's goal (a Soundfont
|
if the project stayed very focussed on its goal (a Soundfont
|
||||||
synthesizer), stayed small (ideally one file) and didn't dependent
|
synthesizer), stayed small (ideally one file) and didn't dependent
|
||||||
on external code.
|
on external code.
|
||||||
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
### Home Page
|
|
||||||
|
|
||||||
- http://www.fluidsynth.org
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
- FluidSynth's wiki, https://github.com/FluidSynth/fluidsynth/wiki
|
|
||||||
|
|
||||||
- FluidSynth's API documentation, http://www.fluidsynth.org/api/
|
|
||||||
|
|
||||||
- Introduction to SoundFonts, by Josh Green,
|
|
||||||
http://smurf.sourceforge.net/sfont_intro.php
|
|
||||||
|
|
||||||
- Soundfont2 Documentation, http://www.synthfont.com/SFSPEC21.PDF (if
|
|
||||||
it moved, do a search on sfspec21.pdf).
|
|
||||||
|
|
||||||
- Soundfont.com FAQ, http://www.soundfont.com/faqs.html
|
|
||||||
|
|
||||||
- The MIDI Manufacturers Association has a standard called "Downloadable
|
|
||||||
Sounds (DLS)" that closely ressembles the Soundfont Specifications,
|
|
||||||
http://www.midi.org/about-midi/dls/abtdls.htm
|
|
||||||
|
|
||||||
|
|
||||||
### Software SoundFont Synthesizers:
|
|
||||||
|
|
||||||
- LiveSynth Pro DXi and Crescendo from LiveUpdate (Win),
|
|
||||||
http://www.livesynth.com/lspro.html
|
|
||||||
|
|
||||||
- Unity DS-1 from Bitheadz (Win & Mac), http://www.bitheadz.com/
|
|
||||||
|
|
||||||
- QuickTime 5 from Apple (Win & Mac), http://www.apple.com/quicktime/
|
|
||||||
|
|
||||||
- Logic from eMagic, http://www.emagic.de
|
|
||||||
|
|
||||||
|
|
||||||
### Soundfont Editors
|
|
||||||
|
|
||||||
- Project SWAMI by Josh Green (Linux), http://www.swamiproject.org/
|
|
||||||
|
|
||||||
- Vienna SoundFont Editor by Creative Technology Ltd. (Win)
|
|
||||||
|
|
||||||
- Alive Soundfont Editor by Soundfaction (Win), http://www.soundfaction.com/alive/index.htm
|
|
||||||
|
|
||||||
- Polyphone, http://polyphone-soundfonts.com/en/
|
|
||||||
|
|
||||||
**Note:** We cannot recommend using Audio Compositor for creating or editing Soundfonts, as it generates files that violate the Soundfont2 spec (specifically the order of generators as defined in section 8.1.2) and are therefore unusable with fluidsynth!
|
|
||||||
|
|
||||||
### Conversion Tools
|
|
||||||
|
|
||||||
- CDxtract from CDxtract (Win), http://www.cdxtract.com
|
|
||||||
|
|
||||||
- ReCycle from Propellerhead Software (Win & Mac),
|
|
||||||
http://www.propellerheads.se/products/recycle/
|
|
||||||
|
|
||||||
- Translator from Rubber Chicken Software (Win & Mac),
|
|
||||||
http://www.chickensys.com/translator
|
|
||||||
|
|
||||||
|
|
||||||
### Soundfont Databases
|
|
||||||
|
|
||||||
- HammerSound, http://www.hammersound.net
|
|
||||||
|
|
||||||
|
|
14
TODO
14
TODO
|
@ -9,7 +9,6 @@ Synthesis
|
||||||
- Dynamic voice killing (based on CPU usage)
|
- Dynamic voice killing (based on CPU usage)
|
||||||
- Batch voice activation (stereo synch. as per SoundFont spec)
|
- Batch voice activation (stereo synch. as per SoundFont spec)
|
||||||
- Pitch control on stereo samples not managed as should
|
- Pitch control on stereo samples not managed as should
|
||||||
- soft clipping, compressor, limitor, or automatic gain control
|
|
||||||
|
|
||||||
Drivers
|
Drivers
|
||||||
-------
|
-------
|
||||||
|
@ -20,7 +19,6 @@ Drivers
|
||||||
|
|
||||||
Bugs to mash
|
Bugs to mash
|
||||||
------------
|
------------
|
||||||
- Add byte swapping support (on synthesis or sample load?)
|
|
||||||
- Investigate why MIDI rendering causes burst of notes at start
|
- Investigate why MIDI rendering causes burst of notes at start
|
||||||
|
|
||||||
Validation
|
Validation
|
||||||
|
@ -37,16 +35,6 @@ Documentation
|
||||||
- Add usage scenarios in the documentation
|
- Add usage scenarios in the documentation
|
||||||
- User and system configuration file
|
- User and system configuration file
|
||||||
|
|
||||||
Binaries
|
|
||||||
--------
|
|
||||||
- FluidSynth
|
|
||||||
* Linux
|
|
||||||
* Win
|
|
||||||
* MacOS X
|
|
||||||
- fluid~ (Pd/Linux, MaxMSP/MacOSX, MaxMSP/Windows)
|
|
||||||
- fluidsynth~ (MaxMSP/MacOSX, MaxMSP/Windows)
|
|
||||||
- FluidXtra
|
|
||||||
|
|
||||||
Misc
|
Misc
|
||||||
----
|
----
|
||||||
- Remove dependency of settings on audio driver and other (see
|
- Remove dependency of settings on audio driver and other (see
|
||||||
|
@ -61,7 +49,6 @@ FluidSynth Next Generation
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
Top of the list
|
Top of the list
|
||||||
- Use FIFOs to send events to the audio thread
|
|
||||||
- 3D audio output
|
- 3D audio output
|
||||||
|
|
||||||
MIDI player
|
MIDI player
|
||||||
|
@ -81,4 +68,3 @@ MIDI Specs
|
||||||
|
|
||||||
Unsorted
|
Unsorted
|
||||||
- rewrite midi file using new sequencer
|
- rewrite midi file using new sequencer
|
||||||
- direct access to audio buffer
|
|
||||||
|
|
|
@ -98,7 +98,9 @@ build-fluidsynth-one:
|
||||||
LD_LIBRARY_PATH=$(DIST_PATH)/android_$(BUILD_ABI)/lib \
|
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_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) \
|
PKG_CONFIG_LIBDIR=$(DIST_PATH)/android_$(BUILD_ABI)/lib/pkgconfig/:$(OBOE_BUILD_PATH)/$(A_ABI) \
|
||||||
$(CMAKE) -Denable-debug=on -DCMAKE_INSTALL_PREFIX=$(PWD)/dist/$(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 \
|
-DCMAKE_TOOLCHAIN_FILE=$(ANDROID_NDK)/build/cmake/android.toolchain.cmake \
|
||||||
-Denable-opensles=on -Denable-oboe=on -Denable-oss=off -Denable-libsndfile=on \
|
-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) ../../../.. && \
|
-DANDROID_NATIVE_API_LEVEL=android-27 -DANDROID_PLATFORM=android-27 -DANDROID_ABI=$(A_ABI) ../../../.. && \
|
||||||
|
|
|
@ -73,7 +73,7 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
|
||||||
<name>chorus.speed</name>
|
<name>chorus.speed</name>
|
||||||
<type>num</type>
|
<type>num</type>
|
||||||
<def>0.3</def>
|
<def>0.3</def>
|
||||||
<min>0.29</min>
|
<min>0.1</min>
|
||||||
<max>5</max>
|
<max>5</max>
|
||||||
<desc>
|
<desc>
|
||||||
Sets the modulation speed in Hz.</desc>
|
Sets the modulation speed in Hz.</desc>
|
||||||
|
|
|
@ -1,846 +0,0 @@
|
||||||
<?xml version="1.0" encoding="koi8-r"?>
|
|
||||||
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
|
||||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
I am currently using 'xsltproc' to generate the HTML files. I use a
|
|
||||||
stylesheet contained in the docbook-xsl-stylesheets Debian package:
|
|
||||||
|
|
||||||
$ xsltproc /usr/share/sgml/docbook/stylesheet/xsl/nwalsh/html/xtchunk.xsl \
|
|
||||||
fluidsynth-v10-devdoc.xml
|
|
||||||
|
|
||||||
An alternative would be to use the Linux Documentation Project's
|
|
||||||
style sheets. See
|
|
||||||
http://www.tldp.org/LDP/LDP-Author-Guide/usingldpxsl.html.
|
|
||||||
|
|
||||||
Suggestions are welcome!
|
|
||||||
|
|
||||||
[PH]
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
<article>
|
|
||||||
<articleinfo>
|
|
||||||
<title>
|
|
||||||
FluidSynth 1.0 — Developer Documentation
|
|
||||||
</title>
|
|
||||||
|
|
||||||
<author>
|
|
||||||
<firstname>Peter</firstname>
|
|
||||||
<surname>Hanappe</surname>
|
|
||||||
</author>
|
|
||||||
|
|
||||||
<revhistory>
|
|
||||||
<revision>
|
|
||||||
<revnumber>1.0</revnumber>
|
|
||||||
<date>2003-12-11</date>
|
|
||||||
<authorinitials>hanappe</authorinitials>
|
|
||||||
<revremark>First attempt.</revremark>
|
|
||||||
</revision>
|
|
||||||
</revhistory>
|
|
||||||
|
|
||||||
<copyright>
|
|
||||||
<year>2003</year>
|
|
||||||
<holder>Copyright Peter Hanappe</holder>
|
|
||||||
</copyright>
|
|
||||||
|
|
||||||
<legalnotice>
|
|
||||||
<para>All the source code examples in this document are in the
|
|
||||||
public domain; you can use them as you please. This document is
|
|
||||||
licensed under the Creative Commons Attribution License. To view
|
|
||||||
a copy of this license, visit
|
|
||||||
http://creativecommons.org/licenses/by/1.0/ or send a letter to
|
|
||||||
Creative Commons, 559 Nathan Abbott Way, Stanford, California
|
|
||||||
94305, USA. The FluidSynth library is distributed under the GNU
|
|
||||||
Library General Public License. A copy of the GNU Library
|
|
||||||
General Public License is contained in the FluidSynth package;
|
|
||||||
if not, write to the Free Foundation, Inc., 59 Temple Place,
|
|
||||||
Suite 330, Boston, MA 02111-1307 USA.</para> </legalnotice>
|
|
||||||
|
|
||||||
<keywordset>
|
|
||||||
<keyword>FluidSynth</keyword>
|
|
||||||
<keyword>software</keyword>
|
|
||||||
<keyword>synthesizer</keyword>
|
|
||||||
<keyword>SoundFont</keyword>
|
|
||||||
<keyword>Linux</keyword>
|
|
||||||
<keyword>audio</keyword>
|
|
||||||
<keyword>development</keyword>
|
|
||||||
<keyword>documentation</keyword>
|
|
||||||
<keyword>HOWTO</keyword>
|
|
||||||
</keywordset>
|
|
||||||
|
|
||||||
<abstract>
|
|
||||||
|
|
||||||
<para>FluidSynth is a software synthesizer based on the
|
|
||||||
SoundFont 2 specifications. The synthesizer is available as a
|
|
||||||
shared object that can easily be reused in any application that
|
|
||||||
wants to use wavetable synthesis. This documents explains the
|
|
||||||
basic usage of FluidSynth. Some of the more advanced features
|
|
||||||
are not yet discussed but will be added in future
|
|
||||||
versions.</para>
|
|
||||||
|
|
||||||
</abstract>
|
|
||||||
</articleinfo>
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Disclaimer</title>
|
|
||||||
|
|
||||||
<para>This documentation, in its current version, is probably
|
|
||||||
outdated and most certainly incomplete. As always, the source code
|
|
||||||
is the final reference.</para>
|
|
||||||
|
|
||||||
<para>SoundFont(R) is a registered trademark of E-mu Systems, Inc.</para>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Introduction</title>
|
|
||||||
|
|
||||||
<para>FluidSynth can easily be embedded in an application. It has
|
|
||||||
a main header file, fluidsynth.h, and one dynamically linkable
|
|
||||||
library. FluidSynth runs on Linux, MacOS 9, MacOS X, and the Win32
|
|
||||||
platforms. It has audio and midi drivers for all mentioned
|
|
||||||
platforms but you can use it with your own drivers if your
|
|
||||||
application already handles audio and MIDI input/output. This
|
|
||||||
document explains the basic usage of FluidSynth and provides
|
|
||||||
examples that you can reuse. </para>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Creating and changing the settings</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Before you can use the synthesizer, you have to create a settings
|
|
||||||
object. The settings objects is used by all components of the
|
|
||||||
FluidSynth library. It gives a unified API to set the parameters
|
|
||||||
of the audio drivers, the midi drivers, the synthesizer,
|
|
||||||
andsoforth. A number of default settings are defined by the
|
|
||||||
current implementation. In future versions, the use of the
|
|
||||||
settings will probably be generalized.</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
All settings have a name that follows the "dotted-name"
|
|
||||||
notation. For example, "synth.polyphony" refers to the number of
|
|
||||||
voices (polyphony) preallocated by the synthesizer. The settings
|
|
||||||
also have a type. There are currently three types: strings,
|
|
||||||
numbers (double floats), and integers. You can change the values
|
|
||||||
of a setting using the <function>fluid_settings_setstr</function>,
|
|
||||||
<function>fluid_settings_setnum</function>, and
|
|
||||||
<function>fluid_settings_setint</function> functions. For example:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
#include <fluidsynth.h>
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
fluid_settings_t* settings = new_fluid_settings();
|
|
||||||
|
|
||||||
fluid_settings_setint(settings, "synth.polyphony", 128);
|
|
||||||
|
|
||||||
delete_fluid_settings(settings);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
</programlisting>
|
|
||||||
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The API contains the functions to query the type, the current
|
|
||||||
value, the default value, the range and the "hints" of a
|
|
||||||
setting. The range is the minumum and maximum value of the
|
|
||||||
setting. The hints gives additional information about a
|
|
||||||
setting. For example, whether a string represents a filename. Or
|
|
||||||
whether a number should be interpreted on on a logarithmic
|
|
||||||
scale. Check the API documentation for a description of all
|
|
||||||
functions.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Creating the synthesizer</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
To create the synthesizer, you pass it the settings object, as
|
|
||||||
in the following example:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
#include <fluidsynth.h>
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
fluid_settings_t* settings;
|
|
||||||
fluid_synth_t* synth;
|
|
||||||
|
|
||||||
fluid_settings_t* settings = new_fluid_settings();
|
|
||||||
|
|
||||||
synth = new_fluid_synth(settings);
|
|
||||||
|
|
||||||
/* Do useful things here */
|
|
||||||
|
|
||||||
delete_fluid_synth(synth);
|
|
||||||
delete_fluid_settings(settings);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The default settings should be fine for most uses. A detailed
|
|
||||||
description of all the settings used by the synthesizer described
|
|
||||||
below.
|
|
||||||
|
|
||||||
<table frame="all"><title>Synthesizer settings</title>
|
|
||||||
<tgroup cols="3" colsep="0" rowsep="0" align="left">
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.gain</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>number</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>0.2</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Min-Max</entry>
|
|
||||||
<entry>0.0-10.0</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>The gain is applied to the final or master output of the
|
|
||||||
synthesizer. It is set to a low value by default to avoid the
|
|
||||||
saturation of the output when random MIDI files are played.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.sample-rate</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>number</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>44100</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Min-Max</entry>
|
|
||||||
<entry>22050-96000</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>The sample rate of the audio generated by the synthesizer.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.polyphony</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>integer</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>256</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Min-Max</entry>
|
|
||||||
<entry>16-4096</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>The polyphony defines how many voices can be played in parallel. The
|
|
||||||
number of voices is not necessarily equivalent to the number of notes
|
|
||||||
played simultaniously. Indeed, when a note is struck on a specific
|
|
||||||
MIDI channel, the preset on that channel may created several voices,
|
|
||||||
for example, one for the left audio channel and one for the right
|
|
||||||
audio channels. The number of voices activated depends on the number
|
|
||||||
of instrument zones that fall in the correspond to the velocity and
|
|
||||||
key of the played note.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.midi-channels</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>integer</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>16</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Min-Max</entry>
|
|
||||||
<entry>16-256</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>This setting defines the number of MIDI channels of the
|
|
||||||
synthesizer. The MIDI standard defines 16 channels, so most hardware
|
|
||||||
keyboards are limited to 16. If you plan to use the synthesizer as a
|
|
||||||
plugin in an application, it might be interesting to set the number of
|
|
||||||
channels to a larger value. In this case you can program a greater
|
|
||||||
number of presets.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.reverb.active</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>string</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>"yes"</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>When set to "yes" the reverb effects module is activated. Otherwise,
|
|
||||||
no reverb will be added to the output signal. Note that when the
|
|
||||||
reverb module is active, the amount of signal send to the reverb
|
|
||||||
module depends on the "reverb send" generator defined in the
|
|
||||||
SoundFont.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.chorus.active</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>string</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>"yes"</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>When set to "yes" the chorus effects module is activated. Otherwise,
|
|
||||||
no chorus will be added to the output signal. Note that when the
|
|
||||||
reverb module is active, the amount of signal send to the chorus
|
|
||||||
module depends on the "chorus send" generator defined in the
|
|
||||||
SoundFont.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.ladspa.active</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>string</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>"no"</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>When set to "yes" the LADSPA subsystem will be called. This subsystem
|
|
||||||
allows to load and interconnect LADSPA plugins. The output of the
|
|
||||||
synthesizer is processed by the LADSPA subsystem. Note that the
|
|
||||||
synthesizer has to be compiled with LADSPA support. More information
|
|
||||||
about the LADSPA subsystem later.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.audio-groups</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>integer</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>1</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Min-Max</entry>
|
|
||||||
<entry>1-128</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>By default, the synthesizer outputs a single stereo signal. Using this
|
|
||||||
option, the synthesizer can output multichannel audio.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.effects-channels</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>integer</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>2</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Min-Max</entry>
|
|
||||||
<entry>2-2</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry></entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.verbose</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>string</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>"no"</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
|
|
||||||
<entry>When set to "yes" the synthesizer will print out information
|
|
||||||
about the received MIDI events to the stdout. This can be helpful
|
|
||||||
for debugging. This setting can not be changed after the synthesizer
|
|
||||||
has started.</entry>
|
|
||||||
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>synth.dump</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>string</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>"no"</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry></entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</tgroup>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
</para>
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Creating the audio driver</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
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.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Creating the audio driver is straightforward: set the appropriate
|
|
||||||
settings and create the driver object. Because the FluidSynth has
|
|
||||||
support for several audio systems, you may want to change which
|
|
||||||
one you want to use. The list below shows theaudio systems that
|
|
||||||
are currently supported. It displays the name, as used by the
|
|
||||||
fluidsynth library, and a description. </para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem><para>alsa: Advanced Linux Sound Architecture</para></listitem>
|
|
||||||
<listitem><para>oss: Open Sound System (Linux)</para></listitem>
|
|
||||||
<listitem><para>jack: JACK Audio Connection Kit (Linux, Mac OS X)</para></listitem>
|
|
||||||
<listitem><para>portaudio: Portaudio Library (MacOS 9 & X, Windows, Linux)</para></listitem>
|
|
||||||
<listitem><para>sndmgr: Apple SoundManager (Mac OS Classic)</para></listitem>
|
|
||||||
<listitem><para>coreaudio: Apple CoreAudio (MacOS X, experimental)</para></listitem>
|
|
||||||
<listitem><para>dsound: Microsoft DirectSound (Windows)</para></listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The default audio driver depends on the settings with which
|
|
||||||
FluidSynth was compiled. You can get the default driver with
|
|
||||||
fluid_settings_getstr_default(settings, "audio.driver"). To get
|
|
||||||
the list of available drivers use the
|
|
||||||
<function>fluid_settings_foreach_option</function>
|
|
||||||
function. Finally, you can set the driver with
|
|
||||||
<function>fluid_settings_setstr</function>. In most cases, the
|
|
||||||
default driver should work out of the box. </para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Additional options that define the audio quality and latency are
|
|
||||||
"audio.sample-format", "audio.period-size", and
|
|
||||||
"audio.periods". The details are described later.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
You create the audio driver with the
|
|
||||||
<function>new_fluid_audio_driver</function> function. This
|
|
||||||
function takes the settings and synthesizer object as
|
|
||||||
arguments. For example:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
As soon as the audio driver is created, it will start playing. The
|
|
||||||
audio driver creates a separate thread that runs in real-time mode
|
|
||||||
(is the application has sufficient privileges) and call the
|
|
||||||
synthesizer object to generate the audio.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
|
|
||||||
<para>
|
|
||||||
There are a number of general audio driver settings. The
|
|
||||||
audio.driver settings defines the audio subsystem that will be
|
|
||||||
used. The audio.periods and audio.period-size settings define the
|
|
||||||
latency and robustness against scheduling delays. There are
|
|
||||||
additional settings for the audio subsystems used. They will be
|
|
||||||
documented later.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<table frame="all"><title>General audio driver settings</title>
|
|
||||||
<tgroup cols="3" align="left" colsep="0" rowsep="0">
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>audio.driver</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>string</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>alsa (Linux), dsound (Windows), sndman (MacOS9), coreaudio (MacOS X)</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Options</entry>
|
|
||||||
<entry>alsa, oss, jack, dsound, sndman, coreaudio, portaudio</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>The audio system to be used.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>audio.periods</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>int</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>16 (Linux, MacOS X), 8 (Windows)</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Min-Max</entry>
|
|
||||||
<entry>2-64</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>The number of the audio buffers used by the driver. This
|
|
||||||
number of buffers, multiplied by the buffer size (see setting
|
|
||||||
audio.period-size), determines the maximum latency of the audio
|
|
||||||
driver.</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>audio.period-size</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>int</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>64 (Linux, MacOS X), 512 (Windows)</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Min-Max</entry>
|
|
||||||
<entry>64-8192</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
<entry>The size of the audio buffers (in frames).</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<!-- separation line -->
|
|
||||||
<row>
|
|
||||||
<entry>audio.sample-format</entry>
|
|
||||||
<entry>Type</entry>
|
|
||||||
<entry>string</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Default</entry>
|
|
||||||
<entry>"16bits"</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Options</entry>
|
|
||||||
<entry>"16bits", "float"</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry></entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
|
|
||||||
<entry>The format of the audio samples. This is currently only an
|
|
||||||
indication; the audio driver may ignore this setting if it can't
|
|
||||||
handle the specified format.</entry>
|
|
||||||
|
|
||||||
</row>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</tgroup>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Using the synthesizer without an audio driver</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
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:
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<function>fluid_synth_write_s16</function> fills two buffers (left
|
|
||||||
and right channel) with samples coded as signed 16 bits (the
|
|
||||||
endian-ness is machine
|
|
||||||
dependent). <function>fluid_synth_write_float</function> fills a
|
|
||||||
left and right audio buffer with 32 bits floating point
|
|
||||||
samples. For multi channel audio output, the function
|
|
||||||
<function>fluid_synth_nwrite_float</function> has to be used.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The function <function>fluid_synth_process</function> is still
|
|
||||||
experimental and its use is therefore not recommended but it will
|
|
||||||
probably become the generic interface in future versions.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Loading and managing SoundFonts</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Before any sound can be produced, the synthesizer needs a
|
|
||||||
SoundFont. For a discussion on SoundFont please refer to some
|
|
||||||
other, not yet existing, therefore virtual document.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
SoundFonts are loaded with the
|
|
||||||
<function>fluid_synth_sfload</function> function. The function
|
|
||||||
takes the path to a SoundFont file as argument and a boolean to
|
|
||||||
indicate whether the presets of the MIDI channels should be
|
|
||||||
updated after the SoundFont is loaded. More on the preset updates
|
|
||||||
below.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The synthesizer can load any number of SoundFonts. This is an
|
|
||||||
advantage, of course, but there are some issues that you must be
|
|
||||||
aware of. The presets in a SoundFont are identified by their bank
|
|
||||||
and preset number. The MIDI specifications allows the change of a
|
|
||||||
preset on a MIDI channel using the combination of "bank select"
|
|
||||||
and the "program change" messages. An ambiguity arrizes when a
|
|
||||||
preset with a specific bank and preset number is defined in
|
|
||||||
multiple loaded SoundFonts. This is solved by searching the
|
|
||||||
SoundFonts in the inverse order they were loaded, i.e. the lastly
|
|
||||||
loaded SoundFont is searched first for the request preset
|
|
||||||
(identified by bank and preset number) then the on but last loaded
|
|
||||||
SoundFont, and so on until. The first preset found is then
|
|
||||||
used. You can somehow consider the SoundFonts placed on a
|
|
||||||
stack. The SoundFont on top of the stack is inspected first,
|
|
||||||
followed by the SoundFont down on the stack. Newly loaded
|
|
||||||
SoundFonts are always placed on top of the stack. This is how
|
|
||||||
commercial, hardware synthesizers work. The inconvenience is that
|
|
||||||
a preset in a SoundFont at the bottom end of the stack may be
|
|
||||||
masked by a preset in a SoundFont at the top of the stack. Using
|
|
||||||
the standard MIDI messages, bank select and program change, there
|
|
||||||
is no way to select a masked preset. However, FluidSynth has an
|
|
||||||
API function to unambiguously select a preset
|
|
||||||
(<function>fluid_synth_program_select</function>). This function
|
|
||||||
is not invokeable through MIDI messages, though.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The <function>fluid_synth_sfload</function> function returns the
|
|
||||||
unique identifier of the loaded SoundFont, or -1 in case of an
|
|
||||||
error. This identifier is used in subsequent management functions:
|
|
||||||
<function>fluid_synth_sfunload</function> removes the SoundFont,
|
|
||||||
<function>fluid_synth_sfreload</function> reloads the
|
|
||||||
SoundFont. When a SoundFont is reloaded, it retains it's ID and
|
|
||||||
position on the SoundFont stack.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Additional API functions are provided to get the number of loaded
|
|
||||||
SoundFonts ot to get a pointer to the SoundFont.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Another issue that needs some explanation is the reprogramming of
|
|
||||||
the presets after a SoundFont load or unload. The default behavior
|
|
||||||
of commercial synthesizers is to reset all the preset that are
|
|
||||||
programmed on the MIDI channels when a SoundFont is loaded or
|
|
||||||
unloaded. Consider the case where MIDI channel 1 uses preset (0,
|
|
||||||
0) (the couple indicates the bank and program number). This preset
|
|
||||||
was found in the SoundFont with ID 3, for example. When a new
|
|
||||||
SoundFont is loaded that also contains a preset with bank number 0
|
|
||||||
and program number 0, then the newly loaded preset will be used on
|
|
||||||
channel 1 for future events. This behavior is as if a bank select
|
|
||||||
and program change message is send to all channels after a
|
|
||||||
load/unload using the channel's bank and program number. This may
|
|
||||||
be sometimes confusing or unwanted. A user may not want to loose
|
|
||||||
its preset setup when a new SoundFont is loaded. To avoid the
|
|
||||||
reprogramming of the presets, the third parameter to the
|
|
||||||
<function>fluid_synth_sfload</function> and
|
|
||||||
<function>fluid_synth_sfunload</function> functions should be set
|
|
||||||
to zero.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Sending MIDI events</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Once the synthesizer is up and running and a SoundFont is loaded,
|
|
||||||
most people will want to do something usefull with it. Make noise,
|
|
||||||
for example. The synthesizer aims to be compatible with the MIDI
|
|
||||||
standard, so it accepts almost all MIDI messages (details on the
|
|
||||||
MIDI compatibility elsewhere). The MIDI channel messages can be
|
|
||||||
send using the <function>fluid_synth_noteon</function>,
|
|
||||||
<function>fluid_synth_noteoff</function>,
|
|
||||||
<function>fluid_synth_cc</function>,
|
|
||||||
<function>fluid_synth_pitch_bend</function>,
|
|
||||||
<function>fluid_synth_pitch_wheel_sens</function>, and
|
|
||||||
<function>fluid_synth_program_change</function> functions. For
|
|
||||||
convenience, there's also a
|
|
||||||
<function>fluid_synth_bank_select</function> function (the bank
|
|
||||||
select message is normally sent using a control change message).
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The following example show a generic graphical button that plays a
|
|
||||||
not when clicked:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Advanced features, not yet documented</title>
|
|
||||||
|
|
||||||
<itemizedlist mark="opencircle">
|
|
||||||
<listitem><para>Accessing low-level voice parameters</para></listitem>
|
|
||||||
<listitem><para>Reverb settings</para></listitem>
|
|
||||||
<listitem><para>Chorus settings</para></listitem>
|
|
||||||
<listitem><para>Interpolation settings (set_gen, get_gen, NRPN)</para></listitem>
|
|
||||||
<listitem><para>Sequencer</para></listitem>
|
|
||||||
<listitem><para>LADSPA effects unit</para></listitem>
|
|
||||||
<listitem><para>MIDI router</para></listitem>
|
|
||||||
<listitem><para>Multi-channel audio</para></listitem>
|
|
||||||
<listitem><para>MIDI tunings</para></listitem>
|
|
||||||
<listitem><para>MIDI file player</para></listitem>
|
|
||||||
<listitem><para>SoundFont loader</para></listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
</article>
|
|
|
@ -21,6 +21,7 @@ All the source code examples in this document are in the public domain; you can
|
||||||
|
|
||||||
- \ref Disclaimer
|
- \ref Disclaimer
|
||||||
- \ref Introduction
|
- \ref Introduction
|
||||||
|
- \ref NewIn2_1_0
|
||||||
- \ref NewIn2_0_7
|
- \ref NewIn2_0_7
|
||||||
- \ref NewIn2_0_6
|
- \ref NewIn2_0_6
|
||||||
- \ref NewIn2_0_5
|
- \ref NewIn2_0_5
|
||||||
|
@ -65,6 +66,9 @@ What is FluidSynth?
|
||||||
|
|
||||||
- FluidSynth is open source, in active development. For more details, take a look at http://www.fluidsynth.org
|
- FluidSynth is open source, in active development. For more details, take a look at http://www.fluidsynth.org
|
||||||
|
|
||||||
|
\section NewIn2_1_0 Whats new in 2.1.0?
|
||||||
|
|
||||||
|
- smallest allowed chorus speed is now 0.1 Hz (previously 0.29 Hz)
|
||||||
|
|
||||||
\section NewIn2_0_7 Whats new in 2.0.7?
|
\section NewIn2_0_7 Whats new in 2.0.7?
|
||||||
|
|
||||||
|
|
|
@ -1,907 +0,0 @@
|
||||||
Xtra iiwu
|
|
||||||
Version 2.7
|
|
||||||
October 2002
|
|
||||||
|
|
||||||
|
|
||||||
--- Introduction
|
|
||||||
The iiwu Xtra integrates the iiwusynth synthesizer into Director.
|
|
||||||
|
|
||||||
The iiwusynth software synthesizer has been designed by Peter Hanappe,
|
|
||||||
and is available under the LGPL licence. It emulates in software the
|
|
||||||
SoundFont 2.01 Specifications (http://www.soundfont.com) designed by
|
|
||||||
Creative Labs (SoundBlaster maker). It is basically a small, fast and
|
|
||||||
robust wavetable synthesizer, with a MIDI-like interface and
|
|
||||||
integrated sequencer. For more information on the iiwu synthesizer :
|
|
||||||
http://www.iiwu.org/iiwusynth
|
|
||||||
|
|
||||||
The iiwu Xtra has been developped as part of the "infiniteCD Author"
|
|
||||||
project managed by Antoine Schmitt and Hyptique, with support from the
|
|
||||||
PRIAM funds from the French government.
|
|
||||||
|
|
||||||
For more information on the infiniteCD Author project :
|
|
||||||
http://www.infiniteCD.org/
|
|
||||||
http://www.hyptique.com/
|
|
||||||
http://www.gratin.org/as/
|
|
||||||
|
|
||||||
|
|
||||||
--- Licencing
|
|
||||||
|
|
||||||
The Version 2.7 of the iiwu Xtra is beta. Do not distribute without
|
|
||||||
permission. Eventually, the Xtra will be part of the iiwu project,
|
|
||||||
and thus will conform to the LGPL licence.
|
|
||||||
|
|
||||||
|
|
||||||
--- Release notes
|
|
||||||
Version 2.0.1 : first beta version - October 2002
|
|
||||||
Version 2.0.1 : first beta version - October 2002
|
|
||||||
Version 2.5 : second beta - November 2002
|
|
||||||
Version 2.6 : third beta - November 2002
|
|
||||||
Version 2.7 : fourth beta - November 2002
|
|
||||||
|
|
||||||
|
|
||||||
--- Technical Requirements
|
|
||||||
OS:
|
|
||||||
- Macintosh >= MacOS8.6.1 < MacOSX
|
|
||||||
- Windows >= 95
|
|
||||||
Director 8.5.1
|
|
||||||
Sound Card
|
|
||||||
|
|
||||||
|
|
||||||
--- Installation
|
|
||||||
Drop the folder "iiwuXtraFolder" into the Xtras folder of Director.
|
|
||||||
|
|
||||||
|
|
||||||
-----------------
|
|
||||||
--- Documentation
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
- Calls
|
|
||||||
|
|
||||||
All function calls are done using lingo's call syntax:
|
|
||||||
|
|
||||||
object.func(args...)
|
|
||||||
or
|
|
||||||
func(object, args...)
|
|
||||||
|
|
||||||
Where object is the instance of the Xtra created with the 'new'
|
|
||||||
method. All functions return either a value, which can be 0 (zero),
|
|
||||||
meaning that the function executed without errors, either a negative
|
|
||||||
number, meaning that an error occurred. If an error occured, the
|
|
||||||
function 'getError' returns a human-readable string describing the
|
|
||||||
error.
|
|
||||||
|
|
||||||
- Instances
|
|
||||||
|
|
||||||
Many instances of the Xtra may coexist at the same time.
|
|
||||||
Instances are created with the 'new' function, and deleted by assigning
|
|
||||||
the lingo variable to VOID, as usual for Xtra instances.
|
|
||||||
|
|
||||||
- SoundFont file, stack, presets
|
|
||||||
|
|
||||||
A SoundFont bank is typically stored in a file, called a SoundFont
|
|
||||||
file, of extension .sf2. In the following documentation, we will refer
|
|
||||||
to the SoundFont file or SoundFont bank by the term
|
|
||||||
'SoundFont'. Please note that the term 'bank' will always refer to a
|
|
||||||
MIDI bank, not a SoundFont bank.
|
|
||||||
|
|
||||||
A SoundFont has a name and contains 'presets'. A preset represents a
|
|
||||||
way to play a sound, as a combination of sample data and parameters on
|
|
||||||
how to play it. The preset is the fundamental sound object of the
|
|
||||||
SoundFont format.
|
|
||||||
|
|
||||||
A preset has a name, and is defined uniquely by a MIDI bank number and
|
|
||||||
a preset number in the bank. The MIDI bank number ranges from 0 to
|
|
||||||
16383 and the preset number from 0 to 127. Thus a SoundFont file may
|
|
||||||
contain up to 128x16384=2097152 presets.
|
|
||||||
|
|
||||||
SoundFont files may be loaded in the synthesizer, using the
|
|
||||||
'loadSoundFont' function, thus making its presets available for
|
|
||||||
playing. A SoundFont may be unloaded using the 'unloadSoundFont'
|
|
||||||
function.
|
|
||||||
|
|
||||||
If more than one SoundFont file is loaded, the SoundFonts are stacked
|
|
||||||
in the synthesizer : when a preset is requested by the 'programChange'
|
|
||||||
function, it is looked up in all succcessive SoundFonts, from first to
|
|
||||||
last, until the preset with the right preset number and bank number is
|
|
||||||
found. The SoundFont stack may be examined but not changed : the
|
|
||||||
SoundFonts are stacked in inverse loading order: the last loaded
|
|
||||||
SoundFont is the first searched.
|
|
||||||
|
|
||||||
A special SoundFont is maintained by the synthesizer, corresponding to
|
|
||||||
user-defined presets, built from user-provided samples. These presets
|
|
||||||
are defined using the 'loadSample' and 'loadSoundMember'
|
|
||||||
functions. This user SoundFont is inserted in the SoundFont stack at
|
|
||||||
the first call of one of these functions. All subsequent calls to one
|
|
||||||
of these functions will insert the created preset in the same user
|
|
||||||
SoundFont.
|
|
||||||
|
|
||||||
- Channels
|
|
||||||
|
|
||||||
According to the MIDI format and protocol, the synthesizer has a fixed
|
|
||||||
number of channels. This is the maximum number of presets that may be
|
|
||||||
playing at the same time. At all times, one given channel plays at
|
|
||||||
most one preset (but can play many notes from this preset). A preset
|
|
||||||
is associated to a channel using the 'programChange' function. The
|
|
||||||
number of channels of the synthesizer is defined at init time using
|
|
||||||
the 'new' function. A given preset may be associated to many
|
|
||||||
channels.
|
|
||||||
|
|
||||||
|
|
||||||
-----------------
|
|
||||||
-- Initialization
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
new(Xtra"iiwusynth")
|
|
||||||
new(Xtra"iiwusynth", plist initParams)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : iiwuObj = new(Xtra "iiwusynth", [#channels: 32])
|
|
||||||
|
|
||||||
Creates a new instance of the iiwu Xtra.
|
|
||||||
The optional initParams argument is a propert-list
|
|
||||||
of key-value paris defining initialization parameters.
|
|
||||||
|
|
||||||
Only one key is currently defined:
|
|
||||||
|
|
||||||
- #channels : int > 0 : the number of channels allocated by the
|
|
||||||
synthesizer. If omitted, the default number of channels is set to 64.
|
|
||||||
Version 2.0.1 : This is not implemented yet : the number of channels is always 64.
|
|
||||||
|
|
||||||
To destroy the synthesizer, simply set its variable to VOID.
|
|
||||||
|
|
||||||
Returns an error code (unable to create, hardware error, bad arguments).
|
|
||||||
|
|
||||||
|
|
||||||
getChannelsCount(object me)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : nbchan = getChannelsCount(iiwuObj)
|
|
||||||
|
|
||||||
Returns the number of channels of the synthesizer. By default, there are 64 channels.
|
|
||||||
|
|
||||||
Returns an error code (no synth).
|
|
||||||
|
|
||||||
|
|
||||||
getMasterGain(object me)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : gain = getMasterGain(iiwuObj)
|
|
||||||
|
|
||||||
Returns the master gain of the synthesizer. By default the gain is 1.0 (full volume).
|
|
||||||
The gain is between 0.0 and 2.0.
|
|
||||||
Returns an error code (no synth).
|
|
||||||
|
|
||||||
|
|
||||||
setMasterGain(object me, float gain)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : setMasterGain(iiwuObj, 0.5)
|
|
||||||
|
|
||||||
Sets the master gain of the synthesizer. By default the gain is 1.0.
|
|
||||||
The gain should between 0.0 and 2.0.
|
|
||||||
Gains superior to 1.0 (full volume, no attenuation) should be handled
|
|
||||||
carefully, as distortion may happen.
|
|
||||||
Returns an error code (no synth).
|
|
||||||
|
|
||||||
|
|
||||||
-----------------
|
|
||||||
-- Reverb/Chorus
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
setReverb(object me)
|
|
||||||
setReverb(object me, int onOrOff)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : setReverb(iiwuObj)
|
|
||||||
ex : setReverb(iiwuObj, FALSE)
|
|
||||||
|
|
||||||
Sets the reverb module of the synthesizer on or off.
|
|
||||||
|
|
||||||
Returns an error code (no synth, bad arguments).
|
|
||||||
|
|
||||||
|
|
||||||
getReverb(object me)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : revOn = getReverb(iiwuObj)
|
|
||||||
|
|
||||||
Return 1 of the reverb module is on (default), 0 if it is off.
|
|
||||||
|
|
||||||
Returns an error code (no synth, bad arguments).
|
|
||||||
|
|
||||||
|
|
||||||
setReverbProp(object me, symbol prop, float value)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : setReverbProp(iiwuObj, #roomsize, 0.9)
|
|
||||||
ex : setReverbProp(iiwuObj, #level, 0.2)
|
|
||||||
|
|
||||||
Sets the value of a property of the reverb module.
|
|
||||||
Accepted properties are:
|
|
||||||
- #level : the level of the reverb (0.0 = no reverb, 1.0 = full reverb).
|
|
||||||
- #roomsize : the size of the room. 0.0 means a very small room, 1.0 means outerspace.
|
|
||||||
- #width : the spatial width of the reverb. 0.0 means a very narrow reverb, 1.0 a wide one.
|
|
||||||
- #damping : how much power is lost at each reverberation. 1.0 : mostly lost, 0.0 : full reverberation.
|
|
||||||
|
|
||||||
All values are between 0.0 and 1.0.
|
|
||||||
|
|
||||||
Returns an error code (no synth, bad arguments).
|
|
||||||
|
|
||||||
|
|
||||||
getReverbProp(object me, symbol prop)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : roomsiz = getReverbProp(iiwuObj, #roomsize)
|
|
||||||
ex : damping = getReverbProp(iiwuObj, #damping)
|
|
||||||
|
|
||||||
Returns the float value of the given property of the reverb module. Accepted properties are listed above.
|
|
||||||
Returns an error code (no synth, bad arguments).
|
|
||||||
|
|
||||||
|
|
||||||
setChorus(object me)
|
|
||||||
setChorus(object me, int onOrOff)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : setChorus(iiwuObj)
|
|
||||||
ex : setChorus(iiwuObj, FALSE)
|
|
||||||
|
|
||||||
Sets the chorus module of the synthesizer on or off.
|
|
||||||
|
|
||||||
Returns an error code (no synth, bad arguments).
|
|
||||||
|
|
||||||
|
|
||||||
getChorus(object me)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : chorusOn = getChorus(iiwuObj)
|
|
||||||
|
|
||||||
Return 1 of the chorus module is on (default), 0 if it is off.
|
|
||||||
|
|
||||||
Returns an error code (no synth, bad arguments).
|
|
||||||
|
|
||||||
|
|
||||||
setChorusProp(object me, symbol prop, float value)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : setChorusProp(iiwuObj, #number, 3)
|
|
||||||
ex : setChorusProp(iiwuObj, #level, 0.2)
|
|
||||||
|
|
||||||
Sets the value of a property of the chorus module.
|
|
||||||
Accepted properties are:
|
|
||||||
- #level : the level of the chorus. Min is 0.0, maximum accepted is 10.0.
|
|
||||||
- #number : the number of secondary voices. Maximum is 99.
|
|
||||||
- #modulation : the amplitude of the frequency modulation, in Hz. Min is 0.29, max is 5.0.
|
|
||||||
- #delay : the maximum delay between secondary voices, in ms. Min is 0, maximum is 100.0.
|
|
||||||
|
|
||||||
Returns an error code (no synth, bad arguments).
|
|
||||||
|
|
||||||
|
|
||||||
getChorusProp(object me, symbol prop)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : number = getChorusProp(iiwuObj, #number)
|
|
||||||
ex : modulation = getChorusProp(iiwuObj, #modulation)
|
|
||||||
|
|
||||||
Returns the float value of the given property of the chorus module. Accepted properties are listed above.
|
|
||||||
Returns an error code (no synth, bad arguments).
|
|
||||||
|
|
||||||
-----------------
|
|
||||||
-- Sound Data
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
loadSoundFont(object me, string filePath)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : soundFontID = loadSoundFont(iiwuObj, the moviePath & "MySoundFonts.sf2")
|
|
||||||
|
|
||||||
Loads a SoundFonts file in the synthesizer and places it at the top of
|
|
||||||
the SoundFont stack : it will be the first searched when looking for a
|
|
||||||
preset.
|
|
||||||
|
|
||||||
The path is absolute, and should be expressed with the local file
|
|
||||||
system conventions.
|
|
||||||
|
|
||||||
The loaded soundFont will be of type #file.
|
|
||||||
|
|
||||||
Returns an ID, an interger uniquely idenfiying the SoundFont in the stack, or an error
|
|
||||||
code (no synth, no file, bad file format, not enough
|
|
||||||
memory).
|
|
||||||
|
|
||||||
|
|
||||||
createSoundFont(object me)
|
|
||||||
createSoundFont(object me, string name)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : soundFontID = createSoundFont(iiwuObj)
|
|
||||||
ex : soundFontID = createSoundFont(iiwuObj, "mysoundfont in memory")
|
|
||||||
|
|
||||||
Creates an empty SoundFont in memory (type #ram) and places it at the top of
|
|
||||||
the SoundFont stack : it will be the first searched when looking for a
|
|
||||||
preset.
|
|
||||||
|
|
||||||
The returned soundFontID can be used in the #soundFont property of the loadSampleFile or
|
|
||||||
loadSampleMember functions.
|
|
||||||
|
|
||||||
The created soundFont can be removed from memory, using the unloadSoundFont function.
|
|
||||||
|
|
||||||
The 'name' argument is optional and defaults to EMPTY (the empty string).
|
|
||||||
|
|
||||||
Returns an ID, an interger uniquely idenfiying the SoundFont in the stack, or an error
|
|
||||||
code (no synth, no file, bad file format, not enough
|
|
||||||
memory).
|
|
||||||
|
|
||||||
|
|
||||||
unloadSoundFont(object me)
|
|
||||||
unloadSoundFont(object me, int soundFontID)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : unloadSoundFont(iiwuObj, 1)
|
|
||||||
ex : unloadSoundFont(iiwuObj)
|
|
||||||
|
|
||||||
Unloads the SoundFont of the given ID of the SoundFont stack.
|
|
||||||
The soundFontID argument is optional, and defaults to the first searched
|
|
||||||
SoundFont (the last loaded).
|
|
||||||
|
|
||||||
All types of soundFonts can be unloaded from memory.
|
|
||||||
|
|
||||||
Returns an error code (no synth, bad ID).
|
|
||||||
|
|
||||||
|
|
||||||
reloadSoundFont(object me)
|
|
||||||
reloadSoundFont(object me, int soundFontID)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : reloadSoundFont(iiwuObj, 1)
|
|
||||||
ex : reloadSoundFont(iiwuObj)
|
|
||||||
|
|
||||||
Version 2.0.1 : Not Yet Implemented.
|
|
||||||
|
|
||||||
Reloads the SoundFont of the given ID of the SoundFont stack.
|
|
||||||
The soundFontID argument is optional, and defaults to the first searched
|
|
||||||
SoundFont (the last loaded).
|
|
||||||
This is useful when a soundFont is known to have changed outside
|
|
||||||
the synthesizer.
|
|
||||||
Only SoundFonts of type #file can be reloaded.
|
|
||||||
|
|
||||||
Returns an error code (no synth, bad ID).
|
|
||||||
|
|
||||||
|
|
||||||
getSoundFontsStack(object me)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : aList = getSoundFontsStack(iiwuObj)
|
|
||||||
|
|
||||||
Returns a lingo list containing all the IDs of the SoundFonts in the stack, sorted from first searched to last searched (last loded to first loaded).
|
|
||||||
May return an error code (no synth).
|
|
||||||
|
|
||||||
|
|
||||||
getSoundFontInfo(object me)
|
|
||||||
getSoundFontInfo(object me, int soundFontID)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : nam = getSoundFontInfo(iiwuObj)
|
|
||||||
|
|
||||||
Returns information about the SoundFont of the given soundFontID of the SoundFont stack.
|
|
||||||
The soundFontID argument is optional, and defaults to the first searched
|
|
||||||
SoundFont (the last loaded).
|
|
||||||
|
|
||||||
The information is returned in the form of a lingo property-list,
|
|
||||||
containing key-value pairs describing the SoundFont: its name, and
|
|
||||||
info about its presets (name, preset number and bank number):
|
|
||||||
|
|
||||||
[
|
|
||||||
#name:"SoundFontName",
|
|
||||||
#type:#file or #ram,
|
|
||||||
#presets:
|
|
||||||
[
|
|
||||||
[#name:"preset1Name", #bank:preset1bankNb, #number:preset1Number],
|
|
||||||
[#name:"preset2Name", #bank:preset2bankNb, #number:preset2Number],
|
|
||||||
...
|
|
||||||
]
|
|
||||||
]
|
|
||||||
(More information may be added in future versions of the Xtra)
|
|
||||||
|
|
||||||
Can return an error code (no synth, bad soundFontID).
|
|
||||||
|
|
||||||
|
|
||||||
loadSampleFile(object me, string filePath, plist presetInfo)
|
|
||||||
loadSampleFile(object me, string filePath, int number)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Version 2.0.1 : Not Yet Implemented.
|
|
||||||
|
|
||||||
ex : loadSampleFile(iiwuObj, the moviePath&"MidPiano.aiff",
|
|
||||||
[#name:"piano", #bank:0, #number:1, #rootKey:60, #keyrange:[50, 70]])
|
|
||||||
|
|
||||||
ex : loadSampleFile(iiwuObj, the moviePath&"Piano.aiff", [#number:2])
|
|
||||||
ex : loadSampleFile(iiwuObj, the moviePath&"Piano.aiff", 2)
|
|
||||||
|
|
||||||
Loads a sample file from filePath and creates a preset from it. This
|
|
||||||
is a simple way of creating a preset from a specific sample.
|
|
||||||
|
|
||||||
The 'filePath' is absolute, and should be expressed with the local
|
|
||||||
file system conventions. Accepted sample formats are : wav, and aiff.
|
|
||||||
|
|
||||||
The preset is added to a ram (memory) SoundFont (type #ram).
|
|
||||||
This SoundFont is either specified using the #soundFont property (see below),
|
|
||||||
either the highest soundfont in the stack is used if it is a ram soundfont,
|
|
||||||
either it is created on the fly in memory, in which case it is placed at the top
|
|
||||||
of the SoundFont stack of the synthesizer.
|
|
||||||
|
|
||||||
The preset is created according to the 'presetInfo'
|
|
||||||
lingo property-list. This plist is a combination of key-value pairs
|
|
||||||
describing the preset. The following keys are taken into account:
|
|
||||||
|
|
||||||
- #number : integer, mandatory. Between 0 and 127 : the preset number in the bank
|
|
||||||
If this preset number (and bank if set) was already in use, the sample is added
|
|
||||||
to the already preset samples of this preset. This is a handy way to define
|
|
||||||
a keyRange for a preset.
|
|
||||||
|
|
||||||
- #bank : integer, optional (default = 0). Between 0 and 16383: the number
|
|
||||||
of the MIDI bank that will be assigned to the preset.
|
|
||||||
|
|
||||||
- #soundFont : integer, optional. Specifies the soundFontID of the SoundFont
|
|
||||||
in which the preset will be created. If specified, it should be a
|
|
||||||
soundFont of type #ram. If not specified, the first soundfont on the stack
|
|
||||||
will be used if it is of type #ram. If not, a new SoundFont
|
|
||||||
is created on the fly.
|
|
||||||
|
|
||||||
- #name : string : the name of the preset - optional
|
|
||||||
|
|
||||||
- #rootKey : int between 0 and 127 : the key at which the sample will
|
|
||||||
be played 'as is' - optional : if omitted, 60 (middle-C) is used.
|
|
||||||
|
|
||||||
- #keyRangeStart : integer, optional (default = 0).
|
|
||||||
Defines the first of two keys between which the sample should be
|
|
||||||
played (the comparison is inclusive).
|
|
||||||
If a noteon asks for a key outside this range, the sample is not
|
|
||||||
played. If omitted, the whole range [0, 127] is used.
|
|
||||||
|
|
||||||
- #keyRangeEnd : integer, optional (default = 127). See keyRangeStart.
|
|
||||||
|
|
||||||
- #loop : boolean, optional. Specifies that the sample should be played
|
|
||||||
as a loop. Default is FALSE.
|
|
||||||
|
|
||||||
- #attack : float, optional (default = 0.0). Duration, in milliseconds of
|
|
||||||
the attack phase of the sound, i.e. the time for the sample to reach peak volume
|
|
||||||
after a noteon is issued.
|
|
||||||
|
|
||||||
- #decay : float, optional (default = 0.0). Duration, in milliseconds of
|
|
||||||
the decay phase of the sound, i.e. the time for the sample to reach sustain volume
|
|
||||||
after the attack.
|
|
||||||
|
|
||||||
- #sustainlevel : float, optional (default = 0.0). Level, in dB relative to the
|
|
||||||
peak level, of the sustain level. A sustainlevel of 0 means the same level than
|
|
||||||
the peak level. A sustainlevel of 10 mean peak-10dB, which is very low.
|
|
||||||
A sustainlevel of 100dB conventionnaly means full attenuation.
|
|
||||||
|
|
||||||
- #release : float, optional (default = 0.0). Duration, in milliseconds of
|
|
||||||
the release phase of the sound, i.e. the time for the sample to reach silence
|
|
||||||
after a noteoff.
|
|
||||||
|
|
||||||
Note that in one preset, two different samples may be used for two
|
|
||||||
different key ranges.
|
|
||||||
|
|
||||||
example :
|
|
||||||
|
|
||||||
loadSampleFile(iiwuObj, the moviePath&"PianoLo.aiff", [#name:"piano",
|
|
||||||
#bank:0, #number:1, #rootKey:30, #keyrange:[0, 60]])
|
|
||||||
|
|
||||||
loadSampleFile(iiwuObj, the moviePath&"PianoHi.aiff", [#name:"piano",
|
|
||||||
#bank:0, #number:1, #rootKey:90, #keyrange:[61, 127]])
|
|
||||||
|
|
||||||
This example defines two samples for two ranges of the same preset.
|
|
||||||
|
|
||||||
A preset may not be unloaded ; only the whole user SoundFont may be
|
|
||||||
unloaded from the SoundFont stack, using the 'unloadSoundFont'
|
|
||||||
function.
|
|
||||||
|
|
||||||
Returns the soundFontID of the SoundFont to which the sample has been added,
|
|
||||||
or an error code (no synth, no file, bad file format, not enough
|
|
||||||
memory, bad bank number, bad preset number, bad key).
|
|
||||||
|
|
||||||
|
|
||||||
loadSampleMember(object me, obj member, plist presetInfo)
|
|
||||||
-----------------
|
|
||||||
ex : loadSampleMember(iiwuObj, member(3), [#name:"piano", #bank:0, #number:1])
|
|
||||||
|
|
||||||
Version 2.0.1 : Not Yet Implemented.
|
|
||||||
|
|
||||||
Loads a sample from the given Director sound cast member and creates a
|
|
||||||
preset from it.
|
|
||||||
|
|
||||||
The preset is created according to the 'presetInfo' lingo
|
|
||||||
property-list. (see above for details on the presetInfo format).
|
|
||||||
|
|
||||||
Returns the soundFontID of the SoundFont to which the sample has been added,
|
|
||||||
or an error code (no synth, no member, bad member format, not enough memory,
|
|
||||||
bad bank number, bad preset number, bad key, not a user soundFont).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------
|
|
||||||
-- Event sequencer
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
- Sequencer
|
|
||||||
|
|
||||||
The API functions for playing music is derived from the MIDI
|
|
||||||
protocol. This protocol defines the use of a number of presets for
|
|
||||||
every synthesizer object. Application communicate with a preset in a
|
|
||||||
synthesizer over a channel. The communication is event based (MIDI
|
|
||||||
events). The API of the music functions (discussed below) can be
|
|
||||||
thought of as sending an event over a channel to a preset.
|
|
||||||
|
|
||||||
The music API functions includes the optional use of a sequencer. A
|
|
||||||
sequencer is an object that assures the delivery of an event at a
|
|
||||||
future time. To use the sequencer object, the events have to specify a
|
|
||||||
time property. Two property key values can be used:
|
|
||||||
|
|
||||||
- #date: specifies the time on an absolute time axis (the creation
|
|
||||||
time of the synthesizer is used as time zero),
|
|
||||||
|
|
||||||
- #delay: specifies the time relative to the sequencer's current time.
|
|
||||||
|
|
||||||
The time is measured in 'ticks'. A 'tick' is an arbitrary unit that
|
|
||||||
can be set by the application. See the sequencer API below.
|
|
||||||
|
|
||||||
- Event destinations
|
|
||||||
|
|
||||||
The API includes the use of several synthesizer objects. The property
|
|
||||||
key '#dest' indicates the destination of the event. The destination
|
|
||||||
value is the name of the synthesizer object. If no destination is
|
|
||||||
specified, the event will be sent to the first known destination which
|
|
||||||
is the default synthesizer.
|
|
||||||
|
|
||||||
- Event sources
|
|
||||||
|
|
||||||
Events can also specify an event source. This is useful when the
|
|
||||||
sender of an event wants to cancel some time the event later. To use
|
|
||||||
this feature, the event simply contains the key '#source' with a string
|
|
||||||
as value.
|
|
||||||
|
|
||||||
- Callbacks
|
|
||||||
|
|
||||||
Lingo objects can ask the sequencer to schedule a callback function at
|
|
||||||
a precise time. The callback is the name of a Lingo handler. The movie
|
|
||||||
will have to call the poll function of the xtra regularly to receive
|
|
||||||
the callbacks.
|
|
||||||
|
|
||||||
|
|
||||||
- Sequencer API
|
|
||||||
|
|
||||||
The following functions address the sequencer directly.
|
|
||||||
|
|
||||||
setTimeUnit(object me, float ticksPerSecond)
|
|
||||||
-----------------
|
|
||||||
ex. setTimeUnit(iiwuObj, 10.0) -- 1 tick equals 100 millisecond
|
|
||||||
|
|
||||||
Sets the tick unit of the sequencer.
|
|
||||||
ticksPerSecond is a float > 0.
|
|
||||||
|
|
||||||
Returns a error indication (bad initialization).
|
|
||||||
|
|
||||||
|
|
||||||
getTimeUnit(object me)
|
|
||||||
-----------------
|
|
||||||
ex. getTimeUnit(iiwuObj)
|
|
||||||
|
|
||||||
Returns the tick unit of the sequencer or an error indication (bad
|
|
||||||
initialization).
|
|
||||||
By default, the tick value is 1000.0 (1 tick = 1 millisecond).
|
|
||||||
|
|
||||||
|
|
||||||
getTime(object me)
|
|
||||||
-----------------
|
|
||||||
ex. getTime(iiwuObj)
|
|
||||||
|
|
||||||
Returns the time in tick from the start of the synthesizer, in tick units
|
|
||||||
of the sequencer or an error indication (bad initialization).
|
|
||||||
|
|
||||||
|
|
||||||
getDestinations(object me)
|
|
||||||
-----------------
|
|
||||||
ex. getDestinations(iiwuObj)
|
|
||||||
|
|
||||||
Returns a list with all the names of possible event destinations.
|
|
||||||
|
|
||||||
|
|
||||||
removeEvents(object me, plist filter)
|
|
||||||
-----------------
|
|
||||||
ex. removeEvents(iiwuObj, [#dest: "iiwusynth", #source: "DrumMachine"])
|
|
||||||
ex. removeEvents(iiwuObj, [#source: "DrumMachine", #type: #note])
|
|
||||||
|
|
||||||
Removes events that are queued for sending in the sequencer. The
|
|
||||||
function takes property list as argument. The property list defines
|
|
||||||
the events that should be filtered. Currently, any of the three
|
|
||||||
following keys can be used:
|
|
||||||
|
|
||||||
- #source: remove the events for the specified source (if not specified,
|
|
||||||
removes all source)
|
|
||||||
- #dest: remove the events sent by the specified destination (if not
|
|
||||||
specified, removes all destination)
|
|
||||||
- #type: remove the events according to this event type (if not
|
|
||||||
specified, removes all events)
|
|
||||||
|
|
||||||
Possible event types are:
|
|
||||||
|
|
||||||
- #note
|
|
||||||
- #noteon
|
|
||||||
- #noteoff
|
|
||||||
- #allsoundsoff
|
|
||||||
- #allnotesoff
|
|
||||||
- #programchange
|
|
||||||
- #controlchange: includes all controlChange events (pitchbend, modulation,
|
|
||||||
sustain, pan, volume, reverbsend, chorussend)
|
|
||||||
- #pitchbend
|
|
||||||
- #modulation
|
|
||||||
- #sustain
|
|
||||||
- #pan
|
|
||||||
- #volume
|
|
||||||
- #reverbsend
|
|
||||||
- #chorussend
|
|
||||||
- #callback
|
|
||||||
|
|
||||||
Returns an error code (no sequencer, invalid argument).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
scheduleCallback(object me, plist callbackInfo)
|
|
||||||
-----------------
|
|
||||||
ex. scheduleCallback(iiwuObj, [#delay: 1200, #handler:"updateDrumMachine", #args: [1,2,3]])
|
|
||||||
ex. scheduleCallback(iiwuObj, [#delay: 1200, #source:"drumMachine", #handler:"updateDrumMachine"])
|
|
||||||
|
|
||||||
Schedules a callback event. When the event is reached, the lingo handler defined in
|
|
||||||
the 'callbackInfo' propertylist is called back. Note that the callback happens during
|
|
||||||
idle time.
|
|
||||||
|
|
||||||
The 'callbackInfo' propertylist has the following possible properties:
|
|
||||||
|
|
||||||
- #handler, string, mandatory. It is the lingo handler that will be called.
|
|
||||||
- #args, a lingo list, optional. This list contains the arguments of the handler.
|
|
||||||
The scheduleCallback function retains a pointer to this list but does not copy it,
|
|
||||||
according to the lingo tradition. The lingo eventually executed is equivalent
|
|
||||||
to "handler(args[1], args[2], ...)". If the first argument is a child object,
|
|
||||||
the corresponding handler will be called on that object.
|
|
||||||
- #delay/#date : integer, optional. Specifies the time of the callback. Defaults to #delay:0
|
|
||||||
- #source : optional. Specifies the source. Useful for removing callbacks.
|
|
||||||
- #dest : optional. Specifies the destination.
|
|
||||||
|
|
||||||
To remove a scheduled callback, use removeEvents:
|
|
||||||
ex : removeEvents(iiwuObj, [#source: "drumMachine", #type: #callback])
|
|
||||||
ex : removeEvents(iiwuObj, [#type: #callback])
|
|
||||||
|
|
||||||
Returns an error code (no sequencer, invalid argument).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------
|
|
||||||
-- Playing music
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Most event functions accept an optional property list that describes
|
|
||||||
the sequencing of the event. This is indicated by the 'seq' argument
|
|
||||||
in the functions below. The following properties are recognized:
|
|
||||||
|
|
||||||
- #date: the absolute time of the event
|
|
||||||
- #delay: the time of the event relative to current time
|
|
||||||
- #source: the event source
|
|
||||||
- #dest: the event destination
|
|
||||||
|
|
||||||
#date and #delay should not be specified together.
|
|
||||||
|
|
||||||
|
|
||||||
programChange(object me, int channel, plist presetInfo, plist seq)
|
|
||||||
programChange(object me, int channel, int presetNumber, plist seq)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : programChange(iiwuObj, 1, [#bank:1, #number:3])
|
|
||||||
ex : programChange(iiwuObj, 1, 3)
|
|
||||||
|
|
||||||
Assigns the given preset of the SoundFont stack to the given channel
|
|
||||||
of the synthethizer.
|
|
||||||
|
|
||||||
The preset may be defined using a property-list defining its MIDI bank
|
|
||||||
number and its preset number, using specific keys:
|
|
||||||
|
|
||||||
- #bank : int between 0 and 16383: the number of the MIDI bank of the
|
|
||||||
preset - optional : if #bank is not present, 0 is the default.
|
|
||||||
|
|
||||||
- #number : int between 0 and 127 : the preset number in the bank -
|
|
||||||
mandatory.
|
|
||||||
|
|
||||||
The preset may also be defined by a unique int, wich is treated as
|
|
||||||
the preset number, assuming that the MIDI bank is 0 (zero).
|
|
||||||
|
|
||||||
In all cases, the preset is looked up in the SoundFont stack, starting
|
|
||||||
from the first SoundFont up the stack, until a corresponding preset is
|
|
||||||
found. The found preset is assigned to the channel. The maximum
|
|
||||||
number of channels are defined using the 'new' method.
|
|
||||||
|
|
||||||
If a preset was previoulsy assigned to that channel, it is forgotten,
|
|
||||||
but all noteon finish normally.
|
|
||||||
|
|
||||||
Returns an error code (no synth, no SoundFonts, bad
|
|
||||||
channel, bad preset definition).
|
|
||||||
|
|
||||||
|
|
||||||
getProgram(object me, int channel)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : getProgram(iiwuObj, 0)
|
|
||||||
|
|
||||||
Returns information about the preset assigned to the channel. The
|
|
||||||
information is of the form : [#name:"presetName", #bank:presetBankNb,
|
|
||||||
#number:presetNumber] The maximum number of channels are defined using
|
|
||||||
the 'new' method.
|
|
||||||
|
|
||||||
May return an error code (no synth, no preset
|
|
||||||
assigned, bad channel number)
|
|
||||||
|
|
||||||
|
|
||||||
note(object me, int channel, int key, float vel, int dur, plist seq)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : note(iiwuObj, 1, 38, 1.0, 1000)
|
|
||||||
|
|
||||||
Plays a note, using the specified 'channel' and 'key', with the
|
|
||||||
specified velocity (strength). The duration 'dur' of the note is
|
|
||||||
specified in ticks.
|
|
||||||
|
|
||||||
0.0 <= vel <= 1.0 (automatically limited if smaller or greater)
|
|
||||||
0 <= key <= 127
|
|
||||||
|
|
||||||
The maximum number of channels are defined using the 'new' method.
|
|
||||||
|
|
||||||
Returns an error code (no synth, no SoundFonts, bad
|
|
||||||
channel, no preset assigned, bad key, bad vel).
|
|
||||||
|
|
||||||
|
|
||||||
noteon(object me, int channel, int key, float vel, plist seq)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : noteon(iiwuObj, 1, 38, 1.0)
|
|
||||||
|
|
||||||
Starts a note, using the specified 'channel' and 'key', with the
|
|
||||||
specified velocity (strength).
|
|
||||||
|
|
||||||
0.0 <= vel <= 1.0 (automatically limited if smaller or greater)
|
|
||||||
0 <= key <= 127
|
|
||||||
|
|
||||||
The maximum number of channels are defined using the 'new' method.
|
|
||||||
|
|
||||||
Returns an error code (no synth, no SoundFonts, bad
|
|
||||||
channel, no preset assigned, bad key, bad vel).
|
|
||||||
|
|
||||||
|
|
||||||
noteoff(object me, int channel, int key, plist seq)
|
|
||||||
-----------------
|
|
||||||
ex : noteoff(iiwuObj, 1)
|
|
||||||
|
|
||||||
Stops all playing notes on the given 'channel' and the given 'key'.
|
|
||||||
|
|
||||||
The maximum number of channels are defined using the 'new' method.
|
|
||||||
|
|
||||||
Returns an error code (no synth, no SoundFonts, bad
|
|
||||||
channel, no preset assigned).
|
|
||||||
|
|
||||||
|
|
||||||
controlChange(object me, int channel, plist controlParams, plist seq)
|
|
||||||
-----------------
|
|
||||||
ex:
|
|
||||||
controlChange(obj, 1, [#pitchbend: 1.0, #sustain:1])
|
|
||||||
controlChange(obj, 1, [#pan: -0.2, #sustain:1, #volume:0.5, #reverbsend:1.0])
|
|
||||||
|
|
||||||
This function allow to change some control parameters of the given
|
|
||||||
channel. The effect is immediate and allows for continuous
|
|
||||||
modification of a sound.
|
|
||||||
|
|
||||||
The 'controlParams' is a lingo property-list of key-value pairs, with
|
|
||||||
the key describing the control parameter to affect, and the value
|
|
||||||
specifying the amount of the change. For some keys, the value is not
|
|
||||||
taken into account. The list of currently implemented keys is the
|
|
||||||
following :
|
|
||||||
|
|
||||||
- #pitchbend: float between -1.0 an 1.0: sets the pitchbend level
|
|
||||||
0.0 means no pitchbend.
|
|
||||||
The pitchrange may be configured in the preset (for
|
|
||||||
loaded SoundFonts), but the default range corresponds to 4 steps
|
|
||||||
(one step = one semi-tone), so a value of -1.0 means two steps down,
|
|
||||||
and a value of 1.0 means two steps up.
|
|
||||||
|
|
||||||
- #pan: float between -1.0 an 1.0: sets the pan level.
|
|
||||||
-1.0 is all sound on left channel, 1.0 is all sound on right channel
|
|
||||||
0.0 is sound in center.
|
|
||||||
|
|
||||||
- #volume: float between 0.0 an 1.0: sets the volume level.
|
|
||||||
0.0 is silence, 1.0 is full volume.
|
|
||||||
|
|
||||||
- #reverbsend: float between 0.0 an 1.0. Sets the volume of the auxiliary
|
|
||||||
output send to the reverb module. 0.0 is no signal, 1.0 is full
|
|
||||||
volume.
|
|
||||||
|
|
||||||
- #chorussend: float between 0.0 an 1.0. Sets the volume of the auxiliary
|
|
||||||
output send to the chorus module. 0.0 is no signal, 1.0 is full
|
|
||||||
volume.
|
|
||||||
|
|
||||||
- #sustain: int = 0 or 1: sets or removes the sustain from the
|
|
||||||
channel.
|
|
||||||
|
|
||||||
- #modulation: float between 0.0 an 1.0. Amplitude modulation.
|
|
||||||
Vibrato.
|
|
||||||
|
|
||||||
Returns an error code (no synth, no SoundFonts, bad
|
|
||||||
channel, no preset assigned, unknown controlKey, bad controlValue).
|
|
||||||
|
|
||||||
|
|
||||||
getControl(object me, int channel, symbol ctrl)
|
|
||||||
-----------------
|
|
||||||
ex. getControl(iiwuObj, 1, #volume)
|
|
||||||
|
|
||||||
Returns the value of a controller.
|
|
||||||
|
|
||||||
|
|
||||||
getControls(object me, int channel)
|
|
||||||
-----------------
|
|
||||||
ex. getControls(iiwuObj, 1)
|
|
||||||
|
|
||||||
Returns a property list with the values of all controllers in the
|
|
||||||
form: [#pan: 0.4, #sustain:1, #volume:0.8, #reverbsend:1.0, ...]
|
|
||||||
|
|
||||||
|
|
||||||
allsoundsoff(object me, int channel, plist seq)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Instantly stops all sound on the channel. The optional argument is
|
|
||||||
the sequencer information. Returns an error code (no sequencer).
|
|
||||||
|
|
||||||
|
|
||||||
allnotesoff(object me, int channel, plist seq)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Sends a noteoff to all currently playing notes on the channel. The
|
|
||||||
optional argument is the sequencer information. Returns an error code
|
|
||||||
(no sequencer).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---------------------------
|
|
||||||
-- Debug and maintenance
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
debug(object me, string logFile)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
ex : debug(iiwuObj, the moviePath&"logfile")
|
|
||||||
|
|
||||||
Sets the debug mode of the Xtra.
|
|
||||||
If a logFile is provided, debug is set.
|
|
||||||
If VOID is provided, debug is turned off.
|
|
||||||
If debug is on, a log of all actions and errors is written in the logFile.
|
|
||||||
|
|
||||||
Returns an error code (cannot open/create log file).
|
|
||||||
|
|
||||||
|
|
||||||
getError(object me)
|
|
||||||
-----------------
|
|
||||||
ex : lastError = getError(iiwuObj)
|
|
||||||
|
|
||||||
returns a human readable string describing the last error that occured
|
|
||||||
in the Xtra.
|
|
||||||
|
|
||||||
|
|
||||||
getCPUUsage(object me)
|
|
||||||
-----------------
|
|
||||||
ex : aPercent = getCPUusage(iiwuObj)
|
|
||||||
|
|
||||||
returns a float representing the estimation of the percentage of CPU
|
|
||||||
used by the synthesizer, or an error code (no synth).
|
|
||||||
|
|
||||||
-------------------------------
|
|
||||||
-------------------------------
|
|
||||||
-------------------------------
|
|
|
@ -141,6 +141,7 @@ new_fluid_opensles_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth
|
||||||
goto error_recovery;
|
goto error_recovery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
SLDataLocator_AndroidSimpleBufferQueue loc_buffer_queue =
|
SLDataLocator_AndroidSimpleBufferQueue loc_buffer_queue =
|
||||||
{
|
{
|
||||||
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
||||||
|
@ -174,6 +175,7 @@ new_fluid_opensles_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth
|
||||||
const SLboolean req1[] = {SL_BOOLEAN_TRUE};
|
const SLboolean req1[] = {SL_BOOLEAN_TRUE};
|
||||||
result = (*engine_interface)->CreateAudioPlayer(engine_interface,
|
result = (*engine_interface)->CreateAudioPlayer(engine_interface,
|
||||||
&(dev->audio_player), &audio_src, &audio_sink, 1, ids1, req1);
|
&(dev->audio_player), &audio_src, &audio_sink, 1, ids1, req1);
|
||||||
|
}
|
||||||
|
|
||||||
if(result != SL_RESULT_SUCCESS)
|
if(result != SL_RESULT_SUCCESS)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,7 +22,10 @@ static void fluid_conversion_config(void)
|
||||||
|
|
||||||
for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++)
|
for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++)
|
||||||
{
|
{
|
||||||
fluid_ct2hz_tab[i] = powl(2.0, (double) i / 1200.0);
|
// 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
|
/* centibels to amplitude conversion
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -49,7 +49,6 @@ typedef enum
|
||||||
*/
|
*/
|
||||||
fluid_chorus_t *new_fluid_chorus(fluid_real_t sample_rate);
|
fluid_chorus_t *new_fluid_chorus(fluid_real_t sample_rate);
|
||||||
void delete_fluid_chorus(fluid_chorus_t *chorus);
|
void delete_fluid_chorus(fluid_chorus_t *chorus);
|
||||||
int fluid_chorus_init(fluid_chorus_t *chorus);
|
|
||||||
void fluid_chorus_reset(fluid_chorus_t *chorus);
|
void fluid_chorus_reset(fluid_chorus_t *chorus);
|
||||||
|
|
||||||
void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
|
void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
|
||||||
|
|
|
@ -609,7 +609,7 @@ fluid_sample_set_sound_data(fluid_sample_t *sample,
|
||||||
goto error_rec;
|
goto error_rec;
|
||||||
}
|
}
|
||||||
|
|
||||||
FLUID_MEMSET(sample->data, 0, storedNbFrames);
|
FLUID_MEMSET(sample->data, 0, storedNbFrames * sizeof(short));
|
||||||
FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short));
|
FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short));
|
||||||
|
|
||||||
if(data24 != NULL)
|
if(data24 != NULL)
|
||||||
|
@ -628,7 +628,7 @@ fluid_sample_set_sound_data(fluid_sample_t *sample,
|
||||||
/* pointers */
|
/* pointers */
|
||||||
/* all from the start of data */
|
/* all from the start of data */
|
||||||
sample->start = SAMPLE_LOOP_MARGIN;
|
sample->start = SAMPLE_LOOP_MARGIN;
|
||||||
sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames - 1;
|
sample->end = SAMPLE_LOOP_MARGIN + nbframes - 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -198,7 +198,7 @@ void fluid_synth_settings(fluid_settings_t *settings)
|
||||||
fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED);
|
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_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.level", FLUID_CHORUS_DEFAULT_LEVEL, 0.0f, 10.0f, 0);
|
||||||
fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.29f, 5.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_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.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED);
|
||||||
|
@ -1129,6 +1129,10 @@ fluid_synth_error(fluid_synth_t *synth)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a note-on event to a FluidSynth object.
|
* 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 synth FluidSynth instance
|
||||||
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
||||||
* @param key MIDI note number (0-127)
|
* @param key MIDI note number (0-127)
|
||||||
|
@ -3384,6 +3388,8 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
|
||||||
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
|
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
|
||||||
fluid_return_val_if_fail(left != 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(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 */
|
/* First, take what's still available in the buffer */
|
||||||
count = 0;
|
count = 0;
|
||||||
|
@ -3668,6 +3674,8 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[],
|
||||||
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
|
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
|
||||||
fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED);
|
fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED);
|
||||||
fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED);
|
fluid_return_val_if_fail(nout % 2 == 0, 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
|
||||||
|
|
||||||
nfxchan = synth->effects_channels;
|
nfxchan = synth->effects_channels;
|
||||||
nfxunits = synth->effects_groups;
|
nfxunits = synth->effects_groups;
|
||||||
|
@ -3800,41 +3808,83 @@ fluid_synth_write_float(fluid_synth_t *synth, int len,
|
||||||
void *lout, int loff, int lincr,
|
void *lout, int loff, int lincr,
|
||||||
void *rout, int roff, int rincr)
|
void *rout, int roff, int rincr)
|
||||||
{
|
{
|
||||||
int i, j, k, l;
|
return fluid_synth_write_float_LOCAL(synth, len, lout, loff, lincr, rout, roff, rincr, fluid_synth_render_blocks);
|
||||||
float *left_out = (float *) lout;
|
}
|
||||||
float *right_out = (float *) rout;
|
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int n, cur, size;
|
||||||
|
float *left_out = (float *) lout + loff;
|
||||||
|
float *right_out = (float *) rout + roff;
|
||||||
fluid_real_t *left_in;
|
fluid_real_t *left_in;
|
||||||
fluid_real_t *right_in;
|
fluid_real_t *right_in;
|
||||||
double time = fluid_utime();
|
double time = fluid_utime();
|
||||||
float cpu_load;
|
float cpu_load;
|
||||||
|
|
||||||
fluid_profile_ref_var(prof_ref);
|
|
||||||
|
|
||||||
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
|
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
|
||||||
fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
|
fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
|
||||||
fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
|
fluid_return_val_if_fail(rout != 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
|
||||||
|
|
||||||
|
fluid_profile_ref_var(prof_ref);
|
||||||
|
|
||||||
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
|
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
|
||||||
l = synth->cur;
|
|
||||||
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
||||||
|
|
||||||
for(i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr)
|
size = len;
|
||||||
|
cur = synth->cur;
|
||||||
|
|
||||||
|
do
|
||||||
{
|
{
|
||||||
/* fill up the buffers as needed */
|
/* fill up the buffers as needed */
|
||||||
if(l >= synth->curmax)
|
if(cur >= synth->curmax)
|
||||||
{
|
{
|
||||||
int blocksleft = (len - i + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
|
int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
|
||||||
synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
|
synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft);
|
||||||
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
||||||
|
cur = 0;
|
||||||
l = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
left_out[j] = (float) left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l];
|
/* calculate amount of available samples */
|
||||||
right_out[k] = (float) right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l];
|
n = synth->curmax - cur;
|
||||||
|
|
||||||
|
/* keep track of emitted samples */
|
||||||
|
if(n > size)
|
||||||
|
{
|
||||||
|
n = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
synth->cur = l;
|
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
|
||||||
|
{
|
||||||
|
*left_out = (float) left_in[n];
|
||||||
|
*right_out = (float) right_in[n];
|
||||||
|
|
||||||
|
left_out += lincr;
|
||||||
|
right_out += rincr;
|
||||||
|
}
|
||||||
|
while(++n < 0);
|
||||||
|
}
|
||||||
|
while(size);
|
||||||
|
|
||||||
|
synth->cur = cur;
|
||||||
|
|
||||||
time = fluid_utime() - time;
|
time = fluid_utime() - time;
|
||||||
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
|
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
|
||||||
|
@ -3926,43 +3976,77 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
|
||||||
void *lout, int loff, int lincr,
|
void *lout, int loff, int lincr,
|
||||||
void *rout, int roff, int rincr)
|
void *rout, int roff, int rincr)
|
||||||
{
|
{
|
||||||
int i, j, k, cur;
|
int di, n, cur, size;
|
||||||
int16_t *left_out = lout;
|
int16_t *left_out = (int16_t *)lout + loff;
|
||||||
int16_t *right_out = rout;
|
int16_t *right_out = (int16_t *)rout + roff;
|
||||||
fluid_real_t *left_in;
|
fluid_real_t *left_in;
|
||||||
fluid_real_t *right_in;
|
fluid_real_t *right_in;
|
||||||
double time = fluid_utime();
|
double time = fluid_utime();
|
||||||
int di;
|
|
||||||
float cpu_load;
|
float cpu_load;
|
||||||
|
|
||||||
|
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
|
||||||
|
fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
|
||||||
|
fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
|
||||||
|
fluid_return_val_if_fail(len >= 0, FLUID_FAILED);
|
||||||
|
fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below
|
||||||
|
|
||||||
fluid_profile_ref_var(prof_ref);
|
fluid_profile_ref_var(prof_ref);
|
||||||
|
|
||||||
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
|
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
|
||||||
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
||||||
|
|
||||||
|
size = len;
|
||||||
cur = synth->cur;
|
cur = synth->cur;
|
||||||
di = synth->dither_index;
|
di = synth->dither_index;
|
||||||
|
|
||||||
for(i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr)
|
do
|
||||||
{
|
{
|
||||||
|
|
||||||
/* fill up the buffers as needed */
|
/* fill up the buffers as needed */
|
||||||
if(cur >= synth->curmax)
|
if(cur >= synth->curmax)
|
||||||
{
|
{
|
||||||
int blocksleft = (len - i + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
|
int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
|
||||||
synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
|
synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
|
||||||
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
||||||
cur = 0;
|
cur = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
left_out[j] = round_clip_to_i16(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]);
|
/* calculate amount of available samples */
|
||||||
right_out[k] = round_clip_to_i16(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]);
|
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
|
||||||
|
{
|
||||||
|
*left_out = round_clip_to_i16(left_in[n] * 32766.0f + rand_table[0][di]);
|
||||||
|
*right_out = round_clip_to_i16(right_in[n] * 32766.0f + rand_table[1][di]);
|
||||||
|
|
||||||
|
left_out += lincr;
|
||||||
|
right_out += rincr;
|
||||||
|
|
||||||
if(++di >= DITHER_SIZE)
|
if(++di >= DITHER_SIZE)
|
||||||
{
|
{
|
||||||
di = 0;
|
di = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
while(++n < 0);
|
||||||
|
}
|
||||||
|
while(size);
|
||||||
|
|
||||||
synth->cur = cur;
|
synth->cur = cur;
|
||||||
synth->dither_index = di; /* keep dither buffer continous */
|
synth->dither_index = di; /* keep dither buffer continous */
|
||||||
|
@ -5187,7 +5271,7 @@ fluid_synth_set_chorus_on(fluid_synth_t *synth, int on)
|
||||||
* @param nr Chorus voice count (0-99, CPU time consumption proportional to
|
* @param nr Chorus voice count (0-99, CPU time consumption proportional to
|
||||||
* this value)
|
* this value)
|
||||||
* @param level Chorus level (0.0-10.0)
|
* @param level Chorus level (0.0-10.0)
|
||||||
* @param speed Chorus speed in Hz (0.29-5.0)
|
* @param speed Chorus speed in Hz (0.1-5.0)
|
||||||
* @param depth_ms Chorus depth (max value depends on synth sample rate,
|
* @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)
|
* 0.0-21.0 is safe for sample rate values up to 96KHz)
|
||||||
* @param type Chorus waveform type (#fluid_chorus_mod)
|
* @param type Chorus waveform type (#fluid_chorus_mod)
|
||||||
|
@ -5245,19 +5329,6 @@ int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type)
|
||||||
return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_TYPE, 0, 0, 0, 0, type);
|
return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_TYPE, 0, 0, 0, 0, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set one or more chorus parameters.
|
|
||||||
* @param synth FluidSynth instance
|
|
||||||
* @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t)
|
|
||||||
* @param nr Chorus voice count (0-99, CPU time consumption proportional to
|
|
||||||
* this value)
|
|
||||||
* @param level Chorus level (0.0-10.0)
|
|
||||||
* @param speed Chorus speed in Hz (0.29-5.0)
|
|
||||||
* @param depth_ms Chorus depth (max value depends on synth sample rate,
|
|
||||||
* 0.0-21.0 is safe for sample rate values up to 96KHz)
|
|
||||||
* @param type Chorus waveform type (#fluid_chorus_mod)
|
|
||||||
* @return #FLUID_OK on success, #FLUID_FAILED otherwise
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level,
|
fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level,
|
||||||
double speed, double depth_ms, int type)
|
double speed, double depth_ms, int type)
|
||||||
|
@ -5314,7 +5385,7 @@ fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level,
|
||||||
/**
|
/**
|
||||||
* Get chorus voice number (delay line count) value.
|
* Get chorus voice number (delay line count) value.
|
||||||
* @param synth FluidSynth instance
|
* @param synth FluidSynth instance
|
||||||
* @return Chorus voice count (0-99)
|
* @return Chorus voice count
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
fluid_synth_get_chorus_nr(fluid_synth_t *synth)
|
fluid_synth_get_chorus_nr(fluid_synth_t *synth)
|
||||||
|
@ -5330,7 +5401,7 @@ fluid_synth_get_chorus_nr(fluid_synth_t *synth)
|
||||||
/**
|
/**
|
||||||
* Get chorus level.
|
* Get chorus level.
|
||||||
* @param synth FluidSynth instance
|
* @param synth FluidSynth instance
|
||||||
* @return Chorus level value (0.0-10.0)
|
* @return Chorus level value
|
||||||
*/
|
*/
|
||||||
double
|
double
|
||||||
fluid_synth_get_chorus_level(fluid_synth_t *synth)
|
fluid_synth_get_chorus_level(fluid_synth_t *synth)
|
||||||
|
@ -5346,7 +5417,7 @@ fluid_synth_get_chorus_level(fluid_synth_t *synth)
|
||||||
/**
|
/**
|
||||||
* Get chorus speed in Hz.
|
* Get chorus speed in Hz.
|
||||||
* @param synth FluidSynth instance
|
* @param synth FluidSynth instance
|
||||||
* @return Chorus speed in Hz (0.29-5.0)
|
* @return Chorus speed in Hz
|
||||||
*/
|
*/
|
||||||
double
|
double
|
||||||
fluid_synth_get_chorus_speed(fluid_synth_t *synth)
|
fluid_synth_get_chorus_speed(fluid_synth_t *synth)
|
||||||
|
|
|
@ -216,6 +216,11 @@ int fluid_synth_set_gen2(fluid_synth_t *synth, int chan,
|
||||||
int
|
int
|
||||||
fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[],
|
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 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
|
* misc
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -846,7 +846,7 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
|
||||||
voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj;
|
voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = (fluid_ct2hz(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate));
|
x = (fluid_ct2hz_real(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -859,7 +859,7 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
|
||||||
voice->root_pitch = 0;
|
voice->root_pitch = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = fluid_ct2hz(voice->root_pitch);
|
x = fluid_ct2hz_real(voice->root_pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */
|
/* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */
|
||||||
|
|
|
@ -23,66 +23,56 @@
|
||||||
#include "fluid_conv_tables.c"
|
#include "fluid_conv_tables.c"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fluid_ct2hz
|
* 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_real_t
|
||||||
fluid_ct2hz_real(fluid_real_t cents)
|
fluid_ct2hz_real(fluid_real_t cents)
|
||||||
{
|
{
|
||||||
if(cents < 0)
|
if(FLUID_UNLIKELY(cents < 0))
|
||||||
{
|
{
|
||||||
return (fluid_real_t) 1.0;
|
return (fluid_real_t) 1.0;
|
||||||
}
|
}
|
||||||
else if(cents < 900)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int)(cents + 300)];
|
|
||||||
}
|
|
||||||
else if(cents < 2100)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int)(cents - 900)];
|
|
||||||
}
|
|
||||||
else if(cents < 3300)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int)(cents - 2100)];
|
|
||||||
}
|
|
||||||
else if(cents < 4500)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int)(cents - 3300)];
|
|
||||||
}
|
|
||||||
else if(cents < 5700)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int)(cents - 4500)];
|
|
||||||
}
|
|
||||||
else if(cents < 6900)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int)(cents - 5700)];
|
|
||||||
}
|
|
||||||
else if(cents < 8100)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int)(cents - 6900)];
|
|
||||||
}
|
|
||||||
else if(cents < 9300)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int)(cents - 8100)];
|
|
||||||
}
|
|
||||||
else if(cents < 10500)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int)(cents - 9300)];
|
|
||||||
}
|
|
||||||
else if(cents < 11700)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int)(cents - 10500)];
|
|
||||||
}
|
|
||||||
else if(cents < 12900)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int)(cents - 11700)];
|
|
||||||
}
|
|
||||||
else if(cents < 14100)
|
|
||||||
{
|
|
||||||
return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int)(cents - 12900)];
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return (fluid_real_t) 1.0; /* some loony trying to make you deaf */
|
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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -313,22 +313,21 @@ void fluid_msleep(unsigned int msecs)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get time in milliseconds to be used in relative timing operations.
|
* Get time in milliseconds to be used in relative timing operations.
|
||||||
* @return Unix time in milliseconds.
|
* @return Monotonic time in milliseconds.
|
||||||
*/
|
*/
|
||||||
unsigned int fluid_curtime(void)
|
unsigned int fluid_curtime(void)
|
||||||
{
|
{
|
||||||
static glong initial_seconds = 0;
|
float now;
|
||||||
GTimeVal timeval;
|
static float initial_time = 0;
|
||||||
|
|
||||||
if(initial_seconds == 0)
|
if(initial_time == 0)
|
||||||
{
|
{
|
||||||
g_get_current_time(&timeval);
|
initial_time = (float)fluid_utime();
|
||||||
initial_seconds = timeval.tv_sec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_get_current_time(&timeval);
|
now = (float)fluid_utime();
|
||||||
|
|
||||||
return (unsigned int)((timeval.tv_sec - initial_seconds) * 1000.0 + timeval.tv_usec / 1000.0);
|
return (unsigned int)((now - initial_time) / 1000.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -124,6 +124,8 @@ typedef gint32 int32_t;
|
||||||
typedef guint32 uint32_t;
|
typedef guint32 uint32_t;
|
||||||
typedef gint64 int64_t;
|
typedef gint64 int64_t;
|
||||||
typedef guint64 uint64_t;
|
typedef guint64 uint64_t;
|
||||||
|
typedef guintptr uintptr_t;
|
||||||
|
typedef gintptr intptr_t;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -167,10 +169,14 @@ typedef guint64 uint64_t;
|
||||||
|
|
||||||
|
|
||||||
#define FLUID_INLINE inline
|
#define FLUID_INLINE inline
|
||||||
#define FLUID_POINTER_TO_UINT GPOINTER_TO_UINT
|
|
||||||
#define FLUID_UINT_TO_POINTER GUINT_TO_POINTER
|
/* Integer<->pointer conversion */
|
||||||
#define FLUID_POINTER_TO_INT GPOINTER_TO_INT
|
#define FLUID_POINTER_TO_UINT(x) ((unsigned int)(uintptr_t)(x))
|
||||||
#define FLUID_INT_TO_POINTER GINT_TO_POINTER
|
#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_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN)
|
||||||
|
|
||||||
#define FLUID_LE32TOH(x) GINT32_FROM_LE(x)
|
#define FLUID_LE32TOH(x) GINT32_FROM_LE(x)
|
||||||
|
@ -197,6 +203,11 @@ char *fluid_strtok(char **str, char *delim);
|
||||||
typedef int socklen_t;
|
typedef int socklen_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
Time functions
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
unsigned int fluid_curtime(void);
|
unsigned int fluid_curtime(void);
|
||||||
double fluid_utime(void);
|
double fluid_utime(void);
|
||||||
|
|
||||||
|
@ -739,6 +750,4 @@ static FLUID_INLINE void *fluid_align_ptr(const void *ptr, unsigned int alignmen
|
||||||
|
|
||||||
#define FLUID_DEFAULT_ALIGNMENT (64U)
|
#define FLUID_DEFAULT_ALIGNMENT (64U)
|
||||||
|
|
||||||
void* fluid_alloc(size_t len);
|
|
||||||
|
|
||||||
#endif /* _FLUID_SYS_H */
|
#endif /* _FLUID_SYS_H */
|
||||||
|
|
|
@ -188,6 +188,8 @@ typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t pa
|
||||||
#define FLUID_ARRAY_ALIGNED(_t,_n,_a) (_t*)FLUID_MALLOC((_n)*sizeof(_t) + ((unsigned int)_a - 1u))
|
#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)
|
#define FLUID_ARRAY(_t,_n) FLUID_ARRAY_ALIGNED(_t,_n,1u)
|
||||||
|
|
||||||
|
void* fluid_alloc(size_t len);
|
||||||
|
|
||||||
/* File access */
|
/* File access */
|
||||||
#define FLUID_FOPEN(_f,_m) fopen(_f,_m)
|
#define FLUID_FOPEN(_f,_m) fopen(_f,_m)
|
||||||
#define FLUID_FCLOSE(_f) fclose(_f)
|
#define FLUID_FCLOSE(_f) fclose(_f)
|
||||||
|
|
|
@ -24,6 +24,7 @@ ADD_FLUID_TEST(test_fluid_zone_check_mod)
|
||||||
ADD_FLUID_TEST(test_fluid_voice_add_mod)
|
ADD_FLUID_TEST(test_fluid_voice_add_mod)
|
||||||
|
|
||||||
ADD_FLUID_TEST(test_synth_process)
|
ADD_FLUID_TEST(test_synth_process)
|
||||||
|
ADD_FLUID_TEST(test_ct2hz)
|
||||||
|
|
||||||
# if ( LIBSNDFILE_HASVORBIS )
|
# if ( LIBSNDFILE_HASVORBIS )
|
||||||
# ADD_FLUID_TEST(test_sf3_sfont_loading)
|
# ADD_FLUID_TEST(test_sf3_sfont_loading)
|
||||||
|
|
40
test/test_ct2hz.c
Normal file
40
test/test_ct2hz.c
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
#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 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;
|
||||||
|
}
|
|
@ -8,18 +8,17 @@
|
||||||
// static const int CHANNELS=16;
|
// static const int CHANNELS=16;
|
||||||
enum { SAMPLES=1024 };
|
enum { SAMPLES=1024 };
|
||||||
|
|
||||||
|
static int smpl;
|
||||||
|
|
||||||
int render_one_mock(fluid_synth_t *synth, int blocks)
|
int render_one_mock(fluid_synth_t *synth, int blocks)
|
||||||
{
|
{
|
||||||
static int smpl;
|
|
||||||
|
|
||||||
fluid_real_t *left_in, *fx_left_in;
|
fluid_real_t *left_in, *fx_left_in;
|
||||||
fluid_real_t *right_in, *fx_right_in;
|
fluid_real_t *right_in, *fx_right_in;
|
||||||
|
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
int nfxchan = fluid_synth_count_effects_channels(synth),
|
int naudchan = fluid_synth_count_audio_channels(synth);
|
||||||
nfxunits = fluid_synth_count_effects_groups(synth),
|
|
||||||
naudchan = fluid_synth_count_audio_channels(synth);
|
|
||||||
|
|
||||||
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
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);
|
fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);
|
||||||
|
@ -37,27 +36,61 @@ int render_one_mock(fluid_synth_t *synth, int blocks)
|
||||||
return blocks;
|
return blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
int render_and_check(fluid_synth_t* synth, int number_of_samples, int offset)
|
int process_and_check(fluid_synth_t* synth, int number_of_samples, int offset)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
float left[SAMPLES], right[SAMPLES];
|
float left[SAMPLES], right[SAMPLES];
|
||||||
float *dry[1 * 2];
|
float *dry[1 * 2];
|
||||||
dry[0] = left;
|
dry[0] = left;
|
||||||
dry[1] = right;
|
dry[1] = right;
|
||||||
memset(left, 0, sizeof(left));
|
FLUID_MEMSET(left, 0, sizeof(left));
|
||||||
memset(right, 0, sizeof(right));
|
FLUID_MEMSET(right, 0, sizeof(right));
|
||||||
|
|
||||||
TEST_SUCCESS(fluid_synth_process_LOCAL(synth, number_of_samples, 0, NULL, 2, dry, render_one_mock));
|
TEST_SUCCESS(fluid_synth_process_LOCAL(synth, number_of_samples, 0, NULL, 2, dry, render_one_mock));
|
||||||
|
|
||||||
for(i=0; i<number_of_samples; i++)
|
for(i=0; i<number_of_samples; i++)
|
||||||
{
|
{
|
||||||
TEST_ASSERT(left[i]==i+offset);
|
TEST_ASSERT(left[i]==offset);
|
||||||
TEST_ASSERT(right[i]==i+offset);
|
TEST_ASSERT(right[i]==offset);
|
||||||
|
offset++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return i+offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int write_and_check(fluid_synth_t* synth, int number_of_samples, int offset)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float buf[2*SAMPLES];
|
||||||
|
FLUID_MEMSET(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
// one buffer, interleaved writing
|
||||||
|
TEST_SUCCESS(fluid_synth_write_float_LOCAL(synth, number_of_samples, buf, 0, 2, buf, 1, 2, render_one_mock));
|
||||||
|
|
||||||
|
for(i=0; i< 2*number_of_samples; i+=2)
|
||||||
|
{
|
||||||
|
TEST_ASSERT(buf[i+0] == offset);
|
||||||
|
TEST_ASSERT(buf[i+1] == offset);
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
FLUID_MEMSET(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
// "two" buffers, planar writing
|
||||||
|
TEST_SUCCESS(fluid_synth_write_float_LOCAL(synth, number_of_samples, buf, 0, 1, buf, SAMPLES, 1, render_one_mock));
|
||||||
|
|
||||||
|
for(i=0; i< number_of_samples; i++)
|
||||||
|
{
|
||||||
|
TEST_ASSERT(buf[i+0] == offset);
|
||||||
|
TEST_ASSERT(buf[i+SAMPLES] == offset);
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// this test should make sure that sample rate changed are handled correctly
|
// this test should make sure that sample rate changed are handled correctly
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
@ -66,19 +99,32 @@ int main(void)
|
||||||
fluid_settings_t *settings = new_fluid_settings();
|
fluid_settings_t *settings = new_fluid_settings();
|
||||||
TEST_ASSERT(settings != NULL);
|
TEST_ASSERT(settings != NULL);
|
||||||
|
|
||||||
// TEST_SUCCESS(fluid_settings_setint(settings, "synth.audio-channels", CHANNELS));
|
|
||||||
// TEST_SUCCESS(fluid_settings_setint(settings, "synth.audio-groups", CHANNELS));
|
|
||||||
|
|
||||||
synth = new_fluid_synth(settings);
|
synth = new_fluid_synth(settings);
|
||||||
TEST_ASSERT(synth != NULL);
|
TEST_ASSERT(synth != NULL);
|
||||||
|
|
||||||
off = render_and_check(synth, 100, off);
|
off = process_and_check(synth, 100, off);
|
||||||
off = render_and_check(synth, 200, off);
|
off = process_and_check(synth, 200, off);
|
||||||
off = render_and_check(synth, 300, off);
|
off = process_and_check(synth, 300, off);
|
||||||
off = render_and_check(synth, 1000, off);
|
// next statement should not result in a call to render_one_mock() and therefore not increase the static "smpl" var
|
||||||
off = render_and_check(synth, 900, off);
|
off = process_and_check(synth, 0, off);
|
||||||
off = render_and_check(synth, 800, off);
|
off = process_and_check(synth, 1000, off);
|
||||||
|
off = process_and_check(synth, SAMPLES, off);
|
||||||
|
off = process_and_check(synth, 900, off);
|
||||||
|
off = process_and_check(synth, 800, off);
|
||||||
|
off = process_and_check(synth, FLUID_BUFSIZE, off);
|
||||||
|
|
||||||
|
// currently it is not possible to call different rendering functions subsequently
|
||||||
|
off = smpl;
|
||||||
|
|
||||||
|
off = write_and_check(synth, 100, off);
|
||||||
|
off = write_and_check(synth, 200, off);
|
||||||
|
off = write_and_check(synth, 300, off);
|
||||||
|
off = write_and_check(synth, 0, off);
|
||||||
|
off = write_and_check(synth, 1000, off);
|
||||||
|
off = write_and_check(synth, SAMPLES, off);
|
||||||
|
off = write_and_check(synth, 900, off);
|
||||||
|
off = write_and_check(synth, 800, off);
|
||||||
|
off = write_and_check(synth, FLUID_BUFSIZE, off);
|
||||||
|
|
||||||
delete_fluid_synth(synth);
|
delete_fluid_synth(synth);
|
||||||
delete_fluid_settings(settings);
|
delete_fluid_settings(settings);
|
||||||
|
|
Loading…
Reference in a new issue