Merge branch 'develop'
This commit is contained in:
commit
65580beadb
308 changed files with 27372 additions and 8396 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -19,6 +19,8 @@ compile_commands.json
|
|||
clients/sfizz_jack
|
||||
clients/sfzprint
|
||||
|
||||
/vst/download/
|
||||
|
||||
# gh-pages unstaged files:
|
||||
_api/
|
||||
_site/
|
||||
|
|
@ -32,3 +34,5 @@ node_modules/
|
|||
*.lock
|
||||
*.sublime-*
|
||||
*.code-*
|
||||
.kak.tags.namecache
|
||||
.clangd
|
||||
|
|
|
|||
7
.gitmodules
vendored
7
.gitmodules
vendored
|
|
@ -1,18 +1,19 @@
|
|||
[submodule "external/abseil-cpp"]
|
||||
path = external/abseil-cpp
|
||||
url = https://github.com/abseil/abseil-cpp
|
||||
branch = lts_2020_02_25
|
||||
branch = lts_2020_02_25
|
||||
shallow = true
|
||||
[submodule "vst/external/VST_SDK/VST3_SDK/base"]
|
||||
path = vst/external/VST_SDK/VST3_SDK/base
|
||||
url = https://github.com/steinbergmedia/vst3_base.git
|
||||
shallow = true
|
||||
[submodule "vst/external/VST_SDK/VST3_SDK/pluginterfaces"]
|
||||
path = vst/external/VST_SDK/VST3_SDK/pluginterfaces
|
||||
url = https://github.com/steinbergmedia/vst3_pluginterfaces.git
|
||||
url = https://github.com/sfztools/vst3_pluginterfaces.git
|
||||
shallow = true
|
||||
[submodule "vst/external/VST_SDK/VST3_SDK/public.sdk"]
|
||||
path = vst/external/VST_SDK/VST3_SDK/public.sdk
|
||||
url = https://github.com/steinbergmedia/vst3_public_sdk.git
|
||||
url = https://github.com/sfztools/vst3_public_sdk.git
|
||||
shallow = true
|
||||
[submodule "vst/external/VST_SDK/VST3_SDK/vstgui4"]
|
||||
path = vst/external/VST_SDK/VST3_SDK/vstgui4
|
||||
|
|
|
|||
25
.travis.yml
25
.travis.yml
|
|
@ -2,6 +2,10 @@ language: cpp
|
|||
os: linux
|
||||
dist: bionic
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vst/download/
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: "clang-tidy checks"
|
||||
|
|
@ -27,7 +31,7 @@ jobs:
|
|||
script: .travis/script_test.sh
|
||||
|
||||
- name: "Linux arm64 tests"
|
||||
arch: amd64
|
||||
arch: arm64
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
|
|
@ -129,7 +133,7 @@ jobs:
|
|||
- INSTALL_DIR="sfizz-${TRAVIS_BRANCH}-${TRAVIS_OS_NAME}-${TRAVIS_CPU_ARCH}"
|
||||
install: .travis/install_osx.sh
|
||||
script: .travis/script_osx.sh
|
||||
after_success: .travis/prepare_tarball.sh
|
||||
after_success: .travis/prepare_osx.sh
|
||||
|
||||
- name: "MOD devices arm"
|
||||
stage: "Build"
|
||||
|
|
@ -174,23 +178,12 @@ after_failure:
|
|||
- bash ${TRAVIS_BUILD_DIR}/.travis/discord_webhook.sh failure
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
skip_cleanup: true
|
||||
file: "${INSTALL_DIR}.tar.gz"
|
||||
edge: true
|
||||
provider: releases
|
||||
file: "${INSTALL_DIR}.tar.gz"
|
||||
token:
|
||||
secure: jBIMWMPva8OazC8Wnqc2vqATa3exjvGDGvejEu6fgwt/BMDkgJCsJ7gS3ITpYFuO0HoHtLGfTVrFcZIE+agGVRdRGRni5xZRp5Vuf9hvLf8hCTaxkS/HYoliYYPp9FLY6J4yq9mp/R/QyGrwgssQEjhYlI2GL4+lJzt750AcmYibnQo2AMe5SSOp3q8KdLBG1vca1o1BZWBhW/S7l2hA1aJ7y1ytwm0ETIdrTZHxgfz7bqdqQcw/Ytqjm1rgzty6iQKX3B/InaZNB6CqXy6FoGLf771lery8nWJbjTjKYD4QK2NelExldyp1vrdHGvknWguFk+8vlQ16Dt0+R7Byr/LOPRLTCf/T+IehMQGtVA9/gjrkH8LqHy9oVFB+33p11gGu3h6hvg7yB6z3sSck0u4FHjrlpNd5XmmCsKQVQ9vI1cPPkVkbMHIewc7uOu+bD6cmotFj1vJ9UYesvN6n4siyCiPOIhgV9++bjcyiLqX3DWP5UWyZ9/VT8bz5VcxUdJnEYtdNPx4x5pRW4081VUjIf2EmHhQTrjb1iz6FAGwNU/fpefA8x+bBxXP9MkFFgU0tbCDKw2y6o0GOXX82mZr/IB0/DIBg7UllTzCBKTuiJQV1prFPlZLc6V22H5ozAXHgu3E+qNjvHoMsoYjGssX3+AYHwu8isBINo/gGo6Y=
|
||||
on:
|
||||
repo: "sfztools/sfizz"
|
||||
tags: true
|
||||
condition: ${TRAVIS_TAG} =~ ^v?[0-9]
|
||||
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "ircs://chat.freenode.net:7070/#sfztools"
|
||||
|
||||
template:
|
||||
- "[%{repository_name}:%{branch}] Build #%{build_number}: %{message}"
|
||||
- "%{author} : %{commit_subject}"
|
||||
- "Commit URL : https://github.com/%{repository_slug}/commit/%{commit}"
|
||||
- "Build Details : %{build_url}"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
set -ex
|
||||
. .travis/docker_container.sh
|
||||
|
||||
buildenv_as_root pacman -Sqy --noconfirm
|
||||
buildenv_as_root pacman -Sqyu --noconfirm
|
||||
buildenv_as_root pacman -Sq --noconfirm base-devel wget mingw-w64-cmake mingw-w64-gcc mingw-w64-pkg-config mingw-w64-libsndfile
|
||||
buildenv i686-w64-mingw32-gcc -v && buildenv i686-w64-mingw32-g++ -v && buildenv i686-w64-mingw32-cmake --version
|
||||
|
|
|
|||
|
|
@ -5,4 +5,6 @@ set -ex
|
|||
sudo ln -s /usr/local /opt/local
|
||||
brew update
|
||||
brew upgrade cmake
|
||||
brew install python || brew link --overwrite python
|
||||
brew install jack
|
||||
brew install dylibbundler
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
diff -Nru VST_SDK.orig/VST3_SDK/pluginterfaces/base/funknown.cpp VST_SDK/VST3_SDK/pluginterfaces/base/funknown.cpp
|
||||
--- VST_SDK.orig/VST3_SDK/pluginterfaces/base/funknown.cpp 2019-12-02 13:58:41.000000000 +0100
|
||||
+++ VST_SDK/VST3_SDK/pluginterfaces/base/funknown.cpp 2020-03-10 22:02:26.220911670 +0100
|
||||
@@ -84,7 +84,7 @@
|
||||
int32 PLUGIN_API atomicAdd (int32& var, int32 d)
|
||||
{
|
||||
#if SMTG_OS_WINDOWS
|
||||
- return InterlockedExchangeAdd (&var, d) + d;
|
||||
+ return InterlockedExchangeAdd ((LONG*)&var, d) + d;
|
||||
#elif SMTG_OS_MACOS
|
||||
#if SMTG_MACOS_USE_STDATOMIC
|
||||
return atomic_fetch_add (reinterpret_cast<atomic_int_least32_t*> (&var), d) + d;
|
||||
39
.travis/prepare_osx.sh
Executable file
39
.travis/prepare_osx.sh
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
test -z "$CONTAINER"
|
||||
|
||||
if ! [ -z "$CONTAINER" ]; then
|
||||
. .travis/docker_container.sh
|
||||
else
|
||||
. .travis/no_container.sh
|
||||
fi
|
||||
|
||||
cd build
|
||||
buildenv make DESTDIR=${PWD}/${INSTALL_DIR} install
|
||||
|
||||
# Bundle LV2 dependencies
|
||||
cd "${INSTALL_DIR}"/Library/Audio/Plug-Ins/LV2
|
||||
dylibbundler -od -b -x sfizz.lv2/sfizz.so -d sfizz.lv2/libs/ -p @loader_path/libs/
|
||||
cd "${TRAVIS_BUILD_DIR}/build"
|
||||
|
||||
# Bundle VST3 dependencies
|
||||
cd "${INSTALL_DIR}"/Library/Audio/Plug-Ins/VST3
|
||||
dylibbundler -od -b -x sfizz.vst3/Contents/MacOS/sfizz -d sfizz.vst3/Contents/libs/ -p @loader_path/../libs/
|
||||
cd "${TRAVIS_BUILD_DIR}/build"
|
||||
|
||||
# Bundle AU dependencies
|
||||
cd "${INSTALL_DIR}"/Library/Audio/Plug-Ins/Components
|
||||
dylibbundler -od -b -x sfizz.component/Contents/Resources/plugin.vst3/Contents/MacOS/sfizz -d sfizz.component/Contents/Resources/plugin.vst3/Contents/libs/ -p @loader_path/../libs/
|
||||
cd "${TRAVIS_BUILD_DIR}/build"
|
||||
|
||||
#
|
||||
tar -zcvf "${INSTALL_DIR}.tar.gz" ${INSTALL_DIR}
|
||||
|
||||
# Only release a tarball if there is a tag
|
||||
if [[ ${TRAVIS_TAG} != "" ]]; then
|
||||
mv "${INSTALL_DIR}.tar.gz" ${TRAVIS_BUILD_DIR}
|
||||
fi
|
||||
|
||||
cd "${TRAVIS_BUILD_DIR}"
|
||||
|
|
@ -8,13 +8,13 @@ else
|
|||
. .travis/no_container.sh
|
||||
fi
|
||||
|
||||
# Do not prepare a tarball without a tag
|
||||
if [[ ${TRAVIS_TAG} == "" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd build
|
||||
buildenv make DESTDIR=${PWD}/${INSTALL_DIR} install
|
||||
tar -zcvf "${INSTALL_DIR}.tar.gz" ${INSTALL_DIR}
|
||||
mv "${INSTALL_DIR}.tar.gz" ${TRAVIS_BUILD_DIR}
|
||||
|
||||
# Only release a tarball if there is a tag
|
||||
if [[ ${TRAVIS_TAG} != "" ]]; then
|
||||
mv "${INSTALL_DIR}.tar.gz" ${TRAVIS_BUILD_DIR}
|
||||
fi
|
||||
|
||||
cd ..
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ if [[ ${CROSS_COMPILE} == "mingw32" ]]; then
|
|||
-DSFIZZ_STATIC_LIBSNDFILE=ON \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
..
|
||||
buildenv patch -p1 -i "$(pwd)"/../.travis/patches/vstsdk-3.6.14-mingw-atomic-add.diff -d ../vst/external/VST_SDK
|
||||
buildenv make -j$(nproc)
|
||||
elif [[ ${CROSS_COMPILE} == "mingw64" ]]; then
|
||||
buildenv x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
|
|
@ -22,6 +21,5 @@ elif [[ ${CROSS_COMPILE} == "mingw64" ]]; then
|
|||
-DSFIZZ_STATIC_LIBSNDFILE=ON \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
..
|
||||
buildenv patch -p1 -i "$(pwd)"/../.travis/patches/vstsdk-3.6.14-mingw-atomic-add.diff -d ../vst/external/VST_SDK
|
||||
buildenv make -j$(nproc)
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -4,8 +4,12 @@ set -ex
|
|||
mkdir -p build/${INSTALL_DIR} && cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DSFIZZ_VST=ON \
|
||||
-DSFIZZ_AU=ON \
|
||||
-DSFIZZ_TESTS=OFF \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-DCMAKE_CXX_STANDARD=14 \
|
||||
-DLV2PLUGIN_INSTALL_DIR=/Library/Audio/Plug-Ins/LV2 \
|
||||
-DVSTPLUGIN_INSTALL_DIR=/Library/Audio/Plug-Ins/VST3 \
|
||||
-DAUPLUGIN_INSTALL_DIR=/Library/Audio/Plug-Ins/Components \
|
||||
..
|
||||
make -j$(sysctl -n hw.ncpu)
|
||||
# Xcode not currently supported, see https://gitlab.kitware.com/cmake/cmake/issues/18088
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ set -x # No fail, we need to go back to the original branch at the end
|
|||
|
||||
mkdir build && cd build && cmake -DSFIZZ_JACK=OFF -DSFIZZ_SHARED=OFF -DSFIZZ_LV2=OFF .. && cd ..
|
||||
doxygen Doxyfile
|
||||
./doxygen/scripts/generate_api_index.sh
|
||||
git fetch --depth=1 https://github.com/${TRAVIS_REPO_SLUG}.git refs/heads/gh-pages:refs/remotes/origin/gh-pages
|
||||
git checkout origin/gh-pages
|
||||
git checkout -b gh-pages
|
||||
mv _api api/${TRAVIS_TAG}
|
||||
git add api/${TRAVIS_TAG} && git commit -m "Release ${TRAVIS_TAG} (Travis build: ${TRAVIS_BUILD_NUMBER})"
|
||||
mv api_index.md api/index.md
|
||||
git add api && git commit -m "Release ${TRAVIS_TAG} (Travis build: ${TRAVIS_BUILD_NUMBER})"
|
||||
git remote add origin-pages https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git > /dev/null 2>&1
|
||||
git push --quiet --set-upstream origin-pages gh-pages
|
||||
git checkout ${TRAVIS_BRANCH}
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ Contributors to `sfizz`, in chronologic order:
|
|||
- Alexander Mitchell (2020)
|
||||
- Michael Willis (2020)
|
||||
- Jean-Pierre Cimalando (2020)
|
||||
- Tobiasz "unfa" Karoń (2020)
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
project (sfizz VERSION 0.3.2 LANGUAGES CXX C)
|
||||
project (sfizz VERSION 0.4.0 LANGUAGES CXX C)
|
||||
set (PROJECT_DESCRIPTION "A library to load SFZ description files and use them to render music.")
|
||||
|
||||
# External configuration CMake scripts
|
||||
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
include (BuildType)
|
||||
include (SfizzConfig)
|
||||
|
||||
# Build Options
|
||||
|
|
@ -26,14 +27,17 @@ endif()
|
|||
|
||||
option (ENABLE_LTO "Enable Link Time Optimization [default: ON]" ON)
|
||||
option (SFIZZ_JACK "Enable JACK stand-alone build [default: ON]" ON)
|
||||
option (SFIZZ_RENDER "Enable renderer of SMF files [default: ON]" ON)
|
||||
option (SFIZZ_LV2 "Enable LV2 plug-in build [default: ON]" ON)
|
||||
option (SFIZZ_VST "Enable VST plug-in build [default: OFF]" OFF)
|
||||
option (SFIZZ_AU "Enable AU plug-in build [default: OFF]" OFF)
|
||||
option (SFIZZ_BENCHMARKS "Enable benchmarks build [default: OFF]" OFF)
|
||||
option (SFIZZ_TESTS "Enable tests build [default: OFF]" OFF)
|
||||
option (SFIZZ_DEVTOOLS "Enable developer tools build [default: OFF]" OFF)
|
||||
option (SFIZZ_SHARED "Enable shared library build [default: ON]" ON)
|
||||
option (SFIZZ_USE_VCPKG "Assume that sfizz is build using vcpkg [default: OFF]" OFF)
|
||||
option (SFIZZ_STATIC_LIBSNDFILE "Link libsndfile statically [default: OFF]" OFF)
|
||||
option (SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds [default: OFF]" OFF)
|
||||
|
||||
# Don't use IPO in non Release builds
|
||||
include (CheckIPO)
|
||||
|
|
@ -45,9 +49,7 @@ add_subdirectory (external/abseil-cpp EXCLUDE_FROM_ALL)
|
|||
add_subdirectory (src)
|
||||
|
||||
# Optional targets
|
||||
if (SFIZZ_JACK)
|
||||
add_subdirectory (clients)
|
||||
endif()
|
||||
add_subdirectory (clients)
|
||||
|
||||
if (SFIZZ_LV2)
|
||||
add_subdirectory (lv2)
|
||||
|
|
@ -55,6 +57,10 @@ endif()
|
|||
|
||||
if (SFIZZ_VST)
|
||||
add_subdirectory (vst)
|
||||
else()
|
||||
if (SFIZZ_AU)
|
||||
message(WARNING "Audio Unit requires VST to be enabled")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (SFIZZ_BENCHMARKS)
|
||||
|
|
|
|||
223
CONTRIBUTING.md
Normal file
223
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
# Contributing to sfizz
|
||||
|
||||
Thanks for considering contributing!
|
||||
There is actually many things anyone can do, programming-related or music-related.
|
||||
|
||||
## Using the software for your projects
|
||||
|
||||
One of the best and probably simplest thing to do is simply to use sfizz in a number of situations.
|
||||
There is only so much we can test as far as sfz libraries go, or differents hosts for the plugin versions.
|
||||
If during the course of your usage you find out that sfizz does not behave as it should with respect to what the SFZ spec dictates, or what other players do, please file in a bug reports.
|
||||
If there is some feature that would be fantastic for you, feel free to post a feature requests in the issues.
|
||||
This will help us track and remember everything.
|
||||
Refer to (#filing-bugs-and-feature-requests) for more precision.
|
||||
|
||||
### Filing bugs and feature requests
|
||||
|
||||
When filing a bug, try to be as precise as possible.
|
||||
Obviously bugs that are easily reproducible are easier to solve.
|
||||
|
||||
- If sfizz fails to play a library correctly, please give us a link to the library and ideally an example of what you except. It helps **tremendously** if you can provide a *minimal* example in the form of a simple custom sfz library that pinpoints the problem exactly, along with instruction or midi files to render.
|
||||
- If the plugins crashes, please try to reproduce and detail everything you did up to the crash, as well as the host and sfz library. It can be really helpful to provide session files, again with a minimal example.
|
||||
- If the behavior of sfizz is clumsy and annoying, and the user experience feels subpar, please tell us and try to propose some solutions on how it could be improved. As developers, we are viewing and using sfizz in specific ways which may actually be quite far from what users would like, but we're all ears :)
|
||||
|
||||
For feature requests, provide details about the expected behavior and possibly example sfz files that will benefit from the new feature.
|
||||
Note that we aim at implementing the sfz spec as a whole, and possibly some extensions by e.g. ARIA so normally all sfz features should come at some point.
|
||||
|
||||
### Using the C or C++ library
|
||||
|
||||
Sfizz is liberally licensed and you can freely use it in any project under the term of its license and its dependencies.
|
||||
We provide a somewhat simple C and C++ API that covers our use cases within plugins mostly, and should be useful.
|
||||
The API is documented over at the [api] page.
|
||||
You can find example of using the C API in the `lv2` directory which contains sfizz's LV2 plugin.
|
||||
You can find example of using the C++ API in the `vst3` directory which contains sfizz's VST3 plugin.
|
||||
In both cases sfizz is built in the plugins as a static library.
|
||||
The project [sfizz-render] illustrates how one might use sfizz as a shared library through a `make`- and `pkg-config`-based process.
|
||||
Other projects that bundle or use sfizz are:
|
||||
|
||||
- (https://github.com/sfztools/beatbox): a live drum machine
|
||||
|
||||
If you use the library, maybe you will have specific needs and requests that could be useful to you.
|
||||
Be sure to post an issue for this so we can improve the API, especially because we have to queue API changes so it stays a bit stable over time.
|
||||
|
||||
## Developing for sfizz
|
||||
|
||||
We welcome all code contributions to sfizz, whatever your coding level.
|
||||
If there is a specific feature you would like, and you are willing to put in the time (and have some form of initial background in modern C++ code), we can mentor you and provide breadcrumbs through the codebase.
|
||||
Sfizz is still very much alpha, and we're refactoring large parts of the code on a regular basis.
|
||||
We try to keep the public API documented, and most of the internal APIs are also documented, but this can be in flux from time to time.
|
||||
If you want to add things we encourage you to ask around through an issue, on Discord, IRC, or by email, so that we may be able to guide you and discuss about how and what to implement.
|
||||
|
||||
### Handling the repository and its submodules
|
||||
|
||||
Sfizz uses a number of submodules, namely abseil and the VST sdk.
|
||||
These submodules are purely consumed by sfizz, and the maintainers will manage updating them when needed.
|
||||
As such, they should *not* pose too much issues.
|
||||
They are all pinned to specific branches or commits in their original repository.
|
||||
|
||||
To clone the sfizz repository, the recommended way is to do
|
||||
```bash
|
||||
git clone https://github.com/sfztools/sfizz.git --recursive
|
||||
# Other possibility
|
||||
git clone https://github.com/sfztools/sfizz.git
|
||||
cd sfizz
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
If after some time you happen to see something like this:
|
||||
```
|
||||
❯ git status
|
||||
On branch develop
|
||||
Your branch is up to date with 'origin/develop'.
|
||||
|
||||
Changes not staged for commit:
|
||||
(use "git add <file>..." to update what will be committed)
|
||||
(use "git checkout -- <file>..." to discard changes in working directory)
|
||||
|
||||
modified: external/abseil-cpp (new commits)
|
||||
|
||||
no changes added to commit (use "git add" and/or "git commit -a")
|
||||
```
|
||||
Then it means that the abseil submodule *on your disk* differs from the one expected by sfizz.
|
||||
This usually happens if i) you modified one of abseil's files, in which case the repository will be dirty, or ii) sfizz updated abseil's and pinned it to another commit.
|
||||
Assuming you were not adding relevant changes to abseil in order to push them upstream, you can usually resolve this issue through
|
||||
```bash
|
||||
git submodule update
|
||||
```
|
||||
**All changes in the submodule will be lost**.
|
||||
If you were in fact modifying abseil for some reason, then consider either pushing your changes upstream or find an alternative.
|
||||
Same goes for the VST SDK.
|
||||
|
||||
### Building sfizz from source
|
||||
|
||||
For details on the dependencies and platform-specific information for building sfizz, please refer to the [build] page.
|
||||
Note that for development you will probably want all dependencies, which means using debian's lingo:
|
||||
|
||||
- `cmake`
|
||||
- `g++` or `clang++`
|
||||
- `clang-format`
|
||||
- `libjack-jackd2-dev`
|
||||
- `libsndfile-dev`
|
||||
|
||||
We also recommend building [benchmark] from source and installing it as a static library.
|
||||
You will also want to configure using the `-DSFIZZ_TESTS=ON` flag and `-DSFIZZ_BENCHMARKS=ON` flag to be able to run the tests and possibly the micro-benchmarks.
|
||||
|
||||
### Forking and creating pull requests
|
||||
|
||||
We do development on the git `develop` branch, and distribute using the `master` branch.
|
||||
We mostly avoid directly committing to the `develop` branch and go through pull requests and issues for development-related discussion and choices.
|
||||
Try to have public and private discussion about development, and if such discussion happen on IRC or Discord, try to sum them up in a pull request or an issue that we can then refer to.
|
||||
Obviously, light technical questions can be asked freely in private, but the goal is to open the discussion for everyone to see.
|
||||
|
||||
To start contributing, we encourage you to fork the main sfizz repository on your personal account and clone it locally on your machine
|
||||
In the clone of your fork, and add the `sfztools` repository as a remote as follows
|
||||
```bash
|
||||
# Clone your own fork in a specific directory
|
||||
git clone https://github.com/MY_GITHUB_NAME/sfizz.git sfizz-fork
|
||||
cd sfizz-fork
|
||||
# Add the upstream sfztools remote
|
||||
git remote add upstream https://github.com/sfztools/sfizz.git
|
||||
```
|
||||
Once done, the contribution process is as follows:
|
||||
|
||||
1. Fetch the latest changes from the `upstream` remote, in particular the `develop` branch.
|
||||
2. Checkout the `upstream/develop` branch, and immediately checkout to a new local branch to start adding changes
|
||||
3. Add commits to your local branch
|
||||
4. Push the local branch to your own fork
|
||||
5. Go back to Github web interface and create a pull request
|
||||
|
||||
In command language, this translates to
|
||||
```bash
|
||||
# Fetch the latest status from upstrea
|
||||
git fetch upstream
|
||||
# Checkout the upstream develop branch
|
||||
git checkout upstream/develop
|
||||
# Create a new local branch
|
||||
git checkout -b my-new-contribution
|
||||
# Do things as usual for git: git add, git commit
|
||||
# ...
|
||||
# Once ready, push the branch to your fork. Github will tell you where to go for a pull request from there.
|
||||
git push --set-upstream origin my-new-contribution
|
||||
```
|
||||
|
||||
### Adding files to the build system
|
||||
|
||||
We use CMake for the build system.
|
||||
For the main sfizz library, the main file is `src/CMakeLists.txt`.
|
||||
In particular if you add a new `.cpp` file to sfizz, you should add it to the `SFIZZ_SOURCES` list in this file so it gets compiled and linked with the rest of the library.
|
||||
|
||||
### Coding style and guidelines
|
||||
|
||||
We have continuous integration targets down to gcc 4.9, so the code has to be fully C++11 compliant.
|
||||
Sadly this means some bonuses from C++14 are not available, although `absl` compensates many things from a standard library point of view.
|
||||
All modern C++ guidelines apply, as well as rules related to audio programming:
|
||||
|
||||
- Raw pointers are non-owning observers; use `unique_ptr` and `shared_ptr` for owning references, and `absl::optional` for nullable/optional/maybe values returned from functions.
|
||||
- Use move semantics when appropriate.
|
||||
- Try to use [RAII] wrappers and write them if necessary.
|
||||
- Avoid allocating memory, calling functions that throw, using system calls, or any launching any blocking operation in the audio thread; for sfizz, that means anything that is called at some point from `Synth::renderBlock` and `Synth::noteOn`, `noteOff`, ... and all midi events.
|
||||
- We have a helper class that helps us to track allocation in the form of the `sfz::Buffer` class and the `sfz::AudioBuffer` class, so use it for heap memory in lieu of naked `new` or `unique_ptr<T[]>` when relevant.
|
||||
- Most things in the [CppCoreGuidelines] are good to take, although be careful as many proposed things may break the *no allocation* rule.
|
||||
|
||||
We also have a `clang-tidy` CI step that may annoy you from time to time.
|
||||
|
||||
We have a `clang-format` coding style in that is loosely based on the WebKit style.
|
||||
We encourage you to use it, as well as `git-clang-format` to format your PR.
|
||||
If possible, find a plugin for your text editor than handles `.editorconfig` files. Most editors have one.
|
||||
Some notes :
|
||||
|
||||
- Class and type names start by a capital letter and follow the `PascalCase`, function names and variables in the C++ part follow the `camelCase`, and function names and variables in the C part follows a `snake_case` to blend with the surroundings.
|
||||
- Try to keep names meaningful, and code clear if possible.
|
||||
- Comment your classes APIs, in a terse and clear way. Comments inside the code are usually not needed *if the code is written clearly with meaningful names*, but if you feel that it's needed please do.
|
||||
- You can use `_` in front of private variables, but we do not have a consistent naming for these yet.
|
||||
|
||||
All these rules are subject to change upon discussion and integration.
|
||||
|
||||
### Testing your changes
|
||||
|
||||
To build and run the tests, you need to have configured sfizz with the `-DSFIZZ_TESTS=ON` flag.
|
||||
You can then use the `make sfizz_tests` command from your build directory.
|
||||
The tests are located in `tests/sfizz_tests` from the same build directory.
|
||||
|
||||
Note that these tests mostly cover the parsing and observable invariants of sfizz, as well as all helper methods as much as possible.
|
||||
The processing and rendering is usually harder to test as it produces alot of data.
|
||||
We are in the process of making a testing platform using rendered wave files, but it is not yet here.
|
||||
If you write anything testable however, we encourage you to write the tests.
|
||||
We use the [Catch] library for testing, and you can check out the files in `tests/` for examples on how to add some.
|
||||
|
||||
To actually try your changes, you can use the `clients/sfizz_jack` client to instantiate a JACK application, or use `jalv` for a similar purpose by launching from your build directory
|
||||
```bash
|
||||
env LV2_PATH=$PWD jalv.gtk3 http://sfztools.github.io/sfizz
|
||||
```
|
||||
Similarly, you can launch e.g. Ardour with suitable reference to your version of the sfizz LV2 plugin with
|
||||
```bash
|
||||
env LV2_PATH=$PWD Ardour6
|
||||
```
|
||||
|
||||
### Running micro-benchmarks
|
||||
|
||||
For micro-optimization purposes we have a number of benchmarks written in `benchmarks/`, build against the [benchmark] library.
|
||||
If you wonder about the performance of something, feel free to write some too and add them to the build system.
|
||||
We tend to name the benchmarks with the `bm_` prefix.
|
||||
It can be as simple as adding a line
|
||||
```cmake
|
||||
sfizz_add_benchmark(bm_wavfile BM_wavfile.cpp)
|
||||
```
|
||||
to the `benchmarks/CMakeLists.txt` file but you might need to link to other libraries depending on what you are benchmarking.
|
||||
The `CMakeLists.txt` file contains example that you can copy and try.
|
||||
|
||||
### Running in-use benchmarks
|
||||
|
||||
We have logging facilities for the processing inside of sfizz, that we use to track how the additions impact the rendering speed.
|
||||
For now the usage is a bit hand-made, as it requires the [sfizz-render] program and manually pre-loading the development `.so` library.
|
||||
The logger outputs CSV files for the rendering and file-loading processes.
|
||||
The file `scripts/performance_report.py` is made to process and produce a nice interactive report from this data.
|
||||
|
||||
[build]: https://sfz.tools/sfizz/development/build/
|
||||
[benchmark]: https://github.com/google/benchmark
|
||||
[sfizz-render]: https://github.com/sfztools/sfizz-render
|
||||
[GOVERNANCE]: GOVERNANCE.md
|
||||
[api]: https://sfz.tools/sfizz/api/
|
||||
[RAII]: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-resource
|
||||
[CppCoreGuidelines]: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
|
||||
[Catch]: https://github.com/catchorg/Catch2
|
||||
1
FUNDING.yml
Normal file
1
FUNDING.yml
Normal file
|
|
@ -0,0 +1 @@
|
|||
open_collective: sfztools
|
||||
50
GOVERNANCE.md
Normal file
50
GOVERNANCE.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
## Leadership
|
||||
|
||||
The current maintainers of sfizz and the sfz tools organization are :
|
||||
|
||||
- Paul "@paulfd" Ferrand
|
||||
- Jean-Pierre "@jpcima" Cimalando
|
||||
- Andrea "@redtide" Zanellato
|
||||
|
||||
1. Sfizz does not follow a so-called "Benevolent Dictatorship for Life" model.
|
||||
Given the current size of the project, we believe an horizontal organization model to be the most relevant.
|
||||
2. Decisions regarding the technical part or the organizational part of the project are taken unanimously between the maintainers, where anyone can obviously abstain on any decision.
|
||||
3. We use Git as a global supporting platform for all decisions regarding the project, in the form of either pull requests or issues.
|
||||
This guarantees that a trace is kept for any contribution or change, and that users may interject and discuss on the subject at hand.
|
||||
|
||||
## Donations and financial contributions
|
||||
|
||||
In most jurisdictions, handling money requires bank accounts and identification as a physical person, or be incorporated as a non-profit or for-profit organization.
|
||||
We wish to:
|
||||
|
||||
- keep things simple;
|
||||
- support the horizontal governance model we chose;
|
||||
- facilitate a transparent handling of the finances of the project from the maintainers;
|
||||
- abide by international laws for the transfer and handling of money in support of a project.
|
||||
|
||||
Considering these constraints, we chose to host the SFZ tools organization within the [SFZTools Open Collective].
|
||||
The Open Collective fiscal host holds the money of unincorporated partnerships such as SFZ tools, providing legal channels and accounting, as well as tools to support a transparent budget management by members of the collective.
|
||||
|
||||
We apply the following ruleset to handle the project's financial resources:
|
||||
|
||||
1. As everything in SFZ tools, usage of the project's financial resources is subject to unanimous approval by all maintainers.
|
||||
2. The money is primarily directed towards expenses to support hosting the project---namely the website, and continuous testing and integration platforms.
|
||||
3. At any time, a maintainer may submit an expense request regarding the development of sfizz, for example but not limited to software resources, commercial SFZ libraries, or hardware devices.
|
||||
4. Every 3 months, during the months of March, June, September and December, the maintainers will gather in a budget meeting. The goal of the budget meeting decide in goodwill how to handle the money left over between contributors to the project.
|
||||
5. The partition should take into account individual technical and organizational contributions.
|
||||
The partition may also include other project that sfizz uses or wish to support.
|
||||
6. Any contributor may waive its own share to benefit either the project---pooling back resources for the next round---or the other contributors.
|
||||
7. Failure to reach a unanimous agreement will see the donations pooled back until the next budget meeting.
|
||||
8. The budget and expenses are kept public so all donators can keep track of how their donations are used.
|
||||
|
||||
Note that the Open Collective platform charges a 10% administrative fee on all donations; subsequent fees may be charged by credit card or banks as the money is moved from the collective to individual accounts.
|
||||
|
||||
## Changes to the governance model
|
||||
|
||||
1. This document is subject to changes and version control.
|
||||
It is the role of maintainers to best adapt the governance model to the evolution of the project.
|
||||
2. Change to the governance model are mapped to changes to this document through the same decision process, e.g. a pull request with approval from all maintainers.
|
||||
3. Maintainers can be invited from regular contributors at any point in time; maintainers wishing to leave the governance of the project may inform the other maintainers.
|
||||
In both cases, updates to this document reflect the evolution of the governance team and rules.
|
||||
|
||||
[Open-source Open Collective]: https://opencollective.com/sfztools
|
||||
32
README.md
32
README.md
|
|
@ -7,6 +7,30 @@ SFZ library and LV2 plugin, please check [our website] for more details.
|
|||
|
||||

|
||||
|
||||
## Using sfizz
|
||||
|
||||
Sfizz can be used most easily within an LV2 host such as [Carla] or [Ardour].
|
||||
It can also be integrated as a library within your own program; check out our [API] bindings for C and C++.
|
||||
Our [releases] are an immediate way to get a working library or LV2/VST plugin, although you might prefer to [build from source]!
|
||||
|
||||
## Contributing to sfizz
|
||||
|
||||
There is actually many things anyone can do, programming-related or music-related.
|
||||
Please check out the [CONTRIBUTING](CONTRIBUTING.md) document for information about filing bug reports or feature requests, and helping the development of sfizz
|
||||
|
||||
## Donating to sfizz
|
||||
|
||||
Sfizz and the work in the SFZ tools organization is purely driven by hobbyists who choose to use their free time to benefit this project.
|
||||
We firmly believe in the honesty and goodwill of users as a whole, and we want to promote a healthy relationship to software and to the cost of producing quality software.
|
||||
|
||||
No financial returns is explicitely required from using sfizz in any shape.
|
||||
However, if you feel that sfizz produces value for you or your products, and if you find that your financial situation allows for it, we put together ways to donate to the project.
|
||||
You are never compelled to do so, the [CONTRIBUTING](CONTRIBUTING.md) file contains different ways to contribute.
|
||||
|
||||
In all of sfizz's governance model, we strive to live in the open.
|
||||
Finances are no different, and we put in place a process so that the use of donations is as transparent as possible through our [Open Collective].
|
||||
We invite you to check out the [GOVERNANCE](GOVERNANCE.md) file to see how the organization is governed and how are donations handled.
|
||||
|
||||
## Dependencies and licenses
|
||||
|
||||
The sfizz library makes primary use of:
|
||||
|
|
@ -21,8 +45,6 @@ The sfizz library also uses in some subprojects:
|
|||
- [benchmark], licensed under the Apache License 2.0
|
||||
- [LV2], licensed under the ISC license
|
||||
- [JACK], licensed under the GNU Lesser General Public License v2.1
|
||||
- `neon_mathfun.h` and `sse_mathfun.h` by Julien Pommier,
|
||||
licensed under the zlib license
|
||||
|
||||
[Abseil]: https://github.com/abseil/abseil-cpp
|
||||
[atomic_queue]: https://github.com/max0x7ba/atomic_queue
|
||||
|
|
@ -34,5 +56,11 @@ The sfizz library also uses in some subprojects:
|
|||
[libsndfile]: https://github.com/erikd/libsndfile/
|
||||
[LV2]: https://lv2plug.in/
|
||||
[our website]: https://sfz.tools/sfizz
|
||||
[releases]: https://github.com/sfztools/sfizz/releases
|
||||
[Carla]: https://kx.studio/Applications:Carla
|
||||
[Ardour]: https://ardour.org/
|
||||
[API]: https://sfz.tools/sfizz/api/
|
||||
[Open Collective]: https://opencollective.com/sfztools
|
||||
[build from source]: https://sfz.tools/sfizz/development/build/
|
||||
[AppVeyor Build Status]: https://img.shields.io/appveyor/ci/sfztools/sfizz.svg?label=Windows&style=popout&logo=appveyor
|
||||
[Travis Build Status]: https://img.shields.io/travis/com/sfztools/sfizz.svg?label=Linux&style=popout&logo=travis
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public:
|
|||
}
|
||||
|
||||
sfz::MidiState midiState;
|
||||
sfz::Region region{midiState};
|
||||
sfz::Region region{0, midiState};
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
std::vector<float> output;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,56 +36,64 @@ public:
|
|||
BENCHMARK_DEFINE_F(AddArray, Value_Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::add<float, false>(1.1f, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add1, false);
|
||||
sfz::add1<float>(1.1f, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, Value_SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::add<float, true>(1.1f, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add1, true);
|
||||
sfz::add1<float>(1.1f, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, Value_Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::add<float, false>(1.1f, absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add1, false);
|
||||
sfz::add1<float>(1.1f, absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, Value_SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::add<float, true>(1.1f, absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add1, true);
|
||||
sfz::add1<float>(1.1f, absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::add<float, false>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, false);
|
||||
sfz::add<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::add<float, true>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, true);
|
||||
sfz::add<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::add<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, false);
|
||||
sfz::add<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::add<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, true);
|
||||
sfz::add<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,14 +43,16 @@ BENCHMARK_DEFINE_F(CopyArray, StdCopy)(benchmark::State& state) {
|
|||
BENCHMARK_DEFINE_F(CopyArray, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::copy<float, false>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, false);
|
||||
sfz::copy<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(CopyArray, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::copy<float, true>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, true);
|
||||
sfz::copy<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,14 +66,16 @@ BENCHMARK_DEFINE_F(CopyArray, StdCopy_Unaligned)(benchmark::State& state) {
|
|||
BENCHMARK_DEFINE_F(CopyArray, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::copy<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, false);
|
||||
sfz::copy<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(CopyArray, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::copy<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, true);
|
||||
sfz::copy<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,28 +35,32 @@ public:
|
|||
BENCHMARK_DEFINE_F(CumArray, Sum_Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::cumsum<float, false>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, false);
|
||||
sfz::cumsum<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(CumArray, Sum_SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::cumsum<float, true>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, true);
|
||||
sfz::cumsum<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(CumArray, Sum_Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::cumsum<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, false);
|
||||
sfz::cumsum<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(CumArray, Sum_SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::cumsum<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, true);
|
||||
sfz::cumsum<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public:
|
|||
input = std::vector<float>(state.range(0));
|
||||
output = std::vector<float>(state.range(0));
|
||||
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
|
||||
sfz::cumsum<float, false>(input, absl::MakeSpan(input));
|
||||
sfz::cumsum<float>(input, absl::MakeSpan(input));
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */) {
|
||||
|
|
@ -37,28 +37,32 @@ public:
|
|||
BENCHMARK_DEFINE_F(DiffArray, Diff_Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::diff<float, false>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, false);
|
||||
sfz::diff<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(DiffArray, Diff_SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::diff<float, true>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, true);
|
||||
sfz::diff<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(DiffArray, Diff_Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::diff<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, false);
|
||||
sfz::diff<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(DiffArray, Diff_SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::diff<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, true);
|
||||
sfz::diff<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,28 +46,32 @@ BENCHMARK_DEFINE_F(Divide, Straight)(benchmark::State& state) {
|
|||
BENCHMARK_DEFINE_F(Divide, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::divide<float, false>(input, divisor, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::divide, false);
|
||||
sfz::divide<float>(input, divisor, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(Divide, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::divide<float, true>(input, divisor, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::divide, true);
|
||||
sfz::divide<float>(input, divisor, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(Divide, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::divide<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(divisor).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::divide, false);
|
||||
sfz::divide<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(divisor).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(Divide, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::divide<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(divisor).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::divide, true);
|
||||
sfz::divide<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(divisor).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include "SfzHelpers.h"
|
||||
#include "ModifierHelpers.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
class EnvelopeFixture : public benchmark::Fixture {
|
||||
|
|
@ -20,7 +20,7 @@ public:
|
|||
input = std::vector<float>(state.range(0));
|
||||
output = std::vector<float>(state.range(0));
|
||||
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
|
||||
sfz::cumsum<float, false>(input, absl::MakeSpan(input));
|
||||
sfz::cumsum<float>(input, absl::MakeSpan(input));
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */)
|
||||
|
|
@ -42,7 +42,16 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, Linear)(benchmark::State& state) {
|
|||
{ 0, 0.0f },
|
||||
{ static_cast<int>(state.range(0) - 1), dist(gen) }
|
||||
};
|
||||
linearEnvelope(events, absl::MakeSpan(output), [](float x) { return x; });
|
||||
sfz::linearEnvelope(events, absl::MakeSpan(output), [](float x) { return x; });
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(EnvelopeFixture, LinearNoEvent) (benchmark::State& state) {
|
||||
for (auto _ : state) {
|
||||
sfz::EventVector events {
|
||||
{ 0, dist(gen) }
|
||||
};
|
||||
sfz::linearEnvelope(events, absl::MakeSpan(output), [](float x) { return x; });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +62,7 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, LinearQuantized)(benchmark::State& state) {
|
|||
{ 0, 0.0f },
|
||||
{ static_cast<int>(state.range(0) - 1), dist(gen) }
|
||||
};
|
||||
linearEnvelope(
|
||||
sfz::linearEnvelope(
|
||||
events, absl::MakeSpan(output), [](float x) { return x; }, 0.5);
|
||||
}
|
||||
}
|
||||
|
|
@ -65,7 +74,16 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, Multiplicative)(benchmark::State& state) {
|
|||
{ 0, 1.0f },
|
||||
{ static_cast<int>(state.range(0) - 1), dist(gen) }
|
||||
};
|
||||
multiplicativeEnvelope(events, absl::MakeSpan(output), [](float x) { return x; });
|
||||
sfz::multiplicativeEnvelope(events, absl::MakeSpan(output), [](float x) { return x; });
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(EnvelopeFixture, MultiplicativeNoEvent) (benchmark::State& state) {
|
||||
for (auto _ : state) {
|
||||
sfz::EventVector events {
|
||||
{ 0, dist(gen) }
|
||||
};
|
||||
sfz::multiplicativeEnvelope(events, absl::MakeSpan(output), [](float x) { return x; });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,14 +94,16 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, MultiplicativeQuantized)(benchmark::State& s
|
|||
{ 0, 1.0f },
|
||||
{ static_cast<int>(state.range(0) - 1), dist(gen) }
|
||||
};
|
||||
multiplicativeEnvelope(
|
||||
sfz::multiplicativeEnvelope(
|
||||
events, absl::MakeSpan(output), [](float x) { return x; }, 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BENCHMARK_REGISTER_F(EnvelopeFixture, Linear)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(EnvelopeFixture, LinearNoEvent)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(EnvelopeFixture, LinearQuantized)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(EnvelopeFixture, Multiplicative)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(EnvelopeFixture, MultiplicativeNoEvent)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(EnvelopeFixture, MultiplicativeQuantized)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_MAIN();
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "SIMDHelpers.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include "Buffer.h"
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
|
||||
static void Dummy(benchmark::State& state) {
|
||||
sfz::Buffer<float> buffer (state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 2 };
|
||||
for (auto _ : state) {
|
||||
auto fillValue = dist(gen);
|
||||
benchmark::DoNotOptimize(fillValue);
|
||||
}
|
||||
}
|
||||
|
||||
static void FillScalar(benchmark::State& state) {
|
||||
sfz::Buffer<float> buffer (state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 2 };
|
||||
for (auto _ : state) {
|
||||
sfz::fill<float, false>(absl::MakeSpan(buffer), dist(gen));
|
||||
}
|
||||
}
|
||||
|
||||
static void FillScalar_unaligned(benchmark::State& state) {
|
||||
sfz::Buffer<float> buffer (state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 2 };
|
||||
for (auto _ : state) {
|
||||
sfz::fill<float, false>(absl::MakeSpan(buffer).subspan(1), dist(gen));
|
||||
}
|
||||
}
|
||||
|
||||
static void FillSIMD(benchmark::State& state) {
|
||||
sfz::Buffer<float> buffer (state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 2 };
|
||||
for (auto _ : state) {
|
||||
sfz::fill<float, true>(absl::MakeSpan(buffer), dist(gen));
|
||||
}
|
||||
}
|
||||
|
||||
static void FillSIMD_unaligned(benchmark::State& state) {
|
||||
sfz::Buffer<float> buffer (state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 2 };
|
||||
for (auto _ : state) {
|
||||
sfz::fill<float, true>(absl::MakeSpan(buffer).subspan(1), dist(gen));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(Dummy)->RangeMultiplier(4)->Range((1<<2), (1<<12));
|
||||
BENCHMARK(FillScalar)->RangeMultiplier(4)->Range((1<<2), (1<<12));
|
||||
BENCHMARK(FillSIMD)->RangeMultiplier(4)->Range((1<<2), (1<<12));
|
||||
BENCHMARK(FillScalar_unaligned)->RangeMultiplier(4)->Range((1<<2), (1<<12));
|
||||
BENCHMARK(FillSIMD_unaligned)->RangeMultiplier(4)->Range((1<<2), (1<<12));
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -7,7 +7,9 @@
|
|||
#include "SIMDHelpers.h"
|
||||
#include "OnePoleFilter.h"
|
||||
#include "SfzFilter.h"
|
||||
#include "SfzHelpers.h"
|
||||
#include "ScopedFTZ.h"
|
||||
#include "SfzHelpers.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
|
|
@ -57,7 +59,7 @@ BENCHMARK_DEFINE_F(FilterFixture, OnePole_VA)(benchmark::State& state) {
|
|||
const auto sentinel = cutoff.data() + blockSize;
|
||||
while (cutoffPtr < sentinel)
|
||||
{
|
||||
const auto gain = sfz::OnePoleFilter<float>::normalizedGain(*cutoffPtr, sampleRate);
|
||||
const auto gain = sfz::vaGain(*cutoffPtr, sampleRate);
|
||||
filter.setGain(gain);
|
||||
filter.processLowpass({ inputPtr, step }, { outputPtr, step } );
|
||||
cutoffPtr += step;
|
||||
|
|
|
|||
|
|
@ -66,14 +66,16 @@ BENCHMARK_DEFINE_F(GainSingle, Straight)(benchmark::State& state) {
|
|||
BENCHMARK_DEFINE_F(GainSingle, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::applyGain<float, false>(gain, input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain1, false);
|
||||
sfz::applyGain1<float>(gain, input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(GainSingle, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::applyGain<float, true>(gain, input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain1, true);
|
||||
sfz::applyGain1<float>(gain, input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,28 +90,32 @@ BENCHMARK_DEFINE_F(GainArray, Straight)(benchmark::State& state) {
|
|||
BENCHMARK_DEFINE_F(GainArray, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::applyGain<float, false>(gain, input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, false);
|
||||
sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(GainArray, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::applyGain<float, true>(gain, input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, true);
|
||||
sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(GainArray, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::applyGain<float, false>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, false);
|
||||
sfz::applyGain<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(GainArray, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::applyGain<float, true>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, true);
|
||||
sfz::applyGain<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "SIMDHelpers.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <absl/algorithm/container.h>
|
||||
|
||||
// In this one we have an array of jumps
|
||||
|
||||
constexpr float maxJump { 4 };
|
||||
|
||||
class InterpolationCast : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 0, maxJump };
|
||||
jumps = std::vector<int>(state.range(0));
|
||||
leftCoeffs = std::vector<float>(state.range(0));
|
||||
rightCoeffs = std::vector<float>(state.range(0));
|
||||
floatJumps = std::vector<float>(state.range(0));
|
||||
absl::c_generate(floatJumps, [&]() { return dist(gen); });
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<int> jumps;
|
||||
std::vector<float> leftCoeffs;
|
||||
std::vector<float> rightCoeffs;
|
||||
std::vector<float> floatJumps;
|
||||
};
|
||||
|
||||
|
||||
BENCHMARK_DEFINE_F(InterpolationCast, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::sfzInterpolationCast<float, false>(floatJumps, absl::MakeSpan(jumps), absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(InterpolationCast, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::sfzInterpolationCast<float, true>(floatJumps, absl::MakeSpan(jumps), absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(InterpolationCast, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::sfzInterpolationCast<float, false>(absl::MakeSpan(floatJumps).subspan(1), absl::MakeSpan(jumps).subspan(3), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(InterpolationCast, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::sfzInterpolationCast<float, true>(absl::MakeSpan(floatJumps).subspan(1), absl::MakeSpan(jumps).subspan(3), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Register the function as a benchmark
|
||||
BENCHMARK_REGISTER_F(InterpolationCast, Scalar)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_REGISTER_F(InterpolationCast, SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_REGISTER_F(InterpolationCast, Scalar_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_REGISTER_F(InterpolationCast, SIMD_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_MAIN();
|
||||
87
benchmarks/BM_interpolators.cpp
Normal file
87
benchmarks/BM_interpolators.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "Config.h"
|
||||
#include "Interpolators.h"
|
||||
#include "ScopedFTZ.h"
|
||||
#include "absl/types/span.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <random>
|
||||
|
||||
class Interpolators : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state)
|
||||
{
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { -1.0f, 1.0f };
|
||||
|
||||
const size_t numFramesIn = state.range(0);
|
||||
inputBuffer = std::vector<float>(numFramesIn + 2 * sfz::config::excessFileFrames);
|
||||
|
||||
// any ratio will do, compute time will be proportional
|
||||
static constexpr float ratio = 1.234;
|
||||
|
||||
const size_t numFramesOut = static_cast<size_t>(std::ceil(numFramesIn * ratio));
|
||||
input = absl::MakeSpan(inputBuffer).subspan(sfz::config::excessFileFrames, numFramesIn);
|
||||
output = std::vector<float>(numFramesOut);
|
||||
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<float> inputBuffer;
|
||||
absl::Span<float> input;
|
||||
std::vector<float> output;
|
||||
|
||||
enum { excessFrames = 8 };
|
||||
};
|
||||
|
||||
template <sfz::InterpolatorModel M>
|
||||
static void doInterpolation(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
const float kOutToIn = static_cast<float>(input.size()) / output.size();
|
||||
|
||||
for (size_t iOut = 0; iOut < output.size(); ++iOut) {
|
||||
float posIn = iOut * kOutToIn;
|
||||
unsigned dec = static_cast<int>(posIn);
|
||||
float frac = posIn - dec;
|
||||
output[iOut] = sfz::interpolate<M>(&input[dec], frac);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(Interpolators, Linear)(benchmark::State& state)
|
||||
{
|
||||
ScopedFTZ ftz;
|
||||
|
||||
for (auto _ : state) {
|
||||
doInterpolation<sfz::kInterpolatorLinear>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(Interpolators, Hermite3)(benchmark::State& state)
|
||||
{
|
||||
ScopedFTZ ftz;
|
||||
|
||||
for (auto _ : state) {
|
||||
doInterpolation<sfz::kInterpolatorHermite3>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(Interpolators, Bspline3)(benchmark::State& state)
|
||||
{
|
||||
ScopedFTZ ftz;
|
||||
|
||||
for (auto _ : state) {
|
||||
doInterpolation<sfz::kInterpolatorBspline3>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(Interpolators, Linear)->RangeMultiplier(4)->Range(1 << 4, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(Interpolators, Hermite3)->RangeMultiplier(4)->Range(1 << 4, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(Interpolators, Bspline3)->RangeMultiplier(4)->Range(1 << 4, 1 << 12);
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include "SIMDHelpers.h"
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <absl/algorithm/container.h>
|
||||
|
||||
// In this one we have an array of indices
|
||||
|
||||
constexpr int loopStart { 5 };
|
||||
constexpr int loopEnd { 1076 };
|
||||
constexpr float maxJump { 4 };
|
||||
|
||||
class LoopingFixture : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 0, maxJump };
|
||||
indices = std::vector<int>(state.range(0));
|
||||
leftCoeffs = std::vector<float>(state.range(0));
|
||||
rightCoeffs = std::vector<float>(state.range(0));
|
||||
jumps = std::vector<float>(state.range(0));
|
||||
absl::c_generate(jumps, [&]() { return dist(gen); });
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<int> indices;
|
||||
std::vector<float> leftCoeffs;
|
||||
std::vector<float> rightCoeffs;
|
||||
std::vector<float> jumps;
|
||||
};
|
||||
|
||||
|
||||
BENCHMARK_DEFINE_F(LoopingFixture, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::loopingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd, loopStart);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(LoopingFixture, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::loopingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd, loopStart);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(LoopingFixture, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::loopingSFZIndex<float, false>(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd, loopStart);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(LoopingFixture, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::loopingSFZIndex<float, true>(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd, loopStart);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Register the function as a benchmark
|
||||
BENCHMARK_REGISTER_F(LoopingFixture, Scalar)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_REGISTER_F(LoopingFixture, SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_REGISTER_F(LoopingFixture, Scalar_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_REGISTER_F(LoopingFixture, SIMD_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -45,96 +45,6 @@ BENCHMARK_DEFINE_F(MyFixture, Dummy)
|
|||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, ScalarExp)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::exp<float, false>(source, absl::MakeSpan(result));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, SIMDExp)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::exp<float, true>(source, absl::MakeSpan(result));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, ScalarExp_Unaligned)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::exp<float, false>(absl::MakeSpan(source).subspan(1), absl::MakeSpan(result).subspan(1));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, SIMDExp_Unaligned)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::exp<float, true>(absl::MakeSpan(source).subspan(1), absl::MakeSpan(result).subspan(1));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, ScalarLog)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::log<float, false>(source, absl::MakeSpan(result));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, SIMDLog)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::log<float, true>(source, absl::MakeSpan(result));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, ScalarSin)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::sin<float, false>(source, absl::MakeSpan(result));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, SIMDSin)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::sin<float, true>(source, absl::MakeSpan(result));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, ScalarCos)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::cos<float, false>(source, absl::MakeSpan(result));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, SIMDCos)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::cos<float, true>(source, absl::MakeSpan(result));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, ScalarLibmFloorLog2)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
|
|
@ -159,16 +69,6 @@ BENCHMARK_DEFINE_F(MyFixture, ScalarFastFloorLog2)
|
|||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(MyFixture, Dummy)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, ScalarExp)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, SIMDExp)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, ScalarExp_Unaligned)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, SIMDExp_Unaligned)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, ScalarLog)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, SIMDLog)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, ScalarSin)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, SIMDSin)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, ScalarCos)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, SIMDCos)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, ScalarLibmFloorLog2)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
BENCHMARK_REGISTER_F(MyFixture, ScalarFastFloorLog2)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ BENCHMARK_DEFINE_F(MeanArray, Scalar)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
auto result = sfz::mean<float, false>(input);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::mean, false);
|
||||
auto result = sfz::mean<float>(input);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +44,8 @@ BENCHMARK_DEFINE_F(MeanArray, SIMD)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
auto result = sfz::mean<float, true>(input);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::mean, true);
|
||||
auto result = sfz::mean<float>(input);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +54,8 @@ BENCHMARK_DEFINE_F(MeanArray, Scalar_Unaligned)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
auto result = sfz::mean<float, false>(absl::MakeSpan(input).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::mean, false);
|
||||
auto result = sfz::mean<float>(absl::MakeSpan(input).subspan(1));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +64,8 @@ BENCHMARK_DEFINE_F(MeanArray, SIMD_Unaligned)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
auto result = sfz::mean<float, true>(absl::MakeSpan(input).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::mean, true);
|
||||
auto result = sfz::mean<float>(absl::MakeSpan(input).subspan(1));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, Scalar)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
auto result = sfz::meanSquared<float, false>(input);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
|
||||
auto result = sfz::meanSquared<float>(input);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +44,8 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, SIMD)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
auto result = sfz::meanSquared<float, true>(input);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
|
||||
auto result = sfz::meanSquared<float>(input);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +54,8 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, Scalar_Unaligned)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
auto result = sfz::meanSquared<float, false>(absl::MakeSpan(input).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
|
||||
auto result = sfz::meanSquared<float>(absl::MakeSpan(input).subspan(1));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +64,8 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, SIMD_Unaligned)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
auto result = sfz::meanSquared<float, true>(absl::MakeSpan(input).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
|
||||
auto result = sfz::meanSquared<float>(absl::MakeSpan(input).subspan(1));
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,28 +46,32 @@ BENCHMARK_DEFINE_F(MultiplyAdd, Straight)(benchmark::State& state) {
|
|||
BENCHMARK_DEFINE_F(MultiplyAdd, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::multiplyAdd<float, false>(gain, input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, false);
|
||||
sfz::multiplyAdd<float>(gain, input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MultiplyAdd, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::multiplyAdd<float, true>(gain, input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, true);
|
||||
sfz::multiplyAdd<float>(gain, input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MultiplyAdd, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::multiplyAdd<float, false>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, false);
|
||||
sfz::multiplyAdd<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MultiplyAdd, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::multiplyAdd<float, true>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, true);
|
||||
sfz::multiplyAdd<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::multiplyAdd<float, false>(gain, input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, false);
|
||||
sfz::multiplyAdd1<float>(gain, input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +57,8 @@ BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::multiplyAdd<float, true>(gain, input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, true);
|
||||
sfz::multiplyAdd1<float>(gain, input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +66,8 @@ BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar_Unaligned)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::multiplyAdd<float, false>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, false);
|
||||
sfz::multiplyAdd1<float>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +75,8 @@ BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD_Unaligned)
|
|||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
sfz::multiplyAdd<float, true>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, true);
|
||||
sfz::multiplyAdd1<float>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "SIMDHelpers.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include "Config.h"
|
||||
#include "ScopedFTZ.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
class PanArray : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 0.001f, 1.0f };
|
||||
pan = std::vector<float>(state.range(0));
|
||||
left = std::vector<float>(state.range(0));
|
||||
right = std::vector<float>(state.range(0));
|
||||
std::generate(pan.begin(), pan.end(), [&]() { return dist(gen); });
|
||||
std::generate(right.begin(), right.end(), [&]() { return dist(gen); });
|
||||
std::generate(left.begin(), left.end(), [&]() { return dist(gen); });
|
||||
temp1 = std::vector<float>(state.range(0));
|
||||
temp2 = std::vector<float>(state.range(0));
|
||||
span1 = absl::MakeSpan(temp1);
|
||||
span2 = absl::MakeSpan(temp2);
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<float> pan;
|
||||
std::vector<float> left;
|
||||
std::vector<float> right;
|
||||
std::vector<float> temp1;
|
||||
std::vector<float> temp2;
|
||||
absl::Span<float> span1;
|
||||
absl::Span<float> span2;
|
||||
};
|
||||
|
||||
|
||||
BENCHMARK_DEFINE_F(PanArray, Scalar)(benchmark::State& state) {
|
||||
ScopedFTZ ftz;
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::pan<float, false>(pan, absl::MakeSpan(left), absl::MakeSpan(right));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(PanArray, SIMD)(benchmark::State& state) {
|
||||
ScopedFTZ ftz;
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::pan<float, true>(pan, absl::MakeSpan(left), absl::MakeSpan(right));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(PanArray, BlockOps)(benchmark::State& state) {
|
||||
ScopedFTZ ftz;
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::fill<float>(span2, 1.0f);
|
||||
sfz::add<float>(span1, span2);
|
||||
sfz::applyGain<float>(piFour<float>(), span2);
|
||||
sfz::cos<float>(span2, span1);
|
||||
sfz::sin<float>(span2, span2);
|
||||
sfz::applyGain<float>(span1, absl::MakeSpan(left));
|
||||
sfz::applyGain<float>(span2, absl::MakeSpan(right));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(PanArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(PanArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(PanArray, BlockOps)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -30,7 +30,8 @@ static void LinearScalar(benchmark::State& state) {
|
|||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::linearRamp<float, false>(absl::MakeSpan(output), 0.0f, value);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, false);
|
||||
sfz::linearRamp<float>(absl::MakeSpan(output), 0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +43,8 @@ static void LinearSIMD(benchmark::State& state) {
|
|||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::linearRamp<float, true>(absl::MakeSpan(output), 0.0f, value);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
|
||||
sfz::linearRamp<float>(absl::MakeSpan(output), 0.0f, value);
|
||||
}
|
||||
}
|
||||
static void LinearScalarUnaligned(benchmark::State& state) {
|
||||
|
|
@ -53,7 +55,8 @@ static void LinearScalarUnaligned(benchmark::State& state) {
|
|||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::linearRamp<float, false>(absl::MakeSpan(output).subspan(1), 0.0f, value);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, false);
|
||||
sfz::linearRamp<float>(absl::MakeSpan(output).subspan(1), 0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +68,8 @@ static void LinearSIMDUnaligned(benchmark::State& state) {
|
|||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::linearRamp<float, true>(absl::MakeSpan(output).subspan(1), 0.0f, value);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
|
||||
sfz::linearRamp<float>(absl::MakeSpan(output).subspan(1), 0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +81,8 @@ static void MulScalar(benchmark::State& state) {
|
|||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::multiplicativeRamp<float, false>(absl::MakeSpan(output), 1.0f, value);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, false);
|
||||
sfz::multiplicativeRamp<float>(absl::MakeSpan(output), 1.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +94,8 @@ static void MulSIMD(benchmark::State& state) {
|
|||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::multiplicativeRamp<float, true>(absl::MakeSpan(output), 1.0f, value);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, true);
|
||||
sfz::multiplicativeRamp<float>(absl::MakeSpan(output), 1.0f, value);
|
||||
}
|
||||
}
|
||||
static void MulScalarUnaligned(benchmark::State& state) {
|
||||
|
|
@ -100,7 +106,8 @@ static void MulScalarUnaligned(benchmark::State& state) {
|
|||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::multiplicativeRamp<float, false>(absl::MakeSpan(output).subspan(1), 1.0f, value);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, false);
|
||||
sfz::multiplicativeRamp<float>(absl::MakeSpan(output).subspan(1), 1.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,64 +119,8 @@ static void MulSIMDUnaligned(benchmark::State& state) {
|
|||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::multiplicativeRamp<float, true>(absl::MakeSpan(output).subspan(1), 1.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void LogDomainScalar(benchmark::State& state) {
|
||||
sfz::Buffer<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 2 };
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::linearRamp<float, false>(absl::MakeSpan(output), 1.0f, value);
|
||||
sfz::applyGain<float, false>(std::log(2.0f), absl::MakeSpan(output));
|
||||
sfz::exp<float, false>(output, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
static void LogDomainSIMD(benchmark::State& state) {
|
||||
sfz::Buffer<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 2 };
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
sfz::linearRamp<float, true>(absl::MakeSpan(output), 1.0f, value);
|
||||
sfz::applyGain<float, true>(std::log(2.0f), absl::MakeSpan(output));
|
||||
sfz::exp<float, true>(output, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
static void LogDomainScalarUnaligned(benchmark::State& state) {
|
||||
sfz::Buffer<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 2 };
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
auto outputSpan = absl::MakeSpan(output).subspan(1);
|
||||
sfz::linearRamp<float, false>(outputSpan, 1.0f, value);
|
||||
sfz::applyGain<float, false>(std::log(2.0f), outputSpan);
|
||||
sfz::exp<float, false>(outputSpan, outputSpan);
|
||||
}
|
||||
}
|
||||
|
||||
static void LogDomainSIMDUnaligned(benchmark::State& state) {
|
||||
sfz::Buffer<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 2 };
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto value = dist(gen);
|
||||
auto outputSpan = absl::MakeSpan(output).subspan(1);
|
||||
sfz::linearRamp<float, true>(outputSpan, 1.0f, value);
|
||||
sfz::applyGain<float, true>(std::log(2.0f), outputSpan);
|
||||
sfz::exp<float, true>(outputSpan, outputSpan);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, true);
|
||||
sfz::multiplicativeRamp<float>(absl::MakeSpan(output).subspan(1), 1.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,8 +134,4 @@ BENCHMARK(MulScalar)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
|
|||
BENCHMARK(MulSIMD)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
|
||||
BENCHMARK(MulScalarUnaligned)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
|
||||
BENCHMARK(MulSIMDUnaligned)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
|
||||
BENCHMARK(LogDomainScalar)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
|
||||
BENCHMARK(LogDomainSIMD)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
|
||||
BENCHMARK(LogDomainScalarUnaligned)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
|
||||
BENCHMARK(LogDomainSIMDUnaligned)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
|
||||
BENCHMARK_MAIN();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "MathHelpers.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
|
||||
class RandomFill : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
output = std::vector<float>(state.range(0));
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& state) {
|
||||
UNUSED(state);
|
||||
}
|
||||
|
||||
std::vector<float> output;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(RandomFill, StdRandom)(benchmark::State& state) {
|
||||
std::minstd_rand prng;
|
||||
std::uniform_real_distribution<float> dist(0.0f, 1.0f);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (float &out : output)
|
||||
out = dist(prng);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(RandomFill, FastRandom)(benchmark::State& state) {
|
||||
fast_rand prng;
|
||||
fast_real_distribution<float> dist(0.0f, 1.0f);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (float &out : output)
|
||||
out = dist(prng);
|
||||
}
|
||||
}
|
||||
BENCHMARK_DEFINE_F(RandomFill, StdRandomBipolar)(benchmark::State& state) {
|
||||
std::minstd_rand prng;
|
||||
std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (float &out : output)
|
||||
out = dist(prng);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(RandomFill, FastRandomBipolar)(benchmark::State& state) {
|
||||
fast_rand prng;
|
||||
fast_real_distribution<float> dist(-1.0f, 1.0f);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (float &out : output)
|
||||
out = dist(prng);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(RandomFill, StdNormal)(benchmark::State& state) {
|
||||
std::minstd_rand prng;
|
||||
std::normal_distribution<float> dist(0.0f, 0.25f);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (float &out : output)
|
||||
out = dist(prng);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(RandomFill, FastNormal)(benchmark::State& state) {
|
||||
fast_gaussian_generator<float, 4> generator(0.0f, 0.25f);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (float &out : output)
|
||||
out = generator();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(RandomFill, StdRandom)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(RandomFill, StdRandomBipolar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(RandomFill, FastRandom)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(RandomFill, FastRandomBipolar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(RandomFill, StdNormal)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(RandomFill, FastNormal)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
|
|
@ -61,7 +61,7 @@ BENCHMARK_DEFINE_F(FileFixture, JustRead)(benchmark::State& state) {
|
|||
{
|
||||
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
|
||||
sndfile.readf(buffer.data(), numFrames);
|
||||
sfz::readInterleaved<float>(buffer, output->getSpan(0), output->getSpan(1));
|
||||
sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ BENCHMARK_DEFINE_F(FileFixture, AllocInside)(benchmark::State& state) {
|
|||
{
|
||||
sfz::Buffer<float> buffer { chunkSize * sndfile.channels() };
|
||||
auto read = sndfile.readf(buffer.data(), chunkSize);
|
||||
sfz::readInterleaved<float>(
|
||||
sfz::readInterleaved(
|
||||
absl::MakeSpan(buffer).first(read),
|
||||
output->getSpan(0).subspan(framesRead),
|
||||
output->getSpan(1).subspan(framesRead)
|
||||
|
|
@ -95,7 +95,7 @@ BENCHMARK_DEFINE_F(FileFixture, AllocOutside)(benchmark::State& state) {
|
|||
while(framesRead < numFrames)
|
||||
{
|
||||
auto read = sndfile.readf(buffer.data(), chunkSize);
|
||||
sfz::readInterleaved<float>(
|
||||
sfz::readInterleaved(
|
||||
absl::MakeSpan(buffer).first(read),
|
||||
output->getSpan(0).subspan(framesRead),
|
||||
output->getSpan(1).subspan(framesRead)
|
||||
|
|
@ -124,7 +124,7 @@ BENCHMARK_DEFINE_F(FileFixture, DrWavChunked)(benchmark::State& state) {
|
|||
while(framesRead < numFrames)
|
||||
{
|
||||
auto read = drwav_read_pcm_frames_f32(&wav, chunkSize, buffer.data());
|
||||
sfz::readInterleaved<float>(
|
||||
sfz::readInterleaved(
|
||||
absl::MakeSpan(buffer).first(read),
|
||||
output->getSpan(0).subspan(framesRead),
|
||||
output->getSpan(1).subspan(framesRead)
|
||||
|
|
|
|||
120
benchmarks/BM_readChunkFlac.cpp
Normal file
120
benchmarks/BM_readChunkFlac.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "SIMDHelpers.h"
|
||||
#include "Buffer.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <sndfile.hh>
|
||||
#include "ghc/filesystem.hpp"
|
||||
#define DR_FLAC_IMPLEMENTATION
|
||||
#include "dr_flac.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#ifndef NDEBUG
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
class FileFixture : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& /* state */) {
|
||||
rootPath = getPath() / "sample1.flac";
|
||||
if (!ghc::filesystem::exists(rootPath)) {
|
||||
#ifndef NDEBUG
|
||||
std::cerr << "Can't find path" << '\n';
|
||||
#endif
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
sndfile = SndfileHandle(rootPath.c_str());
|
||||
numFrames = static_cast<size_t>(sndfile.frames());
|
||||
output = absl::make_unique<sfz::AudioBuffer<float>>(sndfile.channels(), sndfile.frames());
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */) {
|
||||
}
|
||||
|
||||
ghc::filesystem::path getPath()
|
||||
{
|
||||
#ifdef __linux__
|
||||
char buf[PATH_MAX + 1];
|
||||
if (readlink("/proc/self/exe", buf, sizeof(buf) - 1) == -1)
|
||||
return {};
|
||||
std::string str { buf };
|
||||
return str.substr(0, str.rfind('/'));
|
||||
#elif _WIN32
|
||||
return ghc::filesystem::current_path();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<sfz::AudioBuffer<float>> output;
|
||||
SndfileHandle sndfile;
|
||||
ghc::filesystem::path rootPath;
|
||||
size_t numFrames;
|
||||
};
|
||||
|
||||
|
||||
BENCHMARK_DEFINE_F(FileFixture, SndFileOnce)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
|
||||
sndfile.readf(buffer.data(), numFrames);
|
||||
sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(FileFixture, SndFileChunked)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto chunkSize = static_cast<size_t>(state.range(0));
|
||||
size_t framesRead = 0;
|
||||
sndfile.seek(0, SEEK_SET);
|
||||
while(framesRead < numFrames)
|
||||
{
|
||||
sfz::Buffer<float> buffer { chunkSize * sndfile.channels() };
|
||||
auto read = sndfile.readf(buffer.data(), chunkSize);
|
||||
sfz::readInterleaved(
|
||||
absl::MakeSpan(buffer).first(read),
|
||||
output->getSpan(0).subspan(framesRead),
|
||||
output->getSpan(1).subspan(framesRead)
|
||||
);
|
||||
framesRead += read;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BENCHMARK_DEFINE_F(FileFixture, DrWavChunked)(benchmark::State& state) {
|
||||
auto chunkSize = static_cast<size_t>(state.range(0));
|
||||
auto* flac = drflac_open_file(rootPath.c_str(), nullptr);
|
||||
if (!flac) {
|
||||
#ifndef NDEBUG
|
||||
std::cerr << "Can't find path" << '\n';
|
||||
#endif
|
||||
std::terminate();
|
||||
}
|
||||
sfz::Buffer<float> buffer { chunkSize * flac->channels };
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
size_t framesRead = 0;
|
||||
drflac_seek_to_pcm_frame(flac, 0);
|
||||
while(framesRead < numFrames)
|
||||
{
|
||||
auto read = drflac_read_pcm_frames_f32(flac, chunkSize, buffer.data());
|
||||
sfz::readInterleaved(
|
||||
absl::MakeSpan(buffer).first(read),
|
||||
output->getSpan(0).subspan(framesRead),
|
||||
output->getSpan(1).subspan(framesRead)
|
||||
);
|
||||
framesRead += read;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(FileFixture, SndFileOnce);
|
||||
BENCHMARK_REGISTER_F(FileFixture, SndFileChunked)->RangeMultiplier(4)->Range((1 << 10), (1 << 16));
|
||||
BENCHMARK_REGISTER_F(FileFixture, DrWavChunked)->RangeMultiplier(4)->Range((1 << 10), (1 << 16));
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -18,7 +18,8 @@ static void Scalar(benchmark::State& state) {
|
|||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
|
||||
for (auto _ : state) {
|
||||
sfz::readInterleaved<float, false>(input, absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
|
||||
sfz::readInterleaved(input, absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -29,7 +30,8 @@ static void SSE(benchmark::State& state) {
|
|||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
|
||||
for (auto _ : state) {
|
||||
sfz::readInterleaved<float, true>(input, absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
|
||||
sfz::readInterleaved(input, absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +41,12 @@ static void Scalar_Unaligned(benchmark::State& state) {
|
|||
sfz::Buffer<float> outputRight (state.range(0));
|
||||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
sfz::readInterleaved<float, false>(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
|
||||
sfz::readInterleaved(
|
||||
absl::MakeSpan(input).subspan(2),
|
||||
absl::MakeSpan(outputLeft),
|
||||
absl::MakeSpan(outputRight)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +56,12 @@ static void SSE_Unaligned(benchmark::State& state) {
|
|||
sfz::Buffer<float> outputRight (state.range(0));
|
||||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
sfz::readInterleaved<float, true>(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
|
||||
sfz::readInterleaved(
|
||||
absl::MakeSpan(input).subspan(2),
|
||||
absl::MakeSpan(outputLeft),
|
||||
absl::MakeSpan(outputRight)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +71,12 @@ static void Scalar_Unaligned_2(benchmark::State& state) {
|
|||
sfz::Buffer<float> outputRight (state.range(0));
|
||||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
sfz::readInterleaved<float, false>(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft).subspan(1), absl::MakeSpan(outputRight).subspan(3));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
|
||||
sfz::readInterleaved(
|
||||
absl::MakeSpan(input).subspan(2),
|
||||
absl::MakeSpan(outputLeft).subspan(1),
|
||||
absl::MakeSpan(outputRight).subspan(3)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +86,12 @@ static void SSE_Unaligned_2(benchmark::State& state) {
|
|||
sfz::Buffer<float> outputRight (state.range(0));
|
||||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
sfz::readInterleaved<float, true>(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft).subspan(1), absl::MakeSpan(outputRight).subspan(3));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
|
||||
sfz::readInterleaved(
|
||||
absl::MakeSpan(input).subspan(2),
|
||||
absl::MakeSpan(outputLeft).subspan(1),
|
||||
absl::MakeSpan(outputRight).subspan(3)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR2X_scalar)(benchmark::State& state)
|
|||
{
|
||||
for (auto _ : state) {
|
||||
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
|
||||
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
auto outBuffer = upsample2x<float, false>(*baseBuffer);
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
|
|
@ -242,7 +242,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR4X_scalar)(benchmark::State& state)
|
|||
{
|
||||
for (auto _ : state) {
|
||||
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
|
||||
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
auto outBuffer = upsample4x<float, false>(*baseBuffer);
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
|
|
@ -252,7 +252,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR8X_scalar)(benchmark::State& state)
|
|||
{
|
||||
for (auto _ : state) {
|
||||
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
|
||||
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
auto outBuffer = upsample8x<float, false>(*baseBuffer);
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
|
|
@ -262,7 +262,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR2X_vector)(benchmark::State& state)
|
|||
{
|
||||
for (auto _ : state) {
|
||||
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
|
||||
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
auto outBuffer = upsample2x<float, true>(*baseBuffer);
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
|
|
@ -272,7 +272,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR4X_vector)(benchmark::State& state)
|
|||
{
|
||||
for (auto _ : state) {
|
||||
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
|
||||
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
auto outBuffer = upsample4x<float, true>(*baseBuffer);
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
|
|
@ -282,7 +282,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR8X_vector)(benchmark::State& state)
|
|||
{
|
||||
for (auto _ : state) {
|
||||
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
|
||||
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
auto outBuffer = upsample8x<float, true>(*baseBuffer);
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
|
|
@ -300,7 +300,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC2x_BEST)(benchmark::State& state)
|
|||
srcData.output_frames = static_cast<long>(2 * numFrames);
|
||||
src_simple(&srcData, SRC_SINC_BEST_QUALITY, static_cast<int>(numChannels));
|
||||
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
|
||||
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -317,7 +317,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC2x_MEDIUM)(benchmark::State& state)
|
|||
srcData.output_frames = static_cast<long>(2 * numFrames);
|
||||
src_simple(&srcData, SRC_SINC_MEDIUM_QUALITY, static_cast<int>(numChannels));
|
||||
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
|
||||
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -334,7 +334,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC2x_FASTEST)(benchmark::State& state)
|
|||
srcData.output_frames = static_cast<long>(2 * numFrames);
|
||||
src_simple(&srcData, SRC_SINC_FASTEST, static_cast<int>(numChannels));
|
||||
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
|
||||
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -352,7 +352,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC4x_BEST)(benchmark::State& state)
|
|||
srcData.output_frames = static_cast<long>(2 * numFrames);
|
||||
src_simple(&srcData, SRC_SINC_BEST_QUALITY, static_cast<int>(numChannels));
|
||||
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
|
||||
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -369,7 +369,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC4x_MEDIUM)(benchmark::State& state)
|
|||
srcData.output_frames = static_cast<long>(2 * numFrames);
|
||||
src_simple(&srcData, SRC_SINC_MEDIUM_QUALITY, static_cast<int>(numChannels));
|
||||
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
|
||||
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -386,7 +386,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC4x_FASTEST)(benchmark::State& state)
|
|||
srcData.output_frames = static_cast<long>(2 * numFrames);
|
||||
src_simple(&srcData, SRC_SINC_FASTEST, static_cast<int>(numChannels));
|
||||
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
|
||||
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -403,7 +403,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC8x_BEST)(benchmark::State& state)
|
|||
srcData.output_frames = static_cast<long>(2 * numFrames);
|
||||
src_simple(&srcData, SRC_SINC_BEST_QUALITY, static_cast<int>(numChannels));
|
||||
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
|
||||
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -420,7 +420,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC8x_MEDIUM)(benchmark::State& state)
|
|||
srcData.output_frames = static_cast<long>(2 * numFrames);
|
||||
src_simple(&srcData, SRC_SINC_MEDIUM_QUALITY, static_cast<int>(numChannels));
|
||||
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
|
||||
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -437,7 +437,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC8x_FASTEST)(benchmark::State& state)
|
|||
srcData.output_frames = static_cast<long>(2 * numFrames);
|
||||
src_simple(&srcData, SRC_SINC_FASTEST, static_cast<int>(numChannels));
|
||||
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
|
||||
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -446,7 +446,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR8X_default)(benchmark::State& state)
|
|||
{
|
||||
for (auto _ : state) {
|
||||
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
|
||||
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
auto outBuffer = upsample8x<float>(*baseBuffer);
|
||||
benchmark::DoNotOptimize(outBuffer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ BENCHMARK_DEFINE_F(FileFixture, NoResampling)(benchmark::State& state) {
|
|||
{
|
||||
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
|
||||
sndfile.readf(buffer.data(), sndfile.frames());
|
||||
sfz::readInterleaved<float>(buffer, output->getSpan(0), output->getSpan(1));
|
||||
sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ BENCHMARK_DEFINE_F(FileFixture, ResampleAtOnce)(benchmark::State& state) {
|
|||
upsampler4x.set_coefs(coeffsStage4x.data());
|
||||
|
||||
sndfile.readf(buffer.data(), numFrames);
|
||||
sfz::readInterleaved<float>(buffer, output->getSpan(0), output->getSpan(1));
|
||||
sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1));
|
||||
|
||||
upsampler2x.process_block(temp.data(), output->channelReader(0), static_cast<long>(numFrames));
|
||||
upsampler4x.process_block(output->channelWriter(0), temp.data(), static_cast<long>(numFrames * 2));
|
||||
|
|
@ -171,7 +171,7 @@ BENCHMARK_DEFINE_F(FileFixture, ResampleInChunks)(benchmark::State& state) {
|
|||
thisChunkSize * sndfile.channels()
|
||||
);
|
||||
|
||||
sfz::readInterleaved<float>(bufferChunk, leftSpan, rightSpan);
|
||||
sfz::readInterleaved(bufferChunk, leftSpan, rightSpan);
|
||||
|
||||
upsampler2xLeft.process_block(chunkSpan.data(), leftSpan.data(), static_cast<long>(thisChunkSize));
|
||||
upsampler4xLeft.process_block(output->channelWriter(0) + outputFrameCounter, chunkSpan.data(), static_cast<long>(thisChunkSize * 2));
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "SIMDHelpers.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <absl/algorithm/container.h>
|
||||
|
||||
// In this one we have an array of indices
|
||||
|
||||
constexpr int loopEnd { 1076 };
|
||||
constexpr float maxJump { 4 };
|
||||
|
||||
class SaturatingFixture : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 0, maxJump };
|
||||
indices = std::vector<int>(state.range(0));
|
||||
leftCoeffs = std::vector<float>(state.range(0));
|
||||
rightCoeffs = std::vector<float>(state.range(0));
|
||||
jumps = std::vector<float>(state.range(0));
|
||||
absl::c_generate(jumps, [&]() { return dist(gen); });
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<int> indices;
|
||||
std::vector<float> leftCoeffs;
|
||||
std::vector<float> rightCoeffs;
|
||||
std::vector<float> jumps;
|
||||
};
|
||||
|
||||
|
||||
BENCHMARK_DEFINE_F(SaturatingFixture, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::saturatingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(SaturatingFixture, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::saturatingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(SaturatingFixture, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::saturatingSFZIndex<float, false>(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(SaturatingFixture, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::saturatingSFZIndex<float, true>(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Register the function as a benchmark
|
||||
BENCHMARK_REGISTER_F(SaturatingFixture, Scalar)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_REGISTER_F(SaturatingFixture, SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_REGISTER_F(SaturatingFixture, Scalar_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_REGISTER_F(SaturatingFixture, SIMD_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
|
||||
BENCHMARK_MAIN();
|
||||
57
benchmarks/BM_smoothers.cpp
Normal file
57
benchmarks/BM_smoothers.cpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
#include "SIMDHelpers.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include "ModifierHelpers.h"
|
||||
#include "Smoothers.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
class SmootherFixture : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state)
|
||||
{
|
||||
input = std::vector<float>(state.range(0));
|
||||
output = std::vector<float>(state.range(0));
|
||||
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
|
||||
sfz::cumsum<float>(input, absl::MakeSpan(input));
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */)
|
||||
{
|
||||
}
|
||||
std::random_device rd {};
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 0.5, 4 };
|
||||
std::vector<float> input;
|
||||
std::vector<float> output;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(SmootherFixture, Linear) (benchmark::State& state)
|
||||
{
|
||||
sfz::Smoother smoother;
|
||||
smoother.setSmoothing(10, sfz::config::defaultSampleRate);
|
||||
for (auto _ : state) {
|
||||
smoother.process(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
// BENCHMARK_DEFINE_F(SmootherFixture, Multiplicative)(benchmark::State& state) {
|
||||
// sfz::MultiplicativeSmoother smoother;
|
||||
// smoother.setSmoothing(10, sfz::config::defaultSampleRate);
|
||||
// for (auto _ : state)
|
||||
// {
|
||||
// smoother.process(input, absl::MakeSpan(output));
|
||||
// }
|
||||
// }
|
||||
|
||||
BENCHMARK_REGISTER_F(SmootherFixture, Linear)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
// BENCHMARK_REGISTER_F(SmootherFixture, Multiplicative)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -36,28 +36,32 @@ public:
|
|||
BENCHMARK_DEFINE_F(SubArray, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::subtract<float, false>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, false);
|
||||
sfz::subtract<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(SubArray, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::subtract<float, true>(input, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, true);
|
||||
sfz::subtract<float>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(SubArray, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::subtract<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, false);
|
||||
sfz::subtract<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(SubArray, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::subtract<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, true);
|
||||
sfz::subtract<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
59
benchmarks/BM_vaFilters.cpp
Normal file
59
benchmarks/BM_vaFilters.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "SIMDHelpers.h"
|
||||
#include "OnePoleFilter.h"
|
||||
#include "SfzFilter.h"
|
||||
#include "ScopedFTZ.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
constexpr int blockSize { 256 };
|
||||
constexpr float sampleRate { 48000.0f };
|
||||
|
||||
class FilterFixture : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State&)
|
||||
{
|
||||
input = std::vector<float>(blockSize);
|
||||
output = std::vector<float>(blockSize);
|
||||
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */)
|
||||
{
|
||||
}
|
||||
std::random_device rd {};
|
||||
std::mt19937 gen { rd() };
|
||||
std::normal_distribution<float> dist { 1, 0.2 };
|
||||
std::vector<float> input;
|
||||
std::vector<float> output;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(FilterFixture, OnePole_VA) (benchmark::State& state)
|
||||
{
|
||||
ScopedFTZ ftz;
|
||||
sfz::OnePoleFilter<float> filter;
|
||||
for (auto _ : state) {
|
||||
filter.processLowpass(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(FilterFixture, OnePoleMul_VA) (benchmark::State& state)
|
||||
{
|
||||
ScopedFTZ ftz;
|
||||
sfz::OnePoleFilterMul<float> filter;
|
||||
for (auto _ : state) {
|
||||
filter.processLowpass(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
BENCHMARK_REGISTER_F(FilterFixture, OnePole_VA);
|
||||
BENCHMARK_REGISTER_F(FilterFixture, OnePoleMul_VA);
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "SIMDHelpers.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include "Config.h"
|
||||
#include "ScopedFTZ.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
class WidthPosArray : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 0.001f, 1.0f };
|
||||
width = std::vector<float>(state.range(0));
|
||||
position = std::vector<float>(state.range(0));
|
||||
left = std::vector<float>(state.range(0));
|
||||
right = std::vector<float>(state.range(0));
|
||||
std::generate(width.begin(), width.end(), [&]() { return dist(gen); });
|
||||
std::generate(position.begin(), position.end(), [&]() { return dist(gen); });
|
||||
std::generate(right.begin(), right.end(), [&]() { return dist(gen); });
|
||||
std::generate(left.begin(), left.end(), [&]() { return dist(gen); });
|
||||
temp1 = std::vector<float>(state.range(0));
|
||||
temp2 = std::vector<float>(state.range(0));
|
||||
temp3 = std::vector<float>(state.range(0));
|
||||
span1 = absl::MakeSpan(temp1);
|
||||
span2 = absl::MakeSpan(temp2);
|
||||
span3 = absl::MakeSpan(temp3);
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<float> width;
|
||||
std::vector<float> position;
|
||||
std::vector<float> left;
|
||||
std::vector<float> right;
|
||||
std::vector<float> temp1;
|
||||
std::vector<float> temp2;
|
||||
std::vector<float> temp3;
|
||||
absl::Span<float> span1;
|
||||
absl::Span<float> span2;
|
||||
absl::Span<float> span3;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(WidthPosArray, Scalar)(benchmark::State& state) {
|
||||
ScopedFTZ ftz;
|
||||
const auto leftBuffer = absl::MakeSpan(left);
|
||||
const auto rightBuffer = absl::MakeSpan(right);
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::width<float, false>(width, leftBuffer, rightBuffer);
|
||||
sfz::pan<float, false>(position, leftBuffer, rightBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(WidthPosArray, SIMD)(benchmark::State& state) {
|
||||
ScopedFTZ ftz;
|
||||
const auto leftBuffer = absl::MakeSpan(left);
|
||||
const auto rightBuffer = absl::MakeSpan(right);
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::width<float, true>(width, leftBuffer, rightBuffer);
|
||||
sfz::pan<float, true>(position, leftBuffer, rightBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(WidthPosArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(WidthPosArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -19,7 +19,8 @@ static void Interleaved_Write(benchmark::State& state) {
|
|||
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
|
||||
|
||||
for (auto _ : state) {
|
||||
sfz::writeInterleaved<float, false>(inputLeft, inputRight, absl::MakeSpan(output));
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
|
||||
sfz::writeInterleaved(inputLeft, inputRight, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -30,8 +31,8 @@ static void Interleaved_Write_SSE(benchmark::State& state) {
|
|||
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
|
||||
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
sfz::writeInterleaved<float, true>(inputLeft, inputRight, absl::MakeSpan(output));
|
||||
benchmark::DoNotOptimize(output);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, true);
|
||||
sfz::writeInterleaved(inputLeft, inputRight, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,8 +43,12 @@ static void Unaligned_Interleaved_Write(benchmark::State& state) {
|
|||
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
|
||||
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
sfz::writeInterleaved<float, false>(absl::MakeSpan(inputLeft).subspan(1) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2));
|
||||
benchmark::DoNotOptimize(output);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
|
||||
sfz::writeInterleaved(
|
||||
absl::MakeSpan(inputLeft).subspan(1),
|
||||
absl::MakeSpan(inputRight).subspan(1),
|
||||
absl::MakeSpan(output).subspan(2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -54,8 +59,12 @@ static void Unaligned_Interleaved_Write_SSE(benchmark::State& state) {
|
|||
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
|
||||
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
sfz::writeInterleaved<float, true>(absl::MakeSpan(inputLeft).subspan(1) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2));
|
||||
benchmark::DoNotOptimize(output);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, true);
|
||||
sfz::writeInterleaved(
|
||||
absl::MakeSpan(inputLeft).subspan(1),
|
||||
absl::MakeSpan(inputRight).subspan(1),
|
||||
absl::MakeSpan(output).subspan(2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -66,8 +75,12 @@ static void Unaligned_Interleaved_Write_2(benchmark::State& state) {
|
|||
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
|
||||
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
sfz::writeInterleaved<float, false>(absl::MakeSpan(inputLeft) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2));
|
||||
benchmark::DoNotOptimize(output);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
|
||||
sfz::writeInterleaved(
|
||||
absl::MakeSpan(inputLeft),
|
||||
absl::MakeSpan(inputRight).subspan(1),
|
||||
absl::MakeSpan(output).subspan(2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,8 +91,12 @@ static void Unaligned_Interleaved_Write_SSE_2(benchmark::State& state) {
|
|||
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
|
||||
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
sfz::writeInterleaved<float, true>(absl::MakeSpan(inputLeft) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2));
|
||||
benchmark::DoNotOptimize(output);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, true);
|
||||
sfz::writeInterleaved(
|
||||
absl::MakeSpan(inputLeft),
|
||||
absl::MakeSpan(inputRight).subspan(1),
|
||||
absl::MakeSpan(output).subspan(2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ if(SAMPLERATE_LIBRARY AND SAMPLERATE_INCLUDE_DIR)
|
|||
endif()
|
||||
|
||||
add_library(bm_simd STATIC ${BENCHMARK_SIMD_SOURCES})
|
||||
target_link_libraries(bm_simd PRIVATE absl::span)
|
||||
target_link_libraries(bm_simd PRIVATE absl::span sfizz-cpuid)
|
||||
target_include_directories(bm_simd PRIVATE ../src/external)
|
||||
add_library(bm_ftz STATIC ../src/sfizz/ScopedFTZ.cpp)
|
||||
|
||||
|
|
@ -38,12 +38,9 @@ sfizz_add_benchmark(bm_opf_high_vs_low BM_OPF_high_vs_low.cpp)
|
|||
sfizz_add_benchmark(bm_clock BM_clock.cpp)
|
||||
sfizz_add_benchmark(bm_write BM_writeInterleaved.cpp)
|
||||
sfizz_add_benchmark(bm_read BM_readInterleaved.cpp)
|
||||
sfizz_add_benchmark(bm_fill BM_fill.cpp)
|
||||
sfizz_add_benchmark(bm_mathfuns BM_mathfuns.cpp)
|
||||
sfizz_add_benchmark(bm_gain BM_gain.cpp)
|
||||
sfizz_add_benchmark(bm_divide BM_divide.cpp)
|
||||
sfizz_add_benchmark(bm_looping BM_looping.cpp)
|
||||
sfizz_add_benchmark(bm_saturating BM_saturating.cpp)
|
||||
sfizz_add_benchmark(bm_ramp BM_ramp.cpp)
|
||||
sfizz_add_benchmark(bm_ADSR BM_ADSR.cpp)
|
||||
target_link_libraries(bm_ADSR PRIVATE sfizz::sfizz)
|
||||
|
|
@ -53,24 +50,24 @@ sfizz_add_benchmark(bm_multiplyAdd BM_multiplyAdd.cpp)
|
|||
sfizz_add_benchmark(bm_multiplyAddFixedGain BM_multiplyAddFixedGain.cpp)
|
||||
sfizz_add_benchmark(bm_subtract BM_subtract.cpp)
|
||||
sfizz_add_benchmark(bm_copy BM_copy.cpp)
|
||||
sfizz_add_benchmark(bm_pan BM_pan.cpp)
|
||||
sfizz_add_benchmark(bm_mean BM_mean.cpp)
|
||||
sfizz_add_benchmark(bm_meanSquared BM_meanSquared.cpp)
|
||||
sfizz_add_benchmark(bm_cumsum BM_cumsum.cpp)
|
||||
sfizz_add_benchmark(bm_diff BM_diff.cpp)
|
||||
sfizz_add_benchmark(bm_widthPos BM_widthPos.cpp)
|
||||
sfizz_add_benchmark(bm_interpolationCast BM_interpolationCast.cpp)
|
||||
sfizz_add_benchmark(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp)
|
||||
sfizz_add_benchmark(bm_maps BM_maps.cpp)
|
||||
target_link_libraries(bm_maps PRIVATE absl::flat_hash_map)
|
||||
sfizz_add_benchmark(bm_mapVsArray BM_mapVsArray.cpp)
|
||||
sfizz_add_benchmark(bm_random BM_random.cpp)
|
||||
|
||||
sfizz_add_benchmark(bm_logger BM_logger.cpp)
|
||||
target_link_libraries(bm_logger PRIVATE sfizz::sfizz)
|
||||
sfizz_add_benchmark(bm_smoothers BM_smoothers.cpp)
|
||||
target_link_libraries(bm_smoothers PRIVATE sfizz::sfizz)
|
||||
|
||||
if (TARGET sfizz-samplerate)
|
||||
sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES})
|
||||
target_link_libraries(bm_resample PRIVATE sfizz-samplerate sfizz-sndfile)
|
||||
target_link_libraries(bm_resample PRIVATE sfizz-samplerate sfizz-sndfile sfizz-cpuid)
|
||||
endif()
|
||||
|
||||
sfizz_add_benchmark(bm_envelopes BM_envelopes.cpp)
|
||||
|
|
@ -83,10 +80,14 @@ target_link_libraries(bm_flacfile PRIVATE sfizz-sndfile)
|
|||
|
||||
sfizz_add_benchmark(bm_readChunk BM_readChunk.cpp)
|
||||
target_link_libraries(bm_readChunk PRIVATE sfizz-sndfile)
|
||||
sfizz_add_benchmark(bm_readChunkFlac BM_readChunkFlac.cpp)
|
||||
target_link_libraries(bm_readChunkFlac PRIVATE sfizz-sndfile)
|
||||
|
||||
sfizz_add_benchmark(bm_resampleChunk BM_resampleChunk.cpp)
|
||||
target_link_libraries(bm_resampleChunk PRIVATE sfizz-sndfile)
|
||||
|
||||
sfizz_add_benchmark(bm_interpolators BM_interpolators.cpp)
|
||||
|
||||
sfizz_add_benchmark(bm_filterModulation BM_filterModulation.cpp ../src/sfizz/SfzFilter.cpp)
|
||||
target_link_libraries(bm_filterModulation PRIVATE sfizz-sndfile)
|
||||
|
||||
|
|
@ -111,27 +112,21 @@ add_dependencies(sfizz_benchmarks
|
|||
bm_read
|
||||
bm_mean
|
||||
bm_meanSquared
|
||||
bm_fill
|
||||
bm_cumsum
|
||||
bm_diff
|
||||
bm_interpolationCast
|
||||
bm_mathfuns
|
||||
bm_gain
|
||||
bm_divide
|
||||
bm_looping
|
||||
bm_saturating
|
||||
bm_ramp
|
||||
bm_ADSR
|
||||
bm_add
|
||||
bm_logger
|
||||
bm_pan
|
||||
bm_subtract
|
||||
bm_multiplyAdd
|
||||
bm_readChunk
|
||||
bm_resampleChunk
|
||||
bm_envelopes
|
||||
bm_wavfile
|
||||
bm_widthPos
|
||||
bm_flacfile
|
||||
bm_filterModulation
|
||||
bm_filterStereoMono
|
||||
|
|
@ -142,9 +137,9 @@ if (TARGET bm_resample)
|
|||
add_dependencies(sfizz_benchmarks bm_resample)
|
||||
endif()
|
||||
|
||||
file(COPY "sample1.wav" DESTINATION ${CMAKE_BINARY_DIR}/benchmarks)
|
||||
file(COPY "sample2.wav" DESTINATION ${CMAKE_BINARY_DIR}/benchmarks)
|
||||
file(COPY "sample3.wav" DESTINATION ${CMAKE_BINARY_DIR}/benchmarks)
|
||||
file(COPY "sample1.flac" DESTINATION ${CMAKE_BINARY_DIR}/benchmarks)
|
||||
file(COPY "sample2.flac" DESTINATION ${CMAKE_BINARY_DIR}/benchmarks)
|
||||
file(COPY "sample3.flac" DESTINATION ${CMAKE_BINARY_DIR}/benchmarks)
|
||||
configure_file("sample.wav" "${CMAKE_BINARY_DIR}/benchmarks/sample1.wav" COPYONLY)
|
||||
configure_file("sample.wav" "${CMAKE_BINARY_DIR}/benchmarks/sample2.wav" COPYONLY)
|
||||
configure_file("sample.wav" "${CMAKE_BINARY_DIR}/benchmarks/sample3.wav" COPYONLY)
|
||||
configure_file("sample.flac" "${CMAKE_BINARY_DIR}/benchmarks/sample1.flac" COPYONLY)
|
||||
configure_file("sample.flac" "${CMAKE_BINARY_DIR}/benchmarks/sample2.flac" COPYONLY)
|
||||
configure_file("sample.flac" "${CMAKE_BINARY_DIR}/benchmarks/sample3.flac" COPYONLY)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,10 +1,26 @@
|
|||
project (sfizz)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(JACK "jack" REQUIRED)
|
||||
if (SFIZZ_JACK)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(JACK "jack" REQUIRED)
|
||||
|
||||
add_executable (sfizz_jack jack_client.cpp)
|
||||
target_include_directories (sfizz_jack PRIVATE ${JACK_INCLUDE_DIRS})
|
||||
target_link_libraries (sfizz_jack PRIVATE sfizz::sfizz jack absl::flags_parse ${JACK_LIBRARIES})
|
||||
sfizz_enable_lto_if_needed (sfizz_jack)
|
||||
install (TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
|
||||
add_executable (sfizz_jack MidiHelpers.h jack_client.cpp)
|
||||
target_include_directories (sfizz_jack PRIVATE ${JACK_INCLUDE_DIRS})
|
||||
target_link_libraries (sfizz_jack PRIVATE sfizz::sfizz jack absl::flags_parse ${JACK_LIBRARIES})
|
||||
sfizz_enable_lto_if_needed (sfizz_jack)
|
||||
install (TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
COMPONENT "jack" OPTIONAL)
|
||||
endif()
|
||||
|
||||
if (SFIZZ_RENDER)
|
||||
add_library(sfizz-fmidi STATIC
|
||||
"external/fmidi/sources/fmidi/fmidi.h"
|
||||
"external/fmidi/sources/fmidi/fmidi_mini.cpp")
|
||||
target_include_directories(sfizz-fmidi PUBLIC "external/fmidi/sources")
|
||||
target_compile_definitions(sfizz-fmidi PUBLIC "FMIDI_STATIC=1" "FMIDI_DISABLE_DESCRIBE_API=1")
|
||||
|
||||
add_executable(sfizz_render MidiHelpers.h sfizz_render.cpp)
|
||||
target_link_libraries(sfizz_render PRIVATE sfizz::sfizz sfizz-fmidi sfizz-sndfile)
|
||||
sfizz_enable_lto_if_needed (sfizz_render)
|
||||
install (TARGETS sfizz_render DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT "render" OPTIONAL)
|
||||
endif()
|
||||
|
|
|
|||
29
clients/MidiHelpers.h
Normal file
29
clients/MidiHelpers.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace midi {
|
||||
constexpr uint8_t statusMask { 0b11110000 };
|
||||
constexpr uint8_t channelMask { 0b00001111 };
|
||||
constexpr uint8_t noteOff { 0x80 };
|
||||
constexpr uint8_t noteOn { 0x90 };
|
||||
constexpr uint8_t polyphonicPressure { 0xA0 };
|
||||
constexpr uint8_t controlChange { 0xB0 };
|
||||
constexpr uint8_t programChange { 0xC0 };
|
||||
constexpr uint8_t channelPressure { 0xD0 };
|
||||
constexpr uint8_t pitchBend { 0xE0 };
|
||||
constexpr uint8_t systemMessage { 0xF0 };
|
||||
|
||||
constexpr uint8_t status(uint8_t midiStatusByte)
|
||||
{
|
||||
return midiStatusByte & statusMask;
|
||||
}
|
||||
constexpr uint8_t channel(uint8_t midiStatusByte)
|
||||
{
|
||||
return midiStatusByte & channelMask;
|
||||
}
|
||||
|
||||
constexpr int buildAndCenterPitch(uint8_t firstByte, uint8_t secondByte)
|
||||
{
|
||||
return (int)(((unsigned int)secondByte << 7) + (unsigned int)firstByte) - 8192;
|
||||
}
|
||||
}
|
||||
2215
clients/cxxopts.hpp
Normal file
2215
clients/cxxopts.hpp
Normal file
File diff suppressed because it is too large
Load diff
23
clients/external/fmidi/LICENSE.md
vendored
Normal file
23
clients/external/fmidi/LICENSE.md
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
251
clients/external/fmidi/sources/fmidi/fmidi.h
vendored
Normal file
251
clients/external/fmidi/sources/fmidi/fmidi.h
vendored
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
// =============================================================================
|
||||
//
|
||||
// The Fmidi library - a free software toolkit for MIDI file processing
|
||||
//
|
||||
// =============================================================================
|
||||
// Copyright Jean Pierre Cimalando 2018.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.md or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#if !defined(__cplusplus)
|
||||
# include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(FMIDI_BUILD) && !defined(FMIDI_STATIC)
|
||||
# define FMIDI_API __declspec(dllexport)
|
||||
#elif defined(_WIN32) && !defined(FMIDI_BUILD) && !defined(FMIDI_STATIC)
|
||||
# define FMIDI_API __declspec(dllimport)
|
||||
#elif defined(__GNUC__)
|
||||
# define FMIDI_API __attribute__((visibility("default")))
|
||||
#else
|
||||
# define FMIDI_API
|
||||
#endif
|
||||
|
||||
///////////
|
||||
// INPUT //
|
||||
///////////
|
||||
|
||||
typedef struct fmidi_smf fmidi_smf_t;
|
||||
|
||||
FMIDI_API fmidi_smf_t *fmidi_smf_mem_read(const uint8_t *data, size_t length);
|
||||
FMIDI_API fmidi_smf_t *fmidi_smf_file_read(const char *filename);
|
||||
FMIDI_API fmidi_smf_t *fmidi_smf_stream_read(FILE *stream);
|
||||
FMIDI_API void fmidi_smf_free(fmidi_smf_t *smf);
|
||||
|
||||
typedef struct fmidi_smf_info {
|
||||
uint16_t format;
|
||||
uint16_t track_count;
|
||||
uint16_t delta_unit;
|
||||
} fmidi_smf_info_t;
|
||||
|
||||
FMIDI_API const fmidi_smf_info_t *fmidi_smf_get_info(const fmidi_smf_t *smf);
|
||||
FMIDI_API double fmidi_smf_compute_duration(const fmidi_smf_t *smf);
|
||||
|
||||
////////////
|
||||
// OUTPUT //
|
||||
////////////
|
||||
|
||||
FMIDI_API bool fmidi_smf_mem_write(const fmidi_smf_t *smf, uint8_t **data, size_t *length);
|
||||
FMIDI_API bool fmidi_smf_file_write(const fmidi_smf_t *smf, const char *filename);
|
||||
FMIDI_API bool fmidi_smf_stream_write(const fmidi_smf_t *smf, FILE *stream);
|
||||
|
||||
////////////////////
|
||||
// IDENTIFICATION //
|
||||
////////////////////
|
||||
|
||||
typedef enum fmidi_fileformat {
|
||||
fmidi_fileformat_smf,
|
||||
fmidi_fileformat_xmi,
|
||||
} fmidi_fileformat_t;
|
||||
|
||||
FMIDI_API fmidi_fileformat_t fmidi_mem_identify(const uint8_t *data, size_t length);
|
||||
FMIDI_API fmidi_fileformat_t fmidi_stream_identify(FILE *stream);
|
||||
|
||||
FMIDI_API fmidi_smf_t *fmidi_auto_mem_read(const uint8_t *data, size_t length);
|
||||
FMIDI_API fmidi_smf_t *fmidi_auto_file_read(const char *filename);
|
||||
FMIDI_API fmidi_smf_t *fmidi_auto_stream_read(FILE *stream);
|
||||
|
||||
////////////
|
||||
// EVENTS //
|
||||
////////////
|
||||
|
||||
typedef enum fmidi_event_type {
|
||||
fmidi_event_meta = 1,
|
||||
fmidi_event_message = 2,
|
||||
fmidi_event_escape = 3,
|
||||
fmidi_event_xmi_timbre = 4,
|
||||
fmidi_event_xmi_branch_point = 5
|
||||
} fmidi_event_type_t;
|
||||
|
||||
typedef struct fmidi_event {
|
||||
fmidi_event_type_t type;
|
||||
uint32_t delta;
|
||||
uint32_t datalen;
|
||||
uint8_t data[1];
|
||||
} fmidi_event_t;
|
||||
|
||||
#define fmidi_event_sizeof(datalen) \
|
||||
(offsetof(fmidi_event_t, data) + datalen)
|
||||
|
||||
////////////
|
||||
// TRACKS //
|
||||
////////////
|
||||
|
||||
typedef struct fmidi_track_iter {
|
||||
uint16_t track;
|
||||
uint32_t index;
|
||||
} fmidi_track_iter_t;
|
||||
|
||||
FMIDI_API void fmidi_smf_track_begin(fmidi_track_iter_t *it, uint16_t track);
|
||||
FMIDI_API const fmidi_event_t *fmidi_smf_track_next(
|
||||
const fmidi_smf_t *smf, fmidi_track_iter_t *it);
|
||||
|
||||
/////////////
|
||||
// FORMATS //
|
||||
/////////////
|
||||
|
||||
FMIDI_API fmidi_smf_t *fmidi_xmi_mem_read(const uint8_t *data, size_t length);
|
||||
FMIDI_API fmidi_smf_t *fmidi_xmi_file_read(const char *filename);
|
||||
FMIDI_API fmidi_smf_t *fmidi_xmi_stream_read(FILE *stream);
|
||||
|
||||
///////////////
|
||||
// SEQUENCER //
|
||||
///////////////
|
||||
|
||||
typedef struct fmidi_seq fmidi_seq_t;
|
||||
|
||||
typedef struct fmidi_seq_event {
|
||||
double time;
|
||||
uint16_t track;
|
||||
const fmidi_event_t *event;
|
||||
} fmidi_seq_event_t;
|
||||
|
||||
FMIDI_API fmidi_seq_t *fmidi_seq_new(const fmidi_smf_t *smf);
|
||||
FMIDI_API void fmidi_seq_free(fmidi_seq_t *pl);
|
||||
FMIDI_API void fmidi_seq_rewind(fmidi_seq_t *pl);
|
||||
FMIDI_API bool fmidi_seq_peek_event(fmidi_seq_t *pl, fmidi_seq_event_t *plevt);
|
||||
FMIDI_API bool fmidi_seq_next_event(fmidi_seq_t *pl, fmidi_seq_event_t *plevt);
|
||||
|
||||
/////////////
|
||||
// UTILITY //
|
||||
/////////////
|
||||
|
||||
typedef struct fmidi_smpte {
|
||||
uint8_t code[5];
|
||||
} fmidi_smpte_t;
|
||||
|
||||
FMIDI_API double fmidi_smpte_time(const fmidi_smpte_t *smpte);
|
||||
FMIDI_API double fmidi_delta_time(double delta, uint16_t unit, uint32_t tempo);
|
||||
FMIDI_API double fmidi_time_delta(double time, uint16_t unit, uint32_t tempo);
|
||||
|
||||
////////////
|
||||
// ERRORS //
|
||||
////////////
|
||||
|
||||
typedef enum fmidi_status {
|
||||
fmidi_ok,
|
||||
fmidi_err_format,
|
||||
fmidi_err_eof,
|
||||
fmidi_err_input,
|
||||
fmidi_err_largefile,
|
||||
fmidi_err_output
|
||||
} fmidi_status_t;
|
||||
|
||||
FMIDI_API fmidi_status_t fmidi_errno();
|
||||
FMIDI_API const char *fmidi_strerror(fmidi_status_t status);
|
||||
|
||||
typedef struct fmidi_error_info {
|
||||
fmidi_status_t code;
|
||||
#if defined(FMIDI_DEBUG)
|
||||
const char *file; int line;
|
||||
#endif
|
||||
} fmidi_error_info_t;
|
||||
|
||||
FMIDI_API const fmidi_error_info_t *fmidi_errinfo();
|
||||
|
||||
////////////
|
||||
// PLAYER //
|
||||
////////////
|
||||
|
||||
typedef struct fmidi_player fmidi_player_t;
|
||||
FMIDI_API fmidi_player_t *fmidi_player_new(fmidi_smf_t *smf);
|
||||
FMIDI_API void fmidi_player_tick(fmidi_player_t *seq, double delta);
|
||||
FMIDI_API void fmidi_player_free(fmidi_player_t *seq);
|
||||
FMIDI_API void fmidi_player_start(fmidi_player_t *seq);
|
||||
FMIDI_API void fmidi_player_stop(fmidi_player_t *seq);
|
||||
FMIDI_API void fmidi_player_rewind(fmidi_player_t *seq);
|
||||
FMIDI_API bool fmidi_player_running(const fmidi_player_t *seq);
|
||||
FMIDI_API double fmidi_player_current_time(const fmidi_player_t *seq);
|
||||
FMIDI_API void fmidi_player_goto_time(fmidi_player_t *seq, double time);
|
||||
FMIDI_API double fmidi_player_current_speed(const fmidi_player_t *seq);
|
||||
FMIDI_API void fmidi_player_set_speed(fmidi_player_t *seq, double speed);
|
||||
FMIDI_API void fmidi_player_event_callback(
|
||||
fmidi_player_t *seq, void (*cbfn)(const fmidi_event_t *, void *), void *cbdata);
|
||||
FMIDI_API void fmidi_player_finish_callback(
|
||||
fmidi_player_t *seq, void (*cbfn)(void *), void *cbdata);
|
||||
|
||||
//////////////
|
||||
// PRINTERS //
|
||||
//////////////
|
||||
|
||||
#if !defined(FMIDI_DISABLE_DESCRIBE_API)
|
||||
FMIDI_API void fmidi_smf_describe(const fmidi_smf_t *smf, FILE *stream);
|
||||
FMIDI_API void fmidi_event_describe(const fmidi_event_t *evt, FILE *stream);
|
||||
#endif
|
||||
|
||||
////////////
|
||||
// LIMITS //
|
||||
////////////
|
||||
|
||||
enum { fmidi_file_size_limit = 64 * 1024 * 1024 };
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
//////////////////
|
||||
// C++ PRINTERS //
|
||||
//////////////////
|
||||
|
||||
#if defined(__cplusplus) && !defined(FMIDI_DISABLE_DESCRIBE_API)
|
||||
# include <iosfwd>
|
||||
FMIDI_API std::ostream &operator<<(std::ostream &out, const fmidi_smf_t &smf);
|
||||
FMIDI_API std::ostream &operator<<(std::ostream &out, const fmidi_event_t &evt);
|
||||
#endif
|
||||
|
||||
//////////////
|
||||
// C++ RAII //
|
||||
//////////////
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# include <memory>
|
||||
|
||||
struct fmidi_smf_deleter {
|
||||
void operator()(fmidi_smf_t *x) const { fmidi_smf_free(x); } };
|
||||
struct fmidi_seq_deleter {
|
||||
void operator()(fmidi_seq_t *x) const { fmidi_seq_free(x); } };
|
||||
struct fmidi_player_deleter {
|
||||
void operator()(fmidi_player_t *x) const { fmidi_player_free(x); } };
|
||||
|
||||
typedef std::unique_ptr<fmidi_smf_t, fmidi_smf_deleter> fmidi_smf_u;
|
||||
typedef std::unique_ptr<fmidi_seq_t, fmidi_seq_deleter> fmidi_seq_u;
|
||||
typedef std::unique_ptr<fmidi_player_t, fmidi_player_deleter> fmidi_player_u;
|
||||
#endif
|
||||
|
||||
////////////////
|
||||
// C++ ERRORS //
|
||||
////////////////
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# include <system_error>
|
||||
FMIDI_API const std::error_category &fmidi_category();
|
||||
#endif
|
||||
2564
clients/external/fmidi/sources/fmidi/fmidi_mini.cpp
vendored
Normal file
2564
clients/external/fmidi/sources/fmidi/fmidi_mini.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "sfizz/Synth.h"
|
||||
#include "sfizz/Macros.h"
|
||||
#include "MidiHelpers.h"
|
||||
#include <absl/flags/parse.h>
|
||||
#include <absl/flags/flag.h>
|
||||
#include <absl/types/span.h>
|
||||
|
|
@ -44,33 +45,6 @@ static jack_port_t* outputPort1;
|
|||
static jack_port_t* outputPort2;
|
||||
static jack_client_t* client;
|
||||
|
||||
namespace midi {
|
||||
constexpr uint8_t statusMask { 0b11110000 };
|
||||
constexpr uint8_t channelMask { 0b00001111 };
|
||||
constexpr uint8_t noteOff { 0x80 };
|
||||
constexpr uint8_t noteOn { 0x90 };
|
||||
constexpr uint8_t polyphonicPressure { 0xA0 };
|
||||
constexpr uint8_t controlChange { 0xB0 };
|
||||
constexpr uint8_t programChange { 0xC0 };
|
||||
constexpr uint8_t channelPressure { 0xD0 };
|
||||
constexpr uint8_t pitchBend { 0xE0 };
|
||||
constexpr uint8_t systemMessage { 0xF0 };
|
||||
|
||||
constexpr uint8_t status(uint8_t midiStatusByte)
|
||||
{
|
||||
return midiStatusByte & statusMask;
|
||||
}
|
||||
constexpr uint8_t channel(uint8_t midiStatusByte)
|
||||
{
|
||||
return midiStatusByte & channelMask;
|
||||
}
|
||||
|
||||
constexpr int buildAndCenterPitch(uint8_t firstByte, uint8_t secondByte)
|
||||
{
|
||||
return (int)(((unsigned int)secondByte << 7) + (unsigned int)firstByte) - 8192;
|
||||
}
|
||||
}
|
||||
|
||||
int process(jack_nframes_t numFrames, void* arg)
|
||||
{
|
||||
auto synth = reinterpret_cast<sfz::Synth*>(arg);
|
||||
|
|
@ -90,11 +64,13 @@ int process(jack_nframes_t numFrames, void* arg)
|
|||
continue;
|
||||
|
||||
switch (midi::status(event.buffer[0])) {
|
||||
case midi::noteOff:
|
||||
case midi::noteOff: noteoff:
|
||||
// DBG("[MIDI] Note " << +event.buffer[1] << " OFF at time " << event.time);
|
||||
synth->noteOff(event.time, event.buffer[1], event.buffer[2]);
|
||||
break;
|
||||
case midi::noteOn:
|
||||
if (event.buffer[2] == 0)
|
||||
goto noteoff;
|
||||
// DBG("[MIDI] Note " << +event.buffer[1] << " ON at time " << event.time);
|
||||
synth->noteOn(event.time, event.buffer[1], event.buffer[2]);
|
||||
break;
|
||||
|
|
@ -165,6 +141,7 @@ static void done(int sig)
|
|||
ABSL_FLAG(std::string, client_name, "sfizz", "Jack client name");
|
||||
ABSL_FLAG(std::string, oversampling, "1x", "Internal oversampling factor (value values are x1, x2, x4, x8)");
|
||||
ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded value");
|
||||
ABSL_FLAG(bool, state, false, "Output the synth state in the jack loop");
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
|
@ -179,6 +156,7 @@ int main(int argc, char** argv)
|
|||
const std::string clientName = absl::GetFlag(FLAGS_client_name);
|
||||
const std::string oversampling = absl::GetFlag(FLAGS_oversampling);
|
||||
const uint32_t preload_size = absl::GetFlag(FLAGS_preload_size);
|
||||
const bool verboseState = absl::GetFlag(FLAGS_state);
|
||||
|
||||
std::cout << "Flags" << '\n';
|
||||
std::cout << "- Client name: " << clientName << '\n';
|
||||
|
|
@ -285,11 +263,14 @@ int main(int argc, char** argv)
|
|||
signal(SIGQUIT, done);
|
||||
|
||||
while (!shouldClose){
|
||||
if (verboseState) {
|
||||
std::cout << "Active voices: " << synth.getNumActiveVoices() << '\n';
|
||||
#ifndef NDEBUG
|
||||
std::cout << "Allocated buffers: " << synth.getAllocatedBuffers() << '\n';
|
||||
std::cout << "Total size: " << synth.getAllocatedBytes() << '\n';
|
||||
#endif
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
std::cout << "Closing..." << '\n';
|
||||
|
|
|
|||
195
clients/sfizz_render.cpp
Normal file
195
clients/sfizz_render.cpp
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
|
||||
#include <sndfile.hh>
|
||||
#include "sfizz/Synth.h"
|
||||
#include "sfizz/MathHelpers.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "sfizz/SIMDHelpers.h"
|
||||
#include "MidiHelpers.h"
|
||||
#include "cxxopts.hpp"
|
||||
#include <fmidi/fmidi.h>
|
||||
#include <iostream>
|
||||
|
||||
#define LOG_ERROR(ostream) std::cerr << ostream << '\n'
|
||||
#define LOG_INFO(ostream) if (verbose) { std::cout << ostream << '\n'; }
|
||||
#define ERROR_IF(check, ostream) if ((check)) { LOG_ERROR(ostream); std::exit(-1); }
|
||||
|
||||
struct CallbackData {
|
||||
sfz::Synth& synth;
|
||||
unsigned delay;
|
||||
bool finished;
|
||||
};
|
||||
|
||||
void midiCallback(const fmidi_event_t * event, void * cbdata)
|
||||
{
|
||||
auto data = reinterpret_cast<CallbackData*>(cbdata);
|
||||
|
||||
if (event->type != fmidi_event_type::fmidi_event_message)
|
||||
return;
|
||||
|
||||
switch (midi::status(event->data[0])) {
|
||||
case midi::noteOff:
|
||||
data->synth.noteOff(data->delay, event->data[1], event->data[2]);
|
||||
break;
|
||||
case midi::noteOn:
|
||||
data->synth.noteOn(data->delay, event->data[1], event->data[2]);
|
||||
break;
|
||||
case midi::polyphonicPressure:
|
||||
break;
|
||||
case midi::controlChange:
|
||||
data->synth.cc(data->delay, event->data[1], event->data[2]);
|
||||
break;
|
||||
case midi::programChange:
|
||||
break;
|
||||
case midi::channelPressure:
|
||||
break;
|
||||
case midi::pitchBend:
|
||||
data->synth.pitchWheel(data->delay, midi::buildAndCenterPitch(event->data[1], event->data[2]));
|
||||
break;
|
||||
case midi::systemMessage:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void finishedCallback(void * cbdata)
|
||||
{
|
||||
auto data = reinterpret_cast<CallbackData*>(cbdata);
|
||||
data->finished = true;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cxxopts::Options options("sfizz-render", "Render a midi file through an SFZ file using the sfizz library.");
|
||||
|
||||
unsigned blockSize { 1024 };
|
||||
int sampleRate { 48000 };
|
||||
bool verbose { false };
|
||||
bool help { false };
|
||||
bool useEOT { false };
|
||||
int oversampling { 1 };
|
||||
|
||||
options.add_options()
|
||||
("sfz", "SFZ file", cxxopts::value<std::string>())
|
||||
("midi", "Input midi file", cxxopts::value<std::string>())
|
||||
("wav", "Output wav file", cxxopts::value<std::string>())
|
||||
("b,blocksize", "Block size for the sfizz callbacks", cxxopts::value(blockSize))
|
||||
("s,samplerate", "Output sample rate", cxxopts::value(sampleRate))
|
||||
("oversampling", "Internal oversampling factor", cxxopts::value(oversampling))
|
||||
("v,verbose", "Verbose output", cxxopts::value(verbose))
|
||||
("log", "Produce logs", cxxopts::value<std::string>())
|
||||
("use-eot", "End the rendering at the last End of Track Midi message", cxxopts::value(useEOT))
|
||||
("h,help", "Show help", cxxopts::value(help))
|
||||
;
|
||||
auto params = [&]() {
|
||||
try { return options.parse(argc, argv); }
|
||||
catch (std::exception& e) {
|
||||
LOG_ERROR(e.what());
|
||||
std::exit(-1);
|
||||
}
|
||||
}();
|
||||
|
||||
if (help) {
|
||||
std::cout << options.help();
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
ERROR_IF(params.count("sfz") != 1, "Please specify a single SFZ file using --sfz");
|
||||
ERROR_IF(params.count("wav") != 1, "Please specify a single WAV file using --wav");
|
||||
ERROR_IF(params.count("midi") != 1, "Please specify a single MIDI file using --midi");
|
||||
|
||||
fs::path sfzPath = fs::current_path() / params["sfz"].as<std::string>();
|
||||
fs::path outputPath = fs::current_path() / params["wav"].as<std::string>();
|
||||
fs::path midiPath = fs::current_path() / params["midi"].as<std::string>();
|
||||
|
||||
ERROR_IF(!fs::exists(sfzPath) || !fs::is_regular_file(sfzPath),
|
||||
"SFZ file " << sfzPath.string() << " does not exist or is not a regular file");
|
||||
ERROR_IF(!fs::exists(midiPath) || !fs::is_regular_file(midiPath),
|
||||
"MIDI file " << midiPath.string() << " does not exist or is not a regular file");
|
||||
|
||||
if (fs::exists(outputPath)) {
|
||||
LOG_INFO("Output file " << outputPath.string() << " already exists and will be erased.");
|
||||
}
|
||||
|
||||
LOG_INFO("SFZ file: " << sfzPath.string());
|
||||
LOG_INFO("MIDI file: " << midiPath.string());
|
||||
LOG_INFO("Output file: " << outputPath.string());
|
||||
LOG_INFO("Block size: " << blockSize);
|
||||
LOG_INFO("Sample rate: " << sampleRate);
|
||||
|
||||
sfz::Synth synth;
|
||||
synth.setSamplesPerBlock(blockSize);
|
||||
synth.setSampleRate(sampleRate);
|
||||
synth.enableFreeWheeling();
|
||||
|
||||
if (params.count("log") > 0)
|
||||
synth.enableLogging(params["log"].as<std::string>());
|
||||
|
||||
const auto osFactor = [oversampling] {
|
||||
switch (oversampling){
|
||||
case 1:
|
||||
return sfz::Oversampling::x1;
|
||||
case 2:
|
||||
return sfz::Oversampling::x2;
|
||||
case 4:
|
||||
return sfz::Oversampling::x4;
|
||||
case 5:
|
||||
return sfz::Oversampling::x8;
|
||||
default:
|
||||
LOG_ERROR("Bad oversampling factor: " << oversampling);
|
||||
std::exit(-1);
|
||||
}
|
||||
}();
|
||||
synth.setOversamplingFactor(osFactor);
|
||||
|
||||
ERROR_IF(!synth.loadSfzFile(sfzPath), "There was an error loading the SFZ file.");
|
||||
LOG_INFO(synth.getNumRegions() << " regions in the SFZ.");
|
||||
|
||||
fmidi_smf_u midiFile { fmidi_smf_file_read(midiPath.u8string().c_str()) };
|
||||
ERROR_IF(!midiFile, "Can't read " << midiPath);
|
||||
|
||||
const auto* midiInfo = fmidi_smf_get_info(midiFile.get());
|
||||
ERROR_IF(!midiInfo, "Can't get info on the midi file");
|
||||
|
||||
LOG_INFO( midiInfo->track_count << " tracks in the SMF.");
|
||||
|
||||
if (useEOT) {
|
||||
LOG_INFO("-- Cutting the rendering at the last MIDI End of Track message");
|
||||
}
|
||||
|
||||
SndfileHandle outputFile (outputPath.u8string(), SFM_WRITE, SF_FORMAT_WAV | SF_FORMAT_PCM_16, 2, sampleRate);
|
||||
ERROR_IF(outputFile.error() != 0, "Error writing out the wav file: " << outputFile.strError());
|
||||
|
||||
auto sampleRateDouble = static_cast<double>(sampleRate);
|
||||
const double increment { 1.0 / sampleRateDouble };
|
||||
int numFramesWritten { 0 };
|
||||
sfz::AudioBuffer<float> audioBuffer { 2, blockSize };
|
||||
sfz::Buffer<float> interleavedBuffer { 2 * blockSize };
|
||||
|
||||
fmidi_player_u midiPlayer { fmidi_player_new(midiFile.get()) };
|
||||
CallbackData callbackData { synth, 0, false };
|
||||
fmidi_player_event_callback(midiPlayer.get(), &midiCallback, &callbackData);
|
||||
fmidi_player_finish_callback(midiPlayer.get(), &finishedCallback, &callbackData);
|
||||
|
||||
fmidi_player_start(midiPlayer.get());
|
||||
while (!callbackData.finished) {
|
||||
for (callbackData.delay = 0; callbackData.delay < blockSize && !callbackData.finished; callbackData.delay++)
|
||||
fmidi_player_tick(midiPlayer.get(), increment);
|
||||
synth.renderBlock(audioBuffer);
|
||||
sfz::writeInterleaved(audioBuffer.getConstSpan(0), audioBuffer.getConstSpan(1), absl::MakeSpan(interleavedBuffer));
|
||||
numFramesWritten += outputFile.writef(interleavedBuffer.data(), blockSize);
|
||||
}
|
||||
|
||||
if (!useEOT) {
|
||||
auto averagePower = sfz::meanSquared<float>(interleavedBuffer);
|
||||
while (averagePower > 1e-12f) {
|
||||
synth.renderBlock(audioBuffer);
|
||||
sfz::writeInterleaved(audioBuffer.getConstSpan(0), audioBuffer.getConstSpan(1), absl::MakeSpan(interleavedBuffer));
|
||||
numFramesWritten += outputFile.writef(interleavedBuffer.data(), blockSize);
|
||||
averagePower = sfz::meanSquared<float>(interleavedBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
outputFile.writeSync();
|
||||
LOG_INFO("Wrote " << numFramesWritten << " frames of sound data in" << outputPath.string());
|
||||
|
||||
return 0;
|
||||
}
|
||||
17
cmake/BuildType.cmake
Normal file
17
cmake/BuildType.cmake
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause #
|
||||
# Copyright (c) 2015-2017 Marcus D. Hanwell #
|
||||
# Copyright (c) 2020 Jean Pierre Cimalando #
|
||||
#------------------------------------------------------------------------------#
|
||||
|
||||
# Set a default build type if none was specified
|
||||
set(default_build_type "RelWithDebInfo")
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
|
||||
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
|
||||
STRING "Choose the type of build." FORCE)
|
||||
endif()
|
||||
|
||||
# Set the possible values of build type for cmake-gui
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
|
||||
"MinSizeRel" "RelWithDebInfo")
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
# Configuration for this plugin
|
||||
# TODO: generate version from git
|
||||
set (LV2PLUGIN_VERSION_MINOR 4)
|
||||
set (LV2PLUGIN_VERSION_MINOR 6)
|
||||
set (LV2PLUGIN_VERSION_MICRO 0)
|
||||
set (LV2PLUGIN_NAME "sfizz")
|
||||
set (LV2PLUGIN_COMMENT "SFZ sampler")
|
||||
set (LV2PLUGIN_URI "http://sfztools.github.io/sfizz")
|
||||
set (LV2PLUGIN_REPOSITORY "https://github.com/sfztools/sfizz")
|
||||
set (LV2PLUGIN_AUTHOR "Paul Ferrand")
|
||||
set (LV2PLUGIN_AUTHOR "SFZTools")
|
||||
set (LV2PLUGIN_EMAIL "paul@ferrand.cc")
|
||||
if (SFIZZ_USE_VCPKG)
|
||||
set (LV2PLUGIN_SPDX_LICENSE_ID "LGPL-3.0-only")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
include(CMakeDependentOption)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to be used")
|
||||
set(CMAKE_C_STANDARD 99 CACHE STRING "C standard to be used")
|
||||
|
||||
|
|
@ -32,7 +34,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|||
add_compile_options(-Wextra)
|
||||
add_compile_options(-ffast-math)
|
||||
add_compile_options(-fno-omit-frame-pointer) # For debugging purposes
|
||||
if (SFIZZ_SYSTEM_PROCESSOR MATCHES "^i.86$")
|
||||
if (SFIZZ_SYSTEM_PROCESSOR MATCHES "^(i.86|x86_64)$")
|
||||
add_compile_options(-msse2)
|
||||
endif()
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
|
|
@ -60,15 +62,14 @@ else()
|
|||
endif()
|
||||
|
||||
|
||||
# If we build with Clang use libc++
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT ANDROID)
|
||||
set(USE_LIBCPP ON CACHE BOOL "Use libc++ with clang")
|
||||
# If we build with Clang, optionally use libc++. Enabled by default on Apple OS.
|
||||
cmake_dependent_option(USE_LIBCPP "Use libc++ with clang" "${APPLE}"
|
||||
"CMAKE_CXX_COMPILER_ID MATCHES Clang" OFF)
|
||||
if (USE_LIBCPP)
|
||||
add_compile_options(-stdlib=libc++)
|
||||
# Presumably need the above for linking too, maybe other options missing as well
|
||||
add_link_options(-stdlib=libc++) # New command on CMake master, not in 3.12 release
|
||||
add_link_options(-lc++abi) # New command on CMake master, not in 3.12 release
|
||||
endif()
|
||||
add_compile_options(-stdlib=libc++)
|
||||
# Presumably need the above for linking too, maybe other options missing as well
|
||||
add_link_options(-stdlib=libc++) # New command on CMake master, not in 3.12 release
|
||||
add_link_options(-lc++abi) # New command on CMake master, not in 3.12 release
|
||||
endif()
|
||||
|
||||
add_library(sfizz-pugixml STATIC "src/external/pugixml/src/pugixml.cpp")
|
||||
|
|
@ -77,9 +78,22 @@ target_include_directories(sfizz-pugixml PUBLIC "src/external/pugixml/src")
|
|||
add_library(sfizz-spline STATIC "src/external/spline/spline/spline.cpp")
|
||||
target_include_directories(sfizz-spline PUBLIC "src/external/spline")
|
||||
|
||||
add_library(sfizz-tunings STATIC "src/external/tunings/src/Tunings.cpp")
|
||||
target_include_directories(sfizz-tunings PUBLIC "src/external/tunings/include")
|
||||
|
||||
include (CheckLibraryExists)
|
||||
add_library (sfizz-atomic INTERFACE)
|
||||
if (UNIX AND NOT APPLE)
|
||||
check_library_exists(atomic __atomic_load "" LIBATOMIC_FOUND)
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/check_libatomic")
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/check_libatomic/check_libatomic.c" "int main() { return 0; }")
|
||||
try_compile(SFIZZ_LINK_LIBATOMIC "${CMAKE_CURRENT_BINARY_DIR}/check_libatomic"
|
||||
SOURCES "${CMAKE_CURRENT_BINARY_DIR}/check_libatomic/check_libatomic.c"
|
||||
LINK_LIBRARIES "atomic")
|
||||
if (SFIZZ_LINK_LIBATOMIC)
|
||||
target_link_libraries (sfizz-atomic INTERFACE "atomic")
|
||||
endif()
|
||||
else()
|
||||
set(SFIZZ_LINK_LIBATOMIC FALSE)
|
||||
endif()
|
||||
|
||||
# Don't show build information when building a different project
|
||||
|
|
@ -94,10 +108,14 @@ Build as shared library: ${SFIZZ_SHARED}
|
|||
Build JACK stand-alone client: ${SFIZZ_JACK}
|
||||
Build LV2 plug-in: ${SFIZZ_LV2}
|
||||
Build VST plug-in: ${SFIZZ_VST}
|
||||
Build AU plug-in: ${SFIZZ_AU}
|
||||
Build benchmarks: ${SFIZZ_BENCHMARKS}
|
||||
Build tests: ${SFIZZ_TESTS}
|
||||
Use vcpkg: ${SFIZZ_USE_VCPKG}
|
||||
Statically link libsndfile: ${SFIZZ_STATIC_LIBSNDFILE}
|
||||
Link libatomic: ${SFIZZ_LINK_LIBATOMIC}
|
||||
Use clang libc++: ${USE_LIBCPP}
|
||||
Release asserts: ${SFIZZ_RELEASE_ASSERTS}
|
||||
|
||||
Install prefix: ${CMAKE_INSTALL_PREFIX}
|
||||
LV2 destination directory: ${LV2PLUGIN_INSTALL_DIR}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ macro(sfizz_add_simd_sources SOURCES_VAR PREFIX)
|
|||
# It needs a macro, otherwise the source properties cannot take effect.
|
||||
|
||||
list (APPEND ${SOURCES_VAR}
|
||||
${PREFIX}/sfizz/SIMDSSE.cpp
|
||||
${PREFIX}/sfizz/SIMDNEON.cpp
|
||||
${PREFIX}/sfizz/SIMDDummy.cpp)
|
||||
${PREFIX}/sfizz/SIMDHelpers.cpp
|
||||
${PREFIX}/sfizz/simd/HelpersSSE.cpp
|
||||
${PREFIX}/sfizz/simd/HelpersAVX.cpp)
|
||||
|
||||
# For CPU-dispatched X86 sources
|
||||
# Always build them for all X86 targets.
|
||||
|
|
@ -15,6 +15,7 @@ macro(sfizz_add_simd_sources SOURCES_VAR PREFIX)
|
|||
set_source_files_properties(
|
||||
${PREFIX}/sfizz/effects/impl/ResonantStringAVX.cpp
|
||||
${PREFIX}/sfizz/effects/impl/ResonantArrayAVX.cpp
|
||||
${PREFIX}/sfizz/simd/HelpersAVX.cpp
|
||||
PROPERTIES COMPILE_FLAGS "-mavx")
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -8,8 +8,11 @@ endif()
|
|||
find_package(Qt5 COMPONENTS Widgets)
|
||||
|
||||
if(JACK_FOUND AND TARGET Qt5::Widgets)
|
||||
add_executable(sfizz_capture_eg CaptureEG.cpp)
|
||||
add_executable(sfizz_capture_eg CaptureEG.h CaptureEG.cpp)
|
||||
target_include_directories(sfizz_capture_eg PRIVATE . ${JACK_INCLUDE_DIRS})
|
||||
target_link_libraries(sfizz_capture_eg PRIVATE sfizz-sndfile Qt5::Widgets ${JACK_LIBRARIES})
|
||||
set_target_properties(sfizz_capture_eg PROPERTIES AUTOUIC ON)
|
||||
endif()
|
||||
|
||||
add_executable(sfizz_preprocessor Preprocessor.cpp)
|
||||
target_link_libraries(sfizz_preprocessor sfizz_parser)
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ void Application::performSfzUpdate()
|
|||
|
||||
QString code;
|
||||
code += "<region>\n";
|
||||
code += "key=69\n";
|
||||
code += "sample="; code += QFileInfo(samplePath).fileName(); code += "\n";
|
||||
code += _ui->envelopeEdit->toPlainText();
|
||||
|
||||
|
|
|
|||
110
devtools/Preprocessor.cpp
Normal file
110
devtools/Preprocessor.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
/*
|
||||
This program reads a SFZ file, and outputs it back into a single file
|
||||
with all the includes and definitions processed.
|
||||
|
||||
It can serve to facilitate identifying problems, whether these are related to
|
||||
the parser or complicated instrument structures.
|
||||
*/
|
||||
|
||||
#include "parser/Parser.h"
|
||||
#include "../tests/cxxopts.hpp"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include <iostream>
|
||||
|
||||
class MyParserListener : public sfz::Parser::Listener {
|
||||
public:
|
||||
explicit MyParserListener(sfz::Parser& parser)
|
||||
: _parser(parser)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void onParseFullBlock(const std::string& header, const std::vector<sfz::Opcode>& opcodes) override
|
||||
{
|
||||
std::cout << '\n';
|
||||
std::cout << '<' << header << '>' << '\n';
|
||||
for (const sfz::Opcode& opc : opcodes)
|
||||
std::cout << opc.opcode << '=' << opc.value << '\n';
|
||||
}
|
||||
|
||||
void onParseError(const sfz::SourceRange& range, const std::string& message) override
|
||||
{
|
||||
const auto relativePath = range.start.filePath->lexically_relative(_parser.originalDirectory());
|
||||
std::cerr << "Parse error in " << relativePath << " at line " << range.start.lineNumber + 1 << ": " << message << '\n';
|
||||
}
|
||||
|
||||
void onParseWarning(const sfz::SourceRange& range, const std::string& message) override
|
||||
{
|
||||
const auto relativePath = range.start.filePath->lexically_relative(_parser.originalDirectory());
|
||||
std::cerr << "Parse warning in " << relativePath << " at line " << range.start.lineNumber + 1 << ": " << message << '\n';
|
||||
}
|
||||
|
||||
private:
|
||||
sfz::Parser& _parser;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
cxxopts::Options options("sfizz_preprocess", "Preprocess SFZ files");
|
||||
|
||||
options.positional_help("<sfz-file>");
|
||||
|
||||
options.add_options()
|
||||
("D,define", "Add external definition", cxxopts::value<std::vector<std::string>>())
|
||||
("i,input", "Input SFZ file", cxxopts::value<std::string>())
|
||||
("h,help", "Print usage");
|
||||
|
||||
options.parse_positional({"input"});
|
||||
|
||||
std::unique_ptr<cxxopts::ParseResult> resultPtr;
|
||||
try {
|
||||
resultPtr = absl::make_unique<cxxopts::ParseResult>(options.parse(argc, argv));
|
||||
} catch (cxxopts::OptionException& ex) {
|
||||
std::cerr << ex.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
cxxopts::ParseResult& result = *resultPtr;
|
||||
|
||||
if (result.count("help")) {
|
||||
std::cerr << options.help() << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!result.count("input")) {
|
||||
std::cerr << "Please indicate the SFZ file path.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const fs::path sfzFilePath { result["input"].as<std::string>() };
|
||||
|
||||
sfz::Parser parser;
|
||||
MyParserListener listener(parser);
|
||||
parser.setListener(&listener);
|
||||
|
||||
if (result.count("define")) {
|
||||
auto& definitions = result["define"].as<std::vector<std::string>>();
|
||||
for (absl::string_view definition : definitions) {
|
||||
size_t pos = definition.find('=');
|
||||
if (pos == definition.npos) {
|
||||
std::cerr << "The definition is malformed, should be key=value.\n";
|
||||
return 1;
|
||||
}
|
||||
absl::string_view key = definition.substr(0, pos);
|
||||
absl::string_view val = definition.substr(pos + 1);
|
||||
parser.addExternalDefinition(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
parser.parseFile(sfzFilePath);
|
||||
|
||||
if (parser.getErrorCount() > 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
target="_new">Doxygen</a> $doxygenversion</span>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div><!-- container -->
|
||||
<script src="/sfizz/assets/js/scripts.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -26,16 +26,14 @@
|
|||
content="#da532c">
|
||||
<meta name="msapplication-config" content="/sfizz/assets/ico/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link href="$relpath$tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="$relpath$jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath$dynsections.js"></script>
|
||||
<link href="$relpath$tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="$relpath$jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath$dynsections.js"></script>
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
<link href="$relpath$$stylesheet" rel="stylesheet" type="text/css" />
|
||||
<link href="$relpath$$stylesheet" rel="stylesheet" type="text/css" />
|
||||
$extrastylesheet
|
||||
|
||||
<!-- Stylesheets -->
|
||||
<link href="/sfizz/assets/css/style.min.css"
|
||||
rel="stylesheet" media="screen">
|
||||
|
|
@ -44,36 +42,107 @@ $extrastylesheet
|
|||
<script src="https://kit.fontawesome.com/8d25d19870.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<button onclick="topFunction()" id="scrollButton" title="Go to top">Top</button>
|
||||
<a href="#content" class="sr-only">Skip to content</a>
|
||||
<div class="container">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color:#222">
|
||||
<a class="navbar-brand" href="/sfizz/"><img src="/sfizz/assets/img/logo.png"
|
||||
width="30" height="30" class="d-inline-block">sfizz</a>
|
||||
|
||||
<a class="navbar-brand" href="/sfizz/">
|
||||
<img src="/sfizz/assets/img/logo.png"
|
||||
width="30" height="30" class="d-inline-block" alt="">
|
||||
sfizz</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse"
|
||||
data-target="#navbarContent" aria-controls="navbarContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarContent">
|
||||
<ul class="navbar-nav mr-auto mt-2 mt-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/sfizz/status"><i class="fas fa-tasks fa-fw"
|
||||
aria-hidden="true"></i> Status</a>
|
||||
<a class="nav-link" href="/">
|
||||
<i class="fas fa-home fa-fw" aria-hidden="true"></i>
|
||||
Home</a>
|
||||
</li>
|
||||
|
||||
<!-- FIXME: Dropdown buttons are shown but menus are broken for some reason
|
||||
(not the container div)
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#"
|
||||
id="navbarProjects" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-hammer fa-fw" aria-hidden="true"></i>
|
||||
Projects</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarProjects">
|
||||
<a class="dropdown-item"
|
||||
href="/beatbox/">
|
||||
<i class="fas fa-drum fa-fw" aria-hidden="true"></i>
|
||||
BeatBox</a>
|
||||
<a class="dropdown-item disabled"
|
||||
href="/sfizz/">
|
||||
<i class="fas fa-play-circle fa-fw" aria-hidden="true"></i>
|
||||
sfizz</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item"
|
||||
href="https://github.com/sfztools/sfizz-render/">
|
||||
sfizz-render</a>
|
||||
<a class="dropdown-item"
|
||||
href="https://github.com/sfztools/sfz-flat/">
|
||||
sfz-flat</a>
|
||||
<a class="dropdown-item"
|
||||
href="https://github.com/sfztools/sfz-opcode-checker/">
|
||||
sfz-opcode-checker</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item"
|
||||
href="https://github.com/sfztools/emacs-sfz-mode/">
|
||||
Emacs Syntax Highlight</a>
|
||||
<a class="dropdown-item"
|
||||
href="https://github.com/sfztools/syntax-highlighting-gedit/">
|
||||
gedit Syntax Highlight</a>
|
||||
<a class="dropdown-item"
|
||||
href="https://github.com/sfztools/syntax-highlighting-geany/">
|
||||
Geany Syntax Highlight</a>
|
||||
<a class="dropdown-item"
|
||||
href="https://github.com/sfztools/syntax-highlighting-sublime-text/">
|
||||
Sublime Text Syntax Highlight</a>
|
||||
<a class="dropdown-item"
|
||||
href="https://github.com/sfztools/sfz.vim">
|
||||
Vim Syntax Highlight</a>
|
||||
<a class="dropdown-item"
|
||||
href="https://github.com/sfztools/syntax-highlighting-vscode/">
|
||||
VSCode Syntax Highlight</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#"
|
||||
id="navbarDocumentation" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-book fa-fw" aria-hidden="true"></i>
|
||||
Documentation</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDocumentation">
|
||||
<a class="dropdown-item"
|
||||
href="/sfizz/api"><i class="fas fa-code fa-fw" aria-hidden="true"></i>
|
||||
API</a>
|
||||
<a class="dropdown-item"
|
||||
href="/sfizz/development/build">
|
||||
<i class="fas fa-code fa-fw" aria-hidden="true"></i>
|
||||
Build</a>
|
||||
<a class="dropdown-item"
|
||||
href="/sfizz/development/status">
|
||||
<i class="fas fa-tasks fa-fw" aria-hidden="true"></i>
|
||||
SFZ Opcodes</a>
|
||||
</div>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/sfizz/downloads">
|
||||
<i class="fas fa-download fa-fw" aria-hidden="true"></i>
|
||||
Download</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/sfizz/downloads"><i class="fas fa-download fa-fw"
|
||||
aria-hidden="true"></i> Downloads</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/sfizz/contacts"><i class="fas fa-at fa-fw"
|
||||
aria-hidden="true"></i> Contacts</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://sfztools.github.io/"><i class="fas fa-home fa-fw"
|
||||
aria-hidden="true"></i> Home</a>
|
||||
<a class="nav-link" href="/sfizz/search/">
|
||||
<i class="fas fa-search fa-fw" aria-hidden="true"></i>
|
||||
Search</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -83,4 +152,4 @@ $searchbox
|
|||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</nav>
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
|
|
|||
|
|
@ -2,4 +2,5 @@
|
|||
|
||||
SFZ file format library
|
||||
|
||||
[public API](sfizz_8h.html)
|
||||
- [public C API](sfizz_8h.html)
|
||||
- [public C++ API](classsfz_1_1_sfizz.html)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Doxyfile 1.8.16
|
||||
# Doxyfile 1.8.17
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
|
|
@ -251,8 +251,8 @@ TAB_SIZE = 4
|
|||
# commands \{ and \} for these it is advised to use the version @{ and @} or use
|
||||
# a double escape (\\{ and \\})
|
||||
|
||||
ALIASES = true=<b>true</b> \
|
||||
false=<b>false</b>
|
||||
ALIASES = "true=<b style=\"color:blue\">true</b>" \
|
||||
"false=<b style=\"color:blue\">false</b>"
|
||||
|
||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
|
|
@ -300,7 +300,7 @@ OPTIMIZE_OUTPUT_SLICE = NO
|
|||
# parses. With this tag you can assign which parser to use for a given
|
||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
|
||||
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
|
||||
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
|
||||
|
|
@ -515,7 +515,7 @@ EXTRACT_ANON_NSPACES = NO
|
|||
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
|
||||
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
|
||||
# undocumented classes that are normally visible in the class hierarchy. If set
|
||||
|
|
@ -523,11 +523,11 @@ HIDE_UNDOC_MEMBERS = YES
|
|||
# has no effect if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
|
||||
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
|
||||
# (class|struct|union) declarations. If set to NO, these declarations will be
|
||||
# included in the documentation.
|
||||
# declarations. If set to NO, these declarations will be included in the
|
||||
# documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
|
|
@ -843,17 +843,20 @@ INPUT_ENCODING = UTF-8
|
|||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
|
||||
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
|
||||
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.h \
|
||||
*.md
|
||||
*.md \
|
||||
*.hpp
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
# be searched for input files as well.
|
||||
# The default value is: NO.
|
||||
|
||||
RECURSIVE = YES
|
||||
RECURSIVE = NO
|
||||
|
||||
# The EXCLUDE tag can be used to specify files and/or directories that should be
|
||||
# excluded from the INPUT source files. This way you can easily exclude a
|
||||
|
|
@ -862,12 +865,7 @@ RECURSIVE = YES
|
|||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE = sfizz/ghc \
|
||||
sfizz/mathfuns \
|
||||
sfizz/atomicops.h \
|
||||
sfizz/MathHelpers.h \
|
||||
sfizz/readerwriterqueue.h \
|
||||
sfizz/ScopedFTZ.h
|
||||
EXCLUDE =
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
|
|
@ -1220,9 +1218,9 @@ HTML_TIMESTAMP = YES
|
|||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via Javascript. If disabled, the navigation index will
|
||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||
# consists of multiple levels of tabs that are statically embedded in every HTML
|
||||
# page. Disable this option to support browsers that do not have Javascript,
|
||||
# page. Disable this option to support browsers that do not have JavaScript,
|
||||
# like the Qt help browser.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
|
@ -1510,8 +1508,14 @@ FORMULA_FONTSIZE = 10
|
|||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||
# the section "Including formulas" for details.
|
||||
|
||||
FORMULA_MACROFILE =
|
||||
|
||||
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
|
||||
# https://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# https://www.mathjax.org) which uses client side JavaScript for the rendering
|
||||
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
|
||||
# installed or if you want to formulas look prettier in the HTML output. When
|
||||
# enabled you may also need to install MathJax separately and configure the path
|
||||
|
|
@ -1581,7 +1585,7 @@ MATHJAX_CODEFILE =
|
|||
SEARCHENGINE = YES
|
||||
|
||||
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
|
||||
# implemented using a web server instead of a web client using Javascript. There
|
||||
# implemented using a web server instead of a web client using JavaScript. There
|
||||
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
|
||||
# setting. When disabled, doxygen will generate a PHP script for searching and
|
||||
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
#!/bin/bash
|
||||
# Must be called from the root directory
|
||||
cat >>index.md <<EOF
|
||||
if [[ -f api_index.md ]]; then rm api_index.md; fi
|
||||
cat >>api_index.md <<EOF
|
||||
---
|
||||
title: "API"
|
||||
---
|
||||
EOF
|
||||
tags=()
|
||||
for tag in $(git tag --list); do
|
||||
if [[ ${tag} != "list" && ${tag} != *"test"* ]]; then
|
||||
echo "- [${tag}](${tag})" >> index.md
|
||||
tag=$(echo "${tag}" | sed -r 's/v//g')
|
||||
tags+=("${tag}")
|
||||
fi
|
||||
done
|
||||
IFS=$'\n' tags=($(sort <<<"${tags[*]}")); unset IFS
|
||||
for tag in "${tags[@]}"; do
|
||||
echo "- [${tag}](${tag})" >> api_index.md
|
||||
done
|
||||
|
|
|
|||
291
dpf.mk
Normal file
291
dpf.mk
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
#
|
||||
# A build file to help using sfizz with the DISTRHO Plugin Framework (DPF)
|
||||
# ------------------------------------------------------------------------
|
||||
#
|
||||
# Usage notes:
|
||||
#
|
||||
# 1. Start with the template DPF project
|
||||
#
|
||||
# (for example https://github.com/SpotlightKid/cookiecutter-dpf-effect)
|
||||
#
|
||||
# 2. In the top directory of the DPF project, edit `Makefile`
|
||||
#
|
||||
# Add the following line, after the `all` rule definition:
|
||||
#
|
||||
# include <path-to-sfizz>/dpf.mk
|
||||
#
|
||||
# Add `sfizz-lib` to the `libs` rule definition:
|
||||
#
|
||||
# libs: sfizz-lib
|
||||
#
|
||||
# If you want you can add `sfizz-clean` to the `clean` rule.
|
||||
#
|
||||
# clean: sfizz-clean
|
||||
#
|
||||
# 3. In the Makefile of your plugin folder, eg. `plugins/MyPlugin/Makefile`
|
||||
#
|
||||
# Add the following line, after `include ../../dpf/Makefile.plugins.mk`
|
||||
#
|
||||
# include <path-to-sfizz>/dpf.mk
|
||||
# BUILD_C_FLAGS += $(SFIZZ_C_FLAGS)
|
||||
# BUILD_CXX_FLAGS += $(SFIZZ_CXX_FLAGS)
|
||||
# LINK_FLAGS += $(SFIZZ_LINK_FLAGS)
|
||||
#
|
||||
# 4. Build using `make` from the top folder.
|
||||
#
|
||||
|
||||
SFIZZ_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
SFIZZ_BUILD_DIR := $(SFIZZ_DIR)/dpf-build
|
||||
|
||||
SFIZZ_C_FLAGS = -I$(SFIZZ_DIR)/src
|
||||
SFIZZ_CXX_FLAGS = $(SFIZZ_C_FLAGS)
|
||||
SFIZZ_LINK_FLAGS = $(SFIZZ_BUILD_DIR)/libsfizz.a
|
||||
|
||||
ifeq ($(LINUX),true)
|
||||
SFIZZ_LINK_FLAGS += -pthread
|
||||
endif
|
||||
|
||||
sfizz-all: sfizz-lib
|
||||
|
||||
sfizz-lib: $(SFIZZ_BUILD_DIR)/libsfizz.a
|
||||
|
||||
sfizz-clean:
|
||||
rm -rf $(SFIZZ_BUILD_DIR)
|
||||
|
||||
.PHONY: sfizz-all sfizz-lib sfizz-clean
|
||||
|
||||
SFIZZ_SOURCES = \
|
||||
src/sfizz/ADSREnvelope.cpp \
|
||||
src/sfizz/Curve.cpp \
|
||||
src/sfizz/effects/Apan.cpp \
|
||||
src/sfizz/Effects.cpp \
|
||||
src/sfizz/effects/Eq.cpp \
|
||||
src/sfizz/effects/Filter.cpp \
|
||||
src/sfizz/effects/Gain.cpp \
|
||||
src/sfizz/effects/impl/ResonantArrayAVX.cpp \
|
||||
src/sfizz/effects/impl/ResonantArray.cpp \
|
||||
src/sfizz/effects/impl/ResonantArraySSE.cpp \
|
||||
src/sfizz/effects/impl/ResonantStringAVX.cpp \
|
||||
src/sfizz/effects/impl/ResonantString.cpp \
|
||||
src/sfizz/effects/impl/ResonantStringSSE.cpp \
|
||||
src/sfizz/effects/Limiter.cpp \
|
||||
src/sfizz/effects/Lofi.cpp \
|
||||
src/sfizz/effects/Nothing.cpp \
|
||||
src/sfizz/effects/Rectify.cpp \
|
||||
src/sfizz/effects/Strings.cpp \
|
||||
src/sfizz/effects/Width.cpp \
|
||||
src/sfizz/EQPool.cpp \
|
||||
src/sfizz/FileId.cpp \
|
||||
src/sfizz/FileInstrument.cpp \
|
||||
src/sfizz/FilePool.cpp \
|
||||
src/sfizz/FilterPool.cpp \
|
||||
src/sfizz/FloatEnvelopes.cpp \
|
||||
src/sfizz/Logger.cpp \
|
||||
src/sfizz/MidiState.cpp \
|
||||
src/sfizz/OpcodeCleanup.cpp \
|
||||
src/sfizz/Opcode.cpp \
|
||||
src/sfizz/Oversampler.cpp \
|
||||
src/sfizz/Panning.cpp \
|
||||
src/sfizz/Parser.cpp \
|
||||
src/sfizz/parser/Parser.cpp \
|
||||
src/sfizz/parser/ParserPrivate.cpp \
|
||||
src/sfizz/Region.cpp \
|
||||
src/sfizz/RTSemaphore.cpp \
|
||||
src/sfizz/ScopedFTZ.cpp \
|
||||
src/sfizz/sfizz.cpp \
|
||||
src/sfizz/sfizz_wrapper.cpp \
|
||||
src/sfizz/SfzFilter.cpp \
|
||||
src/sfizz/SfzHelpers.cpp \
|
||||
src/sfizz/SIMDHelpers.cpp \
|
||||
src/sfizz/simd/HelpersSSE.cpp \
|
||||
src/sfizz/simd/HelpersAVX.cpp \
|
||||
src/sfizz/Synth.cpp \
|
||||
src/sfizz/Tuning.cpp \
|
||||
src/sfizz/Voice.cpp \
|
||||
src/sfizz/Wavetables.cpp
|
||||
|
||||
### Other internal
|
||||
|
||||
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/sfizz
|
||||
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/external
|
||||
|
||||
# Sndfile dependency
|
||||
|
||||
SFIZZ_C_FLAGS += $(shell $(PKG_CONFIG) --cflags sndfile)
|
||||
SFIZZ_LINK_FLAGS += $(shell $(PKG_CONFIG) --libs sndfile)
|
||||
|
||||
### Abseil dependency
|
||||
|
||||
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/external/abseil-cpp
|
||||
# absl::base
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/base/internal/cycleclock.cc \
|
||||
external/abseil-cpp/absl/base/internal/spinlock.cc \
|
||||
external/abseil-cpp/absl/base/internal/sysinfo.cc \
|
||||
external/abseil-cpp/absl/base/internal/thread_identity.cc \
|
||||
external/abseil-cpp/absl/base/internal/unscaledcycleclock.cc
|
||||
# absl::exponential_biased
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/base/internal/exponential_biased.cc
|
||||
# absl::dynamic_annotations
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/base/dynamic_annotations.cc
|
||||
# absl::malloc_internal
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/base/internal/low_level_alloc.cc
|
||||
# absl::raw_logging_internal
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/base/internal/raw_logging.cc
|
||||
# absl::spinlock_wait
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/base/internal/spinlock_wait.cc
|
||||
# absl::throw_delegate
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/base/internal/throw_delegate.cc
|
||||
# absl::strings
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/strings/ascii.cc \
|
||||
external/abseil-cpp/absl/strings/charconv.cc \
|
||||
external/abseil-cpp/absl/strings/escaping.cc \
|
||||
external/abseil-cpp/absl/strings/internal/charconv_bigint.cc \
|
||||
external/abseil-cpp/absl/strings/internal/charconv_parse.cc \
|
||||
external/abseil-cpp/absl/strings/internal/memutil.cc \
|
||||
external/abseil-cpp/absl/strings/match.cc \
|
||||
external/abseil-cpp/absl/strings/numbers.cc \
|
||||
external/abseil-cpp/absl/strings/str_cat.cc \
|
||||
external/abseil-cpp/absl/strings/str_replace.cc \
|
||||
external/abseil-cpp/absl/strings/str_split.cc \
|
||||
external/abseil-cpp/absl/strings/string_view.cc \
|
||||
external/abseil-cpp/absl/strings/substitute.cc
|
||||
# absl::hashtablez_sampler
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/container/internal/hashtablez_sampler.cc \
|
||||
external/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
|
||||
# absl::raw_hash_set
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/container/internal/raw_hash_set.cc
|
||||
# absl::synchronization
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/synchronization/barrier.cc \
|
||||
external/abseil-cpp/absl/synchronization/blocking_counter.cc \
|
||||
external/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc \
|
||||
external/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc \
|
||||
external/abseil-cpp/absl/synchronization/internal/waiter.cc \
|
||||
external/abseil-cpp/absl/synchronization/notification.cc \
|
||||
external/abseil-cpp/absl/synchronization/mutex.cc
|
||||
# absl::graphcycles_internal
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/synchronization/internal/graphcycles.cc
|
||||
# absl::time
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/time/civil_time.cc \
|
||||
external/abseil-cpp/absl/time/clock.cc \
|
||||
external/abseil-cpp/absl/time/duration.cc \
|
||||
external/abseil-cpp/absl/time/format.cc \
|
||||
external/abseil-cpp/absl/time/time.cc
|
||||
# absl::time_zone
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc \
|
||||
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc \
|
||||
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.cc \
|
||||
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc \
|
||||
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc \
|
||||
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc \
|
||||
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc \
|
||||
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.cc \
|
||||
external/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc
|
||||
# absl::stacktrace
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/debugging/stacktrace.cc
|
||||
# absl::symbolize
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/debugging/symbolize.cc
|
||||
# absl::demangle_internal
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/debugging/internal/demangle.cc
|
||||
# absl::debugging_internal
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/debugging/internal/address_is_readable.cc \
|
||||
external/abseil-cpp/absl/debugging/internal/elf_mem_image.cc \
|
||||
external/abseil-cpp/absl/debugging/internal/vdso_support.cc
|
||||
# absl::hash
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/hash/internal/hash.cc
|
||||
# absl::city
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/hash/internal/city.cc
|
||||
# absl::int128
|
||||
SFIZZ_SOURCES += \
|
||||
external/abseil-cpp/absl/numeric/int128.cc
|
||||
|
||||
### Spline dependency
|
||||
|
||||
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/external/spline
|
||||
SFIZZ_SOURCES += src/external/spline/spline/spline.cpp
|
||||
|
||||
### Cpuid dependency
|
||||
|
||||
SFIZZ_C_FLAGS += \
|
||||
-I$(SFIZZ_DIR)/src/external/cpuid/src \
|
||||
-I$(SFIZZ_DIR)/src/external/cpuid/platform/src
|
||||
SFIZZ_SOURCES += \
|
||||
src/external/cpuid/src/cpuid/cpuinfo.cpp \
|
||||
src/external/cpuid/src/cpuid/version.cpp
|
||||
|
||||
### Pugixml dependency
|
||||
|
||||
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/external/pugixml/src
|
||||
SFIZZ_SOURCES += src/external/pugixml/src/pugixml.cpp
|
||||
|
||||
### Kissfft dependency
|
||||
|
||||
SFIZZ_C_FLAGS += \
|
||||
-I$(SFIZZ_DIR)/src/external/kiss_fft \
|
||||
-I$(SFIZZ_DIR)/src/external/kiss_fft/tools
|
||||
SFIZZ_SOURCES += \
|
||||
src/external/kiss_fft/kiss_fft.c \
|
||||
src/external/kiss_fft/tools/kiss_fftr.c
|
||||
|
||||
###
|
||||
|
||||
SFIZZ_OBJECTS = $(SFIZZ_SOURCES:%=$(SFIZZ_BUILD_DIR)/%.o)
|
||||
|
||||
$(SFIZZ_BUILD_DIR)/libsfizz.a: $(SFIZZ_OBJECTS)
|
||||
-@mkdir -p $(dir $@)
|
||||
@echo "Creating $@"
|
||||
$(SILENT)$(AR) crs $@ $^
|
||||
|
||||
###
|
||||
|
||||
ifeq ($(CPU_I386_OR_X86_64),true)
|
||||
|
||||
$(SFIZZ_BUILD_DIR)/%SSE.cpp.o: $(SFIZZ_DIR)/%SSE.cpp
|
||||
-@mkdir -p $(dir $@)
|
||||
@echo "Compiling $<"
|
||||
$(SILENT)$(CXX) $(BUILD_CXX_FLAGS) $(SFIZZ_CXX_FLAGS) -msse -c -o $@ $<
|
||||
|
||||
$(SFIZZ_BUILD_DIR)/%AVX.cpp.o: $(SFIZZ_DIR)/%AVX.cpp
|
||||
-@mkdir -p $(dir $@)
|
||||
@echo "Compiling $<"
|
||||
$(SILENT)$(CXX) $(BUILD_CXX_FLAGS) $(SFIZZ_CXX_FLAGS) -mavx -c -o $@ $<
|
||||
|
||||
endif
|
||||
|
||||
###
|
||||
|
||||
$(SFIZZ_BUILD_DIR)/%.cpp.o: $(SFIZZ_DIR)/%.cpp
|
||||
-@mkdir -p $(dir $@)
|
||||
@echo "Compiling $<"
|
||||
$(SILENT)$(CXX) $(BUILD_CXX_FLAGS) $(SFIZZ_CXX_FLAGS) -c -o $@ $<
|
||||
|
||||
$(SFIZZ_BUILD_DIR)/%.cc.o: $(SFIZZ_DIR)/%.cc
|
||||
-@mkdir -p $(dir $@)
|
||||
@echo "Compiling $<"
|
||||
$(SILENT)$(CXX) $(BUILD_CXX_FLAGS) $(SFIZZ_CXX_FLAGS) -c -o $@ $<
|
||||
|
||||
$(SFIZZ_BUILD_DIR)/%.c.o: $(SFIZZ_DIR)/%.c
|
||||
-@mkdir -p $(dir $@)
|
||||
@echo "Compiling $<"
|
||||
$(SILENT)$(CC) $(BUILD_C_FLAGS) $(SFIZZ_C_FLAGS) -c -o $@ $<
|
||||
|
||||
-include $(SFIZZ_OBJECTS:%.o=%.d)
|
||||
|
|
@ -11,7 +11,13 @@ set (LV2PLUGIN_TTL_SRC_FILES
|
|||
manifest.ttl.in
|
||||
${PROJECT_NAME}.ttl.in
|
||||
)
|
||||
add_library (${LV2PLUGIN_PRJ_NAME} MODULE ${PROJECT_NAME}.c ${LV2PLUGIN_TTL_SRC_FILES})
|
||||
source_group("Turtle Files" FILES
|
||||
${LV2PLUGIN_TTL_SRC_FILES}
|
||||
)
|
||||
add_library (${LV2PLUGIN_PRJ_NAME} MODULE
|
||||
${PROJECT_NAME}.c
|
||||
atomic_compat.h
|
||||
${LV2PLUGIN_TTL_SRC_FILES})
|
||||
target_link_libraries (${LV2PLUGIN_PRJ_NAME} ${PROJECT_NAME}::${PROJECT_NAME})
|
||||
# Explicitely strip all symbols on Linux but lv2_descriptor()
|
||||
# MacOS linker does not support this apparently https://bugs.webkit.org/show_bug.cgi?id=144555
|
||||
|
|
@ -43,7 +49,19 @@ if (SFIZZ_USE_VCPKG OR SFIZZ_STATIC_LIBSNDFILE OR CMAKE_CXX_COMPILER_ID MATCHES
|
|||
file(COPY "lgpl-3.0.txt" DESTINATION ${PROJECT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
# Copy resource files into the bundle
|
||||
set(LV2_RESOURCES
|
||||
DefaultInstrument.sfz
|
||||
DefaultScale.scl)
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Resources")
|
||||
foreach(res ${LV2_RESOURCES})
|
||||
file (COPY "${CMAKE_CURRENT_SOURCE_DIR}/resources/${res}"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/Resources")
|
||||
endforeach()
|
||||
|
||||
# Installation
|
||||
if (NOT MSVC)
|
||||
install (DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR})
|
||||
install (DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR}
|
||||
COMPONENT "lv2")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
|
||||
<@LV2PLUGIN_URI@>
|
||||
a lv2:Plugin ;
|
||||
lv2:binary <@PROJECT_NAME@@CMAKE_SHARED_LIBRARY_SUFFIX@> ;
|
||||
lv2:binary <@PROJECT_NAME@@CMAKE_SHARED_MODULE_SUFFIX@> ;
|
||||
rdfs:seeAlso <@PROJECT_NAME@.ttl> .
|
||||
|
|
|
|||
3
lv2/resources/DefaultInstrument.sfz
Normal file
3
lv2/resources/DefaultInstrument.sfz
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<region>
|
||||
sample=*sine
|
||||
ampeg_attack=0.02 ampeg_release=0.1
|
||||
14
lv2/resources/DefaultScale.scl
Normal file
14
lv2/resources/DefaultScale.scl
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Equal temperament
|
||||
12
|
||||
100.0
|
||||
200.0
|
||||
300.0
|
||||
400.0
|
||||
500.0
|
||||
600.0
|
||||
700.0
|
||||
800.0
|
||||
900.0
|
||||
1000.0
|
||||
1100.0
|
||||
1200.0
|
||||
612
lv2/sfizz.c
612
lv2/sfizz.c
|
|
@ -46,6 +46,7 @@
|
|||
#include <lv2/worker/worker.h>
|
||||
#include <lv2/log/logger.h>
|
||||
#include <lv2/log/log.h>
|
||||
#include <lv2/time/time.h>
|
||||
|
||||
#include <ardour/lv2_extensions.h>
|
||||
|
||||
|
|
@ -57,10 +58,10 @@
|
|||
|
||||
#include "atomic_compat.h"
|
||||
|
||||
#define DEFAULT_SFZ_FILE "/home/paul/Documents/AVL_Percussions/AVL_Drumkits_Percussion-1.0-Alt.sfz"
|
||||
#define SFIZZ_URI "http://sfztools.github.io/sfizz"
|
||||
#define SFIZZ_PREFIX SFIZZ_URI "#"
|
||||
#define SFIZZ__sfzFile "http://sfztools.github.io/sfizz:sfzfile"
|
||||
#define SFIZZ__tuningfile "http://sfztools.github.io/sfizz:tuningfile"
|
||||
#define SFIZZ__numVoices "http://sfztools.github.io/sfizz:numvoices"
|
||||
#define SFIZZ__preloadSize "http://sfztools.github.io/sfizz:preload_size"
|
||||
#define SFIZZ__oversampling "http://sfztools.github.io/sfizz:oversampling"
|
||||
|
|
@ -81,6 +82,11 @@
|
|||
#define LOG_SAMPLE_COUNT 48000
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
#define DEFAULT_SCALA_FILE "Resources/DefaultScale.scl"
|
||||
#define DEFAULT_SFZ_FILE "Resources/DefaultInstrument.sfz"
|
||||
// This assumes that the longest path is the default sfz file; if not, change it
|
||||
#define MAX_BUNDLE_PATH_SIZE (MAX_PATH_SIZE - sizeof(DEFAULT_SFZ_FILE))
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define LV2_DEBUG(...) lv2_log_note(&self->logger, "[DEBUG] " __VA_ARGS__)
|
||||
#else
|
||||
|
|
@ -105,10 +111,13 @@ typedef struct
|
|||
const float *oversampling_port;
|
||||
const float *preload_port;
|
||||
const float *freewheel_port;
|
||||
const float *scala_root_key_port;
|
||||
const float *tuning_frequency_port;
|
||||
const float *stretch_tuning_port;
|
||||
|
||||
// Atom forge
|
||||
LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread
|
||||
LV2_Atom_Forge_Frame notify_frame; ///< Cached for worker replies
|
||||
LV2_Atom_Forge forge_secondary; ///< Forge for writing into other buffers
|
||||
|
||||
// Logger
|
||||
LV2_Log_Logger logger;
|
||||
|
|
@ -132,25 +141,31 @@ typedef struct
|
|||
LV2_URID patch_body_uri;
|
||||
LV2_URID state_changed_uri;
|
||||
LV2_URID sfizz_sfz_file_uri;
|
||||
LV2_URID sfizz_scala_file_uri;
|
||||
LV2_URID sfizz_num_voices_uri;
|
||||
LV2_URID sfizz_preload_size_uri;
|
||||
LV2_URID sfizz_oversampling_uri;
|
||||
LV2_URID sfizz_log_status_uri;
|
||||
LV2_URID sfizz_check_modification_uri;
|
||||
LV2_URID time_position_uri;
|
||||
|
||||
// Sfizz related data
|
||||
sfizz_synth_t *synth;
|
||||
bool expect_nominal_block_length;
|
||||
char sfz_file_path[MAX_PATH_SIZE];
|
||||
char scala_file_path[MAX_PATH_SIZE];
|
||||
int num_voices;
|
||||
unsigned int preload_size;
|
||||
sfizz_oversampling_factor_t oversampling;
|
||||
// TODO: use atomic flags
|
||||
volatile bool changing_state;
|
||||
float stretch_tuning;
|
||||
volatile bool check_modification;
|
||||
int max_block_size;
|
||||
int sample_counter;
|
||||
float sample_rate;
|
||||
atomic_int must_update_midnam;
|
||||
|
||||
// Paths
|
||||
char bundle_path[MAX_BUNDLE_PATH_SIZE];
|
||||
} sfizz_plugin_t;
|
||||
|
||||
enum
|
||||
|
|
@ -163,7 +178,24 @@ enum
|
|||
SFIZZ_POLYPHONY = 5,
|
||||
SFIZZ_OVERSAMPLING = 6,
|
||||
SFIZZ_PRELOAD = 7,
|
||||
SFIZZ_FREEWHEELING = 8
|
||||
SFIZZ_FREEWHEELING = 8,
|
||||
SFIZZ_SCALA_ROOT_KEY = 9,
|
||||
SFIZZ_TUNING_FREQUENCY = 10,
|
||||
SFIZZ_STRETCH_TUNING = 11,
|
||||
};
|
||||
|
||||
static void
|
||||
sfizz_lv2_state_free_path(LV2_State_Free_Path_Handle handle,
|
||||
char *path)
|
||||
{
|
||||
(void)handle;
|
||||
free(path);
|
||||
}
|
||||
|
||||
static LV2_State_Free_Path sfizz_State_Free_Path =
|
||||
{
|
||||
.handle = NULL,
|
||||
.free_path = &sfizz_lv2_state_free_path,
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
@ -187,11 +219,13 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
|
|||
self->patch_value_uri = map->map(map->handle, LV2_PATCH__value);
|
||||
self->state_changed_uri = map->map(map->handle, LV2_STATE__StateChanged);
|
||||
self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
|
||||
self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile);
|
||||
self->sfizz_num_voices_uri = map->map(map->handle, SFIZZ__numVoices);
|
||||
self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize);
|
||||
self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling);
|
||||
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
|
||||
self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification);
|
||||
self->time_position_uri = map->map(map->handle, LV2_TIME__Position);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -229,6 +263,15 @@ connect_port(LV2_Handle instance,
|
|||
case SFIZZ_FREEWHEELING:
|
||||
self->freewheel_port = (const float *)data;
|
||||
break;
|
||||
case SFIZZ_SCALA_ROOT_KEY:
|
||||
self->scala_root_key_port = (const float *)data;
|
||||
break;
|
||||
case SFIZZ_TUNING_FREQUENCY:
|
||||
self->tuning_frequency_port = (const float *)data;
|
||||
break;
|
||||
case SFIZZ_STRETCH_TUNING:
|
||||
self->stretch_tuning_port = (const float *)data;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -262,14 +305,27 @@ sfizz_lv2_parse_sample_rate(sfizz_plugin_t* self, const LV2_Options_Option* opt)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_get_default_sfz_path(LV2_Handle instance, char *path, size_t size)
|
||||
{
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
snprintf(path, size, "%s/%s", self->bundle_path, DEFAULT_SFZ_FILE);
|
||||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_get_default_scala_path(LV2_Handle instance, char *path, size_t size)
|
||||
{
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
snprintf(path, size, "%s/%s", self->bundle_path, DEFAULT_SCALA_FILE);
|
||||
}
|
||||
|
||||
static LV2_Handle
|
||||
instantiate(const LV2_Descriptor *descriptor,
|
||||
double rate,
|
||||
const char *path,
|
||||
const char *bundle_path,
|
||||
const LV2_Feature *const *features)
|
||||
{
|
||||
UNUSED(descriptor);
|
||||
UNUSED(path);
|
||||
LV2_Options_Option *options = NULL;
|
||||
bool supports_bounded_block_size = false;
|
||||
bool options_has_block_size = false;
|
||||
|
|
@ -280,15 +336,22 @@ instantiate(const LV2_Descriptor *descriptor,
|
|||
if (!self)
|
||||
return NULL;
|
||||
|
||||
LV2_Handle instance = (LV2_Handle)self;
|
||||
|
||||
strncpy(self->bundle_path, bundle_path, MAX_BUNDLE_PATH_SIZE);
|
||||
self->bundle_path[MAX_BUNDLE_PATH_SIZE - 1] = '\0';
|
||||
|
||||
// Set defaults
|
||||
self->max_block_size = MAX_BLOCK_SIZE;
|
||||
self->sample_rate = (float)rate;
|
||||
self->expect_nominal_block_length = false;
|
||||
self->sfz_file_path[0] = '\0';
|
||||
self->scala_file_path[0] = '\0';
|
||||
self->num_voices = DEFAULT_VOICES;
|
||||
self->oversampling = DEFAULT_OVERSAMPLING;
|
||||
self->preload_size = DEFAULT_PRELOAD;
|
||||
self->changing_state = false;
|
||||
self->stretch_tuning = 0.0f;
|
||||
self->check_modification = false;
|
||||
self->sample_counter = 0;
|
||||
|
||||
// Get the features from the host and populate the structure
|
||||
|
|
@ -345,11 +408,12 @@ instantiate(const LV2_Descriptor *descriptor,
|
|||
|
||||
// Initialize the forge
|
||||
lv2_atom_forge_init(&self->forge, self->map);
|
||||
lv2_atom_forge_init(&self->forge_secondary, self->map);
|
||||
|
||||
// Check the options for the block size and sample rate parameters
|
||||
if (options)
|
||||
{
|
||||
for (const LV2_Options_Option *opt = options; opt->value; ++opt)
|
||||
for (const LV2_Options_Option *opt = options; opt->key || opt->value; ++opt)
|
||||
{
|
||||
if (opt->key == self->sample_rate_uri)
|
||||
{
|
||||
|
|
@ -394,6 +458,13 @@ instantiate(const LV2_Descriptor *descriptor,
|
|||
}
|
||||
|
||||
self->synth = sfizz_create_synth();
|
||||
|
||||
sfizz_lv2_get_default_sfz_path(instance, self->sfz_file_path, MAX_PATH_SIZE);
|
||||
sfizz_lv2_get_default_scala_path(instance, self->scala_file_path, MAX_PATH_SIZE);
|
||||
|
||||
sfizz_load_file(self->synth, self->sfz_file_path);
|
||||
sfizz_load_scala_file(self->synth, self->scala_file_path);
|
||||
|
||||
return (LV2_Handle)self;
|
||||
}
|
||||
|
||||
|
|
@ -411,6 +482,7 @@ activate(LV2_Handle instance)
|
|||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
sfizz_set_samples_per_block(self->synth, self->max_block_size);
|
||||
sfizz_set_sample_rate(self->synth, self->sample_rate);
|
||||
atomic_store(&self->must_update_midnam, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -421,16 +493,20 @@ deactivate(LV2_Handle instance)
|
|||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_send_file_path(sfizz_plugin_t *self)
|
||||
sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_URID urid, const char *path)
|
||||
{
|
||||
LV2_Atom_Forge_Frame frame;
|
||||
lv2_atom_forge_frame_time(&self->forge, 0);
|
||||
lv2_atom_forge_object(&self->forge, &frame, 0, self->patch_set_uri);
|
||||
lv2_atom_forge_key(&self->forge, self->patch_property_uri);
|
||||
lv2_atom_forge_urid(&self->forge, self->sfizz_sfz_file_uri);
|
||||
lv2_atom_forge_key(&self->forge, self->patch_value_uri);
|
||||
lv2_atom_forge_path(&self->forge, self->sfz_file_path, (uint32_t)strlen(self->sfz_file_path));
|
||||
lv2_atom_forge_pop(&self->forge, &frame);
|
||||
|
||||
bool write_ok =
|
||||
lv2_atom_forge_frame_time(&self->forge, 0) &&
|
||||
lv2_atom_forge_object(&self->forge, &frame, 0, self->patch_set_uri) &&
|
||||
lv2_atom_forge_key(&self->forge, self->patch_property_uri) &&
|
||||
lv2_atom_forge_urid(&self->forge, urid) &&
|
||||
lv2_atom_forge_key(&self->forge, self->patch_value_uri) &&
|
||||
lv2_atom_forge_path(&self->forge, path, (uint32_t)strlen(path));
|
||||
|
||||
if (write_ok)
|
||||
lv2_atom_forge_pop(&self->forge, &frame);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -466,16 +542,29 @@ sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, const LV2_Atom_Object *obj)
|
|||
return;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LV2_Atom atom;
|
||||
char body[MAX_PATH_SIZE];
|
||||
} sfizz_path_atom_buffer_t;
|
||||
|
||||
if (key == self->sfizz_sfz_file_uri)
|
||||
{
|
||||
const uint32_t original_atom_size = lv2_atom_total_size((const LV2_Atom *)atom);
|
||||
const uint32_t null_terminated_atom_size = original_atom_size + 1;
|
||||
char atom_buffer[MAX_PATH_SIZE];
|
||||
memcpy(&atom_buffer, atom, original_atom_size);
|
||||
atom_buffer[original_atom_size] = 0; // Null terminate the string for safety
|
||||
LV2_Atom *sfz_file_path = (LV2_Atom *)&atom_buffer;
|
||||
sfz_file_path->type = self->sfizz_sfz_file_uri;
|
||||
self->worker->schedule_work(self->worker->handle, null_terminated_atom_size, sfz_file_path);
|
||||
LV2_Atom_Forge *forge = &self->forge_secondary;
|
||||
sfizz_path_atom_buffer_t buffer;
|
||||
lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer));
|
||||
if (lv2_atom_forge_typed_string(forge, self->sfizz_sfz_file_uri, LV2_ATOM_BODY_CONST(atom), strnlen(LV2_ATOM_BODY_CONST(atom), atom->size)))
|
||||
self->worker->schedule_work(self->worker->handle, lv2_atom_total_size(&buffer.atom), &buffer.atom);
|
||||
self->check_modification = false;
|
||||
}
|
||||
else if (key == self->sfizz_scala_file_uri)
|
||||
{
|
||||
LV2_Atom_Forge *forge = &self->forge_secondary;
|
||||
sfizz_path_atom_buffer_t buffer;
|
||||
lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer));
|
||||
if (lv2_atom_forge_typed_string(forge, self->sfizz_scala_file_uri, LV2_ATOM_BODY_CONST(atom), strnlen(LV2_ATOM_BODY_CONST(atom), atom->size)))
|
||||
self->worker->schedule_work(self->worker->handle, lv2_atom_total_size(&buffer.atom), &buffer.atom);
|
||||
self->check_modification = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -495,28 +584,27 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
|||
switch (lv2_midi_message_type(msg))
|
||||
{
|
||||
case LV2_MIDI_MSG_NOTE_ON:
|
||||
// LV2_DEBUG("[process_midi] Received note on %d/%d at time %ld\n", msg[0], msg[1], ev->time.frames);
|
||||
if (msg[2] == 0)
|
||||
goto noteoff; // 0 velocity note-ons should be forbidden but just in case...
|
||||
|
||||
sfizz_send_note_on(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
case LV2_MIDI_MSG_NOTE_OFF:
|
||||
// LV2_DEBUG("[process_midi] Received note off %d/%d at time %ld\n", msg[0], msg[1], ev->time.frames);
|
||||
case LV2_MIDI_MSG_NOTE_OFF: noteoff:
|
||||
sfizz_send_note_off(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
case LV2_MIDI_MSG_CONTROLLER:
|
||||
// LV2_DEBUG("[process_midi] Received CC %d/%d at time %ld\n", msg[0], msg[1], ev->time.frames);
|
||||
sfizz_send_cc(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
case LV2_MIDI_MSG_BENDER:
|
||||
// LV2_DEBUG("[process_midi] Received pitch bend %d on channel %d at time %ld\n", PITCH_BUILD_AND_CENTER(msg[1], msg[2]), MIDI_CHANNEL(msg[0]), ev->time.frames);
|
||||
sfizz_send_pitch_wheel(self->synth,
|
||||
(int)ev->time.frames,
|
||||
PITCH_BUILD_AND_CENTER(msg[1], msg[2]));
|
||||
|
|
@ -535,22 +623,41 @@ sfizz_lv2_status_log(sfizz_plugin_t *self)
|
|||
// lv2_log_note(&self->logger, "[sfizz] Active voices: %d\n", sfizz_get_num_active_voices(self->synth));
|
||||
}
|
||||
|
||||
static int next_pow_2(int v)
|
||||
{
|
||||
if (v < 1)
|
||||
return 1;
|
||||
|
||||
// Bit twiddling hack
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
return v;
|
||||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_check_oversampling(sfizz_plugin_t* self)
|
||||
{
|
||||
sfizz_oversampling_factor_t oversampling = (sfizz_oversampling_factor_t)*self->oversampling_port;
|
||||
if (oversampling != self->oversampling)
|
||||
int port_value = next_pow_2((int)*self->oversampling_port);
|
||||
if (port_value == (int)self->oversampling)
|
||||
return;
|
||||
|
||||
self->oversampling = (sfizz_oversampling_factor_t)port_value;
|
||||
|
||||
LV2_Atom_Int atom = {
|
||||
.atom.type = self->sfizz_oversampling_uri,
|
||||
.atom.size = sizeof(int),
|
||||
.body = self->oversampling
|
||||
};
|
||||
if (self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((LV2_Atom *)&atom),
|
||||
&atom) != LV2_WORKER_SUCCESS)
|
||||
{
|
||||
LV2_Atom_Int atom;
|
||||
atom.atom.type = self->sfizz_oversampling_uri;
|
||||
atom.atom.size = sizeof(int);
|
||||
atom.body = oversampling;
|
||||
if (self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((LV2_Atom *)&atom),
|
||||
&atom) != LV2_WORKER_SUCCESS)
|
||||
{
|
||||
lv2_log_error(&self->logger, "[sfizz] There was an issue changing the oversampling factor\n");
|
||||
}
|
||||
lv2_log_error(&self->logger, "[sfizz] There was an issue changing the oversampling factor\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -560,16 +667,18 @@ sfizz_lv2_check_preload_size(sfizz_plugin_t* self)
|
|||
unsigned int preload_size = (int)*self->preload_port;
|
||||
if (preload_size != self->preload_size)
|
||||
{
|
||||
LV2_Atom_Int atom;
|
||||
atom.atom.type = self->sfizz_preload_size_uri;
|
||||
atom.atom.size = sizeof(int);
|
||||
atom.body = preload_size;
|
||||
LV2_Atom_Int atom = {
|
||||
.atom.type = self->sfizz_preload_size_uri,
|
||||
.atom.size = sizeof(int),
|
||||
.body = preload_size
|
||||
};
|
||||
if (self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((LV2_Atom *)&atom),
|
||||
&atom) != LV2_WORKER_SUCCESS)
|
||||
{
|
||||
lv2_log_error(&self->logger, "[sfizz] There was an issue changing the preload size\n");
|
||||
}
|
||||
self->preload_size = preload_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -579,16 +688,18 @@ sfizz_lv2_check_num_voices(sfizz_plugin_t* self)
|
|||
int num_voices = (int)*self->polyphony_port;
|
||||
if (num_voices != self->num_voices)
|
||||
{
|
||||
LV2_Atom_Int num_voices_atom;
|
||||
num_voices_atom.atom.type = self->sfizz_num_voices_uri;
|
||||
num_voices_atom.atom.size = sizeof(int);
|
||||
num_voices_atom.body = num_voices;
|
||||
LV2_Atom_Int atom = {
|
||||
.atom.type = self->sfizz_num_voices_uri,
|
||||
.atom.size = sizeof(int),
|
||||
.body = num_voices
|
||||
};
|
||||
if (self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((LV2_Atom *)&num_voices_atom),
|
||||
&num_voices_atom) == LV2_WORKER_SUCCESS)
|
||||
lv2_atom_total_size((LV2_Atom *)&atom),
|
||||
&atom) != LV2_WORKER_SUCCESS)
|
||||
{
|
||||
lv2_log_error(&self->logger, "[sfizz] There was an issue changing the number of voices\n");
|
||||
}
|
||||
self->num_voices = num_voices;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -605,6 +716,17 @@ sfizz_lv2_check_freewheeling(sfizz_plugin_t* self)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_check_stretch_tuning(sfizz_plugin_t* self)
|
||||
{
|
||||
float stretch_tuning = (float)*self->stretch_tuning_port;
|
||||
if (stretch_tuning != self->stretch_tuning)
|
||||
{
|
||||
sfizz_load_stretch_tuning_by_ratio(self->synth, stretch_tuning);
|
||||
self->stretch_tuning = stretch_tuning;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
run(LV2_Handle instance, uint32_t sample_count)
|
||||
{
|
||||
|
|
@ -617,7 +739,9 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
lv2_atom_forge_set_buffer(&self->forge, (uint8_t *)self->notify_port, notify_capacity);
|
||||
|
||||
// Start a sequence in the notify output port.
|
||||
lv2_atom_forge_sequence_head(&self->forge, &self->notify_frame, 0);
|
||||
LV2_Atom_Forge_Frame notify_frame;
|
||||
if (!lv2_atom_forge_sequence_head(&self->forge, ¬ify_frame, 0))
|
||||
assert(false);
|
||||
|
||||
LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev)
|
||||
{
|
||||
|
|
@ -635,12 +759,21 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
lv2_atom_object_get(obj, self->patch_property_uri, &property, 0);
|
||||
if (!property) // Send the full state
|
||||
{
|
||||
sfizz_lv2_send_file_path(self);
|
||||
sfizz_lv2_send_file_path(self, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||
sfizz_lv2_send_file_path(self, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||
}
|
||||
else if (property->body == self->sfizz_sfz_file_uri)
|
||||
{
|
||||
sfizz_lv2_send_file_path(self);
|
||||
sfizz_lv2_send_file_path(self, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||
}
|
||||
else if (property->body == self->sfizz_scala_file_uri)
|
||||
{
|
||||
sfizz_lv2_send_file_path(self, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||
}
|
||||
}
|
||||
else if (obj->body.otype == self->time_position_uri)
|
||||
{
|
||||
// TODO: Handle time position atom
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -663,13 +796,16 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
// Check and update parameters if needed
|
||||
sfizz_lv2_check_freewheeling(self);
|
||||
sfizz_set_volume(self->synth, *(self->volume_port));
|
||||
sfizz_set_scala_root_key(self->synth, *(self->scala_root_key_port));
|
||||
sfizz_set_tuning_frequency(self->synth, *(self->tuning_frequency_port));
|
||||
sfizz_lv2_check_stretch_tuning(self);
|
||||
sfizz_lv2_check_preload_size(self);
|
||||
sfizz_lv2_check_oversampling(self);
|
||||
sfizz_lv2_check_num_voices(self);
|
||||
|
||||
// Log the buffer usage
|
||||
self->sample_counter += (int)sample_count;
|
||||
if (self->sample_counter > LOG_SAMPLE_COUNT)
|
||||
if (self->sample_counter > LOG_SAMPLE_COUNT && self->check_modification)
|
||||
{
|
||||
LV2_Atom atom;
|
||||
atom.size = 0;
|
||||
|
|
@ -683,13 +819,14 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
}
|
||||
#endif
|
||||
atom.type = self->sfizz_check_modification_uri;
|
||||
if (!(self->worker->schedule_work(self->worker->handle,
|
||||
if ((self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((LV2_Atom *)&atom),
|
||||
&atom) == LV2_WORKER_SUCCESS))
|
||||
{
|
||||
&atom) == LV2_WORKER_SUCCESS)) {
|
||||
self->check_modification = false;
|
||||
} else {
|
||||
lv2_log_error(&self->logger, "[sfizz] There was an issue sending a notice to check the modification of the SFZ file to the background worker\n");
|
||||
}
|
||||
self->sample_counter -= LOG_SAMPLE_COUNT;
|
||||
self->sample_counter = 0;
|
||||
}
|
||||
|
||||
// Render the block
|
||||
|
|
@ -699,6 +836,8 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
{
|
||||
self->midnam->update(self->midnam->handle);
|
||||
}
|
||||
|
||||
lv2_atom_forge_pop(&self->forge, ¬ify_frame);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
|
|
@ -706,7 +845,7 @@ lv2_get_options(LV2_Handle instance, LV2_Options_Option *options)
|
|||
{
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
LV2_DEBUG("[DEBUG] get_options called\n");
|
||||
for (LV2_Options_Option *opt = options; opt->value; ++opt)
|
||||
for (LV2_Options_Option *opt = options; opt->key || opt->value; ++opt)
|
||||
{
|
||||
if (self->unmap) {
|
||||
LV2_DEBUG("[DEBUG] Called for an option with key (subject): %s (%s) \n",
|
||||
|
|
@ -739,7 +878,7 @@ lv2_set_options(LV2_Handle instance, const LV2_Options_Option *options)
|
|||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
|
||||
// Update the block size and sample rate as needed
|
||||
for (const LV2_Options_Option *opt = options; opt->value; ++opt)
|
||||
for (const LV2_Options_Option *opt = options; opt->key || opt->value; ++opt)
|
||||
{
|
||||
if (opt->key == self->sample_rate_uri)
|
||||
{
|
||||
|
|
@ -771,11 +910,13 @@ lv2_set_options(LV2_Handle instance, const LV2_Options_Option *options)
|
|||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_update_file_info(sfizz_plugin_t* self, const char* file_path)
|
||||
sfizz_lv2_update_file_info(sfizz_plugin_t* self, const char *file_path)
|
||||
{
|
||||
strcpy(self->sfz_file_path, file_path);
|
||||
if (file_path != self->sfz_file_path)
|
||||
strcpy(self->sfz_file_path, file_path);
|
||||
|
||||
lv2_log_note(&self->logger, "[sfizz] File changed to: %s\n", file_path);
|
||||
|
||||
lv2_log_note(&self->logger, "[sfizz] File changed to: %s\n", self->sfz_file_path);
|
||||
char *unknown_opcodes = sfizz_get_unknown_opcodes(self->synth);
|
||||
if (unknown_opcodes)
|
||||
{
|
||||
|
|
@ -789,6 +930,25 @@ sfizz_lv2_update_file_info(sfizz_plugin_t* self, const char* file_path)
|
|||
atomic_store(&self->must_update_midnam, 1);
|
||||
}
|
||||
|
||||
static bool
|
||||
sfizz_lv2_load_file(LV2_Handle instance, const char *file_path)
|
||||
{
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
bool status = sfizz_load_file(self->synth, file_path);
|
||||
sfizz_lv2_update_file_info(self, file_path);
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool
|
||||
sfizz_lv2_load_scala_file(LV2_Handle instance, const char *file_path)
|
||||
{
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
bool status = sfizz_load_scala_file(self->synth, file_path);
|
||||
if (file_path != self->scala_file_path)
|
||||
strcpy(self->scala_file_path, file_path);
|
||||
return status;
|
||||
}
|
||||
|
||||
static LV2_State_Status
|
||||
restore(LV2_Handle instance,
|
||||
LV2_State_Retrieve_Function retrieve,
|
||||
|
|
@ -797,9 +957,26 @@ restore(LV2_Handle instance,
|
|||
const LV2_Feature *const *features)
|
||||
{
|
||||
UNUSED(flags);
|
||||
UNUSED(features);
|
||||
LV2_State_Status status = LV2_STATE_SUCCESS;
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
|
||||
LV2_State_Map_Path *map_path = NULL;
|
||||
LV2_State_Free_Path *free_path = &sfizz_State_Free_Path;
|
||||
for (const LV2_Feature *const *f = features; *f; ++f)
|
||||
{
|
||||
if (!strcmp((*f)->URI, LV2_STATE__mapPath))
|
||||
map_path = (LV2_State_Map_Path *)(**f).data;
|
||||
else if (!strcmp((*f)->URI, LV2_STATE__freePath))
|
||||
free_path = (LV2_State_Free_Path *)(**f).data;
|
||||
}
|
||||
|
||||
// Set default values
|
||||
sfizz_lv2_get_default_sfz_path(instance, self->sfz_file_path, MAX_PATH_SIZE);
|
||||
sfizz_lv2_get_default_scala_path(instance, self->scala_file_path, MAX_PATH_SIZE);
|
||||
self->num_voices = DEFAULT_VOICES;
|
||||
self->preload_size = DEFAULT_PRELOAD;
|
||||
self->oversampling = DEFAULT_OVERSAMPLING;
|
||||
|
||||
// Fetch back the saved file path, if any
|
||||
size_t size;
|
||||
uint32_t type;
|
||||
|
|
@ -808,10 +985,42 @@ restore(LV2_Handle instance,
|
|||
value = retrieve(handle, self->sfizz_sfz_file_uri, &size, &type, &val_flags);
|
||||
if (value)
|
||||
{
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the file %s\n", (const char *)value);
|
||||
if (sfizz_load_file(self->synth, (const char *)value))
|
||||
const char *path = (const char *)value;
|
||||
if (map_path)
|
||||
{
|
||||
sfizz_lv2_update_file_info(self, (const char *)value);
|
||||
path = map_path->absolute_path(map_path->handle, path);
|
||||
if (!path)
|
||||
status = LV2_STATE_ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
if (path)
|
||||
{
|
||||
strncpy(self->sfz_file_path, path, MAX_PATH_SIZE);
|
||||
self->sfz_file_path[MAX_PATH_SIZE - 1] = '\0';
|
||||
|
||||
if (map_path)
|
||||
free_path->free_path(free_path->handle, (char *)path);
|
||||
}
|
||||
}
|
||||
|
||||
value = retrieve(handle, self->sfizz_scala_file_uri, &size, &type, &val_flags);
|
||||
if (value)
|
||||
{
|
||||
const char *path = (const char *)value;
|
||||
if (map_path)
|
||||
{
|
||||
path = map_path->absolute_path(map_path->handle, path);
|
||||
if (!path)
|
||||
status = LV2_STATE_ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
if (path)
|
||||
{
|
||||
strncpy(self->scala_file_path, path, MAX_PATH_SIZE);
|
||||
self->scala_file_path[MAX_PATH_SIZE - 1] = '\0';
|
||||
|
||||
if (map_path)
|
||||
free_path->free_path(free_path->handle, (char *)path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -819,38 +1028,62 @@ restore(LV2_Handle instance,
|
|||
if (value)
|
||||
{
|
||||
int num_voices = *(const int *)value;
|
||||
if (num_voices > 0 && num_voices <= MAX_VOICES && num_voices != self->num_voices)
|
||||
{
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the number of voices to %d\n", num_voices);
|
||||
sfizz_set_num_voices(self->synth, num_voices);
|
||||
if (num_voices > 0 && num_voices <= MAX_VOICES)
|
||||
self->num_voices = num_voices;
|
||||
}
|
||||
}
|
||||
|
||||
value = retrieve(handle, self->sfizz_preload_size_uri, &size, &type, &val_flags);
|
||||
if (value)
|
||||
{
|
||||
unsigned int preload_size = *(const unsigned int *)value;
|
||||
if (preload_size != self->preload_size)
|
||||
{
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the preload size to %d\n", preload_size);
|
||||
sfizz_set_preload_size(self->synth, preload_size);
|
||||
self->preload_size = preload_size;
|
||||
}
|
||||
self->preload_size = preload_size;
|
||||
}
|
||||
|
||||
value = retrieve(handle, self->sfizz_oversampling_uri, &size, &type, &val_flags);
|
||||
if (value)
|
||||
{
|
||||
sfizz_oversampling_factor_t oversampling = *(const sfizz_oversampling_factor_t *)value;
|
||||
if (oversampling != self->oversampling)
|
||||
{
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the oversampling to %d\n", oversampling);
|
||||
sfizz_set_oversampling_factor(self->synth, oversampling);
|
||||
self->oversampling = oversampling;
|
||||
}
|
||||
self->oversampling = oversampling;
|
||||
}
|
||||
return LV2_STATE_SUCCESS;
|
||||
|
||||
// Sync the parameters to the synth
|
||||
|
||||
// Load an empty file to remove the default sine, and then the new file.
|
||||
sfizz_load_string(self->synth, "empty.sfz", "");
|
||||
self->check_modification = false;
|
||||
if (sfizz_lv2_load_file(instance, self->sfz_file_path))
|
||||
{
|
||||
lv2_log_note(&self->logger,
|
||||
"[sfizz] Restoring the file %s\n", self->sfz_file_path);
|
||||
self->check_modification = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
lv2_log_error(&self->logger,
|
||||
"[sfizz] Error while restoring the file %s\n", self->sfz_file_path);
|
||||
}
|
||||
|
||||
if (sfizz_load_scala_file(self->synth, self->scala_file_path))
|
||||
{
|
||||
lv2_log_note(&self->logger,
|
||||
"[sfizz] Restoring the scale %s\n", self->scala_file_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv2_log_error(&self->logger,
|
||||
"[sfizz] Error while restoring the scale %s\n", self->scala_file_path);
|
||||
}
|
||||
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the number of voices to %d\n", self->num_voices);
|
||||
sfizz_set_num_voices(self->synth, self->num_voices);
|
||||
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the preload size to %d\n", self->preload_size);
|
||||
sfizz_set_preload_size(self->synth, self->preload_size);
|
||||
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the oversampling to %d\n", self->oversampling);
|
||||
sfizz_set_oversampling_factor(self->synth, self->oversampling);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static LV2_State_Status
|
||||
|
|
@ -861,15 +1094,55 @@ save(LV2_Handle instance,
|
|||
const LV2_Feature *const *features)
|
||||
{
|
||||
UNUSED(flags);
|
||||
UNUSED(features);
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
|
||||
LV2_State_Map_Path *map_path = NULL;
|
||||
LV2_State_Free_Path *free_path = &sfizz_State_Free_Path;
|
||||
for (const LV2_Feature *const *f = features; *f; ++f)
|
||||
{
|
||||
if (!strcmp((*f)->URI, LV2_STATE__mapPath))
|
||||
map_path = (LV2_State_Map_Path *)(**f).data;
|
||||
else if (!strcmp((*f)->URI, LV2_STATE__freePath))
|
||||
free_path = (LV2_State_Free_Path *)(**f).data;
|
||||
}
|
||||
|
||||
const char *path;
|
||||
|
||||
// Save the file path
|
||||
path = self->sfz_file_path;
|
||||
if (map_path)
|
||||
{
|
||||
path = map_path->abstract_path(map_path->handle, path);
|
||||
if (!path)
|
||||
return LV2_STATE_ERR_UNKNOWN;
|
||||
}
|
||||
store(handle,
|
||||
self->sfizz_sfz_file_uri,
|
||||
self->sfz_file_path,
|
||||
strlen(self->sfz_file_path) + 1,
|
||||
path,
|
||||
strlen(path) + 1,
|
||||
self->atom_path_uri,
|
||||
LV2_STATE_IS_POD);
|
||||
if (map_path)
|
||||
free_path->free_path(free_path->handle, (char *)path);
|
||||
|
||||
// Save the scala file path
|
||||
path = self->scala_file_path;
|
||||
if (map_path)
|
||||
{
|
||||
path = map_path->abstract_path(map_path->handle, path);
|
||||
if (!path)
|
||||
return LV2_STATE_ERR_UNKNOWN;
|
||||
}
|
||||
if (!path)
|
||||
return LV2_STATE_ERR_UNKNOWN;
|
||||
store(handle,
|
||||
self->sfizz_scala_file_uri,
|
||||
path,
|
||||
strlen(path) + 1,
|
||||
self->atom_path_uri,
|
||||
LV2_STATE_IS_POD);
|
||||
if (map_path)
|
||||
free_path->free_path(free_path->handle, (char *)path);
|
||||
|
||||
// Save the number of voices
|
||||
store(handle,
|
||||
|
|
@ -898,6 +1171,19 @@ save(LV2_Handle instance,
|
|||
return LV2_STATE_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_activate_file_checking(
|
||||
sfizz_plugin_t *self,
|
||||
LV2_Worker_Respond_Function respond,
|
||||
LV2_Worker_Respond_Handle handle)
|
||||
{
|
||||
LV2_Atom check_modification_atom = {
|
||||
.size = 0,
|
||||
.type = self->sfizz_check_modification_uri
|
||||
};
|
||||
respond(handle, lv2_atom_total_size(&check_modification_atom), &check_modification_atom);
|
||||
}
|
||||
|
||||
// This runs in a lower priority thread
|
||||
static LV2_Worker_Status
|
||||
work(LV2_Handle instance,
|
||||
|
|
@ -906,9 +1192,9 @@ work(LV2_Handle instance,
|
|||
uint32_t size,
|
||||
const void *data)
|
||||
{
|
||||
UNUSED(size);
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
if (!data)
|
||||
{
|
||||
if (!data) {
|
||||
lv2_log_error(&self->logger, "[sfizz] Ignoring empty data in the worker thread\n");
|
||||
return LV2_WORKER_ERR_UNKNOWN;
|
||||
}
|
||||
|
|
@ -916,70 +1202,58 @@ work(LV2_Handle instance,
|
|||
const LV2_Atom *atom = (const LV2_Atom *)data;
|
||||
if (atom->type == self->sfizz_sfz_file_uri)
|
||||
{
|
||||
if (self->changing_state) {
|
||||
respond(handle, size, data); // send back so that we reschedule the check
|
||||
return LV2_WORKER_SUCCESS;
|
||||
const char *sfz_file_path = LV2_ATOM_BODY_CONST(atom);
|
||||
if (!sfizz_lv2_load_file(self, sfz_file_path)) {
|
||||
lv2_log_error(&self->logger,
|
||||
"[sfizz] Error with %s; no file should be loaded\n", sfz_file_path);
|
||||
}
|
||||
|
||||
const char *sfz_file_path = LV2_ATOM_BODY_CONST(atom);
|
||||
self->changing_state = true;
|
||||
if (sfizz_load_file(self->synth, sfz_file_path))
|
||||
{
|
||||
sfizz_lv2_update_file_info(self, sfz_file_path);
|
||||
// Reactivate checking for file changes
|
||||
sfizz_lv2_activate_file_checking(self, respond, handle);
|
||||
}
|
||||
else if (atom->type == self->sfizz_scala_file_uri)
|
||||
{
|
||||
const char *scala_file_path = LV2_ATOM_BODY_CONST(atom);
|
||||
if (sfizz_lv2_load_scala_file(self->synth, scala_file_path)) {
|
||||
lv2_log_note(&self->logger, "[sfizz] Scala file loaded: %s\n", scala_file_path);
|
||||
} else {
|
||||
lv2_log_error(&self->logger,
|
||||
"[sfizz] Error with %s; no new scala file should be loaded\n", scala_file_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv2_log_error(&self->logger, "[sfizz] Error with %s; no file should be loaded\n", sfz_file_path);
|
||||
}
|
||||
self->changing_state = false;
|
||||
|
||||
// Reactivate checking for file changes
|
||||
sfizz_lv2_activate_file_checking(self, respond, handle);
|
||||
}
|
||||
else if (atom->type == self->sfizz_num_voices_uri)
|
||||
{
|
||||
if (self->changing_state) {
|
||||
respond(handle, size, data); // send back so that we reschedule the check
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
|
||||
self->changing_state = true;
|
||||
const int num_voices = *(const int *)LV2_ATOM_BODY_CONST(atom);
|
||||
sfizz_set_num_voices(self->synth, num_voices);
|
||||
if (sfizz_get_num_voices(self->synth) == num_voices) {
|
||||
self->num_voices = num_voices;
|
||||
lv2_log_note(&self->logger, "[sfizz] Number of voices changed to: %d\n", num_voices);
|
||||
} else {
|
||||
lv2_log_error(&self->logger, "[sfizz] Error changing the number of voices\n");
|
||||
}
|
||||
self->changing_state = false;
|
||||
}
|
||||
else if (atom->type == self->sfizz_preload_size_uri)
|
||||
{
|
||||
if (self->changing_state) {
|
||||
respond(handle, size, data); // send back so that we reschedule the check
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
|
||||
self->changing_state = true;
|
||||
const unsigned int preload_size = *(const unsigned int *)LV2_ATOM_BODY_CONST(atom);
|
||||
sfizz_set_preload_size(self->synth, preload_size);
|
||||
if (sfizz_get_preload_size(self->synth) == preload_size) {
|
||||
self->preload_size = preload_size;
|
||||
lv2_log_note(&self->logger, "[sfizz] Preload size changed to: %d\n", preload_size);
|
||||
} else {
|
||||
lv2_log_error(&self->logger, "[sfizz] Error changing the preload size\n");
|
||||
}
|
||||
self->changing_state = false;
|
||||
}
|
||||
else if (atom->type == self->sfizz_oversampling_uri)
|
||||
{
|
||||
if (self->changing_state) {
|
||||
respond(handle, size, data); // send back so that we reschedule the check
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
self->changing_state = true;
|
||||
const sfizz_oversampling_factor_t oversampling =
|
||||
*(const sfizz_oversampling_factor_t *)LV2_ATOM_BODY_CONST(atom);
|
||||
sfizz_set_oversampling_factor(self->synth, oversampling);
|
||||
if (sfizz_get_oversampling_factor(self->synth) == oversampling) {
|
||||
self->oversampling = oversampling;
|
||||
lv2_log_note(&self->logger, "[sfizz] Oversampling changed to: %d\n", oversampling);
|
||||
} else {
|
||||
lv2_log_error(&self->logger, "[sfizz] Error changing the oversampling\n");
|
||||
}
|
||||
self->changing_state = false;
|
||||
}
|
||||
else if (atom->type == self->sfizz_log_status_uri)
|
||||
{
|
||||
|
|
@ -989,20 +1263,30 @@ work(LV2_Handle instance,
|
|||
{
|
||||
if (sfizz_should_reload_file(self->synth))
|
||||
{
|
||||
// We'll check later
|
||||
if (self->changing_state)
|
||||
return LV2_WORKER_SUCCESS;
|
||||
|
||||
lv2_log_note(&self->logger, "[sfizz] File %s seems to have been updated, reloading\n", self->sfz_file_path);
|
||||
if (sfizz_load_file(self->synth, self->sfz_file_path))
|
||||
{
|
||||
sfizz_lv2_update_file_info(self, self->sfz_file_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv2_log_error(&self->logger, "[sfizz] Error with %s; no file should be loaded\n", self->sfz_file_path);
|
||||
lv2_log_note(&self->logger,
|
||||
"[sfizz] File %s seems to have been updated, reloading\n",
|
||||
self->sfz_file_path);
|
||||
if (!sfizz_lv2_load_file(self, self->sfz_file_path)) {
|
||||
lv2_log_error(&self->logger,
|
||||
"[sfizz] Error with %s; no file should be loaded\n", self->sfz_file_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (sfizz_should_reload_scala(self->synth))
|
||||
{
|
||||
lv2_log_note(&self->logger,
|
||||
"[sfizz] Scala file %s seems to have been updated, reloading\n",
|
||||
self->scala_file_path);
|
||||
if (sfizz_lv2_load_scala_file(self->synth, self->scala_file_path)) {
|
||||
lv2_log_note(&self->logger, "[sfizz] Scala file loaded: %s\n", self->scala_file_path);
|
||||
} else {
|
||||
lv2_log_error(&self->logger,
|
||||
"[sfizz] Error with %s; no new scala file should be loaded\n", self->scala_file_path);
|
||||
}
|
||||
}
|
||||
|
||||
// Reactivate checking for file changes
|
||||
sfizz_lv2_activate_file_checking(self, respond, handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1013,7 +1297,6 @@ work(LV2_Handle instance,
|
|||
self->unmap->unmap(self->unmap->handle, atom->type));
|
||||
return LV2_WORKER_ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
@ -1030,49 +1313,10 @@ work_response(LV2_Handle instance,
|
|||
return LV2_WORKER_ERR_UNKNOWN;
|
||||
|
||||
const LV2_Atom *atom = (const LV2_Atom *)data;
|
||||
if (atom->type == self->sfizz_sfz_file_uri)
|
||||
{
|
||||
// If we're here we need to reschedule
|
||||
lv2_log_note(&self->logger, "[sfizz] Got a pingback from the worker on a file\n");
|
||||
self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((const LV2_Atom *)data),
|
||||
(const LV2_Atom *)data);
|
||||
}
|
||||
else if (atom->type == self->sfizz_num_voices_uri)
|
||||
{
|
||||
// If we're here we need to reschedule
|
||||
lv2_log_note(&self->logger, "[sfizz] Got a pingback from the worker on the number of voices\n");
|
||||
self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((const LV2_Atom *)data),
|
||||
(const LV2_Atom *)data);
|
||||
}
|
||||
else if (atom->type == self->sfizz_preload_size_uri)
|
||||
{
|
||||
// If we're here we need to reschedule
|
||||
lv2_log_note(&self->logger, "[sfizz] Got a pingback from the worker on the preload size\n");
|
||||
self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((const LV2_Atom *)data),
|
||||
(const LV2_Atom *)data);
|
||||
}
|
||||
else if (atom->type == self->sfizz_oversampling_uri)
|
||||
{
|
||||
// If we're here we need to reschedule
|
||||
lv2_log_note(&self->logger, "[sfizz] Got a pingback from the worker on the oversampling factor\n");
|
||||
self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((const LV2_Atom *)data),
|
||||
(const LV2_Atom *)data);
|
||||
}
|
||||
else if (atom->type == self->sfizz_log_status_uri)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
else if (atom->type == self->sfizz_check_modification_uri)
|
||||
{
|
||||
// Nothing to do, it'll get rechecked soon enough
|
||||
}
|
||||
else
|
||||
{
|
||||
lv2_log_error(&self->logger, "[sfizz] Got an unknown atom in work response\n");
|
||||
if (atom->type == self->sfizz_check_modification_uri) {
|
||||
self->check_modification = true; // check changes
|
||||
} else {
|
||||
lv2_log_error(&self->logger, "[sfizz] Got an unexpected atom in work response\n");
|
||||
if (self->unmap)
|
||||
lv2_log_error(&self->logger,
|
||||
"URI: %s\n",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
@prefix state: <http://lv2plug.in/ns/ext/state#> .
|
||||
@prefix time: <http://lv2plug.in/ns/ext/time#> .
|
||||
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
|
||||
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
|
||||
@prefix work: <http://lv2plug.in/ns/ext/worker#> .
|
||||
|
|
@ -27,6 +28,13 @@ midnam:update a lv2:Feature .
|
|||
"Configuration"@fr ,
|
||||
"Impostazioni"@it .
|
||||
|
||||
<@LV2PLUGIN_URI@#tuning>
|
||||
a pg:Group ;
|
||||
lv2:symbol "tuning" ;
|
||||
lv2:name "Tuning",
|
||||
"Accordage"@fr ,
|
||||
"Accordatura"@it .
|
||||
|
||||
<@LV2PLUGIN_URI@:sfzfile>
|
||||
a lv2:Parameter ;
|
||||
rdfs:label "SFZ file",
|
||||
|
|
@ -34,6 +42,14 @@ midnam:update a lv2:Feature .
|
|||
"File SFZ"@it ;
|
||||
rdfs:range atom:Path .
|
||||
|
||||
<@LV2PLUGIN_URI@:tuningfile>
|
||||
a lv2:Parameter ;
|
||||
pg:group <@LV2PLUGIN_URI@#tuning> ;
|
||||
rdfs:label "Scala file",
|
||||
"Fichier Scala"@fr ,
|
||||
"File Scala"@it ;
|
||||
rdfs:range atom:Path .
|
||||
|
||||
<@LV2PLUGIN_URI@>
|
||||
a doap:Project, lv2:Plugin, lv2:InstrumentPlugin ;
|
||||
|
||||
|
|
@ -52,7 +68,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:microVersion @LV2PLUGIN_VERSION_MICRO@ ;
|
||||
|
||||
lv2:requiredFeature urid:map, bufsize:boundedBlockLength, work:schedule ;
|
||||
lv2:optionalFeature lv2:hardRTCapable, opts:options ;
|
||||
lv2:optionalFeature lv2:hardRTCapable, opts:options, state:mapPath, state:freePath ;
|
||||
lv2:extensionData opts:interface, state:interface, work:interface ;
|
||||
|
||||
lv2:optionalFeature midnam:update ;
|
||||
|
|
@ -62,11 +78,12 @@ midnam:update a lv2:Feature .
|
|||
opts:supportedOption bufsize:maxBlockLength, bufsize:nominalBlockLength ;
|
||||
|
||||
patch:writable <@LV2PLUGIN_URI@:sfzfile> ;
|
||||
patch:writable <@LV2PLUGIN_URI@:tuningfile> ;
|
||||
|
||||
lv2:port [
|
||||
a lv2:InputPort, atom:AtomPort ;
|
||||
atom:bufferType atom:Sequence ;
|
||||
atom:supports patch:Message, midi:MidiEvent ;
|
||||
atom:supports patch:Message, midi:MidiEvent, time:Position ;
|
||||
lv2:designation lv2:control ;
|
||||
lv2:index 0 ;
|
||||
lv2:symbol "control" ;
|
||||
|
|
@ -237,4 +254,41 @@ midnam:update a lv2:Feature .
|
|||
lv2:default 0 ;
|
||||
lv2:minimum 0 ;
|
||||
lv2:maximum 1 ;
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 9 ;
|
||||
lv2:symbol "scala_root_key" ;
|
||||
lv2:name "Scala root key",
|
||||
"Tonalité de base Scala"@fr ,
|
||||
"Tonalità di base Scala"@it ;
|
||||
pg:group <@LV2PLUGIN_URI@#tuning> ;
|
||||
lv2:portProperty lv2:integer ;
|
||||
lv2:default 60 ;
|
||||
lv2:minimum 0 ;
|
||||
lv2:maximum 127 ;
|
||||
units:unit units:midiNote
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 10 ;
|
||||
lv2:symbol "tuning_frequency" ;
|
||||
lv2:name "Tuning frequency",
|
||||
"Fréquence d'accordage"@fr ,
|
||||
"Frequenza di accordatura"@it ;
|
||||
pg:group <@LV2PLUGIN_URI@#tuning> ;
|
||||
lv2:default 440.0 ;
|
||||
lv2:minimum 300.0 ;
|
||||
lv2:maximum 500.0 ;
|
||||
units:unit units:hz
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 11 ;
|
||||
lv2:symbol "stretched_tuning" ;
|
||||
lv2:name "Stretched tuning",
|
||||
"Accordage étiré"@fr ,
|
||||
"Tensione di accordatura"@it ;
|
||||
pg:group <@LV2PLUGIN_URI@#tuning> ;
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 1.0 ;
|
||||
units:unit units:coef
|
||||
] .
|
||||
|
|
|
|||
11
scripts/generate_re2c.sh
Executable file
11
scripts/generate_re2c.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if ! test -d "src"; then
|
||||
echo "Please run this in the project root directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for src in src/sfizz/OpcodeCleanup.re; do
|
||||
re2c -o "${src%.re}.cpp" "$src"
|
||||
done
|
||||
|
|
@ -13,10 +13,13 @@ clang-tidy \
|
|||
src/sfizz/Opcode.cpp \
|
||||
src/sfizz/Oversampler.cpp \
|
||||
src/sfizz/Parser.cpp \
|
||||
src/sfizz/Panning.cpp \
|
||||
src/sfizz/sfizz.cpp \
|
||||
src/sfizz/Region.cpp \
|
||||
src/sfizz/SfzHelpers.cpp \
|
||||
src/sfizz/SIMDSSE.cpp \
|
||||
src/sfizz/SIMDHelpers.cpp \
|
||||
src/sfizz/simd/HelpersSSE.cpp \
|
||||
src/sfizz/simd/HelpersAVX.cpp \
|
||||
src/sfizz/Synth.cpp \
|
||||
src/sfizz/Voice.cpp \
|
||||
src/sfizz/effects/Eq.cpp \
|
||||
|
|
@ -28,5 +31,6 @@ clang-tidy \
|
|||
vst/SfizzVstEditor.cpp \
|
||||
vst/SfizzVstState.cpp \
|
||||
-- -Iexternal/abseil-cpp -Isrc/external -Isrc/external/pugixml/src \
|
||||
-Isrc/sfizz -Isrc -Isrc/external/spline \
|
||||
-Ivst -Ivst/external/VST_SDK/VST3_SDK -Ivst/external/VST_SDK/VST3_SDK/vstgui4 -Ivst/external/ring_buffer -DNDEBUG
|
||||
-Isrc/sfizz -Isrc -Isrc/external/spline -Isrc/external/cpuid/src \
|
||||
-Ivst -Ivst/external/VST_SDK/VST3_SDK -Ivst/external/VST_SDK/VST3_SDK/vstgui4 -Ivst/external/ring_buffer \
|
||||
-DNDEBUG -std=c++17
|
||||
|
|
|
|||
|
|
@ -3,9 +3,96 @@ include (GNUInstallDirs)
|
|||
add_subdirectory(external/kiss_fft)
|
||||
add_subdirectory(external/cpuid)
|
||||
|
||||
set (FAUST_FILES
|
||||
sfizz/dsp/filters/filters_modulable.dsp
|
||||
sfizz/dsp/filters/rbj_filters.dsp
|
||||
sfizz/dsp/filters/sallenkey_modulable.dsp
|
||||
sfizz/dsp/filters/sfz_filters.dsp)
|
||||
source_group ("Faust Files" FILES ${FAUST_FILES})
|
||||
|
||||
set (SFIZZ_HEADERS
|
||||
sfizz/ADSREnvelope.h
|
||||
sfizz/AudioBuffer.h
|
||||
sfizz/AudioSpan.h
|
||||
sfizz/Buffer.h
|
||||
sfizz/BufferPool.h
|
||||
sfizz/CCMap.h
|
||||
sfizz/Config.h
|
||||
sfizz/Curve.h
|
||||
sfizz/Debug.h
|
||||
sfizz/effects/impl/ResonantArray.h
|
||||
sfizz/effects/impl/ResonantArrayAVX.h
|
||||
sfizz/effects/impl/ResonantArraySSE.h
|
||||
sfizz/effects/impl/ResonantString.h
|
||||
sfizz/effects/impl/ResonantStringAVX.h
|
||||
sfizz/effects/impl/ResonantStringSSE.h
|
||||
sfizz/effects/Apan.h
|
||||
sfizz/effects/CommonLFO.h
|
||||
sfizz/effects/CommonLFO.hpp
|
||||
sfizz/effects/Eq.h
|
||||
sfizz/effects/Filter.h
|
||||
sfizz/effects/Gain.h
|
||||
sfizz/effects/Limiter.h
|
||||
sfizz/effects/Lofi.h
|
||||
sfizz/effects/Nothing.h
|
||||
sfizz/effects/Rectify.h
|
||||
sfizz/effects/Strings.h
|
||||
sfizz/effects/Width.h
|
||||
sfizz/Effects.h
|
||||
sfizz/EGDescription.h
|
||||
sfizz/EQDescription.h
|
||||
sfizz/EQPool.h
|
||||
sfizz/FileId.h
|
||||
sfizz/FileInstrument.h
|
||||
sfizz/FilePool.h
|
||||
sfizz/FilterDescription.h
|
||||
sfizz/FilterPool.h
|
||||
sfizz/HistoricalBuffer.h
|
||||
sfizz/Interpolators.h
|
||||
sfizz/Interpolators.hpp
|
||||
sfizz/Logger.h
|
||||
sfizz/MathHelpers.h
|
||||
sfizz/MidiState.h
|
||||
sfizz/ModifierHelpers.h
|
||||
sfizz/Modifiers.h
|
||||
sfizz/NumericId.h
|
||||
sfizz/OnePoleFilter.h
|
||||
sfizz/Oversampler.h
|
||||
sfizz/Panning.h
|
||||
sfizz/PolyphonyGroup.h
|
||||
sfizz/railsback/2-1.h
|
||||
sfizz/railsback/4-1.h
|
||||
sfizz/railsback/4-2.h
|
||||
sfizz/Region.h
|
||||
sfizz/RegionSet.h
|
||||
sfizz/Resources.h
|
||||
sfizz/RTSemaphore.h
|
||||
sfizz/ScopedFTZ.h
|
||||
sfizz/SfzFilter.h
|
||||
sfizz/SfzFilterImpls.hpp
|
||||
sfizz/simd/Common.h
|
||||
sfizz/simd/HelpersAVX.h
|
||||
sfizz/simd/HelpersScalar.h
|
||||
sfizz/simd/HelpersSSE.h
|
||||
sfizz/SIMDConfig.h
|
||||
sfizz/SIMDHelpers.h
|
||||
sfizz/SisterVoiceRing.h
|
||||
sfizz/Smoothers.h
|
||||
sfizz/SwapAndPop.h
|
||||
sfizz/Synth.h
|
||||
sfizz/SynthConfig.h
|
||||
sfizz/Tuning.h
|
||||
sfizz/Voice.h
|
||||
sfizz/VoiceStealing.h
|
||||
sfizz/Wavetables.h
|
||||
sfizz.h
|
||||
sfizz.hpp)
|
||||
|
||||
set (SFIZZ_SOURCES
|
||||
sfizz/Synth.cpp
|
||||
sfizz/FileId.cpp
|
||||
sfizz/FilePool.cpp
|
||||
sfizz/FileInstrument.cpp
|
||||
sfizz/FilterPool.cpp
|
||||
sfizz/EQPool.cpp
|
||||
sfizz/Region.cpp
|
||||
|
|
@ -18,7 +105,14 @@ set (SFIZZ_SOURCES
|
|||
sfizz/Logger.cpp
|
||||
sfizz/SfzFilter.cpp
|
||||
sfizz/Curve.cpp
|
||||
sfizz/Smoothers.cpp
|
||||
sfizz/Wavetables.cpp
|
||||
sfizz/Tuning.cpp
|
||||
sfizz/RegionSet.cpp
|
||||
sfizz/PolyphonyGroup.cpp
|
||||
sfizz/VoiceStealing.cpp
|
||||
sfizz/RTSemaphore.cpp
|
||||
sfizz/Panning.cpp
|
||||
sfizz/Effects.cpp
|
||||
sfizz/effects/Nothing.cpp
|
||||
sfizz/effects/Filter.cpp
|
||||
|
|
@ -41,35 +135,64 @@ include (SfizzSIMDSourceFiles)
|
|||
sfizz_add_simd_sources (SFIZZ_SOURCES ".")
|
||||
|
||||
# Parser core library
|
||||
set (SFIZZ_PARSER_HEADERS
|
||||
sfizz/Defaults.h
|
||||
sfizz/LeakDetector.h
|
||||
sfizz/Range.h
|
||||
sfizz/Opcode.h
|
||||
sfizz/Macros.h
|
||||
sfizz/Parser.h
|
||||
sfizz/parser/Parser.h
|
||||
sfizz/parser/ParserPrivate.h
|
||||
sfizz/parser/ParserPrivate.hpp
|
||||
sfizz/SfzHelpers.h
|
||||
sfizz/StringViewHelpers.h)
|
||||
|
||||
set (SFIZZ_PARSER_SOURCES
|
||||
sfizz/Parser.cpp
|
||||
sfizz/Opcode.cpp
|
||||
sfizz/OpcodeCleanup.cpp
|
||||
sfizz/SfzHelpers.cpp
|
||||
sfizz/parser/Parser.cpp
|
||||
sfizz/parser/ParserPrivate.cpp)
|
||||
|
||||
set (SFIZZ_PARSER_OTHER sfizz/OpcodeCleanup.re)
|
||||
source_group ("Other Files" FILES ${SFIZZ_PARSER_OTHER})
|
||||
|
||||
add_library (sfizz_parser STATIC)
|
||||
target_sources (sfizz_parser PRIVATE
|
||||
sfizz/Parser.cpp sfizz/Opcode.cpp sfizz/SfzHelpers.cpp
|
||||
sfizz/parser/Parser.cpp sfizz/parser/ParserPrivate.cpp)
|
||||
${SFIZZ_PARSER_HEADERS} ${SFIZZ_PARSER_SOURCES} ${SFIZZ_PARSER_OTHER})
|
||||
target_include_directories (sfizz_parser PUBLIC sfizz)
|
||||
target_include_directories (sfizz_parser PUBLIC external)
|
||||
target_link_libraries (sfizz_parser PUBLIC absl::strings PRIVATE absl::flat_hash_map)
|
||||
|
||||
# Sfizz static library
|
||||
add_library(sfizz_static STATIC)
|
||||
target_sources(sfizz_static PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
|
||||
target_sources(sfizz_static PRIVATE
|
||||
${SFIZZ_HEADERS} ${SFIZZ_SOURCES} ${FAUST_FILES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
|
||||
target_include_directories (sfizz_static PUBLIC .)
|
||||
target_include_directories (sfizz_static PUBLIC external)
|
||||
target_link_libraries (sfizz_static PUBLIC absl::strings absl::span)
|
||||
target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid)
|
||||
target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-atomic)
|
||||
set_target_properties (sfizz_static PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp")
|
||||
if (WIN32)
|
||||
target_compile_definitions (sfizz_static PRIVATE _USE_MATH_DEFINES)
|
||||
endif()
|
||||
if (SFIZZ_RELEASE_ASSERTS)
|
||||
target_compile_definitions (sfizz_static PRIVATE "SFIZZ_ENABLE_RELEASE_ASSERT=1")
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
install (TARGETS sfizz_static
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
COMPONENT "development")
|
||||
|
||||
configure_file (${PROJECT_SOURCE_DIR}/scripts/sfizz.pc.in sfizz.pc @ONLY)
|
||||
install (FILES ${CMAKE_BINARY_DIR}/src/sfizz.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
COMPONENT "development")
|
||||
endif()
|
||||
if(WIN32)
|
||||
include(VSTConfig)
|
||||
|
|
@ -80,20 +203,21 @@ configure_file (${PROJECT_SOURCE_DIR}/doxygen/scripts/Doxyfile.in ${PROJECT_SOUR
|
|||
|
||||
add_library (sfizz::parser ALIAS sfizz_parser)
|
||||
add_library (sfizz::sfizz ALIAS sfizz_static)
|
||||
if (LIBATOMIC_FOUND)
|
||||
target_link_libraries (sfizz_static PRIVATE atomic)
|
||||
endif()
|
||||
|
||||
# Shared library and installation target
|
||||
if (SFIZZ_SHARED)
|
||||
add_library (sfizz_shared SHARED)
|
||||
target_sources(sfizz_shared PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
|
||||
target_sources(sfizz_shared PRIVATE
|
||||
${SFIZZ_HEADERS} ${SFIZZ_SOURCES} ${FAUST_FILES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
|
||||
target_include_directories (sfizz_shared PRIVATE .)
|
||||
target_include_directories (sfizz_shared PRIVATE external)
|
||||
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid)
|
||||
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-atomic)
|
||||
if (WIN32)
|
||||
target_compile_definitions (sfizz_shared PRIVATE _USE_MATH_DEFINES)
|
||||
endif()
|
||||
if (SFIZZ_RELEASE_ASSERTS)
|
||||
target_compile_definitions (sfizz_shared PRIVATE "SFIZZ_ENABLE_RELEASE_ASSERT=1")
|
||||
endif()
|
||||
target_compile_definitions(sfizz_shared PRIVATE SFIZZ_EXPORT_SYMBOLS)
|
||||
set_target_properties (sfizz_shared PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR} OUTPUT_NAME sfizz)
|
||||
sfizz_enable_lto_if_needed(sfizz_shared)
|
||||
|
|
@ -102,12 +226,7 @@ if (SFIZZ_SHARED)
|
|||
install (TARGETS sfizz_shared
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
if (LIBATOMIC_FOUND)
|
||||
target_link_libraries (sfizz_shared PRIVATE atomic)
|
||||
endif()
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT "runtime")
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
|||
14
src/external/atomic_queue/atomic_queue.h
vendored
14
src/external/atomic_queue/atomic_queue.h
vendored
|
|
@ -55,12 +55,17 @@ struct GetIndexShuffleBits<false, array_size, elements_per_cache_line> {
|
|||
// the element within the cache line) with the next N bits (which are the index of the cache line)
|
||||
// of the element index.
|
||||
template<int BITS>
|
||||
constexpr unsigned remap_index(unsigned index) noexcept {
|
||||
constexpr unsigned MASK = (1u << BITS) - 1;
|
||||
unsigned mix = (index ^ (index >> BITS)) & MASK;
|
||||
constexpr unsigned remap_index_with_mix(unsigned index, unsigned mix)
|
||||
{
|
||||
return index ^ mix ^ (mix << BITS);
|
||||
}
|
||||
|
||||
template<int BITS>
|
||||
constexpr unsigned remap_index(unsigned index) noexcept {
|
||||
return remap_index_with_mix<BITS>(
|
||||
index, (index ^ (index >> BITS)) & ((1u << BITS) - 1));
|
||||
}
|
||||
|
||||
template<>
|
||||
constexpr unsigned remap_index<0>(unsigned index) noexcept {
|
||||
return index;
|
||||
|
|
@ -68,8 +73,7 @@ constexpr unsigned remap_index<0>(unsigned index) noexcept {
|
|||
|
||||
template<int BITS, class T>
|
||||
constexpr T& map(T* elements, unsigned index) noexcept {
|
||||
index = remap_index<BITS>(index);
|
||||
return elements[index];
|
||||
return elements[remap_index<BITS>(index)];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
301
src/external/mathfuns/neon_mathfun.h
vendored
301
src/external/mathfuns/neon_mathfun.h
vendored
|
|
@ -1,301 +0,0 @@
|
|||
/* NEON implementation of sin, cos, exp and log
|
||||
|
||||
Inspired by Intel Approximate Math library, and based on the
|
||||
corresponding algorithms of the cephes math library
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2011 Julien Pommier
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
(this is the zlib license)
|
||||
*/
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
typedef float32x4_t v4sf; // vector of 4 float
|
||||
typedef uint32x4_t v4su; // vector of 4 uint32
|
||||
typedef int32x4_t v4si; // vector of 4 uint32
|
||||
|
||||
#define c_inv_mant_mask ~0x7f800000u
|
||||
#define c_cephes_SQRTHF 0.707106781186547524
|
||||
#define c_cephes_log_p0 7.0376836292E-2
|
||||
#define c_cephes_log_p1 - 1.1514610310E-1
|
||||
#define c_cephes_log_p2 1.1676998740E-1
|
||||
#define c_cephes_log_p3 - 1.2420140846E-1
|
||||
#define c_cephes_log_p4 + 1.4249322787E-1
|
||||
#define c_cephes_log_p5 - 1.6668057665E-1
|
||||
#define c_cephes_log_p6 + 2.0000714765E-1
|
||||
#define c_cephes_log_p7 - 2.4999993993E-1
|
||||
#define c_cephes_log_p8 + 3.3333331174E-1
|
||||
#define c_cephes_log_q1 -2.12194440e-4
|
||||
#define c_cephes_log_q2 0.693359375
|
||||
|
||||
/* natural logarithm computed for 4 simultaneous float
|
||||
return NaN for x <= 0
|
||||
*/
|
||||
v4sf log_ps(v4sf x) {
|
||||
v4sf one = vdupq_n_f32(1);
|
||||
|
||||
x = vmaxq_f32(x, vdupq_n_f32(0)); /* force flush to zero on denormal values */
|
||||
v4su invalid_mask = vcleq_f32(x, vdupq_n_f32(0));
|
||||
|
||||
v4si ux = vreinterpretq_s32_f32(x);
|
||||
|
||||
v4si emm0 = vshrq_n_s32(ux, 23);
|
||||
|
||||
/* keep only the fractional part */
|
||||
ux = vandq_s32(ux, vdupq_n_s32(c_inv_mant_mask));
|
||||
ux = vorrq_s32(ux, vreinterpretq_s32_f32(vdupq_n_f32(0.5f)));
|
||||
x = vreinterpretq_f32_s32(ux);
|
||||
|
||||
emm0 = vsubq_s32(emm0, vdupq_n_s32(0x7f));
|
||||
v4sf e = vcvtq_f32_s32(emm0);
|
||||
|
||||
e = vaddq_f32(e, one);
|
||||
|
||||
/* part2:
|
||||
if( x < SQRTHF ) {
|
||||
e -= 1;
|
||||
x = x + x - 1.0;
|
||||
} else { x = x - 1.0; }
|
||||
*/
|
||||
v4su mask = vcltq_f32(x, vdupq_n_f32(c_cephes_SQRTHF));
|
||||
v4sf tmp = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(x), mask));
|
||||
x = vsubq_f32(x, one);
|
||||
e = vsubq_f32(e, vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(one), mask)));
|
||||
x = vaddq_f32(x, tmp);
|
||||
|
||||
v4sf z = vmulq_f32(x,x);
|
||||
|
||||
v4sf y = vdupq_n_f32(c_cephes_log_p0);
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p1));
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p2));
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p3));
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p4));
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p5));
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p6));
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p7));
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p8));
|
||||
y = vmulq_f32(y, x);
|
||||
|
||||
y = vmulq_f32(y, z);
|
||||
|
||||
|
||||
tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q1));
|
||||
y = vaddq_f32(y, tmp);
|
||||
|
||||
|
||||
tmp = vmulq_f32(z, vdupq_n_f32(0.5f));
|
||||
y = vsubq_f32(y, tmp);
|
||||
|
||||
tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q2));
|
||||
x = vaddq_f32(x, y);
|
||||
x = vaddq_f32(x, tmp);
|
||||
x = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(x), invalid_mask)); // negative arg will be NAN
|
||||
return x;
|
||||
}
|
||||
|
||||
#define c_exp_hi 88.3762626647949f
|
||||
#define c_exp_lo -88.3762626647949f
|
||||
|
||||
#define c_cephes_LOG2EF 1.44269504088896341
|
||||
#define c_cephes_exp_C1 0.693359375
|
||||
#define c_cephes_exp_C2 -2.12194440e-4
|
||||
|
||||
#define c_cephes_exp_p0 1.9875691500E-4
|
||||
#define c_cephes_exp_p1 1.3981999507E-3
|
||||
#define c_cephes_exp_p2 8.3334519073E-3
|
||||
#define c_cephes_exp_p3 4.1665795894E-2
|
||||
#define c_cephes_exp_p4 1.6666665459E-1
|
||||
#define c_cephes_exp_p5 5.0000001201E-1
|
||||
|
||||
/* exp() computed for 4 float at once */
|
||||
v4sf exp_ps(v4sf x) {
|
||||
v4sf tmp, fx;
|
||||
|
||||
v4sf one = vdupq_n_f32(1);
|
||||
x = vminq_f32(x, vdupq_n_f32(c_exp_hi));
|
||||
x = vmaxq_f32(x, vdupq_n_f32(c_exp_lo));
|
||||
|
||||
/* express exp(x) as exp(g + n*log(2)) */
|
||||
fx = vmlaq_f32(vdupq_n_f32(0.5f), x, vdupq_n_f32(c_cephes_LOG2EF));
|
||||
|
||||
/* perform a floorf */
|
||||
tmp = vcvtq_f32_s32(vcvtq_s32_f32(fx));
|
||||
|
||||
/* if greater, substract 1 */
|
||||
v4su mask = vcgtq_f32(tmp, fx);
|
||||
mask = vandq_u32(mask, vreinterpretq_u32_f32(one));
|
||||
|
||||
|
||||
fx = vsubq_f32(tmp, vreinterpretq_f32_u32(mask));
|
||||
|
||||
tmp = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C1));
|
||||
v4sf z = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C2));
|
||||
x = vsubq_f32(x, tmp);
|
||||
x = vsubq_f32(x, z);
|
||||
|
||||
static const float cephes_exp_p[6] = { c_cephes_exp_p0, c_cephes_exp_p1, c_cephes_exp_p2, c_cephes_exp_p3, c_cephes_exp_p4, c_cephes_exp_p5 };
|
||||
v4sf y = vld1q_dup_f32(cephes_exp_p+0);
|
||||
v4sf c1 = vld1q_dup_f32(cephes_exp_p+1);
|
||||
v4sf c2 = vld1q_dup_f32(cephes_exp_p+2);
|
||||
v4sf c3 = vld1q_dup_f32(cephes_exp_p+3);
|
||||
v4sf c4 = vld1q_dup_f32(cephes_exp_p+4);
|
||||
v4sf c5 = vld1q_dup_f32(cephes_exp_p+5);
|
||||
|
||||
y = vmulq_f32(y, x);
|
||||
z = vmulq_f32(x,x);
|
||||
y = vaddq_f32(y, c1);
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, c2);
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, c3);
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, c4);
|
||||
y = vmulq_f32(y, x);
|
||||
y = vaddq_f32(y, c5);
|
||||
|
||||
y = vmulq_f32(y, z);
|
||||
y = vaddq_f32(y, x);
|
||||
y = vaddq_f32(y, one);
|
||||
|
||||
/* build 2^n */
|
||||
int32x4_t mm;
|
||||
mm = vcvtq_s32_f32(fx);
|
||||
mm = vaddq_s32(mm, vdupq_n_s32(0x7f));
|
||||
mm = vshlq_n_s32(mm, 23);
|
||||
v4sf pow2n = vreinterpretq_f32_s32(mm);
|
||||
|
||||
y = vmulq_f32(y, pow2n);
|
||||
return y;
|
||||
}
|
||||
|
||||
#define c_minus_cephes_DP1 -0.78515625
|
||||
#define c_minus_cephes_DP2 -2.4187564849853515625e-4
|
||||
#define c_minus_cephes_DP3 -3.77489497744594108e-8
|
||||
#define c_sincof_p0 -1.9515295891E-4
|
||||
#define c_sincof_p1 8.3321608736E-3
|
||||
#define c_sincof_p2 -1.6666654611E-1
|
||||
#define c_coscof_p0 2.443315711809948E-005
|
||||
#define c_coscof_p1 -1.388731625493765E-003
|
||||
#define c_coscof_p2 4.166664568298827E-002
|
||||
#define c_cephes_FOPI 1.27323954473516 // 4 / M_PI
|
||||
|
||||
/* evaluation of 4 sines & cosines at once.
|
||||
|
||||
The code is the exact rewriting of the cephes sinf function.
|
||||
Precision is excellent as long as x < 8192 (I did not bother to
|
||||
take into account the special handling they have for greater values
|
||||
-- it does not return garbage for arguments over 8192, though, but
|
||||
the extra precision is missing).
|
||||
|
||||
Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the
|
||||
surprising but correct result.
|
||||
|
||||
Note also that when you compute sin(x), cos(x) is available at
|
||||
almost no extra price so both sin_ps and cos_ps make use of
|
||||
sincos_ps..
|
||||
*/
|
||||
void sincos_ps(v4sf x, v4sf *ysin, v4sf *ycos) { // any x
|
||||
v4sf xmm1, xmm2, xmm3, y;
|
||||
|
||||
v4su emm2;
|
||||
|
||||
v4su sign_mask_sin, sign_mask_cos;
|
||||
sign_mask_sin = vcltq_f32(x, vdupq_n_f32(0));
|
||||
x = vabsq_f32(x);
|
||||
|
||||
/* scale by 4/Pi */
|
||||
y = vmulq_f32(x, vdupq_n_f32(c_cephes_FOPI));
|
||||
|
||||
/* store the integer part of y in mm0 */
|
||||
emm2 = vcvtq_u32_f32(y);
|
||||
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||
emm2 = vaddq_u32(emm2, vdupq_n_u32(1));
|
||||
emm2 = vandq_u32(emm2, vdupq_n_u32(~1));
|
||||
y = vcvtq_f32_u32(emm2);
|
||||
|
||||
/* get the polynom selection mask
|
||||
there is one polynom for 0 <= x <= Pi/4
|
||||
and another one for Pi/4<x<=Pi/2
|
||||
|
||||
Both branches will be computed.
|
||||
*/
|
||||
v4su poly_mask = vtstq_u32(emm2, vdupq_n_u32(2));
|
||||
|
||||
/* The magic pass: "Extended precision modular arithmetic"
|
||||
x = ((x - y * DP1) - y * DP2) - y * DP3; */
|
||||
xmm1 = vmulq_n_f32(y, c_minus_cephes_DP1);
|
||||
xmm2 = vmulq_n_f32(y, c_minus_cephes_DP2);
|
||||
xmm3 = vmulq_n_f32(y, c_minus_cephes_DP3);
|
||||
x = vaddq_f32(x, xmm1);
|
||||
x = vaddq_f32(x, xmm2);
|
||||
x = vaddq_f32(x, xmm3);
|
||||
|
||||
sign_mask_sin = veorq_u32(sign_mask_sin, vtstq_u32(emm2, vdupq_n_u32(4)));
|
||||
sign_mask_cos = vtstq_u32(vsubq_u32(emm2, vdupq_n_u32(2)), vdupq_n_u32(4));
|
||||
|
||||
/* Evaluate the first polynom (0 <= x <= Pi/4) in y1,
|
||||
and the second polynom (Pi/4 <= x <= 0) in y2 */
|
||||
v4sf z = vmulq_f32(x,x);
|
||||
v4sf y1, y2;
|
||||
|
||||
y1 = vmulq_n_f32(z, c_coscof_p0);
|
||||
y2 = vmulq_n_f32(z, c_sincof_p0);
|
||||
y1 = vaddq_f32(y1, vdupq_n_f32(c_coscof_p1));
|
||||
y2 = vaddq_f32(y2, vdupq_n_f32(c_sincof_p1));
|
||||
y1 = vmulq_f32(y1, z);
|
||||
y2 = vmulq_f32(y2, z);
|
||||
y1 = vaddq_f32(y1, vdupq_n_f32(c_coscof_p2));
|
||||
y2 = vaddq_f32(y2, vdupq_n_f32(c_sincof_p2));
|
||||
y1 = vmulq_f32(y1, z);
|
||||
y2 = vmulq_f32(y2, z);
|
||||
y1 = vmulq_f32(y1, z);
|
||||
y2 = vmulq_f32(y2, x);
|
||||
y1 = vsubq_f32(y1, vmulq_f32(z, vdupq_n_f32(0.5f)));
|
||||
y2 = vaddq_f32(y2, x);
|
||||
y1 = vaddq_f32(y1, vdupq_n_f32(1));
|
||||
|
||||
/* select the correct result from the two polynoms */
|
||||
v4sf ys = vbslq_f32(poly_mask, y1, y2);
|
||||
v4sf yc = vbslq_f32(poly_mask, y2, y1);
|
||||
*ysin = vbslq_f32(sign_mask_sin, vnegq_f32(ys), ys);
|
||||
*ycos = vbslq_f32(sign_mask_cos, yc, vnegq_f32(yc));
|
||||
}
|
||||
|
||||
v4sf sin_ps(v4sf x) {
|
||||
v4sf ysin, ycos;
|
||||
sincos_ps(x, &ysin, &ycos);
|
||||
return ysin;
|
||||
}
|
||||
|
||||
v4sf cos_ps(v4sf x) {
|
||||
v4sf ysin, ycos;
|
||||
sincos_ps(x, &ysin, &ycos);
|
||||
return ycos;
|
||||
}
|
||||
|
||||
|
||||
713
src/external/mathfuns/sse_mathfun.h
vendored
713
src/external/mathfuns/sse_mathfun.h
vendored
|
|
@ -1,713 +0,0 @@
|
|||
/* SIMD (SSE1+MMX or SSE2) implementation of sin, cos, exp and log
|
||||
|
||||
Inspired by Intel Approximate Math library, and based on the
|
||||
corresponding algorithms of the cephes math library
|
||||
|
||||
The default is to use the SSE1 version. If you define USE_SSE2 the
|
||||
the SSE2 intrinsics will be used in place of the MMX intrinsics. Do
|
||||
not expect any significant performance improvement with SSE2.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2007 Julien Pommier
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
(this is the zlib license)
|
||||
*/
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
/* yes I know, the top of this file is quite ugly */
|
||||
|
||||
#ifdef _MSC_VER /* visual c++ */
|
||||
# define ALIGN16_BEG __declspec(align(16))
|
||||
# define ALIGN16_END
|
||||
#else /* gcc or icc */
|
||||
# define ALIGN16_BEG
|
||||
# define ALIGN16_END __attribute__((aligned(16)))
|
||||
#endif
|
||||
|
||||
#define USE_SSE2
|
||||
|
||||
/* __m128 is ugly to write */
|
||||
typedef __m128 v4sf; // vector of 4 float (sse1)
|
||||
|
||||
#ifdef USE_SSE2
|
||||
# include <emmintrin.h>
|
||||
typedef __m128i v4si; // vector of 4 int (sse2)
|
||||
#else
|
||||
typedef __m64 v2si; // vector of 2 int (mmx)
|
||||
#endif
|
||||
|
||||
/* declare some SSE constants -- why can't I figure a better way to do that? */
|
||||
#define _PS_CONST(Name, Val) \
|
||||
static const ALIGN16_BEG float _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
|
||||
#define _PI32_CONST(Name, Val) \
|
||||
static const ALIGN16_BEG int _pi32_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
|
||||
#define _PS_CONST_TYPE(Name, Type, Val) \
|
||||
static const ALIGN16_BEG Type _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
|
||||
|
||||
_PS_CONST(1 , 1.0f);
|
||||
_PS_CONST(0p5, 0.5f);
|
||||
/* the smallest non denormalized float number */
|
||||
_PS_CONST_TYPE(min_norm_pos, int, 0x00800000);
|
||||
_PS_CONST_TYPE(mant_mask, int, 0x7f800000);
|
||||
_PS_CONST_TYPE(inv_mant_mask, int, ~0x7f800000);
|
||||
|
||||
_PS_CONST_TYPE(sign_mask, int, (int)0x80000000);
|
||||
_PS_CONST_TYPE(inv_sign_mask, int, ~0x80000000);
|
||||
|
||||
_PI32_CONST(1, 1);
|
||||
_PI32_CONST(inv1, ~1);
|
||||
_PI32_CONST(2, 2);
|
||||
_PI32_CONST(4, 4);
|
||||
_PI32_CONST(0x7f, 0x7f);
|
||||
|
||||
_PS_CONST(cephes_SQRTHF, 0.707106781186547524f);
|
||||
_PS_CONST(cephes_log_p0, 7.0376836292E-2f);
|
||||
_PS_CONST(cephes_log_p1, - 1.1514610310E-1f);
|
||||
_PS_CONST(cephes_log_p2, 1.1676998740E-1f);
|
||||
_PS_CONST(cephes_log_p3, - 1.2420140846E-1f);
|
||||
_PS_CONST(cephes_log_p4, + 1.4249322787E-1f);
|
||||
_PS_CONST(cephes_log_p5, - 1.6668057665E-1f);
|
||||
_PS_CONST(cephes_log_p6, + 2.0000714765E-1f);
|
||||
_PS_CONST(cephes_log_p7, - 2.4999993993E-1f);
|
||||
_PS_CONST(cephes_log_p8, + 3.3333331174E-1f);
|
||||
_PS_CONST(cephes_log_q1, -2.12194440e-4f);
|
||||
_PS_CONST(cephes_log_q2, 0.693359375f);
|
||||
|
||||
#ifndef USE_SSE2
|
||||
typedef union xmm_mm_union {
|
||||
__m128 xmm;
|
||||
__m64 mm[2];
|
||||
} xmm_mm_union;
|
||||
|
||||
#define COPY_XMM_TO_MM(xmm_, mm0_, mm1_) { \
|
||||
xmm_mm_union u; u.xmm = xmm_; \
|
||||
mm0_ = u.mm[0]; \
|
||||
mm1_ = u.mm[1]; \
|
||||
}
|
||||
|
||||
#define COPY_MM_TO_XMM(mm0_, mm1_, xmm_) { \
|
||||
xmm_mm_union u; u.mm[0]=mm0_; u.mm[1]=mm1_; xmm_ = u.xmm; \
|
||||
}
|
||||
|
||||
#endif // USE_SSE2
|
||||
|
||||
/* natural logarithm computed for 4 simultaneous float
|
||||
return NaN for x <= 0
|
||||
*/
|
||||
v4sf log_ps(v4sf x) {
|
||||
#ifdef USE_SSE2
|
||||
v4si emm0;
|
||||
#else
|
||||
v2si mm0, mm1;
|
||||
#endif
|
||||
v4sf one = *(v4sf*)_ps_1;
|
||||
|
||||
v4sf invalid_mask = _mm_cmple_ps(x, _mm_setzero_ps());
|
||||
|
||||
x = _mm_max_ps(x, *(v4sf*)_ps_min_norm_pos); /* cut off denormalized stuff */
|
||||
|
||||
#ifndef USE_SSE2
|
||||
/* part 1: x = frexpf(x, &e); */
|
||||
COPY_XMM_TO_MM(x, mm0, mm1);
|
||||
mm0 = _mm_srli_pi32(mm0, 23);
|
||||
mm1 = _mm_srli_pi32(mm1, 23);
|
||||
#else
|
||||
emm0 = _mm_srli_epi32(_mm_castps_si128(x), 23);
|
||||
#endif
|
||||
/* keep only the fractional part */
|
||||
x = _mm_and_ps(x, *(v4sf*)_ps_inv_mant_mask);
|
||||
x = _mm_or_ps(x, *(v4sf*)_ps_0p5);
|
||||
|
||||
#ifndef USE_SSE2
|
||||
/* now e=mm0:mm1 contain the really base-2 exponent */
|
||||
mm0 = _mm_sub_pi32(mm0, *(v2si*)_pi32_0x7f);
|
||||
mm1 = _mm_sub_pi32(mm1, *(v2si*)_pi32_0x7f);
|
||||
v4sf e = _mm_cvtpi32x2_ps(mm0, mm1);
|
||||
_mm_empty(); /* bye bye mmx */
|
||||
#else
|
||||
emm0 = _mm_sub_epi32(emm0, *(v4si*)_pi32_0x7f);
|
||||
v4sf e = _mm_cvtepi32_ps(emm0);
|
||||
#endif
|
||||
|
||||
e = _mm_add_ps(e, one);
|
||||
|
||||
/* part2:
|
||||
if( x < SQRTHF ) {
|
||||
e -= 1;
|
||||
x = x + x - 1.0;
|
||||
} else { x = x - 1.0; }
|
||||
*/
|
||||
v4sf mask = _mm_cmplt_ps(x, *(v4sf*)_ps_cephes_SQRTHF);
|
||||
v4sf tmp = _mm_and_ps(x, mask);
|
||||
x = _mm_sub_ps(x, one);
|
||||
e = _mm_sub_ps(e, _mm_and_ps(one, mask));
|
||||
x = _mm_add_ps(x, tmp);
|
||||
|
||||
|
||||
v4sf z = _mm_mul_ps(x,x);
|
||||
|
||||
v4sf y = *(v4sf*)_ps_cephes_log_p0;
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p1);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p2);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p3);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p4);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p5);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p6);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p7);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p8);
|
||||
y = _mm_mul_ps(y, x);
|
||||
|
||||
y = _mm_mul_ps(y, z);
|
||||
|
||||
|
||||
tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q1);
|
||||
y = _mm_add_ps(y, tmp);
|
||||
|
||||
|
||||
tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
|
||||
y = _mm_sub_ps(y, tmp);
|
||||
|
||||
tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q2);
|
||||
x = _mm_add_ps(x, y);
|
||||
x = _mm_add_ps(x, tmp);
|
||||
x = _mm_or_ps(x, invalid_mask); // negative arg will be NAN
|
||||
return x;
|
||||
}
|
||||
|
||||
_PS_CONST(exp_hi, 88.3762626647949f);
|
||||
_PS_CONST(exp_lo, -88.3762626647949f);
|
||||
|
||||
_PS_CONST(cephes_LOG2EF, 1.44269504088896341f);
|
||||
_PS_CONST(cephes_exp_C1, 0.693359375f);
|
||||
_PS_CONST(cephes_exp_C2, -2.12194440e-4f);
|
||||
|
||||
_PS_CONST(cephes_exp_p0, 1.9875691500E-4f);
|
||||
_PS_CONST(cephes_exp_p1, 1.3981999507E-3f);
|
||||
_PS_CONST(cephes_exp_p2, 8.3334519073E-3f);
|
||||
_PS_CONST(cephes_exp_p3, 4.1665795894E-2f);
|
||||
_PS_CONST(cephes_exp_p4, 1.6666665459E-1f);
|
||||
_PS_CONST(cephes_exp_p5, 5.0000001201E-1f);
|
||||
|
||||
v4sf exp_ps(v4sf x) {
|
||||
v4sf tmp = _mm_setzero_ps(), fx;
|
||||
#ifdef USE_SSE2
|
||||
v4si emm0;
|
||||
#else
|
||||
v2si mm0, mm1;
|
||||
#endif
|
||||
v4sf one = *(v4sf*)_ps_1;
|
||||
|
||||
x = _mm_min_ps(x, *(v4sf*)_ps_exp_hi);
|
||||
x = _mm_max_ps(x, *(v4sf*)_ps_exp_lo);
|
||||
|
||||
/* express exp(x) as exp(g + n*log(2)) */
|
||||
fx = _mm_mul_ps(x, *(v4sf*)_ps_cephes_LOG2EF);
|
||||
fx = _mm_add_ps(fx, *(v4sf*)_ps_0p5);
|
||||
|
||||
/* how to perform a floorf with SSE: just below */
|
||||
#ifndef USE_SSE2
|
||||
/* step 1 : cast to int */
|
||||
tmp = _mm_movehl_ps(tmp, fx);
|
||||
mm0 = _mm_cvttps_pi32(fx);
|
||||
mm1 = _mm_cvttps_pi32(tmp);
|
||||
/* step 2 : cast back to float */
|
||||
tmp = _mm_cvtpi32x2_ps(mm0, mm1);
|
||||
#else
|
||||
emm0 = _mm_cvttps_epi32(fx);
|
||||
tmp = _mm_cvtepi32_ps(emm0);
|
||||
#endif
|
||||
/* if greater, substract 1 */
|
||||
v4sf mask = _mm_cmpgt_ps(tmp, fx);
|
||||
mask = _mm_and_ps(mask, one);
|
||||
fx = _mm_sub_ps(tmp, mask);
|
||||
|
||||
tmp = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C1);
|
||||
v4sf z = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C2);
|
||||
x = _mm_sub_ps(x, tmp);
|
||||
x = _mm_sub_ps(x, z);
|
||||
|
||||
z = _mm_mul_ps(x,x);
|
||||
|
||||
v4sf y = *(v4sf*)_ps_cephes_exp_p0;
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p1);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p2);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p3);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p4);
|
||||
y = _mm_mul_ps(y, x);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p5);
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_add_ps(y, x);
|
||||
y = _mm_add_ps(y, one);
|
||||
|
||||
/* build 2^n */
|
||||
#ifndef USE_SSE2
|
||||
z = _mm_movehl_ps(z, fx);
|
||||
mm0 = _mm_cvttps_pi32(fx);
|
||||
mm1 = _mm_cvttps_pi32(z);
|
||||
mm0 = _mm_add_pi32(mm0, *(v2si*)_pi32_0x7f);
|
||||
mm1 = _mm_add_pi32(mm1, *(v2si*)_pi32_0x7f);
|
||||
mm0 = _mm_slli_pi32(mm0, 23);
|
||||
mm1 = _mm_slli_pi32(mm1, 23);
|
||||
|
||||
v4sf pow2n;
|
||||
COPY_MM_TO_XMM(mm0, mm1, pow2n);
|
||||
_mm_empty();
|
||||
#else
|
||||
emm0 = _mm_cvttps_epi32(fx);
|
||||
emm0 = _mm_add_epi32(emm0, *(v4si*)_pi32_0x7f);
|
||||
emm0 = _mm_slli_epi32(emm0, 23);
|
||||
v4sf pow2n = _mm_castsi128_ps(emm0);
|
||||
#endif
|
||||
y = _mm_mul_ps(y, pow2n);
|
||||
return y;
|
||||
}
|
||||
|
||||
_PS_CONST(minus_cephes_DP1, -0.78515625f);
|
||||
_PS_CONST(minus_cephes_DP2, -2.4187564849853515625e-4f);
|
||||
_PS_CONST(minus_cephes_DP3, -3.77489497744594108e-8f);
|
||||
_PS_CONST(sincof_p0, -1.9515295891E-4f);
|
||||
_PS_CONST(sincof_p1, 8.3321608736E-3f);
|
||||
_PS_CONST(sincof_p2, -1.6666654611E-1f);
|
||||
_PS_CONST(coscof_p0, 2.443315711809948E-005f);
|
||||
_PS_CONST(coscof_p1, -1.388731625493765E-003f);
|
||||
_PS_CONST(coscof_p2, 4.166664568298827E-002f);
|
||||
_PS_CONST(cephes_FOPI, 1.27323954473516f); // 4 / M_PI
|
||||
|
||||
|
||||
/* evaluation of 4 sines at onces, using only SSE1+MMX intrinsics so
|
||||
it runs also on old athlons XPs and the pentium III of your grand
|
||||
mother.
|
||||
|
||||
The code is the exact rewriting of the cephes sinf function.
|
||||
Precision is excellent as long as x < 8192 (I did not bother to
|
||||
take into account the special handling they have for greater values
|
||||
-- it does not return garbage for arguments over 8192, though, but
|
||||
the extra precision is missing).
|
||||
|
||||
Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the
|
||||
surprising but correct result.
|
||||
|
||||
Performance is also surprisingly good, 1.33 times faster than the
|
||||
macos vsinf SSE2 function, and 1.5 times faster than the
|
||||
__vrs4_sinf of amd's ACML (which is only available in 64 bits). Not
|
||||
too bad for an SSE1 function (with no special tuning) !
|
||||
However the latter libraries probably have a much better handling of NaN,
|
||||
Inf, denormalized and other special arguments..
|
||||
|
||||
On my core 1 duo, the execution of this function takes approximately 95 cycles.
|
||||
|
||||
From what I have observed on the experiments with Intel AMath lib, switching to an
|
||||
SSE2 version would improve the perf by only 10%.
|
||||
|
||||
Since it is based on SSE intrinsics, it has to be compiled at -O2 to
|
||||
deliver full speed.
|
||||
*/
|
||||
v4sf sin_ps(v4sf x) { // any x
|
||||
v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y;
|
||||
|
||||
#ifdef USE_SSE2
|
||||
v4si emm0, emm2;
|
||||
#else
|
||||
v2si mm0, mm1, mm2, mm3;
|
||||
#endif
|
||||
sign_bit = x;
|
||||
/* take the absolute value */
|
||||
x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
|
||||
/* extract the sign bit (upper one) */
|
||||
sign_bit = _mm_and_ps(sign_bit, *(v4sf*)_ps_sign_mask);
|
||||
|
||||
/* scale by 4/Pi */
|
||||
y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
|
||||
|
||||
#ifdef USE_SSE2
|
||||
/* store the integer part of y in mm0 */
|
||||
emm2 = _mm_cvttps_epi32(y);
|
||||
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||
emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
|
||||
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
|
||||
y = _mm_cvtepi32_ps(emm2);
|
||||
|
||||
/* get the swap sign flag */
|
||||
emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4);
|
||||
emm0 = _mm_slli_epi32(emm0, 29);
|
||||
/* get the polynom selection mask
|
||||
there is one polynom for 0 <= x <= Pi/4
|
||||
and another one for Pi/4<x<=Pi/2
|
||||
|
||||
Both branches will be computed.
|
||||
*/
|
||||
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
|
||||
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
|
||||
|
||||
v4sf swap_sign_bit = _mm_castsi128_ps(emm0);
|
||||
v4sf poly_mask = _mm_castsi128_ps(emm2);
|
||||
sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
|
||||
|
||||
#else
|
||||
/* store the integer part of y in mm0:mm1 */
|
||||
xmm2 = _mm_movehl_ps(xmm2, y);
|
||||
mm2 = _mm_cvttps_pi32(y);
|
||||
mm3 = _mm_cvttps_pi32(xmm2);
|
||||
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||
mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
|
||||
mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
|
||||
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
|
||||
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
|
||||
y = _mm_cvtpi32x2_ps(mm2, mm3);
|
||||
/* get the swap sign flag */
|
||||
mm0 = _mm_and_si64(mm2, *(v2si*)_pi32_4);
|
||||
mm1 = _mm_and_si64(mm3, *(v2si*)_pi32_4);
|
||||
mm0 = _mm_slli_pi32(mm0, 29);
|
||||
mm1 = _mm_slli_pi32(mm1, 29);
|
||||
/* get the polynom selection mask */
|
||||
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
|
||||
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
|
||||
mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
|
||||
mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
|
||||
v4sf swap_sign_bit, poly_mask;
|
||||
COPY_MM_TO_XMM(mm0, mm1, swap_sign_bit);
|
||||
COPY_MM_TO_XMM(mm2, mm3, poly_mask);
|
||||
sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
|
||||
_mm_empty(); /* good-bye mmx */
|
||||
#endif
|
||||
|
||||
/* The magic pass: "Extended precision modular arithmetic"
|
||||
x = ((x - y * DP1) - y * DP2) - y * DP3; */
|
||||
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
|
||||
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
|
||||
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
|
||||
xmm1 = _mm_mul_ps(y, xmm1);
|
||||
xmm2 = _mm_mul_ps(y, xmm2);
|
||||
xmm3 = _mm_mul_ps(y, xmm3);
|
||||
x = _mm_add_ps(x, xmm1);
|
||||
x = _mm_add_ps(x, xmm2);
|
||||
x = _mm_add_ps(x, xmm3);
|
||||
|
||||
/* Evaluate the first polynom (0 <= x <= Pi/4) */
|
||||
y = *(v4sf*)_ps_coscof_p0;
|
||||
v4sf z = _mm_mul_ps(x,x);
|
||||
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_mul_ps(y, z);
|
||||
v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
|
||||
y = _mm_sub_ps(y, tmp);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_1);
|
||||
|
||||
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
|
||||
|
||||
v4sf y2 = *(v4sf*)_ps_sincof_p0;
|
||||
y2 = _mm_mul_ps(y2, z);
|
||||
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
|
||||
y2 = _mm_mul_ps(y2, z);
|
||||
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
|
||||
y2 = _mm_mul_ps(y2, z);
|
||||
y2 = _mm_mul_ps(y2, x);
|
||||
y2 = _mm_add_ps(y2, x);
|
||||
|
||||
/* select the correct result from the two polynoms */
|
||||
xmm3 = poly_mask;
|
||||
y2 = _mm_and_ps(xmm3, y2); //, xmm3);
|
||||
y = _mm_andnot_ps(xmm3, y);
|
||||
y = _mm_add_ps(y,y2);
|
||||
/* update the sign */
|
||||
y = _mm_xor_ps(y, sign_bit);
|
||||
return y;
|
||||
}
|
||||
|
||||
/* almost the same as sin_ps */
|
||||
v4sf cos_ps(v4sf x) { // any x
|
||||
v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, y;
|
||||
#ifdef USE_SSE2
|
||||
v4si emm0, emm2;
|
||||
#else
|
||||
v2si mm0, mm1, mm2, mm3;
|
||||
#endif
|
||||
/* take the absolute value */
|
||||
x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
|
||||
|
||||
/* scale by 4/Pi */
|
||||
y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
|
||||
|
||||
#ifdef USE_SSE2
|
||||
/* store the integer part of y in mm0 */
|
||||
emm2 = _mm_cvttps_epi32(y);
|
||||
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||
emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
|
||||
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
|
||||
y = _mm_cvtepi32_ps(emm2);
|
||||
|
||||
emm2 = _mm_sub_epi32(emm2, *(v4si*)_pi32_2);
|
||||
|
||||
/* get the swap sign flag */
|
||||
emm0 = _mm_andnot_si128(emm2, *(v4si*)_pi32_4);
|
||||
emm0 = _mm_slli_epi32(emm0, 29);
|
||||
/* get the polynom selection mask */
|
||||
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
|
||||
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
|
||||
|
||||
v4sf sign_bit = _mm_castsi128_ps(emm0);
|
||||
v4sf poly_mask = _mm_castsi128_ps(emm2);
|
||||
#else
|
||||
/* store the integer part of y in mm0:mm1 */
|
||||
xmm2 = _mm_movehl_ps(xmm2, y);
|
||||
mm2 = _mm_cvttps_pi32(y);
|
||||
mm3 = _mm_cvttps_pi32(xmm2);
|
||||
|
||||
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||
mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
|
||||
mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
|
||||
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
|
||||
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
|
||||
|
||||
y = _mm_cvtpi32x2_ps(mm2, mm3);
|
||||
|
||||
|
||||
mm2 = _mm_sub_pi32(mm2, *(v2si*)_pi32_2);
|
||||
mm3 = _mm_sub_pi32(mm3, *(v2si*)_pi32_2);
|
||||
|
||||
/* get the swap sign flag in mm0:mm1 and the
|
||||
polynom selection mask in mm2:mm3 */
|
||||
|
||||
mm0 = _mm_andnot_si64(mm2, *(v2si*)_pi32_4);
|
||||
mm1 = _mm_andnot_si64(mm3, *(v2si*)_pi32_4);
|
||||
mm0 = _mm_slli_pi32(mm0, 29);
|
||||
mm1 = _mm_slli_pi32(mm1, 29);
|
||||
|
||||
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
|
||||
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
|
||||
|
||||
mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
|
||||
mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
|
||||
|
||||
v4sf sign_bit, poly_mask;
|
||||
COPY_MM_TO_XMM(mm0, mm1, sign_bit);
|
||||
COPY_MM_TO_XMM(mm2, mm3, poly_mask);
|
||||
_mm_empty(); /* good-bye mmx */
|
||||
#endif
|
||||
/* The magic pass: "Extended precision modular arithmetic"
|
||||
x = ((x - y * DP1) - y * DP2) - y * DP3; */
|
||||
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
|
||||
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
|
||||
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
|
||||
xmm1 = _mm_mul_ps(y, xmm1);
|
||||
xmm2 = _mm_mul_ps(y, xmm2);
|
||||
xmm3 = _mm_mul_ps(y, xmm3);
|
||||
x = _mm_add_ps(x, xmm1);
|
||||
x = _mm_add_ps(x, xmm2);
|
||||
x = _mm_add_ps(x, xmm3);
|
||||
|
||||
/* Evaluate the first polynom (0 <= x <= Pi/4) */
|
||||
y = *(v4sf*)_ps_coscof_p0;
|
||||
v4sf z = _mm_mul_ps(x,x);
|
||||
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_mul_ps(y, z);
|
||||
v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
|
||||
y = _mm_sub_ps(y, tmp);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_1);
|
||||
|
||||
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
|
||||
|
||||
v4sf y2 = *(v4sf*)_ps_sincof_p0;
|
||||
y2 = _mm_mul_ps(y2, z);
|
||||
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
|
||||
y2 = _mm_mul_ps(y2, z);
|
||||
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
|
||||
y2 = _mm_mul_ps(y2, z);
|
||||
y2 = _mm_mul_ps(y2, x);
|
||||
y2 = _mm_add_ps(y2, x);
|
||||
|
||||
/* select the correct result from the two polynoms */
|
||||
xmm3 = poly_mask;
|
||||
y2 = _mm_and_ps(xmm3, y2); //, xmm3);
|
||||
y = _mm_andnot_ps(xmm3, y);
|
||||
y = _mm_add_ps(y,y2);
|
||||
/* update the sign */
|
||||
y = _mm_xor_ps(y, sign_bit);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
/* since sin_ps and cos_ps are almost identical, sincos_ps could replace both of them..
|
||||
it is almost as fast, and gives you a free cosine with your sine */
|
||||
void sincos_ps(v4sf x, v4sf *s, v4sf *c) {
|
||||
v4sf xmm1, xmm2, xmm3 = _mm_setzero_ps(), sign_bit_sin, y;
|
||||
#ifdef USE_SSE2
|
||||
v4si emm0, emm2, emm4;
|
||||
#else
|
||||
v2si mm0, mm1, mm2, mm3, mm4, mm5;
|
||||
#endif
|
||||
sign_bit_sin = x;
|
||||
/* take the absolute value */
|
||||
x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
|
||||
/* extract the sign bit (upper one) */
|
||||
sign_bit_sin = _mm_and_ps(sign_bit_sin, *(v4sf*)_ps_sign_mask);
|
||||
|
||||
/* scale by 4/Pi */
|
||||
y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
|
||||
|
||||
#ifdef USE_SSE2
|
||||
/* store the integer part of y in emm2 */
|
||||
emm2 = _mm_cvttps_epi32(y);
|
||||
|
||||
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||
emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
|
||||
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
|
||||
y = _mm_cvtepi32_ps(emm2);
|
||||
|
||||
emm4 = emm2;
|
||||
|
||||
/* get the swap sign flag for the sine */
|
||||
emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4);
|
||||
emm0 = _mm_slli_epi32(emm0, 29);
|
||||
v4sf swap_sign_bit_sin = _mm_castsi128_ps(emm0);
|
||||
|
||||
/* get the polynom selection mask for the sine*/
|
||||
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
|
||||
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
|
||||
v4sf poly_mask = _mm_castsi128_ps(emm2);
|
||||
#else
|
||||
/* store the integer part of y in mm2:mm3 */
|
||||
xmm3 = _mm_movehl_ps(xmm3, y);
|
||||
mm2 = _mm_cvttps_pi32(y);
|
||||
mm3 = _mm_cvttps_pi32(xmm3);
|
||||
|
||||
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||
mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
|
||||
mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
|
||||
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
|
||||
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
|
||||
|
||||
y = _mm_cvtpi32x2_ps(mm2, mm3);
|
||||
|
||||
mm4 = mm2;
|
||||
mm5 = mm3;
|
||||
|
||||
/* get the swap sign flag for the sine */
|
||||
mm0 = _mm_and_si64(mm2, *(v2si*)_pi32_4);
|
||||
mm1 = _mm_and_si64(mm3, *(v2si*)_pi32_4);
|
||||
mm0 = _mm_slli_pi32(mm0, 29);
|
||||
mm1 = _mm_slli_pi32(mm1, 29);
|
||||
v4sf swap_sign_bit_sin;
|
||||
COPY_MM_TO_XMM(mm0, mm1, swap_sign_bit_sin);
|
||||
|
||||
/* get the polynom selection mask for the sine */
|
||||
|
||||
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
|
||||
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
|
||||
mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
|
||||
mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
|
||||
v4sf poly_mask;
|
||||
COPY_MM_TO_XMM(mm2, mm3, poly_mask);
|
||||
#endif
|
||||
|
||||
/* The magic pass: "Extended precision modular arithmetic"
|
||||
x = ((x - y * DP1) - y * DP2) - y * DP3; */
|
||||
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
|
||||
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
|
||||
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
|
||||
xmm1 = _mm_mul_ps(y, xmm1);
|
||||
xmm2 = _mm_mul_ps(y, xmm2);
|
||||
xmm3 = _mm_mul_ps(y, xmm3);
|
||||
x = _mm_add_ps(x, xmm1);
|
||||
x = _mm_add_ps(x, xmm2);
|
||||
x = _mm_add_ps(x, xmm3);
|
||||
|
||||
#ifdef USE_SSE2
|
||||
emm4 = _mm_sub_epi32(emm4, *(v4si*)_pi32_2);
|
||||
emm4 = _mm_andnot_si128(emm4, *(v4si*)_pi32_4);
|
||||
emm4 = _mm_slli_epi32(emm4, 29);
|
||||
v4sf sign_bit_cos = _mm_castsi128_ps(emm4);
|
||||
#else
|
||||
/* get the sign flag for the cosine */
|
||||
mm4 = _mm_sub_pi32(mm4, *(v2si*)_pi32_2);
|
||||
mm5 = _mm_sub_pi32(mm5, *(v2si*)_pi32_2);
|
||||
mm4 = _mm_andnot_si64(mm4, *(v2si*)_pi32_4);
|
||||
mm5 = _mm_andnot_si64(mm5, *(v2si*)_pi32_4);
|
||||
mm4 = _mm_slli_pi32(mm4, 29);
|
||||
mm5 = _mm_slli_pi32(mm5, 29);
|
||||
v4sf sign_bit_cos;
|
||||
COPY_MM_TO_XMM(mm4, mm5, sign_bit_cos);
|
||||
_mm_empty(); /* good-bye mmx */
|
||||
#endif
|
||||
|
||||
sign_bit_sin = _mm_xor_ps(sign_bit_sin, swap_sign_bit_sin);
|
||||
|
||||
|
||||
/* Evaluate the first polynom (0 <= x <= Pi/4) */
|
||||
v4sf z = _mm_mul_ps(x,x);
|
||||
y = *(v4sf*)_ps_coscof_p0;
|
||||
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
|
||||
y = _mm_mul_ps(y, z);
|
||||
y = _mm_mul_ps(y, z);
|
||||
v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
|
||||
y = _mm_sub_ps(y, tmp);
|
||||
y = _mm_add_ps(y, *(v4sf*)_ps_1);
|
||||
|
||||
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
|
||||
|
||||
v4sf y2 = *(v4sf*)_ps_sincof_p0;
|
||||
y2 = _mm_mul_ps(y2, z);
|
||||
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
|
||||
y2 = _mm_mul_ps(y2, z);
|
||||
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
|
||||
y2 = _mm_mul_ps(y2, z);
|
||||
y2 = _mm_mul_ps(y2, x);
|
||||
y2 = _mm_add_ps(y2, x);
|
||||
|
||||
/* select the correct result from the two polynoms */
|
||||
xmm3 = poly_mask;
|
||||
v4sf ysin2 = _mm_and_ps(xmm3, y2);
|
||||
v4sf ysin1 = _mm_andnot_ps(xmm3, y);
|
||||
y2 = _mm_sub_ps(y2,ysin2);
|
||||
y = _mm_sub_ps(y, ysin1);
|
||||
|
||||
xmm1 = _mm_add_ps(ysin1,ysin2);
|
||||
xmm2 = _mm_add_ps(y,y2);
|
||||
|
||||
/* update the sign */
|
||||
*s = _mm_xor_ps(xmm1, sign_bit_sin);
|
||||
*c = _mm_xor_ps(xmm2, sign_bit_cos);
|
||||
}
|
||||
|
||||
9
src/external/tunings/LICENSE.md
vendored
Normal file
9
src/external/tunings/LICENSE.md
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
Copyright 2019-2020, Paul Walker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
2
src/external/tunings/README.txt
vendored
Normal file
2
src/external/tunings/README.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Based on the Surge tuning library, revision a5f2879, with small modifications
|
||||
https://github.com/surge-synthesizer/tuning-library
|
||||
245
src/external/tunings/include/Tunings.h
vendored
Normal file
245
src/external/tunings/include/Tunings.h
vendored
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
// -*-c++-*-
|
||||
|
||||
/**
|
||||
* Tunings.h
|
||||
* Copyright Paul Walker, 2019-2020
|
||||
* Released under the MIT License. See LICENSE.md
|
||||
*
|
||||
* Tunings.h contains the public API required to determine full keyboard frequency maps
|
||||
* for a scala SCL and KBM file in standalone, tested, open licensed C++ header only library.
|
||||
*
|
||||
* An example of using the API is
|
||||
*
|
||||
* ```
|
||||
* auto s = Tunings::readSCLFile( "./my-scale.scl" );
|
||||
* auto k = Tunings::readKBMFile( "./my-mapping.kbm" );
|
||||
*
|
||||
* Tunings::Tuning t( s, k );
|
||||
*
|
||||
* std::cout << "The frequency of C4 and A4 are "
|
||||
* << t.frequencyForMidiNote( 60 ) << " and "
|
||||
* << t.frequencyForMidiNote( 69 ) << std::endl;
|
||||
* ```
|
||||
*
|
||||
* The API provides several other points, such as access to the structure of the SCL and KBM,
|
||||
* the ability to create several prototype SCL and KBM files wthout SCL or KBM content,
|
||||
* a frequency measure which is normalized by the frequency of standard tuning midi note 0
|
||||
* and the logarithmic frequency scale, with a doubling per frequency doubling.
|
||||
*
|
||||
* Documentation is in the class header below; tests are in `tests/all_tests.cpp` and
|
||||
* a variety of command line tools accompany the header.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <array>
|
||||
|
||||
namespace Tunings
|
||||
{
|
||||
static constexpr double MIDI_0_FREQ = 8.17579891564371; // or 440.0 * pow( 2.0, - (69.0/12.0 ) )
|
||||
|
||||
static constexpr int MAX_CAPACITY = 64; // fixed capacity of note/key lists
|
||||
|
||||
/**
|
||||
* A Tone is a single entry in an SCL file. It is expressed either in cents or in
|
||||
* a ratio, as described in the SCL documentation.
|
||||
*
|
||||
* In most normal use, you will not use this class, and it will be internal to a Scale
|
||||
*/
|
||||
struct Tone
|
||||
{
|
||||
typedef enum Type
|
||||
{
|
||||
kToneCents, // An SCL representation like "133.0"
|
||||
kToneRatio // An SCL representation like "3/7"
|
||||
} Type;
|
||||
|
||||
Type type = kToneRatio;
|
||||
double cents = 0;
|
||||
int ratio_d = 1, ratio_n = 1;
|
||||
double floatValue = 1.0; // cents / 1200 + 1.
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an SCL string like "100.231" or "3/7" set up a Tone
|
||||
*/
|
||||
Tone toneFromString(const std::string &t, int lineno=-1);
|
||||
|
||||
/**
|
||||
* The Scale is the representation of the SCL file. It contains several key
|
||||
* features. Most importantly it has a count and a vector of Tones.
|
||||
*
|
||||
* In most normal use, you will simply pass around instances of this class
|
||||
* to a Tunings::Tuning instance, but in some cases you may want to create
|
||||
* or inspect this class yourself.
|
||||
*/
|
||||
struct Scale
|
||||
{
|
||||
std::string description; // The description in the SCL file. Informational only
|
||||
int count = 0; // The number of tones.
|
||||
Tone tones[MAX_CAPACITY]; // The tones
|
||||
};
|
||||
|
||||
/**
|
||||
* The KeyboardMapping class represents a KBM file. In most cases, the salient
|
||||
* features are the tuningConstantNote and tuningFrequency, which allow you to
|
||||
* pick a fixed note in the midi keyboard when retuning. The KBM file can also
|
||||
* remap individual keys to individual points in a scale, which kere is done with the
|
||||
* keys vector.
|
||||
*/
|
||||
|
||||
struct KeyboardMapping
|
||||
{
|
||||
int count = 0;
|
||||
int firstMidi = 0, lastMidi = 127;
|
||||
int middleNote = 60;
|
||||
int tuningConstantNote = 60;
|
||||
double tuningFrequency = MIDI_0_FREQ * 32.0, tuningPitch = 32.0; // pitch = frequency / MIDI_0_FREQ
|
||||
int octaveDegrees = 12;
|
||||
int keys[MAX_CAPACITY]; // rather than an 'x' we use a '-1' for skipped keys
|
||||
};
|
||||
|
||||
/**
|
||||
* In some failure states, the tuning library will throw an exception of
|
||||
* type TuningError with a descriptive message.
|
||||
*/
|
||||
class TuningError : public std::exception {
|
||||
public:
|
||||
explicit TuningError(std::string m) : whatv(std::move(m)) { }
|
||||
virtual const char* what() const noexcept override { return whatv.c_str(); }
|
||||
private:
|
||||
std::string whatv;
|
||||
};
|
||||
|
||||
/**
|
||||
* readSCLStream returns a Scale from the SCL input stream
|
||||
*/
|
||||
Scale readSCLStream(std::istream &inf);
|
||||
|
||||
/**
|
||||
* readSCLFile returns a Scale from the SCL File in fname
|
||||
*/
|
||||
Scale readSCLFile(std::string fname);
|
||||
|
||||
/**
|
||||
* parseSCLData returns a scale from the SCL file contents in memory
|
||||
*/
|
||||
Scale parseSCLData(const std::string &sclContents);
|
||||
|
||||
/**
|
||||
* evenTemperament12NoteScale provides a utility scale which is
|
||||
* the "standard tuning" scale
|
||||
*/
|
||||
Scale evenTemperament12NoteScale();
|
||||
|
||||
#if 0 // Note(jpc): not rewritten RT-safe, not used, disabled
|
||||
/**
|
||||
* evenDivisionOfSpanByM provides a scale referd to as "ED2-17" or
|
||||
* "ED3-24" by dividing the Span into M points. eventDivisionOfSpanByM(2,12)
|
||||
* should be the evenTemperament12NoteScale
|
||||
*/
|
||||
Scale evenDivisionOfSpanByM( int Span, int M );
|
||||
#endif
|
||||
|
||||
/**
|
||||
* readKBMStream returns a KeyboardMapping from a KBM input stream
|
||||
*/
|
||||
KeyboardMapping readKBMStream(std::istream &inf);
|
||||
|
||||
/**
|
||||
* readKBMFile returns a KeyboardMapping from a KBM file name
|
||||
*/
|
||||
KeyboardMapping readKBMFile(std::string fname);
|
||||
|
||||
/**
|
||||
* parseKBMData returns a KeyboardMapping from a KBM data in memory
|
||||
*/
|
||||
KeyboardMapping parseKBMData(const std::string &kbmContents);
|
||||
|
||||
/**
|
||||
* tuneA69To creates a KeyboardMapping which keeps the midi note 69 (A4) set
|
||||
* to a constant frequency, given
|
||||
*/
|
||||
KeyboardMapping tuneA69To(double freq);
|
||||
|
||||
/**
|
||||
* tuneNoteTo creates a KeyboardMapping which keeps the midi note given is set
|
||||
* to a constant frequency, given
|
||||
*/
|
||||
KeyboardMapping tuneNoteTo(int midiNote, double freq);
|
||||
|
||||
/**
|
||||
* startScaleOnAndTuneNoteTo generates a KBM where scaleStart is the note 0
|
||||
* of the scale, where midiNote is the tuned note, and where feq is the frequency
|
||||
*/
|
||||
KeyboardMapping startScaleOnAndTuneNoteTo(int scaleStart, int midiNote, double freq);
|
||||
|
||||
/**
|
||||
* The Tuning class is the primary place where you will interact with this library.
|
||||
* It is constructed for a scale and mapping and then gives you the ability to
|
||||
* determine frequencies across and beyond the midi keyboard. Since modulation
|
||||
* can force key number well outside the [0,127] range in some of our synths we
|
||||
* support a midi note range from -256 to + 256 spanning more than the entire frequency
|
||||
* space reasonable.
|
||||
*
|
||||
* To use this class, you construct a fresh instance every time you want to use a
|
||||
* different Scale and Keyboard. If you want to tune to a different scale or mapping,
|
||||
* just construct a new instance.
|
||||
*/
|
||||
class Tuning {
|
||||
public:
|
||||
// The number of notes we pre-compute
|
||||
constexpr static int N = 512;
|
||||
|
||||
// Construct a tuning with even temperament and standard mapping
|
||||
Tuning();
|
||||
|
||||
/**
|
||||
* Construct a tuning for a particular scale, mapping, or for both.
|
||||
*/
|
||||
explicit Tuning( const Scale &s );
|
||||
explicit Tuning( const KeyboardMapping &k );
|
||||
Tuning( const Scale &s, const KeyboardMapping &k );
|
||||
|
||||
/**
|
||||
* These three related functions provide you the information you
|
||||
* need to use this tuning.
|
||||
*
|
||||
* frequencyForMidiNote returns the Frequency in HZ for a given midi
|
||||
* note. In standard tuning, FrequencyForMidiNote(69) will be 440
|
||||
* and frequencyForMidiNote(60) will be 261.62 - the standard frequencies
|
||||
* for A and middle C.
|
||||
*
|
||||
* frequencyForMidiNoteScaledByMidi0 returns the frequency but with the
|
||||
* standard frequency of midi note 0 divided out. So in standard tuning
|
||||
* frequencyForMidiNoteScaledByMidi0(0) = 1 and frequencyForMidiNoteScaledByMidi0(60) = 32
|
||||
*
|
||||
* Finally logScaledFrequencyForMidiNote returns the log base 2 of the scaled frequency.
|
||||
* So logScaledFrequencyForMidiNote(0) = 0 and logScaledFrequencyForMidiNote(60) = 5.
|
||||
*
|
||||
* Both the frequency measures have the feature of doubling when frequency doubles
|
||||
* (or when a standard octave is spanned), whereas the log one increase by 1 per frequency double.
|
||||
*
|
||||
* Depending on your internal pitch model, one of these three methods should allow you
|
||||
* to calibrate your oscillators to the appropriate frequency based on the midi note
|
||||
* at hand.
|
||||
*
|
||||
* The scalePositionForMidiNote returns the space in the logical scale. Note 0 is the root.
|
||||
* It has a maxiumum value of count-1. Note that SCL files omit the root internally and so
|
||||
* this logical scale position is off by 1 from the index in the tones array of the Scale data.
|
||||
*/
|
||||
double frequencyForMidiNote( int mn ) const;
|
||||
double frequencyForMidiNoteScaledByMidi0( int mn ) const;
|
||||
double logScaledFrequencyForMidiNote( int mn ) const;
|
||||
int scalePositionForMidiNote( int mn ) const;
|
||||
|
||||
// For convenience, the scale and mapping used to construct this are kept as public copies
|
||||
Scale scale;
|
||||
KeyboardMapping keyboardMapping;
|
||||
private:
|
||||
std::array<double, N> ptable, lptable;
|
||||
std::array<int, N> scalepositiontable;
|
||||
};
|
||||
|
||||
} // namespace Tunings
|
||||
522
src/external/tunings/src/Tunings.cpp
vendored
Normal file
522
src/external/tunings/src/Tunings.cpp
vendored
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
// -*-c++-*-
|
||||
/**
|
||||
* TuningsImpl.h
|
||||
* Copyright 2019-2020 Paul Walker
|
||||
* Released under the MIT License. See LICENSE.md
|
||||
*
|
||||
* This contains the nasty nitty gritty implementation of the api in Tunings.h. You probably
|
||||
* don't need to read it unless you have found and are fixing a bug, are curious, or want
|
||||
* to add a feature to the API. For usages of this library, the documentation in Tunings.h and
|
||||
* the usages in tests/all_tests.cpp should provide you more than enough guidance.
|
||||
*/
|
||||
|
||||
#include "Tunings.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
|
||||
namespace Tunings
|
||||
{
|
||||
static double locale_atof(const char* s)
|
||||
{
|
||||
double result = 0;
|
||||
std::istringstream istr(s);
|
||||
istr.imbue(std::locale("C"));
|
||||
istr >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
Tone toneFromString(const std::string &line, int lineno)
|
||||
{
|
||||
Tone t;
|
||||
if (line.find(".") != std::string::npos)
|
||||
{
|
||||
t.type = Tone::kToneCents;
|
||||
t.cents = locale_atof(line.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
t.type = Tone::kToneRatio;
|
||||
auto slashPos = line.find("/");
|
||||
if (slashPos == std::string::npos)
|
||||
{
|
||||
t.ratio_n = atoi(line.c_str());
|
||||
t.ratio_d = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
t.ratio_n = atoi(line.substr(0, slashPos).c_str());
|
||||
t.ratio_d = atoi(line.substr(slashPos + 1).c_str());
|
||||
}
|
||||
|
||||
if( t.ratio_n == 0 || t.ratio_d == 0 )
|
||||
{
|
||||
std::string s = "Invalid Tone in SCL file.";
|
||||
if( lineno >= 0 )
|
||||
s += "Line " + std::to_string(lineno) + ".";
|
||||
s += " Line is '" + line + "'.";
|
||||
throw TuningError(s);
|
||||
}
|
||||
// 2^(cents/1200) = n/d
|
||||
// cents = 1200 * log2(n/d)
|
||||
|
||||
t.cents = 1200 * log2(1.0 * t.ratio_n/t.ratio_d);
|
||||
}
|
||||
t.floatValue = t.cents / 1200.0 + 1.0;
|
||||
return t;
|
||||
}
|
||||
|
||||
Scale readSCLStream(std::istream &inf)
|
||||
{
|
||||
std::string line;
|
||||
const int read_header = 0, read_count = 1, read_note = 2, trailing = 3;
|
||||
int state = read_header;
|
||||
int tone_index = 0;
|
||||
|
||||
Scale res;
|
||||
int lineno = 0;
|
||||
while (std::getline(inf, line))
|
||||
{
|
||||
lineno ++;
|
||||
|
||||
if (line.empty() || line[0] == '!')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (state)
|
||||
{
|
||||
case read_header:
|
||||
res.description = line;
|
||||
state = read_count;
|
||||
break;
|
||||
case read_count:
|
||||
res.count = atoi(line.c_str());
|
||||
if(res.count < 0 || res.count > MAX_CAPACITY)
|
||||
{
|
||||
throw TuningError( "Tone count invalid or too large in SCL file." );
|
||||
}
|
||||
state = ( res.count > 0 ) ? read_note : trailing;
|
||||
break;
|
||||
case read_note:
|
||||
res.tones[tone_index++] = toneFromString(line, lineno);
|
||||
if( tone_index == res.count )
|
||||
state = trailing;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( ! ( state == read_note || state == trailing ) )
|
||||
{
|
||||
throw TuningError( "Incomplete SCL file. Found no notes section in the file" );
|
||||
}
|
||||
|
||||
if( tone_index != res.count )
|
||||
{
|
||||
std::string s = "Read fewer notes than count in file. Count=" + std::to_string( res.count )
|
||||
+ " notes array size=" + std::to_string( tone_index );
|
||||
throw TuningError(s);
|
||||
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Scale readSCLFile(std::string fname)
|
||||
{
|
||||
std::ifstream inf;
|
||||
inf.open(fname);
|
||||
if (!inf.is_open())
|
||||
{
|
||||
std::string s = "Unable to open file '" + fname + "'";
|
||||
throw TuningError(s);
|
||||
}
|
||||
|
||||
auto res = readSCLStream(inf);
|
||||
return res;
|
||||
}
|
||||
|
||||
Scale parseSCLData(const std::string &d)
|
||||
{
|
||||
std::istringstream iss(d);
|
||||
auto res = readSCLStream(iss);
|
||||
return res;
|
||||
}
|
||||
|
||||
Scale evenTemperament12NoteScale()
|
||||
{
|
||||
Scale res;
|
||||
res.count = 12;
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
Tone &t = res.tones[i];
|
||||
t.type = Tone::kToneCents;
|
||||
t.cents = 100 * (i + 1);
|
||||
t.floatValue = t.cents / 1200.0 + 1.0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#if 0
|
||||
Scale evenDivisionOfSpanByM( int Span, int M )
|
||||
{
|
||||
if( Span <= 0 )
|
||||
throw Tunings::TuningError( "Span should be a positive number. You entered " + std::to_string( Span ) );
|
||||
if( M <= 0 )
|
||||
throw Tunings::TuningError( "You must divide the period into at least one step. You entered " + std::to_string( M ) );
|
||||
|
||||
std::ostringstream oss;
|
||||
oss.imbue( std::locale( "C" ) );
|
||||
oss << "! Automatically generated ED" << Span << "-" << M << " scale\n";
|
||||
oss << "Automatically generated ED" << Span << "-" << M << " scale\n";
|
||||
oss << M << "\n";
|
||||
oss << "!\n";
|
||||
|
||||
|
||||
double topCents = 1200.0 * log2(1.0 * Span);
|
||||
double dCents = topCents / M;
|
||||
for( int i=1; i<M; ++i )
|
||||
oss << std::fixed << dCents * i << "\n";
|
||||
oss << Span << "/1\n";
|
||||
|
||||
return parseSCLData( oss.str() );
|
||||
}
|
||||
#endif
|
||||
|
||||
KeyboardMapping readKBMStream(std::istream &inf)
|
||||
{
|
||||
std::string line;
|
||||
|
||||
KeyboardMapping res;
|
||||
|
||||
enum parsePosition {
|
||||
map_size = 0,
|
||||
first_midi,
|
||||
last_midi,
|
||||
middle,
|
||||
reference,
|
||||
freq,
|
||||
degree,
|
||||
keys,
|
||||
trailing
|
||||
};
|
||||
parsePosition state = map_size;
|
||||
int key_index = 0;
|
||||
|
||||
int lineno = 0;
|
||||
while (std::getline(inf, line))
|
||||
{
|
||||
lineno ++;
|
||||
if (line.empty() || line[0] == '!')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( line == "x" ) line = "-1";
|
||||
else if( state != trailing )
|
||||
{
|
||||
const char* lc = line.c_str();
|
||||
bool validLine = line.length() > 0;
|
||||
char badChar = '\0';
|
||||
while( validLine && *lc != '\0' )
|
||||
{
|
||||
if( ! ( *lc == ' ' || std::isdigit( *lc ) || *lc == '.' || *lc == (char)13 || *lc == '\n' ) )
|
||||
{
|
||||
validLine = false;
|
||||
badChar = *lc;
|
||||
}
|
||||
lc ++;
|
||||
}
|
||||
if( ! validLine )
|
||||
{
|
||||
throw TuningError( "Invalid line " + std::to_string( lineno ) + ". line='" + line + "'. Bad char is '" +
|
||||
badChar + "/" + std::to_string( (int)badChar ) + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
int i = std::atoi(line.c_str());
|
||||
double v = locale_atof(line.c_str());
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case map_size:
|
||||
res.count = i;
|
||||
if(res.count < 0 || res.count > MAX_CAPACITY)
|
||||
{
|
||||
throw TuningError( "Key count invalid or too large in KBM file." );
|
||||
}
|
||||
break;
|
||||
case first_midi:
|
||||
res.firstMidi = i;
|
||||
break;
|
||||
case last_midi:
|
||||
res.lastMidi = i;
|
||||
break;
|
||||
case middle:
|
||||
res.middleNote = i;
|
||||
break;
|
||||
case reference:
|
||||
res.tuningConstantNote = i;
|
||||
break;
|
||||
case freq:
|
||||
res.tuningFrequency = v;
|
||||
res.tuningPitch = res.tuningFrequency / 8.17579891564371;
|
||||
break;
|
||||
case degree:
|
||||
res.octaveDegrees = i;
|
||||
break;
|
||||
case keys:
|
||||
res.keys[key_index++] = i;
|
||||
if( key_index == res.count ) state = trailing;
|
||||
break;
|
||||
case trailing:
|
||||
break;
|
||||
}
|
||||
if( ! ( state == keys || state == trailing ) ) state = (parsePosition)(state + 1);
|
||||
if( state == keys && res.count == 0 ) state = trailing;
|
||||
|
||||
}
|
||||
|
||||
if( ! ( state == keys || state == trailing ) )
|
||||
{
|
||||
throw TuningError( "Incomplete KBM file. Ubable to get to keys section of file" );
|
||||
}
|
||||
|
||||
if( key_index != res.count )
|
||||
{
|
||||
throw TuningError( "Different number of keys than mapping file indicates. Count is "
|
||||
+ std::to_string( res.count ) + " and we parsed " + std::to_string( key_index ) + " keys." );
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
KeyboardMapping readKBMFile(std::string fname)
|
||||
{
|
||||
std::ifstream inf;
|
||||
inf.open(fname);
|
||||
if (!inf.is_open())
|
||||
{
|
||||
std::string s = "Unable to open file '" + fname + "'";
|
||||
throw TuningError(s);
|
||||
}
|
||||
|
||||
auto res = readKBMStream(inf);
|
||||
//res.name = std::move(fname);
|
||||
return res;
|
||||
}
|
||||
|
||||
KeyboardMapping parseKBMData(const std::string &d)
|
||||
{
|
||||
std::istringstream iss(d);
|
||||
auto res = readKBMStream(iss);
|
||||
//res.name = "Mapping from Patch";
|
||||
return res;
|
||||
}
|
||||
|
||||
Tuning::Tuning() : Tuning( evenTemperament12NoteScale(), KeyboardMapping() ) { }
|
||||
Tuning::Tuning(const Scale &s ) : Tuning( s, KeyboardMapping() ) {}
|
||||
Tuning::Tuning(const KeyboardMapping &k ) : Tuning( evenTemperament12NoteScale(), k ) {}
|
||||
|
||||
Tuning::Tuning(const Scale& s, const KeyboardMapping &k)
|
||||
{
|
||||
scale = s;
|
||||
keyboardMapping = k;
|
||||
|
||||
if( s.count <= 0 )
|
||||
throw TuningError( "Unable to tune to a scale with no notes. Your scale provided " + std::to_string( s.count ) + " notes." );
|
||||
|
||||
|
||||
double pitches[N];
|
||||
|
||||
int posPitch0 = 256 + k.tuningConstantNote;
|
||||
int posScale0 = 256 + k.middleNote;
|
||||
|
||||
double pitchMod = log2(k.tuningPitch) - 1;
|
||||
|
||||
int scalePositionOfTuningNote = k.tuningConstantNote - k.middleNote;
|
||||
if( k.count > 0 )
|
||||
scalePositionOfTuningNote = k.keys[scalePositionOfTuningNote];
|
||||
|
||||
double tuningCenterPitchOffset;
|
||||
if( scalePositionOfTuningNote == 0 )
|
||||
tuningCenterPitchOffset = 0;
|
||||
else
|
||||
{
|
||||
double tshift = 0;
|
||||
double dt = s.tones[s.count -1].floatValue - 1.0;
|
||||
while( scalePositionOfTuningNote < 0 )
|
||||
{
|
||||
scalePositionOfTuningNote += s.count;
|
||||
tshift += dt;
|
||||
}
|
||||
while( scalePositionOfTuningNote > s.count )
|
||||
{
|
||||
scalePositionOfTuningNote -= s.count;
|
||||
tshift -= dt;
|
||||
}
|
||||
|
||||
if( scalePositionOfTuningNote == 0 )
|
||||
tuningCenterPitchOffset = -tshift;
|
||||
else
|
||||
tuningCenterPitchOffset = s.tones[scalePositionOfTuningNote-1].floatValue - 1.0 - tshift;
|
||||
}
|
||||
|
||||
for (int i=0; i<N; ++i)
|
||||
{
|
||||
// TODO: ScaleCenter and PitchCenter are now two different notes.
|
||||
int distanceFromPitch0 = i - posPitch0;
|
||||
int distanceFromScale0 = i - posScale0;
|
||||
|
||||
if( distanceFromPitch0 == 0 )
|
||||
{
|
||||
pitches[i] = 1;
|
||||
lptable[i] = pitches[i] + pitchMod;
|
||||
ptable[i] = pow( 2.0, lptable[i] );
|
||||
scalepositiontable[i] = scalePositionOfTuningNote % s.count;
|
||||
#if DEBUG_SCALES
|
||||
std::cout << "PITCH: i=" << i << " n=" << i - 256
|
||||
<< " p=" << pitches[i]
|
||||
<< " lp=" << lptable[i]
|
||||
<< " tp=" << ptable[i]
|
||||
<< " fr=" << ptable[i] * 8.175798915
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
We used to have this which assumed 1-12
|
||||
Now we have our note number, our distance from the
|
||||
center note, and the key remapping
|
||||
int rounds = (distanceFromScale0-1) / s.count;
|
||||
int thisRound = (distanceFromScale0-1) % s.count;
|
||||
*/
|
||||
|
||||
int rounds;
|
||||
int thisRound;
|
||||
int disable = false;
|
||||
if( ( k.count == 0 ) )
|
||||
{
|
||||
rounds = (distanceFromScale0-1) / s.count;
|
||||
thisRound = (distanceFromScale0-1) % s.count;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** Now we have this situation. We are at note i so we
|
||||
** are m away from the center note which is distanceFromScale0
|
||||
**
|
||||
** If we mod that by the mapping size we know which note we are on
|
||||
*/
|
||||
int mappingKey = distanceFromScale0 % k.count;
|
||||
if( mappingKey < 0 )
|
||||
mappingKey += k.count;
|
||||
int cm = k.keys[mappingKey];
|
||||
int push = 0;
|
||||
if( cm < 0 )
|
||||
{
|
||||
disable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
push = mappingKey - cm;
|
||||
}
|
||||
rounds = (distanceFromScale0 - push - 1) / s.count;
|
||||
thisRound = (distanceFromScale0 - push - 1) % s.count;
|
||||
#ifdef DEBUG_SCALES
|
||||
if( i > 296 && i < 340 )
|
||||
std::cout << "MAPPING n=" << i - 256 << " pushes ds0=" << distanceFromScale0 << " cmc=" << k.count << " tr=" << thisRound << " r=" << rounds << " mk=" << mappingKey << " cm=" << cm << " push=" << push << " dis=" << disable << " mk-p-1=" << mappingKey - push - 1 << std::endl;
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
if( thisRound < 0 )
|
||||
{
|
||||
thisRound += s.count;
|
||||
rounds -= 1;
|
||||
}
|
||||
|
||||
if( disable )
|
||||
{
|
||||
pitches[i] = 0;
|
||||
scalepositiontable[i] = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pitches[i] = s.tones[thisRound].floatValue + rounds * (s.tones[s.count - 1].floatValue - 1.0) - tuningCenterPitchOffset;
|
||||
scalepositiontable[i] = ( thisRound + 1 ) % s.count;
|
||||
}
|
||||
|
||||
lptable[i] = pitches[i] + pitchMod;
|
||||
ptable[i] = pow( 2.0, pitches[i] + pitchMod );
|
||||
|
||||
#if DEBUG_SCALES
|
||||
if( i > 296 && i < 340 )
|
||||
std::cout << "PITCH: i=" << i << " n=" << i - 256
|
||||
<< " ds0=" << distanceFromScale0
|
||||
<< " dp0=" << distanceFromPitch0
|
||||
<< " r=" << rounds << " t=" << thisRound
|
||||
<< " p=" << pitches[i]
|
||||
<< " t=" << s.tones[thisRound].floatValue << " " << s.tones[thisRound ].cents
|
||||
<< " dis=" << disable
|
||||
<< " tp=" << ptable[i]
|
||||
<< " fr=" << ptable[i] * 8.175798915
|
||||
<< " tcpo=" << tuningCenterPitchOffset
|
||||
|
||||
//<< " l2p=" << log2(otp)
|
||||
//<< " l2p-p=" << log2(otp) - pitches[i] - rounds - 3
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double Tuning::frequencyForMidiNote( int mn ) const {
|
||||
auto mni = std::min( std::max( 0, mn + 256 ), N-1 );
|
||||
return ptable[ mni ] * MIDI_0_FREQ;
|
||||
}
|
||||
|
||||
double Tuning::frequencyForMidiNoteScaledByMidi0( int mn ) const {
|
||||
auto mni = std::min( std::max( 0, mn + 256 ), N-1 );
|
||||
return ptable[ mni ];
|
||||
}
|
||||
|
||||
double Tuning::logScaledFrequencyForMidiNote( int mn ) const {
|
||||
auto mni = std::min( std::max( 0, mn + 256 ), N-1 );
|
||||
return lptable[ mni ];
|
||||
}
|
||||
|
||||
int Tuning::scalePositionForMidiNote( int mn ) const {
|
||||
auto mni = std::min( std::max( 0, mn + 256 ), N-1 );
|
||||
return scalepositiontable[ mni ];
|
||||
}
|
||||
|
||||
KeyboardMapping tuneA69To(double freq)
|
||||
{
|
||||
return tuneNoteTo( 69, freq );
|
||||
}
|
||||
|
||||
KeyboardMapping tuneNoteTo( int midiNote, double freq )
|
||||
{
|
||||
return startScaleOnAndTuneNoteTo( 60, midiNote, freq );
|
||||
}
|
||||
|
||||
KeyboardMapping startScaleOnAndTuneNoteTo( int scaleStart, int midiNote, double freq )
|
||||
{
|
||||
KeyboardMapping res;
|
||||
res.count = 0;
|
||||
res.firstMidi = 0;
|
||||
res.lastMidi = 127;
|
||||
res.middleNote = scaleStart;
|
||||
res.tuningConstantNote = midiNote;
|
||||
res.tuningFrequency = freq;
|
||||
res.tuningPitch = freq / MIDI_0_FREQ;
|
||||
res.octaveDegrees = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
445
src/sfizz.h
445
src/sfizz.h
|
|
@ -27,27 +27,39 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Synth handle
|
||||
*/
|
||||
typedef struct sfizz_synth_t sfizz_synth_t;
|
||||
/**
|
||||
* @brief Oversampling factor
|
||||
*/
|
||||
typedef enum {
|
||||
SFIZZ_OVERSAMPLING_X1 = 1,
|
||||
SFIZZ_OVERSAMPLING_X2 = 2,
|
||||
SFIZZ_OVERSAMPLING_X4 = 4,
|
||||
SFIZZ_OVERSAMPLING_X8 = 8
|
||||
} sfizz_oversampling_factor_t;
|
||||
/**
|
||||
* @brief Processing mode
|
||||
*/
|
||||
typedef enum {
|
||||
SFIZZ_PROCESS_LIVE,
|
||||
SFIZZ_PROCESS_FREEWHEELING,
|
||||
} sfizz_process_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Creates a sfizz synth. This object has to be freed by the caller
|
||||
* using sfizz_free(). The synth by default is set at 48 kHz
|
||||
* and a maximum block size of 1024. You should change these values
|
||||
* if they are not correct for your application.
|
||||
*
|
||||
* @return sfizz_synth_t*
|
||||
*/
|
||||
SFIZZ_EXPORTED_API sfizz_synth_t* sfizz_create_synth();
|
||||
|
||||
/**
|
||||
* @brief Frees an existing sfizz synth.
|
||||
*
|
||||
* @param synth The synth to destroy
|
||||
* @param synth The synth to destroy.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_free(sfizz_synth_t* synth);
|
||||
|
||||
|
|
@ -60,88 +72,160 @@ SFIZZ_EXPORTED_API void sfizz_free(sfizz_synth_t* synth);
|
|||
* @param path A null-terminated string representing a path to an SFZ
|
||||
* file.
|
||||
*
|
||||
* @return true when file loading went OK.
|
||||
* @return false if some error occured while loading.
|
||||
* @return @true when file loading went OK,
|
||||
* @false if some error occured while loading.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API bool sfizz_load_file(sfizz_synth_t* synth, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of regions in the currently loaded SFZ file.
|
||||
* @brief Loads an SFZ file from textual data. This accepts a virtual
|
||||
* path name for the imaginary sfz file, which is not required to
|
||||
* exist on disk. The purpose of the virtual path is to locate
|
||||
* samples with relative paths.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param synth The sfizz synth.
|
||||
* @param path The virtual path of the SFZ file.
|
||||
* @param text The contents of the virtual SFZ file.
|
||||
*
|
||||
* @return int the number of regions
|
||||
* @return @true when file loading went OK,
|
||||
* @false if some error occured while loading.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API bool sfizz_load_string(sfizz_synth_t* synth, const char* path, const char* text);
|
||||
|
||||
/**
|
||||
* @brief Sets the tuning from a Scala file loaded from the file system.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The sfizz synth.
|
||||
* @param path The path to the file in Scala format.
|
||||
* @return @true when tuning scale loaded OK,
|
||||
* @false if some error occurred.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API bool sfizz_load_scala_file(sfizz_synth_t* synth, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Sets the tuning from a Scala file loaded from memory.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The sfizz synth.
|
||||
* @param text The contents of the file in Scala format.
|
||||
* @return @true when tuning scale loaded OK,
|
||||
* @false if some error occurred.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API bool sfizz_load_scala_string(sfizz_synth_t* synth, const char* text);
|
||||
|
||||
/**
|
||||
* @brief Sets the scala root key.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The sfizz synth.
|
||||
* @param root_key The MIDI number of the Scala root key (default 60 for C4).
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_scala_root_key(sfizz_synth_t* synth, int root_key);
|
||||
|
||||
/**
|
||||
* @brief Gets the scala root key.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The sfizz synth.
|
||||
* @return The MIDI number of the Scala root key (default 60 for C4).
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_scala_root_key(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Sets the reference tuning frequency.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The sfizz synth.
|
||||
* @param frequency The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_tuning_frequency(sfizz_synth_t* synth, float frequency);
|
||||
|
||||
/**
|
||||
* @brief Gets the reference tuning frequency.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The sfizz synth.
|
||||
* @return The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||
*/
|
||||
SFIZZ_EXPORTED_API float sfizz_get_tuning_frequency(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Configure stretch tuning using a predefined parametric Railsback curve.
|
||||
* A ratio 1/2 is supposed to match the average piano; 0 disables (the default).
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The sfizz synth.
|
||||
* @param ratio The parameter in domain 0-1.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_load_stretch_tuning_by_ratio(sfizz_synth_t* synth, float ratio);
|
||||
|
||||
/**
|
||||
* @brief Return the number of regions in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_num_regions(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Returns the number of groups in the currently loaded SFZ file.
|
||||
* @brief Return the number of groups in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of groups
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_num_groups(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Returns the number of masters in the currently loaded SFZ file.
|
||||
* @brief Return the number of masters in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of masters
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_num_masters(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Returns the number of curves in the currently loaded SFZ file.
|
||||
* @brief Return the number of curves in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of curves
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_num_curves(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Export a MIDI Name document describing the the currently loaded
|
||||
* SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param model the model name used if a non-empty string, otherwise generated
|
||||
* @param synth The synth.
|
||||
* @param model The model name used if a non-empty string, otherwise generated.
|
||||
*
|
||||
* @return char* a newly allocated XML string, which must be freed after use
|
||||
* @return A newly allocated XML string, which must be freed after use.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API char* sfizz_export_midnam(sfizz_synth_t* synth, const char* model);
|
||||
/**
|
||||
* @brief Returns the number of preloaded samples for the current SFZ file.
|
||||
* @brief Return the number of preloaded samples for the current SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of preloaded samples
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API size_t sfizz_get_num_preloaded_samples(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Returns the number of active voices. Note that this function is a
|
||||
* @brief Return the number of active voices. Note that this function is a
|
||||
* basic indicator and does not aim to be perfect. In particular, it
|
||||
* runs on the calling thread so voices may well start or stop while
|
||||
* the function is checking which voice is active.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return size_t the number of playing voices
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_num_active_voices(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Sets the expected number of samples per block. If unsure, give an
|
||||
* @brief Set the expected number of samples per block. If unsure, give an
|
||||
* upper bound since right now ugly things may happen if you go over
|
||||
* this number.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param samples_per_block the number of samples per block
|
||||
* @param synth The synth.
|
||||
* @param samples_per_block The number of samples per block.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_samples_per_block(sfizz_synth_t* synth, int samples_per_block);
|
||||
/**
|
||||
* @brief Sets the sample rate for the synth. This is the output sample
|
||||
* @brief Set the sample rate for the synth. This is the output sample
|
||||
* rate. This setting does not affect the internal processing.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param sample_rate the sample rate
|
||||
* @param sample_rate The sample rate.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_sample_rate(sfizz_synth_t* synth, float sample_rate);
|
||||
|
||||
|
|
@ -150,10 +234,10 @@ SFIZZ_EXPORTED_API void sfizz_set_sample_rate(sfizz_synth_t* synth, float sample
|
|||
* needs to happen before the call to sfizz_render_block in each
|
||||
* block and should appear in order of the delays.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
* @param synth The synth.
|
||||
* @param delay The delay of the event in the block, in samples.
|
||||
* @param note_number The MIDI note number.
|
||||
* @param velocity The MIDI velocity.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int note_number, char velocity);
|
||||
|
||||
|
|
@ -164,10 +248,10 @@ SFIZZ_EXPORTED_API void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int
|
|||
* As per the SFZ spec the velocity of note-off events is usually replaced by
|
||||
* the note-on velocity.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
* @param synth The synth.
|
||||
* @param delay The delay of the event in the block, in samples.
|
||||
* @param note_number The MIDI note number.
|
||||
* @param velocity The MIDI velocity.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int note_number, char velocity);
|
||||
|
||||
|
|
@ -176,38 +260,53 @@ SFIZZ_EXPORTED_API void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int
|
|||
* to happen before the call to sfizz_render_block in each block and
|
||||
* should appear in order of the delays.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param cc_number the MIDI CC number
|
||||
* @param cc_value the MIDI CC value
|
||||
* @param synth The synth.
|
||||
* @param delay The delay of the event in the block, in samples.
|
||||
* @param cc_number The MIDI CC number.
|
||||
* @param cc_value The MIDI CC value.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_send_cc(sfizz_synth_t* synth, int delay, int cc_number, char cc_value);
|
||||
|
||||
/**
|
||||
* @brief Send a high precision CC event to the synth. As with all MIDI
|
||||
* events, this needs to happen before the call to
|
||||
* sfizz_render_block in each block and should appear in order of
|
||||
* the delays.
|
||||
*
|
||||
* @param synth The synth.
|
||||
* @param delay The delay of the event in the block, in samples.
|
||||
* @param cc_number The MIDI CC number.
|
||||
* @param norm_value The normalized CC value, in domain 0 to 1.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_send_hdcc(sfizz_synth_t* synth, int delay, int cc_number, float norm_value);
|
||||
|
||||
/**
|
||||
* @brief Send a pitch wheel event. As with all MIDI events, this needs
|
||||
* to happen before the call to sfizz_render_block in each block and
|
||||
* should appear in order of the delays.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay The delay
|
||||
* @param pitch The pitch
|
||||
* @param synth The synth.
|
||||
* @param delay The delay.
|
||||
* @param pitch The pitch.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay, int pitch);
|
||||
|
||||
/**
|
||||
* @brief Send an aftertouch event. (CURRENTLY UNIMPLEMENTED)
|
||||
* @brief Send an aftertouch event. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param synth
|
||||
* @param delay
|
||||
* @param aftertouch
|
||||
* @param synth
|
||||
* @param delay
|
||||
* @param aftertouch
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_send_aftertouch(sfizz_synth_t* synth, int delay, char aftertouch);
|
||||
|
||||
/**
|
||||
* @brief Send a tempo event. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay The delay
|
||||
* @param seconds_per_quarter The seconds per quarter
|
||||
* @param synth The synth.
|
||||
* @param delay The delay.
|
||||
* @param seconds_per_quarter The seconds per quarter.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_send_tempo(sfizz_synth_t* synth, int delay, float seconds_per_quarter);
|
||||
|
||||
|
|
@ -219,11 +318,11 @@ SFIZZ_EXPORTED_API void sfizz_send_tempo(sfizz_synth_t* synth, int delay, float
|
|||
* block. The synth will memorize the inputs and render sample
|
||||
* accurates envelopes depending on the input events passed to it.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param channels pointers to the left and right channel of the
|
||||
* output
|
||||
* @param num_channels should be equal to 2 for the time being.
|
||||
* @param num_frames number of frames to fill. This should be less than
|
||||
* @param synth The synth.
|
||||
* @param channels Pointers to the left and right channel of the
|
||||
* output.
|
||||
* @param num_channels Should be equal to 2 for the time being.
|
||||
* @param num_frames Number of frames to fill. This should be less than
|
||||
* or equal to the expected samples_per_block.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_render_block(sfizz_synth_t* synth, float** channels, int num_channels, int num_frames);
|
||||
|
|
@ -232,18 +331,19 @@ SFIZZ_EXPORTED_API void sfizz_render_block(sfizz_synth_t* synth, float** channel
|
|||
* @brief Get the size of the preloaded data. This returns the number of
|
||||
* floats used in the preloading buffers.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return the preloaded data size in sizeof(floats)
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API unsigned int sfizz_get_preload_size(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Sets the size of the preloaded data in number of floats (not
|
||||
* @brief Set the size of the preloaded data in number of floats (not
|
||||
* bytes). This will disable the callbacks for the duration of the
|
||||
* load.
|
||||
* load. This function takes a lock ; prefer calling
|
||||
* it out of the RT thread. It can also take a long time to return.
|
||||
* If the new preload size is the same as the current one, it will
|
||||
* release the lock immediately and exit.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param[in] preload_size The preload size
|
||||
* @param synth The synth.
|
||||
* @param[in] preload_size The preload size.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_preload_size(sfizz_synth_t* synth, unsigned int preload_size);
|
||||
|
||||
|
|
@ -252,9 +352,7 @@ SFIZZ_EXPORTED_API void sfizz_set_preload_size(sfizz_synth_t* synth, unsigned in
|
|||
* the engine, not the output or expected rate of the calling
|
||||
* function. For the latter use the `get_sample_rate()` functions.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return The internal sample rate of the engine
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfizz_synth_t* synth);
|
||||
/**
|
||||
|
|
@ -272,51 +370,84 @@ SFIZZ_EXPORTED_API sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfi
|
|||
* to compensate for the memory increase, but the full loading will
|
||||
* need to take place anyway.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param[in] preload_size The preload size
|
||||
* This function takes a lock and disables the callback; prefer calling
|
||||
* it out of the RT thread. It can also take a long time to return.
|
||||
* If the new oversampling factor is the same as the current one, it will
|
||||
* release the lock immediately and exit.
|
||||
*
|
||||
* @return True if the oversampling factor was correct
|
||||
* @param synth The synth.
|
||||
* @param[in] oversampling The oversampling factor.
|
||||
*
|
||||
* @return @true if the oversampling factor was correct, @false otherwise.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API bool sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfizz_oversampling_factor_t oversampling);
|
||||
|
||||
/**
|
||||
* @brief Get the default resampling quality. This is the quality setting
|
||||
* which the engine uses when the instrument does not use the
|
||||
* opcode `sample_quality`. The engine uses distinct default quality
|
||||
* settings for live mode and freewheeling mode, which both can be
|
||||
* accessed by the means of this function.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The synth.
|
||||
* @param[in] mode The processing mode.
|
||||
*
|
||||
* @return The sample quality for the given mode, in the range 1 to 10.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_sample_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Set the default resampling quality. This is the quality setting
|
||||
* which the engine uses when the instrument does not use the
|
||||
* opcode `sample_quality`. The engine uses distinct default quality
|
||||
* settings for live mode and freewheeling mode, which both can be
|
||||
* accessed by the means of this function.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth The synth.
|
||||
* @param[in] mode The processing mode.
|
||||
* @param[in] quality The desired sample quality, in the range 1 to 10.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_sample_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode, int quality);
|
||||
|
||||
/**
|
||||
* @brief Set the global instrument volume.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param volume the new volume
|
||||
* @param synth The synth.
|
||||
* @param volume The new volume.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_volume(sfizz_synth_t* synth, float volume);
|
||||
|
||||
/**
|
||||
* @brief Get the global instrument volume.
|
||||
* @brief Return the global instrument volume.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return float the instrument volume
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API float sfizz_get_volume(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Sets the number of voices used by the synth
|
||||
* @brief Set the number of voices used by the synth.
|
||||
* This function takes a lock and disables the callback; prefer calling
|
||||
* it out of the RT thread. It can also take a long time to return.
|
||||
* If the new number of voices is the same as the current one, it will
|
||||
* release the lock immediately and exit.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param num_voices The number voices
|
||||
* @param synth The synth.
|
||||
* @param num_voices The number of voices.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_num_voices(sfizz_synth_t* synth, int num_voices);
|
||||
/**
|
||||
* @brief Returns the number of voices
|
||||
* @brief Return the number of voices.
|
||||
*
|
||||
* @param synth
|
||||
* @return num_voices
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_num_voices(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Get the number of allocated buffers from the synth.
|
||||
* @brief Return the number of allocated buffers from the synth.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return The number of buffers held by the synth
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_num_buffers(sfizz_synth_t* synth);
|
||||
/**
|
||||
|
|
@ -324,75 +455,149 @@ SFIZZ_EXPORTED_API int sfizz_get_num_buffers(sfizz_synth_t* synth);
|
|||
* value can be less than the actual memory usage since it only
|
||||
* counts the buffer objects managed by sfizz.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return The number of bytes held by the synth in buffers;
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_num_bytes(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Enables freewheeling on the synth.
|
||||
* @brief Enable freewheeling on the synth.
|
||||
*
|
||||
* @param synth
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_enable_freewheeling(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Disables freewheeling on the synth.
|
||||
* @brief Disable freewheeling on the synth.
|
||||
*
|
||||
* @param synth
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_disable_freewheeling(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Get a comma separated list of unknown opcodes. The caller has to free()
|
||||
* the string returned. This function allocates memory, do not call on the
|
||||
* audio thread.
|
||||
* @brief Return a comma separated list of unknown opcodes.
|
||||
* The caller has to free() the string returned.
|
||||
* This function allocates memory, do not call on the audio thread.
|
||||
*
|
||||
* @param synth
|
||||
* @return char*
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API char* sfizz_get_unknown_opcodes(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Check if the SFZ should be reloaded.
|
||||
* @brief Check if the SFZ should be reloaded.
|
||||
* Depending on the platform this can create file descriptors.
|
||||
*
|
||||
* Depending on the platform this can create file descriptors.
|
||||
* @param synth The synth.
|
||||
*
|
||||
* @param synth
|
||||
* @return true if any included files (including the root file) have
|
||||
* been modified since the sfz file was loaded.
|
||||
* @return false
|
||||
* @return @true if any included files (including the root file) have
|
||||
* been modified since the sfz file was loaded, @false otherwise.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API bool sfizz_should_reload_file(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Enable logging of timings to sidecar CSV files. This can produce
|
||||
* many outputs so use with caution.
|
||||
* @brief Check if the scala file should be reloaded.
|
||||
* Depending on the platform this can create file descriptors.
|
||||
*
|
||||
* @param synth
|
||||
* @param synth The synth.
|
||||
*
|
||||
* @return @true if the scala file has been modified since loading.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API bool sfizz_should_reload_scala(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Enable logging of timings to sidecar CSV files. This can produce
|
||||
* many outputs so use with caution.
|
||||
*
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_enable_logging(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Disable logging
|
||||
* @brief Disable logging.
|
||||
*
|
||||
* @param synth
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_disable_logging(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Enable logging of timings to sidecar CSV files. This can produce
|
||||
* many outputs so use with caution.
|
||||
* @brief Enable logging of timings to sidecar CSV files. This can produce
|
||||
* many outputs so use with caution.
|
||||
*
|
||||
* @param synth
|
||||
* @param synth The synth.
|
||||
* @param prefix The prefix.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_logging_prefix(sfizz_synth_t* synth, const char* prefix);
|
||||
|
||||
/**
|
||||
* @brief Shuts down the current processing, clear buffers and reset the voices.
|
||||
* @brief Shuts down the current processing, clear buffers and reset the voices.
|
||||
*
|
||||
* @param synth The synth.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_all_sound_off(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Add external definitions prior to loading;
|
||||
* Note that these do not get reset by loading or resetting the synth.
|
||||
* You need to call sfizz_clear_external_definitions() to erase them.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth
|
||||
* @param id
|
||||
* @param value
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_add_external_definitions(sfizz_synth_t* synth, const char* id, const char* value);
|
||||
|
||||
/**
|
||||
* @brief Clears external definitions for the next file loading.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param synth
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_all_sound_off(sfizz_synth_t* synth);
|
||||
SFIZZ_EXPORTED_API void sfizz_clear_external_definitions(sfizz_synth_t* synth);
|
||||
|
||||
#define SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX -1
|
||||
|
||||
/**
|
||||
* @brief Get the number of key labels registered in the current sfz file
|
||||
* @since 0.4.0
|
||||
*/
|
||||
SFIZZ_EXPORTED_API unsigned int sfizz_get_num_key_labels(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Get the key number for the label registered at index label_index.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @returns the number or SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_key_label_number(sfizz_synth_t* synth, int label_index);
|
||||
|
||||
/**
|
||||
* @brief Get the key text for the label registered at index label_index.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @returns the label or NULL if the index is out of bounds.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API const char * sfizz_get_key_label_text(sfizz_synth_t* synth, int label_index);
|
||||
|
||||
/**
|
||||
* @brief Get the number of CC labels registered in the current sfz file
|
||||
* @since 0.4.0
|
||||
*
|
||||
*/
|
||||
SFIZZ_EXPORTED_API unsigned int sfizz_get_num_cc_labels(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Get the CC number for the label registered at index label_index.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @returns the number or SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds.
|
||||
*/
|
||||
|
||||
SFIZZ_EXPORTED_API int sfizz_get_cc_label_number(sfizz_synth_t* synth, int label_index);
|
||||
/**
|
||||
* @brief Get the CC text for the label registered at index label_index.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @returns the label or NULL if the index is out of bounds.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API const char * sfizz_get_cc_label_text(sfizz_synth_t* synth, int label_index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
352
src/sfizz.hpp
352
src/sfizz.hpp
|
|
@ -4,7 +4,14 @@
|
|||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
/**
|
||||
@file
|
||||
@brief sfizz public C++ API
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
|
|
@ -21,6 +28,9 @@
|
|||
namespace sfz
|
||||
{
|
||||
class Synth;
|
||||
/**
|
||||
* @brief Main class
|
||||
*/
|
||||
class SFIZZ_EXPORTED_API Sfizz
|
||||
{
|
||||
public:
|
||||
|
|
@ -32,6 +42,15 @@ public:
|
|||
*/
|
||||
Sfizz();
|
||||
~Sfizz();
|
||||
|
||||
/**
|
||||
* @brief Processing mode
|
||||
*/
|
||||
enum ProcessMode {
|
||||
ProcessLive,
|
||||
ProcessFreewheeling,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Empties the current regions and load a new SFZ file into the synth.
|
||||
*
|
||||
|
|
@ -39,68 +58,175 @@ public:
|
|||
* UI thread for example, although it may generate a click. However it is
|
||||
* not reentrant, so you should not call it from concurrent threads.
|
||||
*
|
||||
* @param file
|
||||
* @return true
|
||||
* @return false if the file was not found or no regions were loaded.
|
||||
* @param path The path to the file to load, as string.
|
||||
*
|
||||
* @return @false if the file was not found or no regions were loaded,
|
||||
* @true otherwise.
|
||||
*/
|
||||
bool loadSfzFile(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief Get the current number of regions loaded
|
||||
* @brief Empties the current regions and load a new SFZ document from memory.
|
||||
*
|
||||
* @return int
|
||||
* This is similar to loadSfzFile() in functionality.
|
||||
* This accepts a virtual path name for the imaginary sfz file, which is not
|
||||
* required to exist on disk. The purpose of the virtual path is to locate
|
||||
* samples with relative paths.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param path The virtual path of the SFZ file, as string.
|
||||
* @param text The contents of the virtual SFZ file.
|
||||
*
|
||||
* @return @false if no regions were loaded,
|
||||
* @true otherwise.
|
||||
*/
|
||||
bool loadSfzString(const std::string& path, const std::string& text);
|
||||
|
||||
/**
|
||||
* @brief Sets the tuning from a Scala file loaded from the file system.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param path The path to the file in Scala format.
|
||||
* @return @true when tuning scale loaded OK,
|
||||
* @false if some error occurred.
|
||||
*/
|
||||
bool loadScalaFile(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief Sets the tuning from a Scala file loaded from memory.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param text The contents of the file in Scala format.
|
||||
* @return @true when tuning scale loaded OK,
|
||||
* @false if some error occurred.
|
||||
*/
|
||||
bool loadScalaString(const std::string& text);
|
||||
|
||||
/**
|
||||
* @brief Sets the scala root key.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param rootKey The MIDI number of the Scala root key (default 60 for C4).
|
||||
*/
|
||||
void setScalaRootKey(int rootKey);
|
||||
|
||||
/**
|
||||
* @brief Gets the scala root key.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @return The MIDI number of the Scala root key (default 60 for C4).
|
||||
*/
|
||||
int getScalaRootKey() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the reference tuning frequency.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param frequency The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||
*/
|
||||
void setTuningFrequency(float frequency);
|
||||
|
||||
/**
|
||||
* @brief Gets the reference tuning frequency.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @return The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||
*/
|
||||
float getTuningFrequency() const;
|
||||
|
||||
/**
|
||||
* @brief Configure stretch tuning using a predefined parametric Railsback curve.
|
||||
* A ratio 1/2 is supposed to match the average piano; 0 disables (the default).
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param ratio The parameter in domain 0-1.
|
||||
*/
|
||||
void loadStretchTuningByRatio(float ratio);
|
||||
|
||||
/**
|
||||
* @brief Return the current number of regions loaded.
|
||||
*/
|
||||
int getNumRegions() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the current number of groups loaded
|
||||
*
|
||||
* @return int
|
||||
* @brief Return the current number of groups loaded.
|
||||
*/
|
||||
int getNumGroups() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the current number of masters loaded
|
||||
*
|
||||
* @return int
|
||||
* @brief Return the current number of masters loaded.
|
||||
*/
|
||||
int getNumMasters() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the current number of curves loaded
|
||||
*
|
||||
* @return int
|
||||
* @brief Return the current number of curves loaded.
|
||||
*/
|
||||
int getNumCurves() const noexcept;
|
||||
const std::vector<std::string>& getUnknownOpcodes() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the number of preloaded samples in the synth
|
||||
*
|
||||
* @return size_t
|
||||
* @brief Return a list of unsupported opcodes, if any.
|
||||
*/
|
||||
const std::vector<std::string>& getUnknownOpcodes() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Return the number of preloaded samples in the synth.
|
||||
*/
|
||||
size_t getNumPreloadedSamples() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Set the maximum size of the blocks for the callback. The actual
|
||||
* size can be lower in each callback but should not be larger
|
||||
* than this value.
|
||||
*
|
||||
* @param samplesPerBlock
|
||||
* @param samplesPerBlock The number of samples per block.
|
||||
*/
|
||||
void setSamplesPerBlock(int samplesPerBlock) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Set the sample rate. If you do not call it it is initialized
|
||||
* to sfz::config::defaultSampleRate.
|
||||
*
|
||||
* @param sampleRate
|
||||
* @param sampleRate The sample rate.
|
||||
*/
|
||||
void setSampleRate(float sampleRate) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the current value for the volume, in dB.
|
||||
* @brief Get the default resampling quality. This is the quality setting
|
||||
* which the engine uses when the instrument does not use the
|
||||
* opcode `sample_quality`. The engine uses distinct default quality
|
||||
* settings for live mode and freewheeling mode, which both can be
|
||||
* accessed by the means of this function.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @return float
|
||||
* @param[in] mode The processing mode.
|
||||
*
|
||||
* @return The sample quality for the given mode, in the range 1 to 10.
|
||||
*/
|
||||
int getSampleQuality(ProcessMode mode);
|
||||
|
||||
/**
|
||||
* @brief Set the default resampling quality. This is the quality setting
|
||||
* which the engine uses when the instrument does not use the
|
||||
* opcode `sample_quality`. The engine uses distinct default quality
|
||||
* settings for live mode and freewheeling mode, which both can be
|
||||
* accessed by the means of this function.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param[in] mode The processing mode.
|
||||
* @param[in] quality The desired sample quality, in the range 1 to 10.
|
||||
*/
|
||||
void setSampleQuality(ProcessMode mode, int quality);
|
||||
|
||||
/**
|
||||
* @brief Return the current value for the volume, in dB.
|
||||
*/
|
||||
float getVolume() const noexcept;
|
||||
/**
|
||||
|
||||
/**
|
||||
* @brief Set the value for the volume. This value will be
|
||||
* clamped within sfz::default::volumeRange.
|
||||
*
|
||||
* @param volume
|
||||
* @param volume The new volume.
|
||||
*/
|
||||
void setVolume(float volume) noexcept;
|
||||
|
||||
|
|
@ -109,125 +235,156 @@ public:
|
|||
*
|
||||
* @param delay the delay at which the event occurs; this should be lower
|
||||
* than the size of the block in the next call to renderBlock().
|
||||
* @param noteNumber the midi note number
|
||||
* @param velocity the midi note velocity
|
||||
* @param noteNumber the midi note number.
|
||||
* @param velocity the midi note velocity.
|
||||
*/
|
||||
void noteOn(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Send a note off event to the synth
|
||||
*
|
||||
* @param delay the delay at which the event occurs; this should be lower
|
||||
* than the size of the block in the next call to renderBlock().
|
||||
* @param noteNumber the midi note number
|
||||
* @param velocity the midi note velocity
|
||||
* @param noteNumber the midi note number.
|
||||
* @param velocity the midi note velocity.
|
||||
*/
|
||||
void noteOff(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Send a CC event to the synth
|
||||
*
|
||||
* @param delay the delay at which the event occurs; this should be lower than the size of
|
||||
* the block in the next call to renderBlock().
|
||||
* @param ccNumber the cc number
|
||||
* @param ccValue the cc value
|
||||
* @param ccNumber the cc number.
|
||||
* @param ccValue the cc value.
|
||||
*/
|
||||
void cc(int delay, int ccNumber, uint8_t ccValue) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Send a high precision CC event to the synth
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param delay the delay at which the event occurs; this should be lower than the size of
|
||||
* the block in the next call to renderBlock().
|
||||
* @param ccNumber the cc number.
|
||||
* @param normValue the normalized cc value, in domain 0 to 1.
|
||||
*/
|
||||
void hdcc(int delay, int ccNumber, float normValue) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Send a pitch bend event to the synth
|
||||
*
|
||||
* @param delay the delay at which the event occurs; this should be lower
|
||||
* than the size of the block in the next call to
|
||||
* renderBlock().
|
||||
* @param pitch the pitch value centered between -8192 and 8192
|
||||
* @param pitch the pitch value centered between -8192 and 8192.
|
||||
*/
|
||||
void pitchWheel(int delay, int pitch) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Send a aftertouch event to the synth
|
||||
* @brief Send a aftertouch event to the synth. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param delay the delay at which the event occurs; this should be lower than the size of
|
||||
* the block in the next call to renderBlock().
|
||||
* @param aftertouch the aftertouch value
|
||||
* @param aftertouch the aftertouch value.
|
||||
*/
|
||||
void aftertouch(int delay, uint8_t aftertouch) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Send a tempo event to the synth
|
||||
* @brief Send a tempo event to the synth. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param delay the delay at which the event occurs; this should be lower than the size of
|
||||
* the block in the next call to renderBlock().
|
||||
* @param secondsPerQuarter the new period of the quarter note
|
||||
* @param secondsPerQuarter the new period of the quarter note.
|
||||
*/
|
||||
void tempo(int delay, float secondsPerQuarter) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Render an block of audio data in the buffer. This call will reset
|
||||
* the synth in its waiting state for the next batch of events. The buffers must
|
||||
* be float[numSamples][numOutputs * 2].
|
||||
*
|
||||
* @param buffers the buffers to write the next block into
|
||||
* @param numSamples the number of stereo frames in the block
|
||||
* @param numOutputs the number of stereo outputs
|
||||
* @param buffers the buffers to write the next block into.
|
||||
* @param numFrames the number of stereo frames in the block.
|
||||
* @param numOutputs the number of stereo outputs.
|
||||
*/
|
||||
void renderBlock(float** buffers, size_t numFrames, int numOutputs = 1) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the number of active voices
|
||||
*
|
||||
* @return int
|
||||
* @brief Return the number of active voices.
|
||||
*/
|
||||
int getNumActiveVoices() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the total number of voices in the synth (the polyphony)
|
||||
*
|
||||
* @return int
|
||||
* @brief Return the total number of voices in the synth (the polyphony).
|
||||
*/
|
||||
int getNumVoices() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Change the number of voices (the polyphony)
|
||||
* @brief Change the number of voices (the polyphony).
|
||||
* This function takes a lock and disables the callback; prefer calling
|
||||
* it out of the RT thread. It can also take a long time to return.
|
||||
* If the new number of voices is the same as the current one, it will
|
||||
* release the lock immediately and exit.
|
||||
*
|
||||
* @param numVoices
|
||||
* @param numVoices The number of voices.
|
||||
*/
|
||||
void setNumVoices(int numVoices) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Set the oversampling factor to a new value. This will disable all callbacks
|
||||
* kill all the voices, and trigger a reloading of every file in the FilePool under
|
||||
* the new oversampling.
|
||||
/**
|
||||
* @brief Set the oversampling factor to a new value.
|
||||
* It will kill all the voices, and trigger a reloading of every file in
|
||||
* the FilePool under the new oversampling.
|
||||
*
|
||||
* @param factor
|
||||
* @return true if the factor did indeed change
|
||||
* Increasing this value (up to x8 oversampling) improves the
|
||||
* quality of the output at the expense of memory consumption and
|
||||
* background loading speed. The main render path still uses the
|
||||
* same linear interpolation algorithm and should not see its
|
||||
* performance decrease, but the files are oversampled upon loading
|
||||
* which increases the stress on the background loader and reduce
|
||||
* the loading speed. You can tweak the size of the preloaded data
|
||||
* to compensate for the memory increase, but the full loading will
|
||||
* need to take place anyway.
|
||||
*
|
||||
* This function takes a lock and disables the callback; prefer calling
|
||||
* it out of the RT thread. It can also take a long time to return.
|
||||
* If the new oversampling factor is the same as the current one, it will
|
||||
* release the lock immediately and exit.
|
||||
*
|
||||
* @param factor The oversampling factor.
|
||||
*
|
||||
* @return @true if the factor did indeed change, @false otherwise.
|
||||
*/
|
||||
bool setOversamplingFactor(int factor) noexcept;
|
||||
|
||||
/**
|
||||
* @brief get the current oversampling factor
|
||||
*
|
||||
* @return Oversampling
|
||||
* @brief Return the current oversampling factor.
|
||||
*/
|
||||
int getOversamplingFactor() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Set the preloaded file size. This will disable the callback.
|
||||
* @brief Set the preloaded file size.
|
||||
* This function takes a lock and disables the callback; prefer calling
|
||||
* it out of the RT thread. It can also take a long time to return.
|
||||
* If the new preload size is the same as the current one, it will
|
||||
* release the lock immediately and exit.
|
||||
*
|
||||
* @param factor
|
||||
* @param preloadSize The preload size.
|
||||
*/
|
||||
void setPreloadSize(uint32_t preloadSize) noexcept;
|
||||
|
||||
/**
|
||||
* @brief get the current preloaded file size
|
||||
*
|
||||
* @return Oversampling
|
||||
* @brief Return the current preloaded file size.
|
||||
*/
|
||||
uint32_t getPreloadSize() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets the number of allocated buffers.
|
||||
*
|
||||
* @return The allocated buffers.
|
||||
* @brief Return the number of allocated buffers.
|
||||
*/
|
||||
int getAllocatedBuffers() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets the number of bytes allocated through the buffers
|
||||
*
|
||||
* @return The allocated bytes.
|
||||
* @brief Return the number of bytes allocated through the buffers.
|
||||
*/
|
||||
int getAllocatedBytes() const noexcept;
|
||||
|
||||
|
|
@ -235,55 +392,100 @@ public:
|
|||
* @brief Enable freewheeling on the synth. This will wait for background
|
||||
* loaded files to finish loading before each render callback to ensure that
|
||||
* there will be no dropouts.
|
||||
*
|
||||
*/
|
||||
void enableFreeWheeling() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Disable freewheeling on the synth. You should disable freewheeling
|
||||
* before live use of the plugin otherwise the audio thread will lock.
|
||||
*
|
||||
*/
|
||||
void disableFreeWheeling() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Check if the SFZ should be reloaded.
|
||||
*
|
||||
* Depending on the platform this can create file descriptors.
|
||||
*
|
||||
* @return true if any included files (including the root file) have
|
||||
* been modified since the sfz file was loaded.
|
||||
* @return false
|
||||
* @return @true if any included files (including the root file) have
|
||||
* been modified since the sfz file was loaded, @false otherwise.
|
||||
*/
|
||||
bool shouldReloadFile();
|
||||
|
||||
/**
|
||||
* @brief Check if the tuning (scala) file should be reloaded.
|
||||
*
|
||||
* Depending on the platform this can create file descriptors.
|
||||
*
|
||||
* @return true if a scala file has been loaded and has changed
|
||||
* @return false
|
||||
*/
|
||||
bool shouldReloadScala();
|
||||
|
||||
/**
|
||||
* @brief Enable logging of timings to sidecar CSV files. This can produce
|
||||
* many outputs so use with caution.
|
||||
*
|
||||
* @param prefix the file prefix to use for logging
|
||||
* @param prefix the file prefix to use for logging.
|
||||
*/
|
||||
void enableLogging() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Enable logging of timings to sidecar CSV files. This can produce
|
||||
* many outputs so use with caution.
|
||||
*
|
||||
* @param prefix the file prefix to use for logging
|
||||
* @param prefix the file prefix to use for logging.
|
||||
*/
|
||||
void enableLogging(const std::string& prefix) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Set the logging prefix
|
||||
* @brief Set the logging prefix.
|
||||
*
|
||||
* @param prefix
|
||||
*/
|
||||
void setLoggingPrefix(const std::string& prefix) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Disable logging;
|
||||
* @brief
|
||||
*
|
||||
*/
|
||||
void disableLogging() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Shuts down the current processing, clear buffers and reset the voices.
|
||||
*
|
||||
*/
|
||||
void allSoundOff() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Add external definitions prior to loading;
|
||||
* Note that these do not get reset by loading or resetting the synth.
|
||||
* You need to call clearExternalDefintions() to erase them.
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param id
|
||||
* @param value
|
||||
*/
|
||||
void addExternalDefinition(const std::string& id, const std::string& value);
|
||||
|
||||
/**
|
||||
* @brief Clears external definitions for the next file loading.
|
||||
* @since 0.4.0
|
||||
*
|
||||
*/
|
||||
void clearExternalDefinitions();
|
||||
|
||||
/**
|
||||
* @brief Get the key labels, if any
|
||||
* @since 0.4.0
|
||||
*
|
||||
*/
|
||||
const std::vector<std::pair<uint8_t, std::string>>& getKeyLabels() const noexcept;
|
||||
/**
|
||||
* @brief Get the CC labels, if any
|
||||
* @since 0.4.0
|
||||
*
|
||||
*/
|
||||
const std::vector<std::pair<uint16_t, std::string>>& getCCLabels() const noexcept;
|
||||
private:
|
||||
std::unique_ptr<sfz::Synth> synth;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,11 +40,9 @@ void ADSREnvelope<Type>::reset(const EGDescription& desc, const Region& region,
|
|||
|
||||
releaseDelay = 0;
|
||||
shouldRelease = false;
|
||||
freeRunning = (
|
||||
(region.trigger == SfzTrigger::release)
|
||||
freeRunning = ((region.trigger == SfzTrigger::release)
|
||||
|| (region.trigger == SfzTrigger::release_key)
|
||||
|| region.loopMode == SfzLoopMode::one_shot
|
||||
);
|
||||
|| (region.loopMode == SfzLoopMode::one_shot && (region.isGenerator() || region.oscillator)));
|
||||
currentValue = this->start;
|
||||
currentState = State::Delay;
|
||||
}
|
||||
|
|
@ -117,8 +115,7 @@ void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
|
|||
// release takes effect this frame
|
||||
currentState = State::Release;
|
||||
releaseDelay = -1;
|
||||
}
|
||||
else if (shouldRelease && releaseDelay > 0) {
|
||||
} else if (shouldRelease && releaseDelay > 0) {
|
||||
// prevent computing the segment further than release point
|
||||
size = std::min<size_t>(size, releaseDelay);
|
||||
}
|
||||
|
|
@ -188,6 +185,8 @@ void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
|
|||
this->currentValue = currentValue;
|
||||
this->shouldRelease = shouldRelease;
|
||||
this->releaseDelay = releaseDelay;
|
||||
|
||||
ASSERT(!hasNanInf(output));
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
|
|
|
|||
|
|
@ -1,114 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
/**
|
||||
* @brief This file contains a pair of RAII helpers that handle some form
|
||||
* of lock-free mutex-type protection adapter to audio applications where you have 1 priority thread
|
||||
* that should never block and would rather return silence than wait, and another low-priority
|
||||
* thread that handles long computations.
|
||||
*
|
||||
* @code{.cpp}
|
||||
*
|
||||
* // Somewhere in a class...
|
||||
* std::atomic<bool> canEnterCallback;
|
||||
* std::atomic<bool> inCallback;
|
||||
*
|
||||
* void functionThatSuspendsCallback()
|
||||
* {
|
||||
* AtomicDisabler callbackDisabler { canEnterCallback };
|
||||
*
|
||||
* while (inCallback) {
|
||||
* std::this_thread::sleep_for(1ms);
|
||||
* }
|
||||
*
|
||||
* // Do your thing.
|
||||
* }
|
||||
*
|
||||
* void callback(int samplesPerBlock) noexcept
|
||||
* {
|
||||
* AtomicGuard callbackGuard { inCallback };
|
||||
* if (!canEnterCallback)
|
||||
* return;
|
||||
*
|
||||
* // Do your thing.
|
||||
* }
|
||||
* @endcode
|
||||
* There are probably many ways to improve these and probably even debug them.
|
||||
* The spinlocking itself could be integrated in the constructor, although the
|
||||
* check for return in the callback could not.
|
||||
*/
|
||||
#include <atomic>
|
||||
|
||||
namespace sfz
|
||||
{
|
||||
/**
|
||||
* @brief Simple class to set an atomic to true and automatically set it back to false on
|
||||
* destruction.
|
||||
*
|
||||
* You call it like this assuming you need indicate that you are in e.g. a callback
|
||||
* @code{.cpp}
|
||||
* void functionToProtect()
|
||||
* {
|
||||
* AtomicGuard { guard };
|
||||
*
|
||||
* // Do stuff, the atomic will be set back to false as soon as you're back
|
||||
* }
|
||||
* @endcode
|
||||
* Note that this is not thread-safe at all, in the sense that it is only meant to be
|
||||
* used with 2 threads along with the AtomicDisabler. One thread uses AtomicGuards, the other
|
||||
* AtomicDisablers, and no other contending thread can share this pair of atomics.
|
||||
*/
|
||||
class AtomicGuard
|
||||
{
|
||||
public:
|
||||
AtomicGuard() = delete;
|
||||
AtomicGuard(std::atomic<bool>& guard)
|
||||
: guard(guard)
|
||||
{
|
||||
guard = true;
|
||||
}
|
||||
~AtomicGuard()
|
||||
{
|
||||
guard = false;
|
||||
}
|
||||
private:
|
||||
std::atomic<bool>& guard;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Simple class to set an atomic to false and automatically set it back to true on
|
||||
* destruction.
|
||||
*
|
||||
* You call it like this assuming you need to disable e.g. a callback
|
||||
* @code{.cpp}
|
||||
* void functionThatDisableAnotherFunction()
|
||||
* {
|
||||
* AtomicDisabler { disabler };
|
||||
*
|
||||
* // Do stuff, the atomic will be set back to true as soon as you're back
|
||||
* }
|
||||
* @endcode
|
||||
* Note that this is not thread-safe at all, in the sense that it is only meant to be
|
||||
* used with 2 threads along with the AtomicGuard. One thread uses AtomicGuards, the other
|
||||
* AtomicDisabler, and no other contending thread can share this pair of atomics.
|
||||
*/
|
||||
class AtomicDisabler
|
||||
{
|
||||
public:
|
||||
AtomicDisabler() = delete;
|
||||
AtomicDisabler(std::atomic<bool>& allowed)
|
||||
: allowed(allowed)
|
||||
{
|
||||
allowed = false;
|
||||
}
|
||||
~AtomicDisabler()
|
||||
{
|
||||
allowed = true;
|
||||
}
|
||||
private:
|
||||
std::atomic<bool>& allowed;
|
||||
};
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
#include "Config.h"
|
||||
#include "Debug.h"
|
||||
#include "LeakDetector.h"
|
||||
#include "SIMDHelpers.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include <array>
|
||||
|
|
@ -25,7 +26,9 @@ namespace sfz
|
|||
* @tparam MaxChannels the maximum number of channels in the buffer
|
||||
* @tparam Alignment the alignment for the buffers
|
||||
*/
|
||||
template <class Type, size_t MaxChannels = sfz::config::numChannels, unsigned int Alignment = SIMDConfig::defaultAlignment>
|
||||
template <class Type, size_t MaxChannels = config::numChannels,
|
||||
unsigned int Alignment = config::defaultAlignment,
|
||||
size_t PaddingLeft_ = 0, size_t PaddingRight_ = 0>
|
||||
class AudioBuffer {
|
||||
public:
|
||||
using value_type = typename std::remove_cv<Type>::type;
|
||||
|
|
@ -34,6 +37,15 @@ public:
|
|||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
enum {
|
||||
//! Increased left padding to preserve required alignment
|
||||
PaddingLeft = PaddingLeft_ + (Alignment - (PaddingLeft_ % Alignment)) % Alignment,
|
||||
//! Padding to the right
|
||||
PaddingRight = PaddingRight_,
|
||||
//! Total padding left and right
|
||||
PaddingTotal = PaddingLeft + PaddingRight,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct a new Audio Buffer object
|
||||
*
|
||||
|
|
@ -54,7 +66,7 @@ public:
|
|||
, numFrames(numFrames)
|
||||
{
|
||||
for (size_t i = 0; i < numChannels; ++i)
|
||||
buffers[i] = absl::make_unique<buffer_type>(numFrames);
|
||||
buffers[i] = absl::make_unique<buffer_type>(numFrames + PaddingTotal);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -64,12 +76,12 @@ public:
|
|||
* @return true if the resize worked
|
||||
* @return false otherwise
|
||||
*/
|
||||
bool resize(size_t newSize)
|
||||
bool resize(size_t newSize, std::nothrow_t) noexcept
|
||||
{
|
||||
bool returnedOK = true;
|
||||
|
||||
for (size_t i = 0; i < numChannels; ++i)
|
||||
returnedOK &= buffers[i]->resize(newSize);
|
||||
returnedOK &= buffers[i]->resize(newSize + PaddingTotal, std::nothrow);
|
||||
|
||||
if (returnedOK)
|
||||
numFrames = newSize;
|
||||
|
|
@ -77,6 +89,19 @@ public:
|
|||
return returnedOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resizes all the underlying buffers to a new size.
|
||||
*
|
||||
* @param newSize
|
||||
*/
|
||||
void resize(size_t newSize)
|
||||
{
|
||||
for (size_t i = 0; i < numChannels; ++i)
|
||||
buffers[i]->resize(newSize + PaddingTotal);
|
||||
|
||||
numFrames = newSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator to a specific channel with a non-const type.
|
||||
*
|
||||
|
|
@ -87,7 +112,7 @@ public:
|
|||
{
|
||||
ASSERT(channelIndex < numChannels);
|
||||
if (channelIndex < numChannels)
|
||||
return buffers[channelIndex]->data();
|
||||
return buffers[channelIndex]->data() + PaddingLeft;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -102,7 +127,7 @@ public:
|
|||
{
|
||||
ASSERT(channelIndex < numChannels);
|
||||
if (channelIndex < numChannels)
|
||||
return buffers[channelIndex]->end();
|
||||
return buffers[channelIndex]->end() - PaddingRight;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -117,7 +142,7 @@ public:
|
|||
{
|
||||
ASSERT(channelIndex < numChannels);
|
||||
if (channelIndex < numChannels)
|
||||
return buffers[channelIndex]->data();
|
||||
return buffers[channelIndex]->data() + PaddingLeft;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -132,7 +157,7 @@ public:
|
|||
{
|
||||
ASSERT(channelIndex < numChannels);
|
||||
if (channelIndex < numChannels)
|
||||
return buffers[channelIndex]->end();
|
||||
return buffers[channelIndex]->end() - PaddingRight;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -147,7 +172,7 @@ public:
|
|||
{
|
||||
ASSERT(channelIndex < numChannels);
|
||||
if (channelIndex < numChannels)
|
||||
return { buffers[channelIndex]->data(), buffers[channelIndex]->size() };
|
||||
return { buffers[channelIndex]->data() + PaddingLeft, numFrames };
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -170,7 +195,7 @@ public:
|
|||
void addChannel()
|
||||
{
|
||||
if (numChannels < MaxChannels)
|
||||
buffers[numChannels++] = absl::make_unique<buffer_type>(numFrames);
|
||||
buffers[numChannels++] = absl::make_unique<buffer_type>(numFrames + PaddingTotal);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -219,7 +244,7 @@ public:
|
|||
ASSERT(buffers[channelIndex] != nullptr);
|
||||
ASSERT(frameIndex < numFrames);
|
||||
|
||||
return *(buffers[channelIndex]->data() + frameIndex);
|
||||
return *(buffers[channelIndex]->data() + PaddingLeft + frameIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -246,6 +271,17 @@ public:
|
|||
numChannels = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes zeros in the buffer
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
for (size_t i = 0; i < numChannels; ++i) {
|
||||
absl::Span<Type> paddedSpan { buffers[i]->data(), numFrames + PaddingTotal };
|
||||
fill<Type>(paddedSpan, Type{ 0.0 });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a positive number of channels to the buffer
|
||||
*
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue