Merge branch 'develop'

This commit is contained in:
Paul Ferrand 2020-01-30 10:16:15 +01:00
commit 35fbf7d36b
362 changed files with 50367 additions and 8207 deletions

View file

@ -4,11 +4,15 @@
# Top-most EditorConfig file
root = true
# No trailing whitespaces, but with a newline ending every file,
# UTF-8 charset, set indent to spaces with width of four
# UTF-8 charset, set indent to spaces with width of four,
# with no trailing whitespaces and a newline ending every file.
[*]
charset = utf-8
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.{ttl,in,sh,yml}]
indent_size = 2
indent_style = space
indent_size = 4

22
.gitignore vendored
View file

@ -1,9 +1,5 @@
build
build-arm
build-asan
build-release
build-reldeb
build-debug
build*
docs
.vscode
perf.data
perf.data.old
@ -15,6 +11,7 @@ cmake_install.cmake
compile_commands.json
*.a
*.txt.user
*.autosave
.DS_Store
@ -22,10 +19,15 @@ clients/sfizz_jack
clients/sfzprint
# gh-pages unstaged files:
.bundle/
Gemfile.lock
_api/
_site/
.bundle/
api/
assets/
node_modules/
yarn.lock
.jekyll-cache
.jekyll-metadata
.sass-cache
*.lock
*.sublime-*
*.code-*

6
.gitmodules vendored
View file

@ -1,8 +1,4 @@
[submodule "external/abseil-cpp"]
path = external/abseil-cpp
url = https://github.com/abseil/abseil-cpp
branch = lts_2019_08_08
[submodule "external/benchmark"]
path = external/benchmark
url = https://github.com/google/benchmark
branch = master
branch = lts_2019_08_08

View file

@ -1,43 +1,66 @@
language: cpp
env:
global:
- INSTALL_DIR="sfizz-${TRAVIS_BRANCH}-${TRAVIS_OS_NAME}-${TRAVIS_CPU_ARCH}"
matrix:
jobs:
include:
- os: linux
dist: xenial
- os: linux
arch: amd64
dist: bionic
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-8
- g++-8
env:
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
- sourceline: 'ppa:ubuntu-toolchain-r/test'
# - os: osx
# osx_image: xcode10.1
packages:
- doxygen
- os: linux
arch: arm64
dist: bionic
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- os: osx
osx_image: xcode10.1
before_install:
- sh ${TRAVIS_BUILD_DIR}/.travis/before_install.sh
- bash ${TRAVIS_BUILD_DIR}/.travis/before_install.sh
install:
- sh ${TRAVIS_BUILD_DIR}/.travis/install.sh
- bash ${TRAVIS_BUILD_DIR}/.travis/install.sh
script:
- sh ${TRAVIS_BUILD_DIR}/.travis/script.sh
- bash ${TRAVIS_BUILD_DIR}/.travis/script.sh
after_failure:
- sh ${TRAVIS_BUILD_DIR}/.travis/after_failure.sh
- bash ${TRAVIS_BUILD_DIR}/.travis/discord_webhook.sh failure
after_success:
- sh ${TRAVIS_BUILD_DIR}/.travis/after_success.sh
- bash ${TRAVIS_BUILD_DIR}/.travis/discord_webhook.sh success
- bash ${TRAVIS_BUILD_DIR}/.travis/update_dox.sh
- bash ${TRAVIS_BUILD_DIR}/.travis/after_success.sh
deploy:
provider: releases
provider: releases
skip_cleanup: true
api_key:
secure: cc6IkdalZRb+lOWzPkubCUvTcP9lBwiNBBPkXWVmeX7YXkdQlsoVQ11bZThd1Go+5Lt+jGFtEqEjJ0L7GxpdatEeBMEK5HXl0QtHAybl9J4Ax7hKba8FSX5RqPFOuKixCob6eCqJRchT26qRsQE/WYMY8DmproEFGMBHk0K4HXDWEq0HP8dbWVx3KudvellCYszUrVQYb2s49sDiF8OwSog5TiqdBqO5PRcqyt2ySmHgqwBYq7XV0pb+DxcKJcc7/gCBpBN7nyEhl8gmKELypLF81l8xUDBUjRy1jjf0r33ISHpX1MWeWIN9YzSNL0aLPQvwynAQ7OeckFAIMABah2UuYcxdd4ioXduPTbprVYpSwIyyxkeafK2jxgxwoKWi9GJZ3yAUHNS/V7Gh5DsFb7C6QDqr5gOxOMtjHwjmHnxmD44SsugjSosVzirvQR+JbrSsPsmZThQjCvwG+7hO3JatX4BXA7bp9WtgsoMrCH61gCp2GogQVLvYpgRpVErKK8PMTkt71OouewTDqD5iwc3N/2pNWP3cXxpPegerjl4R2sY0sZ57WRfW8ts7akPyqJZa2uKpzpM0eXy35fZl6WoJSlIv3akCnzdtLtaWAptzfXhgRSlF/33+gZ3jRcSP7ca2YpUviuZ+KYk+uGfMh092CMRMjPv1Et5h7zYGD+o=
file: "$DEPLOYFILE"
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
repo: ${TRAVIS_REPO_SLUG}
tags: true
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}"

View file

@ -1,25 +1,9 @@
#!/bin/bash
set -e
set -ex
# Travis Webhook
wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
chmod +x send.sh
./send.sh success $WEBHOOK_URL
# Deploy
export VERSION=$(git describe --tags)
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
export DEPLOYFILE=Sfizz-$VERSION-x86_64.AppImage
wget -c -nv "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
chmod +x linuxdeploy-x86_64.AppImage
for size in 16 32 48 128 256; do dirname="sfizz/usr/share/icons/hicolor/${size}x${size}/apps"; mkdir -p $dirname; cp ./resources/icons/icon_${size}px.png ./${dirname}/sfizz.png; done
./linuxdeploy-x86_64.AppImage --appdir=sfizz --desktop-file=./resources/linux/sfizz.desktop --executable=./build/clients/sfizz_jack --output=appimage
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
export DEPLOYFILE=sfizz-$VERSION.dmg
mkdir ./output
cp -r /build/* ./output/
cp ./resources/icons/icon.icns ./output/sfizz.app/Contents/Resources/
hdiutil create /tmp/tmp.dmg -ov -volname "sfizz-$VERSION" -fs HFS+ -srcfolder "./output/"
hdiutil convert /tmp/tmp.dmg -format UDZO -o ./$DEPLOYFILE;
fi
cd build
make install
tar -zcvf "${INSTALL_DIR}.tar.gz" ${INSTALL_DIR}
mv "${INSTALL_DIR}.tar.gz" ${TRAVIS_BUILD_DIR}
cd ..

View file

@ -1,12 +1,19 @@
#!/bin/bash
set -e
set -ex
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
wget https://cmake.org/files/v3.13/cmake-3.13.0-Linux-x86_64.sh
sudo sh cmake-3.13.0-Linux-x86_64.sh --skip-license --prefix=/usr/local
cmake_dir="cmake-3.13.0-${TRAVIS_OS_NAME}-${TRAVIS_CPU_ARCH}"
cmake_arc="${cmake_dir}.tar.gz"
cmake_url="https://github.com/sfztools/cmake/releases/download/${TRAVIS_OS_NAME}/${cmake_arc}"
if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
wget -q ${cmake_url}
tar xzf ${cmake_arc}
cd ${TRAVIS_BUILD_DIR}/${cmake_dir}
sudo cp bin/* /usr/local/bin/
sudo cp -r share/* /usr/local/share/
export PATH="/usr/local/bin:$PATH" # FIXME
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then
sudo ln -s /usr/local /opt/local
brew update
brew upgrade cmake

View file

@ -1,8 +1,8 @@
#!/bin/bash
set -e
set -ex
# Travis Webhook
wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
chmod +x send.sh
./send.sh failure $WEBHOOK_URL
./send.sh ${1} ${WEBHOOK_URL}

View file

@ -1,7 +1,8 @@
#!/bin/bash
set -e
set -ex
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
sudo apt-get install libasound2-dev libjack-jackd2-dev libsndfile1-dev
if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
sudo apt-get install libasound2-dev libjack-jackd2-dev libsndfile1-dev lv2-dev
gcc -v && g++ -v && cmake --version && /usr/local/bin/cmake --version && $SHELL --version
fi

View file

@ -1,16 +1,17 @@
#!/bin/bash
set -e
set -ex
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 100
gcc -v && g++ -v && cmake --version && /usr/local/bin/cmake --version && $SHELL --version
mkdir -p build/${INSTALL_DIR} && cd build
/usr/local/bin/cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=${PWD}/${INSTALL_DIR} ..
if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
mkdir build && cd build
/usr/local/bin/cmake -D CMAKE_BUILD_TYPE=Debug -D SFIZZ_CLIENTS=ON ..
make -j$(nproc)
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
mkdir build && cd build
cmake -D SFIZZ_CLIENTS=OFF -G Xcode .. # FIXME: client build
xcodebuild -project sfizz.xcodeproj -alltargets -configuration Debug build
elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then
make -j$(sysctl -n hw.ncpu)
# Xcode not currently supported, see https://gitlab.kitware.com/cmake/cmake/issues/18088
# xcodebuild -project sfizz.xcodeproj -alltargets -configuration Debug build
fi

19
.travis/update_dox.sh Executable file
View file

@ -0,0 +1,19 @@
#!/bin/bash
set -x # No fail, we need to go back to the original branch at the end
# Build documentation only from Linux x86_64 builds
if [[ ${TRAVIS_CPU_ARCH} != "amd64" || ${TRAVIS_OS_NAME} != "linux" ]]; then
exit 0
fi
doxygen Doxyfile
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
rm -rf ./api
mv _api api
git add api && git commit -m "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}

View file

@ -1,98 +1,56 @@
cmake_minimum_required(VERSION 3.13)
project(sfizz VERSION 1.0.0 LANGUAGES CXX)
if(WIN32)
cmake_minimum_required (VERSION 3.15)
cmake_policy(SET CMP0091 NEW)
else()
cmake_minimum_required (VERSION 3.11)
endif()
project (sfizz VERSION 0.2.0 LANGUAGES CXX C)
set(PROJECT_DESCRIPTION "A library to load SFZ description files and use them to render music.")
# Do not override if set to more than 14
if (NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 14)
set(CMAKE_CXX_STANDARD 14)
# External configuration CMake scripts
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include (SfizzConfig)
# Build Options
set (BUILD_TESTING OFF CACHE BOOL "Disable Abseil's tests [default: OFF]")
# On macOS add the needed directories for both library and jack client
if (APPLE)
include_directories (SYSTEM /usr/local/opt/libsndfile/include)
link_directories (/usr/local/opt/libsndfile/lib)
endif()
# Enable LTO
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # To override the policy in abseil and benchmark
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
option (ENABLE_LTO "Enable Link Time Optimization [default: ON]" ON)
option (SFIZZ_JACK "Enable JACK stand-alone build [default: ON]" ON)
option (SFIZZ_LV2 "Enable LV2 plug-in build [default: ON]" ON)
option (SFIZZ_BENCHMARKS "Enable benchmarks build [default: OFF]" OFF)
option (SFIZZ_TESTS "Enable tests build [default: OFF]" OFF)
option (SFIZZ_SHARED "Enable shared library build [default: ON]" ON)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT ANDROID)
set(USE_LIBCPP ON CACHE BOOL "Use libc++ with clang")
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
# Don't use IPO in non Release builds
include (CheckIPO)
# Add Abseil
add_subdirectory (external/abseil-cpp EXCLUDE_FROM_ALL)
# Add the static library targets and sources
add_subdirectory (src)
# Optional targets
if (SFIZZ_JACK)
add_subdirectory (clients)
endif()
if (UNIX)
add_compile_options(-Wall)
add_compile_options(-Wextra)
add_compile_options(-ffast-math)
add_compile_options(-fno-omit-frame-pointer)
endif()
if (WIN32)
# TODO: Remove when abseil updates (deprecation warning from c++17 onwards)
add_compile_options(/wd4996)
# add_compile_options(/W3)
# add_compile_options(/GR-)
# add_compile_options(/permissive-)
# add_compile_options(/fp:fast)
endif()
message("Cmake flags: ${CMAKE_CXX_FLAGS}")
# Export the compile_commands.json file
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# find_package(Git QUIET)
# if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
# Update submodules as needed
# option(GIT_SUBMODULE "Check submodules during build" ON)
# if(GIT_SUBMODULE)
# message(STATUS "Submodule update")
# execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive --remote
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
# RESULT_VARIABLE GIT_SUBMOD_RESULT)
# if(NOT GIT_SUBMOD_RESULT EQUAL "0")
# message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
# endif()
# endif()
# endif()
# Build options and includes
set(BUILD_TESTING OFF CACHE BOOL "Disable Abseil's tests")
add_subdirectory(external/abseil-cpp EXCLUDE_FROM_ALL)
# Download libsndfile
if (WIN32)
# file(DOWNLOAD https://github.com/bastibe/libsndfile-binaries/raw/master/libsndfile32bit.dll external/libsndfile-win/libsndfile32bit.dll)
# file(DOWNLOAD https://github.com/bastibe/libsndfile-binaries/raw/master/libsndfile64bit.dll external/libsndfile-win/libsndfile64bit.dll)
# message(STATUS "Downloading libsndfile")
# # find_library(sndfile libsndfile-1.lib HINTS ${WIN_SNDFILE_PATH}/lib)
set(WIN_SNDFILE_PATH "C:/Program Files/Mega-Nerd/libsndfile")
add_library(sndfile STATIC IMPORTED)
set_target_properties(sndfile PROPERTIES IMPORTED_LOCATION "${WIN_SNDFILE_PATH}/lib/libsndfile-1.lib")
set_target_properties(sndfile PROPERTIES LINKER_LANGUAGE CXX)
file(COPY "${WIN_SNDFILE_PATH}/bin/libsndfile-1.dll" DESTINATION ${CMAKE_BINARY_DIR})
target_include_directories(sndfile INTERFACE "${WIN_SNDFILE_PATH}/include")
elseif(APPLE)
find_path(SNDFILE_INCLUDE_DIR NAMES sndfile.hh PATHS "/usr/local/include")
find_library(SNDFILE_LIBRARY NAMES sndfile PATHS "/usr/local/lib")
message(STATUS "include dir: " ${SNDFILE_INCLUDE_DIR} " lib: " ${SNDFILE_LIBRARY})
add_library(sndfile SHARED IMPORTED GLOBAL)
target_include_directories(sndfile INTERFACE ${SNDFILE_INCLUDE_DIR})
target_link_libraries(sndfile INTERFACE ${SNDFILE_LIBRARY})
endif()
add_subdirectory(sfizz)
if (SFIZZ_CLIENTS)
add_subdirectory(clients)
endif()
if (SFIZZ_TESTS)
add_subdirectory(tests)
if (SFIZZ_LV2)
add_subdirectory (lv2)
endif()
if (SFIZZ_BENCHMARKS)
# Benchmarks
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Disable Google Benchmark tests")
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "Disable Google Benchmark install")
add_subdirectory(external/benchmark EXCLUDE_FROM_ALL)
add_subdirectory(benchmarks)
add_subdirectory (benchmarks)
endif()
if (SFIZZ_TESTS)
add_subdirectory (tests)
endif()
show_build_info_if_needed()

2492
Doxyfile Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,15 @@
BSD 2-Clause License
Copyright (c) 2019, Paul Ferrand
The code is copyrighted by their respective authors, as indicated by the
source control mechanism.
Contributors include:
- Paul Ferrand (2019-) paul at ferrand dot cc
- Andrea Zanellato (2019-)
- Jean-Pierre Cimalando (2020-)
- Michael Willis (2020-)
- Alexander Mitchell (2020-)
All rights reserved.
Redistribution and use in source and binary forms, with or without

118
README.md
View file

@ -1,53 +1,61 @@
# sfizz
[![Travis Build Status](https://img.shields.io/travis/com/sfztools/sfizz.svg?label=Linux&style=popout&logo=travis)](https://travis-ci.com/sfztools/sfizz)
## Building
[![Travis Build Status]](https://travis-ci.com/sfztools/sfizz)
Most people will probably want the LV2 plugin with `libsndfile` built-in statically.
You can directly build it this way through Docker:
```
wget https://raw.githubusercontent.com/sfztools/sfizz/develop/scripts/Dockerfile
wget https://raw.githubusercontent.com/sfztools/sfizz/develop/scripts/x64-linux-hidden.cmake
docker build -t sfizz .
docker cp $(docker create sfizz:latest):/tmp/sfizz/build/sfizz.lv2 .
```
Note that the statically linked LV2 plugin is to be distributed under the LGPL license, as per the terms of the `libsndfile` library.
Sfizz depends on the [sndfile] library.
The [JACK] client that you will probably build depends on the `jack` library.
To build `sfizz` you need to install both as shared libraries on the system.
### More generic builds and development
`sfizz` depends mainly on the `libsndfile` library.
To build the other `sfizz` targets you need to install both `libsndfile` and `JACK` as shared libraries on the system.
In Debian-based distributions, this translates into
```bash
sudo apt install libjack-jackd2-dev libsndfile1-dev
```
The benchmarks depend on the `benchmark` library (https://github.com/google/benchmark).
If you wish to build the benchmarks you should either build it from source and install the static library, or use the library from your distribution---Ubuntu proposes a `libbenchmark-dev` package that does this.
The process is as follows:
1. Clone the repository with all the submodules
2. Create a build directory for CMake and `cd` into it
3. Build
3. Build as release
4. Enjoy :)
In the shell world, this means
```bash
git clone --recursive https://github.com/sfztools/sfizz.git
git checkout develop
cd sfizz
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make
sudo make install
```
Current configuration switches for CMake are:
By default this builds and installs:
- The shared library version of sfizz with both C and C++ interfaces
- The JACK client
The JACK client client will forcefully connect to the system output, and open an event input in Jack for you to connect a midi capable software or hardware (e.g. `jack-keyboard`).
Note that you can disable all targets but the LV2 plugin using
```sh
cmake -DCMAKE_BUILD_TYPE=Release -DSFIZZ_JACK=OFF -DSFIZZ_SHARED=OFF ..
```
ENABLE_LTO Enable Link Time Optimization [default: ON]
SFIZZ_JACK Enable JACK stand-alone build [default: ON]
SFIZZ_LV2 Enable LV2 plug-in build [default: ON]
SFIZZ_BENCHMARKS Enable benchmarks build [default: OFF]
SFIZZ_TESTS Enable tests build [default: OFF]
SFIZZ_SHARED Enable shared library build [default: ON]
```
For details about building under macOS, see [here].
You can then find the JACK client in `clients/sfizz_jack`.
Just specify an `.sfz` file as a parameter and you are good to go.
The client will forcefully connect to the system output,
and open an event input in JACK for you to connect a midi capable software
or hardware (e.g. `jack-keyboard`).
If no Jack server is already started it will start one with basic options.
and process as before.
In this case, the LV2 plugin will load `libsndfile` dynamically from your system.
## Possible pitfalls and alternatives
@ -58,11 +66,65 @@ update the submodules manually with
git submodule update --init --recursive
```
You can build with `clang`, although in that case the CMakeFile
defaults to using `libc++` instead of `libstdc++`.
You can build with `clang`, although in that case the CMakeFile defaults to using `libc++` instead of `libstdc++`.
[Travis Build Status]: https://img.shields.io/travis/com/sfztools/sfizz.svg?label=Linux-macOS&style=popout&logo=travis
[JACK]: https://jackaudio.org/
[sndfile]: http://mega-nerd.com/libsndfile/
[here]: https://sfztools.github.io/sfizz/macos
### Building with MSVC on windows
The simplest is to use `vcpkg` as a package manager, which will give you access to `libsndfile`.
```powershell
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install
.\vcpkg integrate powershell
```
Assuming you want to build for x64, install the relevant packages as follows
```powershell
.\vcpkg.exe install libsndfile:x64-windows-static benchmark:x64-windows-static
```
In the sfizz source directory, you can then build with CMake as usual, although you should clone the windows branch:
```powershell
git clone --recursive https://github.com/sfztools/sfizz.git
cd sfizz
mkdir build
cd build
cmake .. -DSFIZZ_JACK=OFF "-DCMAKE_TOOLCHAIN_FILE=C:\Users\Paul\source\vcpkg\scripts\buildsystems\vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static
cmake --build . -j 16 --config Release
```
This builds the lv2 plugin in `build\lv2\Release` and the Turtle files in `build\sfizz.lv2`, but the installation procedure is not entirely automatic yet.
In particular, having the required DLLs along with `sfizz.dll` is actually not helpful because the system cannot find dlls from another dll's root directory.
You thus need to add all of `FLAC.dll`, `libsndfile-1.dll`, `vorbis.dll`, `ogg.dll`, `vorbisenc.dll` in some directory in your `PATH` variable, and manually add `sfizz.dll` to the `sfizz.lv2` directory, and the the `sfizz.lv2` directory to `%APPDATA%\Roaming\LV2`.
Ardour should then find the plugin correctly and load it without issues.
### Building the LV2 plugin with static linkage to `libsndfile` on Linux
This uses Docker and `vcpkg` on Linux.
Install a Docker version and use the `Dockerfile` located in `scripts/`.
The `scripts/` directory also contains a `docker_lv2_release.sh` file that automates downloading the current `develop` branch and building an LV2 release plugin.
Note that the statically linked LV2 plugin is to be distributed under the LGPL license, as per the terms of the `libsndfile` library.
## License and contribution information
Contributors to `sfizz` include:
- Paul Ferrand (2019-) (maintainer)
- Andrea Zanellato (2019-) (devops, documentation and distribution)
- Jean-Pierre Cimalando (2020-)
- Michael Willis (2020-)
- Alexander Mitchell (2020-)
The sfizz library makes primary use of:
- [libsndfile](https://github.com/erikd/libsndfile/) (licensed under the GNU Lesser General Public License v2.1)
- [Abseil](https://github.com/abseil/abseil-cpp) (licensed under the Apache License 2.0)
- [atomic_queue](https://github.com/max0x7ba/atomic_queue) by Maxim Egorushkin (licensed under the MIT license)
- [filesystem](https://github.com/gulrak/filesystem) by Steffen Schümann (licensed under the BSD 3-Clause license)
- [hiir](http://ldesoras.free.fr/prod.html#src_hiir) by Laurent de Soras (licensed under the Do What The Fuck You Want To Public License v2 license)
The sfizz library also uses in some subprojects:
- [Catch2](https://github.com/catchorg/Catch2) (licensed under the Boost Software License 1.0)
- [benchmark](https://github.com/google/benchmark) (licensed under the Apache License 2.0)
- [LV2](https://lv2plug.in/) (licensed under the ISC license)
- [JACK](https://github.com/jackaudio/jack2) (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)

View file

@ -1,28 +1,12 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "ADSREnvelope.h"
#include <benchmark/benchmark.h>
#include "../sfizz/ADSREnvelope.h"
#include <algorithm>
#include <random>
#include <numeric>
@ -35,7 +19,7 @@ constexpr int release = attack;
constexpr int releaseTime = envelopeSize - static_cast<int>(envelopeSize / 4);
// Explicitely instantiate class
#include "../sfizz/ADSREnvelope.cpp"
#include "ADSREnvelope.cpp"
template class sfz::ADSREnvelope<float>;
static void Point(benchmark::State& state) {
@ -44,12 +28,12 @@ static void Point(benchmark::State& state) {
for (auto _ : state) {
envelope.reset(attack, release, 1.0, fixedAmount, decay, fixedAmount);
envelope.startRelease(releaseTime);
for(int offset = 0; offset < envelopeSize; offset += state.range(0))
for(int offset = 0; offset < envelopeSize; offset += static_cast<int>(state.range(0)))
for (auto& out: output)
out = envelope.getNextValue();
benchmark::DoNotOptimize(output);
}
state.counters["Per block"] = benchmark::Counter(envelopeSize / state.range(0), benchmark::Counter::kIsIterationInvariantRate);
state.counters["Per block"] = benchmark::Counter(envelopeSize / static_cast<double>(state.range(0)), benchmark::Counter::kIsRate);
}
static void Block(benchmark::State& state) {
@ -58,14 +42,14 @@ static void Block(benchmark::State& state) {
for (auto _ : state) {
envelope.reset(attack, release, 1.0, fixedAmount, decay, fixedAmount);
envelope.startRelease(releaseTime);
for(int offset = 0; offset < envelopeSize; offset += state.range(0))
for(int offset = 0; offset < envelopeSize; offset += static_cast<int>(state.range(0)))
envelope.getBlock(absl::MakeSpan(output));
benchmark::DoNotOptimize(output);
}
state.counters["Per block"] = benchmark::Counter(envelopeSize / state.range(0), benchmark::Counter::kIsIterationInvariantRate);
state.counters["Per block"] = benchmark::Counter(envelopeSize / static_cast<double>(state.range(0)), benchmark::Counter::kIsRate);
}
BENCHMARK(Point)->RangeMultiplier(2)->Range((2<<6), (2<<11));
BENCHMARK(Block)->RangeMultiplier(2)->Range((2<<6), (2<<11));
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,26 +1,10 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 <benchmark/benchmark.h>
#include <absl/types/span.h>
#include <random>
@ -111,7 +95,7 @@ static void Low(benchmark::State& state) {
std::vector<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::normal_distribution<float> dist { };
std::generate(input.begin(), input.end(), [&]() {
return dist(gen);
@ -126,7 +110,7 @@ static void High(benchmark::State& state) {
std::vector<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::normal_distribution<float> dist { };
std::generate(input.begin(), input.end(), [&]() {
return dist(gen);
@ -141,7 +125,7 @@ static void High_Raw(benchmark::State& state) {
std::vector<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::normal_distribution<float> dist { };
std::generate(input.begin(), input.end(), [&]() {
return dist(gen);
@ -156,7 +140,7 @@ static void High_SSE(benchmark::State& state) {
std::vector<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::normal_distribution<float> dist { };
std::generate(input.begin(), input.end(), [&]() {
return dist(gen);
@ -171,4 +155,4 @@ BENCHMARK(Low)->RangeMultiplier(2)->Range((2<<5), (2<<10));
BENCHMARK(High)->RangeMultiplier(2)->Range((2<<5), (2<<10));
BENCHMARK(High_Raw)->RangeMultiplier(2)->Range((2<<5), (2<<10));
BENCHMARK(High_SSE)->RangeMultiplier(2)->Range((2<<5), (2<<10));
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,33 +1,16 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
class AddArray : public benchmark::Fixture {
public:
@ -113,4 +96,4 @@ BENCHMARK_REGISTER_F(AddArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 1
BENCHMARK_REGISTER_F(AddArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(AddArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(AddArray, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,33 +1,16 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
class CopyArray : public benchmark::Fixture {
public:
@ -98,4 +81,4 @@ BENCHMARK_REGISTER_F(CopyArray, SIMD)->RangeMultiplier(4)->Range(1 << 4, 1 << 16
BENCHMARK_REGISTER_F(CopyArray, StdCopy_Unaligned)->RangeMultiplier(4)->Range(1 << 4, 1 << 16);
BENCHMARK_REGISTER_F(CopyArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 4, 1 << 16);
BENCHMARK_REGISTER_F(CopyArray, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 4, 1 << 16);
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,40 +1,23 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
class CumArray : 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.1, 1 };
std::uniform_real_distribution<float> dist { 0.1f, 1.0f };
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
@ -82,4 +65,4 @@ BENCHMARK_REGISTER_F(CumArray, Sum_Scalar)->RangeMultiplier(4)->Range(1 << 2, 1
BENCHMARK_REGISTER_F(CumArray, Sum_SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(CumArray, Sum_Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(CumArray, Sum_SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,33 +1,16 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
#include "absl/types/span.h"
class DiffArray : public benchmark::Fixture {
@ -35,7 +18,7 @@ public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0.1, 1 };
std::uniform_real_distribution<float> dist { 0.1f, 1.0f };
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
@ -84,4 +67,4 @@ BENCHMARK_REGISTER_F(DiffArray, Diff_Scalar)->RangeMultiplier(4)->Range(1 << 2,
BENCHMARK_REGISTER_F(DiffArray, Diff_SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(DiffArray, Diff_Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(DiffArray, Diff_SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();
BENCHMARK_MAIN();

79
benchmarks/BM_divide.cpp Normal file
View file

@ -0,0 +1,79 @@
// 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>
class Divide : 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, 1 };
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
divisor = std::vector<float>(state.range(0));
std::generate(divisor.begin(), divisor.end(), [&]() { return dist(gen); });
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& state [[maybe_unused]]) {
}
std::vector<float> divisor;
std::vector<float> input;
std::vector<float> output;
};
BENCHMARK_DEFINE_F(Divide, Straight)(benchmark::State& state) {
for (auto _ : state)
{
for (int i = 0; i < state.range(0); ++i)
output[i] = input[i] / divisor[i];
}
}
BENCHMARK_DEFINE_F(Divide, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::divide<float, false>(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));
}
}
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));
}
}
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));
}
}
BENCHMARK_REGISTER_F(Divide, Straight)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(Divide, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(Divide, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(Divide, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(Divide, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -0,0 +1,81 @@
// 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 "EventEnvelopes.h"
#include "absl/types/span.h"
#include "FloatEnvelopes.cpp"
class EnvelopeFixture : 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, false>(input, absl::MakeSpan(input));
}
void TearDown(const ::benchmark::State& state [[maybe_unused]])
{
}
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 30 };
std::vector<float> input;
std::vector<float> output;
};
BENCHMARK_DEFINE_F(EnvelopeFixture, Linear)(benchmark::State& state) {
sfz::LinearEnvelope<float> envelope;
for (auto _ : state)
{
envelope.registerEvent(static_cast<int>(state.range(0) - 1), dist(gen));
envelope.getBlock(absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(EnvelopeFixture, LinearQuantized)(benchmark::State& state) {
sfz::LinearEnvelope<float> envelope;
for (auto _ : state)
{
envelope.registerEvent(static_cast<int>(state.range(0) - 1), dist(gen));
envelope.getQuantizedBlock(absl::MakeSpan(output), 0.5);
}
}
BENCHMARK_DEFINE_F(EnvelopeFixture, Multiplicative)(benchmark::State& state) {
sfz::MultiplicativeEnvelope<float> envelope;
for (auto _ : state)
{
envelope.registerEvent(static_cast<int>(state.range(0) - 1), dist(gen));
envelope.getBlock(absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(EnvelopeFixture, MultiplicativeQuantized)(benchmark::State& state) {
sfz::MultiplicativeEnvelope<float> envelope;
for (auto _ : state)
{
envelope.registerEvent(static_cast<int>(state.range(0) - 1), dist(gen));
envelope.getQuantizedBlock(absl::MakeSpan(output), 1.5);
}
}
BENCHMARK_REGISTER_F(EnvelopeFixture, Linear)->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, MultiplicativeQuantized)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -1,29 +1,12 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
#include "../sfizz/Buffer.h"
#include "Buffer.h"
#include <algorithm>
#include <random>
#include <numeric>
@ -79,9 +62,9 @@ static void FillSIMD_unaligned(benchmark::State& state) {
}
}
BENCHMARK(Dummy)->Range((2<<6), (2<<16));
BENCHMARK(FillScalar)->Range((2<<6), (2<<16));
BENCHMARK(FillSIMD)->Range((2<<6), (2<<16));
BENCHMARK(FillScalar_unaligned)->Range((2<<6), (2<<16));
BENCHMARK(FillSIMD_unaligned)->Range((2<<6), (2<<16));
BENCHMARK_MAIN();
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();

View file

@ -0,0 +1,90 @@
// 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 "Buffer.h"
#include <benchmark/benchmark.h>
#include <sndfile.hh>
#define DR_FLAC_IMPLEMENTATION
#include "dr_flac.h"
#include "ghc/filesystem.hpp"
#include <memory>
#ifndef NDEBUG
#include <iostream>
#endif
// #include "libnyquist/Decoders.h"
class FileFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state [[maybe_unused]]) {
filePath1 = getPath() / "sample1.flac";
filePath2 = getPath() / "sample2.flac";
filePath3 = getPath() / "sample3.flac";
if ( !ghc::filesystem::exists(filePath1)
|| !ghc::filesystem::exists(filePath2)
|| !ghc::filesystem::exists(filePath3) ) {
#ifndef NDEBUG
std::cerr << "Can't find path" << '\n';
#endif
std::terminate();
}
}
void TearDown(const ::benchmark::State& state [[maybe_unused]]) {
}
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::Buffer<float>> buffer;
ghc::filesystem::path filePath1;
ghc::filesystem::path filePath2;
ghc::filesystem::path filePath3;
};
BENCHMARK_DEFINE_F(FileFixture, SndFile)(benchmark::State& state) {
for (auto _ : state)
{
SndfileHandle sndfile(filePath1.c_str());
buffer = std::make_unique<sfz::Buffer<float>>(sndfile.channels() * sndfile.frames());
sndfile.readf(buffer->data(), sndfile.frames());
}
}
BENCHMARK_DEFINE_F(FileFixture, DrFlac)(benchmark::State& state) {
for (auto _ : state)
{
auto* flac = drflac_open_file(filePath2.c_str(), nullptr);
buffer = std::make_unique<sfz::Buffer<float>>(flac->channels * flac->totalPCMFrameCount);
drflac_read_pcm_frames_f32(flac, flac->totalPCMFrameCount, buffer->data());
}
}
// BENCHMARK_DEFINE_F(FileFixture, LibNyquist)(benchmark::State& state) {
// for (auto _ : state)
// {
// nqr::AudioData data;
// nqr::NyquistIO loader;
// loader.Load(&data, filePath3.string());
// benchmark::DoNotOptimize(data);
// }
// }
BENCHMARK_REGISTER_F(FileFixture, SndFile);
BENCHMARK_REGISTER_F(FileFixture, DrFlac);
// BENCHMARK_REGISTER_F(FileFixture, LibNyquist);
BENCHMARK_MAIN();

View file

@ -1,33 +1,16 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
class GainSingle : public benchmark::Fixture {
public:
@ -140,4 +123,4 @@ BENCHMARK_REGISTER_F(GainArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 <<
BENCHMARK_REGISTER_F(GainArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(GainArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(GainArray, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,32 +1,15 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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>
#include "../sfizz/SIMDHelpers.h"
// In this one we have an array of jumps
@ -90,4 +73,4 @@ BENCHMARK_REGISTER_F(InterpolationCast, Scalar)->RangeMultiplier(2)->Range((2<<6
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();
BENCHMARK_MAIN();

View file

@ -1,32 +1,15 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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>
#include "../sfizz/SIMDHelpers.h"
// In this one we have an array of indices
@ -92,4 +75,4 @@ BENCHMARK_REGISTER_F(LoopingFixture, Scalar)->RangeMultiplier(2)->Range((2<<6),
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();
BENCHMARK_MAIN();

View file

@ -1,27 +1,10 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 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
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../sfizz/SIMDHelpers.h"
#include "SIMDHelpers.h"
#include "absl/types/span.h"
#include <benchmark/benchmark.h>
#include <cmath>
@ -36,7 +19,7 @@ public:
{
std::random_device rd {};
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0.1, 1 };
std::uniform_real_distribution<float> dist { 0.1f, 1.0f };
source = std::vector<float>(state.range(0));
result = std::vector<float>(state.range(0));
std::generate(source.begin(), source.end(), [&]() { return dist(gen); });
@ -162,4 +145,4 @@ BENCHMARK_REGISTER_F(MyFixture, SIMDSin)->RangeMultiplier(4)->Range(1 << 6, 1 <<
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_MAIN();
BENCHMARK_MAIN();

View file

@ -1,27 +1,10 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 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
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../sfizz/SIMDHelpers.h"
#include "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <cmath>
#include <iostream>
@ -87,4 +70,4 @@ BENCHMARK_REGISTER_F(MeanArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 <<
BENCHMARK_REGISTER_F(MeanArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MeanArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MeanArray, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,27 +1,10 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 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
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../sfizz/SIMDHelpers.h"
#include "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <cmath>
#include <iostream>
@ -87,4 +70,4 @@ BENCHMARK_REGISTER_F(MeanSquaredArray, Scalar)->RangeMultiplier(4)->Range(1 << 2
BENCHMARK_REGISTER_F(MeanSquaredArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MeanSquaredArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MeanSquaredArray, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,33 +1,16 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
class MultiplyAdd : public benchmark::Fixture {
public:
@ -93,4 +76,4 @@ BENCHMARK_REGISTER_F(MultiplyAdd, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 <
BENCHMARK_REGISTER_F(MultiplyAdd, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyAdd, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyAdd, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,34 +1,17 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
#include "../sfizz/Config.h"
#include "Config.h"
#include "absl/types/span.h"
class PanArray : public benchmark::Fixture {
@ -36,7 +19,7 @@ public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0.001, 1 };
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));
@ -93,4 +76,4 @@ BENCHMARK_DEFINE_F(PanArray, BlockOps)(benchmark::State& state) {
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();
BENCHMARK_MAIN();

View file

@ -1,30 +1,13 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 <absl/algorithm/container.h>
#include "../sfizz/SIMDHelpers.h"
#include "absl/types/span.h"
constexpr int bigNumber { 2399132 };
@ -33,8 +16,8 @@ class IterOffset : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state [[maybe_unused]]) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0.001, 1 };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0.001f, 1.0f };
std::uniform_int_distribution<int> jumpDist { 0, 3 };
source = std::vector<float>(bigNumber);
result.resize(state.range(0));
@ -57,7 +40,7 @@ public:
BENCHMARK_DEFINE_F(IterOffset, Pointers)(benchmark::State& state) {
for (auto _ : state)
{
{
sfz::diff<int>(offsets, absl::MakeSpan(jumps));
auto jump = jumps.begin();
auto in = source.begin();
@ -71,7 +54,7 @@ BENCHMARK_DEFINE_F(IterOffset, Pointers)(benchmark::State& state) {
BENCHMARK_DEFINE_F(IterOffset, Offsets)(benchmark::State& state) {
for (auto _ : state)
{
{
auto offset = offsets.begin();
auto out = result.begin();
while (offset < offsets.end())
@ -83,4 +66,4 @@ BENCHMARK_DEFINE_F(IterOffset, Offsets)(benchmark::State& state) {
// Register the function as a benchmark
BENCHMARK_REGISTER_F(IterOffset, Pointers)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK_REGISTER_F(IterOffset, Offsets)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,30 +1,13 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
#include "../sfizz/Buffer.h"
#include "Buffer.h"
static void Dummy(benchmark::State& state) {
@ -133,6 +116,63 @@ static void MulSIMDUnaligned(benchmark::State& state) {
}
}
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);
}
}
// Register the function as a benchmark
BENCHMARK(Dummy)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK(LinearScalar)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
@ -143,4 +183,8 @@ 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_MAIN();
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();

141
benchmarks/BM_readChunk.cpp Normal file
View file

@ -0,0 +1,141 @@
// 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_WAV_IMPLEMENTATION
#include "dr_wav.h"
#include "AudioBuffer.h"
#include <memory>
#ifndef NDEBUG
#include <iostream>
#endif
class FileFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& /* state */) {
rootPath = getPath() / "sample1.wav";
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 = std::make_unique<sfz::AudioBuffer<float>>(sndfile.channels(), sndfile.frames());
}
void TearDown(const ::benchmark::State& state [[maybe_unused]]) {
}
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, JustRead)(benchmark::State& state) {
for (auto _ : state)
{
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
sndfile.readf(buffer.data(), numFrames);
sfz::readInterleaved<float>(buffer, output->getSpan(0), output->getSpan(1));
}
}
BENCHMARK_DEFINE_F(FileFixture, AllocInside)(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<float>(
absl::MakeSpan(buffer).first(read),
output->getSpan(0).subspan(framesRead),
output->getSpan(1).subspan(framesRead)
);
framesRead += read;
}
}
}
BENCHMARK_DEFINE_F(FileFixture, AllocOutside)(benchmark::State& state) {
auto chunkSize = static_cast<size_t>(state.range(0));
sfz::Buffer<float> buffer { chunkSize * sndfile.channels() };
for (auto _ : state)
{
size_t framesRead = 0;
sndfile.seek(0, SEEK_SET);
while(framesRead < numFrames)
{
auto read = sndfile.readf(buffer.data(), chunkSize);
sfz::readInterleaved<float>(
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));
drwav wav;
if (!drwav_init_file(&wav, rootPath.c_str(), nullptr)) {
#ifndef NDEBUG
std::cerr << "Can't find path" << '\n';
#endif
std::terminate();
}
sfz::Buffer<float> buffer { chunkSize * wav.channels };
for (auto _ : state)
{
size_t framesRead = 0;
drwav_seek_to_first_pcm_frame(&wav);
while(framesRead < numFrames)
{
auto read = drwav_read_pcm_frames_f32(&wav, chunkSize, buffer.data());
sfz::readInterleaved<float>(
absl::MakeSpan(buffer).first(read),
output->getSpan(0).subspan(framesRead),
output->getSpan(1).subspan(framesRead)
);
framesRead += read;
}
}
}
BENCHMARK_REGISTER_F(FileFixture, JustRead);
BENCHMARK_REGISTER_F(FileFixture, AllocInside)->RangeMultiplier(4)->Range((1 << 8), (1 << 16));
BENCHMARK_REGISTER_F(FileFixture, AllocOutside)->RangeMultiplier(4)->Range((1 << 8), (1 << 16));
BENCHMARK_REGISTER_F(FileFixture, DrWavChunked)->RangeMultiplier(4)->Range((1 << 8), (1 << 16));
BENCHMARK_MAIN();

View file

@ -1,29 +1,12 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
#include "../sfizz/Buffer.h"
#include <algorithm>
#include <numeric>
#include <absl/types/span.h>
@ -96,4 +79,4 @@ BENCHMARK(Scalar_Unaligned)->Range((8<<10), (8<<20));
BENCHMARK(SSE_Unaligned)->Range((8<<10), (8<<20));
BENCHMARK(Scalar_Unaligned_2)->Range((8<<10), (8<<20));
BENCHMARK(SSE_Unaligned_2)->Range((8<<10), (8<<20));
BENCHMARK_MAIN();
BENCHMARK_MAIN();

471
benchmarks/BM_resample.cpp Normal file
View file

@ -0,0 +1,471 @@
// 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 "Buffer.h"
#include "AudioBuffer.h"
#include "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <memory>
#include <samplerate.h>
#include <sndfile.hh>
#include "ghc/filesystem.hpp"
#include "hiir/Upsampler2xFpu.h"
constexpr std::array<double, 12> coeffsStage2x {
0.036681502163648017,
0.13654762463195771,
0.27463175937945411,
0.42313861743656667,
0.56109869787919475,
0.67754004997416162,
0.76974183386322659,
0.83988962484963803,
0.89226081800387891,
0.9315419599631839,
0.96209454837808395,
0.98781637073289708
};
constexpr std::array<double, 4> coeffsStage4x {
0.042448989488488006,
0.17072114107630679,
0.39329183835224008,
0.74569514831986694
};
constexpr std::array<double, 3> coeffsStage8x {
0.055748680811302048,
0.24305119574153092,
0.6466991311926823
};
template<bool SIMD=false>
void upsample2xStage(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xFpu<coeffsStage2x.size()> upsampler;
upsampler.set_coefs(coeffsStage2x.data());
upsampler.process_block(output.data(), input.data(), static_cast<long>(input.size()));
}
template<bool SIMD=false>
void upsample4xStage(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xFpu<coeffsStage4x.size()> upsampler;
upsampler.set_coefs(coeffsStage4x.data());
upsampler.process_block(output.data(), input.data(), static_cast<long>(input.size()));
}
template<bool SIMD=false>
void upsample8xStage(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xFpu<coeffsStage8x.size()> upsampler;
upsampler.set_coefs(coeffsStage8x.data());
upsampler.process_block(output.data(), input.data(), static_cast<long>(input.size()));
}
#if defined(__x86_64__) || defined(__i386__)
#include "hiir/Upsampler2xSse.h"
template<>
void upsample2xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xSse<coeffsStage2x.size()> upsampler;
upsampler.set_coefs(coeffsStage2x.data());
upsampler.process_block(output.data(), input.data(), static_cast<long>(input.size()));
}
template<>
void upsample4xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xSse<coeffsStage4x.size()> upsampler;
upsampler.set_coefs(coeffsStage4x.data());
upsampler.process_block(output.data(), input.data(), static_cast<long>(input.size()));
}
template<>
void upsample8xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xSse<coeffsStage8x.size()> upsampler;
upsampler.set_coefs(coeffsStage8x.data());
upsampler.process_block(output.data(), input.data(), static_cast<long>(input.size()));
}
#elif defined(__arm__) || defined (__aarch64__)
#include "hiir/Upsampler2xNeon.h"
template<>
void upsample2xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xNeon<coeffsStage2x.size()> upsampler;
upsampler.set_coefs(coeffsStage2x.data());
upsampler.process_block(output.data(), input.data(), static_cast<long>(input.size()));
}
template<>
void upsample4xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xNeon<coeffsStage4x.size()> upsampler;
upsampler.set_coefs(coeffsStage4x.data());
upsampler.process_block(output.data(), input.data(), static_cast<long>(input.size()));
}
template<>
void upsample8xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xNeon<coeffsStage8x.size()> upsampler;
upsampler.set_coefs(coeffsStage8x.data());
upsampler.process_block(output.data(), input.data(), static_cast<long>(input.size()));
}
#else
template<>
void upsample2xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
upsample2xStage<false>(input, output);
}
template<>
void upsample4xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
upsample4xStage<false>(input, output);
}
template<>
void upsample8xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
upsample8xStage<false>(input, output);
}
#endif
template <class T, bool SIMD=false>
std::unique_ptr<sfz::AudioBuffer<T>> upsample2x(const sfz::AudioBuffer<T>& buffer)
{
// auto tempBuffer = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 2);
for (size_t channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
upsample2xStage<SIMD>(buffer.getConstSpan(channelIdx), outputBuffer->getSpan(channelIdx));
}
return outputBuffer;
}
template <class T, bool SIMD=false>
std::unique_ptr<sfz::AudioBuffer<T>> upsample4x(const sfz::AudioBuffer<T>& buffer)
{
auto tempBuffer = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 4);
for (size_t channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
upsample2xStage<SIMD>(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer));
upsample4xStage<SIMD>(absl::MakeConstSpan(*tempBuffer), outputBuffer->getSpan(channelIdx));
}
return outputBuffer;
}
template <class T, bool SIMD=false>
std::unique_ptr<sfz::AudioBuffer<T>> upsample8x(const sfz::AudioBuffer<T>& buffer)
{
auto tempBuffer2x = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
auto tempBuffer4x = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 4);
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 8);
for (size_t channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
upsample2xStage<SIMD>(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer2x));
upsample4xStage<SIMD>(absl::MakeConstSpan(*tempBuffer2x), absl::MakeSpan(*tempBuffer4x));
upsample8xStage<SIMD>(absl::MakeConstSpan(*tempBuffer4x), outputBuffer->getSpan(channelIdx));
}
return outputBuffer;
}
class SndFile : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& /* state */)
{
const auto rootPath = getPath() / "sample1.wav";
if (!ghc::filesystem::exists(rootPath)) {
#ifndef NDEBUG
std::cerr << "Can't find path" << '\n';
#endif
std::terminate();
}
SndfileHandle sndfile(rootPath.c_str());
numFrames = sndfile.frames();
numChannels = sndfile.channels();
interleavedBuffer = std::make_unique<sfz::Buffer<float>>(numChannels * numFrames);
sndfile.readf(interleavedBuffer->data(), sndfile.frames());
}
void TearDown(const ::benchmark::State& state [[maybe_unused]])
{
}
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
}
size_t numChannels;
size_t numFrames;
std::unique_ptr<sfz::Buffer<float>> interleavedBuffer;
};
BENCHMARK_DEFINE_F(SndFile, HIIR2X_scalar)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample2x<float, false>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, HIIR4X_scalar)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample4x<float, false>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, HIIR8X_scalar)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample8x<float, false>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, HIIR2X_vector)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample2x<float, true>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, HIIR4X_vector)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample4x<float, true>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, HIIR8X_vector)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample8x<float, true>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, SRC2x_BEST)(benchmark::State& state)
{
for (auto _ : state) {
auto intermediateBuffer = std::make_unique<sfz::Buffer<float>>(2 * numChannels * numFrames);
SRC_DATA srcData;
srcData.data_in = interleavedBuffer->data();
srcData.data_out = intermediateBuffer->data();
srcData.src_ratio = 2.0;
srcData.input_frames = static_cast<long>(numFrames);
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_BEST_QUALITY, static_cast<int>(numChannels));
auto outBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, SRC2x_MEDIUM)(benchmark::State& state)
{
for (auto _ : state) {
auto intermediateBuffer = std::make_unique<sfz::Buffer<float>>(2 * numChannels * numFrames);
SRC_DATA srcData;
srcData.data_in = interleavedBuffer->data();
srcData.data_out = intermediateBuffer->data();
srcData.src_ratio = 2.0;
srcData.input_frames = static_cast<long>(numFrames);
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_MEDIUM_QUALITY, static_cast<int>(numChannels));
auto outBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, SRC2x_FASTEST)(benchmark::State& state)
{
for (auto _ : state) {
auto intermediateBuffer = std::make_unique<sfz::Buffer<float>>(2 * numChannels * numFrames);
SRC_DATA srcData;
srcData.data_in = interleavedBuffer->data();
srcData.data_out = intermediateBuffer->data();
srcData.src_ratio = 2.0;
srcData.input_frames = static_cast<long>(numFrames);
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_FASTEST, static_cast<int>(numChannels));
auto outBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, SRC4x_BEST)(benchmark::State& state)
{
for (auto _ : state) {
auto intermediateBuffer = std::make_unique<sfz::Buffer<float>>(2 * numChannels * numFrames);
SRC_DATA srcData;
srcData.data_in = interleavedBuffer->data();
srcData.data_out = intermediateBuffer->data();
srcData.src_ratio = 4.0;
srcData.input_frames = static_cast<long>(numFrames);
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_BEST_QUALITY, static_cast<int>(numChannels));
auto outBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, SRC4x_MEDIUM)(benchmark::State& state)
{
for (auto _ : state) {
auto intermediateBuffer = std::make_unique<sfz::Buffer<float>>(2 * numChannels * numFrames);
SRC_DATA srcData;
srcData.data_in = interleavedBuffer->data();
srcData.data_out = intermediateBuffer->data();
srcData.src_ratio = 4.0;
srcData.input_frames = static_cast<long>(numFrames);
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_MEDIUM_QUALITY, static_cast<int>(numChannels));
auto outBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, SRC4x_FASTEST)(benchmark::State& state)
{
for (auto _ : state) {
auto intermediateBuffer = std::make_unique<sfz::Buffer<float>>(2 * numChannels * numFrames);
SRC_DATA srcData;
srcData.data_in = interleavedBuffer->data();
srcData.data_out = intermediateBuffer->data();
srcData.src_ratio = 4.0;
srcData.input_frames = static_cast<long>(numFrames);
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_FASTEST, static_cast<int>(numChannels));
auto outBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, SRC8x_BEST)(benchmark::State& state)
{
for (auto _ : state) {
auto intermediateBuffer = std::make_unique<sfz::Buffer<float>>(2 * numChannels * numFrames);
SRC_DATA srcData;
srcData.data_in = interleavedBuffer->data();
srcData.data_out = intermediateBuffer->data();
srcData.src_ratio = 8.0;
srcData.input_frames = static_cast<long>(numFrames);
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_BEST_QUALITY, static_cast<int>(numChannels));
auto outBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, SRC8x_MEDIUM)(benchmark::State& state)
{
for (auto _ : state) {
auto intermediateBuffer = std::make_unique<sfz::Buffer<float>>(2 * numChannels * numFrames);
SRC_DATA srcData;
srcData.data_in = interleavedBuffer->data();
srcData.data_out = intermediateBuffer->data();
srcData.src_ratio = 8.0;
srcData.input_frames = static_cast<long>(numFrames);
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_MEDIUM_QUALITY, static_cast<int>(numChannels));
auto outBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, SRC8x_FASTEST)(benchmark::State& state)
{
for (auto _ : state) {
auto intermediateBuffer = std::make_unique<sfz::Buffer<float>>(2 * numChannels * numFrames);
SRC_DATA srcData;
srcData.data_in = interleavedBuffer->data();
srcData.data_out = intermediateBuffer->data();
srcData.src_ratio = 8.0;
srcData.input_frames = static_cast<long>(numFrames);
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_FASTEST, static_cast<int>(numChannels));
auto outBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_DEFINE_F(SndFile, HIIR8X_default)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = std::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample8x<float>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
}
BENCHMARK_REGISTER_F(SndFile, HIIR2X_scalar);
BENCHMARK_REGISTER_F(SndFile, HIIR4X_scalar);
BENCHMARK_REGISTER_F(SndFile, HIIR8X_scalar);
BENCHMARK_REGISTER_F(SndFile, HIIR2X_vector);
BENCHMARK_REGISTER_F(SndFile, HIIR4X_vector);
BENCHMARK_REGISTER_F(SndFile, HIIR8X_vector);
BENCHMARK_REGISTER_F(SndFile, SRC2x_BEST);
BENCHMARK_REGISTER_F(SndFile, SRC4x_BEST);
BENCHMARK_REGISTER_F(SndFile, SRC8x_BEST);
BENCHMARK_REGISTER_F(SndFile, SRC2x_MEDIUM);
BENCHMARK_REGISTER_F(SndFile, SRC4x_MEDIUM);
BENCHMARK_REGISTER_F(SndFile, SRC2x_FASTEST);
BENCHMARK_REGISTER_F(SndFile, SRC8x_MEDIUM);
BENCHMARK_REGISTER_F(SndFile, SRC4x_FASTEST);
BENCHMARK_REGISTER_F(SndFile, SRC8x_FASTEST);
BENCHMARK_REGISTER_F(SndFile, HIIR8X_default);
BENCHMARK_MAIN();

