Merge branch 'master' into linked-modulators

This commit is contained in:
jjceresa 2019-10-23 18:05:12 +02:00
commit d234f70ab5
30 changed files with 1232 additions and 2401 deletions

24
.azure-pipelines-mac.yml Normal file
View 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'

View file

@ -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
View 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

View file

@ -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

View file

@ -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:

View file

@ -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
View file

@ -1,2 +0,0 @@
Check the web site at http://www.fluidsynth.org for the latest news.

View file

@ -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
View file

@ -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
View file

@ -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

View file

@ -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) ../../../.. && \

View file

@ -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>

View file

@ -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 &mdash; 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 &lt;fluidsynth.h&gt;
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 &lt;fluidsynth.h&gt;
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 &amp; 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>

View file

@ -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?

View file

@ -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).
-------------------------------
-------------------------------
-------------------------------

View file

@ -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)
{ {

View file

@ -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

View file

@ -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,

View file

@ -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
{ {

View file

@ -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)

View file

@ -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
*/ */

View file

@ -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 */

View file

@ -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];
} }
} }

View file

@ -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);
} }
/** /**

View file

@ -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 */

View file

@ -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)

View file

@ -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
View 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;
}

View file

@ -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);