View file

@ -0,0 +1,191 @@
// 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 "Buffer.h"
#include "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <sndfile.hh>
#include "ghc/filesystem.hpp"
#include "Oversampler.h"
#include "AudioBuffer.h"
#include <memory>
#include <iostream>
constexpr std::array<double, 12> coeffsStage2x {
0.036681502163648017,
0.13654762463195771,
0.27463175937945411,
0.42313861743656667,
0.56109869787919475,
0.67754004997416162,
0.76974183386322659,
0.83988962484963803,
0.89226081800387891,
0.9315419599631839,
0.96209454837808395,
0.98781637073289708
};
constexpr std::array<double, 4> coeffsStage4x {
0.042448989488488006,
0.17072114107630679,
0.39329183835224008,
0.74569514831986694
};
constexpr std::array<double, 3> coeffsStage8x {
0.055748680811302048,
0.24305119574153092,
0.6466991311926823
};
#if defined(__x86_64__) || defined(__i386__)
#include "hiir/Upsampler2xSse.h"
using Upsampler2x = hiir::Upsampler2xSse<coeffsStage2x.size()>;
using Upsampler4x = hiir::Upsampler2xSse<coeffsStage4x.size()>;
using Upsampler8x = hiir::Upsampler2xSse<coeffsStage8x.size()>;
#elif defined(__arm__) || defined(__aarch64__)
#include "hiir/Upsampler2xNeon.h"
using Upsampler2x = hiir::Upsampler2xNeon<coeffsStage2x.size()>;
using Upsampler4x = hiir::Upsampler2xNeon<coeffsStage4x.size()>;
using Upsampler8x = hiir::Upsampler2xNeon<coeffsStage8x.size()>;
#else
#include "hiir/Upsampler2xFpu.h"
using Upsampler2x = hiir::Upsampler2xFpu<coeffsStage2x.size()>;
using Upsampler4x = hiir::Upsampler2xFpu<coeffsStage4x.size()>;
using Upsampler8x = hiir::Upsampler2xFpu<coeffsStage8x.size()>;
#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 = std::make_unique<sfz::AudioBuffer<float>>(sndfile.channels(), numFrames * 4);
}
void TearDown(const ::benchmark::State& state [[maybe_unused]]) {
}
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 { 0 };
};
BENCHMARK_DEFINE_F(FileFixture, NoResampling)(benchmark::State& state) {
for (auto _ : state)
{
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
sndfile.readf(buffer.data(), sndfile.frames());
sfz::readInterleaved<float>(buffer, output->getSpan(0), output->getSpan(1));
}
}
BENCHMARK_DEFINE_F(FileFixture, ResampleAtOnce)(benchmark::State& state) {
for (auto _ : state)
{
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
sfz::Buffer<float> temp { numFrames * 4 };
Upsampler2x upsampler2x;
Upsampler4x upsampler4x;
upsampler2x.set_coefs(coeffsStage2x.data());
upsampler4x.set_coefs(coeffsStage4x.data());
sndfile.readf(buffer.data(), numFrames);
sfz::readInterleaved<float>(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));
upsampler2x.clear_buffers();
upsampler4x.clear_buffers();
upsampler2x.process_block(temp.data(), output->channelReader(1), static_cast<long>(numFrames));
upsampler4x.process_block(output->channelWriter(1), temp.data(), static_cast<long>(numFrames * 2));
}
}
BENCHMARK_DEFINE_F(FileFixture, ResampleInChunks)(benchmark::State& state) {
for (auto _ : state)
{
auto chunkSize = static_cast<size_t>(state.range(0));
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
sfz::Buffer<float> leftInput { chunkSize };
sfz::Buffer<float> rightInput { chunkSize };
sfz::Buffer<float> chunk { chunkSize * 2 };
sndfile.readf(buffer.data(), numFrames);
auto bufferSpan = absl::MakeSpan(buffer);
auto leftSpan = absl::MakeSpan(leftInput);
auto rightSpan = absl::MakeSpan(rightInput);
auto chunkSpan = absl::MakeSpan(chunk);
Upsampler2x upsampler2xLeft;
Upsampler2x upsampler2xRight;
Upsampler4x upsampler4xLeft;
Upsampler4x upsampler4xRight;
upsampler2xLeft.set_coefs(coeffsStage2x.data());
upsampler2xRight.set_coefs(coeffsStage2x.data());
upsampler4xLeft.set_coefs(coeffsStage4x.data());
upsampler4xRight.set_coefs(coeffsStage4x.data());
size_t inputFrameCounter { 0 };
size_t outputFrameCounter { 0 };
while(inputFrameCounter < numFrames)
{
// std::cout << "Input frames: " << inputFrameCounter << "/" << numFrames << '\n';
const auto thisChunkSize = std::min(chunkSize, numFrames - inputFrameCounter);
const auto bufferChunk = bufferSpan.subspan(
inputFrameCounter * sndfile.channels(),
thisChunkSize * sndfile.channels()
);
sfz::readInterleaved<float>(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));
upsampler2xRight.process_block(chunkSpan.data(), rightSpan.data(), static_cast<long>(thisChunkSize));
upsampler4xRight.process_block(output->channelWriter(1) + outputFrameCounter, chunkSpan.data(), static_cast<long>(thisChunkSize * 2));
inputFrameCounter += chunkSize;
outputFrameCounter += chunkSize * 4;
}
}
}
BENCHMARK_REGISTER_F(FileFixture, NoResampling);
BENCHMARK_REGISTER_F(FileFixture, ResampleAtOnce);
BENCHMARK_REGISTER_F(FileFixture, ResampleInChunks)->RangeMultiplier(4)->Range((1 << 4), (1 << 16));
BENCHMARK_MAIN();

View file

@ -1,32 +1,15 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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>
#include "../sfizz/SIMDHelpers.h"
// In this one we have an array of indices
@ -91,4 +74,4 @@ BENCHMARK_REGISTER_F(SaturatingFixture, Scalar)->RangeMultiplier(2)->Range((2<<6
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();
BENCHMARK_MAIN();

View file

@ -1,33 +1,16 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
class SubArray : public benchmark::Fixture {
public:
@ -82,4 +65,4 @@ BENCHMARK_REGISTER_F(SubArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 1
BENCHMARK_REGISTER_F(SubArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(SubArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(SubArray, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();
BENCHMARK_MAIN();

95
benchmarks/BM_wavfile.cpp Normal file
View file

@ -0,0 +1,95 @@
// 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 "Buffer.h"
#include <benchmark/benchmark.h>
#include <sndfile.hh>
#define DR_WAV_IMPLEMENTATION
#include "dr_wav.h"
#include "ghc/filesystem.hpp"
#include <memory>
#ifndef NDEBUG
#include <iostream>
#endif
// #include "libnyquist/Decoders.h"
class FileFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state [[maybe_unused]]) {
filePath1 = getPath() / "sample1.wav";
filePath2 = getPath() / "sample2.wav";
filePath3 = getPath() / "sample3.wav";
if ( !ghc::filesystem::exists(filePath1)
|| !ghc::filesystem::exists(filePath2)
|| !ghc::filesystem::exists(filePath3)) {
#ifndef NDEBUG
std::cerr << "Can't find path" << '\n';
#endif
std::terminate();
}
}
void TearDown(const ::benchmark::State& state [[maybe_unused]]) {
}
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::Buffer<float>> buffer;
ghc::filesystem::path filePath1;
ghc::filesystem::path filePath2;
ghc::filesystem::path filePath3;
};
BENCHMARK_DEFINE_F(FileFixture, SndFile)(benchmark::State& state) {
for (auto _ : state)
{
SndfileHandle sndfile(filePath1.c_str());
buffer = std::make_unique<sfz::Buffer<float>>(sndfile.channels() * sndfile.frames());
sndfile.readf(buffer->data(), sndfile.frames());
}
}
BENCHMARK_DEFINE_F(FileFixture, DrWav)(benchmark::State& state) {
for (auto _ : state)
{
drwav wav;
if (!drwav_init_file(&wav, filePath2.c_str(), nullptr)) {
#ifndef NDEBUG
std::cerr << "Can't find path" << '\n';
#endif
std::terminate();
}
buffer = std::make_unique<sfz::Buffer<float>>(wav.channels * wav.totalPCMFrameCount);
drwav_read_pcm_frames_f32(&wav, wav.totalPCMFrameCount, buffer->data());
}
}
// BENCHMARK_DEFINE_F(FileFixture, LibNyquist)(benchmark::State& state) {
// for (auto _ : state)
// {
// nqr::AudioData data;
// nqr::NyquistIO loader;
// loader.Load(&data, filePath3.string());
// benchmark::DoNotOptimize(data);
// }
// }
BENCHMARK_REGISTER_F(FileFixture, SndFile);
BENCHMARK_REGISTER_F(FileFixture, DrWav);
// BENCHMARK_REGISTER_F(FileFixture, LibNyquist);
BENCHMARK_MAIN();

View file

@ -1,29 +1,12 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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 "../sfizz/SIMDHelpers.h"
#include "../sfizz/Buffer.h"
#include <algorithm>
#include <numeric>
#include <absl/types/span.h>
@ -106,4 +89,4 @@ BENCHMARK(Unaligned_Interleaved_Write)->Range((8<<10), (8<<20));
BENCHMARK(Unaligned_Interleaved_Write_SSE)->Range((8<<10), (8<<20));
BENCHMARK(Unaligned_Interleaved_Write_2)->Range((8<<10), (8<<20));
BENCHMARK(Unaligned_Interleaved_Write_SSE_2)->Range((8<<10), (8<<20));
BENCHMARK_MAIN();
BENCHMARK_MAIN();

View file

@ -1,95 +1,133 @@
project(sfizz)
# Check SIMD
include(CheckIncludeFiles)
CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_H)
CHECK_INCLUDE_FILES(intrin.h HAVE_INTRIN_H)
CHECK_INCLUDE_FILES(arm_neon.h HAVE_ARM_NEON_H)
# SIMD checks
if (HAVE_X86INTRIN_H AND UNIX)
add_compile_options(-DHAVE_X86INTRIN_H)
set(SFIZZ_SIMD_SOURCES ../sfizz/SIMDSSE.cpp)
elseif (HAVE_INTRIN_H AND WIN32)
add_compile_options(/DHAVE_INTRIN_H)
set(SFIZZ_SIMD_SOURCES ../sfizz/SIMDSSE.cpp)
elseif (HAVE_ARM_NEON_H AND UNIX)
add_compile_options(-DHAVE_ARM_NEON_H)
add_compile_options(-mfpu=neon-fp-armv8)
add_compile_options(-march=native)
add_compile_options(-mtune=cortex-a53)
add_compile_options(-funsafe-math-optimizations)
set(SFIZZ_SIMD_SOURCES ../sfizz/SIMDNEON.cpp)
else()
set(SFIZZ_SIMD_SOURCES ../sfizz/SIMDDummy.cpp)
endif()
include (SfizzSIMDSourceFilesCheck)
set(BENCHMARK_SIMD_SOURCES ${SFIZZ_SIMD_SOURCES})
list(TRANSFORM BENCHMARK_SIMD_SOURCES PREPEND "../src/")
find_package(benchmark CONFIG REQUIRED)
add_executable(bm_opf_high_vs_low BM_OPF_high_vs_low.cpp)
target_link_libraries(bm_opf_high_vs_low benchmark absl::span)
target_link_libraries(bm_opf_high_vs_low PRIVATE absl::span benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_opf_high_vs_low PRIVATE ../src/sfizz ../src/external)
add_executable(bm_write BM_writeInterleaved.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_write benchmark absl::span)
add_executable(bm_write BM_writeInterleaved.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_write PRIVATE absl::span benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_write PRIVATE ../src/sfizz ../src/external)
add_executable(bm_read BM_readInterleaved.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_read benchmark absl::span)
add_executable(bm_read BM_readInterleaved.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_read PRIVATE absl::span benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_read PRIVATE ../src/sfizz ../src/external)
add_executable(bm_fill BM_fill.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_fill benchmark absl::span)
add_executable(bm_fill BM_fill.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_fill PRIVATE absl::span benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_fill PRIVATE ../src/sfizz ../src/external)
add_executable(bm_mathfuns BM_mathfuns.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_mathfuns benchmark absl::span)
add_executable(bm_mathfuns BM_mathfuns.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_mathfuns PRIVATE absl::span benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_mathfuns PRIVATE ../src/sfizz ../src/external)
add_executable(bm_gain BM_gain.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_gain benchmark absl::span)
add_executable(bm_gain BM_gain.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_gain PRIVATE absl::span benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_gain PRIVATE ../src/sfizz ../src/external)
add_executable(bm_looping BM_looping.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_looping benchmark absl::span absl::algorithm)
add_executable(bm_divide BM_divide.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_divide PRIVATE absl::span benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_divide PRIVATE ../src/sfizz ../src/external)
add_executable(bm_saturating BM_saturating.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_saturating benchmark absl::span absl::algorithm)
add_executable(bm_looping BM_looping.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_looping PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_looping PRIVATE ../src/sfizz ../src/external)
add_executable(bm_ramp BM_ramp.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_ramp benchmark absl::span absl::algorithm)
add_executable(bm_saturating BM_saturating.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_saturating PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_saturating PRIVATE ../src/sfizz ../src/external)
add_executable(bm_ADSR BM_ADSR.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_ADSR benchmark absl::span absl::algorithm)
add_executable(bm_ramp BM_ramp.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_ramp PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_ramp PRIVATE ../src/sfizz ../src/external)
add_executable(bm_add BM_add.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_add benchmark absl::span absl::algorithm)
add_executable(bm_ADSR BM_ADSR.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_ADSR PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_ADSR PRIVATE ../src/sfizz ../src/external)
add_executable(bm_multiplyAdd BM_multiplyAdd.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_multiplyAdd benchmark absl::span absl::algorithm)
add_executable(bm_add BM_add.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_add PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_add PRIVATE ../src/sfizz ../src/external)
add_executable(bm_subtract BM_subtract.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_subtract benchmark absl::span absl::algorithm)
add_executable(bm_multiplyAdd BM_multiplyAdd.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_multiplyAdd PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_multiplyAdd PRIVATE ../src/sfizz ../src/external)
add_executable(bm_copy BM_copy.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_copy benchmark absl::span absl::algorithm)
add_executable(bm_subtract BM_subtract.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_subtract PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_subtract PRIVATE ../src/sfizz ../src/external)
add_executable(bm_pan BM_pan.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_pan benchmark absl::span absl::algorithm)
add_executable(bm_copy BM_copy.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_copy PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_copy PRIVATE ../src/sfizz ../src/external)
add_executable(bm_mean BM_mean.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_mean benchmark absl::span absl::algorithm)
add_executable(bm_pan BM_pan.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_pan PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_pan PRIVATE ../src/sfizz ../src/external)
add_executable(bm_meanSquared BM_meanSquared.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_meanSquared benchmark absl::span absl::algorithm)
add_executable(bm_mean BM_mean.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_mean PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_mean PRIVATE ../src/sfizz ../src/external)
add_executable(bm_cumsum BM_cumsum.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_cumsum benchmark absl::span absl::algorithm)
add_executable(bm_meanSquared BM_meanSquared.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_meanSquared PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_meanSquared PRIVATE ../src/sfizz ../src/external)
add_executable(bm_diff BM_diff.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_diff benchmark absl::span absl::algorithm)
add_executable(bm_cumsum BM_cumsum.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_cumsum PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_cumsum PRIVATE ../src/sfizz ../src/external)
add_executable(bm_interpolationCast BM_interpolationCast.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_interpolationCast benchmark absl::span absl::algorithm)
add_executable(bm_diff BM_diff.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_diff PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_diff PRIVATE ../src/sfizz ../src/external)
add_executable(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_pointerIterationOrOffsets benchmark absl::span absl::algorithm)
add_executable(bm_interpolationCast BM_interpolationCast.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_interpolationCast PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_interpolationCast PRIVATE ../src/sfizz ../src/external)
add_executable(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_pointerIterationOrOffsets PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_pointerIterationOrOffsets PRIVATE ../src/sfizz ../src/external)
if (NOT WIN32)
add_executable(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_resample PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main samplerate)
sfizz_link_libsndfile(bm_resample)
target_include_directories(bm_resample PRIVATE ../src/sfizz ../src/external)
endif()
add_executable(bm_envelopes BM_envelopes.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_envelopes PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
target_include_directories(bm_envelopes PRIVATE ../src/sfizz ../src/external)
add_executable(bm_wavfile BM_wavfile.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_wavfile PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
sfizz_link_libsndfile(bm_wavfile)
target_include_directories(bm_wavfile PRIVATE ../src/sfizz ../src/external)
add_executable(bm_flacfile BM_flacfile.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_flacfile PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
sfizz_link_libsndfile(bm_flacfile)
target_include_directories(bm_flacfile PRIVATE ../src/sfizz ../src/external)
add_executable(bm_readChunk BM_readChunk.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_readChunk PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
sfizz_link_libsndfile(bm_readChunk)
target_include_directories(bm_readChunk PRIVATE ../src/sfizz ../src/external)
add_executable(bm_resampleChunk BM_resampleChunk.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_resampleChunk PRIVATE absl::span absl::algorithm benchmark::benchmark benchmark::benchmark_main)
sfizz_link_libsndfile(bm_resampleChunk)
target_include_directories(bm_resampleChunk PRIVATE ../src/sfizz ../src/external)
add_custom_target(sfizz_benchmarks)
add_dependencies(sfizz_benchmarks
bm_opf_high_vs_low
add_dependencies(sfizz_benchmarks
bm_opf_high_vs_low
bm_write
bm_pointerIterationOrOffsets
bm_read
@ -101,6 +139,7 @@ add_dependencies(sfizz_benchmarks
bm_interpolationCast
bm_mathfuns
bm_gain
bm_divide
bm_looping
bm_saturating
bm_ramp
@ -109,4 +148,20 @@ add_dependencies(sfizz_benchmarks
bm_pan
bm_subtract
bm_multiplyAdd
)
bm_readChunk
bm_resampleChunk
bm_envelopes
bm_wavfile
bm_flacfile
)
if (NOT WIN32)
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)

11060
benchmarks/dr_flac.h Normal file

File diff suppressed because it is too large Load diff

5358
benchmarks/dr_wav.h Normal file

File diff suppressed because it is too large Load diff

BIN
benchmarks/ride.wav Executable file

Binary file not shown.

BIN
benchmarks/ride2.wav Executable file

Binary file not shown.

BIN
benchmarks/sample1.flac Executable file

Binary file not shown.

BIN
benchmarks/sample1.wav Executable file

Binary file not shown.

BIN
benchmarks/sample2.flac Executable file

Binary file not shown.

BIN
benchmarks/sample2.wav Executable file

Binary file not shown.

BIN
benchmarks/sample3.flac Executable file

Binary file not shown.

BIN
benchmarks/sample3.wav Executable file

Binary file not shown.

View file

@ -1,19 +1,11 @@
project(sfizz)
project (sfizz)
###############################
add_executable(sfzprint sfzprint.cpp)
target_link_libraries(sfzprint sfizz::parser absl::flags_parse)
###############################
# Basic command line program
if(APPLE)
find_path(JACK_INCLUDE_DIR NAMES jack.h PATHS "/usr/local/include/jack")
find_library(JACK_LIBRARY NAMES jack PATHS "/usr/local/lib")
message(STATUS "include dir: " ${JACK_INCLUDE_DIR} " lib: " ${JACK_LIBRARY})
add_library(jack SHARED IMPORTED GLOBAL)
target_include_directories(jack INTERFACE ${JACK_INCLUDE_DIR})
target_link_libraries(jack INTERFACE ${JACK_LIBRARY})
if (APPLE)
include_directories (SYSTEM /usr/local/opt/jack/include)
link_directories (/usr/local/opt/jack/lib)
endif()
add_executable(sfizz_jack jack_client.cpp)
target_link_libraries(sfizz_jack sfizz::sfizz jack absl::flags_parse)
add_executable (sfizz_jack jack_client.cpp)
target_link_libraries (sfizz_jack sfizz::sfizz jack absl::flags_parse)
sfizz_enable_lto_if_needed (sfizz_jack)
install (TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)

View file

@ -21,9 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "AudioSpan.h"
#include "Synth.h"
#include "sfizz/Synth.h"
#include <absl/flags/parse.h>
#include <absl/flags/flag.h>
#include <absl/types/span.h>
#include <atomic>
#include <cstddef>
@ -64,6 +64,11 @@ 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;
}
}
static std::atomic<bool> keepRunning [[maybe_unused]] { true };
@ -88,31 +93,32 @@ int process(jack_nframes_t numFrames, void* arg [[maybe_unused]])
switch (midi::status(event.buffer[0])) {
case midi::noteOff:
DBG("[MIDI] Note " << +event.buffer[1] << " OFF at time " << event.time);
synth->noteOff(event.time, midi::channel(event.buffer[0]) + 1, event.buffer[1], event.buffer[2]);
// 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:
DBG("[MIDI] Note " << +event.buffer[1] << " ON at time " << event.time);
synth->noteOn(event.time, midi::channel(event.buffer[0]) + 1, event.buffer[1], event.buffer[2]);
// DBG("[MIDI] Note " << +event.buffer[1] << " ON at time " << event.time);
synth->noteOn(event.time, event.buffer[1], event.buffer[2]);
break;
case midi::polyphonicPressure:
DBG("[MIDI] Polyphonic pressure on at time " << event.time);
// DBG("[MIDI] Polyphonic pressure on at time " << event.time);
break;
case midi::controlChange:
DBG("[MIDI] CC " << +event.buffer[1] << " at time " << event.time);
synth->cc(event.time, midi::channel(event.buffer[0]) + 1, event.buffer[1], event.buffer[2]);
// DBG("[MIDI] CC " << +event.buffer[1] << " at time " << event.time);
synth->cc(event.time, event.buffer[1], event.buffer[2]);
break;
case midi::programChange:
DBG("[MIDI] Program change at time " << event.time);
// DBG("[MIDI] Program change at time " << event.time);
break;
case midi::channelPressure:
DBG("[MIDI] Channel pressure at time " << event.time);
// DBG("[MIDI] Channel pressure at time " << event.time);
break;
case midi::pitchBend:
DBG("[MIDI] Pitch bend at time " << event.time);
synth->pitchWheel(event.time, midi::buildAndCenterPitch(event.buffer[1], event.buffer[2]));
// DBG("[MIDI] Pitch bend at time " << event.time);
break;
case midi::systemMessage:
DBG("[MIDI] System message at time " << event.time);
// DBG("[MIDI] System message at time " << event.time);
break;
}
}
@ -130,7 +136,7 @@ int sampleBlockChanged(jack_nframes_t nframes, void* arg [[maybe_unused]])
return 0;
auto synth = reinterpret_cast<sfz::Synth*>(arg);
DBG("Sample per block changed to " << nframes);
// DBG("Sample per block changed to " << nframes);
synth->setSamplesPerBlock(nframes);
return 0;
}
@ -141,7 +147,7 @@ int sampleRateChanged(jack_nframes_t nframes, void* arg [[maybe_unused]])
return 0;
auto synth = reinterpret_cast<sfz::Synth*>(arg);
DBG("Sample rate changed to " << nframes);
// DBG("Sample rate changed to " << nframes);
synth->setSampleRate(nframes);
return 0;
}
@ -157,17 +163,41 @@ static void done(int sig [[maybe_unused]])
// exit(0);
}
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");
int main(int argc, char** argv)
{
// std::ios::sync_with_stdio(false);
auto arguments = absl::ParseCommandLine(argc, argv);
if (arguments.size() < 2) {
std::cout << "You need to specify an SFZ file to load." << '\n';
return -1;
}
auto filesToParse = absl::MakeConstSpan(arguments).subspan(1);
const std::string oversampling = absl::GetFlag(FLAGS_oversampling);
const uint32_t preload_size = absl::GetFlag(FLAGS_preload_size);
std::cout << "Flags" << '\n';
std::cout << "- Oversampling: " << oversampling << '\n';
std::cout << "- Preloaded Size: " << preload_size << '\n';
const auto factor = [&]() {
if (oversampling == "x1") return sfz::Oversampling::x1;
if (oversampling == "x2") return sfz::Oversampling::x2;
if (oversampling == "x4") return sfz::Oversampling::x4;
if (oversampling == "x8") return sfz::Oversampling::x8;
return sfz::Oversampling::x1;
}();
std::cout << "Positional arguments:";
for (auto& file : filesToParse)
std::cout << " " << file << ',';
std::cout << '\n';
sfz::Synth synth;
synth.setOversamplingFactor(factor);
synth.setPreloadSize(preload_size);
synth.loadSfzFile(filesToParse[0]);
std::cout << "==========" << '\n';
std::cout << "Total:" << '\n';
@ -254,11 +284,14 @@ int main(int argc, char** argv)
signal(SIGQUIT, done);
while (!shouldClose){
// synth.garbageCollect();
std::this_thread::sleep_for(1s);
#ifndef NDEBUG
std::cout << "Allocated buffers: " << synth.getAllocatedBuffers() << '\n';
std::cout << "Total size: " << synth.getAllocatedBytes() << '\n';
#endif
std::this_thread::sleep_for(2s);
}
std::cout << "Closing..." << '\n';
jack_client_close(client);
return 0;
}
}

View file

@ -1,93 +0,0 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Parser.h"
#include "StringViewHelpers.h"
#include <iostream>
#include <string_view>
#include <absl/flags/parse.h>
#include <absl/types/span.h>
class PrintingParser: public sfz::Parser
{
public:
int getNumRegions() const noexcept { return numRegions; }
int getNumGroups() const noexcept { return numGroups; }
int getNumMasters() const noexcept { return numMasters; }
int getNumCurves() const noexcept { return numCurves; }
protected:
void callback(absl::string_view header, const std::vector<sfz::Opcode>& members [[maybe_unused]]) final
{
switch (hash(header))
{
case hash("master"): numMasters++; break;
case hash("group"): numGroups++; break;
case hash("region"): numRegions++; break;
case hash("curve"): numCurves++; break;
}
std::cout << "[" << header << "]" << ' ';
for (auto& member: members)
{
std::cout << member.opcode << "=" << member.value;
if (member.parameter)
std::cout << " (" << (int)*member.parameter << ")";
std::cout << ' ';
}
std::cout << '\n';
}
private:
int numRegions { 0 };
int numGroups { 0 };
int numMasters { 0 };
int numCurves { 0 };
};
int main(int argc, char** argv)
{
std::ios::sync_with_stdio(false);
auto arguments = absl::ParseCommandLine(argc, argv);
auto filesToParse = absl::MakeConstSpan(arguments).subspan(1);
std::cout << "Positional arguments:";
for (auto& file: filesToParse)
std::cout << " " << file << ',';
std::cout << '\n';
PrintingParser parser;
parser.loadSfzFile(filesToParse[0]);
std::cout << "==========" << '\n';
std::cout << "Total:" << '\n';
std::cout << "\tMasters: " << parser.getNumMasters() << '\n';
std::cout << "\tGroups: " << parser.getNumGroups() << '\n';
std::cout << "\tRegions: " << parser.getNumRegions() << '\n';
std::cout << "\tCurves: " << parser.getNumCurves() << '\n';
std::cout << "==========" << '\n';
std::cout << "Included files:" << '\n';
for (auto& file: parser.getIncludedFiles())
std::cout << '\t' << file.c_str() << '\n';
std::cout << "==========" << '\n';
std::cout << "Defines:" << '\n';
for (auto& define: parser.getDefines())
std::cout << '\t' << define.first << '=' << define.second << '\n';
// spdlog::info("Done!");
return 0;
}

25
cmake/CheckIPO.cmake Normal file
View file

@ -0,0 +1,25 @@
# Added in CMake 3.9
include (CheckIPOSupported)
check_ipo_supported (RESULT result OUTPUT output)
if (CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l)
set(ENABLE_LTO OFF)
endif()
if (result AND ENABLE_LTO AND CMAKE_BUILD_TYPE STREQUAL "Release")
message (STATUS "\nLTO enabled.")
else()
if (${output})
message (WARNING "\nIPO disabled: ${output}")
else()
message (WARNING "\nIPO was disabled or not in a Release build.")
endif()
set (ENABLE_LTO OFF)
endif()
function (SFIZZ_ENABLE_LTO_IF_NEEDED TARGET)
if (${ENABLE_LTO})
message(STATUS "Enabling LTO on ${TARGET}")
set_property (TARGET ${TARGET} PROPERTY INTERPROCEDURAL_OPTIMIZATION True)
endif()
endfunction()

24
cmake/LV2Config.cmake Normal file
View file

@ -0,0 +1,24 @@
# Configuration for this plugin
# TODO: generate version from git
set (LV2PLUGIN_VERSION_MINOR 2)
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_EMAIL "paul at ferrand dot cc")
if (SFIZZ_USE_VCPKG)
set (LV2PLUGIN_SPDX_LICENSE_ID "LGPL-3.0-only")
else()
set (LV2PLUGIN_SPDX_LICENSE_ID "ISC")
endif()
if (WIN32)
set (LV2PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lv2" CACHE STRING
"Install destination for LV2 bundle [default: ${CMAKE_INSTALL_PREFIX}/lv2}]")
else()
set (LV2PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/lv2" CACHE STRING
"Install destination for LV2 bundle [default: ${CMAKE_INSTALL_PREFIX}/lib/lv2}]")
endif()

75
cmake/SfizzConfig.cmake Normal file
View file

@ -0,0 +1,75 @@
# Do not override the C++ standard if set to more than 14
if (NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 14)
set(CMAKE_CXX_STANDARD 14)
endif()
# Export the compile_commands.json file
set (CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Only install what's explicitely said
set (CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
set (CMAKE_CXX_VISIBILITY_PRESET hidden)
set (CMAKE_VISIBILITY_INLINES_HIDDEN ON)
# Add required flags for the builds
if (UNIX)
add_compile_options(-Wall)
add_compile_options(-Wextra)
add_compile_options(-ffast-math)
add_compile_options(-fno-omit-frame-pointer) # For debugging purposes
endif()
if (WIN32)
set(CMAKE_CXX_STANDARD 17)
add_compile_options(/Zc:__cplusplus)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
if (WIN32 OR SFIZZ_USE_VCPKG)
find_package(LibSndFile REQUIRED)
find_path(SNDFILE_INCLUDE_DIR sndfile.hh)
endif()
function(SFIZZ_LINK_LIBSNDFILE TARGET)
if (WIN32 OR SFIZZ_USE_VCPKG)
target_link_libraries (${TARGET} PRIVATE sndfile-static)
target_include_directories(${TARGET} PUBLIC ${SNDFILE_INCLUDE_DIR})
else()
target_link_libraries(${TARGET} PRIVATE sndfile)
endif()
endfunction(SFIZZ_LINK_LIBSNDFILE)
# 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")
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()
# Don't show build information when building a different project
function (show_build_info_if_needed)
if (CMAKE_PROJECT_NAME STREQUAL "sfizz")
message (STATUS "
Project name: ${PROJECT_NAME}
Build type: ${CMAKE_BUILD_TYPE}
Build using LTO: ${ENABLE_LTO}
Build as shared library: ${SFIZZ_SHARED}
Build JACK stand-alone client: ${SFIZZ_JACK}
Build LV2 plug-in: ${SFIZZ_LV2}
Build benchmarks: ${SFIZZ_BENCHMARKS}
Build tests: ${SFIZZ_TESTS}
Install prefix: ${CMAKE_INSTALL_PREFIX}
LV2 destination directory: ${LV2PLUGIN_INSTALL_DIR}
Compiler CXX debug flags: ${CMAKE_CXX_FLAGS_DEBUG}
Compiler CXX release flags: ${CMAKE_CXX_FLAGS_RELEASE}
Compiler CXX min size flags: ${CMAKE_CXX_FLAGS_MINSIZEREL}
")
endif()
endfunction()
find_package (Threads REQUIRED)

View file

@ -0,0 +1,27 @@
# Check SIMD
include (CheckIncludeFiles)
CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_H)
CHECK_INCLUDE_FILES(intrin.h HAVE_INTRIN_H)
if (!APPLE)
CHECK_INCLUDE_FILES (arm_neon.h HAVE_ARM_NEON_H)
endif()
# SIMD checks
if (HAVE_X86INTRIN_H AND UNIX)
add_compile_options (-DHAVE_X86INTRIN_H)
set (SFIZZ_SIMD_SOURCES sfizz/SIMDSSE.cpp)
elseif (HAVE_INTRIN_H AND WIN32)
add_compile_options (/DHAVE_INTRIN_H)
set (SFIZZ_SIMD_SOURCES sfizz/SIMDSSE.cpp)
elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7l")
add_compile_options (-DHAVE_ARM_NEON_H)
add_compile_options (-mfpu=neon)
add_compile_options (-march=native)
add_compile_options (-mtune=cortex-a53)
set (SFIZZ_SIMD_SOURCES sfizz/SIMDNEON.cpp)
else()
set (SFIZZ_SIMD_SOURCES sfizz/SIMDDummy.cpp)
endif()
set (SFIZZ_SOURCES ${SFIZZ_SOURCES} ${SFIZZ_SIMD_SOURCES})

View file

@ -0,0 +1,187 @@
<doxygenlayout version="1.0">
<!-- Navigation index tabs for HTML output -->
<navindex>
<tab type="mainpage" visible="no" title=""/>
<tab type="pages" visible="yes" title="" intro=""/>
<tab type="modules" visible="yes" title="Categories" intro=""/>
<tab type="namespaces" visible="yes" title="">
<tab type="namespacelist" visible="yes" title="" intro=""/>
<tab type="namespacemembers" visible="yes" title="" intro=""/>
</tab>
<tab type="classes" visible="yes" title="">
<tab type="classlist" visible="yes" title="" intro=""/>
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="hierarchy" visible="yes" title="" intro=""/>
<tab type="classmembers" visible="yes" title="" intro=""/>
</tab>
<tab type="files" visible="yes" title="">
<tab type="filelist" visible="yes" title="" intro=""/>
<tab type="globals" visible="yes" title="" intro=""/>
</tab>
<tab type="examples" visible="yes" title="" intro=""/>
</navindex>
<!-- Layout definition for a class page -->
<class>
<briefdescription visible="no"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<inheritancegraph visible="$CLASS_GRAPH"/>
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
<detaileddescription title=""/>
<memberdecl>
<membergroups visible="yes"/>
<nestedclasses visible="yes" title=""/>
<publictypes title=""/>
<publicslots title=""/>
<signals title=""/>
<publicmethods title=""/>
<publicstaticmethods title=""/>
<publicattributes title=""/>
<publicstaticattributes title=""/>
<protectedtypes title=""/>
<protectedslots title=""/>
<protectedmethods title=""/>
<protectedstaticmethods title=""/>
<protectedattributes title=""/>
<protectedstaticattributes title=""/>
<packagetypes title=""/>
<packagemethods title=""/>
<packagestaticmethods title=""/>
<packageattributes title=""/>
<packagestaticattributes title=""/>
<properties title=""/>
<events title=""/>
<privatetypes title=""/>
<privateslots title=""/>
<privatemethods title=""/>
<privatestaticmethods title=""/>
<privateattributes title=""/>
<privatestaticattributes title=""/>
<friends title=""/>
<related title="" subtitle=""/>
</memberdecl>
<allmemberslink visible="yes"/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<constructors title=""/>
<functions title=""/>
<related title=""/>
<variables title=""/>
<properties title=""/>
<events title=""/>
</memberdef>
<usedfiles visible="$SHOW_USED_FILES"/>
<authorsection visible="yes"/>
</class>
<!-- Layout definition for a namespace page -->
<namespace>
<briefdescription visible="no"/>
<detaileddescription title=""/>
<memberdecl>
<membergroups visible="yes"/>
<nestednamespaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdecl>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection visible="yes"/>
</namespace>
<!-- Layout definition for a file page -->
<file>
<briefdescription visible="no"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="$INCLUDE_GRAPH"/>
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
<sourcelink visible="yes"/>
<detaileddescription title=""/>
<memberdecl>
<membergroups visible="yes"/>
<classes visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdecl>
<memberdef>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection/>
</file>
<!-- Layout definition for a group page -->
<group>
<briefdescription visible="no"/>
<groupgraph visible="$GROUP_GRAPHS"/>
<detaileddescription title=""/>
<memberdecl>
<membergroups visible="yes"/>
<nestedgroups visible="yes" title=""/>
<dirs visible="yes" title=""/>
<files visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
</memberdecl>
<memberdef>
<pagedocs/>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
</memberdef>
<authorsection visible="yes"/>
</group>
<!-- Layout definition for a directory page -->
<directory>
<briefdescription visible="yes"/>
<directorygraph visible="yes"/>
<detaileddescription title=""/>
<memberdecl>
<dirs visible="yes"/>
<files visible="yes"/>
</memberdecl>
</directory>
</doxygenlayout>

View file

@ -0,0 +1,12 @@
<hr>
<footer>
<p style="text-align:left">Copyright &copy; 2019 Paul Ferrand
<span style="float:right">Generated for sfizz by
<a href="http://www.doxygen.org/index.html"
target="_new">Doxygen</a> $doxygenversion</span>
</p>
</footer>
</div>
<script src="./../assets/js/scripts.min.js"></script>
</body>
</html>

View file

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Home - Sfizz</title>
<meta name="author" content="Paul Ferrand">
<meta name="web-author" content="RedTide">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Atom Feeds -->
<link href="./../atom.xml" rel="alternate" title="News"
type="application/atom+xml" />
<!-- Favicon Generator Icons -->
<link rel="apple-touch-icon" sizes="180x180"
href="./../assets/ico/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="./../assets/ico/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="./../assets/ico/favicon-16x16.png">
<link rel="manifest" href="./../assets/ico/site.webmanifest">
<link rel="mask-icon" href="./../assets/ico/safari-pinned-tab.svg"
color="#5bbad5">
<link rel="shortcut icon" href="./../assets/ico/favicon.ico">
<meta name="msapplication-TileColor"
content="#da532c">
<meta name="msapplication-config" content="./../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>
$treeview
$search
$mathjax
<link href="$relpath$$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<!-- Stylesheets -->
<link href="./../assets/css/style.min.css"
rel="stylesheet" media="screen">
<!-- Font Awesome Icons -->
<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>
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color:#222">
<a class="navbar-brand" href="./../"><img src="./../assets/img/logo.png"
width="30" height="30" class="d-inline-block">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="./../status"><i class="fas fa-tasks fa-fw"
aria-hidden="true"></i>&nbsp;Status</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./../downloads"><i class="fas fa-download fa-fw"
aria-hidden="true"></i>&nbsp;Downloads</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./../contacts"><i class="fas fa-at fa-fw"
aria-hidden="true"></i>&nbsp;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>&nbsp;Home</a>
</li>
</ul>
</div>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
$searchbox
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</nav>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->

View file

@ -0,0 +1,198 @@
#page_container {
position: relative;
margin: 0;
padding: 0;
height: auto !important;
height: 100%;
min-height: 100%;
}
div.contents, div.searchresults {
margin-top: 10px;
margin-right: 12px;
padding-bottom: 70px;
}
#projectlogo {
text-align: left;
}
#projectnumber {
font-size: 120%;
font-family: Tahoma, Arial, sans-serif;
text-align: right;
padding: 0.5em 1em;
}
.tabs {
font-size: 14px;
}
.tabs2, .tabs3 {
font-size: 12px;
}
.navpath ul {
font-size: 12px;
}
h1, h2, h3, h4, h5, h6 {
color: #002D88;
font-weight: normal;
margin-top: 1em;
margin-bottom: 0.5em;
padding-top: 8px;
padding-bottom: 4px;
width: 100%;
}
h1 {
font-size: 150%;
border-bottom: 1px solid #3276FF;
}
h2 {
font-size: 135%;
margin-top: 0.75em;
}
h3 {
font-size: 120%;
margin-top: 0.5em;
}
h4 {
font-size: 100%;
margin-top: 0.5em;
}
div.headertitle h1 {
margin: 10px 2px;
border: none;
padding: 0;
width: auto;
color: black;
font-weight: bold;
}
div.toc h3 {
font-size: 14px;
}
div.toc li {
font-size: 12px;
line-height: 1.3;
padding-left: 14px;
}
img.logo {
float: right;
margin: 20px;
}
div.logo {
float: right;
margin: 20px;
}
table.directory {
line-height: 1.5;
}
.icon {
line-height: 1.25;
}
div.appearance {
margin: 1em 0em;
}
div.appearance table {
margin: 0.5em 0em;
width: 100%;
text-align: center;
}
div.appearance img {
margin: 0.5em;
}
div.appearance .caption {
font-style: italic;
font-weight: normal;
font-size: 90%;
}
div.appearance_brief table {
width: 100%;
table-layout: fixed;
text-align: center;
border-collapse: collapse;
}
div.appearance_brief table td:first-child {
width: 20em;
text-align: left;
padding-left: 2em;
}
div.appearance_brief table td {
border-style: none solid solid none;
border-width: 1px;
border-color: lightblue;
}
td.green { color: green; }
td.orange { color: #ff8000; }
td.red { color: red; }
span.literal {
text-decoration: none;
font-weight: bold;
font-family: monospace, fixed;
}
/* we make all the following <span> tags render the text just like
the standard Doxygen @remarks, @see tags do, to obtain a uniform
look and feel */
span.itemdef, span.lib, span.category, span.stdobj, span.styles,
span.events, span.flags, span.appearance, span.impl, span.avail {
font-weight: bold;
line-height: 130%;
}
span.style, span.event, span.flag {
font-weight: bold;
color: #880000;
}
div.styleDesc, div.eventDesc, div.flagDesc {
margin-left: 3%;
margin-bottom: 1ex;
}
div.eventHandler {
margin: 1em;
text-indent: 3%;
}
div.eventHandler span {
padding: 5px;
background-color: #eeeeee;
font-family: monospace, fixed;
}
code {
font-size: 110%;
color: #444444;
}
address.footer {
position: absolute;
bottom: 0;
margin: 0;
padding: 10px 0;
width: 100%;
border-top: 1px solid #0043CC;
background-image: url('nav_h.png');
background-repeat: repeat-x;
background-color: #F4F8FF;
}
address.footer small {
padding: 0 10px;
}

View file

@ -0,0 +1,154 @@
# Global view of the engine
The sfizz engine is basically a "Synth" object that takes an SFZ file in, receives MIDI-type events
and is able to render audio through successive calls to a callback function. This is in line with
the way most audio applications and plugins are working. A high-level overview is presented in the
following diagram.
```
C and C++ API entry point
|
|
|
|
+--------v-------+
| |
+-------------------------- Synth -----------------------------+
| | | |
| +----------------+ |
| | |
+--------v------+ | +---------v--------+
| | +---------v---------+ | |
| Region list | | Common resources | | Voice pool |
| | | and state | | |
+---------------+ | ----------------- | +------------------+
Built from the SFZ file | File pool |
| Envelope pool | The voices are the polyphony
Each region is a semi-passive | LFO pool | of the synth. They are idle
description object that can | Buffer pool | and they get activated by the
decide whether it is "active" | Midi state | synth to play a region on a
or not depending on the chain | ... | specific event. They are then
of MIDI events it receives. +-------------------+ "linked" to the region while
Once activated, a voice is There are a number of common it is played, and reset to
chosen to play the region until resources that are needed for their idle state when they
it ends naturally or through all the regions and in parti- are done playing the region.
note-offs or off-groups. cular the voices. This includes
all the (preloaded) files for
the SFZ instrument, but will
include in the future the EG
and LFOs that are needed to
achieve compliance with the
SFZ v2 specification. This will
also include a temporary buffer
holder that voices may share.
A common resource of importance
is the MIDI state: note durations
are needed for some opcodes --
for example rt_decay -- and
triggering velocities too.
```
The Synth, Voices and Regions form the bulk of the code complexity. The rest of the engine is dedicated of
mostly helper classes to enable easy management of floating-point buffers in which the audio data is held,
signal processing and accelerated (SIMD) computations, and abstractions that are specific to the SFZ format
such as envelope generators, curves or LFOs.
# Parsing the SFZ files
The sfz file logic is pretty simple and well defined. The sfzformat.com website contains an extensive documentation
on it. At its core, an SFZ file describes a list of `region` objects on which a certain number of "opcodes" will
apply. Opcodes can determine the sample played, the event conditions that will trigger the sample such as the range
of notes, channels, velocities, the processing to apply on the sample while playing, and many more things. It is
also possible to describe a `group` of regions, as well as exclusive groups that will shut off other regions that may
already be playing. There are also `master` groups, and `global` opcodes and some other types.
All the opcodes are declared within a header, in a pseudo-xml markup language that looks like this
```sfz
<global> volume=6
<control> set_cc4=5
<region> key=36 sample=kick.wav
```
Here we have 3 headers (`global`, `control` and `region`) and each header holds some opcodes. All of these opcodes
have a value---for example the volume is equal to 6 in the `global` header. Some opcodes also have parameters.
The `control` header holds an opcode `set_cc` with the parameter `4` and value `5`. The parameter here is the CC to set,
and the value at which to set it is 5.
The parsing logic of sfizz is handled through a base class called Parser---a very original choice. This parser has
a virtual callback that gets called whenever a header description is "complete", along with a list of opcodes that
apply to the header. Subclassing the Parser then allows to build different SFZ handlers, from full-blown synths as
with sfizz to simpler things such as printers (see in particular https://github.com/sfztools/sfz-flat/). If we look
at the core of the latter example, it will look something like the following
```cpp
class PrintingParser: public sfz::Parser
{
protected:
void callback(absl::string_view header, const std::vector<sfz::Opcode>& members) final
{
switch (hash(header)) // The hash(...) function transforms strings to large integers
{
case hash("global"): // It is also compile-time defined, which allows to do switch-case
// statements on strings, something that is usually not possible
globalMembers = members; // We save the global headers since they apply to the next
// region (and groups and masters)
masterMembers.clear();
groupMembers.clear();
break;
case hash("master"):
masterMembers = members; // So on
groupMembers.clear();
break;
case hash("group"):
groupMembers = members; // .. and so forth
break;
case hash("region"):
std::cout << "<" << header << ">" << ' '; // Now we print the region along with all the opcodes
// we memorized from earlier headers.
printMembers(globalMembers);
printMembers(masterMembers);
printMembers(groupMembers);
printMembers(members);
std::cout << '\n';
break;
default:
std::cout << "<" << header << ">" << ' ';
printMembers(members);
std::cout << '\n';
break;
}
}
private:
std::vector<sfz::Opcode> globalMembers;
std::vector<sfz::Opcode> masterMembers;
std::vector<sfz::Opcode> groupMembers;
void printMembers(const std::vector<sfz::Opcode>& members)
{
for (auto& member: members)
{
std::cout << member.opcode;
if (member.parameter)
std::cout << +*member.parameter;
std::cout << "=" << member.value;
std::cout << ' ';
}
}
};
```
The main function is then quite straightforward and we call a function from the Parser class that loads a file
```cpp
PrintingParser parser;
parser.loadSfzFile("my_sfz_file.sfz");
```
If you circle back to the parser you will see that opcodes are stored in an `Opcode` class. This class does some parsing
itself and separates the opcode name itself, parameters if any, and the value. Opcodes are very cheap to copy and pass
around because they only refer to characters in the file that are stored inside the `Parser` class, so feel free to
create vectors of them and move them around.
Note that you may also derive the loadSfzFile method if you have any processing you need to do before the actual parsing happens.
# Building the region list in sfizz
The callback method from sfizz is actually quite similar to the one shown above, except that instead of printing the region
we actually fill a big structure from it.

5
doxygen/pages/index.md Normal file
View file

@ -0,0 +1,5 @@
# sfizz
SFZ file format library
[public API](sfizz_8h.html)

1
external/benchmark vendored

@ -1 +0,0 @@
Subproject commit 090faecb454fbd6e6e17a75ef8146acb037118d4

37
lv2/CMakeLists.txt Normal file
View file

@ -0,0 +1,37 @@
set (LV2PLUGIN_PRJ_NAME "${PROJECT_NAME}_lv2")
# Set the build directory as <build_dir>/lv2/<plugin_name>.lv2/
set (PROJECT_BINARY_DIR "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.lv2")
# LV2 plugin specific settings
include (LV2Config)
# Keep non build turtle files in IDE
set (LV2PLUGIN_TTL_SRC_FILES
manifest.ttl.in
${PROJECT_NAME}.ttl.in
)
add_library (${LV2PLUGIN_PRJ_NAME} SHARED ${PROJECT_NAME}.c ${LV2PLUGIN_TTL_SRC_FILES})
target_link_libraries (${LV2PLUGIN_PRJ_NAME} ${PROJECT_NAME}::${PROJECT_NAME})
target_include_directories(${LV2PLUGIN_PRJ_NAME} PRIVATE .)
sfizz_enable_lto_if_needed (${LV2PLUGIN_PRJ_NAME})
# Remove the "lib" prefix, rename the target name and build it in the .lv build dir
# <build_dir>/lv2/<plugin_name>_lv2.<ext> to
# <build_dir>/lv2/<plugin_name>.lv2/<plugin_name>.<ext>
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES PREFIX "")
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES OUTPUT_NAME "${PROJECT_NAME}")
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
# Generate *.ttl files from *.in sources,
# create the destination directory if it doesn't exists and copy needed files
file (MAKE_DIRECTORY ${PROJECT_BINARY_DIR})
configure_file (manifest.ttl.in ${PROJECT_BINARY_DIR}/manifest.ttl)
configure_file (${PROJECT_NAME}.ttl.in ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.ttl)
configure_file (LICENSE.md.in ${PROJECT_BINARY_DIR}/LICENSE.md)
if (SFIZZ_USE_VCPKG)
file(COPY "lgpl-3.0.txt" DESTINATION ${PROJECT_BINARY_DIR})
endif()
# Installation
install (DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR})

19
lv2/LICENSE.md.in Normal file
View file

@ -0,0 +1,19 @@
The `sfizz` LV2 plugin is able to be built statically against all its components,
which is a desirable property for audio plugin hosts. These components include
`libsndfile` from Erik de Castro Lopo (http://www.mega-nerd.com/libsndfile).
`libsndfile` is distributed under the terms of the LGPL v2 or v3. As such,
statically-linked LV2 version of `sfizz` are to be distributed under the terms
of the LGPL-v3.0 license. You should be able to rebuild from source the sfizz LV2
plugin using any `libsndfile` version you wish to use.
See the file `lgpl-3.0.txt` or (https://www.gnu.org/licenses/lgpl-3.0.txt) for more
information about the LGPL v3 license.
Most of the code in the plug comes from various examples in the LV2 distribution and
example plugins. As such, the source code of the LV2 plugin version of `sfizz` is
distributed under the same terms as the LV2 specification, which is the ISC license.
Binaries of this LV2 plugin dynamically linked against e.g. a system installation
of `libsndfile` thus have to respect the terms of the ISC license and BSD 2-clause
license. Check the LICENSE.md at (@LV2PLUGIN_REPOSITORY@) file for more
information about the license of the source code of the `sfizz` library and its
contributors.

165
lv2/lgpl-3.0.txt Normal file
View file

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View file

@ -0,0 +1,71 @@
/*
Copyright 2012-2018 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "lv2/atom/atom.h"
#include "lv2/atom/forge.h"
#include "lv2/atom/util.h"
#include "lv2/urid/urid.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
char** uris = NULL;
uint32_t n_uris = 0;
static char*
copy_string(const char* str)
{
const size_t len = strlen(str);
char* dup = (char*)malloc(len + 1);
memcpy(dup, str, len + 1);
return dup;
}
static LV2_URID
urid_map(LV2_URID_Map_Handle handle, const char* uri)
{
for (uint32_t i = 0; i < n_uris; ++i) {
if (!strcmp(uris[i], uri)) {
return i + 1;
}
}
uris = (char**)realloc(uris, ++n_uris * sizeof(char*));
uris[n_uris - 1] = copy_string(uri);
return n_uris;
}
static void
free_urid_map(void)
{
for (uint32_t i = 0; i < n_uris; ++i) {
free(uris[i]);
}
free(uris);
}
static int
test_fail(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "error: ");
vfprintf(stderr, fmt, args);
va_end(args);
return 1;
}

363
lv2/lv2/atom/atom-test.c Normal file
View file

@ -0,0 +1,363 @@
/*
Copyright 2012-2015 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "lv2/atom/atom-test-utils.c"
#include "lv2/atom/atom.h"
#include "lv2/atom/forge.h"
#include "lv2/atom/util.h"
#include "lv2/urid/urid.h"
#include <stdint.h>
#include <stdlib.h>
int
main(void)
{
LV2_URID_Map map = { NULL, urid_map };
LV2_Atom_Forge forge;
lv2_atom_forge_init(&forge, &map);
LV2_URID eg_Object = urid_map(NULL, "http://example.org/Object");
LV2_URID eg_one = urid_map(NULL, "http://example.org/one");
LV2_URID eg_two = urid_map(NULL, "http://example.org/two");
LV2_URID eg_three = urid_map(NULL, "http://example.org/three");
LV2_URID eg_four = urid_map(NULL, "http://example.org/four");
LV2_URID eg_true = urid_map(NULL, "http://example.org/true");
LV2_URID eg_false = urid_map(NULL, "http://example.org/false");
LV2_URID eg_path = urid_map(NULL, "http://example.org/path");
LV2_URID eg_uri = urid_map(NULL, "http://example.org/uri");
LV2_URID eg_urid = urid_map(NULL, "http://example.org/urid");
LV2_URID eg_string = urid_map(NULL, "http://example.org/string");
LV2_URID eg_literal = urid_map(NULL, "http://example.org/literal");
LV2_URID eg_tuple = urid_map(NULL, "http://example.org/tuple");
LV2_URID eg_vector = urid_map(NULL, "http://example.org/vector");
LV2_URID eg_vector2 = urid_map(NULL, "http://example.org/vector2");
LV2_URID eg_seq = urid_map(NULL, "http://example.org/seq");
#define BUF_SIZE 1024
#define NUM_PROPS 15
uint8_t buf[BUF_SIZE];
lv2_atom_forge_set_buffer(&forge, buf, BUF_SIZE);
LV2_Atom_Forge_Frame obj_frame;
LV2_Atom* obj = lv2_atom_forge_deref(
&forge, lv2_atom_forge_object(&forge, &obj_frame, 0, eg_Object));
// eg_one = (Int)1
lv2_atom_forge_key(&forge, eg_one);
LV2_Atom_Int* one = (LV2_Atom_Int*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_int(&forge, 1));
if (one->body != 1) {
return test_fail("%d != 1\n", one->body);
}
// eg_two = (Long)2
lv2_atom_forge_key(&forge, eg_two);
LV2_Atom_Long* two = (LV2_Atom_Long*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_long(&forge, 2));
if (two->body != 2) {
return test_fail("%ld != 2\n", two->body);
}
// eg_three = (Float)3.0
lv2_atom_forge_key(&forge, eg_three);
LV2_Atom_Float* three = (LV2_Atom_Float*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_float(&forge, 3.0f));
if (three->body != 3) {
return test_fail("%f != 3\n", three->body);
}
// eg_four = (Double)4.0
lv2_atom_forge_key(&forge, eg_four);
LV2_Atom_Double* four = (LV2_Atom_Double*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_double(&forge, 4.0));
if (four->body != 4) {
return test_fail("%ld != 4\n", four->body);
}
// eg_true = (Bool)1
lv2_atom_forge_key(&forge, eg_true);
LV2_Atom_Bool* t = (LV2_Atom_Bool*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_bool(&forge, true));
if (t->body != 1) {
return test_fail("%ld != 1 (true)\n", t->body);
}
// eg_false = (Bool)0
lv2_atom_forge_key(&forge, eg_false);
LV2_Atom_Bool* f = (LV2_Atom_Bool*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_bool(&forge, false));
if (f->body != 0) {
return test_fail("%ld != 0 (false)\n", f->body);
}
// eg_path = (Path)"/foo/bar"
const char* pstr = "/foo/bar";
const uint32_t pstr_len = (uint32_t)strlen(pstr);
lv2_atom_forge_key(&forge, eg_path);
LV2_Atom_String* path = (LV2_Atom_String*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_uri(&forge, pstr, pstr_len));
char* pbody = (char*)LV2_ATOM_BODY(path);
if (strcmp(pbody, pstr)) {
return test_fail("%s != \"%s\"\n", pbody, pstr);
}
// eg_uri = (URI)"http://example.org/value"
const char* ustr = "http://example.org/value";
const uint32_t ustr_len = (uint32_t)strlen(ustr);
lv2_atom_forge_key(&forge, eg_uri);
LV2_Atom_String* uri = (LV2_Atom_String*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_uri(&forge, ustr, ustr_len));
char* ubody = (char*)LV2_ATOM_BODY(uri);
if (strcmp(ubody, ustr)) {
return test_fail("%s != \"%s\"\n", ubody, ustr);
}
// eg_urid = (URID)"http://example.org/value"
LV2_URID eg_value = urid_map(NULL, "http://example.org/value");
lv2_atom_forge_key(&forge, eg_urid);
LV2_Atom_URID* urid = (LV2_Atom_URID*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_urid(&forge, eg_value));
if (urid->body != eg_value) {
return test_fail("%u != %u\n", urid->body, eg_value);
}
// eg_string = (String)"hello"
lv2_atom_forge_key(&forge, eg_string);
LV2_Atom_String* string = (LV2_Atom_String*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_string(
&forge, "hello", strlen("hello")));
char* sbody = (char*)LV2_ATOM_BODY(string);
if (strcmp(sbody, "hello")) {
return test_fail("%s != \"hello\"\n", sbody);
}
// eg_literal = (Literal)"hello"@fr
lv2_atom_forge_key(&forge, eg_literal);
LV2_Atom_Literal* literal = (LV2_Atom_Literal*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_literal(
&forge, "bonjour", strlen("bonjour"),
0, urid_map(NULL, "http://lexvo.org/id/term/fr")));
char* lbody = (char*)LV2_ATOM_CONTENTS(LV2_Atom_Literal, literal);
if (strcmp(lbody, "bonjour")) {
return test_fail("%s != \"bonjour\"\n", lbody);
}
// eg_tuple = "foo",true
lv2_atom_forge_key(&forge, eg_tuple);
LV2_Atom_Forge_Frame tuple_frame;
LV2_Atom_Tuple* tuple = (LV2_Atom_Tuple*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_tuple(&forge, &tuple_frame));
LV2_Atom_String* tup0 = (LV2_Atom_String*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_string(
&forge, "foo", strlen("foo")));
LV2_Atom_Bool* tup1 = (LV2_Atom_Bool*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_bool(&forge, true));
lv2_atom_forge_pop(&forge, &tuple_frame);
LV2_Atom* i = lv2_atom_tuple_begin(tuple);
if (lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), tuple->atom.size, i)) {
return test_fail("Tuple iterator is empty\n");
}
LV2_Atom* tup0i = i;
if (!lv2_atom_equals((LV2_Atom*)tup0, tup0i)) {
return test_fail("Corrupt tuple element 0\n");
}
i = lv2_atom_tuple_next(i);
if (lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), tuple->atom.size, i)) {
return test_fail("Premature end of tuple iterator\n");
}
LV2_Atom* tup1i = i;
if (!lv2_atom_equals((LV2_Atom*)tup1, tup1i)) {
return test_fail("Corrupt tuple element 1\n");
}
i = lv2_atom_tuple_next(i);
if (!lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), tuple->atom.size, i)) {
return test_fail("Tuple iter is not at end\n");
}
// eg_vector = (Vector<Int>)1,2,3,4
lv2_atom_forge_key(&forge, eg_vector);
int32_t elems[] = { 1, 2, 3, 4 };
LV2_Atom_Vector* vector = (LV2_Atom_Vector*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_vector(
&forge, sizeof(int32_t), forge.Int, 4, elems));
void* vec_body = LV2_ATOM_CONTENTS(LV2_Atom_Vector, vector);
if (memcmp(elems, vec_body, sizeof(elems))) {
return test_fail("Corrupt vector\n");
}
// eg_vector2 = (Vector<Int>)1,2,3,4
lv2_atom_forge_key(&forge, eg_vector2);
LV2_Atom_Forge_Frame vec_frame;
LV2_Atom_Vector* vector2 = (LV2_Atom_Vector*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_vector_head(
&forge, &vec_frame, sizeof(int32_t), forge.Int));
for (unsigned e = 0; e < sizeof(elems) / sizeof(int32_t); ++e) {
lv2_atom_forge_int(&forge, elems[e]);
}
lv2_atom_forge_pop(&forge, &vec_frame);
if (!lv2_atom_equals(&vector->atom, &vector2->atom)) {
return test_fail("Vector != Vector2\n");
}
// eg_seq = (Sequence)1, 2
lv2_atom_forge_key(&forge, eg_seq);
LV2_Atom_Forge_Frame seq_frame;
LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)lv2_atom_forge_deref(
&forge, lv2_atom_forge_sequence_head(&forge, &seq_frame, 0));
lv2_atom_forge_frame_time(&forge, 0);
lv2_atom_forge_int(&forge, 1);
lv2_atom_forge_frame_time(&forge, 1);
lv2_atom_forge_int(&forge, 2);
lv2_atom_forge_pop(&forge, &seq_frame);
lv2_atom_forge_pop(&forge, &obj_frame);
// Test equality
LV2_Atom_Int itwo = { { forge.Int, sizeof(int32_t) }, 2 };
if (lv2_atom_equals((LV2_Atom*)one, (LV2_Atom*)two)) {
return test_fail("1 == 2.0\n");
} else if (lv2_atom_equals((LV2_Atom*)one, (LV2_Atom*)&itwo)) {
return test_fail("1 == 2\n");
} else if (!lv2_atom_equals((LV2_Atom*)one, (LV2_Atom*)one)) {
return test_fail("1 != 1\n");
}
unsigned n_events = 0;
LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
if (ev->time.frames != n_events) {
return test_fail("Corrupt event %u has bad time\n", n_events);
} else if (ev->body.type != forge.Int) {
return test_fail("Corrupt event %u has bad type\n", n_events);
} else if (((LV2_Atom_Int*)&ev->body)->body != (int)n_events + 1) {
return test_fail("Event %u != %d\n", n_events, n_events + 1);
}
++n_events;
}
int n_props = 0;
LV2_ATOM_OBJECT_FOREACH((LV2_Atom_Object*)obj, prop) {
if (!prop->key) {
return test_fail("Corrupt property %u has no key\n", n_props);
} else if (prop->context) {
return test_fail("Corrupt property %u has context\n", n_props);
}
++n_props;
}
if (n_props != NUM_PROPS) {
return test_fail("Corrupt object has %u properties != %u\n",
n_props, NUM_PROPS);
}
struct {
const LV2_Atom* one;
const LV2_Atom* two;
const LV2_Atom* three;
const LV2_Atom* four;
const LV2_Atom* affirmative;
const LV2_Atom* negative;
const LV2_Atom* path;
const LV2_Atom* uri;
const LV2_Atom* urid;
const LV2_Atom* string;
const LV2_Atom* literal;
const LV2_Atom* tuple;
const LV2_Atom* vector;
const LV2_Atom* vector2;
const LV2_Atom* seq;
} matches;
memset(&matches, 0, sizeof(matches));
LV2_Atom_Object_Query q[] = {
{ eg_one, &matches.one },
{ eg_two, &matches.two },
{ eg_three, &matches.three },
{ eg_four, &matches.four },
{ eg_true, &matches.affirmative },
{ eg_false, &matches.negative },
{ eg_path, &matches.path },
{ eg_uri, &matches.uri },
{ eg_urid, &matches.urid },
{ eg_string, &matches.string },
{ eg_literal, &matches.literal },
{ eg_tuple, &matches.tuple },
{ eg_vector, &matches.vector },
{ eg_vector2, &matches.vector2 },
{ eg_seq, &matches.seq },
LV2_ATOM_OBJECT_QUERY_END
};
int n_matches = lv2_atom_object_query((LV2_Atom_Object*)obj, q);
for (int n = 0; n < 2; ++n) {
if (n_matches != n_props) {
return test_fail("Query failed, %u matches != %u\n",
n_matches, n_props);
} else if (!lv2_atom_equals((LV2_Atom*)one, matches.one)) {
return test_fail("Bad match one\n");
} else if (!lv2_atom_equals((LV2_Atom*)two, matches.two)) {
return test_fail("Bad match two\n");
} else if (!lv2_atom_equals((LV2_Atom*)three, matches.three)) {
return test_fail("Bad match three\n");
} else if (!lv2_atom_equals((LV2_Atom*)four, matches.four)) {
return test_fail("Bad match four\n");
} else if (!lv2_atom_equals((LV2_Atom*)t, matches.affirmative)) {
return test_fail("Bad match true\n");
} else if (!lv2_atom_equals((LV2_Atom*)f, matches.negative)) {
return test_fail("Bad match false\n");
} else if (!lv2_atom_equals((LV2_Atom*)path, matches.path)) {
return test_fail("Bad match path\n");
} else if (!lv2_atom_equals((LV2_Atom*)uri, matches.uri)) {
return test_fail("Bad match URI\n");
} else if (!lv2_atom_equals((LV2_Atom*)string, matches.string)) {
return test_fail("Bad match string\n");
} else if (!lv2_atom_equals((LV2_Atom*)literal, matches.literal)) {
return test_fail("Bad match literal\n");
} else if (!lv2_atom_equals((LV2_Atom*)tuple, matches.tuple)) {
return test_fail("Bad match tuple\n");
} else if (!lv2_atom_equals((LV2_Atom*)vector, matches.vector)) {
return test_fail("Bad match vector\n");
} else if (!lv2_atom_equals((LV2_Atom*)vector, matches.vector2)) {
return test_fail("Bad match vector2\n");
} else if (!lv2_atom_equals((LV2_Atom*)seq, matches.seq)) {
return test_fail("Bad match sequence\n");
}
memset(&matches, 0, sizeof(matches));
n_matches = lv2_atom_object_get((LV2_Atom_Object*)obj,
eg_one, &matches.one,
eg_two, &matches.two,
eg_three, &matches.three,
eg_four, &matches.four,
eg_true, &matches.affirmative,
eg_false, &matches.negative,
eg_path, &matches.path,
eg_uri, &matches.uri,
eg_urid, &matches.urid,
eg_string, &matches.string,
eg_literal, &matches.literal,
eg_tuple, &matches.tuple,
eg_vector, &matches.vector,
eg_vector2, &matches.vector2,
eg_seq, &matches.seq,
0);
}
free_urid_map();
return 0;
}

256
lv2/lv2/atom/atom.h Normal file
View file

@ -0,0 +1,256 @@
/*
Copyright 2008-2016 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@defgroup atom Atom
A generic value container and several data types, see
<http://lv2plug.in/ns/ext/atom> for details.
@{
*/
#ifndef LV2_ATOM_H
#define LV2_ATOM_H
#include <stddef.h>
#include <stdint.h>
#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom" ///< http://lv2plug.in/ns/ext/atom
#define LV2_ATOM_PREFIX LV2_ATOM_URI "#" ///< http://lv2plug.in/ns/ext/atom#
#define LV2_ATOM__Atom LV2_ATOM_PREFIX "Atom" ///< http://lv2plug.in/ns/ext/atom#Atom
#define LV2_ATOM__AtomPort LV2_ATOM_PREFIX "AtomPort" ///< http://lv2plug.in/ns/ext/atom#AtomPort
#define LV2_ATOM__Blank LV2_ATOM_PREFIX "Blank" ///< http://lv2plug.in/ns/ext/atom#Blank
#define LV2_ATOM__Bool LV2_ATOM_PREFIX "Bool" ///< http://lv2plug.in/ns/ext/atom#Bool
#define LV2_ATOM__Chunk LV2_ATOM_PREFIX "Chunk" ///< http://lv2plug.in/ns/ext/atom#Chunk
#define LV2_ATOM__Double LV2_ATOM_PREFIX "Double" ///< http://lv2plug.in/ns/ext/atom#Double
#define LV2_ATOM__Event LV2_ATOM_PREFIX "Event" ///< http://lv2plug.in/ns/ext/atom#Event
#define LV2_ATOM__Float LV2_ATOM_PREFIX "Float" ///< http://lv2plug.in/ns/ext/atom#Float
#define LV2_ATOM__Int LV2_ATOM_PREFIX "Int" ///< http://lv2plug.in/ns/ext/atom#Int
#define LV2_ATOM__Literal LV2_ATOM_PREFIX "Literal" ///< http://lv2plug.in/ns/ext/atom#Literal
#define LV2_ATOM__Long LV2_ATOM_PREFIX "Long" ///< http://lv2plug.in/ns/ext/atom#Long
#define LV2_ATOM__Number LV2_ATOM_PREFIX "Number" ///< http://lv2plug.in/ns/ext/atom#Number
#define LV2_ATOM__Object LV2_ATOM_PREFIX "Object" ///< http://lv2plug.in/ns/ext/atom#Object
#define LV2_ATOM__Path LV2_ATOM_PREFIX "Path" ///< http://lv2plug.in/ns/ext/atom#Path
#define LV2_ATOM__Property LV2_ATOM_PREFIX "Property" ///< http://lv2plug.in/ns/ext/atom#Property
#define LV2_ATOM__Resource LV2_ATOM_PREFIX "Resource" ///< http://lv2plug.in/ns/ext/atom#Resource
#define LV2_ATOM__Sequence LV2_ATOM_PREFIX "Sequence" ///< http://lv2plug.in/ns/ext/atom#Sequence
#define LV2_ATOM__Sound LV2_ATOM_PREFIX "Sound" ///< http://lv2plug.in/ns/ext/atom#Sound
#define LV2_ATOM__String LV2_ATOM_PREFIX "String" ///< http://lv2plug.in/ns/ext/atom#String
#define LV2_ATOM__Tuple LV2_ATOM_PREFIX "Tuple" ///< http://lv2plug.in/ns/ext/atom#Tuple
#define LV2_ATOM__URI LV2_ATOM_PREFIX "URI" ///< http://lv2plug.in/ns/ext/atom#URI
#define LV2_ATOM__URID LV2_ATOM_PREFIX "URID" ///< http://lv2plug.in/ns/ext/atom#URID
#define LV2_ATOM__Vector LV2_ATOM_PREFIX "Vector" ///< http://lv2plug.in/ns/ext/atom#Vector
#define LV2_ATOM__atomTransfer LV2_ATOM_PREFIX "atomTransfer" ///< http://lv2plug.in/ns/ext/atom#atomTransfer
#define LV2_ATOM__beatTime LV2_ATOM_PREFIX "beatTime" ///< http://lv2plug.in/ns/ext/atom#beatTime
#define LV2_ATOM__bufferType LV2_ATOM_PREFIX "bufferType" ///< http://lv2plug.in/ns/ext/atom#bufferType
#define LV2_ATOM__childType LV2_ATOM_PREFIX "childType" ///< http://lv2plug.in/ns/ext/atom#childType
#define LV2_ATOM__eventTransfer LV2_ATOM_PREFIX "eventTransfer" ///< http://lv2plug.in/ns/ext/atom#eventTransfer
#define LV2_ATOM__frameTime LV2_ATOM_PREFIX "frameTime" ///< http://lv2plug.in/ns/ext/atom#frameTime
#define LV2_ATOM__supports LV2_ATOM_PREFIX "supports" ///< http://lv2plug.in/ns/ext/atom#supports
#define LV2_ATOM__timeUnit LV2_ATOM_PREFIX "timeUnit" ///< http://lv2plug.in/ns/ext/atom#timeUnit
#define LV2_ATOM_REFERENCE_TYPE 0 ///< The special type for a reference atom
#ifdef __cplusplus
extern "C" {
#endif
/** @cond */
/** This expression will fail to compile if double does not fit in 64 bits. */
typedef char lv2_atom_assert_double_fits_in_64_bits[
((sizeof(double) <= sizeof(uint64_t)) * 2) - 1];
/** @endcond */
/**
Return a pointer to the contents of an Atom. The "contents" of an atom
is the data past the complete type-specific header.
@param type The type of the atom, e.g. LV2_Atom_String.
@param atom A variable-sized atom.
*/
#define LV2_ATOM_CONTENTS(type, atom) \
((void*)((uint8_t*)(atom) + sizeof(type)))
/**
Const version of LV2_ATOM_CONTENTS.
*/
#define LV2_ATOM_CONTENTS_CONST(type, atom) \
((const void*)((const uint8_t*)(atom) + sizeof(type)))
/**
Return a pointer to the body of an Atom. The "body" of an atom is the
data just past the LV2_Atom head (i.e. the same offset for all types).
*/
#define LV2_ATOM_BODY(atom) LV2_ATOM_CONTENTS(LV2_Atom, atom)
/**
Const version of LV2_ATOM_BODY.
*/
#define LV2_ATOM_BODY_CONST(atom) LV2_ATOM_CONTENTS_CONST(LV2_Atom, atom)
/** The header of an atom:Atom. */
typedef struct {
uint32_t size; /**< Size in bytes, not including type and size. */
uint32_t type; /**< Type of this atom (mapped URI). */
} LV2_Atom;
/** An atom:Int or atom:Bool. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
int32_t body; /**< Integer value. */
} LV2_Atom_Int;
/** An atom:Long. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
int64_t body; /**< Integer value. */
} LV2_Atom_Long;
/** An atom:Float. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
float body; /**< Floating point value. */
} LV2_Atom_Float;
/** An atom:Double. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
double body; /**< Floating point value. */
} LV2_Atom_Double;
/** An atom:Bool. May be cast to LV2_Atom. */
typedef LV2_Atom_Int LV2_Atom_Bool;
/** An atom:URID. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
uint32_t body; /**< URID. */
} LV2_Atom_URID;
/** An atom:String. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
/* Contents (a null-terminated UTF-8 string) follow here. */
} LV2_Atom_String;
/** The body of an atom:Literal. */
typedef struct {
uint32_t datatype; /**< Datatype URID. */
uint32_t lang; /**< Language URID. */
/* Contents (a null-terminated UTF-8 string) follow here. */
} LV2_Atom_Literal_Body;
/** An atom:Literal. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Literal_Body body; /**< Body. */
} LV2_Atom_Literal;
/** An atom:Tuple. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
/* Contents (a series of complete atoms) follow here. */
} LV2_Atom_Tuple;
/** The body of an atom:Vector. */
typedef struct {
uint32_t child_size; /**< The size of each element in the vector. */
uint32_t child_type; /**< The type of each element in the vector. */
/* Contents (a series of packed atom bodies) follow here. */
} LV2_Atom_Vector_Body;
/** An atom:Vector. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Vector_Body body; /**< Body. */
} LV2_Atom_Vector;
/** The body of an atom:Property (e.g. in an atom:Object). */
typedef struct {
uint32_t key; /**< Key (predicate) (mapped URI). */
uint32_t context; /**< Context URID (may be, and generally is, 0). */
LV2_Atom value; /**< Value atom header. */
/* Value atom body follows here. */
} LV2_Atom_Property_Body;
/** An atom:Property. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Property_Body body; /**< Body. */
} LV2_Atom_Property;
/** The body of an atom:Object. May be cast to LV2_Atom. */
typedef struct {
uint32_t id; /**< URID, or 0 for blank. */
uint32_t otype; /**< Type URID (same as rdf:type, for fast dispatch). */
/* Contents (a series of property bodies) follow here. */
} LV2_Atom_Object_Body;
/** An atom:Object. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Object_Body body; /**< Body. */
} LV2_Atom_Object;
/** The header of an atom:Event. Note this type is NOT an LV2_Atom. */
typedef struct {
/** Time stamp. Which type is valid is determined by context. */
union {
int64_t frames; /**< Time in audio frames. */
double beats; /**< Time in beats. */
} time;
LV2_Atom body; /**< Event body atom header. */
/* Body atom contents follow here. */
} LV2_Atom_Event;
/**
The body of an atom:Sequence (a sequence of events).
The unit field is either a URID that described an appropriate time stamp
type, or may be 0 where a default stamp type is known. For
LV2_Descriptor::run(), the default stamp type is audio frames.
The contents of a sequence is a series of LV2_Atom_Event, each aligned
to 64-bits, e.g.:
<pre>
| Event 1 (size 6) | Event 2
| | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|FRAMES |SUBFRMS|TYPE |SIZE |DATADATADATAPAD|FRAMES |SUBFRMS|...
</pre>
*/
typedef struct {
uint32_t unit; /**< URID of unit of event time stamps. */
uint32_t pad; /**< Currently unused. */
/* Contents (a series of events) follow here. */
} LV2_Atom_Sequence_Body;
/** An atom:Sequence. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Sequence_Body body; /**< Body. */
} LV2_Atom_Sequence;
/**
@}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV2_ATOM_H */

602
lv2/lv2/atom/atom.ttl Normal file
View file

@ -0,0 +1,602 @@
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://lv2plug.in/ns/ext/atom>
a owl:Ontology ;
rdfs:seeAlso <atom.h> ,
<util.h> ,
<forge.h> ,
<lv2-atom.doap.ttl> ;
lv2:documentation """
<p>An #Atom is a simple generic data container for holding any type of Plain
Old Data (POD). An #Atom can contain simple primitive types like integers,
floating point numbers, and strings; as well as structured data like lists and
dictionary-like <q>Objects</q>. Since Atoms are POD, they can be easily copied
(e.g. using <code>memcpy</code>) anywhere and are suitable for use in real-time
code.</p>
<p>Every atom starts with an LV2_Atom header, followed by the contents. This
allows code to process atoms without requiring special code for every type of
data. For example, plugins that mutually understand a type can be used
together in a host that does not understand that type, because the host is only
required to copy atoms, not interpret their contents. Similarly, plugins (such
as routers, delays, or data structures) can meaningfully process atoms of a
type unknown to them.</p>
<p>Atoms should be used anywhere values of various types must be stored or
transmitted. The port type #AtomPort can be used to transmit atoms via ports.
An #AtomPort that contains an #Sequence can be used for sample accurate event
communication, such as MIDI, and replaces the earlier event extension.</p>
<h3>Serialisation</h3>
<p>Each Atom type defines a binary format for use at runtime, but also a
serialisation that is natural to express in Turtle format. Thus, this
specification defines a powerful real-time appropriate data model, as well as a
portable way to serialise any data in that model. This is particularly useful
for inter-process communication, saving/restoring state, and describing values
in plugin data files.</p>
<h3>Custom Atom Types</h3>
<p>While it is possible to define new Atom types for any binary format, the
standard types defined here are powerful enough to describe almost anything.
Implementations SHOULD build structures out of the types provided here, rather
than define new binary formats (e.g. use #Tuple or #Object rather than
a new C <code>struct</code> type). Current implementations have support for
serialising all standard types, so new binary formats are an implementation
burden which harms interoperabilty. In particular, plugins SHOULD NOT expect
UI communication or state saving with custom Atom types to work. In general,
new Atom types should only be defined where absolutely necessary due to
performance reasons and serialisation is not a concern.</p>
""" .
atom:cType
a rdf:Property ,
owl:DatatypeProperty ,
owl:FunctionalProperty ;
rdfs:label "C type" ;
rdfs:domain rdfs:Class ;
rdfs:range lv2:Symbol ;
rdfs:comment """The identifier for a C type describing the binary representation of an Atom of this type.""" .
atom:Atom
a rdfs:Class ;
rdfs:label "Atom" ;
atom:cType "LV2_Atom" ;
lv2:documentation """
<p>Abstract base class for all atoms. An LV2_Atom has a 32-bit
<code>size</code> and <code>type</code> followed by a body of <code>size</code>
bytes. Atoms MUST be 64-bit aligned.</p>
<p>All concrete Atom types (subclasses of this class) MUST define a precise
binary layout for their body.</p>
<p>The <code>type</code> field is the URI of an Atom type mapped to an integer.
Implementations SHOULD gracefully pass through, or ignore, atoms with unknown
types.</p>
<p>All atoms are POD by definition except references, which as a special case
have <code>type = 0</code>. An Atom MUST NOT contain a Reference. It is safe
to copy any non-reference Atom with a simple <code>memcpy</code>, even if the
implementation does not understand <code>type</code>. Though this extension
reserves the type 0 for references, the details of reference handling are
currently unspecified. A future revision of this extension, or a different
extension, may define how to use non-POD data and references. Implementations
MUST NOT send references to another implementation unless the receiver is
explicitly known to support references (e.g. by supporting a feature).</p>
<p>The atom with both <code>type</code> <em>and</em> <code>size</code> 0 is
<q>null</q>, which is not considered a Reference.</p>
""" .
atom:Chunk
a rdfs:Class ,
rdfs:Datatype ;
rdfs:subClassOf atom:Atom ;
rdfs:label "Chunk of memory" ;
owl:onDatatype xsd:base64Binary ;
lv2:documentation """
<p>A chunk of memory with undefined contents. This type is used to indicate a
certain amount of space is available. For example, output ports with a
variably sized type are connected to a Chunk so the plugin knows the size of
the buffer available for writing.</p>
<p>The use of a Chunk should be constrained to a local scope, since
interpreting it is impossible without context. However, if serialised to RDF,
a Chunk may be represented directly as an xsd:base64Binary string, e.g.:</p>
<pre class="turtle-code">
[] eg:someChunk "vu/erQ=="^^xsd:base64Binary .
</pre>
""" .
atom:Number
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
rdfs:label "Number" .
atom:Int
a rdfs:Class ,
rdfs:Datatype ;
rdfs:subClassOf atom:Number ;
rdfs:label "Signed 32-bit integer" ;
atom:cType "LV2_Atom_Int" ;
owl:onDatatype xsd:int .
atom:Long
a rdfs:Class ,
rdfs:Datatype ;
rdfs:subClassOf atom:Number ;
rdfs:label "Signed 64-bit integer" ;
atom:cType "LV2_Atom_Long" ;
owl:onDatatype xsd:long .
atom:Float
a rdfs:Class ,
rdfs:Datatype ;
rdfs:subClassOf atom:Number ;
rdfs:label "32-bit floating point number" ;
atom:cType "LV2_Atom_Float" ;
owl:onDatatype xsd:float .
atom:Double
a rdfs:Class ,
rdfs:Datatype ;
rdfs:subClassOf atom:Number ;
rdfs:label "64-bit floating point number" ;
atom:cType "LV2_Atom_Double" ;
owl:onDatatype xsd:double .
atom:Bool
a rdfs:Class ,
rdfs:Datatype ;
rdfs:subClassOf atom:Atom ;
rdfs:label "Boolean" ;
atom:cType "LV2_Atom_Bool" ;
owl:onDatatype xsd:boolean ;
rdfs:comment "An Int where 0 is false and any other value is true." .
atom:String
a rdfs:Class ,
rdfs:Datatype ;
rdfs:subClassOf atom:Atom ;
rdfs:label "String" ;
atom:cType "LV2_Atom_String" ;
owl:onDatatype xsd:string ;
lv2:documentation """
<p>A UTF-8 encoded string.</p>
<p>The body of an LV2_Atom_String is a C string in UTF-8 encoding, i.e. an
array of bytes (<code>uint8_t</code>) terminated with a NULL byte
(<code>'\\0'</code>).</p>
<p>This type is for free-form strings, but SHOULD NOT be used for typed data or
text in any language. Use atom:Literal unless translating the string does not
make sense and the string has no meaningful datatype.</p>
""" .
atom:Literal
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
rdfs:label "String Literal" ;
atom:cType "LV2_Atom_Literal" ;
lv2:documentation """
<p>A UTF-8 encoded string literal, with an optional datatype or language.</p>
<p>This type is compatible with rdfs:Literal and is capable of expressing a
string in any language or a value of any type. A Literal has a
<code>datatype</code> and <code>lang</code> followed by string data in UTF-8
encoding. The length of the string data in bytes is <code>size -
sizeof(LV2_Atom_Literal)</code>, including the terminating NULL character. The
<code>lang</code> field SHOULD be a URI of the form
&lt;http://lexvo.org/id/iso639-3/LANG&gt; or
&lt;http://lexvo.org/id/iso639-1/LANG&gt; where LANG is a 3-character ISO 693-3
language code, or a 2-character ISO 693-1 language code, respectively.</p>
<p>A Literal may have a <code>datatype</code> OR a <code>lang</code>, but never
both.</p>
<p>For example, a Literal can be "Hello" in English:</p>
<pre class="c-code">
void set_to_hello_in_english(LV2_Atom_Literal* lit) {
lit->atom.type = map(expand("atom:Literal"));
lit->atom.size = 14;
lit->body.datatype = 0;
lit->body.lang = map("http://lexvo.org/id/iso639-1/en");
memcpy(LV2_ATOM_CONTENTS(LV2_Atom_Literal, lit),
"Hello",
sizeof("Hello")); // Assumes enough space
}
</pre>
<p>or a Turtle string:</p>
<pre class="c-code">
void set_to_turtle_string(LV2_Atom_Literal* lit, const char* ttl) {
lit->atom.type = map(expand("atom:Literal"));
lit->atom.size = 64;
lit->body.datatype = map("http://www.w3.org/2008/turtle#turtle");
lit->body.lang = 0;
memcpy(LV2_ATOM_CONTENTS(LV2_Atom_Literal, lit),
ttl,
strlen(ttl) + 1); // Assumes enough space
}
</pre>
""" .
atom:Path
a rdfs:Class ,
rdfs:Datatype ;
rdfs:subClassOf atom:URI ;
owl:onDatatype atom:URI ;
rdfs:label "File path string" ;
lv2:documentation """
<p>A local file path.</p>
<p>A Path is a URI reference with only a path component: no scheme, authority,
query, or fragment. In particular, paths to files in the same bundle may be
cleanly written in Turtle files as a relative URI. However, implementations
may assume any binary Path (e.g. in an event payload) is a valid file path
which can passed to system functions like fopen() directly, without any
character encoding or escape expansion required.</p>
<p>Any implemenation that creates a Path atom to transmit to another is
responsible for ensuring it is valid. A Path SHOULD always be absolute, unless
there is some mechanism in place that defines a base path. Since this is not
the case for plugin instances, effectively any Path sent to or received from a
plugin instance MUST be absolute.</p>
""" .
atom:URI
a rdfs:Class ,
rdfs:Datatype ;
rdfs:subClassOf atom:String ;
owl:onDatatype xsd:anyURI ;
rdfs:label "URI string" ;
lv2:documentation """
<p>A URI string. This is useful when a URI is needed but mapping is
inappropriate, for example with temporary or relative URIs. Since the ability
to distinguish URIs from plain strings is often necessary, URIs MUST NOT be
transmitted as atom:String.</p>
<p>This is not strictly a URI, since UTF-8 is allowed. Escaping and related
issues are the host's responsibility.</p>
""" .
atom:URID
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
rdfs:label "Integer URID" ;
atom:cType "LV2_Atom_URID" ;
lv2:documentation """
<p>An unsigned 32-bit integer mapped from a URI (e.g. with LV2_URID_Map).</p>
""" .
atom:Vector
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
rdfs:label "Vector" ;
atom:cType "LV2_Atom_Vector" ;
lv2:documentation """
<p>A homogeneous series of atom bodies with equivalent type and size.</p>
<p>An LV2_Atom_Vector is a 32-bit <code>child_size</code> and
<code>child_type</code> followed by <code>size / child_size</code> atom
bodies.</p>
<p>For example, an atom:Vector containing 42 elements of type atom:Float:</p>
<pre class="c-code">
struct VectorOf42Floats {
uint32_t size; // sizeof(LV2_Atom_Vector_Body) + (42 * sizeof(float);
uint32_t type; // map(expand("atom:Vector"))
uint32_t child_size; // sizeof(float)
uint32_t child_type; // map(expand("atom:Float"))
float elems[42];
};
</pre>
<p>Note that it is possible to construct a valid Atom for each element
of the vector, even by an implementation which does not understand
<code>child_type</code>.</p>
<p>If serialised to RDF, a Vector SHOULD have the form:</p>
<pre class="turtle-code">
eg:someVector
a atom:Vector ;
atom:childType atom:Int ;
rdf:value (
"1"^^xsd:int
"2"^^xsd:int
"3"^^xsd:int
"4"^^xsd:int
) .
</pre>
""" .
atom:Tuple
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
rdfs:label "Tuple" ;
lv2:documentation """
<p>A series of Atoms with varying <code>type</code> and <code>size</code>.</p>
<p>The body of a Tuple is simply a series of complete atoms, each aligned to
64 bits.</p>
<p>If serialised to RDF, a Tuple SHOULD have the form:</p>
<pre class="turtle-code">
eg:someVector
a atom:Tuple ;
rdf:value (
"1"^^xsd:int
"3.5"^^xsd:float
"etc"
) .
</pre>
""" .
atom:Property
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
rdfs:label "Property" ;
atom:cType "LV2_Atom_Property" ;
lv2:documentation """
<p>A property of an atom:Object. An LV2_Atom_Property has a URID
<code>key</code> and <code>context</code>, and an Atom <code>value</code>.
This corresponds to an RDF Property, where the <q>key</q> is the <q>predicate</q>
and the <q>value</q> is the object.</p>
<p>The <code>context</code> field can be used to specify a different context
for each property, where this is useful. Otherwise, it may be 0.</p>
<p>Properties generally only exist as part of an atom:Object. Accordingly,
they will typically be represented directly as properties in RDF (see
atom:Object). If this is not possible, they may be expressed as partial
reified statements, e.g.:</p>
<pre class="turtle-code">
eg:someProperty
rdf:predicate eg:theKey ;
rdf:object eg:theValue .
</pre>
""" .
atom:Object
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
rdfs:label "Object" ;
atom:cType "LV2_Atom_Object" ;
lv2:documentation """
<p>An <q>Object</q> is an atom with a set of properties. This corresponds to
an RDF Resource, and can be thought of as a dictionary with URID keys.</p>
<p>An LV2_Atom_Object body has a uint32_t <code>id</code> and
<code>type</code>, followed by a series of atom:Property bodies
(LV2_Atom_Property_Body). The LV2_Atom_Object_Body::otype field is equivalent
to a property with key rdf:type, but is included in the structure to allow for
fast dispatching.</p>
<p>Code SHOULD check for objects using lv2_atom_forge_is_object() or
lv2_atom_forge_is_blank() if a forge is available, rather than checking the
atom type directly. This will correctly handle the deprecated atom:Resource
and atom:Blank types.</p>
<p>When serialised to RDF, an Object is represented as a resource, e.g.:</p>
<pre class="turtle-code">
eg:someObject
eg:firstPropertyKey "first property value" ;
eg:secondPropertyKey "first loser" ;
eg:andSoOn "and so on" .
</pre>
""" .
atom:Resource
a rdfs:Class ;
rdfs:subClassOf atom:Object ;
rdfs:label "Resource" ;
owl:deprecated "true"^^xsd:boolean ;
atom:cType "LV2_Atom_Object" ;
lv2:documentation """
<p>This class is deprecated. Use atom:Object instead.</p>
<p>An atom:Object where the <code>id</code> field is a URID, i.e. an Object
with a URI.</p>
""" .
atom:Blank
a rdfs:Class ;
rdfs:subClassOf atom:Object ;
rdfs:label "Blank" ;
owl:deprecated "true"^^xsd:boolean ;
atom:cType "LV2_Atom_Object" ;
lv2:documentation """
<p>This class is deprecated. Use atom:Object with ID 0 instead.</p>
<p>An atom:Object where the LV2_Atom_Object::id is a blank node ID (NOT a URI).
The ID of a Blank is valid only within the context the Blank appears in. For
ports this is the context of the associated run() call, i.e. all ports share
the same context so outputs can contain IDs that correspond to IDs of blanks in
the input.</p>
""" .
atom:Sound
a rdfs:Class ;
rdfs:subClassOf atom:Vector ;
rdfs:label "Sound" ;
atom:cType "LV2_Atom_Sound" ;
lv2:documentation """
<p>An atom:Vector of atom:Float which represents an audio waveform. The format
is the same as the buffer format for lv2:AudioPort (except the size may be
arbitrary). An atom:Sound inherently depends on the sample rate, which is
assumed to be known from context. Because of this, directly serialising an
atom:Sound is probably a bad idea, use a standard format like WAV instead.</p>
""" .
atom:frameTime
a rdf:Property ,
owl:DatatypeProperty ,
owl:FunctionalProperty ;
rdfs:range xsd:decimal ;
rdfs:label "frame time" ;
lv2:documentation """
<p>Time stamp in audio frames. Typically used for events.</p>
""" .
atom:beatTime
a rdf:Property ,
owl:DatatypeProperty ,
owl:FunctionalProperty ;
rdfs:range xsd:decimal ;
rdfs:label "beat time" ;
lv2:documentation """
<p>Time stamp in beats. Typically used for events.</p>
""" .
atom:Event
a rdfs:Class ;
rdfs:label "Event" ;
atom:cType "LV2_Atom_Event" ;
lv2:documentation """
<p>An atom with a time stamp prefix, typically an element of an atom:Sequence.
Note this is not an Atom type.</p>
""" .
atom:Sequence
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
rdfs:label "Sequence" ;
atom:cType "LV2_Atom_Sequence" ;
lv2:documentation """
<p>A sequence of atom:Event, i.e. a series of time-stamped Atoms.</p>
<p>LV2_Atom_Sequence_Body.unit describes the time unit for the contained atoms.
If the unit is known from context (e.g. run() stamps are always audio frames),
this field may be zero. Otherwise, it SHOULD be either units:frame or
units:beat, in which case ev.time.frames or ev.time.beats is valid,
respectively.</p>
<p>If serialised to RDF, a Sequence has a similar form to atom:Vector, but for
brevity the elements may be assumed to be atom:Event, e.g.:</p>
<pre class="turtle-code">
eg:someSequence
a atom:Sequence ;
rdf:value (
[
atom:frameTime 1 ;
rdf:value "901A01"^^midi:MidiEvent
] [
atom:frameTime 3 ;
rdf:value "902B02"^^midi:MidiEvent
]
) .
</pre>
""" .
atom:AtomPort
a rdfs:Class ;
rdfs:subClassOf lv2:Port ;
rdfs:label "Atom Port" ;
lv2:documentation """
<p>A port which contains an atom:Atom. Ports of this type are connected to an
LV2_Atom with a type specified by atom:bufferType.</p>
<p>Output ports with a variably sized type MUST be initialised by the host
before every run() to an atom:Chunk with size set to the available space. The
plugin reads this size to know how much space is available for writing. In all
cases, the plugin MUST write a complete atom (including header) to outputs.
However, to be robust, hosts SHOULD initialise output ports to a safe sentinel
(e.g. the null Atom) before calling run().</p>
""" .
atom:bufferType
a rdf:Property ,
owl:ObjectProperty ;
rdfs:domain atom:AtomPort ;
rdfs:range rdfs:Class ;
rdfs:label "buffer type" ;
lv2:documentation """
<p>Indicates that an AtomPort may be connected to a certain Atom type. A port
MAY support several buffer types. The host MUST NOT connect a port to an Atom
with a type not explicitly listed with this property. The value of this
property MUST be a sub-class of atom:Atom. For example, an input port that is
connected directly to an LV2_Atom_Double value is described like so:</p>
<pre class="turtle-code">
&lt;plugin&gt;
lv2:port [
a lv2:InputPort , atom:AtomPort ;
atom:bufferType atom:Double ;
] .
</pre>
<p>This property only describes the types a port may be <em>directly</em>
connected to. It says nothing about the expected contents of containers. For
that, use atom:supports.</p>
""" .
atom:childType
a rdf:Property ,
owl:ObjectProperty ;
rdfs:label "child type" ;
rdfs:comment "The type of a container's children." .
atom:supports
a rdf:Property ;
rdfs:label "supports" ;
rdfs:range rdfs:Class ;
lv2:documentation """
<p>Indicates that a particular Atom type is supported.</p>
<p>This property is defined loosely, it may be used to indicate that anything
<q>supports</q> an Atom type, wherever that may be useful. It applies
<q>recursively</q> where collections are involved.</p>
<p>In particular, this property can be used to describe which event types are
expected by a port. For example, a port that receives MIDI events is described
like so:</p>
<pre class="turtle-code">
&lt;plugin&gt;
lv2:port [
a lv2:InputPort , atom:AtomPort ;
atom:bufferType atom:Sequence ;
atom:supports midi:MidiEvent ;
] .
</pre>
""" .
atom:eventTransfer
a ui:PortProtocol ;
rdfs:label "event transfer" ;
lv2:documentation """
<p>Transfer of individual events in a port buffer. Useful as the
<code>format</code> for a LV2UI_Write_Function.</p>
<p>This protocol applies to ports which contain events, usually in an
atom:Sequence. The host must transfer each individual event to the recipient.
The format of the received data is an LV2_Atom, there is no timestamp
header.</p>
""" .
atom:atomTransfer
a ui:PortProtocol ;
rdfs:label "atom transfer" ;
lv2:documentation """
<p>Transfer of the complete atom in a port buffer. Useful as the
<code>format</code> for a LV2UI_Write_Function.</p>
<p>This protocol applies to atom ports. The host must transfer the complete
atom contained in the port, including header.</p>
""" .

View file

@ -0,0 +1,237 @@
/*
Copyright 2019 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "lv2/atom/atom-test-utils.c"
#include "lv2/atom/atom.h"
#include "lv2/atom/forge.h"
#include "lv2/atom/util.h"
#include "lv2/urid/urid.h"
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
static int
test_string_overflow(void)
{
#define MAX_CHARS 15
static const size_t capacity = sizeof(LV2_Atom_String) + MAX_CHARS + 1;
static const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
uint8_t* buf = (uint8_t*)malloc(capacity);
LV2_URID_Map map = { NULL, urid_map };
LV2_Atom_Forge forge;
lv2_atom_forge_init(&forge, &map);
// Check that writing increasingly long strings fails at the right point
for (size_t count = 0; count < MAX_CHARS; ++count) {
lv2_atom_forge_set_buffer(&forge, buf, capacity);
const LV2_Atom_Forge_Ref ref =
lv2_atom_forge_string(&forge, str, count);
if (!ref) {
return test_fail("Failed to write %zu byte string\n", count);
}
}
// Failure writing to an exactly full forge
LV2_Atom_Forge_Ref ref = 0;
if ((ref = lv2_atom_forge_string(&forge, str, MAX_CHARS + 1))) {
return test_fail("Successfully wrote past end of buffer\n");
}
// Failure writing body after successfully writing header
lv2_atom_forge_set_buffer(&forge, buf, sizeof(LV2_Atom) + 1);
if ((ref = lv2_atom_forge_string(&forge, "AB", 2))) {
return test_fail("Successfully wrote atom header past end\n");
}
free(buf);
return 0;
}
static int
test_literal_overflow(void)
{
static const size_t capacity = sizeof(LV2_Atom_Literal) + 2;
uint8_t* buf = (uint8_t*)malloc(capacity);
LV2_URID_Map map = { NULL, urid_map };
LV2_Atom_Forge_Ref ref = 0;
LV2_Atom_Forge forge;
lv2_atom_forge_init(&forge, &map);
// Failure in atom header
lv2_atom_forge_set_buffer(&forge, buf, 1);
if ((ref = lv2_atom_forge_literal(&forge, "A", 1, 0, 0))) {
return test_fail("Successfully wrote atom header past end\n");
}
// Failure in literal header
lv2_atom_forge_set_buffer(&forge, buf, sizeof(LV2_Atom) + 1);
if ((ref = lv2_atom_forge_literal(&forge, "A", 1, 0, 0))) {
return test_fail("Successfully wrote literal header past end\n");
}
// Success (only room for one character + null terminator)
lv2_atom_forge_set_buffer(&forge, buf, capacity);
if (!(ref = lv2_atom_forge_literal(&forge, "A", 1, 0, 0))) {
return test_fail("Failed to write small enough literal\n");
}
// Failure in body
lv2_atom_forge_set_buffer(&forge, buf, capacity);
if ((ref = lv2_atom_forge_literal(&forge, "AB", 2, 0, 0))) {
return test_fail("Successfully wrote literal body past end\n");
}
free(buf);
return 0;
}
static int
test_sequence_overflow(void)
{
static const size_t size = sizeof(LV2_Atom_Sequence) + 6 * sizeof(LV2_Atom);
LV2_URID_Map map = { NULL, urid_map };
// Test over a range that fails in the sequence header and event components
for (size_t capacity = 1; capacity < size; ++capacity) {
uint8_t* buf = (uint8_t*)malloc(capacity);
LV2_Atom_Forge forge;
lv2_atom_forge_init(&forge, &map);
lv2_atom_forge_set_buffer(&forge, buf, capacity);
LV2_Atom_Forge_Frame frame;
LV2_Atom_Forge_Ref ref =
lv2_atom_forge_sequence_head(&forge, &frame, 0);
assert(capacity >= sizeof(LV2_Atom_Sequence) || !frame.ref);
assert(capacity >= sizeof(LV2_Atom_Sequence) || !ref);
lv2_atom_forge_frame_time(&forge, 0);
lv2_atom_forge_int(&forge, 42);
lv2_atom_forge_pop(&forge, &frame);
free(buf);
}
return 0;
}
static int
test_vector_head_overflow(void)
{
static const size_t size = sizeof(LV2_Atom_Vector) + 3 * sizeof(LV2_Atom);
LV2_URID_Map map = { NULL, urid_map };
// Test over a range that fails in the vector header and elements
for (size_t capacity = 1; capacity < size; ++capacity) {
uint8_t* buf = (uint8_t*)malloc(capacity);
LV2_Atom_Forge forge;
lv2_atom_forge_init(&forge, &map);
lv2_atom_forge_set_buffer(&forge, buf, capacity);
LV2_Atom_Forge_Frame frame;
LV2_Atom_Forge_Ref ref = lv2_atom_forge_vector_head(
&forge, &frame, sizeof(int32_t), forge.Int);
assert(capacity >= sizeof(LV2_Atom_Vector) || !frame.ref);
assert(capacity >= sizeof(LV2_Atom_Vector) || !ref);
lv2_atom_forge_int(&forge, 1);
lv2_atom_forge_int(&forge, 2);
lv2_atom_forge_int(&forge, 3);
lv2_atom_forge_pop(&forge, &frame);
free(buf);
}
return 0;
}
static int
test_vector_overflow(void)
{
static const size_t size = sizeof(LV2_Atom_Vector) + 3 * sizeof(LV2_Atom);
static const int32_t vec[] = { 1, 2, 3 };
LV2_URID_Map map = { NULL, urid_map };
// Test over a range that fails in the vector header and elements
for (size_t capacity = 1; capacity < size; ++capacity) {
uint8_t* buf = (uint8_t*)malloc(capacity);
LV2_Atom_Forge forge;
lv2_atom_forge_init(&forge, &map);
lv2_atom_forge_set_buffer(&forge, buf, capacity);
LV2_Atom_Forge_Ref ref = lv2_atom_forge_vector(
&forge, sizeof(int32_t), forge.Int, 3, vec);
assert(capacity >= sizeof(LV2_Atom_Vector) || !ref);
free(buf);
}
return 0;
}
static int
test_tuple_overflow(void)
{
static const size_t size = sizeof(LV2_Atom_Tuple) + 3 * sizeof(LV2_Atom);
LV2_URID_Map map = { NULL, urid_map };
// Test over a range that fails in the tuple header and elements
for (size_t capacity = 1; capacity < size; ++capacity) {
uint8_t* buf = (uint8_t*)malloc(capacity);
LV2_Atom_Forge forge;
lv2_atom_forge_init(&forge, &map);
lv2_atom_forge_set_buffer(&forge, buf, capacity);
LV2_Atom_Forge_Frame frame;
LV2_Atom_Forge_Ref ref = lv2_atom_forge_tuple(&forge, &frame);
assert(capacity >= sizeof(LV2_Atom_Tuple) || !frame.ref);
assert(capacity >= sizeof(LV2_Atom_Tuple) || !ref);
lv2_atom_forge_int(&forge, 1);
lv2_atom_forge_float(&forge, 2.0f);
lv2_atom_forge_string(&forge, "three", 5);
lv2_atom_forge_pop(&forge, &frame);
free(buf);
}
return 0;
}
int
main(void)
{
const int ret = test_string_overflow() || test_literal_overflow() ||
test_sequence_overflow() || test_vector_head_overflow() ||
test_vector_overflow() || test_tuple_overflow();
free_urid_map();
return ret;
}

696
lv2/lv2/atom/forge.h Normal file
View file

@ -0,0 +1,696 @@
/*
Copyright 2008-2016 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@file forge.h An API for constructing LV2 atoms.
This file provides an API for constructing Atoms which makes it relatively
simple to build nested atoms of arbitrary complexity without requiring
dynamic memory allocation.
The API is based on successively appending the appropriate pieces to build a
complete Atom. The size of containers is automatically updated. Functions
that begin a container return (via their frame argument) a stack frame which
must be popped when the container is finished.
All output is written to a user-provided buffer or sink function. This
makes it popssible to create create atoms on the stack, on the heap, in LV2
port buffers, in a ringbuffer, or elsewhere, all using the same API.
This entire API is realtime safe if used with a buffer or a realtime safe
sink, except lv2_atom_forge_init() which is only realtime safe if the URI
map function is.
Note these functions are all static inline, do not take their address.
This header is non-normative, it is provided for convenience.
*/
/**
@defgroup forge Forge
@ingroup atom
@{
*/
#ifndef LV2_ATOM_FORGE_H
#define LV2_ATOM_FORGE_H
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
#include "lv2/core/attributes.h"
#include "lv2/urid/urid.h"
#include <assert.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
// Disable deprecation warnings for Blank and Resource
LV2_DISABLE_DEPRECATION_WARNINGS
/** Handle for LV2_Atom_Forge_Sink. */
typedef void* LV2_Atom_Forge_Sink_Handle;
/** A reference to a chunk of written output. */
typedef intptr_t LV2_Atom_Forge_Ref;
/** Sink function for writing output. See lv2_atom_forge_set_sink(). */
typedef LV2_Atom_Forge_Ref
(*LV2_Atom_Forge_Sink)(LV2_Atom_Forge_Sink_Handle handle,
const void* buf,
uint32_t size);
/** Function for resolving a reference. See lv2_atom_forge_set_sink(). */
typedef LV2_Atom*
(*LV2_Atom_Forge_Deref_Func)(LV2_Atom_Forge_Sink_Handle handle,
LV2_Atom_Forge_Ref ref);
/** A stack frame used for keeping track of nested Atom containers. */
typedef struct _LV2_Atom_Forge_Frame {
struct _LV2_Atom_Forge_Frame* parent;
LV2_Atom_Forge_Ref ref;
} LV2_Atom_Forge_Frame;
/** A "forge" for creating atoms by appending to a buffer. */
typedef struct {
uint8_t* buf;
uint32_t offset;
uint32_t size;
LV2_Atom_Forge_Sink sink;
LV2_Atom_Forge_Deref_Func deref;
LV2_Atom_Forge_Sink_Handle handle;
LV2_Atom_Forge_Frame* stack;
LV2_URID Blank LV2_DEPRECATED;
LV2_URID Bool;
LV2_URID Chunk;
LV2_URID Double;
LV2_URID Float;
LV2_URID Int;
LV2_URID Long;
LV2_URID Literal;
LV2_URID Object;
LV2_URID Path;
LV2_URID Property;
LV2_URID Resource LV2_DEPRECATED;
LV2_URID Sequence;
LV2_URID String;
LV2_URID Tuple;
LV2_URID URI;
LV2_URID URID;
LV2_URID Vector;
} LV2_Atom_Forge;
static inline void
lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size);
/**
Initialise `forge`.
URIs will be mapped using `map` and stored, a reference to `map` itself is
not held.
*/
static inline void
lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map)
{
lv2_atom_forge_set_buffer(forge, NULL, 0);
forge->Blank = map->map(map->handle, LV2_ATOM__Blank);
forge->Bool = map->map(map->handle, LV2_ATOM__Bool);
forge->Chunk = map->map(map->handle, LV2_ATOM__Chunk);
forge->Double = map->map(map->handle, LV2_ATOM__Double);
forge->Float = map->map(map->handle, LV2_ATOM__Float);
forge->Int = map->map(map->handle, LV2_ATOM__Int);
forge->Long = map->map(map->handle, LV2_ATOM__Long);
forge->Literal = map->map(map->handle, LV2_ATOM__Literal);
forge->Object = map->map(map->handle, LV2_ATOM__Object);
forge->Path = map->map(map->handle, LV2_ATOM__Path);
forge->Property = map->map(map->handle, LV2_ATOM__Property);
forge->Resource = map->map(map->handle, LV2_ATOM__Resource);
forge->Sequence = map->map(map->handle, LV2_ATOM__Sequence);
forge->String = map->map(map->handle, LV2_ATOM__String);
forge->Tuple = map->map(map->handle, LV2_ATOM__Tuple);
forge->URI = map->map(map->handle, LV2_ATOM__URI);
forge->URID = map->map(map->handle, LV2_ATOM__URID);
forge->Vector = map->map(map->handle, LV2_ATOM__Vector);
}
/** Access the Atom pointed to by a reference. */
static inline LV2_Atom*
lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref)
{
return forge->buf ? (LV2_Atom*)ref : forge->deref(forge->handle, ref);
}
/**
@name Object Stack
@{
*/
/**
Push a stack frame.
This is done automatically by container functions (which take a stack frame
pointer), but may be called by the user to push the top level container when
writing to an existing Atom.
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_push(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Frame* frame,
LV2_Atom_Forge_Ref ref)
{
frame->parent = forge->stack;
frame->ref = ref;
if (ref) {
forge->stack = frame; // Don't push, so walking the stack is always safe
}
return ref;
}
/** Pop a stack frame. This must be called when a container is finished. */
static inline void
lv2_atom_forge_pop(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame)
{
if (frame->ref) {
// If frame has a valid ref, it must be the top of the stack
assert(frame == forge->stack);
forge->stack = frame->parent;
}
// Otherwise, frame was not pushed because of overflow, do nothing
}
/** Return true iff the top of the stack has the given type. */
static inline bool
lv2_atom_forge_top_is(LV2_Atom_Forge* forge, uint32_t type)
{
return forge->stack && forge->stack->ref &&
(lv2_atom_forge_deref(forge, forge->stack->ref)->type == type);
}
/** Return true iff `type` is an atom:Object. */
static inline bool
lv2_atom_forge_is_object_type(const LV2_Atom_Forge* forge, uint32_t type)
{
return (type == forge->Object ||
type == forge->Blank ||
type == forge->Resource);
}
/** Return true iff `type` is an atom:Object with a blank ID. */
static inline bool
lv2_atom_forge_is_blank(const LV2_Atom_Forge* forge,
uint32_t type,
const LV2_Atom_Object_Body* body)
{
return (type == forge->Blank ||
(type == forge->Object && body->id == 0));
}
/**
@}
@name Output Configuration
@{
*/
/** Set the output buffer where `forge` will write atoms. */
static inline void
lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size)
{
forge->buf = buf;
forge->size = (uint32_t)size;
forge->offset = 0;
forge->deref = NULL;
forge->sink = NULL;
forge->handle = NULL;
forge->stack = NULL;
}
/**
Set the sink function where `forge` will write output.
The return value of forge functions is an LV2_Atom_Forge_Ref which is an
integer type safe to use as a pointer but is otherwise opaque. The sink
function must return a ref that can be dereferenced to access as least
sizeof(LV2_Atom) bytes of the written data, so sizes can be updated. For
ringbuffers, this should be possible as long as the size of the buffer is a
multiple of sizeof(LV2_Atom), since atoms are always aligned.
Note that 0 is an invalid reference, so if you are using a buffer offset be
sure to offset it such that 0 is never a valid reference. You will get
confusing errors otherwise.
*/
static inline void
lv2_atom_forge_set_sink(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Sink sink,
LV2_Atom_Forge_Deref_Func deref,
LV2_Atom_Forge_Sink_Handle handle)
{
forge->buf = NULL;
forge->size = forge->offset = 0;
forge->deref = deref;
forge->sink = sink;
forge->handle = handle;
forge->stack = NULL;
}
/**
@}
@name Low Level Output
@{
*/
/**
Write raw output. This is used internally, but is also useful for writing
atom types not explicitly supported by the forge API. Note the caller is
responsible for ensuring the output is approriately padded.
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_raw(LV2_Atom_Forge* forge, const void* data, uint32_t size)
{
LV2_Atom_Forge_Ref out = 0;
if (forge->sink) {
out = forge->sink(forge->handle, data, size);
} else {
out = (LV2_Atom_Forge_Ref)forge->buf + forge->offset;
uint8_t* mem = forge->buf + forge->offset;
if (forge->offset + size > forge->size) {
return 0;
}
forge->offset += size;
memcpy(mem, data, size);
}
for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) {
lv2_atom_forge_deref(forge, f->ref)->size += size;
}
return out;
}
/** Pad output accordingly so next write is 64-bit aligned. */
static inline void
lv2_atom_forge_pad(LV2_Atom_Forge* forge, uint32_t written)
{
const uint64_t pad = 0;
const uint32_t pad_size = lv2_atom_pad_size(written) - written;
lv2_atom_forge_raw(forge, &pad, pad_size);
}
/** Write raw output, padding to 64-bits as necessary. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size)
{
LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size);
if (out) {
lv2_atom_forge_pad(forge, size);
}
return out;
}
/** Write a null-terminated string body. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_string_body(LV2_Atom_Forge* forge,
const char* str,
uint32_t len)
{
LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, str, len);
if (out && (out = lv2_atom_forge_raw(forge, "", 1))) {
lv2_atom_forge_pad(forge, len + 1);
}
return out;
}
/**
@}
@name Atom Output
@{
*/
/** Write an atom:Atom header. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_atom(LV2_Atom_Forge* forge, uint32_t size, uint32_t type)
{
const LV2_Atom a = { size, type };
return lv2_atom_forge_raw(forge, &a, sizeof(a));
}
/** Write a primitive (fixed-size) atom. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_primitive(LV2_Atom_Forge* forge, const LV2_Atom* a)
{
return (lv2_atom_forge_top_is(forge, forge->Vector)
? lv2_atom_forge_raw(forge, LV2_ATOM_BODY_CONST(a), a->size)
: lv2_atom_forge_write(
forge, a, (uint32_t)sizeof(LV2_Atom) + a->size));
}
/** Write an atom:Int. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_int(LV2_Atom_Forge* forge, int32_t val)
{
const LV2_Atom_Int a = { { sizeof(val), forge->Int }, val };
return lv2_atom_forge_primitive(forge, &a.atom);
}
/** Write an atom:Long. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_long(LV2_Atom_Forge* forge, int64_t val)
{
const LV2_Atom_Long a = { { sizeof(val), forge->Long }, val };
return lv2_atom_forge_primitive(forge, &a.atom);
}
/** Write an atom:Float. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_float(LV2_Atom_Forge* forge, float val)
{
const LV2_Atom_Float a = { { sizeof(val), forge->Float }, val };
return lv2_atom_forge_primitive(forge, &a.atom);
}
/** Write an atom:Double. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_double(LV2_Atom_Forge* forge, double val)
{
const LV2_Atom_Double a = { { sizeof(val), forge->Double }, val };
return lv2_atom_forge_primitive(forge, &a.atom);
}
/** Write an atom:Bool. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_bool(LV2_Atom_Forge* forge, bool val)
{
const LV2_Atom_Bool a = { { sizeof(int32_t), forge->Bool }, val ? 1 : 0 };
return lv2_atom_forge_primitive(forge, &a.atom);
}
/** Write an atom:URID. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_URID id)
{
const LV2_Atom_URID a = { { sizeof(id), forge->URID }, id };
return lv2_atom_forge_primitive(forge, &a.atom);
}
/** Write an atom compatible with atom:String. Used internally. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_typed_string(LV2_Atom_Forge* forge,
uint32_t type,
const char* str,
uint32_t len)
{
const LV2_Atom_String a = { { len + 1, type } };
LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a));
if (out) {
if (!lv2_atom_forge_string_body(forge, str, len)) {
LV2_Atom* atom = lv2_atom_forge_deref(forge, out);
atom->size = atom->type = 0;
out = 0;
}
}
return out;
}
/** Write an atom:String. Note that `str` need not be NULL terminated. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len)
{
return lv2_atom_forge_typed_string(forge, forge->String, str, len);
}
/**
Write an atom:URI. Note that `uri` need not be NULL terminated.
This does not map the URI, but writes the complete URI string. To write
a mapped URI, use lv2_atom_forge_urid().
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_uri(LV2_Atom_Forge* forge, const char* uri, uint32_t len)
{
return lv2_atom_forge_typed_string(forge, forge->URI, uri, len);
}
/** Write an atom:Path. Note that `path` need not be NULL terminated. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_path(LV2_Atom_Forge* forge, const char* path, uint32_t len)
{
return lv2_atom_forge_typed_string(forge, forge->Path, path, len);
}
/** Write an atom:Literal. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_literal(LV2_Atom_Forge* forge,
const char* str,
uint32_t len,
uint32_t datatype,
uint32_t lang)
{
const LV2_Atom_Literal a = {
{ (uint32_t)(sizeof(LV2_Atom_Literal) - sizeof(LV2_Atom) + len + 1),
forge->Literal },
{ datatype,
lang }
};
LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a));
if (out) {
if (!lv2_atom_forge_string_body(forge, str, len)) {
LV2_Atom* atom = lv2_atom_forge_deref(forge, out);
atom->size = atom->type = 0;
out = 0;
}
}
return out;
}
/** Start an atom:Vector. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_vector_head(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Frame* frame,
uint32_t child_size,
uint32_t child_type)
{
const LV2_Atom_Vector a = {
{ sizeof(LV2_Atom_Vector_Body), forge->Vector },
{ child_size, child_type }
};
return lv2_atom_forge_push(
forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
}
/** Write a complete atom:Vector. */
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_vector(LV2_Atom_Forge* forge,
uint32_t child_size,
uint32_t child_type,
uint32_t n_elems,
const void* elems)
{
const LV2_Atom_Vector a = {
{ (uint32_t)(sizeof(LV2_Atom_Vector_Body) + n_elems * child_size),
forge->Vector },
{ child_size, child_type }
};
LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a));
if (out) {
lv2_atom_forge_write(forge, elems, child_size * n_elems);
}
return out;
}
/**
Write the header of an atom:Tuple.
The passed frame will be initialised to represent this tuple. To complete
the tuple, write a sequence of atoms, then pop the frame with
lv2_atom_forge_pop().
For example:
@code
// Write tuple (1, 2.0)
LV2_Atom_Forge_Frame frame;
LV2_Atom* tup = (LV2_Atom*)lv2_atom_forge_tuple(forge, &frame);
lv2_atom_forge_int32(forge, 1);
lv2_atom_forge_float(forge, 2.0);
lv2_atom_forge_pop(forge, &frame);
@endcode
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_tuple(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame)
{
const LV2_Atom_Tuple a = { { 0, forge->Tuple } };
return lv2_atom_forge_push(
forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
}
/**
Write the header of an atom:Object.
The passed frame will be initialised to represent this object. To complete
the object, write a sequence of properties, then pop the frame with
lv2_atom_forge_pop().
For example:
@code
LV2_URID eg_Cat = map("http://example.org/Cat");
LV2_URID eg_name = map("http://example.org/name");
// Start object with type eg_Cat and blank ID
LV2_Atom_Forge_Frame frame;
lv2_atom_forge_object(forge, &frame, 0, eg_Cat);
// Append property eg:name = "Hobbes"
lv2_atom_forge_key(forge, eg_name);
lv2_atom_forge_string(forge, "Hobbes", strlen("Hobbes"));
// Finish object
lv2_atom_forge_pop(forge, &frame);
@endcode
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_object(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Frame* frame,
LV2_URID id,
LV2_URID otype)
{
const LV2_Atom_Object a = {
{ (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Object },
{ id, otype }
};
return lv2_atom_forge_push(
forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
}
/**
The same as lv2_atom_forge_object(), but for object:Resource.
This function is deprecated and should not be used in new code.
Use lv2_atom_forge_object() directly instead.
*/
LV2_DEPRECATED
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_resource(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Frame* frame,
LV2_URID id,
LV2_URID otype)
{
const LV2_Atom_Object a = {
{ (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Resource },
{ id, otype }
};
return lv2_atom_forge_push(
forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
}
/**
The same as lv2_atom_forge_object(), but for object:Blank.
This function is deprecated and should not be used in new code.
Use lv2_atom_forge_object() directly instead.
*/
LV2_DEPRECATED
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_blank(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Frame* frame,
uint32_t id,
LV2_URID otype)
{
const LV2_Atom_Object a = {
{ (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Blank },
{ id, otype }
};
return lv2_atom_forge_push(
forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
}
/**
Write a property key in an Object, to be followed by the value.
See lv2_atom_forge_object() documentation for an example.
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_key(LV2_Atom_Forge* forge,
LV2_URID key)
{
const LV2_Atom_Property_Body a = { key, 0, { 0, 0 } };
return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t));
}
/**
Write the header for a property body in an object, with context.
If you do not need the context, which is almost certainly the case,
use the simpler lv2_atom_forge_key() instead.
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_property_head(LV2_Atom_Forge* forge,
LV2_URID key,
LV2_URID context)
{
const LV2_Atom_Property_Body a = { key, context, { 0, 0 } };
return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t));
}
/**
Write the header for a Sequence.
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_sequence_head(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Frame* frame,
uint32_t unit)
{
const LV2_Atom_Sequence a = {
{ (uint32_t)sizeof(LV2_Atom_Sequence_Body), forge->Sequence },
{ unit, 0 }
};
return lv2_atom_forge_push(
forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
}
/**
Write the time stamp header of an Event (in a Sequence) in audio frames.
After this, call the appropriate forge method(s) to write the body. Note
the returned reference is to an LV2_Event which is NOT an Atom.
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames)
{
return lv2_atom_forge_write(forge, &frames, sizeof(frames));
}
/**
Write the time stamp header of an Event (in a Sequence) in beats. After
this, call the appropriate forge method(s) to write the body. Note the
returned reference is to an LV2_Event which is NOT an Atom.
*/
static inline LV2_Atom_Forge_Ref
lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats)
{
return lv2_atom_forge_write(forge, &beats, sizeof(beats));
}
/**
@}
@}
*/
LV2_RESTORE_WARNINGS
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV2_ATOM_FORGE_H */

View file

@ -0,0 +1,103 @@
@prefix dcs: <http://ontologi.es/doap-changeset#> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/ext/atom>
a doap:Project ;
doap:name "LV2 Atom" ;
doap:shortdesc "A generic value container and several data types." ;
doap:license <http://opensource.org/licenses/isc> ;
doap:created "2007-00-00" ;
doap:developer <http://drobilla.net/drobilla#me> ;
doap:release [
doap:revision "2.2" ;
doap:created "2019-02-03" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.16.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add lv2_atom_object_get_typed() for easy type-safe access to object properties."
]
]
] , [
doap:revision "2.0" ;
doap:created "2014-08-08" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.10.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Deprecate Blank and Resource in favour of just Object."
] , [
rdfs:label "Add lv2_atom_forge_is_object_type() and lv2_atom_forge_is_blank() to ease backwards compatibility."
] , [
rdfs:label "Add lv2_atom_forge_key() for terser object writing."
] , [
rdfs:label "Add lv2_atom_sequence_clear() and lv2_atom_sequence_append_event() helper functions."
]
]
] , [
doap:revision "1.8" ;
doap:created "2014-01-04" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.8.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Make lv2_atom_*_is_end() arguments const."
]
]
] , [
doap:revision "1.6" ;
doap:created "2013-05-26" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.6.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Fix crash in forge.h when pushing atoms to a full buffer."
]
]
] , [
doap:revision "1.4" ;
doap:created "2013-01-27" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.4.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Fix lv2_atom_sequence_end()."
] , [
rdfs:label "Remove atom:stringType in favour of owl:onDatatype so generic tools can understand and validate atom literals."
] , [
rdfs:label "Improve atom documentation."
]
]
] , [
doap:revision "1.2" ;
doap:created "2012-10-14" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.2.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Fix implicit conversions in forge.h that are invalid in C++11."
] , [
rdfs:label "Fix lv2_atom_object_next() on 32-bit platforms."
] , [
rdfs:label "Add lv2_atom_object_body_get()."
] , [
rdfs:label "Fix outdated documentation in forge.h."
] , [
rdfs:label "Use consistent label style."
] , [
rdfs:label "Add LV2_ATOM_CONTENTS_CONST and LV2_ATOM_BODY_CONST."
]
]
] , [
doap:revision "1.0" ;
doap:created "2012-04-17" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.0.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Initial release."
]
]
] .

View file

@ -0,0 +1,8 @@
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/ext/atom>
a lv2:Specification ;
lv2:minorVersion 2 ;
lv2:microVersion 2 ;
rdfs:seeAlso <atom.ttl> .

509
lv2/lv2/atom/util.h Normal file
View file

@ -0,0 +1,509 @@
/*
Copyright 2008-2015 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@file util.h Helper functions for the LV2 Atom extension.
Note these functions are all static inline, do not take their address.
This header is non-normative, it is provided for convenience.
*/
/**
@defgroup util Utilities
@ingroup atom
@{
*/
#ifndef LV2_ATOM_UTIL_H
#define LV2_ATOM_UTIL_H
#include "lv2/atom/atom.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Pad a size to 64 bits. */
static inline uint32_t
lv2_atom_pad_size(uint32_t size)
{
return (size + 7U) & (~7U);
}
/** Return the total size of `atom`, including the header. */
static inline uint32_t
lv2_atom_total_size(const LV2_Atom* atom)
{
return (uint32_t)sizeof(LV2_Atom) + atom->size;
}
/** Return true iff `atom` is null. */
static inline bool
lv2_atom_is_null(const LV2_Atom* atom)
{
return !atom || (atom->type == 0 && atom->size == 0);
}
/** Return true iff `a` is equal to `b`. */
static inline bool
lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b)
{
return (a == b) || ((a->type == b->type) &&
(a->size == b->size) &&
!memcmp(a + 1, b + 1, a->size));
}
/**
@name Sequence Iterator
@{
*/
/** Get an iterator pointing to the first event in a Sequence body. */
static inline LV2_Atom_Event*
lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body)
{
return (LV2_Atom_Event*)(body + 1);
}
/** Get an iterator pointing to the end of a Sequence body. */
static inline LV2_Atom_Event*
lv2_atom_sequence_end(const LV2_Atom_Sequence_Body* body, uint32_t size)
{
return (LV2_Atom_Event*)((const uint8_t*)body + lv2_atom_pad_size(size));
}
/** Return true iff `i` has reached the end of `body`. */
static inline bool
lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body,
uint32_t size,
const LV2_Atom_Event* i)
{
return (const uint8_t*)i >= ((const uint8_t*)body + size);
}
/** Return an iterator to the element following `i`. */
static inline LV2_Atom_Event*
lv2_atom_sequence_next(const LV2_Atom_Event* i)
{
return (LV2_Atom_Event*)((const uint8_t*)i
+ sizeof(LV2_Atom_Event)
+ lv2_atom_pad_size(i->body.size));
}
/**
A macro for iterating over all events in a Sequence.
@param seq The sequence to iterate over
@param iter The name of the iterator
This macro is used similarly to a for loop (which it expands to), e.g.:
@code
LV2_ATOM_SEQUENCE_FOREACH(sequence, ev) {
// Do something with ev (an LV2_Atom_Event*) here...
}
@endcode
*/
#define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \
for (LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(&(seq)->body); \
!lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \
(iter) = lv2_atom_sequence_next(iter))
/** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */
#define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \
for (LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(body); \
!lv2_atom_sequence_is_end(body, size, (iter)); \
(iter) = lv2_atom_sequence_next(iter))
/**
@}
@name Sequence Utilities
@{
*/
/**
Clear all events from `sequence`.
This simply resets the size field, the other fields are left untouched.
*/
static inline void
lv2_atom_sequence_clear(LV2_Atom_Sequence* seq)
{
seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
}
/**
Append an event at the end of `sequence`.
@param seq Sequence to append to.
@param capacity Total capacity of the sequence atom
(e.g. as set by the host for sequence output ports).
@param event Event to write.
@return A pointer to the newly written event in `seq`,
or NULL on failure (insufficient space).
*/
static inline LV2_Atom_Event*
lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq,
uint32_t capacity,
const LV2_Atom_Event* event)
{
const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size;
if (capacity - seq->atom.size < total_size) {
return NULL;
}
LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size);
memcpy(e, event, total_size);
seq->atom.size += lv2_atom_pad_size(total_size);
return e;
}
/**
@}
@name Tuple Iterator
@{
*/
/** Get an iterator pointing to the first element in `tup`. */
static inline LV2_Atom*
lv2_atom_tuple_begin(const LV2_Atom_Tuple* tup)
{
return (LV2_Atom*)(LV2_ATOM_BODY(tup));
}
/** Return true iff `i` has reached the end of `body`. */
static inline bool
lv2_atom_tuple_is_end(const void* body, uint32_t size, const LV2_Atom* i)
{
return (const uint8_t*)i >= ((const uint8_t*)body + size);
}
/** Return an iterator to the element following `i`. */
static inline LV2_Atom*
lv2_atom_tuple_next(const LV2_Atom* i)
{
return (LV2_Atom*)(
(const uint8_t*)i + sizeof(LV2_Atom) + lv2_atom_pad_size(i->size));
}
/**
A macro for iterating over all properties of a Tuple.
@param tuple The tuple to iterate over
@param iter The name of the iterator
This macro is used similarly to a for loop (which it expands to), e.g.:
@code
LV2_ATOM_TUPLE_FOREACH(tuple, elem) {
// Do something with elem (an LV2_Atom*) here...
}
@endcode
*/
#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \
for (LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \
!lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \
(iter) = lv2_atom_tuple_next(iter))
/** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */
#define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \
for (LV2_Atom* (iter) = (LV2_Atom*)(body); \
!lv2_atom_tuple_is_end(body, size, (iter)); \
(iter) = lv2_atom_tuple_next(iter))
/**
@}
@name Object Iterator
@{
*/
/** Return a pointer to the first property in `body`. */
static inline LV2_Atom_Property_Body*
lv2_atom_object_begin(const LV2_Atom_Object_Body* body)
{
return (LV2_Atom_Property_Body*)(body + 1);
}
/** Return true iff `i` has reached the end of `obj`. */
static inline bool
lv2_atom_object_is_end(const LV2_Atom_Object_Body* body,
uint32_t size,
const LV2_Atom_Property_Body* i)
{
return (const uint8_t*)i >= ((const uint8_t*)body + size);
}
/** Return an iterator to the property following `i`. */
static inline LV2_Atom_Property_Body*
lv2_atom_object_next(const LV2_Atom_Property_Body* i)
{
const LV2_Atom* const value = (const LV2_Atom*)(
(const uint8_t*)i + 2 * sizeof(uint32_t));
return (LV2_Atom_Property_Body*)(
(const uint8_t*)i + lv2_atom_pad_size(
(uint32_t)sizeof(LV2_Atom_Property_Body) + value->size));
}
/**
A macro for iterating over all properties of an Object.
@param obj The object to iterate over
@param iter The name of the iterator
This macro is used similarly to a for loop (which it expands to), e.g.:
@code
LV2_ATOM_OBJECT_FOREACH(object, i) {
// Do something with i (an LV2_Atom_Property_Body*) here...
}
@endcode
*/
#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \
for (LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(&(obj)->body); \
!lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \
(iter) = lv2_atom_object_next(iter))
/** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */
#define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \
for (LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(body); \
!lv2_atom_object_is_end(body, size, (iter)); \
(iter) = lv2_atom_object_next(iter))
/**
@}
@name Object Query
@{
*/
/** A single entry in an Object query. */
typedef struct {
uint32_t key; /**< Key to query (input set by user) */
const LV2_Atom** value; /**< Found value (output set by query function) */
} LV2_Atom_Object_Query;
/** Sentinel for lv2_atom_object_query(). */
static const LV2_Atom_Object_Query LV2_ATOM_OBJECT_QUERY_END = { 0, NULL };
/**
Get an object's values for various keys.
The value pointer of each item in `query` will be set to the location of
the corresponding value in `object`. Every value pointer in `query` MUST
be initialised to NULL. This function reads `object` in a single linear
sweep. By allocating `query` on the stack, objects can be "queried"
quickly without allocating any memory. This function is realtime safe.
This function can only do "flat" queries, it is not smart enough to match
variables in nested objects.
For example:
@code
const LV2_Atom* name = NULL;
const LV2_Atom* age = NULL;
LV2_Atom_Object_Query q[] = {
{ urids.eg_name, &name },
{ urids.eg_age, &age },
LV2_ATOM_OBJECT_QUERY_END
};
lv2_atom_object_query(obj, q);
// name and age are now set to the appropriate values in obj, or NULL.
@endcode
*/
static inline int
lv2_atom_object_query(const LV2_Atom_Object* object,
LV2_Atom_Object_Query* query)
{
int matches = 0;
int n_queries = 0;
/* Count number of query keys so we can short-circuit when done */
for (LV2_Atom_Object_Query* q = query; q->key; ++q) {
++n_queries;
}
LV2_ATOM_OBJECT_FOREACH(object, prop) {
for (LV2_Atom_Object_Query* q = query; q->key; ++q) {
if (q->key == prop->key && !*q->value) {
*q->value = &prop->value;
if (++matches == n_queries) {
return matches;
}
break;
}
}
}
return matches;
}
/**
Body only version of lv2_atom_object_get().
*/
static inline int
lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...)
{
int matches = 0;
int n_queries = 0;
/* Count number of keys so we can short-circuit when done */
va_list args;
va_start(args, body);
for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) {
if (!va_arg(args, const LV2_Atom**)) {
return -1;
}
}
va_end(args);
LV2_ATOM_OBJECT_BODY_FOREACH(body, size, prop) {
va_start(args, body);
for (int i = 0; i < n_queries; ++i) {
uint32_t qkey = va_arg(args, uint32_t);
const LV2_Atom** qval = va_arg(args, const LV2_Atom**);
if (qkey == prop->key && !*qval) {
*qval = &prop->value;
if (++matches == n_queries) {
return matches;
}
break;
}
}
va_end(args);
}
return matches;
}
/**
Variable argument version of lv2_atom_object_query().
This is nicer-looking in code, but a bit more error-prone since it is not
type safe and the argument list must be terminated.
The arguments should be a series of uint32_t key and const LV2_Atom** value
pairs, terminated by a zero key. The value pointers MUST be initialized to
NULL. For example:
@code
const LV2_Atom* name = NULL;
const LV2_Atom* age = NULL;
lv2_atom_object_get(obj,
uris.name_key, &name,
uris.age_key, &age,
0);
@endcode
*/
static inline int
lv2_atom_object_get(const LV2_Atom_Object* object, ...)
{
int matches = 0;
int n_queries = 0;
/* Count number of keys so we can short-circuit when done */
va_list args;
va_start(args, object);
for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) {
if (!va_arg(args, const LV2_Atom**)) {
return -1;
}
}
va_end(args);
LV2_ATOM_OBJECT_FOREACH(object, prop) {
va_start(args, object);
for (int i = 0; i < n_queries; ++i) {
uint32_t qkey = va_arg(args, uint32_t);
const LV2_Atom** qval = va_arg(args, const LV2_Atom**);
if (qkey == prop->key && !*qval) {
*qval = &prop->value;
if (++matches == n_queries) {
return matches;
}
break;
}
}
va_end(args);
}
return matches;
}
/**
Variable argument version of lv2_atom_object_query() with types.
This is like lv2_atom_object_get(), but each entry has an additional
parameter to specify the required type. Only atoms with a matching type
will be selected.
The arguments should be a series of uint32_t key, const LV2_Atom**, uint32_t
type triples, terminated by a zero key. The value pointers MUST be
initialized to NULL. For example:
@code
const LV2_Atom_String* name = NULL;
const LV2_Atom_Int* age = NULL;
lv2_atom_object_get(obj,
uris.name_key, &name, uris.atom_String,
uris.age_key, &age, uris.atom_Int
0);
@endcode
*/
static inline int
lv2_atom_object_get_typed(const LV2_Atom_Object* object, ...)
{
int matches = 0;
int n_queries = 0;
/* Count number of keys so we can short-circuit when done */
va_list args;
va_start(args, object);
for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) {
if (!va_arg(args, const LV2_Atom**) ||
!va_arg(args, uint32_t)) {
return -1;
}
}
va_end(args);
LV2_ATOM_OBJECT_FOREACH(object, prop) {
va_start(args, object);
for (int i = 0; i < n_queries; ++i) {
const uint32_t qkey = va_arg(args, uint32_t);
const LV2_Atom** qval = va_arg(args, const LV2_Atom**);
const uint32_t qtype = va_arg(args, uint32_t);
if (!*qval && qkey == prop->key && qtype == prop->value.type) {
*qval = &prop->value;
if (++matches == n_queries) {
return matches;
}
break;
}
}
va_end(args);
}
return matches;
}
/**
@}
@}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV2_ATOM_UTIL_H */

View file

@ -0,0 +1,44 @@
/*
Copyright 2007-2016 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LV2_BUF_SIZE_H
#define LV2_BUF_SIZE_H
/**
@defgroup buf-size Buffer Size
Access to, and restrictions on, buffer sizes; see
<http://lv2plug.in/ns/ext/buf-size> for details.
@{
*/
#define LV2_BUF_SIZE_URI "http://lv2plug.in/ns/ext/buf-size" ///< http://lv2plug.in/ns/ext/buf-size
#define LV2_BUF_SIZE_PREFIX LV2_BUF_SIZE_URI "#" ///< http://lv2plug.in/ns/ext/buf-size#
#define LV2_BUF_SIZE__boundedBlockLength LV2_BUF_SIZE_PREFIX "boundedBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#boundedBlockLength
#define LV2_BUF_SIZE__fixedBlockLength LV2_BUF_SIZE_PREFIX "fixedBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#fixedBlockLength
#define LV2_BUF_SIZE__maxBlockLength LV2_BUF_SIZE_PREFIX "maxBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#maxBlockLength
#define LV2_BUF_SIZE__minBlockLength LV2_BUF_SIZE_PREFIX "minBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#minBlockLength
#define LV2_BUF_SIZE__nominalBlockLength LV2_BUF_SIZE_PREFIX "nominalBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#nominalBlockLength
#define LV2_BUF_SIZE__powerOf2BlockLength LV2_BUF_SIZE_PREFIX "powerOf2BlockLength" ///< http://lv2plug.in/ns/ext/buf-size#powerOf2BlockLength
#define LV2_BUF_SIZE__sequenceSize LV2_BUF_SIZE_PREFIX "sequenceSize" ///< http://lv2plug.in/ns/ext/buf-size#sequenceSize
/**
@}
*/
#endif /* LV2_BUF_SIZE_H */

View file

@ -0,0 +1,129 @@
@prefix bufsz: <http://lv2plug.in/ns/ext/buf-size#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://lv2plug.in/ns/ext/buf-size>
a lv2:Specification ;
rdfs:seeAlso <buf-size.h> ,
<lv2-buf-size.doap.ttl> ;
lv2:documentation """
<p>This extension defines a facility for plugins to get information about the
block length (the sample_count parameter of LV2_Descriptor::run) and
port buffer sizes, as well as several features which can be used to restrict
the block length.</p>
<p>This extension defines features and properties but has no special purpose
API of its own. The host provides all the relevant information to the plugin
as <a href="options.html#options">options</a>.</p>
<p>To require restrictions on the block length, plugins can require additional
features: bufsz:boundedBlockLength, bufsz:powerOf2BlockLength, and
bufsz:fixedBlockLength. These features are data-only, that is they merely
indicate a restriction and do not carry any data or API.</p>
""" .
bufsz:boundedBlockLength
a lv2:Feature ;
lv2:documentation """
<p>A feature that indicates the host will provide both the bufsz:minBlockLength
and bufsz:maxBlockLength options to the plugin. Plugins that copy data from
audio inputs can require this feature to ensure they know how much space is
required for auxiliary buffers. Note the minimum may be zero, this feature is
mainly useful to ensure a maximum is available.</p>
<p>All hosts SHOULD support this feature, since it is simple to support and
necessary for any plugins that may need to copy the input.</p>
""" .
bufsz:fixedBlockLength
a lv2:Feature ;
lv2:documentation """
<p>A feature that indicates the host will always call LV2_Descriptor::run()
with the same value for sample_count. This length MUST be provided as the
value of both the bufsz:minBlockLength and bufsz:maxBlockLength options.</p>
<p>Note that requiring this feature may severely limit the number of hosts
capable of running the plugin.</p>
""" .
bufsz:powerOf2BlockLength
a lv2:Feature ;
lv2:documentation """
<p>A feature that indicates the host will always call LV2_Descriptor::run()
with a power of two sample_count. Note that this feature does not guarantee
the value is the same each call, to guarantee a fixed power of two block length
plugins must require both this feature and bufsz:fixedBlockLength.</p>
<p>Note that requiring this feature may severely limit the number of hosts
capable of running the plugin.</p>
""" .
bufsz:coarseBlockLength
a lv2:Feature ;
rdfs:label "coarse block length" ;
lv2:documentation """
<p>A feature that indicates the plugin prefers coarse, regular block lengths.
For example, plugins that do not implement sample-accurate control use this
feature to indicate that the host should not split the run cycle because
controls have changed.</p>
<p>Note that this feature is merely a hint, and does not guarantee a fixed
block length. The run cycle may be split for other reasons, and the blocksize
itself may change anytime.</p>
""" .
bufsz:maxBlockLength
a rdf:Property ,
owl:DatatypeProperty ,
opts:Option ;
rdfs:label "maximum block length" ;
rdfs:range xsd:nonNegativeInteger ;
lv2:documentation """
<p>The maximum block length the host will ever request the plugin to process at
once, that is, the maximum <code>sample_count</code> parameter that will ever
be passed to LV2_Descriptor::run().</p>
""" .
bufsz:minBlockLength
a rdf:Property ,
owl:DatatypeProperty ,
opts:Option ;
rdfs:label "minimum block length" ;
rdfs:range xsd:nonNegativeInteger ;
lv2:documentation """
<p>The minimum block length the host will ever request the plugin to process at
once, that is, the minimum <code>sample_count</code> parameter that will ever
be passed to LV2_Descriptor::run().</p>
""" .
bufsz:nominalBlockLength
a rdf:Property ,
owl:DatatypeProperty ,
opts:Option ;
rdfs:label "nominal block length" ;
rdfs:range xsd:nonNegativeInteger ;
lv2:documentation """
<p>The typical block length the host will request the plugin to process at
once, that is, the typical <code>sample_count</code> parameter that will
be passed to LV2_Descriptor::run(). This will usually be equivalent, or close
to, the maximum block length, but there are no strong guarantees about this
value whatsoever. Plugins may use this length for optimization purposes, but
MUST NOT assume the host will always process blocks of this length. In
particular, the host MAY process longer blocks.</p>
""" .
bufsz:sequenceSize
a rdf:Property ,
owl:DatatypeProperty ,
opts:Option ;
rdfs:label "sequence size" ;
rdfs:range xsd:nonNegativeInteger ;
lv2:documentation """
<p>The maximum size of a sequence, in bytes. This should be provided as an
option by hosts that support event ports (including but not limited to MIDI),
so plugins have the ability to allocate auxiliary buffers large enough to copy
the input.</p> """ .

View file

@ -0,0 +1,44 @@
@prefix dcs: <http://ontologi.es/doap-changeset#> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/ext/buf-size>
a doap:Project ;
doap:name "LV2 Buf Size" ;
doap:shortdesc "Access to, and restrictions on, buffer sizes." ;
doap:created "2012-08-07" ;
doap:developer <http://drobilla.net/drobilla#me> ;
doap:release [
doap:revision "1.4" ;
doap:created "2015-09-18" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.14.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add bufsz:nominalBlockLength option."
] , [
rdfs:label "Add bufsz:coarseBlockLength feature."
]
]
] , [
doap:revision "1.2" ;
doap:created "2012-12-21" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.4.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Fix typo in bufsz:sequenceSize label."
]
]
] , [
doap:revision "1.0" ;
doap:created "2012-10-14" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.2.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Initial release."
]
]
] .

View file

@ -0,0 +1,9 @@
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/ext/buf-size>
a lv2:Specification ;
lv2:minorVersion 1 ;
lv2:microVersion 4 ;
rdfs:seeAlso <buf-size.ttl> .

58
lv2/lv2/core/attributes.h Normal file
View file

@ -0,0 +1,58 @@
/*
Copyright 2018 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LV2_CORE_ATTRIBUTES_H
#define LV2_CORE_ATTRIBUTES_H
/**
@defgroup attributes Attributes
Macros for source code attributes.
@{
*/
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
#define LV2_DEPRECATED __attribute__((__deprecated__))
#else
#define LV2_DEPRECATED
#endif
#if defined(__clang__)
#define LV2_DISABLE_DEPRECATION_WARNINGS \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#define LV2_DISABLE_DEPRECATION_WARNINGS \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#else
#define LV2_DISABLE_DEPRECATION_WARNINGS
#endif
#if defined(__clang__)
#define LV2_RESTORE_WARNINGS _Pragma("clang diagnostic pop")
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#define LV2_RESTORE_WARNINGS _Pragma("GCC diagnostic pop")
#else
#define LV2_RESTORE_WARNINGS
#endif
/**
@}
*/
#endif /* LV2_CORE_ATTRIBUTES_H */

474
lv2/lv2/core/lv2.h Normal file
View file

@ -0,0 +1,474 @@
/*
LV2 - An audio plugin interface specification.
Copyright 2006-2012 Steve Harris, David Robillard.
Based on LADSPA, Copyright 2000-2002 Richard W.E. Furse,
Paul Barton-Davis, Stefan Westerfeld.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@defgroup lv2core LV2 Core
Core LV2 specification, see <http://lv2plug.in/ns/lv2core> for details.
@{
*/
#ifndef LV2_H_INCLUDED
#define LV2_H_INCLUDED
#include <stdint.h>
#define LV2_CORE_URI "http://lv2plug.in/ns/lv2core" ///< http://lv2plug.in/ns/lv2core
#define LV2_CORE_PREFIX LV2_CORE_URI "#" ///< http://lv2plug.in/ns/lv2core#
#define LV2_CORE__AllpassPlugin LV2_CORE_PREFIX "AllpassPlugin" ///< http://lv2plug.in/ns/lv2core#AllpassPlugin
#define LV2_CORE__AmplifierPlugin LV2_CORE_PREFIX "AmplifierPlugin" ///< http://lv2plug.in/ns/lv2core#AmplifierPlugin
#define LV2_CORE__AnalyserPlugin LV2_CORE_PREFIX "AnalyserPlugin" ///< http://lv2plug.in/ns/lv2core#AnalyserPlugin
#define LV2_CORE__AudioPort LV2_CORE_PREFIX "AudioPort" ///< http://lv2plug.in/ns/lv2core#AudioPort
#define LV2_CORE__BandpassPlugin LV2_CORE_PREFIX "BandpassPlugin" ///< http://lv2plug.in/ns/lv2core#BandpassPlugin
#define LV2_CORE__CVPort LV2_CORE_PREFIX "CVPort" ///< http://lv2plug.in/ns/lv2core#CVPort
#define LV2_CORE__ChorusPlugin LV2_CORE_PREFIX "ChorusPlugin" ///< http://lv2plug.in/ns/lv2core#ChorusPlugin
#define LV2_CORE__CombPlugin LV2_CORE_PREFIX "CombPlugin" ///< http://lv2plug.in/ns/lv2core#CombPlugin
#define LV2_CORE__CompressorPlugin LV2_CORE_PREFIX "CompressorPlugin" ///< http://lv2plug.in/ns/lv2core#CompressorPlugin
#define LV2_CORE__ConstantPlugin LV2_CORE_PREFIX "ConstantPlugin" ///< http://lv2plug.in/ns/lv2core#ConstantPlugin
#define LV2_CORE__ControlPort LV2_CORE_PREFIX "ControlPort" ///< http://lv2plug.in/ns/lv2core#ControlPort
#define LV2_CORE__ConverterPlugin LV2_CORE_PREFIX "ConverterPlugin" ///< http://lv2plug.in/ns/lv2core#ConverterPlugin
#define LV2_CORE__DelayPlugin LV2_CORE_PREFIX "DelayPlugin" ///< http://lv2plug.in/ns/lv2core#DelayPlugin
#define LV2_CORE__DistortionPlugin LV2_CORE_PREFIX "DistortionPlugin" ///< http://lv2plug.in/ns/lv2core#DistortionPlugin
#define LV2_CORE__DynamicsPlugin LV2_CORE_PREFIX "DynamicsPlugin" ///< http://lv2plug.in/ns/lv2core#DynamicsPlugin
#define LV2_CORE__EQPlugin LV2_CORE_PREFIX "EQPlugin" ///< http://lv2plug.in/ns/lv2core#EQPlugin
#define LV2_CORE__EnvelopePlugin LV2_CORE_PREFIX "EnvelopePlugin" ///< http://lv2plug.in/ns/lv2core#EnvelopePlugin
#define LV2_CORE__ExpanderPlugin LV2_CORE_PREFIX "ExpanderPlugin" ///< http://lv2plug.in/ns/lv2core#ExpanderPlugin
#define LV2_CORE__ExtensionData LV2_CORE_PREFIX "ExtensionData" ///< http://lv2plug.in/ns/lv2core#ExtensionData
#define LV2_CORE__Feature LV2_CORE_PREFIX "Feature" ///< http://lv2plug.in/ns/lv2core#Feature
#define LV2_CORE__FilterPlugin LV2_CORE_PREFIX "FilterPlugin" ///< http://lv2plug.in/ns/lv2core#FilterPlugin
#define LV2_CORE__FlangerPlugin LV2_CORE_PREFIX "FlangerPlugin" ///< http://lv2plug.in/ns/lv2core#FlangerPlugin
#define LV2_CORE__FunctionPlugin LV2_CORE_PREFIX "FunctionPlugin" ///< http://lv2plug.in/ns/lv2core#FunctionPlugin
#define LV2_CORE__GatePlugin LV2_CORE_PREFIX "GatePlugin" ///< http://lv2plug.in/ns/lv2core#GatePlugin
#define LV2_CORE__GeneratorPlugin LV2_CORE_PREFIX "GeneratorPlugin" ///< http://lv2plug.in/ns/lv2core#GeneratorPlugin
#define LV2_CORE__HighpassPlugin LV2_CORE_PREFIX "HighpassPlugin" ///< http://lv2plug.in/ns/lv2core#HighpassPlugin
#define LV2_CORE__InputPort LV2_CORE_PREFIX "InputPort" ///< http://lv2plug.in/ns/lv2core#InputPort
#define LV2_CORE__InstrumentPlugin LV2_CORE_PREFIX "InstrumentPlugin" ///< http://lv2plug.in/ns/lv2core#InstrumentPlugin
#define LV2_CORE__LimiterPlugin LV2_CORE_PREFIX "LimiterPlugin" ///< http://lv2plug.in/ns/lv2core#LimiterPlugin
#define LV2_CORE__LowpassPlugin LV2_CORE_PREFIX "LowpassPlugin" ///< http://lv2plug.in/ns/lv2core#LowpassPlugin
#define LV2_CORE__MixerPlugin LV2_CORE_PREFIX "MixerPlugin" ///< http://lv2plug.in/ns/lv2core#MixerPlugin
#define LV2_CORE__ModulatorPlugin LV2_CORE_PREFIX "ModulatorPlugin" ///< http://lv2plug.in/ns/lv2core#ModulatorPlugin
#define LV2_CORE__MultiEQPlugin LV2_CORE_PREFIX "MultiEQPlugin" ///< http://lv2plug.in/ns/lv2core#MultiEQPlugin
#define LV2_CORE__OscillatorPlugin LV2_CORE_PREFIX "OscillatorPlugin" ///< http://lv2plug.in/ns/lv2core#OscillatorPlugin
#define LV2_CORE__OutputPort LV2_CORE_PREFIX "OutputPort" ///< http://lv2plug.in/ns/lv2core#OutputPort
#define LV2_CORE__ParaEQPlugin LV2_CORE_PREFIX "ParaEQPlugin" ///< http://lv2plug.in/ns/lv2core#ParaEQPlugin
#define LV2_CORE__PhaserPlugin LV2_CORE_PREFIX "PhaserPlugin" ///< http://lv2plug.in/ns/lv2core#PhaserPlugin
#define LV2_CORE__PitchPlugin LV2_CORE_PREFIX "PitchPlugin" ///< http://lv2plug.in/ns/lv2core#PitchPlugin
#define LV2_CORE__Plugin LV2_CORE_PREFIX "Plugin" ///< http://lv2plug.in/ns/lv2core#Plugin
#define LV2_CORE__PluginBase LV2_CORE_PREFIX "PluginBase" ///< http://lv2plug.in/ns/lv2core#PluginBase
#define LV2_CORE__Point LV2_CORE_PREFIX "Point" ///< http://lv2plug.in/ns/lv2core#Point
#define LV2_CORE__Port LV2_CORE_PREFIX "Port" ///< http://lv2plug.in/ns/lv2core#Port
#define LV2_CORE__PortProperty LV2_CORE_PREFIX "PortProperty" ///< http://lv2plug.in/ns/lv2core#PortProperty
#define LV2_CORE__Resource LV2_CORE_PREFIX "Resource" ///< http://lv2plug.in/ns/lv2core#Resource
#define LV2_CORE__ReverbPlugin LV2_CORE_PREFIX "ReverbPlugin" ///< http://lv2plug.in/ns/lv2core#ReverbPlugin
#define LV2_CORE__ScalePoint LV2_CORE_PREFIX "ScalePoint" ///< http://lv2plug.in/ns/lv2core#ScalePoint
#define LV2_CORE__SimulatorPlugin LV2_CORE_PREFIX "SimulatorPlugin" ///< http://lv2plug.in/ns/lv2core#SimulatorPlugin
#define LV2_CORE__SpatialPlugin LV2_CORE_PREFIX "SpatialPlugin" ///< http://lv2plug.in/ns/lv2core#SpatialPlugin
#define LV2_CORE__Specification LV2_CORE_PREFIX "Specification" ///< http://lv2plug.in/ns/lv2core#Specification
#define LV2_CORE__SpectralPlugin LV2_CORE_PREFIX "SpectralPlugin" ///< http://lv2plug.in/ns/lv2core#SpectralPlugin
#define LV2_CORE__UtilityPlugin LV2_CORE_PREFIX "UtilityPlugin" ///< http://lv2plug.in/ns/lv2core#UtilityPlugin
#define LV2_CORE__WaveshaperPlugin LV2_CORE_PREFIX "WaveshaperPlugin" ///< http://lv2plug.in/ns/lv2core#WaveshaperPlugin
#define LV2_CORE__appliesTo LV2_CORE_PREFIX "appliesTo" ///< http://lv2plug.in/ns/lv2core#appliesTo
#define LV2_CORE__binary LV2_CORE_PREFIX "binary" ///< http://lv2plug.in/ns/lv2core#binary
#define LV2_CORE__connectionOptional LV2_CORE_PREFIX "connectionOptional" ///< http://lv2plug.in/ns/lv2core#connectionOptional
#define LV2_CORE__control LV2_CORE_PREFIX "control" ///< http://lv2plug.in/ns/lv2core#control
#define LV2_CORE__default LV2_CORE_PREFIX "default" ///< http://lv2plug.in/ns/lv2core#default
#define LV2_CORE__designation LV2_CORE_PREFIX "designation" ///< http://lv2plug.in/ns/lv2core#designation
#define LV2_CORE__documentation LV2_CORE_PREFIX "documentation" ///< http://lv2plug.in/ns/lv2core#documentation
#define LV2_CORE__enumeration LV2_CORE_PREFIX "enumeration" ///< http://lv2plug.in/ns/lv2core#enumeration
#define LV2_CORE__extensionData LV2_CORE_PREFIX "extensionData" ///< http://lv2plug.in/ns/lv2core#extensionData
#define LV2_CORE__freeWheeling LV2_CORE_PREFIX "freeWheeling" ///< http://lv2plug.in/ns/lv2core#freeWheeling
#define LV2_CORE__hardRTCapable LV2_CORE_PREFIX "hardRTCapable" ///< http://lv2plug.in/ns/lv2core#hardRTCapable
#define LV2_CORE__inPlaceBroken LV2_CORE_PREFIX "inPlaceBroken" ///< http://lv2plug.in/ns/lv2core#inPlaceBroken
#define LV2_CORE__index LV2_CORE_PREFIX "index" ///< http://lv2plug.in/ns/lv2core#index
#define LV2_CORE__integer LV2_CORE_PREFIX "integer" ///< http://lv2plug.in/ns/lv2core#integer
#define LV2_CORE__isLive LV2_CORE_PREFIX "isLive" ///< http://lv2plug.in/ns/lv2core#isLive
#define LV2_CORE__latency LV2_CORE_PREFIX "latency" ///< http://lv2plug.in/ns/lv2core#latency
#define LV2_CORE__maximum LV2_CORE_PREFIX "maximum" ///< http://lv2plug.in/ns/lv2core#maximum
#define LV2_CORE__microVersion LV2_CORE_PREFIX "microVersion" ///< http://lv2plug.in/ns/lv2core#microVersion
#define LV2_CORE__minimum LV2_CORE_PREFIX "minimum" ///< http://lv2plug.in/ns/lv2core#minimum
#define LV2_CORE__minorVersion LV2_CORE_PREFIX "minorVersion" ///< http://lv2plug.in/ns/lv2core#minorVersion
#define LV2_CORE__name LV2_CORE_PREFIX "name" ///< http://lv2plug.in/ns/lv2core#name
#define LV2_CORE__optionalFeature LV2_CORE_PREFIX "optionalFeature" ///< http://lv2plug.in/ns/lv2core#optionalFeature
#define LV2_CORE__port LV2_CORE_PREFIX "port" ///< http://lv2plug.in/ns/lv2core#port
#define LV2_CORE__portProperty LV2_CORE_PREFIX "portProperty" ///< http://lv2plug.in/ns/lv2core#portProperty
#define LV2_CORE__project LV2_CORE_PREFIX "project" ///< http://lv2plug.in/ns/lv2core#project
#define LV2_CORE__prototype LV2_CORE_PREFIX "prototype" ///< http://lv2plug.in/ns/lv2core#prototype
#define LV2_CORE__reportsLatency LV2_CORE_PREFIX "reportsLatency" ///< http://lv2plug.in/ns/lv2core#reportsLatency
#define LV2_CORE__requiredFeature LV2_CORE_PREFIX "requiredFeature" ///< http://lv2plug.in/ns/lv2core#requiredFeature
#define LV2_CORE__sampleRate LV2_CORE_PREFIX "sampleRate" ///< http://lv2plug.in/ns/lv2core#sampleRate
#define LV2_CORE__scalePoint LV2_CORE_PREFIX "scalePoint" ///< http://lv2plug.in/ns/lv2core#scalePoint
#define LV2_CORE__symbol LV2_CORE_PREFIX "symbol" ///< http://lv2plug.in/ns/lv2core#symbol
#define LV2_CORE__toggled LV2_CORE_PREFIX "toggled" ///< http://lv2plug.in/ns/lv2core#toggled
#ifdef __cplusplus
extern "C" {
#endif
/**
Plugin Instance Handle.
This is a handle for one particular instance of a plugin. It is valid to
compare to NULL (or 0 for C++) but otherwise the host MUST NOT attempt to
interpret it.
*/
typedef void * LV2_Handle;
/**
Feature.
Features allow hosts to make additional functionality available to plugins
without requiring modification to the LV2 API. Extensions may define new
features and specify the `URI` and `data` to be used if necessary.
Some features, such as lv2:isLive, do not require the host to pass data.
*/
typedef struct _LV2_Feature {
/**
A globally unique, case-sensitive identifier (URI) for this feature.
This MUST be a valid URI string as defined by RFC 3986.
*/
const char * URI;
/**
Pointer to arbitrary data.
The format of this data is defined by the extension which describes the
feature with the given `URI`.
*/
void * data;
} LV2_Feature;
/**
Plugin Descriptor.
This structure provides the core functions necessary to instantiate and use
a plugin.
*/
typedef struct _LV2_Descriptor {
/**
A globally unique, case-sensitive identifier for this plugin.
This MUST be a valid URI string as defined by RFC 3986. All plugins with
the same URI MUST be compatible to some degree, see
http://lv2plug.in/ns/lv2core for details.
*/
const char * URI;
/**
Instantiate the plugin.
Note that instance initialisation should generally occur in activate()
rather than here. If a host calls instantiate(), it MUST call cleanup()
at some point in the future.
@param descriptor Descriptor of the plugin to instantiate.
@param sample_rate Sample rate, in Hz, for the new plugin instance.
@param bundle_path Path to the LV2 bundle which contains this plugin
binary. It MUST include the trailing directory separator (e.g. '/') so
that simply appending a filename will yield the path to that file in the
bundle.
@param features A NULL terminated array of LV2_Feature structs which
represent the features the host supports. Plugins may refuse to
instantiate if required features are not found here. However, hosts MUST
NOT use this as a discovery mechanism: instead, use the RDF data to
determine which features are required and do not attempt to instantiate
unsupported plugins at all. This parameter MUST NOT be NULL, i.e. a host
that supports no features MUST pass a single element array containing
NULL.
@return A handle for the new plugin instance, or NULL if instantiation
has failed.
*/
LV2_Handle (*instantiate)(const struct _LV2_Descriptor * descriptor,
double sample_rate,
const char * bundle_path,
const LV2_Feature *const * features);
/**
Connect a port on a plugin instance to a memory location.
Plugin writers should be aware that the host may elect to use the same
buffer for more than one port and even use the same buffer for both
input and output (see lv2:inPlaceBroken in lv2.ttl).
If the plugin has the feature lv2:hardRTCapable then there are various
things that the plugin MUST NOT do within the connect_port() function;
see lv2core.ttl for details.
connect_port() MUST be called at least once for each port before run()
is called, unless that port is lv2:connectionOptional. The plugin must
pay careful attention to the block size passed to run() since the block
allocated may only just be large enough to contain the data, and is not
guaranteed to remain constant between run() calls.
connect_port() may be called more than once for a plugin instance to
allow the host to change the buffers that the plugin is reading or
writing. These calls may be made before or after activate() or
deactivate() calls.
@param instance Plugin instance containing the port.
@param port Index of the port to connect. The host MUST NOT try to
connect a port index that is not defined in the plugin's RDF data. If
it does, the plugin's behaviour is undefined (a crash is likely).
@param data_location Pointer to data of the type defined by the port
type in the plugin's RDF data (e.g. an array of float for an
lv2:AudioPort). This pointer must be stored by the plugin instance and
used to read/write data when run() is called. Data present at the time
of the connect_port() call MUST NOT be considered meaningful.
*/
void (*connect_port)(LV2_Handle instance,
uint32_t port,
void * data_location);
/**
Initialise a plugin instance and activate it for use.
This is separated from instantiate() to aid real-time support and so
that hosts can reinitialise a plugin instance by calling deactivate()
and then activate(). In this case the plugin instance MUST reset all
state information dependent on the history of the plugin instance except
for any data locations provided by connect_port(). If there is nothing
for activate() to do then this field may be NULL.
When present, hosts MUST call this function once before run() is called
for the first time. This call SHOULD be made as close to the run() call
as possible and indicates to real-time plugins that they are now live,
however plugins MUST NOT rely on a prompt call to run() after
activate().
The host MUST NOT call activate() again until deactivate() has been
called first. If a host calls activate(), it MUST call deactivate() at
some point in the future. Note that connect_port() may be called before
or after activate().
*/
void (*activate)(LV2_Handle instance);
/**
Run a plugin instance for a block.
Note that if an activate() function exists then it must be called before
run(). If deactivate() is called for a plugin instance then run() may
not be called until activate() has been called again.
If the plugin has the feature lv2:hardRTCapable then there are various
things that the plugin MUST NOT do within the run() function (see
lv2core.ttl for details).
As a special case, when `sample_count` is 0, the plugin should update
any output ports that represent a single instant in time (e.g. control
ports, but not audio ports). This is particularly useful for latent
plugins, which should update their latency output port so hosts can
pre-roll plugins to compute latency. Plugins MUST NOT crash when
`sample_count` is 0.
@param instance Instance to be run.
@param sample_count The block size (in samples) for which the plugin
instance must run.
*/
void (*run)(LV2_Handle instance,
uint32_t sample_count);
/**
Deactivate a plugin instance (counterpart to activate()).
Hosts MUST deactivate all activated instances after they have been run()
for the last time. This call SHOULD be made as close to the last run()
call as possible and indicates to real-time plugins that they are no
longer live, however plugins MUST NOT rely on prompt deactivation. If
there is nothing for deactivate() to do then this field may be NULL
Deactivation is not similar to pausing since the plugin instance will be
reinitialised by activate(). However, deactivate() itself MUST NOT fully
reset plugin state. For example, the host may deactivate a plugin, then
store its state (using some extension to do so).
Hosts MUST NOT call deactivate() unless activate() was previously
called. Note that connect_port() may be called before or after
deactivate().
*/
void (*deactivate)(LV2_Handle instance);
/**
Clean up a plugin instance (counterpart to instantiate()).
Once an instance of a plugin has been finished with it must be deleted
using this function. The instance handle passed ceases to be valid after
this call.
If activate() was called for a plugin instance then a corresponding call
to deactivate() MUST be made before cleanup() is called. Hosts MUST NOT
call cleanup() unless instantiate() was previously called.
*/
void (*cleanup)(LV2_Handle instance);
/**
Return additional plugin data defined by some extenion.
A typical use of this facility is to return a struct containing function
pointers to extend the LV2_Descriptor API.
The actual type and meaning of the returned object MUST be specified
precisely by the extension. This function MUST return NULL for any
unsupported URI. If a plugin does not support any extension data, this
field may be NULL.
The host is never responsible for freeing the returned value.
*/
const void * (*extension_data)(const char * uri);
} LV2_Descriptor;
/**
Helper macro needed for LV2_SYMBOL_EXPORT when using C++.
*/
#ifdef __cplusplus
# define LV2_SYMBOL_EXTERN extern "C"
#else
# define LV2_SYMBOL_EXTERN
#endif
/**
Put this (LV2_SYMBOL_EXPORT) before any functions that are to be loaded
by the host as a symbol from the dynamic library.
*/
#ifdef _WIN32
# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __declspec(dllexport)
#else
# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __attribute__((visibility("default")))
#endif
/**
Prototype for plugin accessor function.
Plugins are discovered by hosts using RDF data (not by loading libraries).
See http://lv2plug.in for details on the discovery process, though most
hosts should use an existing library to implement this functionality.
This is the simple plugin discovery API, suitable for most statically
defined plugins. Advanced plugins that need access to their bundle during
discovery can use lv2_lib_descriptor() instead. Plugin libraries MUST
include a function called "lv2_descriptor" or "lv2_lib_descriptor" with
C-style linkage, but SHOULD provide "lv2_descriptor" wherever possible.
When it is time to load a plugin (designated by its URI), the host loads the
plugin's library, gets the lv2_descriptor() function from it, and uses this
function to find the LV2_Descriptor for the desired plugin. Plugins are
accessed by index using values from 0 upwards. This function MUST return
NULL for out of range indices, so the host can enumerate plugins by
increasing `index` until NULL is returned.
Note that `index` has no meaning, hosts MUST NOT depend on it remaining
consistent between loads of the plugin library.
*/
LV2_SYMBOL_EXPORT
const LV2_Descriptor * lv2_descriptor(uint32_t index);
/**
Type of the lv2_descriptor() function in a library (old discovery API).
*/
typedef const LV2_Descriptor *
(*LV2_Descriptor_Function)(uint32_t index);
/**
Handle for a library descriptor.
*/
typedef void* LV2_Lib_Handle;
/**
Descriptor for a plugin library.
To access a plugin library, the host creates an LV2_Lib_Descriptor via the
lv2_lib_descriptor() function in the shared object.
*/
typedef struct {
/**
Opaque library data which must be passed as the first parameter to all
the methods of this struct.
*/
LV2_Lib_Handle handle;
/**
The total size of this struct. This allows for this struct to be
expanded in the future if necessary. This MUST be set by the library to
sizeof(LV2_Lib_Descriptor). The host MUST NOT access any fields of this
struct beyond get_plugin() unless this field indicates they are present.
*/
uint32_t size;
/**
Destroy this library descriptor and free all related resources.
*/
void (*cleanup)(LV2_Lib_Handle handle);
/**
Plugin accessor.
Plugins are accessed by index using values from 0 upwards. Out of range
indices MUST result in this function returning NULL, so the host can
enumerate plugins by increasing `index` until NULL is returned.
*/
const LV2_Descriptor * (*get_plugin)(LV2_Lib_Handle handle,
uint32_t index);
} LV2_Lib_Descriptor;
/**
Prototype for library accessor function.
This is the more advanced discovery API, which allows plugin libraries to
access their bundles during discovery, which makes it possible for plugins to
be dynamically defined by files in their bundle. This API also has an
explicit cleanup function, removing any need for non-portable shared library
destructors. Simple plugins that do not require these features may use
lv2_descriptor() instead.
This is the entry point for a plugin library. Hosts load this symbol from
the library and call this function to obtain a library descriptor which can
be used to access all the contained plugins. The returned object must not
be destroyed (using LV2_Lib_Descriptor::cleanup()) until all plugins loaded
from that library have been destroyed.
*/
LV2_SYMBOL_EXPORT
const LV2_Lib_Descriptor *
lv2_lib_descriptor(const char * bundle_path,
const LV2_Feature *const * features);
/**
Type of the lv2_lib_descriptor() function in an LV2 library.
*/
typedef const LV2_Lib_Descriptor *
(*LV2_Lib_Descriptor_Function)(const char * bundle_path,
const LV2_Feature *const * features);
#ifdef __cplusplus
}
#endif
#endif /* LV2_H_INCLUDED */
/**
@}
*/

103
lv2/lv2/core/lv2_util.h Normal file
View file

@ -0,0 +1,103 @@
/*
Copyright 2016 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@defgroup util Utilities
@ingroup lv2core
@{
*/
#include "lv2/core/lv2.h"
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Return the data for a feature in a features array.
If the feature is not found, NULL is returned. Note that this function is
only useful for features with data, and can not detect features that are
present but have NULL data.
*/
static inline void*
lv2_features_data(const LV2_Feature*const* features,
const char* const uri)
{
if (features) {
for (const LV2_Feature*const* f = features; *f; ++f) {
if (!strcmp(uri, (*f)->URI)) {
return (*f)->data;
}
}
}
return NULL;
}
/**
Query a features array.
This function allows getting several features in one call, and detect
missing required features, with the same caveat of lv2_features_data().
The arguments should be a series of const char* uri, void** data, bool
required, terminated by a NULL URI. The data pointers MUST be initialized
to NULL. For example:
@code
LV2_URID_Log* log = NULL;
LV2_URID_Map* map = NULL;
const char* missing = lv2_features_query(
features,
LV2_LOG__log, &log, false,
LV2_URID__map, &map, true,
NULL);
@endcode
@return NULL on success, otherwise the URI of this missing feature.
*/
static inline const char*
lv2_features_query(const LV2_Feature* const* features, ...)
{
va_list args;
va_start(args, features);
const char* uri = NULL;
while ((uri = va_arg(args, const char*))) {
void** data = va_arg(args, void**);
bool required = va_arg(args, int);
*data = lv2_features_data(features, uri);
if (required && !*data) {
return uri;
}
}
return NULL;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
/**
@}
@}
*/

View file

@ -0,0 +1,204 @@
@prefix dcs: <http://ontologi.es/doap-changeset#> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/lv2core>
a doap:Project ;
rdfs:seeAlso <meta.ttl> ;
doap:license <http://opensource.org/licenses/isc> ;
doap:name "LV2" ;
doap:homepage <http://lv2plug.in> ;
doap:created "2004-04-21" ;
doap:shortdesc "An open and extensible audio plugin standard" ;
doap:programming-language "C" ;
doap:developer <http://plugin.org.uk/swh.xrdf#me> ,
<http://drobilla.net/drobilla#me> ;
doap:maintainer <http://drobilla.net/drobilla#me> ;
doap:release [
doap:revision "16.0" ;
doap:created "2019-02-03" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.16.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add lv2:MIDIPlugin class."
] , [
rdfs:label "Rework port restrictions so that presets can be validated."
]
]
] , [
doap:revision "14.0" ;
doap:created "2016-09-18" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.14.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add lv2_util.h with lv2_features_data() and lv2_features_query()."
] , [
rdfs:label "Add lv2:enabled designation."
]
]
] , [
doap:revision "12.4" ;
doap:created "2015-04-07" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.12.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Relax domain of lv2:minimum lv2:maximum and lv2:default so they can be used to describe properties/parameters as well."
] , [
rdfs:label "Add extern C and visibility attribute to LV2_SYMBOL_EXPORT."
] , [
rdfs:label "Add lv2:isSideChain port property."
]
]
] , [
doap:revision "12.2" ;
doap:created "2014-08-08" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.10.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Clarify lv2_descriptor() and lv2_lib_descriptor() documentation."
]
]
] , [
doap:revision "12.0" ;
doap:created "2014-01-04" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.8.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add lv2:prototype for property inheritance."
]
]
] , [
doap:revision "10.0" ;
doap:created "2013-02-17" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.4.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add lv2:EnvelopePlugin class."
] , [
rdfs:label "Add lv2:control for designating primary event-based control ports."
] , [
rdfs:label "Set range of lv2:designation to lv2:Designation."
] , [
rdfs:label "Make lv2:Parameter rdfs:subClassOf rdf:Property."
] , [
rdfs:label "Reserve minor version 0 for unstable development plugins."
]
]
] , [
doap:revision "8.2" ;
doap:created "2012-10-14" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.2.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Use consistent label style."
]
]
] , [
doap:revision "8.0" ;
doap:created "2012-04-17" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.0.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Fix LV2_SYMBOL_EXPORT and lv2_descriptor prototype for Windows."
] , [
rdfs:label "Add metadata concept of a designation, a channel or parameter description which can be assigned to ports for more intelligent use by hosts."
] , [
rdfs:label "Add new discovery API which allows libraries to read bundle files during discovery, makes library construction/destruction explicit, and adds extensibility to prevent future breakage."
] , [
rdfs:label "Relax the range of lv2:index so it can be used for things other than ports."
] , [
rdfs:label "Remove lv2:Resource, which turned out to be meaningless."
] , [
rdfs:label "Add lv2:CVPort."
] , [
rdfs:label "Merge with unified LV2 package."
]
]
] , [
doap:revision "6.0" ;
doap:created "2011-11-21" ;
doap:file-release <http://lv2plug.in/spec/lv2core-6.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Rename core.lv2 and lv2.ttl to lv2core.lv2 and lv2core.ttl to adhere to modern conventions."
] , [
rdfs:label "Add lv2:extensionData and lv2:ExtensionData for plugins to indicate that they support some URI for extension_data()."
] , [
rdfs:label "Remove lv2config in favour of the simple convention that specifications install headers to standard URI-based paths."
] , [
rdfs:label "Switch to the ISC license, a simple BSD-style license (with permission of all contributors to lv2.h and its ancestor, ladspa.h)."
] , [
rdfs:label "Make lv2core.ttl a valid OWL 2 DL ontology."
] , [
rdfs:label "Improve documentation."
]
]
] , [
doap:revision "4.0" ;
doap:created "2011-03-18" ;
doap:file-release <http://lv2plug.in/spec/lv2core-4.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Make doap:license suggested, but not required (for wrappers)."
] , [
rdfs:label "Define lv2:binary (MUST be in manifest.ttl)."
] , [
rdfs:label "Define lv2:minorVersion and lv2:microVersion (MUST be in manifest.ttl)."
] , [
rdfs:label "Define lv2:documentation and use it to document lv2core."
] , [
rdfs:label "Add lv2:FunctionPlugin and lv2:ConstantPlugin classes."
] , [
rdfs:label "Move lv2:AmplifierPlugin under lv2:DynamicsPlugin."
] , [
rdfs:label "Loosen domain of lv2:optionalFeature and lv2:requiredFeature (to allow re-use in extensions)."
] , [
rdfs:label "Add generic lv2:Resource and lv2:PluginBase classes."
] , [
rdfs:label "Fix definition of lv2:minimum etc. (used for values, not scale points)."
] , [
rdfs:label "More precisely define properties with OWL."
] , [
rdfs:label "Move project metadata to manifest."
] , [
rdfs:label "Add lv2:enumeration port property."
] , [
rdfs:label "Define run() pre-roll special case (sample_count == 0)."
]
]
] , [
doap:revision "3.0" ;
doap:created "2008-11-08" ;
doap:file-release <http://lv2plug.in/spec/lv2core-3.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Require that serialisations refer to ports by symbol rather than index."
] , [
rdfs:label "Minor stylistic changes to lv2.ttl."
] , [
rdfs:label "No header changes."
]
]
] , [
doap:revision "2.0" ;
doap:created "2008-02-10" ;
doap:file-release <http://lv2plug.in/spec/lv2core-2.0.tar.gz> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Initial release."
]
]
] .

1065
lv2/lv2/core/lv2core.ttl Normal file

File diff suppressed because it is too large Load diff

13
lv2/lv2/core/manifest.ttl Normal file
View file

@ -0,0 +1,13 @@
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/lv2core>
a lv2:Specification ;
lv2:minorVersion 16 ;
lv2:microVersion 0 ;
rdfs:seeAlso <lv2core.ttl> .
<http://lv2plug.in/ns/lv2>
a doap:Project ;
rdfs:seeAlso <meta.ttl> .

230
lv2/lv2/core/meta.ttl Normal file
View file

@ -0,0 +1,230 @@
@prefix dcs: <http://ontologi.es/doap-changeset#> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix meta: <http://lv2plug.in/ns/meta#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://opensource.org/licenses/isc>
rdf:value """
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
""" .
<http://lv2plug.in/ns/lv2>
a doap:Project ;
lv2:symbol "lv2" ;
doap:name "LV2" ;
doap:license <http://opensource.org/licenses/isc> ;
doap:shortdesc "The LV2 Plugin Interface Project" ;
doap:description "LV2 is a plugin standard for audio systems. It defines a minimal yet extensible C API for plugin code and a format for plugin bundles" ;
doap:created "2006-05-10" ;
doap:homepage <http://lv2plug.in/> ;
doap:mailing-list <http://lists.lv2plug.in/listinfo.cgi/devel-lv2plug.in> ;
doap:programming-language "C" ;
doap:repository [
a doap:SVNRepository ;
doap:location <http://lv2plug.in/repo>
] ;
doap:developer <http://drobilla.net/drobilla#me> ,
<http://plugin.org.uk/swh.xrdf#me> ;
doap:helper meta:larsl ,
meta:bmwiedemann ,
meta:gabrbedd ,
meta:daste ,
meta:kfoltman ,
meta:paniq ;
doap:release [
doap:revision "1.16.1" ;
doap:created "2019-03-27" ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Improve documentation."
]
]
] , [
doap:revision "1.16.0" ;
doap:created "2019-02-03" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.16.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add core/attributes.h utility header."
] , [
rdfs:label "eg-sampler: Add waveform display to UI."
] , [
rdfs:label "eg-midigate: Respond to \"all notes off\" MIDI message."
] , [
rdfs:label "Simplify use of lv2specgen."
] , [
rdfs:label "Add lv2_validate utility."
] , [
rdfs:label "Install headers to simpler paths."
] , [
rdfs:label "Aggressively deprecate uri-map and event extensions."
] , [
rdfs:label "Upgrade build system and fix building with Python 3.7."
]
]
] , [
doap:revision "1.14.0" ;
doap:created "2016-09-19" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.14.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "eg-scope: Don't feed back UI state updates."
] , [
rdfs:label "eg-sampler: Fix handling of state file paths."
] , [
rdfs:label "eg-sampler: Support thread-safe state restoration."
]
]
] , [
doap:revision "1.12.0" ;
doap:created "2015-04-07" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.12.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "eg-sampler: Support patch:Get, and request initial state from UI."
] , [
rdfs:label "eg-sampler: Add gain parameter."
] , [
rdfs:label "Fix merging of version histories in specification documentation."
] , [
rdfs:label "Improve API documentation."
] , [
rdfs:label "Simplify property restrictions by removing redundancy."
]
]
] , [
doap:revision "1.10.0" ;
doap:created "2014-08-08" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.10.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "lv2specgen: Display deprecated warning on classes marked owl:deprecated."
] , [
rdfs:label "Fix -Wconversion warnings in headers."
] , [
rdfs:label "Upgrade to waf 1.7.16."
]
]
] , [
doap:revision "1.8.0" ;
doap:created "2014-01-04" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.8.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add scope example plugin from Robin Gareus."
] , [
rdfs:label "lv2specgen: Fix links to externally defined terms."
] , [
rdfs:label "Install lv2specgen for use by other projects."
]
]
] , [
doap:revision "1.6.0" ;
doap:created "2013-08-09" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.6.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Fix port indices of metronome example."
] , [
rdfs:label "Fix lv2specgen usage from command line."
] , [
rdfs:label "Upgrade to waf 1.7.11."
]
]
] , [
doap:revision "1.4.0" ;
doap:created "2013-02-17" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.4.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add metronome example plugin to demonstrate sample accurate tempo sync."
] , [
rdfs:label "Generate book-style HTML documentation from example plugins."
]
]
] , [
doap:revision "1.2.0" ;
doap:created "2012-10-14" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.2.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Move all project metadata for extensions (e.g. change log) to separate files to spare hosts from loading them during discovery."
] , [
rdfs:label "Use stricter datatype definitions conformant with the XSD and OWL specifications for better validation."
]
]
] , [
doap:revision "1.0.0" ;
doap:created "2012-04-16" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.0.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Initial release as a unified project. Projects can now simply depend on the pkg-config package 'lv2' for all official LV2 APIs."
] , [
rdfs:label "New extensions: atom, log, parameters, patch, port-groups, port-props, resize-port, state, time, worker."
]
]
] .
<http://drobilla.net/drobilla#me>
a foaf:Person ;
foaf:name "David Robillard" ;
foaf:mbox <mailto:d@drobilla.net> ;
rdfs:seeAlso <http://drobilla.net/drobilla> .
<http://plugin.org.uk/swh.xrdf#me>
a foaf:Person ;
foaf:name "Steve Harris" ;
foaf:mbox <mailto:steve@plugin.org.uk> ;
rdfs:seeAlso <http://plugin.org.uk/swh.xrdf> .
meta:larsl
a foaf:Person ;
foaf:name "Lars Luthman" ;
foaf:mbox <mailto:lars.luthman@gmail.com> .
meta:gabrbedd
a foaf:Person ;
foaf:name "Gabriel M. Beddingfield" ;
foaf:mbox <mailto:gabrbedd@gmail.com> .
meta:daste
a foaf:Person ;
foaf:name "Stefano D'Angelo" ;
foaf:mbox <mailto:zanga.mail@gmail.com> .
meta:kfoltman
a foaf:Person ;
foaf:name "Krzysztof Foltman" ;
foaf:mbox <mailto:wdev@foltman.com> .
meta:paniq
a foaf:Person ;
foaf:name "Leonard Ritter" ;
foaf:mbox <mailto:paniq@paniq.org> .
meta:harry
a foaf:Person ;
foaf:name "Harry van Haaren" ;
foaf:mbox <harryhaaren@gmail.com> .
meta:bmwiedemann
a foaf:Person ;
foaf:name "Bernhard M. Wiedemann" ;
foaf:mbox <bwiedemann@suse.de> .

View file

@ -0,0 +1,68 @@
/*
LV2 Data Access Extension
Copyright 2008-2016 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@defgroup data-access Data Access
Access to plugin extension_data() for UIs, see
<http://lv2plug.in/ns/ext/data-access> for details.
@{
*/
#ifndef LV2_DATA_ACCESS_H
#define LV2_DATA_ACCESS_H
#define LV2_DATA_ACCESS_URI "http://lv2plug.in/ns/ext/data-access" ///< http://lv2plug.in/ns/ext/data-access
#define LV2_DATA_ACCESS_PREFIX LV2_DATA_ACCESS_URI "#" ///< http://lv2plug.in/ns/ext/data-access#
#ifdef __cplusplus
extern "C" {
#endif
/**
The data field of the LV2_Feature for this extension.
To support this feature the host must pass an LV2_Feature struct to the
instantiate method with URI "http://lv2plug.in/ns/ext/data-access"
and data pointed to an instance of this struct.
*/
typedef struct {
/**
A pointer to a method the UI can call to get data (of a type specified
by some other extension) from the plugin.
This call never is never guaranteed to return anything, UIs should
degrade gracefully if direct access to the plugin data is not possible
(in which case this function will return NULL).
This is for access to large data that can only possibly work if the UI
and plugin are running in the same process. For all other things, use
the normal LV2 UI communication system.
*/
const void* (*data_access)(const char* uri);
} LV2_Extension_Data_Feature;
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV2_DATA_ACCESS_H */
/**
@}
*/

View file

@ -0,0 +1,23 @@
@prefix da: <http://lv2plug.in/ns/ext/data-access#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/ext/data-access>
a lv2:Feature ;
rdfs:seeAlso <data-access.h> ,
<lv2-data-access.doap.ttl> ;
lv2:documentation """
<p>This extension defines a feature, LV2_Extension_Data_Feature, which provides
access to LV2_Descriptor::extension_data() for plugin UIs or other potentially
remote users of a plugin.</p>
<p>Note that the use of this extension by UIs violates the important principle
of UI/plugin separation, and is potentially a source of many problems.
Accordingly, <strong>use of this extension is highly discouraged</strong>, and
plugins should not expect hosts to support it, since it is often impossible to
do so.</p>
<p>To support this feature the host must pass an LV2_Feature struct to the
instantiate method with URI LV2_DATA_ACCESS_URI and data pointed to an instance
of LV2_Extension_Data_Feature.</p>
""" .

View file

@ -0,0 +1,58 @@
@prefix dcs: <http://ontologi.es/doap-changeset#> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/ext/data-access>
a doap:Project ;
rdfs:seeAlso <data-access.h> ;
doap:license <http://opensource.org/licenses/isc> ;
doap:name "LV2 Data Access" ;
doap:shortdesc "Provides access to LV2_Descriptor::extension_data()." ;
doap:created "2008-00-00" ;
doap:developer <http://drobilla.net/drobilla#me> ;
doap:release [
doap:revision "1.6" ;
doap:created "2012-04-17" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.0.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Merge with unified LV2 package."
]
]
] , [
doap:revision "1.4" ;
doap:created "2011-11-21" ;
doap:file-release <http://lv2plug.in/spec/lv2-data-access-1.4.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Update packaging."
] , [
rdfs:label "Improve documentation."
]
]
] , [
doap:revision "1.2" ;
doap:created "2011-05-26" ;
doap:file-release <http://lv2plug.in/spec/lv2-data-access-1.2.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Add build system for installation."
] , [
rdfs:label "Switch to ISC license."
]
]
] , [
doap:revision "1.0" ;
doap:created "2010-10-04" ;
doap:file-release <http://lv2plug.in/spec/lv2-data-access-1.0.tar.gz> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Initial release."
]
]
] .

View file

@ -0,0 +1,9 @@
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/ext/data-access>
a lv2:Specification ;
lv2:minorVersion 1 ;
lv2:microVersion 6 ;
rdfs:seeAlso <data-access.ttl> .

View file

@ -0,0 +1,151 @@
/*
Dynamic manifest specification for LV2
Copyright 2008-2011 Stefano D'Angelo <zanga.mail@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@defgroup dynmanifest Dynamic Manifest
Support for dynamic data generation, see
<http://lv2plug.in/ns/ext/dynmanifest> for details.
@{
*/
#ifndef LV2_DYN_MANIFEST_H_INCLUDED
#define LV2_DYN_MANIFEST_H_INCLUDED
#include "lv2/core/lv2.h"
#include <stdio.h>
#define LV2_DYN_MANIFEST_URI "http://lv2plug.in/ns/ext/dynmanifest" ///< http://lv2plug.in/ns/ext/dynmanifest
#define LV2_DYN_MANIFEST_PREFIX LV2_DYN_MANIFEST_URI "#" ///< http://lv2plug.in/ns/ext/dynmanifest#
#ifdef __cplusplus
extern "C" {
#endif
/**
Dynamic manifest generator handle.
This handle indicates a particular status of a dynamic manifest generator.
The host MUST NOT attempt to interpret it and, unlikely LV2_Handle, it is
NOT even valid to compare this to NULL. The dynamic manifest generator MAY
use it to reference internal data.
*/
typedef void * LV2_Dyn_Manifest_Handle;
/**
Generate the dynamic manifest.
@param handle Pointer to an uninitialized dynamic manifest generator handle.
@param features NULL terminated array of LV2_Feature structs which represent
the features the host supports. The dynamic manifest generator may refuse to
(re)generate the dynamic manifest if required features are not found here
(however hosts SHOULD NOT use this as a discovery mechanism, instead of
reading the static manifest file). This array must always exist; if a host
has no features, it MUST pass a single element array containing NULL.
@return 0 on success, otherwise a non-zero error code. The host SHOULD
evaluate the result of the operation by examining the returned value and
MUST NOT try to interpret the value of handle.
*/
int lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle * handle,
const LV2_Feature *const * features);
/**
Fetch a "list" of subject URIs described in the dynamic manifest.
The dynamic manifest generator has to fill the resource only with the needed
triples to make the host aware of the "objects" it wants to expose. For
example, if the plugin library exposes a regular LV2 plugin, it should
output only a triple like the following:
<http://example.org/plugin> a lv2:Plugin .
The objects that are elegible for exposure are those that would need to be
represented by a subject node in a static manifest.
@param handle Dynamic manifest generator handle.
@param fp FILE * identifying the resource the host has to set up for the
dynamic manifest generator. The host MUST pass a writable, empty resource to
this function, and the dynamic manifest generator MUST ONLY perform write
operations on it at the end of the stream (e.g., using only fprintf(),
fwrite() and similar).
@return 0 on success, otherwise a non-zero error code.
*/
int lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle,
FILE * fp);
/**
Function that fetches data related to a specific URI.
The dynamic manifest generator has to fill the resource with data related to
object represented by the given URI. For example, if the library exposes a
regular LV2 plugin whose URI, as retrieved by the host using
lv2_dyn_manifest_get_subjects() is http://example.org/plugin then it
should output something like:
<pre>
<http://example.org/plugin>
a lv2:Plugin ;
doap:name "My Plugin" ;
lv2:binary <mylib.so> ;
etc:etc "..." .
</pre>
@param handle Dynamic manifest generator handle.
@param fp FILE * identifying the resource the host has to set up for the
dynamic manifest generator. The host MUST pass a writable resource to this
function, and the dynamic manifest generator MUST ONLY perform write
operations on it at the current position of the stream (e.g. using only
fprintf(), fwrite() and similar).
@param uri URI to get data about (in the "plain" form, i.e., absolute URI
without Turtle prefixes).
@return 0 on success, otherwise a non-zero error code.
*/
int lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle,
FILE * fp,
const char * uri);
/**
Function that ends the operations on the dynamic manifest generator.
This function SHOULD be used by the dynamic manifest generator to perform
cleanup operations, etc.
Once this function is called, referring to handle will cause undefined
behavior.
@param handle Dynamic manifest generator handle.
*/
void lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle);
#ifdef __cplusplus
}
#endif
#endif /* LV2_DYN_MANIFEST_H_INCLUDED */
/**
@}
*/

View file

@ -0,0 +1,85 @@
@prefix dman: <http://lv2plug.in/ns/ext/dynmanifest#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://lv2plug.in/ns/ext/dynmanifest>
a lv2:Specification ;
rdfs:seeAlso <dynmanifest.h> ,
<lv2-dynmanifest.doap.ttl> ;
lv2:documentation """
<p>The LV2 API, on its own, cannot be used to write plugin libraries where data
is dynamically generated at runtime (e.g. API wrappers), since LV2 requires
needed information to be provided in one or more static data (RDF) files. This
API addresses this limitation by extending the LV2 API.</p>
<p>To detect that a plugin library implements a dynamic manifest generator,
the host checks its static manifest for a description like:</p>
<pre class="turtle-code">
&lt;http://example.org/my-dynamic-manifest&gt;
a dman:DynManifest ;
lv2:binary &lt;mydynmanifest.so&gt; .
</pre>
<p>To load the data, the host loads the library
(e.g. <code>mydynmanifest.so</code>) as usual and fetches the dynamic Turtle
data from it using this API.</p>
<p>The host is allowed to request regeneration of the dynamic manifest multiple
times, and the plugin library is expected to provide updated data if/when
possible. All data and references provided via this API before the last
regeneration of the dynamic manifest is to be considered invalid by the host,
including plugin descriptors whose URIs were discovered using this API.</p>
<h3>Accessing Data</h3>
<p>Whenever a host wants to access data using this API, it could:</p>
<ol>
<li>Call lv2_dyn_manifest_open().</li>
<li>Create a FILE for functions to write data to (e.g. using tmpfile()).</li>
<li>Get a <q>list</q> of exposed subject URIs using
lv2_dyn_manifest_get_subjects().</li>
<li>Call lv2_dyn_manifest_get_data() for each URI of interest to
get the data related to that URI (which can be written to any FILE).</li>
<li>Call lv2_dyn_manifest_close().</li>
<li>Parse the content of the FILE(s).</li>
<li>Free/delete/unlink the FILE(s).</li>
</ol>
<p>Each call to the above mentioned dynamic manifest functions MUST write a
complete, valid Turtle document (including all needed prefix definitions) to
the output FILE.</p>
<p>Each call to lv2_dyn_manifest_open() causes the (re)generation of the
dynamic manifest data, and invalidates all data fetched before the call.</p>
<p>In case the plugin library uses this same API to access other dynamic
manifests, it MUST implement some mechanism to avoid potentially endless loops
(such as A loads B, B loads A, etc.) and, in case such a loop is detected, the
operation MUST fail. For this purpose, use of a static boolean flag is
suggested.</p>
<h3>Threading Rules</h3>
<p>All of the functions defined by this specification belong to the Discovery
class.</p>
""" .
dman:DynManifest
a rdfs:Class ;
rdfs:label "Dynamic Manifest" ;
rdfs:subClassOf [
a owl:Restriction ;
owl:onProperty lv2:binary ;
owl:minCardinality 1 ;
rdfs:comment "A DynManifest MUST have at least 1 lv2:binary, which MUST implement all the functions defined in dynmanifest.h."
] ;
rdfs:comment """The class which represents a dynamic manifest generator.
There MUST NOT be any instances of dman:DynManifest in the generated manifest.
All relative URIs in the generated data MUST be relative to the base path that would be used to parse a normal LV2 manifest (the bundle path).""" .

View file

@ -0,0 +1,55 @@
@prefix dcs: <http://ontologi.es/doap-changeset#> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/ext/dynmanifest>
a doap:Project ;
doap:license <http://opensource.org/licenses/isc> ;
doap:name "LV2 Dynamic Manifest" ;
doap:homepage <http://naspro.atheme.org> ;
doap:created "2009-06-13" ;
doap:shortdesc "Support for dynamic data generation." ;
doap:programming-language "C" ;
doap:developer <http://lv2plug.in/ns/meta#daste> ;
doap:release [
doap:revision "1.6" ;
doap:created "2012-10-14" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.2.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Use consistent label style."
]
]
] , [
doap:revision "1.4" ;
doap:created "2012-04-17" ;
doap:file-release <http://lv2plug.in/spec/lv2-1.0.0.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Merge with unified LV2 package."
]
]
] , [
doap:revision "1.2" ;
doap:created "2011-11-21" ;
doap:file-release <http://lv2plug.in/spec/lv2-dynmanifest-1.2.tar.bz2> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Improve documentation."
]
]
] , [
doap:revision "1.0" ;
doap:created "2010-04-10" ;
doap:file-release <http://lv2plug.in/spec/lv2-dyn-manifest-1.0.tar.gz> ;
dcs:blame <http://drobilla.net/drobilla#me> ;
dcs:changeset [
dcs:item [
rdfs:label "Initial release."
]
]
] .

View file

@ -0,0 +1,8 @@
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://lv2plug.in/ns/ext/dynmanifest>
a lv2:Specification ;
lv2:minorVersion 1 ;
lv2:microVersion 6 ;
rdfs:seeAlso <dynmanifest.ttl> .

View file

@ -0,0 +1,268 @@
/*
Copyright 2008-2015 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@file event-helpers.h Helper functions for the LV2 Event extension
<http://lv2plug.in/ns/ext/event>.
*/
#ifndef LV2_EVENT_HELPERS_H
#define LV2_EVENT_HELPERS_H
#include "lv2/core/attributes.h"
#include "lv2/event/event.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
LV2_DISABLE_DEPRECATION_WARNINGS
/** @file
* Helper functions for the LV2 Event extension
* <http://lv2plug.in/ns/ext/event>.
*
* These functions are provided for convenience only, use of them is not
* required for supporting lv2ev (i.e. the events extension is defined by the
* raw buffer format described in lv2_event.h and NOT by this API).
*
* Note that these functions are all static inline which basically means:
* do not take the address of these functions. */
/** Pad a size to 64 bits (for event sizes) */
static inline uint16_t
lv2_event_pad_size(uint16_t size)
{
return (uint16_t)(size + 7U) & (uint16_t)(~7U);
}
/** Initialize (empty, reset..) an existing event buffer.
* The contents of buf are ignored entirely and overwritten, except capacity
* which is unmodified. */
static inline void
lv2_event_buffer_reset(LV2_Event_Buffer* buf,
uint16_t stamp_type,
uint8_t* data)
{
buf->data = data;
buf->header_size = sizeof(LV2_Event_Buffer);
buf->stamp_type = stamp_type;
buf->event_count = 0;
buf->size = 0;
}
/** Allocate a new, empty event buffer. */
static inline LV2_Event_Buffer*
lv2_event_buffer_new(uint32_t capacity, uint16_t stamp_type)
{
const size_t size = sizeof(LV2_Event_Buffer) + capacity;
LV2_Event_Buffer* buf = (LV2_Event_Buffer*)malloc(size);
if (buf != NULL) {
buf->capacity = capacity;
lv2_event_buffer_reset(buf, stamp_type, (uint8_t *)(buf + 1));
return buf;
}
return NULL;
}
/** An iterator over an LV2_Event_Buffer.
*
* Multiple simultaneous read iterators over a single buffer is fine,
* but changing the buffer invalidates all iterators (e.g. RW Lock). */
typedef struct {
LV2_Event_Buffer* buf;
uint32_t offset;
} LV2_Event_Iterator;
/** Reset an iterator to point to the start of `buf`.
* @return True if `iter` is valid, otherwise false (buffer is empty) */
static inline bool
lv2_event_begin(LV2_Event_Iterator* iter,
LV2_Event_Buffer* buf)
{
iter->buf = buf;
iter->offset = 0;
return (buf->size > 0);
}
/** Check if `iter` is valid.
* @return True if `iter` is valid, otherwise false (past end of buffer) */
static inline bool
lv2_event_is_valid(LV2_Event_Iterator* iter)
{
return (iter->buf && (iter->offset < iter->buf->size));
}
/** Advance `iter` forward one event.
* `iter` must be valid.
* @return True if `iter` is valid, otherwise false (reached end of buffer) */
static inline bool
lv2_event_increment(LV2_Event_Iterator* iter)
{
if (!lv2_event_is_valid(iter)) {
return false;
}
LV2_Event* const ev = (LV2_Event*)(iter->buf->data + iter->offset);
iter->offset += lv2_event_pad_size(
(uint16_t)((uint16_t)sizeof(LV2_Event) + ev->size));
return true;
}
/** Dereference an event iterator (get the event currently pointed at).
* `iter` must be valid.
* `data` if non-NULL, will be set to point to the contents of the event
* returned.
* @return A Pointer to the event `iter` is currently pointing at, or NULL
* if the end of the buffer is reached (in which case `data` is
* also set to NULL). */
static inline LV2_Event*
lv2_event_get(LV2_Event_Iterator* iter,
uint8_t** data)
{
if (!lv2_event_is_valid(iter)) {
return NULL;
}
LV2_Event* const ev = (LV2_Event*)(iter->buf->data + iter->offset);
if (data) {
*data = (uint8_t*)ev + sizeof(LV2_Event);
}
return ev;
}
/** Write an event at `iter`.
* The event (if any) pointed to by `iter` will be overwritten, and `iter`
* incremented to point to the following event (i.e. several calls to this
* function can be done in sequence without twiddling iter in-between).
* @return True if event was written, otherwise false (buffer is full). */
static inline bool
lv2_event_write(LV2_Event_Iterator* iter,
uint32_t frames,
uint32_t subframes,
uint16_t type,
uint16_t size,
const uint8_t* data)
{
if (!iter->buf) {
return false;
}
if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) {
return false;
}
LV2_Event* const ev = (LV2_Event*)(iter->buf->data + iter->offset);
ev->frames = frames;
ev->subframes = subframes;
ev->type = type;
ev->size = size;
memcpy((uint8_t*)ev + sizeof(LV2_Event), data, size);
++iter->buf->event_count;
size = lv2_event_pad_size((uint16_t)(sizeof(LV2_Event) + size));
iter->buf->size += size;
iter->offset += size;
return true;
}
/** Reserve space for an event in the buffer and return a pointer to
the memory where the caller can write the event data, or NULL if there
is not enough room in the buffer. */
static inline uint8_t*
lv2_event_reserve(LV2_Event_Iterator* iter,
uint32_t frames,
uint32_t subframes,
uint16_t type,
uint16_t size)
{
const uint16_t total_size = (uint16_t)(sizeof(LV2_Event) + size);
if (iter->buf->capacity - iter->buf->size < total_size) {
return NULL;
}
LV2_Event* const ev = (LV2_Event*)(iter->buf->data + iter->offset);
ev->frames = frames;
ev->subframes = subframes;
ev->type = type;
ev->size = size;
++iter->buf->event_count;
const uint16_t padded_size = lv2_event_pad_size(total_size);
iter->buf->size += padded_size;
iter->offset += padded_size;
return (uint8_t*)ev + sizeof(LV2_Event);
}
/** Write an event at `iter`.
* The event (if any) pointed to by `iter` will be overwritten, and `iter`
* incremented to point to the following event (i.e. several calls to this
* function can be done in sequence without twiddling iter in-between).
* @return True if event was written, otherwise false (buffer is full). */
static inline bool
lv2_event_write_event(LV2_Event_Iterator* iter,
const LV2_Event* ev,
const uint8_t* data)
{
const uint16_t total_size = (uint16_t)(sizeof(LV2_Event) + ev->size);
if (iter->buf->capacity - iter->buf->size < total_size) {
return false;
}
LV2_Event* const write_ev = (LV2_Event*)(iter->buf->data + iter->offset);
*write_ev = *ev;
memcpy((uint8_t*)write_ev + sizeof(LV2_Event), data, ev->size);
++iter->buf->event_count;
const uint16_t padded_size = lv2_event_pad_size(total_size);
iter->buf->size += padded_size;
iter->offset += padded_size;
return true;
}
LV2_RESTORE_WARNINGS
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV2_EVENT_HELPERS_H */

303
lv2/lv2/event/event.h Normal file
View file

@ -0,0 +1,303 @@
/*
Copyright 2008-2016 David Robillard <http://drobilla.net>
Copyright 2006-2007 Lars Luthman <lars.luthman@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@defgroup event Event
Generic time-stamped events, see <http://lv2plug.in/ns/ext/event> for
details.
@{
*/
#ifndef LV2_EVENT_H
#define LV2_EVENT_H
#define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" ///< http://lv2plug.in/ns/ext/event
#define LV2_EVENT_PREFIX LV2_EVENT_URI "#" ///< http://lv2plug.in/ns/ext/event#
#define LV2_EVENT__Event LV2_EVENT_PREFIX "Event" ///< http://lv2plug.in/ns/ext/event#Event
#define LV2_EVENT__EventPort LV2_EVENT_PREFIX "EventPort" ///< http://lv2plug.in/ns/ext/event#EventPort
#define LV2_EVENT__FrameStamp LV2_EVENT_PREFIX "FrameStamp" ///< http://lv2plug.in/ns/ext/event#FrameStamp
#define LV2_EVENT__TimeStamp LV2_EVENT_PREFIX "TimeStamp" ///< http://lv2plug.in/ns/ext/event#TimeStamp
#define LV2_EVENT__generatesTimeStamp LV2_EVENT_PREFIX "generatesTimeStamp" ///< http://lv2plug.in/ns/ext/event#generatesTimeStamp
#define LV2_EVENT__generic LV2_EVENT_PREFIX "generic" ///< http://lv2plug.in/ns/ext/event#generic
#define LV2_EVENT__inheritsEvent LV2_EVENT_PREFIX "inheritsEvent" ///< http://lv2plug.in/ns/ext/event#inheritsEvent
#define LV2_EVENT__inheritsTimeStamp LV2_EVENT_PREFIX "inheritsTimeStamp" ///< http://lv2plug.in/ns/ext/event#inheritsTimeStamp
#define LV2_EVENT__supportsEvent LV2_EVENT_PREFIX "supportsEvent" ///< http://lv2plug.in/ns/ext/event#supportsEvent
#define LV2_EVENT__supportsTimeStamp LV2_EVENT_PREFIX "supportsTimeStamp" ///< http://lv2plug.in/ns/ext/event#supportsTimeStamp
#define LV2_EVENT_AUDIO_STAMP 0 ///< Special timestamp type for audio frames
#include "lv2/core/attributes.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
LV2_DISABLE_DEPRECATION_WARNINGS
/**
The best Pulses Per Quarter Note for tempo-based uint32_t timestamps.
Equal to 2^12 * 5 * 7 * 9 * 11 * 13 * 17, which is evenly divisble
by all integers from 1 through 18 inclusive, and powers of 2 up to 2^12.
*/
LV2_DEPRECATED
static const uint32_t LV2_EVENT_PPQN = 3136573440U;
/**
An LV2 event (header only).
LV2 events are generic time-stamped containers for any type of event.
The type field defines the format of a given event's contents.
This struct defines the header of an LV2 event. An LV2 event is a single
chunk of POD (plain old data), usually contained in a flat buffer (see
LV2_EventBuffer below). Unless a required feature says otherwise, hosts may
assume a deep copy of an LV2 event can be created safely using a simple:
memcpy(ev_copy, ev, sizeof(LV2_Event) + ev->size); (or equivalent)
*/
LV2_DEPRECATED
typedef struct {
/**
The frames portion of timestamp. The units used here can optionally be
set for a port (with the lv2ev:timeUnits property), otherwise this is
audio frames, corresponding to the sample_count parameter of the LV2 run
method (e.g. frame 0 is the first frame for that call to run).
*/
uint32_t frames;
/**
The sub-frames portion of timestamp. The units used here can optionally
be set for a port (with the lv2ev:timeUnits property), otherwise this is
1/(2^32) of an audio frame.
*/
uint32_t subframes;
/**
The type of this event, as a number which represents some URI
defining an event type. This value MUST be some value previously
returned from a call to the uri_to_id function defined in the LV2
URI map extension (see lv2_uri_map.h).
There are special rules which must be followed depending on the type
of an event. If the plugin recognizes an event type, the definition
of that event type will describe how to interpret the event, and
any required behaviour. Otherwise, if the type is 0, this event is a
non-POD event and lv2_event_unref MUST be called if the event is
'dropped' (see above). Even if the plugin does not understand an event,
it may pass the event through to an output by simply copying (and NOT
calling lv2_event_unref). These rules are designed to allow for generic
event handling plugins and large non-POD events, but with minimal hassle
on simple plugins that "don't care" about these more advanced features.
*/
uint16_t type;
/**
The size of the data portion of this event in bytes, which immediately
follows. The header size (12 bytes) is not included in this value.
*/
uint16_t size;
/* size bytes of data follow here */
} LV2_Event;
/**
A buffer of LV2 events (header only).
Like events (which this contains) an event buffer is a single chunk of POD:
the entire buffer (including contents) can be copied with a single memcpy.
The first contained event begins sizeof(LV2_EventBuffer) bytes after the
start of this struct.
After this header, the buffer contains an event header (defined by struct
LV2_Event), followed by that event's contents (padded to 64 bits), followed
by another header, etc:
| | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | |
|FRAMES |SUBFRMS|TYP|LEN|DATA..DATA..PAD|FRAMES | ...
*/
LV2_DEPRECATED
typedef struct {
/**
The contents of the event buffer. This may or may not reside in the
same block of memory as this header, plugins must not assume either.
The host guarantees this points to at least capacity bytes of allocated
memory (though only size bytes of that are valid events).
*/
uint8_t* data;
/**
The size of this event header in bytes (including everything).
This is to allow for extending this header in the future without
breaking binary compatibility. Whenever this header is copied,
it MUST be done using this field (and NOT the sizeof this struct).
*/
uint16_t header_size;
/**
The type of the time stamps for events in this buffer.
As a special exception, '0' always means audio frames and subframes
(1/UINT32_MAX'th of a frame) in the sample rate passed to instantiate.
INPUTS: The host must set this field to the numeric ID of some URI
defining the meaning of the frames/subframes fields of contained events
(obtained by the LV2 URI Map uri_to_id function with the URI of this
extension as the 'map' argument, see lv2_uri_map.h). The host must
never pass a plugin a buffer which uses a stamp type the plugin does not
'understand'. The value of this field must never change, except when
connect_port is called on the input port, at which time the host MUST
have set the stamp_type field to the value that will be used for all
subsequent run calls.
OUTPUTS: The plugin may set this to any value that has been returned
from uri_to_id with the URI of this extension for a 'map' argument.
When connected to a buffer with connect_port, output ports MUST set this
field to the type of time stamp they will be writing. On any call to
connect_port on an event input port, the plugin may change this field on
any output port, it is the responsibility of the host to check if any of
these values have changed and act accordingly.
*/
uint16_t stamp_type;
/**
The number of events in this buffer.
INPUTS: The host must set this field to the number of events contained
in the data buffer before calling run(). The plugin must not change
this field.
OUTPUTS: The plugin must set this field to the number of events it has
written to the buffer before returning from run(). Any initial value
should be ignored by the plugin.
*/
uint32_t event_count;
/**
The size of the data buffer in bytes.
This is set by the host and must not be changed by the plugin.
The host is allowed to change this between run() calls.
*/
uint32_t capacity;
/**
The size of the initial portion of the data buffer containing data.
INPUTS: The host must set this field to the number of bytes used
by all events it has written to the buffer (including headers)
before calling the plugin's run().
The plugin must not change this field.
OUTPUTS: The plugin must set this field to the number of bytes
used by all events it has written to the buffer (including headers)
before returning from run().
Any initial value should be ignored by the plugin.
*/
uint32_t size;
} LV2_Event_Buffer;
/**
Opaque pointer to host data.
*/
LV2_DEPRECATED
typedef void* LV2_Event_Callback_Data;
/**
Non-POD events feature.
To support this feature the host must pass an LV2_Feature struct to the
plugin's instantiate method with URI "http://lv2plug.in/ns/ext/event"
and data pointed to an instance of this struct. Note this feature
is not mandatory to support the event extension.
*/
LV2_DEPRECATED
typedef struct {
/**
Opaque pointer to host data.
The plugin MUST pass this to any call to functions in this struct.
Otherwise, it must not be interpreted in any way.
*/
LV2_Event_Callback_Data callback_data;
/**
Take a reference to a non-POD event.
If a plugin receives an event with type 0, it means the event is a
pointer to some object in memory and not a flat sequence of bytes
in the buffer. When receiving a non-POD event, the plugin already
has an implicit reference to the event. If the event is stored AND
passed to an output, lv2_event_ref MUST be called on that event.
If the event is only stored OR passed through, this is not necessary
(as the plugin already has 1 implicit reference).
@param event An event received at an input that will not be copied to
an output or stored in any way.
@param context The calling context. Like event types, this is a mapped
URI, see lv2_context.h. Simple plugin with just a run() method should
pass 0 here (the ID of the 'standard' LV2 run context). The host
guarantees that this function is realtime safe iff the context is
realtime safe.
PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS.
*/
uint32_t (*lv2_event_ref)(LV2_Event_Callback_Data callback_data,
LV2_Event* event);
/**
Drop a reference to a non-POD event.
If a plugin receives an event with type 0, it means the event is a
pointer to some object in memory and not a flat sequence of bytes
in the buffer. If the plugin does not pass the event through to
an output or store it internally somehow, it MUST call this function
on the event (more information on using non-POD events below).
@param event An event received at an input that will not be copied to an
output or stored in any way.
@param context The calling context. Like event types, this is a mapped
URI, see lv2_context.h. Simple plugin with just a run() method should
pass 0 here (the ID of the 'standard' LV2 run context). The host
guarantees that this function is realtime safe iff the context is
realtime safe.
PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS.
*/
uint32_t (*lv2_event_unref)(LV2_Event_Callback_Data callback_data,
LV2_Event* event);
} LV2_Event_Feature;
LV2_RESTORE_WARNINGS
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV2_EVENT_H */
/**
@}
*/

Some files were not shown because too many files have changed in this diff Show more