Merge branch 'develop' into master

This commit is contained in:
Jean Pierre Cimalando 2020-10-15 21:59:39 +02:00
commit a6bb8d676b
293 changed files with 26202 additions and 6047 deletions

3
.gitattributes vendored Normal file
View file

@ -0,0 +1,3 @@
.* export-ignore
.*/** export-ignore
appveyor.yml export-ignore

2
.gitignore vendored
View file

@ -21,6 +21,8 @@ clients/sfzprint
/vst/download/
/editor/external/fluentui-system-icons/
# gh-pages unstaged files:
_api/
_site/

4
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "external/abseil-cpp"]
path = external/abseil-cpp
url = https://github.com/abseil/abseil-cpp
url = https://github.com/abseil/abseil-cpp.git
branch = lts_2020_02_25
shallow = true
[submodule "vst/external/VST_SDK/VST3_SDK/base"]
@ -16,6 +16,6 @@
url = https://github.com/sfztools/vst3_public_sdk.git
shallow = true
[submodule "vst/external/VST_SDK/VST3_SDK/vstgui4"]
path = vst/external/VST_SDK/VST3_SDK/vstgui4
path = editor/external/vstgui4
url = https://github.com/sfztools/vstgui.git
shallow = true

View file

@ -40,8 +40,33 @@ jobs:
install: .travis/download_cmake.sh
script: .travis/script_test.sh
- name: "Windows mingw32"
- name: "macOS"
stage: "Build"
os: osx
osx_image: xcode11.3
addons:
homebrew:
packages:
- cmake
- libsndfile
- jack
- dylibbundler
env:
- INSTALL_DIR="sfizz-${TRAVIS_BRANCH}-${TRAVIS_OS_NAME}-${TRAVIS_CPU_ARCH}"
script: .travis/script_osx.sh
after_success: .travis/prepare_osx.sh
- name: "MOD devices arm"
env:
- CONTAINER=jpcima/mod-plugin-builder
- CROSS_COMPILE=moddevices-arm
- INSTALL_DIR="sfizz-${TRAVIS_BRANCH}-moddevices"
before_install: .travis/before_install_moddevices.sh
install: .travis/install_moddevices.sh
script: .travis/script_moddevices.sh
after_success: .travis/prepare_tarball.sh
- name: "Windows mingw32"
env:
- CROSS_COMPILE=mingw32
- CONTAINER=archlinux
@ -92,6 +117,7 @@ jobs:
env:
- INSTALL_DIR="sfizz-plugins-${TRAVIS_BRANCH}-${TRAVIS_OS_NAME}-${TRAVIS_CPU_ARCH}"
- ENABLE_VST_PLUGIN=OFF
- ENABLE_LV2_UI=OFF
addons:
apt:
packages:
@ -107,6 +133,7 @@ jobs:
env:
- INSTALL_DIR="sfizz-plugins-${TRAVIS_BRANCH}-${TRAVIS_OS_NAME}-${TRAVIS_CPU_ARCH}"
- ENABLE_VST_PLUGIN=ON
- ENABLE_LV2_UI=ON
addons:
apt:
packages:
@ -127,28 +154,9 @@ jobs:
script: .travis/script_plugins.sh
after_success: .travis/prepare_tarball.sh
- name: "macOS"
os: osx
env:
- INSTALL_DIR="sfizz-${TRAVIS_BRANCH}-${TRAVIS_OS_NAME}-${TRAVIS_CPU_ARCH}"
install: .travis/install_osx.sh
script: .travis/script_osx.sh
after_success: .travis/prepare_osx.sh
- name: "MOD devices arm"
stage: "Build"
env:
- CONTAINER=jpcima/mod-plugin-builder
- CROSS_COMPILE=moddevices-arm
- INSTALL_DIR="sfizz-${TRAVIS_BRANCH}-moddevices"
before_install: .travis/before_install_moddevices.sh
install: .travis/install_moddevices.sh
script: .travis/script_moddevices.sh
after_success: .travis/prepare_tarball.sh
- stage: "Deploy"
name: "Source packaging"
if: (tag IS present) AND (branch = master) AND (type = push)
if: (tag =~ /^v?[0-9]/) AND (type = push)
env:
- INSTALL_DIR="sfizz-${TRAVIS_BRANCH}-src"
addons:
@ -158,17 +166,6 @@ jobs:
install: sudo pip install git-archive-all
script: git-archive-all --prefix="sfizz-${TRAVIS_BRANCH}/" -9 "${INSTALL_DIR}.tar.gz"
- name: "Generate documentation"
if: (tag IS present) AND (branch = master) AND (type = push)
addons:
apt:
packages:
- doxygen
- cmake
- libsndfile-dev
install: skip
script: .travis/update_dox.sh
- name: "Discord Webhook"
install: skip
script: bash ${TRAVIS_BUILD_DIR}/.travis/discord_webhook.sh success

View file

@ -1,10 +0,0 @@
#!/bin/bash
set -ex
sudo ln -s /usr/local /opt/local
brew update
brew upgrade cmake
brew install python || brew link --overwrite python
brew install jack
brew install dylibbundler

View file

@ -15,7 +15,8 @@ buildenv make DESTDIR=${PWD}/${INSTALL_DIR} install
# Bundle LV2 dependencies
cd "${INSTALL_DIR}"/Library/Audio/Plug-Ins/LV2
dylibbundler -od -b -x sfizz.lv2/sfizz.so -d sfizz.lv2/libs/ -p @loader_path/libs/
dylibbundler -od -b -x sfizz.lv2/Contents/Binary/sfizz.so -d sfizz.lv2/Contents/libs/ -p @loader_path/../libs/
dylibbundler -od -b -x sfizz.lv2/Contents/Binary/sfizz_ui.so -d sfizz.lv2/Contents/libs/ -p @loader_path/../libs/
cd "${TRAVIS_BUILD_DIR}/build"
# Bundle VST3 dependencies

View file

@ -8,4 +8,4 @@ cmake -DCMAKE_BUILD_TYPE=Release \
-DSFIZZ_TESTS=OFF \
-DCMAKE_CXX_STANDARD=17 \
..
make -j$(nproc)
make -j2

View file

@ -9,17 +9,17 @@ if [[ ${CROSS_COMPILE} == "mingw32" ]]; then
-DENABLE_LTO=OFF \
-DSFIZZ_JACK=OFF \
-DSFIZZ_VST=ON \
-DSFIZZ_STATIC_LIBSNDFILE=ON \
-DSFIZZ_STATIC_DEPENDENCIES=ON \
-DCMAKE_CXX_STANDARD=17 \
..
buildenv make -j$(nproc)
buildenv make -j2
elif [[ ${CROSS_COMPILE} == "mingw64" ]]; then
buildenv x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=Release \
-DENABLE_LTO=OFF \
-DSFIZZ_JACK=OFF \
-DSFIZZ_VST=ON \
-DSFIZZ_STATIC_LIBSNDFILE=ON \
-DSFIZZ_STATIC_DEPENDENCIES=ON \
-DCMAKE_CXX_STANDARD=17 \
..
buildenv make -j$(nproc)
buildenv make -j2
fi

View file

@ -7,5 +7,5 @@ mkdir -p build/${INSTALL_DIR} && cd build
buildenv mod-plugin-builder /usr/local/bin/cmake \
-DSFIZZ_SYSTEM_PROCESSOR=armv7-a \
-DCMAKE_BUILD_TYPE=Release -DSFIZZ_JACK=OFF ..
buildenv mod-plugin-builder make -j
-DCMAKE_BUILD_TYPE=Release -DSFIZZ_JACK=OFF -DSFIZZ_LV2_UI=OFF ..
buildenv mod-plugin-builder make -j2

View file

@ -5,9 +5,10 @@ mkdir -p build/${INSTALL_DIR} && cd build
cmake -DCMAKE_BUILD_TYPE=Release \
-DSFIZZ_JACK=OFF \
-DSFIZZ_VST="$ENABLE_VST_PLUGIN" \
-DSFIZZ_LV2_UI="$ENABLE_LV2_UI" \
-DSFIZZ_TESTS=OFF \
-DSFIZZ_SHARED=OFF \
-DSFIZZ_STATIC_LIBSNDFILE=ON \
-DSFIZZ_STATIC_DEPENDENCIES=ON \
-DCMAKE_CXX_STANDARD=17 \
..
make -j$(nproc)
make -j2

View file

@ -6,9 +6,9 @@ cmake -DCMAKE_BUILD_TYPE=Release \
-DSFIZZ_JACK=OFF \
-DSFIZZ_TESTS=ON \
-DSFIZZ_SHARED=OFF \
-DSFIZZ_STATIC_LIBSNDFILE=OFF \
-DSFIZZ_STATIC_DEPENDENCIES=OFF \
-DSFIZZ_LV2=OFF \
-DCMAKE_CXX_STANDARD=17 \
..
make -j$(nproc) sfizz_tests
make -j2 sfizz_tests
tests/sfizz_tests

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -x # No fail, we need to go back to the original branch at the end
. .travis/environment.sh
mkdir build && cd build && cmake -DSFIZZ_JACK=OFF -DSFIZZ_SHARED=OFF -DSFIZZ_LV2=OFF .. && cd ..
doxygen Doxyfile
./doxygen/scripts/generate_api_index.sh
git fetch --depth=1 https://github.com/${TRAVIS_REPO_SLUG}.git refs/heads/gh-pages:refs/remotes/origin/gh-pages
git checkout origin/gh-pages
git checkout -b gh-pages
mv _api api/${TRAVIS_TAG}
mv api_index.md api/index.md
git add api && git commit -m "Release ${TRAVIS_TAG} (Travis build: ${TRAVIS_BUILD_NUMBER})"
git remote add origin-pages https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git > /dev/null 2>&1
git push --quiet --set-upstream origin-pages gh-pages
git checkout ${TRAVIS_BRANCH}

View file

@ -8,3 +8,5 @@ Contributors to `sfizz`, in chronologic order:
- Michael Willis (2020)
- Jean-Pierre Cimalando (2020)
- Tobiasz "unfa" Karoń (2020)
- Kinwie (2020)
- Atsushi Eno (2020)

View file

@ -8,27 +8,21 @@ else()
endif()
endif()
project (sfizz VERSION 0.4.0 LANGUAGES CXX C)
project (sfizz VERSION 0.5.0 LANGUAGES CXX C)
set (PROJECT_DESCRIPTION "A library to load SFZ description files and use them to render music.")
# External configuration CMake scripts
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include (BuildType)
include (SfizzConfig)
# Build Options
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()
option (ENABLE_LTO "Enable Link Time Optimization [default: ON]" ON)
option (SFIZZ_JACK "Enable JACK stand-alone build [default: ON]" ON)
option (SFIZZ_RENDER "Enable renderer of SMF files [default: ON]" ON)
option (SFIZZ_LV2 "Enable LV2 plug-in build [default: ON]" ON)
option (SFIZZ_LV2_UI "Enable LV2 plug-in user interface [default: ON]" ON)
option (SFIZZ_VST "Enable VST plug-in build [default: OFF]" OFF)
option (SFIZZ_AU "Enable AU plug-in build [default: OFF]" OFF)
option (SFIZZ_BENCHMARKS "Enable benchmarks build [default: OFF]" OFF)
@ -36,9 +30,11 @@ option (SFIZZ_TESTS "Enable tests build [default: OFF]" OFF)
option (SFIZZ_DEVTOOLS "Enable developer tools build [default: OFF]" OFF)
option (SFIZZ_SHARED "Enable shared library build [default: ON]" ON)
option (SFIZZ_USE_VCPKG "Assume that sfizz is build using vcpkg [default: OFF]" OFF)
option (SFIZZ_STATIC_LIBSNDFILE "Link libsndfile statically [default: OFF]" OFF)
option (SFIZZ_STATIC_DEPENDENCIES "Link dependencies statically [default: OFF]" OFF)
option (SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds [default: OFF]" OFF)
include (SfizzConfig)
# Don't use IPO in non Release builds
include (CheckIPO)
@ -51,6 +47,10 @@ add_subdirectory (src)
# Optional targets
add_subdirectory (clients)
if ((SFIZZ_LV2 AND SFIZZ_LV2_UI) OR SFIZZ_VST)
add_subdirectory (editor)
endif()
if (SFIZZ_LV2)
add_subdirectory (lv2)
endif()

View file

@ -47,4 +47,10 @@ It is the role of maintainers to best adapt the governance model to the evolutio
3. Maintainers can be invited from regular contributors at any point in time; maintainers wishing to leave the governance of the project may inform the other maintainers.
In both cases, updates to this document reflect the evolution of the governance team and rules.
## License for the GOVERNANCE.md file
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided that this notice
is preserved. This file is offered as-is, without any warranty.
[Open-source Open Collective]: https://opencollective.com/sfztools

View file

@ -3,12 +3,7 @@ BSD 2-Clause License
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-)
Please refer to AUTHORS.md for the list of contributors.
All rights reserved.

View file

@ -3,7 +3,10 @@
[![Travis Build Status]](https://travis-ci.com/sfztools/sfizz)
[![AppVeyor Build Status]](https://ci.appveyor.com/project/sfztools/sfizz)
SFZ library and LV2 plugin, please check [our website] for more details.
[![Discord Badge Image]](https://discord.gg/3ArE9Mw)
SFZ parser and synth c++ library, providing AU / LV2 / VST3 plugins
and JACK standalone client, please check [our website] for more details.
![Screenshot](screenshot.png)
@ -64,3 +67,4 @@ The sfizz library also uses in some subprojects:
[build from source]: https://sfz.tools/sfizz/development/build/
[AppVeyor Build Status]: https://img.shields.io/appveyor/ci/sfztools/sfizz.svg?label=Windows&style=popout&logo=appveyor
[Travis Build Status]: https://img.shields.io/travis/com/sfztools/sfizz.svg?label=Linux&style=popout&logo=travis
[Discord Badge Image]: https://img.shields.io/discord/587748534321807416?label=discord&logo=discord

View file

@ -1,5 +1,5 @@
version: build-{build}
image: Visual Studio 2017
image: Visual Studio 2019
configuration: Release
platform:
- Win32
@ -12,20 +12,22 @@ install:
- cmd: set PATH=C:\Program Files (x86)\Inno Setup 6;%PATH%
- cmd: if %platform%==Win32 set VCPKG_TRIPLET=x86-windows-static
- cmd: if %platform%==x64 set VCPKG_TRIPLET=x64-windows-static
# - cmd: cd c:\tools\vcpkg\
# - cmd: git pull
# - cmd: .\bootstrap-vcpkg.bat
# - cmd: cd %APPVEYOR_BUILD_FOLDER%
- cmd: vcpkg install libsndfile:%VCPKG_TRIPLET%
before_build:
- cmd: git submodule update --init
- cmd: mkdir CMakeBuild
- cmd: cd CMakeBuild
- cmd: cmake .. -G"Visual Studio 15 2017" -A"%platform%" -DSFIZZ_JACK=OFF -DSFIZZ_BENCHMARKS=OFF -DSFIZZ_TESTS=OFF -DSFIZZ_LV2=ON -DSFIZZ_VST=ON -DCMAKE_BUILD_TYPE=Release -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake
- cmd: cmake .. -G"Visual Studio 16 2019" -A"%platform%" -DSFIZZ_JACK=OFF -DSFIZZ_BENCHMARKS=OFF -DSFIZZ_TESTS=OFF -DSFIZZ_LV2=ON -DSFIZZ_VST=ON -DCMAKE_BUILD_TYPE=Release -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake
build_script:
- cmd: cmake --build . --config Release -j
after_build:
- cmd: cp sfizz.lv2/Release/sfizz.dll sfizz.lv2/
- cmd: rm -rf sfizz.lv2/Release
- cmd: if %platform%==Win32 set RELEASE_ARCH=x86
- cmd: if %platform%==x64 set RELEASE_ARCH=x64
- cmd: 7z a sfizz-lv2-%APPVEYOR_REPO_TAG_NAME%-%RELEASE_ARCH%-msvc.zip sfizz.lv2

View file

@ -0,0 +1,70 @@
// 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 "Macros.h"
#include <benchmark/benchmark.h>
#include <random>
#include <numeric>
#include <vector>
#include <cmath>
#include <iostream>
class WithinArray : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 10 };
input = std::vector<float>(state.range(0));
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& state) {
UNUSED(state);
}
std::vector<float> input;
};
BENCHMARK_DEFINE_F(WithinArray, ScalarFalse)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, false);
sfz::allWithin<float>(input, 1.2f, 3.8f);
}
}
BENCHMARK_DEFINE_F(WithinArray, SIMDFalse)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, true);
sfz::allWithin<float>(input, 1.2f, 3.8f);
}
}
BENCHMARK_DEFINE_F(WithinArray, ScalarTrue)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, false);
sfz::allWithin<float>(input, 0.0f, 11.0f);
}
}
BENCHMARK_DEFINE_F(WithinArray, SIMDTrue)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, true);
sfz::allWithin<float>(input, 0.0f, 11.0f);
}
}
BENCHMARK_REGISTER_F(WithinArray, ScalarFalse)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(WithinArray, SIMDFalse)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(WithinArray, ScalarTrue)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(WithinArray, SIMDTrue)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -0,0 +1,233 @@
// 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 "AudioReader.h"
#include <benchmark/benchmark.h>
#include <sndfile.hh>
#include <ghc/fs_std.hpp>
#include <vector>
#include <memory>
#include <system_error>
#include <stdexcept>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#ifndef _WIN32
#include <sys/types.h>
#include <unistd.h>
#else
#include <io.h>
#endif
///
struct TemporaryFile {
TemporaryFile();
~TemporaryFile();
TemporaryFile(const TemporaryFile&) = delete;
TemporaryFile& operator=(const TemporaryFile&) = delete;
TemporaryFile(TemporaryFile&&) = default;
TemporaryFile& operator=(TemporaryFile&&) = default;
const fs::path& path() const { return path_; }
private:
fs::path path_;
static fs::path createTemporaryFile();
};
TemporaryFile::TemporaryFile()
: path_(createTemporaryFile())
{
}
TemporaryFile::~TemporaryFile()
{
std::error_code ec;
fs::remove(path_, ec);
}
#if !defined(_WIN32)
fs::path TemporaryFile::createTemporaryFile()
{
char path[] = P_tmpdir "/sndXXXXXX";
int fd = mkstemp(path);
if (fd == -1)
throw std::runtime_error("Cannot create temporary file.");
close(fd);
return path;
}
#else
fs::path TemporaryFile::createTemporaryFile()
{
DWORD ret = GetTempPathW(0, buffer);
std::unique_ptr<WCHAR[]> path;
if (ret != 0) {
path.reset(new WCHAR[ret + 8]{});
ret = GetTempPathW(0, buffer);
if (ret != 0)
wcscat(path.get(), L"\\XXXXXX");
}
if (ret == 0 || !_wmktemp(path.get()))
throw std::runtime_error("Cannot create temporary file.");
return path.get();
}
#endif
///
class AudioReaderFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) override
{
workBuffer.resize(2 * static_cast<size_t>(state.range(0)));
}
void TearDown(const ::benchmark::State& /* state */) override
{
}
static TemporaryFile createAudioFile(int format);
static TemporaryFile fileWav;
static TemporaryFile fileFlac;
static TemporaryFile fileOgg;
std::vector<float> workBuffer;
};
TemporaryFile AudioReaderFixture::fileWav = createAudioFile(SF_FORMAT_WAV|SF_FORMAT_PCM_16);
TemporaryFile AudioReaderFixture::fileFlac = createAudioFile(SF_FORMAT_FLAC|SF_FORMAT_PCM_16);
TemporaryFile AudioReaderFixture::fileOgg = createAudioFile(SF_FORMAT_OGG|SF_FORMAT_VORBIS);
TemporaryFile AudioReaderFixture::createAudioFile(int format)
{
constexpr unsigned sampleRate = 44100;
constexpr unsigned fileDuration = 10;
constexpr unsigned fileFrames = sampleRate * fileDuration;
// synth 2 channels of arbitrary waveform
std::unique_ptr<double[]> sndData { new double[2 * fileFrames] };
double phase = 0.0;
for (unsigned i = 0; i < fileFrames; ++i) {
sndData[2 * i ] = std::sin(2.0 * M_PI * phase);
sndData[2 * i + 1] = std::cos(2.0 * M_PI * phase);
phase += 440.0 * (1.0 / sampleRate);
phase -= static_cast<long>(phase);
}
// create temp file
TemporaryFile temp;
fprintf(stderr, "* Temporary file: %s\n", temp.path().u8string().c_str());
// write to file
#if !defined(_WIN32)
SndfileHandle snd(temp.path().c_str(), SFM_WRITE, format, 2, sampleRate);
#else
SndfileHandle snd(temp.path().wstring().c_str(), SFM_WRITE, format, 2, sampleRate);
#endif
if (snd.error())
throw std::runtime_error("cannot open sound file for writing");
snd.writef(sndData.get(), fileFrames);
snd = SndfileHandle();
return temp;
}
static void doReaderBenchmark(const fs::path& path, std::vector<float> &buffer, sfz::AudioReaderType type)
{
sfz::AudioReaderPtr reader = sfz::createExplicitAudioReader(path, type);
while (reader->readNextBlock(buffer.data(), buffer.size() / 2) > 0);
}
static void doEntireRead(const fs::path& path)
{
#if !defined(_WIN32)
SndfileHandle handle(path.c_str());
#else
SndfileHandle handle(path.wstring().c_str());
#endif
if (handle.error())
throw std::runtime_error("cannot open sound file for reading");
std::vector<float> buffer(static_cast<size_t>(2 * handle.frames()));
handle.read(buffer.data(), buffer.size());
}
BENCHMARK_DEFINE_F(AudioReaderFixture, EntireWav)(benchmark::State& state)
{
for (auto _ : state) {
doEntireRead(fileWav.path());
}
}
BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardWav)(benchmark::State& state)
{
for (auto _ : state) {
doReaderBenchmark(fileWav.path(), workBuffer, sfz::AudioReaderType::Forward);
}
}
BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseWav)(benchmark::State& state)
{
for (auto _ : state) {
doReaderBenchmark(fileWav.path(), workBuffer, sfz::AudioReaderType::Reverse);
}
}
BENCHMARK_DEFINE_F(AudioReaderFixture, EntireFlac)(benchmark::State& state)
{
for (auto _ : state) {
doEntireRead(fileFlac.path());
}
}
BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardFlac)(benchmark::State& state)
{
for (auto _ : state) {
doReaderBenchmark(fileFlac.path(), workBuffer, sfz::AudioReaderType::Forward);
}
}
BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseFlac)(benchmark::State& state)
{
for (auto _ : state) {
doReaderBenchmark(fileFlac.path(), workBuffer, sfz::AudioReaderType::Reverse);
}
}
BENCHMARK_DEFINE_F(AudioReaderFixture, EntireOgg)(benchmark::State& state)
{
for (auto _ : state) {
doEntireRead(fileOgg.path());
}
}
BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardOgg)(benchmark::State& state)
{
for (auto _ : state) {
doReaderBenchmark(fileOgg.path(), workBuffer, sfz::AudioReaderType::Forward);
}
}
//BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseOgg)(benchmark::State& state)
//{
// for (auto _ : state) {
// doReaderBenchmark(fileOgg.path(), workBuffer, sfz::AudioReaderType::Reverse);
// }
//}
BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardWav)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseWav)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
BENCHMARK_REGISTER_F(AudioReaderFixture, EntireWav)->Range(1, 1);
BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardFlac)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseFlac)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
BENCHMARK_REGISTER_F(AudioReaderFixture, EntireFlac)->Range(1, 1);
BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardOgg)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
//BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseOgg)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
BENCHMARK_REGISTER_F(AudioReaderFixture, EntireOgg)->Range(1, 1);
BENCHMARK_MAIN();

52
benchmarks/BM_clamp.cpp Normal file
View file

@ -0,0 +1,52 @@
// 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 "Macros.h"
#include <benchmark/benchmark.h>
#include <random>
#include <numeric>
#include <vector>
#include <cmath>
#include <iostream>
class ClampArray : 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, 10 };
input = std::vector<float>(state.range(0));
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& state) {
UNUSED(state);
}
std::vector<float> input;
};
BENCHMARK_DEFINE_F(ClampArray, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::clampAll, false);
sfz::clampAll<float>(absl::MakeSpan(input), 1.2f, 3.8f);
}
}
BENCHMARK_DEFINE_F(ClampArray, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::clampAll, true);
sfz::clampAll<float>(absl::MakeSpan(input), 1.2f, 3.8f);
}
}
BENCHMARK_REGISTER_F(ClampArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(ClampArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -20,7 +20,7 @@ constexpr float sampleRate { 48000.0f };
class FilterFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
void SetUp(const ::benchmark::State& /* state */) {
inputLeft = std::vector<float>(blockSize);
inputRight = std::vector<float>(blockSize);
outputLeft = std::vector<float>(blockSize);

View file

@ -19,7 +19,7 @@
class FileFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
void SetUp(const ::benchmark::State& /* state */) {
filePath1 = getPath() / "sample1.flac";
filePath2 = getPath() / "sample2.flac";
filePath3 = getPath() / "sample3.flac";

View file

@ -34,7 +34,7 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, Scalar)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, false);
auto result = sfz::meanSquared<float>(input);
benchmark::DoNotOptimize(result);
}
@ -44,7 +44,7 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, SIMD)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, true);
auto result = sfz::meanSquared<float>(input);
benchmark::DoNotOptimize(result);
}
@ -54,7 +54,7 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, Scalar_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, false);
auto result = sfz::meanSquared<float>(absl::MakeSpan(input).subspan(1));
benchmark::DoNotOptimize(result);
}
@ -64,7 +64,7 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, SIMD_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, true);
auto result = sfz::meanSquared<float>(absl::MakeSpan(input).subspan(1));
benchmark::DoNotOptimize(result);
}

View file

@ -0,0 +1,83 @@
// 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 MultiplyMul : 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.1f, 1.0f };
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
gain = std::vector<float>(state.range(0));
std::fill(output.begin(), output.end(), 2.0f );
std::generate(gain.begin(), gain.end(), [&]() { return dist(gen); });
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& /* state */) {
}
std::vector<float> gain;
std::vector<float> input;
std::vector<float> output;
};
BENCHMARK_DEFINE_F(MultiplyMul, Straight)(benchmark::State& state) {
for (auto _ : state)
{
for (int i = 0; i < state.range(0); ++i)
output[i] *= gain[i] * input[i];
}
}
BENCHMARK_DEFINE_F(MultiplyMul, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, false);
sfz::multiplyMul<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyMul, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, true);
sfz::multiplyMul<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyMul, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, false);
sfz::multiplyMul<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(MultiplyMul, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, true);
sfz::multiplyMul<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_REGISTER_F(MultiplyMul, Straight)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMul, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMul, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMul, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMul, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -0,0 +1,88 @@
// 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 MultiplyMulFixedGain : 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.1f, 1.0f };
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
gain = dist(gen);
std::fill(output.begin(), output.end(), 2.0f);
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& /* state */)
{
}
float gain = {};
std::vector<float> input;
std::vector<float> output;
};
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, Straight)
(benchmark::State& state)
{
for (auto _ : state) {
for (int i = 0; i < state.range(0); ++i)
output[i] *= gain * input[i];
}
}
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, Scalar)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, false);
sfz::multiplyMul1<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, SIMD)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, true);
sfz::multiplyMul1<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, Scalar_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, false);
sfz::multiplyMul1<float>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, SIMD_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, true);
sfz::multiplyMul1<float>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, Straight)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

134
benchmarks/BM_pan_arm.cpp Normal file
View file

@ -0,0 +1,134 @@
// 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 "Panning.h"
#include "simd/Common.h"
#include <benchmark/benchmark.h>
#include <random>
#include <absl/algorithm/container.h>
#include "absl/types/span.h"
#include <arm_neon.h>
#include <jsl/allocator>
template <class T, std::size_t A = 16>
using aligned_vector = std::vector<T, jsl::aligned_allocator<T, A>>;
// Number of elements in the table, odd for equal volume at center
constexpr int panSize = 4095;
// Table of pan values for the left channel, extra element for safety
static const auto panData = []()
{
std::array<float, panSize + 1> pan;
int i = 0;
for (; i < panSize; ++i)
pan[i] = std::cos(i * (piTwo<double>() / (panSize - 1)));
for (; i < static_cast<int>(pan.size()); ++i)
pan[i] = pan[panSize - 1];
return pan;
}();
float _panLookup(float pan)
{
// reduce range, round to nearest
int index = lroundPositive(pan * (panSize - 1));
return panData[index];
}
void panScalar(const float* panEnvelope, float* leftBuffer, float* rightBuffer, unsigned size) noexcept
{
const auto sentinel = panEnvelope + size;
while (panEnvelope < sentinel) {
auto p =(*panEnvelope + 1.0f) * 0.5f;
p = clamp(p, 0.0f, 1.0f);
*leftBuffer *= _panLookup(p);
*rightBuffer *= _panLookup(1 - p);
incrementAll(panEnvelope, leftBuffer, rightBuffer);
}
}
void panSIMD(const float* panEnvelope, float* leftBuffer, float* rightBuffer, unsigned size) noexcept
{
const auto sentinel = panEnvelope + size;
int32_t indices[4];
while (panEnvelope < sentinel) {
float32x4_t mmPan = vld1q_f32(panEnvelope);
mmPan = vaddq_f32(mmPan, vdupq_n_f32(1.0f));
mmPan = vmulq_n_f32(mmPan, 0.5f * panSize);
mmPan = vaddq_f32(mmPan, vdupq_n_f32(0.5f));
mmPan = vminq_f32(mmPan, vdupq_n_f32(panSize));
mmPan = vmaxq_f32(mmPan, vdupq_n_f32(0.0f));
int32x4_t mmIdx = vcvtq_s32_f32(mmPan);
vst1q_s32(indices, mmIdx);
leftBuffer[0] *= panData[indices[0]];
rightBuffer[0] *= panData[panSize - indices[0] - 1];
leftBuffer[1] *= panData[indices[1]];
rightBuffer[1] *= panData[panSize - indices[1]- 1];
leftBuffer[2] *= panData[indices[2]];
rightBuffer[2] *= panData[panSize - indices[2]- 1];
leftBuffer[3] *= panData[indices[3]];
rightBuffer[3] *= panData[panSize - indices[3]- 1];
incrementAll<4>(panEnvelope, leftBuffer, rightBuffer);
}
}
class PanFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { -1.0f, 1.0f };
pan.resize(state.range(0));
right.resize(state.range(0));
left.resize(state.range(0));
if (!willAlign<16>(pan.data(), left.data(), right.data()))
std::cout << "Will not align!" << '\n';
absl::c_generate(pan, [&]() { return dist(gen); });
absl::c_generate(left, [&]() { return dist(gen); });
absl::c_generate(right, [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& /* state */) {
}
aligned_vector<float> pan;
aligned_vector<float> right;
aligned_vector<float> left;
};
BENCHMARK_DEFINE_F(PanFixture, PanScalar)(benchmark::State& state) {
for (auto _ : state)
{
panScalar(pan.data(), left.data(), right.data(), state.range(0));
}
}
BENCHMARK_DEFINE_F(PanFixture, PanSIMD)(benchmark::State& state) {
for (auto _ : state)
{
panSIMD(pan.data(), left.data(), right.data(), state.range(0));
}
}
BENCHMARK_DEFINE_F(PanFixture, PanSfizz)(benchmark::State& state) {
for (auto _ : state)
{
sfz::pan(pan.data(), left.data(), right.data(), state.range(0));
}
}
// Register the function as a benchmark
BENCHMARK_REGISTER_F(PanFixture, PanScalar)->RangeMultiplier(4)->Range((1 << 4), (1 << 12));
BENCHMARK_REGISTER_F(PanFixture, PanSIMD)->RangeMultiplier(4)->Range((1 << 4), (1 << 12));
BENCHMARK_REGISTER_F(PanFixture, PanSfizz)->RangeMultiplier(4)->Range((1 << 4), (1 << 12));
BENCHMARK_MAIN();

View file

@ -0,0 +1,125 @@
// 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 "PowerFollower.h"
#include "AudioBuffer.h"
#include "Config.h"
#include <benchmark/benchmark.h>
#include <cmath>
class PowerFollowerFixture : public benchmark::Fixture {
public:
PowerFollowerFixture()
{
inputSignal_ = sfz::AudioBuffer<float>(2, numFrames);
auto leftSignal = inputSignal_.getSpan(0);
auto rightSignal = inputSignal_.getSpan(1);
float phase = 0;
for (size_t i = 0; i < numFrames; ++i) {
constexpr float k2pi = 2.0 * M_PI;
leftSignal[i] = std::sin(k2pi * phase);
rightSignal[i] = std::cos(k2pi * phase);
phase += 440.0f / sfz::config::defaultSampleRate;
phase -= static_cast<int>(phase);
}
}
void SetUp(const ::benchmark::State& state)
{
auto blockSize = static_cast<size_t>(state.range(0));
follower_.setSampleRate(sfz::config::defaultSampleRate);
follower_.setSamplesPerBlock(blockSize);
follower_.clear();
//
refFollower_.init(sfz::config::defaultSampleRate);
refFollower_.clear();
}
void TearDown(const ::benchmark::State& /* state */)
{
}
static constexpr size_t numFrames = 65536;
sfz::PowerFollower follower_;
sfz::AudioBuffer<float> inputSignal_;
//
struct ReferenceFollower {
/*
import("stdfaust.lib");
process = (_, _) : + : an.amp_follower_ud(att, rel) with { att = 5e-3; rel = 200e-3; };
*/
void init(float sampleRate)
{
fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(sampleRate)));
fConst1 = std::exp((0.0f - (200.0f / fConst0)));
fConst2 = (1.0f - fConst1);
fConst3 = std::exp((0.0f - (5.0f / fConst0)));
fConst4 = (1.0f - fConst3);
}
void clear()
{
for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
fRec1[l0] = 0.0f;
}
for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) {
fRec0[l1] = 0.0f;
}
}
float process(float input0, float input1)
{
float fTemp0 = std::fabs((float(input0) + float(input1)));
fRec1[0] = std::max<float>(fTemp0, ((fConst3 * fRec1[1]) + (fConst4 * fTemp0)));
fRec0[0] = ((fConst1 * fRec0[1]) + (fConst2 * fRec1[0]));
float output = fRec0[0];
fRec1[1] = fRec1[0];
fRec0[1] = fRec0[0];
return output;
}
float fConst0;
float fConst1;
float fConst2;
float fConst3;
float fConst4;
float fRec1[2];
float fRec0[2];
};
ReferenceFollower refFollower_;
};
constexpr size_t PowerFollowerFixture::numFrames;
BENCHMARK_DEFINE_F(PowerFollowerFixture, ReferenceFollower) (benchmark::State& state)
{
sfz::AudioSpan<float> inputSignal(inputSignal_);
auto input0 = inputSignal.getConstSpan(0);
auto input1 = inputSignal.getConstSpan(1);
for (auto _ : state) {
auto& follower = refFollower_;
float output = 0;
for (size_t i = 0, n = inputSignal.getNumFrames(); i < n; ++i)
output = follower.process(input0[i], input1[i]);
benchmark::DoNotOptimize(output);
}
}
BENCHMARK_DEFINE_F(PowerFollowerFixture, Follower) (benchmark::State& state)
{
sfz::AudioSpan<float> inputSignal(inputSignal_);
for (auto _ : state) {
auto& follower = follower_;
auto blockSize = static_cast<size_t>(state.range(0));
for (size_t i = 0; i < numFrames; i += blockSize)
follower.process(inputSignal.subspan(i, blockSize));
}
}
BENCHMARK_REGISTER_F(PowerFollowerFixture, ReferenceFollower)->Range(1, 1);
BENCHMARK_REGISTER_F(PowerFollowerFixture, Follower)->RangeMultiplier(2)->Range(1 << 5, 1 << 12);

View file

@ -32,6 +32,7 @@ macro(sfizz_add_benchmark TARGET)
target_link_libraries ("${TARGET}" PRIVATE atomic)
endif()
target_include_directories("${TARGET}" PRIVATE ../src/sfizz ../src/external)
sfizz_enable_fast_math("${TARGET}")
endmacro()
sfizz_add_benchmark(bm_opf_high_vs_low BM_OPF_high_vs_low.cpp)
@ -48,6 +49,8 @@ target_link_libraries(bm_ADSR PRIVATE sfizz::sfizz)
sfizz_add_benchmark(bm_add BM_add.cpp)
sfizz_add_benchmark(bm_multiplyAdd BM_multiplyAdd.cpp)
sfizz_add_benchmark(bm_multiplyAddFixedGain BM_multiplyAddFixedGain.cpp)
sfizz_add_benchmark(bm_multiplyMul BM_multiplyMul.cpp)
sfizz_add_benchmark(bm_multiplyMulFixedGain BM_multiplyMulFixedGain.cpp)
sfizz_add_benchmark(bm_subtract BM_subtract.cpp)
sfizz_add_benchmark(bm_copy BM_copy.cpp)
sfizz_add_benchmark(bm_mean BM_mean.cpp)
@ -59,11 +62,15 @@ sfizz_add_benchmark(bm_maps BM_maps.cpp)
target_link_libraries(bm_maps PRIVATE absl::flat_hash_map)
sfizz_add_benchmark(bm_mapVsArray BM_mapVsArray.cpp)
sfizz_add_benchmark(bm_random BM_random.cpp)
sfizz_add_benchmark(bm_clamp BM_clamp.cpp)
sfizz_add_benchmark(bm_allWithin BM_allWithin.cpp)
sfizz_add_benchmark(bm_logger BM_logger.cpp)
target_link_libraries(bm_logger PRIVATE sfizz::sfizz)
sfizz_add_benchmark(bm_smoothers BM_smoothers.cpp)
target_link_libraries(bm_smoothers PRIVATE sfizz::sfizz)
sfizz_add_benchmark(bm_powerFollower BM_powerFollower.cpp)
target_link_libraries(bm_powerFollower PRIVATE sfizz::sfizz)
if (TARGET sfizz-samplerate)
sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES})
@ -78,6 +85,9 @@ target_link_libraries(bm_wavfile PRIVATE sfizz-sndfile)
sfizz_add_benchmark(bm_flacfile BM_flacfile.cpp)
target_link_libraries(bm_flacfile PRIVATE sfizz-sndfile)
sfizz_add_benchmark(bm_audioReaders BM_audioReaders.cpp ../src/sfizz/AudioReader.cpp)
target_link_libraries(bm_audioReaders PRIVATE sfizz-sndfile)
sfizz_add_benchmark(bm_readChunk BM_readChunk.cpp)
target_link_libraries(bm_readChunk PRIVATE sfizz-sndfile)
sfizz_add_benchmark(bm_readChunkFlac BM_readChunkFlac.cpp)
@ -137,6 +147,12 @@ if (TARGET bm_resample)
add_dependencies(sfizz_benchmarks bm_resample)
endif()
if (SFIZZ_SYSTEM_PROCESSOR MATCHES "armv7l")
sfizz_add_benchmark(bm_pan_arm BM_pan_arm.cpp ../src/sfizz/Panning.cpp)
target_link_libraries(bm_pan_arm PRIVATE sfizz-jsl)
add_dependencies(sfizz_benchmarks bm_pan_arm)
endif()
configure_file("sample.wav" "${CMAKE_BINARY_DIR}/benchmarks/sample1.wav" COPYONLY)
configure_file("sample.wav" "${CMAKE_BINARY_DIR}/benchmarks/sample2.wav" COPYONLY)
configure_file("sample.wav" "${CMAKE_BINARY_DIR}/benchmarks/sample3.wav" COPYONLY)

View file

@ -3,10 +3,11 @@ project (sfizz)
if (SFIZZ_JACK)
find_package(PkgConfig REQUIRED)
pkg_check_modules(JACK "jack" REQUIRED)
link_directories (${JACK_LIBRARY_DIRS})
add_executable (sfizz_jack MidiHelpers.h jack_client.cpp)
target_include_directories (sfizz_jack PRIVATE ${JACK_INCLUDE_DIRS})
target_link_libraries (sfizz_jack PRIVATE sfizz::sfizz jack absl::flags_parse ${JACK_LIBRARIES})
target_link_libraries (sfizz_jack PRIVATE sfizz::sfizz absl::flags_parse ${JACK_LIBRARIES})
sfizz_enable_lto_if_needed (sfizz_jack)
install (TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT "jack" OPTIONAL)

View file

@ -21,8 +21,7 @@
// (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/Synth.h"
#include "sfizz/Macros.h"
#include "sfizz.hpp"
#include "MidiHelpers.h"
#include <absl/flags/parse.h>
#include <absl/flags/flag.h>
@ -47,9 +46,9 @@ static jack_client_t* client;
int process(jack_nframes_t numFrames, void* arg)
{
auto synth = reinterpret_cast<sfz::Synth*>(arg);
auto* synth = reinterpret_cast<sfz::Sfizz*>(arg);
auto buffer = jack_port_get_buffer(midiInputPort, numFrames);
auto* buffer = jack_port_get_buffer(midiInputPort, numFrames);
assert(buffer);
auto numMidiEvents = jack_midi_get_event_count(buffer);
@ -97,9 +96,11 @@ int process(jack_nframes_t numFrames, void* arg)
}
}
auto leftOutput = reinterpret_cast<float*>(jack_port_get_buffer(outputPort1, numFrames));
auto rightOutput = reinterpret_cast<float*>(jack_port_get_buffer(outputPort2, numFrames));
synth->renderBlock({ { leftOutput, rightOutput }, numFrames });
auto* leftOutput = reinterpret_cast<float*>(jack_port_get_buffer(outputPort1, numFrames));
auto* rightOutput = reinterpret_cast<float*>(jack_port_get_buffer(outputPort2, numFrames));
float* stereoOutput[] = { leftOutput, rightOutput };
synth->renderBlock(stereoOutput, numFrames);
return 0;
}
@ -109,7 +110,7 @@ int sampleBlockChanged(jack_nframes_t nframes, void* arg)
if (arg == nullptr)
return 0;
auto synth = reinterpret_cast<sfz::Synth*>(arg);
auto* synth = reinterpret_cast<sfz::Sfizz*>(arg);
// DBG("Sample per block changed to " << nframes);
synth->setSamplesPerBlock(nframes);
return 0;
@ -120,19 +121,19 @@ int sampleRateChanged(jack_nframes_t nframes, void* arg)
if (arg == nullptr)
return 0;
auto synth = reinterpret_cast<sfz::Synth*>(arg);
auto* synth = reinterpret_cast<sfz::Sfizz*>(arg);
// DBG("Sample rate changed to " << nframes);
synth->setSampleRate(nframes);
return 0;
}
static bool shouldClose { false };
static volatile sig_atomic_t shouldClose { false };
static void done(int sig)
{
std::cout << "Signal received" << '\n';
shouldClose = true;
UNUSED(sig);
(void)sig;
// if (client != nullptr)
// exit(0);
@ -163,11 +164,11 @@ int main(int argc, char** argv)
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;
if (oversampling == "x1") return 1;
if (oversampling == "x2") return 2;
if (oversampling == "x4") return 4;
if (oversampling == "x8") return 8;
return 1;
}();
std::cout << "Positional arguments:";
@ -175,7 +176,7 @@ int main(int argc, char** argv)
std::cout << " " << file << ',';
std::cout << '\n';
sfz::Synth synth;
sfz::Sfizz synth;
synth.setOversamplingFactor(factor);
synth.setPreloadSize(preload_size);
synth.loadSfzFile(filesToParse[0]);
@ -186,6 +187,7 @@ int main(int argc, char** argv)
std::cout << "\tRegions: " << synth.getNumRegions() << '\n';
std::cout << "\tCurves: " << synth.getNumCurves() << '\n';
std::cout << "\tPreloadedSamples: " << synth.getNumPreloadedSamples() << '\n';
#if 0 // not currently in public API
std::cout << "==========" << '\n';
std::cout << "Included files:" << '\n';
for (auto& file : synth.getParser().getIncludedFiles())
@ -194,6 +196,7 @@ int main(int argc, char** argv)
std::cout << "Defines:" << '\n';
for (auto& define : synth.getParser().getDefines())
std::cout << '\t' << define.first << '=' << define.second << '\n';
#endif
std::cout << "==========" << '\n';
std::cout << "Unknown opcodes:";
for (auto& opcode : synth.getUnknownOpcodes())

View file

@ -65,6 +65,7 @@ int main(int argc, char** argv)
bool verbose { false };
bool help { false };
bool useEOT { false };
int quality { 2 };
int oversampling { 1 };
options.add_options()
@ -74,6 +75,7 @@ int main(int argc, char** argv)
("b,blocksize", "Block size for the sfizz callbacks", cxxopts::value(blockSize))
("s,samplerate", "Output sample rate", cxxopts::value(sampleRate))
("oversampling", "Internal oversampling factor", cxxopts::value(oversampling))
("q,quality", "Resampling quality", cxxopts::value(quality))
("v,verbose", "Verbose output", cxxopts::value(verbose))
("log", "Produce logs", cxxopts::value<std::string>())
("use-eot", "End the rendering at the last End of Track Midi message", cxxopts::value(useEOT))
@ -118,6 +120,7 @@ int main(int argc, char** argv)
sfz::Synth synth;
synth.setSamplesPerBlock(blockSize);
synth.setSampleRate(sampleRate);
synth.setSampleQuality(sfz::Synth::ProcessMode::ProcessFreewheeling, quality);
synth.enableFreeWheeling();
if (params.count("log") > 0)

View file

@ -14,6 +14,22 @@ else()
set (LV2PLUGIN_SPDX_LICENSE_ID "ISC")
endif()
if(SFIZZ_LV2_UI)
set(LV2PLUGIN_IF_ENABLE_UI "")
else()
set(LV2PLUGIN_IF_ENABLE_UI "#")
endif()
if(WIN32)
set(LV2_UI_TYPE "WindowsUI")
elseif(APPLE)
set(LV2_UI_TYPE "CocoaUI")
elseif(HAIKU)
set(LV2_UI_TYPE "BeUI")
else()
set(LV2_UI_TYPE "X11UI")
endif()
if (MSVC)
set (LV2PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lv2" CACHE STRING
"Install destination for LV2 bundle [default: ${CMAKE_INSTALL_PREFIX}/lv2}]")

View file

@ -17,6 +17,31 @@ if (WIN32)
add_compile_definitions(_WIN32_WINNT=0x601)
endif()
# Set macOS compatibility level
if (APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9")
endif()
# Do not define macros `min` and `max`
if (WIN32)
add_compile_definitions(NOMINMAX)
endif()
# Find macOS system libraries
if(APPLE)
find_library(APPLE_COREFOUNDATION_LIBRARY "CoreFoundation")
find_library(APPLE_FOUNDATION_LIBRARY "Foundation")
find_library(APPLE_COCOA_LIBRARY "Cocoa")
find_library(APPLE_CARBON_LIBRARY "Carbon")
find_library(APPLE_OPENGL_LIBRARY "OpenGL")
find_library(APPLE_ACCELERATE_LIBRARY "Accelerate")
find_library(APPLE_QUARTZCORE_LIBRARY "QuartzCore")
find_library(APPLE_AUDIOTOOLBOX_LIBRARY "AudioToolbox")
find_library(APPLE_AUDIOUNIT_LIBRARY "AudioUnit")
find_library(APPLE_COREAUDIO_LIBRARY "CoreAudio")
find_library(APPLE_COREMIDI_LIBRARY "CoreMIDI")
endif()
# The variable CMAKE_SYSTEM_PROCESSOR is incorrect on Visual studio...
# see https://gitlab.kitware.com/cmake/cmake/issues/15170
@ -32,10 +57,15 @@ endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_compile_options(-Wall)
add_compile_options(-Wextra)
add_compile_options(-ffast-math)
add_compile_options(-fno-omit-frame-pointer) # For debugging purposes
add_compile_options(-Wno-multichar)
add_compile_options(-Werror=return-type)
if (SFIZZ_SYSTEM_PROCESSOR MATCHES "^(i.86|x86_64)$")
add_compile_options(-msse2)
elseif(SFIZZ_SYSTEM_PROCESSOR MATCHES "^(arm.*)$")
add_compile_options(-mfpu=neon)
if (NOT ANDROID)
add_compile_options(-mfloat-abi=hard)
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_CXX_STANDARD 17)
@ -43,22 +73,34 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
function(sfizz_enable_fast_math NAME)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options("${NAME}" PRIVATE "-ffast-math")
endif()
endfunction()
# The sndfile library
add_library(sfizz-sndfile INTERFACE)
# The jsl utility library for C++
add_library(sfizz-jsl INTERFACE)
target_include_directories(sfizz-jsl INTERFACE "external/jsl/include")
if (SFIZZ_USE_VCPKG OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
find_package(LibSndFile REQUIRED)
find_package(SndFile CONFIG REQUIRED)
find_path(SNDFILE_INCLUDE_DIR sndfile.hh)
target_include_directories(sfizz-sndfile INTERFACE "${SNDFILE_INCLUDE_DIR}")
target_link_libraries(sfizz-sndfile INTERFACE sndfile-static)
target_link_libraries(sfizz-sndfile INTERFACE SndFile::sndfile)
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(SNDFILE "sndfile" REQUIRED)
target_include_directories(sfizz-sndfile INTERFACE ${SNDFILE_INCLUDE_DIRS})
if (SFIZZ_STATIC_LIBSNDFILE)
if (SFIZZ_STATIC_DEPENDENCIES)
target_link_libraries(sfizz-sndfile INTERFACE ${SNDFILE_STATIC_LIBRARIES})
else()
target_link_libraries(sfizz-sndfile INTERFACE ${SNDFILE_LIBRARIES})
endif()
link_directories(${SNDFILE_LIBRARY_DIRS})
endif()
@ -107,12 +149,13 @@ 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 LV2 user interface: ${SFIZZ_LV2_UI}
Build VST plug-in: ${SFIZZ_VST}
Build AU plug-in: ${SFIZZ_AU}
Build benchmarks: ${SFIZZ_BENCHMARKS}
Build tests: ${SFIZZ_TESTS}
Use vcpkg: ${SFIZZ_USE_VCPKG}
Statically link libsndfile: ${SFIZZ_STATIC_LIBSNDFILE}
Statically link dependencies: ${SFIZZ_STATIC_DEPENDENCIES}
Link libatomic: ${SFIZZ_LINK_LIBATOMIC}
Use clang libc++: ${USE_LIBCPP}
Release asserts: ${SFIZZ_RELEASE_ASSERTS}

View file

@ -3,6 +3,7 @@ macro(sfizz_add_simd_sources SOURCES_VAR PREFIX)
list (APPEND ${SOURCES_VAR}
${PREFIX}/sfizz/SIMDHelpers.cpp
${PREFIX}/sfizz/simd/HelpersNEON.cpp
${PREFIX}/sfizz/simd/HelpersSSE.cpp
${PREFIX}/sfizz/simd/HelpersAVX.cpp)

View file

@ -30,10 +30,10 @@ if(NOT VST3_PACKAGE_ARCHITECTURE)
else()
set(VST3_PACKAGE_ARCHITECTURE "i386")
endif()
elseif(VST3_SYSTEM_PROCESSOR MATCHES "^(armv7l)$")
set(VST3_PACKAGE_ARCHITECTURE "armv7l")
elseif(VST3_SYSTEM_PROCESSOR MATCHES "^(armv[3-8][a-z]*)$")
set(VST3_PACKAGE_ARCHITECTURE "${VST3_SYSTEM_PROCESSOR}")
elseif(VST3_SYSTEM_PROCESSOR MATCHES "^(aarch64)$")
set(VST3_PACKAGE_ARCHITECTURE "aarch64")
set(VST3_PACKAGE_ARCHITECTURE "aarch64")
else()
message(FATAL_ERROR "We don't know this architecture for VST3: ${VST3_SYSTEM_PROCESSOR}.")
endif()

296
common.mk Normal file
View file

@ -0,0 +1,296 @@
# Common definitions for builds based on GNU Make
ifndef SFIZZ_DIR
$(error sfizz: The source directory must be set before including)
endif
###
SFIZZ_MACHINE := $(shell $(CC) -dumpmachine)
SFIZZ_PROCESSOR := $(firstword $(subst -, ,$(SFIZZ_MACHINE)))
ifneq (,$(filter i%86,$(SFIZZ_PROCESSOR)))
SFIZZ_CPU_I386 := 1
SFIZZ_CPU_I386_OR_X86_64 := 1
endif
ifneq (,$(filter x86_64,$(SFIZZ_PROCESSOR)))
SFIZZ_CPU_X86_64 := 1
SFIZZ_CPU_I386_OR_X86_64 := 1
endif
ifneq (,$(filter arm%,$(SFIZZ_PROCESSOR)))
SFIZZ_CPU_ARM := 1
SFIZZ_CPU_ARM_OR_AARCH64 := 1
endif
ifneq (,$(filter aarch64%,$(SFIZZ_PROCESSOR)))
SFIZZ_CPU_AARCH64 := 1
SFIZZ_CPU_ARM_OR_AARCH64 := 1
endif
ifneq (,$(findstring linux,$(SFIZZ_MACHINE)))
SFIZZ_OS_LINUX := 1
endif
ifneq (,$(findstring apple,$(SFIZZ_MACHINE)))
SFIZZ_OS_APPLE := 1
endif
ifneq (,$(findstring mingw,$(SFIZZ_MACHINE)))
SFIZZ_OS_WINDOWS := 1
endif
###
SFIZZ_C_FLAGS = -I$(SFIZZ_DIR)/src
SFIZZ_CXX_FLAGS = $(SFIZZ_C_FLAGS)
SFIZZ_SOURCES = \
src/sfizz/ADSREnvelope.cpp \
src/sfizz/AudioReader.cpp \
src/sfizz/Curve.cpp \
src/sfizz/effects/Apan.cpp \
src/sfizz/Effects.cpp \
src/sfizz/modulations/ModId.cpp \
src/sfizz/modulations/ModKey.cpp \
src/sfizz/modulations/ModKeyHash.cpp \
src/sfizz/modulations/ModMatrix.cpp \
src/sfizz/modulations/sources/ADSREnvelope.cpp \
src/sfizz/modulations/sources/Controller.cpp \
src/sfizz/modulations/sources/FlexEnvelope.cpp \
src/sfizz/modulations/sources/LFO.cpp \
src/sfizz/effects/Compressor.cpp \
src/sfizz/effects/Disto.cpp \
src/sfizz/effects/Eq.cpp \
src/sfizz/effects/Filter.cpp \
src/sfizz/effects/Fverb.cpp \
src/sfizz/effects/Gain.cpp \
src/sfizz/effects/Gate.cpp \
src/sfizz/effects/impl/ResonantArrayAVX.cpp \
src/sfizz/effects/impl/ResonantArray.cpp \
src/sfizz/effects/impl/ResonantArraySSE.cpp \
src/sfizz/effects/impl/ResonantStringAVX.cpp \
src/sfizz/effects/impl/ResonantString.cpp \
src/sfizz/effects/impl/ResonantStringSSE.cpp \
src/sfizz/effects/Limiter.cpp \
src/sfizz/effects/Lofi.cpp \
src/sfizz/effects/Nothing.cpp \
src/sfizz/effects/Rectify.cpp \
src/sfizz/effects/Strings.cpp \
src/sfizz/effects/Width.cpp \
src/sfizz/EQPool.cpp \
src/sfizz/FileId.cpp \
src/sfizz/FileMetadata.cpp \
src/sfizz/FilePool.cpp \
src/sfizz/FilterPool.cpp \
src/sfizz/FlexEGDescription.cpp \
src/sfizz/FlexEnvelope.cpp \
src/sfizz/FloatEnvelopes.cpp \
src/sfizz/Logger.cpp \
src/sfizz/LFO.cpp \
src/sfizz/LFODescription.cpp \
src/sfizz/MidiState.cpp \
src/sfizz/OpcodeCleanup.cpp \
src/sfizz/Opcode.cpp \
src/sfizz/Oversampler.cpp \
src/sfizz/Panning.cpp \
src/sfizz/Parser.cpp \
src/sfizz/parser/Parser.cpp \
src/sfizz/parser/ParserPrivate.cpp \
src/sfizz/PolyphonyGroup.cpp \
src/sfizz/PowerFollower.cpp \
src/sfizz/Region.cpp \
src/sfizz/RegionSet.cpp \
src/sfizz/RTSemaphore.cpp \
src/sfizz/ScopedFTZ.cpp \
src/sfizz/sfizz.cpp \
src/sfizz/sfizz_wrapper.cpp \
src/sfizz/SfzFilter.cpp \
src/sfizz/SfzHelpers.cpp \
src/sfizz/SIMDHelpers.cpp \
src/sfizz/simd/HelpersSSE.cpp \
src/sfizz/simd/HelpersAVX.cpp \
src/sfizz/Smoothers.cpp \
src/sfizz/Synth.cpp \
src/sfizz/Tuning.cpp \
src/sfizz/utility/SpinMutex.cpp \
src/sfizz/Voice.cpp \
src/sfizz/VoiceStealing.cpp \
src/sfizz/Wavetables.cpp
### Other internal
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/sfizz
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/external
# Pkg-config dependency
SFIZZ_PKG_CONFIG ?= pkg-config
# Sndfile dependency
SFIZZ_SNDFILE_C_FLAGS ?= $(shell $(SFIZZ_PKG_CONFIG) --cflags sndfile)
SFIZZ_SNDFILE_CXX_FLAGS ?= $(SFIZZ_SNDFILE_C_FLAGS)
SFIZZ_SNDFILE_LINK_FLAGS ?= $(shell $(SFIZZ_PKG_CONFIG) --libs sndfile)
SFIZZ_C_FLAGS += $(SFIZZ_SNDFILE_C_FLAGS)
SFIZZ_CXX_FLAGS += $(SFIZZ_SNDFILE_CXX_FLAGS)
SFIZZ_LINK_FLAGS += $(SFIZZ_SNDFILE_LINK_FLAGS)
### Abseil dependency
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/external/abseil-cpp
# absl::base
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/cycleclock.cc \
external/abseil-cpp/absl/base/internal/spinlock.cc \
external/abseil-cpp/absl/base/internal/sysinfo.cc \
external/abseil-cpp/absl/base/internal/thread_identity.cc \
external/abseil-cpp/absl/base/internal/unscaledcycleclock.cc
# absl::exponential_biased
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/exponential_biased.cc
# absl::dynamic_annotations
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/dynamic_annotations.cc
# absl::malloc_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/low_level_alloc.cc
# absl::raw_logging_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/raw_logging.cc
# absl::spinlock_wait
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/spinlock_wait.cc
# absl::throw_delegate
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/throw_delegate.cc
# absl::strings
SFIZZ_SOURCES += \
external/abseil-cpp/absl/strings/ascii.cc \
external/abseil-cpp/absl/strings/charconv.cc \
external/abseil-cpp/absl/strings/escaping.cc \
external/abseil-cpp/absl/strings/internal/charconv_bigint.cc \
external/abseil-cpp/absl/strings/internal/charconv_parse.cc \
external/abseil-cpp/absl/strings/internal/memutil.cc \
external/abseil-cpp/absl/strings/match.cc \
external/abseil-cpp/absl/strings/numbers.cc \
external/abseil-cpp/absl/strings/str_cat.cc \
external/abseil-cpp/absl/strings/str_replace.cc \
external/abseil-cpp/absl/strings/str_split.cc \
external/abseil-cpp/absl/strings/string_view.cc \
external/abseil-cpp/absl/strings/substitute.cc
# absl::hashtablez_sampler
SFIZZ_SOURCES += \
external/abseil-cpp/absl/container/internal/hashtablez_sampler.cc \
external/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
# absl::raw_hash_set
SFIZZ_SOURCES += \
external/abseil-cpp/absl/container/internal/raw_hash_set.cc
# absl::synchronization
SFIZZ_SOURCES += \
external/abseil-cpp/absl/synchronization/barrier.cc \
external/abseil-cpp/absl/synchronization/blocking_counter.cc \
external/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc \
external/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc \
external/abseil-cpp/absl/synchronization/internal/waiter.cc \
external/abseil-cpp/absl/synchronization/notification.cc \
external/abseil-cpp/absl/synchronization/mutex.cc
# absl::graphcycles_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/synchronization/internal/graphcycles.cc
# absl::time
SFIZZ_SOURCES += \
external/abseil-cpp/absl/time/civil_time.cc \
external/abseil-cpp/absl/time/clock.cc \
external/abseil-cpp/absl/time/duration.cc \
external/abseil-cpp/absl/time/format.cc \
external/abseil-cpp/absl/time/time.cc
# absl::time_zone
SFIZZ_SOURCES += \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.cc \
external/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc
# absl::stacktrace
SFIZZ_SOURCES += \
external/abseil-cpp/absl/debugging/stacktrace.cc
# absl::symbolize
SFIZZ_SOURCES += \
external/abseil-cpp/absl/debugging/symbolize.cc
# absl::demangle_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/debugging/internal/demangle.cc
# absl::debugging_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/debugging/internal/address_is_readable.cc \
external/abseil-cpp/absl/debugging/internal/elf_mem_image.cc \
external/abseil-cpp/absl/debugging/internal/vdso_support.cc
# absl::hash
SFIZZ_SOURCES += \
external/abseil-cpp/absl/hash/internal/hash.cc
# absl::city
SFIZZ_SOURCES += \
external/abseil-cpp/absl/hash/internal/city.cc
# absl::int128
SFIZZ_SOURCES += \
external/abseil-cpp/absl/numeric/int128.cc
ifdef SFIZZ_OS_WINDOWS
SFIZZ_LINK_FLAGS += -ldbghelp
endif
### Spline dependency
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/external/spline
SFIZZ_SOURCES += src/external/spline/spline/spline.cpp
### Cpuid dependency
SFIZZ_C_FLAGS += \
-I$(SFIZZ_DIR)/src/external/cpuid/src \
-I$(SFIZZ_DIR)/src/external/cpuid/platform/src
SFIZZ_SOURCES += \
src/external/cpuid/src/cpuid/cpuinfo.cpp \
src/external/cpuid/src/cpuid/version.cpp
### Pugixml dependency
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/external/pugixml/src
SFIZZ_SOURCES += src/external/pugixml/src/pugixml.cpp
### Kissfft dependency
SFIZZ_C_FLAGS += \
-I$(SFIZZ_DIR)/src/external/kiss_fft \
-I$(SFIZZ_DIR)/src/external/kiss_fft/tools
SFIZZ_SOURCES += \
src/external/kiss_fft/kiss_fft.c \
src/external/kiss_fft/tools/kiss_fftr.c
### Surge tuning library dependency
SFIZZ_CXX_FLAGS += \
-I$(SFIZZ_DIR)/src/external/tunings/include
SFIZZ_SOURCES += \
src/external/tunings/src/Tunings.cpp
### jsl dependency
SFIZZ_CXX_FLAGS += \
-I$(SFIZZ_DIR)/external/jsl/include
### math dependency
ifdef SFIZZ_OS_LINUX
SFIZZ_LINK_FLAGS += -lm
endif
### pthread dependency
ifdef SFIZZ_OS_LINUX
SFIZZ_C_FLAGS += -pthread
SFIZZ_CXX_FLAGS += -pthread
SFIZZ_LINK_FLAGS += -pthread
endif

View file

@ -1,187 +0,0 @@
<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

@ -1,12 +0,0 @@
<hr>
<footer>
<p style="text-align:left">Copyright &copy; 2019-2020 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><!-- container -->
<script src="/sfizz/assets/js/scripts.min.js"></script>
</body>
</html>

View file

@ -1,155 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Home - sfizz</title>
<meta name="author" content="Paul Ferrand">
<meta name="web-author" content="Andrea Zanellato">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Atom Feeds -->
<link href="/sfizz/atom.xml" rel="alternate" title="News"
type="application/atom+xml" />
<!-- Favicon Generator Icons -->
<link rel="apple-touch-icon" sizes="180x180"
href="/sfizz/assets/ico/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/sfizz/assets/ico/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/sfizz/assets/ico/favicon-16x16.png">
<link rel="manifest" href="/sfizz/assets/ico/site.webmanifest">
<link rel="mask-icon" href="/sfizz/assets/ico/safari-pinned-tab.svg"
color="#5bbad5">
<link rel="shortcut icon" href="/sfizz/assets/ico/favicon.ico">
<meta name="msapplication-TileColor"
content="#da532c">
<meta name="msapplication-config" content="/sfizz/assets/ico/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<link href="$relpath$tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath$jquery.js"></script>
<script type="text/javascript" src="$relpath$dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath$$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<!-- Stylesheets -->
<link href="/sfizz/assets/css/style.min.css"
rel="stylesheet" media="screen">
<!-- Font Awesome Icons -->
<script src="https://kit.fontawesome.com/8d25d19870.js"></script>
</head>
<body>
<button onclick="topFunction()" id="scrollButton" title="Go to top">Top</button>
<div class="container">
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color:#222">
<a class="navbar-brand" href="/sfizz/">
<img src="/sfizz/assets/img/logo.png"
width="30" height="30" class="d-inline-block" alt="">
sfizz</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#navbarContent" aria-controls="navbarContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarContent">
<ul class="navbar-nav mr-auto mt-2 mt-lg-0">
<li class="nav-item">
<a class="nav-link" href="/">
<i class="fas fa-home fa-fw" aria-hidden="true"></i>
&nbsp;Home</a>
</li>
<!-- FIXME: Dropdown buttons are shown but menus are broken for some reason
(not the container div)
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#"
id="navbarProjects" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fas fa-hammer fa-fw" aria-hidden="true"></i>
&nbsp;Projects</a>
<div class="dropdown-menu" aria-labelledby="navbarProjects">
<a class="dropdown-item"
href="/beatbox/">
<i class="fas fa-drum fa-fw" aria-hidden="true"></i>
&nbsp;BeatBox</a>
<a class="dropdown-item disabled"
href="/sfizz/">
<i class="fas fa-play-circle fa-fw" aria-hidden="true"></i>
&nbsp;sfizz</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item"
href="https://github.com/sfztools/sfizz-render/">
sfizz-render</a>
<a class="dropdown-item"
href="https://github.com/sfztools/sfz-flat/">
sfz-flat</a>
<a class="dropdown-item"
href="https://github.com/sfztools/sfz-opcode-checker/">
sfz-opcode-checker</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item"
href="https://github.com/sfztools/emacs-sfz-mode/">
Emacs Syntax Highlight</a>
<a class="dropdown-item"
href="https://github.com/sfztools/syntax-highlighting-gedit/">
gedit Syntax Highlight</a>
<a class="dropdown-item"
href="https://github.com/sfztools/syntax-highlighting-geany/">
Geany Syntax Highlight</a>
<a class="dropdown-item"
href="https://github.com/sfztools/syntax-highlighting-sublime-text/">
Sublime Text Syntax Highlight</a>
<a class="dropdown-item"
href="https://github.com/sfztools/sfz.vim">
Vim Syntax Highlight</a>
<a class="dropdown-item"
href="https://github.com/sfztools/syntax-highlighting-vscode/">
VSCode Syntax Highlight</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#"
id="navbarDocumentation" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fas fa-book fa-fw" aria-hidden="true"></i>
&nbsp;Documentation</a>
<div class="dropdown-menu" aria-labelledby="navbarDocumentation">
<a class="dropdown-item"
href="/sfizz/api"><i class="fas fa-code fa-fw" aria-hidden="true"></i>
&nbsp;API</a>
<a class="dropdown-item"
href="/sfizz/development/build">
<i class="fas fa-code fa-fw" aria-hidden="true"></i>
&nbsp;Build</a>
<a class="dropdown-item"
href="/sfizz/development/status">
<i class="fas fa-tasks fa-fw" aria-hidden="true"></i>
&nbsp;SFZ Opcodes</a>
</div>
</li>
-->
</ul>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link" href="/sfizz/downloads">
<i class="fas fa-download fa-fw" aria-hidden="true"></i>
Download</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/sfizz/search/">
<i class="fas fa-search fa-fw" aria-hidden="true"></i>
Search</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

@ -1,198 +0,0 @@
#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

@ -1,154 +0,0 @@
# 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.

View file

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

View file

@ -1,19 +0,0 @@
#!/bin/bash
# Must be called from the root directory
if [[ -f api_index.md ]]; then rm api_index.md; fi
cat >>api_index.md <<EOF
---
title: "API"
---
EOF
tags=()
for tag in $(git tag --list); do
if [[ ${tag} != "list" && ${tag} != *"test"* ]]; then
tag=$(echo "${tag}" | sed -r 's/v//g')
tags+=("${tag}")
fi
done
IFS=$'\n' tags=($(sort <<<"${tags[*]}")); unset IFS
for tag in "${tags[@]}"; do
echo "- [${tag}](${tag})" >> api_index.md
done

202
dpf.mk
View file

@ -1,3 +1,4 @@
#
# A build file to help using sfizz with the DISTRHO Plugin Framework (DPF)
# ------------------------------------------------------------------------
@ -36,14 +37,9 @@
SFIZZ_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
SFIZZ_BUILD_DIR := $(SFIZZ_DIR)/dpf-build
SFIZZ_C_FLAGS = -I$(SFIZZ_DIR)/src
SFIZZ_CXX_FLAGS = $(SFIZZ_C_FLAGS)
SFIZZ_LINK_FLAGS = $(SFIZZ_BUILD_DIR)/libsfizz.a
ifeq ($(LINUX),true)
SFIZZ_LINK_FLAGS += -pthread
endif
SFIZZ_PKG_CONFIG ?= $(PKG_CONFIG)
include $(SFIZZ_DIR)/common.mk
sfizz-all: sfizz-lib
@ -54,198 +50,6 @@ sfizz-clean:
.PHONY: sfizz-all sfizz-lib sfizz-clean
SFIZZ_SOURCES = \
src/sfizz/ADSREnvelope.cpp \
src/sfizz/Curve.cpp \
src/sfizz/effects/Apan.cpp \
src/sfizz/Effects.cpp \
src/sfizz/effects/Eq.cpp \
src/sfizz/effects/Filter.cpp \
src/sfizz/effects/Gain.cpp \
src/sfizz/effects/impl/ResonantArrayAVX.cpp \
src/sfizz/effects/impl/ResonantArray.cpp \
src/sfizz/effects/impl/ResonantArraySSE.cpp \
src/sfizz/effects/impl/ResonantStringAVX.cpp \
src/sfizz/effects/impl/ResonantString.cpp \
src/sfizz/effects/impl/ResonantStringSSE.cpp \
src/sfizz/effects/Limiter.cpp \
src/sfizz/effects/Lofi.cpp \
src/sfizz/effects/Nothing.cpp \
src/sfizz/effects/Rectify.cpp \
src/sfizz/effects/Strings.cpp \
src/sfizz/effects/Width.cpp \
src/sfizz/EQPool.cpp \
src/sfizz/FileId.cpp \
src/sfizz/FileInstrument.cpp \
src/sfizz/FilePool.cpp \
src/sfizz/FilterPool.cpp \
src/sfizz/FloatEnvelopes.cpp \
src/sfizz/Logger.cpp \
src/sfizz/MidiState.cpp \
src/sfizz/OpcodeCleanup.cpp \
src/sfizz/Opcode.cpp \
src/sfizz/Oversampler.cpp \
src/sfizz/Panning.cpp \
src/sfizz/Parser.cpp \
src/sfizz/parser/Parser.cpp \
src/sfizz/parser/ParserPrivate.cpp \
src/sfizz/Region.cpp \
src/sfizz/RTSemaphore.cpp \
src/sfizz/ScopedFTZ.cpp \
src/sfizz/sfizz.cpp \
src/sfizz/sfizz_wrapper.cpp \
src/sfizz/SfzFilter.cpp \
src/sfizz/SfzHelpers.cpp \
src/sfizz/SIMDHelpers.cpp \
src/sfizz/simd/HelpersSSE.cpp \
src/sfizz/simd/HelpersAVX.cpp \
src/sfizz/Synth.cpp \
src/sfizz/Tuning.cpp \
src/sfizz/Voice.cpp \
src/sfizz/Wavetables.cpp
### Other internal
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/sfizz
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/external
# Sndfile dependency
SFIZZ_C_FLAGS += $(shell $(PKG_CONFIG) --cflags sndfile)
SFIZZ_LINK_FLAGS += $(shell $(PKG_CONFIG) --libs sndfile)
### Abseil dependency
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/external/abseil-cpp
# absl::base
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/cycleclock.cc \
external/abseil-cpp/absl/base/internal/spinlock.cc \
external/abseil-cpp/absl/base/internal/sysinfo.cc \
external/abseil-cpp/absl/base/internal/thread_identity.cc \
external/abseil-cpp/absl/base/internal/unscaledcycleclock.cc
# absl::exponential_biased
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/exponential_biased.cc
# absl::dynamic_annotations
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/dynamic_annotations.cc
# absl::malloc_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/low_level_alloc.cc
# absl::raw_logging_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/raw_logging.cc
# absl::spinlock_wait
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/spinlock_wait.cc
# absl::throw_delegate
SFIZZ_SOURCES += \
external/abseil-cpp/absl/base/internal/throw_delegate.cc
# absl::strings
SFIZZ_SOURCES += \
external/abseil-cpp/absl/strings/ascii.cc \
external/abseil-cpp/absl/strings/charconv.cc \
external/abseil-cpp/absl/strings/escaping.cc \
external/abseil-cpp/absl/strings/internal/charconv_bigint.cc \
external/abseil-cpp/absl/strings/internal/charconv_parse.cc \
external/abseil-cpp/absl/strings/internal/memutil.cc \
external/abseil-cpp/absl/strings/match.cc \
external/abseil-cpp/absl/strings/numbers.cc \
external/abseil-cpp/absl/strings/str_cat.cc \
external/abseil-cpp/absl/strings/str_replace.cc \
external/abseil-cpp/absl/strings/str_split.cc \
external/abseil-cpp/absl/strings/string_view.cc \
external/abseil-cpp/absl/strings/substitute.cc
# absl::hashtablez_sampler
SFIZZ_SOURCES += \
external/abseil-cpp/absl/container/internal/hashtablez_sampler.cc \
external/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
# absl::raw_hash_set
SFIZZ_SOURCES += \
external/abseil-cpp/absl/container/internal/raw_hash_set.cc
# absl::synchronization
SFIZZ_SOURCES += \
external/abseil-cpp/absl/synchronization/barrier.cc \
external/abseil-cpp/absl/synchronization/blocking_counter.cc \
external/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc \
external/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc \
external/abseil-cpp/absl/synchronization/internal/waiter.cc \
external/abseil-cpp/absl/synchronization/notification.cc \
external/abseil-cpp/absl/synchronization/mutex.cc
# absl::graphcycles_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/synchronization/internal/graphcycles.cc
# absl::time
SFIZZ_SOURCES += \
external/abseil-cpp/absl/time/civil_time.cc \
external/abseil-cpp/absl/time/clock.cc \
external/abseil-cpp/absl/time/duration.cc \
external/abseil-cpp/absl/time/format.cc \
external/abseil-cpp/absl/time/time.cc
# absl::time_zone
SFIZZ_SOURCES += \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc \
external/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.cc \
external/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc
# absl::stacktrace
SFIZZ_SOURCES += \
external/abseil-cpp/absl/debugging/stacktrace.cc
# absl::symbolize
SFIZZ_SOURCES += \
external/abseil-cpp/absl/debugging/symbolize.cc
# absl::demangle_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/debugging/internal/demangle.cc
# absl::debugging_internal
SFIZZ_SOURCES += \
external/abseil-cpp/absl/debugging/internal/address_is_readable.cc \
external/abseil-cpp/absl/debugging/internal/elf_mem_image.cc \
external/abseil-cpp/absl/debugging/internal/vdso_support.cc
# absl::hash
SFIZZ_SOURCES += \
external/abseil-cpp/absl/hash/internal/hash.cc
# absl::city
SFIZZ_SOURCES += \
external/abseil-cpp/absl/hash/internal/city.cc
# absl::int128
SFIZZ_SOURCES += \
external/abseil-cpp/absl/numeric/int128.cc
### Spline dependency
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/external/spline
SFIZZ_SOURCES += src/external/spline/spline/spline.cpp
### Cpuid dependency
SFIZZ_C_FLAGS += \
-I$(SFIZZ_DIR)/src/external/cpuid/src \
-I$(SFIZZ_DIR)/src/external/cpuid/platform/src
SFIZZ_SOURCES += \
src/external/cpuid/src/cpuid/cpuinfo.cpp \
src/external/cpuid/src/cpuid/version.cpp
### Pugixml dependency
SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/src/external/pugixml/src
SFIZZ_SOURCES += src/external/pugixml/src/pugixml.cpp
### Kissfft dependency
SFIZZ_C_FLAGS += \
-I$(SFIZZ_DIR)/src/external/kiss_fft \
-I$(SFIZZ_DIR)/src/external/kiss_fft/tools
SFIZZ_SOURCES += \
src/external/kiss_fft/kiss_fft.c \
src/external/kiss_fft/tools/kiss_fftr.c
###
SFIZZ_OBJECTS = $(SFIZZ_SOURCES:%=$(SFIZZ_BUILD_DIR)/%.o)

88
editor/CMakeLists.txt Normal file
View file

@ -0,0 +1,88 @@
set(VSTGUI_BASEDIR "${CMAKE_CURRENT_SOURCE_DIR}/external/vstgui4")
include("cmake/Vstgui.cmake")
set(EDITOR_RESOURCES
logo.png
logo_text.png
logo_text_white.png
logo_text@2x.png
logo_text_white@2x.png
background.png
background@2x.png
icon_white.png
icon_white@2x.png
knob48.png
knob48@2x.png
Fonts/sfizz-fluentui-system-r20.ttf
Fonts/Roboto-Regular.ttf
PARENT_SCOPE)
function(copy_editor_resources SOURCE_DIR DESTINATION_DIR)
foreach(res ${EDITOR_RESOURCES})
get_filename_component(_dir "${res}" DIRECTORY)
file(MAKE_DIRECTORY "${DESTINATION_DIR}/${_dir}")
file(COPY "${SOURCE_DIR}/${res}" DESTINATION "${DESTINATION_DIR}/${_dir}")
endforeach()
endfunction()
# editor
add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
src/editor/EditIds.h
src/editor/EditIds.cpp
src/editor/Editor.h
src/editor/Editor.cpp
src/editor/EditorController.h
src/editor/GUIComponents.h
src/editor/GUIComponents.cpp
src/editor/GUIPiano.h
src/editor/GUIPiano.cpp
src/editor/NativeHelpers.h
src/editor/NativeHelpers.cpp
src/editor/layout/main.hpp
src/editor/utility/vstgui_after.h
src/editor/utility/vstgui_before.h)
target_include_directories(sfizz_editor PUBLIC "src")
target_link_libraries(sfizz_editor PRIVATE sfizz-vstgui)
target_link_libraries(sfizz_editor PUBLIC absl::strings)
if(APPLE)
find_library(APPLE_APPKIT_LIBRARY "AppKit")
find_library(APPLE_CORESERVICES_LIBRARY "CoreServices")
find_library(APPLE_FOUNDATION_LIBRARY "Foundation")
target_sources(sfizz_editor PRIVATE
src/editor/NativeHelpers.mm)
target_link_libraries(sfizz_editor PRIVATE
"${APPLE_APPKIT_LIBRARY}"
"${APPLE_CORESERVICES_LIBRARY}"
"${APPLE_FOUNDATION_LIBRARY}")
target_compile_options(sfizz_editor PRIVATE "-fobjc-arc")
endif()
# dependencies
if(WIN32)
#
elseif(APPLE)
#
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(sfizz-gio "gio-2.0" REQUIRED)
target_include_directories(sfizz_editor PRIVATE ${sfizz-gio_INCLUDE_DIRS})
target_link_libraries(sfizz_editor PRIVATE ${sfizz-gio_LIBRARIES})
endif()
target_include_directories(sfizz_editor PRIVATE "../src/external") # ghc::filesystem
# layout tool
if(NOT CMAKE_CROSSCOMPILING)
add_executable(layout-maker
"tools/layout-maker/sources/layout.h"
"tools/layout-maker/sources/reader.cpp"
"tools/layout-maker/sources/reader.h"
"tools/layout-maker/sources/main.cpp")
target_link_libraries(layout-maker PRIVATE absl::strings)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/editor/layout/main.hpp"
COMMAND "$<TARGET_FILE:layout-maker>"
"${CMAKE_CURRENT_SOURCE_DIR}/layout/main.fl"
> "${CMAKE_CURRENT_SOURCE_DIR}/src/editor/layout/main.hpp"
DEPENDS layout-maker "${CMAKE_CURRENT_SOURCE_DIR}/layout/main.fl")
endif()

224
editor/cmake/Vstgui.cmake Normal file
View file

@ -0,0 +1,224 @@
add_library(sfizz-vstgui STATIC EXCLUDE_FROM_ALL
"${VSTGUI_BASEDIR}/vstgui/lib/animation/animations.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/animation/animator.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/animation/timingfunctions.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cbitmap.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cbitmapfilter.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/ccolor.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cdatabrowser.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cdrawcontext.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cdrawmethods.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cdropsource.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cfileselector.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cfont.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cframe.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cgradientview.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cgraphicspath.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/clayeredviewcontainer.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/clinestyle.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/coffscreencontext.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cautoanimation.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cbuttons.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/ccolorchooser.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/ccontrol.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cfontchooser.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cknob.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/clistcontrol.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cmoviebitmap.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cmoviebutton.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/coptionmenu.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cparamdisplay.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cscrollbar.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/csearchtextedit.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/csegmentbutton.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cslider.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cspecialdigit.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/csplashscreen.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cstringlist.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cswitch.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/ctextedit.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/ctextlabel.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cvumeter.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/controls/cxypad.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/copenglview.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cpoint.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/crect.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/crowcolumnview.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cscrollview.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cshadowviewcontainer.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/csplitview.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cstring.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/ctabview.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/ctooltipsupport.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cview.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cviewcontainer.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/cvstguitimer.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/genericstringlistdatabrowsersource.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/common/genericoptionmenu.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/platformfactory.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/vstguidebug.cpp")
if(WIN32)
target_sources(sfizz-vstgui PRIVATE
"${VSTGUI_BASEDIR}/vstgui/lib/platform/common/fileresourceinputstream.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dbitmap.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dfont.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32datapackage.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32dragging.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32frame.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32openglview.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32optionmenu.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32support.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32resourcestream.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32textedit.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32factory.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/winfileselector.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/winstring.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/wintimer.cpp")
elseif(APPLE)
target_sources(sfizz-vstgui PRIVATE
"${VSTGUI_BASEDIR}/vstgui/lib/platform/common/fileresourceinputstream.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/common/genericoptionmenu.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/common/generictextedit.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/carbon/hiviewframe.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/carbon/hiviewoptionmenu.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/carbon/hiviewtextedit.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/caviewlayer.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cfontmac.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cgbitmap.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cgdrawcontext.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/autoreleasepool.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/cocoaopenglview.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/nsviewframe.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macclipboard.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macfactory.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macfileselector.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macglobals.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macstring.mm"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/mactimer.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/quartzgraphicspath.cpp")
else()
target_sources(sfizz-vstgui PRIVATE
"${VSTGUI_BASEDIR}/vstgui/lib/platform/common/fileresourceinputstream.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/common/generictextedit.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairobitmap.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairocontext.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairofont.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairogradient.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairopath.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/linuxfactory.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/linuxstring.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11dragging.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11fileselector.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11frame.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11platform.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11timer.cpp"
"${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11utils.cpp")
endif()
target_include_directories(sfizz-vstgui PUBLIC "${VSTGUI_BASEDIR}")
if(WIN32)
if (NOT MSVC)
# autolinked on MSVC with pragmas
target_link_libraries(sfizz-vstgui PRIVATE
"opengl32"
"d2d1"
"dwrite"
"dwmapi"
"windowscodecs"
"shlwapi")
endif()
elseif(APPLE)
target_link_libraries(sfizz-vstgui PRIVATE
"${APPLE_COREFOUNDATION_LIBRARY}"
"${APPLE_FOUNDATION_LIBRARY}"
"${APPLE_COCOA_LIBRARY}"
"${APPLE_OPENGL_LIBRARY}"
"${APPLE_ACCELERATE_LIBRARY}"
"${APPLE_QUARTZCORE_LIBRARY}"
"${APPLE_CARBON_LIBRARY}"
"${APPLE_AUDIOTOOLBOX_LIBRARY}"
"${APPLE_COREAUDIO_LIBRARY}"
"${APPLE_COREMIDI_LIBRARY}")
else()
find_package(X11 REQUIRED)
find_package(Freetype REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBXCB REQUIRED xcb)
pkg_check_modules(LIBXCB_UTIL REQUIRED xcb-util)
pkg_check_modules(LIBXCB_CURSOR REQUIRED xcb-cursor)
pkg_check_modules(LIBXCB_KEYSYMS REQUIRED xcb-keysyms)
pkg_check_modules(LIBXCB_XKB REQUIRED xcb-xkb)
pkg_check_modules(LIBXKB_COMMON REQUIRED xkbcommon)
pkg_check_modules(LIBXKB_COMMON_X11 REQUIRED xkbcommon-x11)
pkg_check_modules(CAIRO REQUIRED cairo)
pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
pkg_check_modules(GLIB REQUIRED glib-2.0)
target_include_directories(sfizz-vstgui PRIVATE
${X11_INCLUDE_DIRS}
${FREETYPE_INCLUDE_DIRS}
${LIBXCB_INCLUDE_DIRS}
${LIBXCB_UTIL_INCLUDE_DIRS}
${LIBXCB_CURSOR_INCLUDE_DIRS}
${LIBXCB_KEYSYMS_INCLUDE_DIRS}
${LIBXCB_XKB_INCLUDE_DIRS}
${LIBXKB_COMMON_INCLUDE_DIRS}
${LIBXKB_COMMON_X11_INCLUDE_DIRS}
${CAIRO_INCLUDE_DIRS}
${FONTCONFIG_INCLUDE_DIRS}
${GLIB_INCLUDE_DIRS})
target_link_libraries(sfizz-vstgui PRIVATE
${X11_LIBRARIES}
${FREETYPE_LIBRARIES}
${LIBXCB_LIBRARIES}
${LIBXCB_UTIL_LIBRARIES}
${LIBXCB_CURSOR_LIBRARIES}
${LIBXCB_KEYSYMS_LIBRARIES}
${LIBXCB_XKB_LIBRARIES}
${LIBXKB_COMMON_LIBRARIES}
${LIBXKB_COMMON_X11_LIBRARIES}
${CAIRO_LIBRARIES}
${FONTCONFIG_LIBRARIES}
${GLIB_LIBRARIES})
find_library(DL_LIBRARY "dl")
if(DL_LIBRARY)
target_link_libraries(sfizz-vstgui PRIVATE "${DL_LIBRARY}")
endif()
endif()
if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
target_compile_definitions(sfizz-vstgui PRIVATE "DEVELOPMENT")
endif()
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
target_compile_definitions(sfizz-vstgui PRIVATE "RELEASE")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# higher C++ requirement on Windows
set_property(TARGET sfizz-vstgui PROPERTY CXX_STANDARD 14)
# Windows 10 RS2 DDI for custom fonts
target_compile_definitions(sfizz-vstgui PRIVATE "NTDDI_VERSION=0x0A000003")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(sfizz-vstgui PRIVATE
"-Wno-deprecated-copy"
"-Wno-deprecated-declarations"
"-Wno-extra"
"-Wno-ignored-qualifiers"
"-Wno-multichar"
"-Wno-reorder"
"-Wno-sign-compare"
"-Wno-unknown-pragmas"
"-Wno-unused-function"
"-Wno-unused-parameter"
"-Wno-unused-variable")
endif()

1
editor/external/vstgui4 vendored Submodule

@ -0,0 +1 @@
Subproject commit 055cbcc9ae858f0b07d5d86c205a1111e2fba7a4

312
editor/layout/main.fl Normal file
View file

@ -0,0 +1,312 @@
# data file for the Fltk User Interface Designer (fluid)
version 1.0305
header_name {.h}
code_name {.cxx}
widget_class mainView {open
xywh {576 416 800 475} type Double
class LogicalGroup visible
} {
Fl_Box {} {
image {../resources/background.png} xywh {190 110 600 280}
class Background
}
Fl_Group {} {
comment {theme=darkTheme} open
xywh {0 0 800 110}
class LogicalGroup
} {
Fl_Group {} {open
xywh {5 4 175 101} box ROUNDED_BOX align 0
class RoundedGroup
} {
Fl_Box {} {
comment {tag=kTagFirstChangePanel+kPanelGeneral}
image {../resources/logo_text_white.png} xywh {35 9 120 60}
class SfizzMainButton
}
Fl_Button {} {
comment {tag=kTagFirstChangePanel+kPanelGeneral}
xywh {49 73 25 25} labelsize 24
class HomeButton
}
Fl_Button {} {
comment {tag=kTagFirstChangePanel+kPanelControls}
xywh {81 73 25 25} labelsize 24
class CCButton
}
Fl_Button {} {
comment {tag=kTagFirstChangePanel+kPanelSettings}
xywh {112 73 25 25} labelsize 24
class SettingsButton
}
}
Fl_Group {} {open
xywh {185 5 380 100} box ROUNDED_BOX
class RoundedGroup
} {
Fl_Box {} {
label {Separator 1}
xywh {195 41 360 5} box BORDER_BOX labeltype NO_LABEL
class HLine
}
Fl_Box {} {
label {Separator 2}
xywh {195 73 360 5} box BORDER_BOX labeltype NO_LABEL
class HLine
}
Fl_Box sfzFileLabel_ {
label {DefaultInstrument.sfz}
comment {tag=kTagLoadSfzFile}
xywh {195 11 250 31} labelsize 20 align 20
class ClickableLabel
}
Fl_Box {} {
label {Key switch:}
xywh {195 44 250 30} labelsize 20 align 20
class Label
}
Fl_Box {} {
label {Voices:}
xywh {195 76 60 25} labelsize 12 align 24
class Label
}
Fl_Button {} {
comment {tag=kTagPreviousSfzFile}
xywh {480 14 25 25} labelsize 24
class PreviousFileButton
}
Fl_Button {} {
comment {tag=kTagNextSfzFile}
xywh {505 14 25 25} labelsize 24
class NextFileButton
}
Fl_Button fileOperationsMenu_ {
comment {tag=kTagFileOperations}
xywh {530 14 25 25} labelsize 24
class ChevronDropDown
}
Fl_Box infoVoicesLabel_ {
xywh {260 76 50 25} labelsize 12 align 16
class Label
}
Fl_Box {} {
label {Max:}
xywh {315 76 60 25} labelsize 12 align 24
class Label
}
Fl_Box numVoicesLabel_ {
xywh {380 76 50 25} labelsize 12 align 16
class Label
}
Fl_Box {} {
label {Memory:}
xywh {435 76 60 25} labelsize 12 align 24
class Label
}
Fl_Box memoryLabel_ {
xywh {500 76 50 25} labelsize 12 align 16
class Label
}
}
Fl_Group {} {open
xywh {570 5 225 100} box ROUNDED_BOX
class RoundedGroup
} {
Fl_Dial {} {
xywh {615 20 48 48} value 0.5 hide
class Knob48
}
Fl_Box {} {
label Center
xywh {610 70 60 5} labelsize 12 hide
class ValueLabel
}
Fl_Dial volumeSlider_ {
comment {tag=kTagSetVolume}
xywh {680 20 48 48} value 0.5
class StyledKnob
}
Fl_Box volumeLabel_ {
label {0.0 dB}
xywh {675 70 60 22} labelsize 12
class ValueLabel
}
Fl_Box {} {
xywh {745 20 35 55} box BORDER_BOX
class VMeter
}
}
}
Fl_Group {subPanels_[kPanelGeneral]} {
xywh {5 110 791 285}
class LogicalGroup
} {
Fl_Group {} {open
xywh {5 110 175 280} box ROUNDED_BOX
class RoundedGroup
} {
Fl_Box {} {
label {Curves:}
xywh {20 120 60 25} align 20
class Label
}
Fl_Box {} {
label {Masters:}
xywh {20 145 60 25} align 20
class Label
}
Fl_Box {} {
label {Groups:}
xywh {20 170 60 25} align 20
class Label
}
Fl_Box {} {
label {Regions:}
xywh {20 195 60 25} align 20
class Label
}
Fl_Box {} {
label {Samples:}
xywh {20 220 60 25} align 20
class Label
}
Fl_Box infoCurvesLabel_ {
label 0
xywh {120 120 40 25} align 16
class Label
}
Fl_Box infoMastersLabel_ {
label 0
xywh {120 145 40 25} align 16
class Label
}
Fl_Box infoGroupsLabel_ {
label 0
xywh {120 170 40 25} align 16
class Label
}
Fl_Box infoRegionsLabel_ {
label 0
xywh {120 195 40 25} align 16
class Label
}
Fl_Box infoSamplesLabel_ {
label 0
xywh {120 220 40 25} align 16
class Label
}
}
}
Fl_Group {subPanels_[kPanelControls]} {
xywh {5 110 790 285} hide
class LogicalGroup
} {
Fl_Group {} {open
xywh {5 110 790 285} box ROUNDED_BOX
class RoundedGroup
} {
Fl_Box {} {
label {Controls not available}
xywh {5 110 790 285} labelsize 40
class Label
}
}
}
Fl_Group {subPanels_[kPanelSettings]} {open
xywh {5 109 790 286} hide
class LogicalGroup
} {
Fl_Group {} {
label Engine open
xywh {260 135 280 100} box ROUNDED_BOX labelsize 12 align 17
class TitleGroup
} {
Fl_Spinner numVoicesSlider_ {
comment {tag=kTagSetNumVoices}
xywh {285 195 60 25} labelsize 12 textsize 12
class ValueMenu
}
Fl_Box {} {
label Polyphony
xywh {275 155 80 25} labelsize 12
class ValueLabel
}
Fl_Spinner oversamplingSlider_ {
comment {tag=kTagSetOversampling}
xywh {370 195 60 25} labelsize 12 textsize 12
class ValueMenu
}
Fl_Box {} {
label Oversampling
xywh {360 155 80 25} labelsize 12
class ValueLabel
}
Fl_Box {} {
label {Preload size}
xywh {445 155 80 25} labelsize 12
class ValueLabel
}
Fl_Spinner preloadSizeSlider_ {
comment {tag=kTagSetPreloadSize}
xywh {455 195 60 25} labelsize 12 textsize 12
class ValueMenu
}
}
Fl_Group {} {
label Tuning open
xywh {205 270 390 100} box ROUNDED_BOX labelsize 12 align 17
class TitleGroup
} {
Fl_Box {} {
label {Root key}
xywh {330 290 80 25} labelsize 12
class ValueLabel
}
Fl_Spinner tuningFrequencySlider_ {
comment {tag=kTagSetTuningFrequency}
xywh {425 330 60 25} labelsize 12 textsize 12
class ValueMenu
}
Fl_Box {} {
label Frequency
xywh {415 290 80 25} labelsize 12
class ValueLabel
}
Fl_Dial stretchedTuningSlider_ {
comment {tag=kTagSetStretchedTuning}
xywh {515 315 48 48} value 0.5
class StyledKnob
}
Fl_Box {} {
label Stretch
xywh {500 290 80 25} labelsize 12
class ValueLabel
}
Fl_Box {} {
label {Scala file}
xywh {225 290 100 25} labelsize 12
class ValueLabel
}
Fl_Button scalaFileButton_ {
label DefaultScale
comment {tag=kTagLoadScalaFile}
xywh {225 330 100 25} labelsize 12
class ValueButton
}
Fl_Spinner scalaRootKeySlider_ {
comment {tag=kTagSetScalaRootKey}
xywh {340 330 35 25} labelsize 12 textsize 12
class ValueMenu
}
Fl_Spinner scalaRootOctaveSlider_ {
comment {tag=kTagSetScalaRootKey}
xywh {375 330 30 25} labelsize 12 textsize 12
class ValueMenu
}
}
}
Fl_Box piano_ {selected
xywh {5 400 790 70} labelsize 12
class Piano
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
editor/resources/knob.knob Normal file

Binary file not shown.

BIN
editor/resources/knob48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="192"
inkscape:export-xdpi="192"
inkscape:export-filename="/home/jpc/documents/projects/sfizz/editor/resources/logo_text@2x.png"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="logo_text.svg"
id="svg8"
version="1.1"
viewBox="0 0 105.83333 66.145836"
height="250"
width="400">
<title
id="title1469">sfizz logo</title>
<defs
id="defs2">
<linearGradient
id="linearGradient1445"
inkscape:collect="always">
<stop
id="stop1441"
offset="0"
style="stop-color:#646464;stop-opacity:1" />
<stop
id="stop1443"
offset="1"
style="stop-color:#000000;stop-opacity:1" />
</linearGradient>
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect1150"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.47797039,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.51256137,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.33778335,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.27944107,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.25554572,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.29009315,0,2"
unit="mm"
method="auto"
mode="C"
radius="1"
chamfer_steps="2"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="2"
radius="1"
mode="C"
method="auto"
unit="mm"
satellites_param="F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.47797039,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.51256137,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.33778335,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.27944107,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.25554572,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.29009315,0,2"
lpeversion="1"
is_visible="true"
id="path-effect1136"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="2"
radius="1"
mode="C"
method="auto"
unit="mm"
satellites_param="F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.47797039,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.51256137,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.33778335,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.27944107,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.25554572,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.29009315,0,2"
lpeversion="1"
is_visible="true"
id="path-effect968"
effect="fillet_chamfer" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect921"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.47797039,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.51256137,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.33778335,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.27944107,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.25554572,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.29009315,0,2"
unit="mm"
method="auto"
mode="C"
radius="1"
chamfer_steps="2"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="2"
radius="1"
mode="C"
method="auto"
unit="mm"
satellites_param="F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,1,0,2 @ F,0,0,1,0,0,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.47797039,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.51256137,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.33778335,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.27944107,0,2 | F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.25554572,0,2 @ F,0,0,1,0,0,0,2 @ F,0,0,1,0,0.29009315,0,2"
lpeversion="1"
is_visible="true"
id="path-effect1183"
effect="fillet_chamfer" />
<inkscape:path-effect
end_linecap_type="zerowidth"
scale_width="1"
miter_limit="4"
linejoin_type="round"
start_linecap_type="zerowidth"
interpolator_beta="0.2"
interpolator_type="CubicBezierFit"
sort_points="true"
offset_points="0.19099662,0.36320029 | 0.41721681,0.40988822 | 0.9095762,0.070646707"
lpeversion="1"
is_visible="true"
id="path-effect1172"
effect="powerstroke" />
<clipPath
id="clipPath917"
clipPathUnits="userSpaceOnUse">
<rect
y="68.872978"
x="24.792475"
height="25.252825"
width="39.659527"
id="rect919"
style="fill:#ffffff;stroke:none;stroke-width:2;stroke-linecap:round;paint-order:markers stroke fill" />
</clipPath>
<linearGradient
gradientUnits="userSpaceOnUse"
y2="231.60674"
x2="539.22601"
y1="190.42654"
x1="539.22601"
id="linearGradient1447"
xlink:href="#linearGradient1445"
inkscape:collect="always"
gradientTransform="translate(34.589224,10.333824)" />
</defs>
<sodipodi:namedview
fit-margin-bottom="10"
fit-margin-right="10"
fit-margin-left="10"
fit-margin-top="10"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-center="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox="true"
inkscape:guide-bbox="true"
inkscape:window-maximized="1"
inkscape:window-y="27"
inkscape:window-x="0"
inkscape:window-height="1008"
inkscape:window-width="1920"
showguides="false"
inkscape:snap-global="false"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="px"
inkscape:cy="53.822531"
inkscape:cx="17.632385"
inkscape:zoom="1"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
units="px">
<sodipodi:guide
id="guide899"
orientation="1,0"
position="-452.4563,176.31198" />
<sodipodi:guide
id="guide901"
orientation="0,-1"
position="-351.71366,154.32488" />
<sodipodi:guide
id="guide1008"
orientation="1,0"
position="-536.98274,156.82863" />
<sodipodi:guide
id="guide1185"
orientation="1,0"
position="-351.75629,154.33106" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>sfizz logo</dc:title>
<dc:date>2020-05-16</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Tobiasz 'unfa' Karoń</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title>CC-0</dc:title>
</cc:Agent>
</dc:rights>
<cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
transform="translate(-495.92008,-191.91875)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
id="path1439"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:34.8154px;line-height:1.25;font-family:ABeeZee;-inkscape-font-specification:ABeeZee;letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient1447);fill-opacity:1;stroke:none;stroke-width:0.264584"
d="m 569.95053,215.93967 h 13.61282 v 2.40226 l -9.9572,12.91651 h 10.13128 v 2.78523 h -13.99579 v -2.47189 l 9.99202,-12.91651 h -9.78313 z m -16.4328,0 h 13.61282 v 2.40226 l -9.9572,12.91651 h 10.13128 v 2.78523 h -13.99579 v -2.47189 l 9.99202,-12.91651 h -9.78313 z m -6.38378,18.104 h -3.13338 l 3.1682,-18.104 h 3.13339 z m 2.33264,-21.20257 c -0.64989,0 -1.14891,-0.18568 -1.49707,-0.55705 -0.32494,-0.39457 -0.48741,-0.84717 -0.48741,-1.3578 0,-0.74273 0.2321,-1.33459 0.69631,-1.77559 0.4642,-0.46421 1.04446,-0.69631 1.74077,-0.69631 0.64989,0 1.1373,0.19729 1.46224,0.59187 0.34815,0.37136 0.52223,0.81235 0.52223,1.32298 0,0.74273 -0.2321,1.34619 -0.6963,1.8104 -0.46421,0.441 -1.04446,0.6615 -1.74077,0.6615 z m -12.24537,3.76006 h 5.36157 l -0.48741,2.7156 h -5.36157 l -3.06376,17.23362 c -0.18568,1.02125 -0.4526,1.88003 -0.80075,2.57634 -0.34815,0.71952 -0.76594,1.29978 -1.25336,1.74077 -0.48741,0.44099 -1.04446,0.75433 -1.67114,0.94002 -0.62667,0.20889 -1.29977,0.31334 -2.01929,0.31334 -0.76594,0 -1.46225,-0.10445 -2.08892,-0.31334 -0.64989,-0.20889 -1.21854,-0.49902 -1.70596,-0.87039 l 1.25336,-2.57634 c 0.32494,0.32495 0.70791,0.58026 1.1489,0.76594 0.41779,0.18568 0.8936,0.27852 1.42744,0.27852 0.62667,0 1.16051,-0.2205 1.6015,-0.66149 0.46421,-0.44099 0.78915,-1.14891 0.97484,-2.12374 l 3.06375,-17.30325 h -2.78523 l 0.48741,-2.7156 h 2.78524 l 0.52223,-2.78523 c 0.39457,-2.13535 1.14891,-3.65562 2.263,-4.56082 1.11409,-0.92841 2.54152,-1.39262 4.28229,-1.39262 0.95162,0 1.83361,0.12766 2.64597,0.38297 0.81236,0.23211 1.54348,0.52224 2.19337,0.87039 l -1.21854,2.64597 c -0.53383,-0.27853 -1.09088,-0.53384 -1.67114,-0.76594 -0.55704,-0.23211 -1.21853,-0.34816 -1.98447,-0.34816 -0.85878,0 -1.5899,0.25531 -2.19337,0.76594 -0.60347,0.48742 -0.99804,1.25336 -1.18373,2.29782 z m -21.96217,13.36911 c 0.60347,0.53384 1.3578,0.96323 2.263,1.28817 0.92841,0.32495 1.91485,0.48742 2.95931,0.48742 1.29977,0 2.2746,-0.20889 2.92449,-0.62668 0.6731,-0.44099 1.00965,-0.99804 1.00965,-1.67114 0,-0.34815 -0.0696,-0.6731 -0.20889,-0.97483 -0.11605,-0.30173 -0.34816,-0.59186 -0.69631,-0.87039 -0.32495,-0.27852 -0.77755,-0.54544 -1.3578,-0.80075 -0.58026,-0.27852 -1.33459,-0.58025 -2.263,-0.9052 -1.71756,-0.58026 -2.99413,-1.26496 -3.8297,-2.05411 -0.83557,-0.81236 -1.25335,-1.92645 -1.25335,-3.34228 0,-0.69631 0.15087,-1.34619 0.4526,-1.94966 0.32495,-0.60347 0.77755,-1.1257 1.3578,-1.56669 0.60347,-0.46421 1.32299,-0.82397 2.15856,-1.07928 0.85878,-0.25531 1.83361,-0.38297 2.92449,-0.38297 1.18372,0 2.21658,0.12766 3.09857,0.38297 0.9052,0.2321 1.71756,0.53383 2.43708,0.9052 l -0.87039,2.57634 c -0.62667,-0.37137 -1.34619,-0.6731 -2.15855,-0.9052 -0.78915,-0.2321 -1.67114,-0.34815 -2.64597,-0.34815 -1.20693,0 -2.12374,0.24371 -2.75042,0.73112 -0.62668,0.48741 -0.94002,1.02125 -0.94002,1.60151 0,0.27852 0.0464,0.53383 0.13927,0.76594 0.11605,0.2321 0.31333,0.4526 0.59186,0.66149 0.27852,0.20889 0.64988,0.41779 1.11409,0.62668 0.46421,0.20889 1.04446,0.42938 1.74077,0.66149 1.13731,0.39457 2.10053,0.80075 2.88968,1.21854 0.78915,0.39457 1.42743,0.83557 1.91485,1.32298 0.48741,0.48742 0.83557,1.02126 1.04446,1.60151 0.20889,0.55705 0.31334,1.18372 0.31334,1.88003 0,0.81236 -0.17408,1.54348 -0.52223,2.19337 -0.34816,0.64989 -0.83558,1.20693 -1.46225,1.67114 -0.62668,0.44099 -1.39262,0.77754 -2.29782,1.00965 -0.88199,0.25531 -1.86842,0.38297 -2.95931,0.38297 -1.39261,0 -2.63436,-0.17408 -3.72524,-0.52223 -1.09089,-0.34815 -1.99609,-0.80075 -2.71561,-1.3578 z"
sodipodi:nodetypes="cccccccccccccccccccccccccccscscscscsccccccscsccccsccccccccsccccscccccscscccccscccsccccssscssccccssccsscc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -0,0 +1,32 @@
// 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 "EditIds.h"
EditRange EditRange::get(EditId id)
{
switch (id) {
default:
assert(false);
return {};
case EditId::Volume:
return { 0, -60, 6 };
case EditId::Polyphony:
return { 64, 1, 256 };
case EditId::Oversampling:
return { 0, 0, 3 };
case EditId::PreloadSize:
return { 8192, 1024, 65536 };
case EditId::ScalaRootKey:
return { 60, 0, 127 };
case EditId::TuningFrequency:
return { 440, 300, 500 };
case EditId::StretchTuning:
return { 0, 0, 1 };
case EditId::UIActivePanel:
return { 0, 0, 255 };
}
}

View file

@ -0,0 +1,37 @@
// 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
#pragma once
#include <cassert>
enum class EditId : int {
SfzFile,
Volume,
Polyphony,
Oversampling,
PreloadSize,
ScalaFile,
ScalaRootKey,
TuningFrequency,
StretchTuning,
UINumCurves,
UINumMasters,
UINumGroups,
UINumRegions,
UINumPreloadedSamples,
UINumActiveVoices,
UIActivePanel,
};
struct EditRange {
float def = 0.0;
float min = 0.0;
float max = 1.0;
constexpr EditRange() = default;
constexpr EditRange(float def, float min, float max)
: def(def), min(min), max(max) {}
static EditRange get(EditId id);
};

View file

@ -0,0 +1,67 @@
// 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
#pragma once
#include <string>
#include <new>
#include <stdexcept>
class EditValue {
public:
constexpr EditValue() : tag(Nil) {}
EditValue(float value) { reset(value); }
EditValue(std::string value) { reset(value); }
~EditValue() { reset(); }
void reset() noexcept
{
if (tag == String)
destruct(u.s);
tag = Nil;
}
void reset(float value) noexcept
{
reset();
u.f = value;
tag = Float;
}
void reset(std::string value) noexcept
{
reset();
new (&u.s) std::string(std::move(value));
tag = String;
}
float to_float() const
{
if (tag != Float)
throw std::runtime_error("the tagged union does not contain `float`");
return u.f;
}
const std::string& to_string() const
{
if (tag != String)
throw std::runtime_error("the tagged union does not contain `string`");
return u.s;
}
private:
template <class T> static void destruct(T& obj) { obj.~T(); }
private:
enum TypeTag { Nil, Float, String };
union Union {
constexpr explicit Union(float f = 0.0f) noexcept : f(f) {}
~Union() noexcept {}
float f;
std::string s;
};
TypeTag tag { Nil };
Union u;
};

1175
editor/src/editor/Editor.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,30 @@
// 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
#pragma once
#include <memory>
class EditorController;
#include "utility/vstgui_before.h"
#include "vstgui/lib/vstguifwd.h"
#include "utility/vstgui_after.h"
using VSTGUI::CFrame;
class Editor {
public:
static const int viewWidth;
static const int viewHeight;
explicit Editor(EditorController& ctrl);
~Editor();
void open(CFrame& frame);
void close();
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};

View file

@ -0,0 +1,43 @@
// 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
#pragma once
#include "EditValue.h"
#include <absl/strings/string_view.h>
#include <string>
#include <cstdint>
enum class EditId : int;
class EditorController {
public:
virtual ~EditorController() {}
// called by Editor
virtual void uiSendValue(EditId id, const EditValue& v) = 0;
virtual void uiBeginSend(EditId id) = 0;
virtual void uiEndSend(EditId id) = 0;
virtual void uiSendMIDI(const uint8_t* msg, uint32_t len) = 0;
class Receiver;
void decorate(Receiver* r) { r_ = r; }
class Receiver {
public:
virtual ~Receiver() {}
virtual void uiReceiveValue(EditId id, const EditValue& v) = 0;
};
// called by DSP
void uiReceiveValue(EditId id, const EditValue& v);
private:
Receiver* r_ = nullptr;
};
inline void EditorController::uiReceiveValue(EditId id, const EditValue& v)
{
if (r_)
r_->uiReceiveValue(id, v);
}

View file

@ -0,0 +1,495 @@
// 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 "GUIComponents.h"
#include <complex>
#include <cmath>
#include "utility/vstgui_before.h"
#include "vstgui/lib/cdrawcontext.h"
#include "vstgui/lib/cgraphicspath.h"
#include "vstgui/lib/cframe.h"
#include "utility/vstgui_after.h"
///
SBoxContainer::SBoxContainer(const CRect& size)
: CViewContainer(size)
{
CViewContainer::setBackgroundColor(CColor(0, 0, 0, 0));
}
void SBoxContainer::setCornerRadius(CCoord radius)
{
cornerRadius_ = radius;
invalid();
}
void SBoxContainer::setBackgroundColor(const CColor& color)
{
backgroundColor_ = color;
invalid();
}
CColor SBoxContainer::getBackgroundColor() const
{
return backgroundColor_;
}
void SBoxContainer::drawRect(CDrawContext* dc, const CRect& updateRect)
{
CRect bounds = getViewSize();
dc->setDrawMode(kAntiAliasing);
SharedPointer<CGraphicsPath> path = owned(dc->createGraphicsPath());
path->addRoundRect(bounds, cornerRadius_);
dc->setFillColor(backgroundColor_);
dc->drawGraphicsPath(path.get(), CDrawContext::kPathFilled);
CViewContainer::drawRect(dc, updateRect);
}
///
STitleContainer::STitleContainer(const CRect& size, UTF8StringPtr text)
: SBoxContainer(size), text_(text ? text : ""), titleFont_(kNormalFont)
{
}
void STitleContainer::setTitleFont(CFontRef font)
{
titleFont_ = font;
invalid();
}
void STitleContainer::setTitleFontColor(CColor color)
{
titleFontColor_ = color;
invalid();
}
void STitleContainer::setTitleBackgroundColor(CColor color)
{
titleBackgroundColor_ = color;
invalid();
}
void STitleContainer::drawRect(CDrawContext* dc, const CRect& updateRect)
{
SBoxContainer::drawRect(dc, updateRect);
CRect bounds = getViewSize();
CCoord cornerRadius = cornerRadius_;
dc->setDrawMode(kAntiAliasing);
CCoord fontHeight = titleFont_->getSize();
CCoord titleHeight = fontHeight + 8.0;
CRect titleBounds = bounds;
titleBounds.bottom = titleBounds.top + titleHeight;
SharedPointer<CGraphicsPath> path = owned(dc->createGraphicsPath());
path->beginSubpath(titleBounds.getBottomRight());
path->addLine(titleBounds.getBottomLeft());
path->addArc(CRect(titleBounds.left, titleBounds.top, titleBounds.left + 2.0 * cornerRadius, titleBounds.top + 2.0 * cornerRadius), 180., 270., true);
path->addArc(CRect(titleBounds.right - 2.0 * cornerRadius, titleBounds.top, titleBounds.right, titleBounds.top + 2.0 * cornerRadius), 270., 360., true);
path->closeSubpath();
dc->setFillColor(titleBackgroundColor_);
dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
dc->setFont(titleFont_);
dc->setFontColor(titleFontColor_);
dc->drawString(text_.c_str(), titleBounds, kCenterText);
}
///
void SFileDropTarget::setFileDropFunction(FileDropFunction f)
{
dropFunction_ = std::move(f);
}
DragOperation SFileDropTarget::onDragEnter(DragEventData data)
{
op_ = isFileDrop(data.drag) ?
DragOperation::Copy : DragOperation::None;
return op_;
}
DragOperation SFileDropTarget::onDragMove(DragEventData data)
{
(void)data;
return op_;
}
void SFileDropTarget::onDragLeave(DragEventData data)
{
(void)data;
op_ = DragOperation::None;
}
bool SFileDropTarget::onDrop(DragEventData data)
{
if (op_ != DragOperation::Copy || !isFileDrop(data.drag))
return false;
IDataPackage::Type type;
const void* bytes;
uint32_t size = data.drag->getData(0, bytes, type);
std::string path(reinterpret_cast<const char*>(bytes), size);
if (dropFunction_)
dropFunction_(path);
return true;
}
bool SFileDropTarget::isFileDrop(IDataPackage* package)
{
return package->getCount() == 1 &&
package->getDataType(0) == IDataPackage::kFilePath;
}
///
SValueMenu::SValueMenu(const CRect& bounds, IControlListener* listener, int32_t tag)
: CParamDisplay(bounds), menuListener_(owned(new MenuListener(*this)))
{
setListener(listener);
setTag(tag);
}
CMenuItem* SValueMenu::addEntry(CMenuItem* item, float value, int32_t index)
{
if (index < 0 || index > getNbEntries()) {
menuItems_.emplace_back(owned(item));
menuItemValues_.emplace_back(value);
}
else
{
menuItems_.insert(menuItems_.begin() + index, owned(item));
menuItemValues_.insert(menuItemValues_.begin() + index, value);
}
return item;
}
CMenuItem* SValueMenu::addEntry(const UTF8String& title, float value, int32_t index, int32_t itemFlags)
{
if (title == "-")
return addSeparator(index);
CMenuItem* item = new CMenuItem(title, nullptr, 0, nullptr, itemFlags);
return addEntry(item, value, index);
}
CMenuItem* SValueMenu::addSeparator(int32_t index)
{
CMenuItem* item = new CMenuItem("", nullptr, 0, nullptr, CMenuItem::kSeparator);
return addEntry(item, 0.0f, index);
}
int32_t SValueMenu::getNbEntries() const
{
return static_cast<int32_t>(menuItems_.size());
}
CMouseEventResult SValueMenu::onMouseDown(CPoint& where, const CButtonState& buttons)
{
(void)where;
if (buttons & (kLButton|kRButton|kApple)) {
CFrame* frame = getFrame();
CRect bounds = getViewSize();
CPoint frameWhere = bounds.getBottomLeft();
this->localToFrame(frameWhere);
auto self = shared(this);
frame->doAfterEventProcessing([self, frameWhere]() {
if (CFrame* frame = self->getFrame()) {
SharedPointer<COptionMenu> menu = owned(new COptionMenu(CRect(), self->menuListener_, -1, nullptr, nullptr, COptionMenu::kPopupStyle));
for (const SharedPointer<CMenuItem>& item : self->menuItems_) {
menu->addEntry(item);
item->remember(); // above call does not increment refcount
}
menu->setFont(self->getFont());
menu->setFontColor(self->getFontColor());
menu->setBackColor(self->getBackColor());
menu->popup(frame, frameWhere + CPoint(0.0, 1.0));
}
});
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
return kMouseEventNotHandled;
}
void SValueMenu::onItemClicked(int32_t index)
{
float oldValue = getValue();
setValue(menuItemValues_[index]);
if (getValue() != oldValue)
valueChanged();
}
///
SActionMenu::SActionMenu(const CRect& bounds, IControlListener* listener)
: CParamDisplay(bounds), menuListener_(owned(new MenuListener(*this)))
{
setListener(listener);
auto toString = [](float, std::string& result, CParamDisplay* display) {
result = static_cast<SActionMenu*>(display)->getTitle();
return true;
};
setValueToStringFunction2(toString);
}
void SActionMenu::setTitle(std::string title)
{
title_ = std::move(title);
invalid();
}
void SActionMenu::setHoverColor(const CColor& color)
{
hoverColor_ = color;
invalid();
}
CMenuItem* SActionMenu::addEntry(CMenuItem* item, int32_t tag, int32_t index)
{
if (index < 0 || index > getNbEntries()) {
menuItems_.emplace_back(owned(item));
menuItemTags_.emplace_back(tag);
}
else
{
menuItems_.insert(menuItems_.begin() + index, owned(item));
menuItemTags_.insert(menuItemTags_.begin() + index, tag);
}
return item;
}
CMenuItem* SActionMenu::addEntry(const UTF8String& title, int32_t tag, int32_t index, int32_t itemFlags)
{
if (title == "-")
return addSeparator(index);
CMenuItem* item = new CMenuItem(title, nullptr, 0, nullptr, itemFlags);
return addEntry(item, tag, index);
}
CMenuItem* SActionMenu::addSeparator(int32_t index)
{
CMenuItem* item = new CMenuItem("", nullptr, 0, nullptr, CMenuItem::kSeparator);
return addEntry(item, 0.0f, index);
}
int32_t SActionMenu::getNbEntries() const
{
return static_cast<int32_t>(menuItems_.size());
}
void SActionMenu::draw(CDrawContext* dc)
{
CColor backupColor = fontColor;
if (hovered_)
fontColor = hoverColor_;
CParamDisplay::draw(dc);
if (hovered_)
fontColor = backupColor;
}
CMouseEventResult SActionMenu::onMouseEntered(CPoint& where, const CButtonState& buttons)
{
hovered_ = true;
invalid();
return CParamDisplay::onMouseEntered(where, buttons);
}
CMouseEventResult SActionMenu::onMouseExited(CPoint& where, const CButtonState& buttons)
{
hovered_ = false;
invalid();
return CParamDisplay::onMouseExited(where, buttons);
}
CMouseEventResult SActionMenu::onMouseDown(CPoint& where, const CButtonState& buttons)
{
(void)where;
if (buttons & (kLButton|kRButton|kApple)) {
CFrame* frame = getFrame();
CRect bounds = getViewSize();
CPoint frameWhere = bounds.getBottomLeft();
this->localToFrame(frameWhere);
auto self = shared(this);
frame->doAfterEventProcessing([self, frameWhere]() {
if (CFrame* frame = self->getFrame()) {
SharedPointer<COptionMenu> menu = owned(new COptionMenu(CRect(), self->menuListener_, -1, nullptr, nullptr, COptionMenu::kPopupStyle));
for (const SharedPointer<CMenuItem>& item : self->menuItems_) {
menu->addEntry(item);
item->remember(); // above call does not increment refcount
}
menu->setFont(self->getFont());
menu->setFontColor(self->getFontColor());
menu->setBackColor(self->getBackColor());
menu->popup(frame, frameWhere + CPoint(0.0, 1.0));
}
});
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
return kMouseEventNotHandled;
}
void SActionMenu::onItemClicked(int32_t index)
{
setTag(menuItemTags_[index]);
setValue(1.0f);
if (listener)
listener->valueChanged(this);
setValue(0.0f);
if (listener)
listener->valueChanged(this);
}
///
void STextButton::setHoverColor(const CColor& color)
{
hoverColor_ = color;
invalid();
}
void STextButton::setInactiveColor(const CColor& color)
{
inactiveColor_ = color;
invalid();
}
void STextButton::setInactive(bool b)
{
inactive_ = b;
invalid();
}
void STextButton::draw(CDrawContext* context)
{
CColor backupColor = textColor;
if (hovered_)
textColor = hoverColor_; // textColor is protected
else if (inactive_)
textColor = inactiveColor_;
CTextButton::draw(context);
textColor = backupColor;
}
CMouseEventResult STextButton::onMouseEntered (CPoint& where, const CButtonState& buttons)
{
hovered_ = true;
invalid();
return CTextButton::onMouseEntered(where, buttons);
}
CMouseEventResult STextButton::onMouseExited (CPoint& where, const CButtonState& buttons)
{
hovered_ = false;
invalid();
return CTextButton::onMouseExited(where, buttons);
}
///
SStyledKnob::SStyledKnob(const CRect& size, IControlListener* listener, int32_t tag)
: CKnobBase(size, listener, tag, nullptr)
{
}
void SStyledKnob::setActiveTrackColor(const CColor& color)
{
if (activeTrackColor_ == color)
return;
activeTrackColor_ = color;
invalid();
}
void SStyledKnob::setInactiveTrackColor(const CColor& color)
{
if (inactiveTrackColor_ == color)
return;
inactiveTrackColor_ = color;
invalid();
}
void SStyledKnob::setLineIndicatorColor(const CColor& color)
{
if (lineIndicatorColor_ == color)
return;
lineIndicatorColor_ = color;
invalid();
}
void SStyledKnob::draw(CDrawContext* dc)
{
const CCoord lineWidth = 4.0;
const CCoord indicatorLineLength = 10.0;
const CCoord angleSpread = 250.0;
const CCoord angle1 = 270.0 - 0.5 * angleSpread;
const CCoord angle2 = 270.0 + 0.5 * angleSpread;
dc->setDrawMode(kAntiAliasing);
const CRect bounds = getViewSize();
// compute inner bounds
CRect rect(bounds);
rect.setWidth(std::min(rect.getWidth(), rect.getHeight()));
rect.setHeight(rect.getWidth());
rect.centerInside(bounds);
rect.extend(-lineWidth, -lineWidth);
SharedPointer<CGraphicsPath> path;
// inactive track
path = owned(dc->createGraphicsPath());
path->addArc(rect, angle1, angle2, true);
dc->setFrameColor(inactiveTrackColor_);
dc->setLineWidth(lineWidth);
dc->setLineStyle(kLineSolid);
dc->drawGraphicsPath(path, CDrawContext::kPathStroked);
// active track
const CCoord v = getValueNormalized();
const CCoord vAngle = angle1 + v * angleSpread;
path = owned(dc->createGraphicsPath());
path->addArc(rect, angle1, vAngle, true);
dc->setFrameColor(activeTrackColor_);
dc->setLineWidth(lineWidth + 0.5);
dc->setLineStyle(kLineSolid);
dc->drawGraphicsPath(path, CDrawContext::kPathStroked);
// indicator line
{
CCoord module1 = 0.5 * rect.getWidth() - indicatorLineLength;
CCoord module2 = 0.5 * rect.getWidth();
std::complex<CCoord> c1 = std::polar(module1, vAngle * (M_PI / 180.0));
std::complex<CCoord> c2 = std::polar(module2, vAngle * (M_PI / 180.0));
CPoint p1(c1.real(), c1.imag());
CPoint p2(c2.real(), c2.imag());
p1.offset(rect.getCenter());
p2.offset(rect.getCenter());
dc->setFrameColor(lineIndicatorColor_);
dc->setLineWidth(1.0);
dc->setLineStyle(kLineSolid);
dc->drawLine(p1, p2);
}
}

View file

@ -0,0 +1,210 @@
// 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
#pragma once
#include <bitset>
#include "utility/vstgui_before.h"
#include "vstgui/lib/controls/cslider.h"
#include "vstgui/lib/controls/cknob.h"
#include "vstgui/lib/controls/ctextlabel.h"
#include "vstgui/lib/controls/cbuttons.h"
#include "vstgui/lib/controls/coptionmenu.h"
#include "vstgui/lib/cviewcontainer.h"
#include "vstgui/lib/ccolor.h"
#include "vstgui/lib/dragging.h"
#include "utility/vstgui_after.h"
using namespace VSTGUI;
///
class SBoxContainer : public CViewContainer {
public:
explicit SBoxContainer(const CRect& size);
virtual ~SBoxContainer() {}
void setCornerRadius(CCoord radius);
void setBackgroundColor(const CColor& color) override;
CColor getBackgroundColor() const override;
protected:
void drawRect(CDrawContext* dc, const CRect& updateRect) override;
protected:
CCoord cornerRadius_ = 0.0;
CColor backgroundColor_;
};
///
class STitleContainer : public SBoxContainer {
public:
explicit STitleContainer(const CRect& size, UTF8StringPtr text = nullptr);
~STitleContainer() {}
void setTitleFont(CFontRef font);
CFontRef getTitleFont() { return titleFont_; }
void setTitleFontColor(CColor color);
CColor getTitleFontColor() const { return titleFontColor_; }
void setTitleBackgroundColor(CColor color);
CColor getTitleBackgroundColor() const { return titleBackgroundColor_; }
protected:
void drawRect(CDrawContext* dc, const CRect& updateRect) override;
private:
std::string text_;
CColor titleFontColor_;
CColor titleBackgroundColor_;
SharedPointer<CFontDesc> titleFont_;
};
///
class SFileDropTarget : public IDropTarget,
public NonAtomicReferenceCounted {
public:
typedef std::function<void(const std::string&)> FileDropFunction;
void setFileDropFunction(FileDropFunction f);
protected:
DragOperation onDragEnter(DragEventData data) override;
DragOperation onDragMove(DragEventData data) override;
void onDragLeave(DragEventData data) override;
bool onDrop(DragEventData data) override;
private:
static bool isFileDrop(IDataPackage* package);
private:
DragOperation op_ = DragOperation::None;
FileDropFunction dropFunction_;
};
///
class SValueMenu : public CParamDisplay {
public:
explicit SValueMenu(const CRect& bounds, IControlListener* listener, int32_t tag);
CMenuItem* addEntry(CMenuItem* item, float value, int32_t index = -1);
CMenuItem* addEntry(const UTF8String& title, float value, int32_t index = -1, int32_t itemFlags = CMenuItem::kNoFlags);
CMenuItem* addSeparator(int32_t index = -1);
int32_t getNbEntries() const;
protected:
CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override;
private:
class MenuListener;
//
void onItemClicked(int32_t index);
//
CMenuItemList menuItems_;
std::vector<float> menuItemValues_;
SharedPointer<MenuListener> menuListener_;
//
class MenuListener : public IControlListener, public NonAtomicReferenceCounted {
public:
explicit MenuListener(SValueMenu& menu) : menu_(menu) {}
void valueChanged(CControl* control) override
{
menu_.onItemClicked(static_cast<int32_t>(control->getValue()));
}
private:
SValueMenu& menu_;
};
};
///
class SActionMenu : public CParamDisplay {
public:
explicit SActionMenu(const CRect& bounds, IControlListener* listener);
std::string getTitle() const { return title_; }
void setTitle(std::string title);
CColor getHoverColor() const { return hoverColor_; }
void setHoverColor(const CColor& color);
CMenuItem* addEntry(CMenuItem* item, int32_t tag, int32_t index = -1);
CMenuItem* addEntry(const UTF8String& title, int32_t tag, int32_t index = -1, int32_t itemFlags = CMenuItem::kNoFlags);
CMenuItem* addSeparator(int32_t index = -1);
int32_t getNbEntries() const;
protected:
void draw(CDrawContext* dc) override;
CMouseEventResult onMouseEntered(CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseExited(CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override;
private:
std::string title_;
CColor hoverColor_;
bool hovered_ = false;
class MenuListener;
//
void onItemClicked(int32_t index);
//
CMenuItemList menuItems_;
std::vector<int32_t> menuItemTags_;
SharedPointer<MenuListener> menuListener_;
//
class MenuListener : public IControlListener, public NonAtomicReferenceCounted {
public:
explicit MenuListener(SActionMenu& menu) : menu_(menu) {}
void valueChanged(CControl* control) override
{
menu_.onItemClicked(static_cast<int32_t>(control->getValue()));
}
private:
SActionMenu& menu_;
};
};
///
class STextButton: public CTextButton {
public:
STextButton(const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1, UTF8StringPtr title = nullptr)
: CTextButton(size, listener, tag, title) {}
CColor getHoverColor() const { return hoverColor_; }
void setHoverColor(const CColor& color);
CColor getInactiveColor() const { return inactiveColor_; }
void setInactiveColor(const CColor& color);
bool isInactive() const { return inactive_; }
void setInactive(bool b);
CMouseEventResult onMouseEntered (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) override;
void draw(CDrawContext* context) override;
private:
CColor hoverColor_;
bool hovered_ { false };
CColor inactiveColor_;
bool inactive_ { false };
};
///
class SStyledKnob : public CKnobBase {
public:
SStyledKnob(const CRect& size, IControlListener* listener, int32_t tag);
const CColor& getActiveTrackColor() const { return activeTrackColor_; }
void setActiveTrackColor(const CColor& color);
const CColor& getInactiveTrackColor() const { return inactiveTrackColor_; }
void setInactiveTrackColor(const CColor& color);
const CColor& getLineIndicatorColor() const { return lineIndicatorColor_; }
void setLineIndicatorColor(const CColor& color);
CLASS_METHODS(SStyledKnob, CKnobBase)
protected:
void draw(CDrawContext* dc) override;
private:
CColor activeTrackColor_;
CColor inactiveTrackColor_;
CColor lineIndicatorColor_;
};

View file

@ -0,0 +1,238 @@
// 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 "GUIPiano.h"
#include "utility/vstgui_before.h"
#include "vstgui/lib/cdrawcontext.h"
#include "vstgui/lib/cgraphicspath.h"
#include "utility/vstgui_after.h"
#include <algorithm>
#include <cmath>
static constexpr CCoord keyoffs[12] = {0, 0.6, 1, 1.8, 2, 3,
3.55, 4, 4.7, 5, 5.85, 6};
static constexpr bool black[12] = {0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0};
SPiano::SPiano(CRect bounds)
: CView(bounds)
{
setNumOctaves(10);
}
void SPiano::setFont(CFontRef font)
{
font_ = font;
getDimensions(true);
invalid();
}
void SPiano::setNumOctaves(unsigned octs)
{
keyval_.resize(octs * 12);
octs_ = std::max(1u, octs);
getDimensions(true);
invalid();
}
void SPiano::draw(CDrawContext* dc)
{
const Dimensions dim = getDimensions(false);
const unsigned octs = octs_;
const unsigned keyCount = octs * 12;
dc->setDrawMode(kAntiAliasing);
if (backgroundFill_.alpha > 0) {
SharedPointer<CGraphicsPath> path;
path = owned(dc->createGraphicsPath());
path->addRoundRect(dim.bounds, backgroundRadius_);
dc->setFillColor(CColor(0xca, 0xca, 0xca));
dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
}
for (unsigned key = 0; key < keyCount; ++key) {
if (!black[key % 12]) {
CRect rect = keyRect(key);
CColor keycolor = whiteFill_;
if (keyval_[key])
keycolor = pressedFill_;
dc->setFillColor(keycolor);
dc->drawRect(rect, kDrawFilled);
}
}
dc->setFrameColor(outline_);
dc->drawLine(dim.keyBounds.getTopLeft(), dim.keyBounds.getBottomLeft());
for (unsigned key = 0; key < keyCount; ++key) {
if (!black[key % 12]) {
CRect rect = keyRect(key);
dc->drawLine(rect.getTopRight(), rect.getBottomRight());
}
}
for (unsigned key = 0; key < keyCount; ++key) {
if (black[key % 12]) {
CRect rect = keyRect(key);
CColor keycolor = blackFill_;
if (keyval_[key])
keycolor = pressedFill_;
dc->setFillColor(keycolor);
dc->drawRect(rect, kDrawFilled);
dc->setFrameColor(outline_);
dc->drawRect(rect);
}
}
if (const CFontRef& font = font_) {
for (unsigned o = 0; o < octs; ++o) {
CRect rect = keyRect(o * 12);
CRect textRect(
rect.left, dim.labelBounds.top,
rect.right, dim.labelBounds.bottom);
dc->setFont(font_);
dc->setFontColor(labelStroke_);
std::string text = std::to_string(static_cast<int>(o) - 1);
dc->drawString(text.c_str(), textRect, kCenterText);
}
}
{
dc->setFrameColor(outline_);
dc->drawLine(dim.keyBounds.getTopLeft(), dim.keyBounds.getTopRight());
dc->setFrameColor(shadeOutline_);
dc->drawLine(dim.keyBounds.getBottomLeft(), dim.keyBounds.getBottomRight());
}
dc->setFrameColor(outline_);
}
CMouseEventResult SPiano::onMouseDown(CPoint& where, const CButtonState& buttons)
{
unsigned key = keyAtPos(where);
if (key != ~0u) {
keyval_[key] = 1;
mousePressedKey_ = key;
if (onKeyPressed)
onKeyPressed(key, mousePressVelocity(key, where.y));
invalid();
return kMouseEventHandled;
}
return CView::onMouseDown(where, buttons);
}
CMouseEventResult SPiano::onMouseUp(CPoint& where, const CButtonState& buttons)
{
unsigned key = mousePressedKey_;
if (key != ~0u) {
keyval_[key] = 0;
if (onKeyReleased)
onKeyReleased(key, mousePressVelocity(key, where.y));
mousePressedKey_ = ~0u;
invalid();
return kMouseEventHandled;
}
return CView::onMouseUp(where, buttons);
}
CMouseEventResult SPiano::onMouseMoved(CPoint& where, const CButtonState& buttons)
{
if (mousePressedKey_ != ~0u) {
unsigned key = keyAtPos(where);
if (mousePressedKey_ != key) {
keyval_[mousePressedKey_] = 0;
if (onKeyReleased)
onKeyReleased(mousePressedKey_, mousePressVelocity(key, where.y));
// mousePressedKey_ = ~0u;
if (key != ~0u) {
keyval_[key] = 1;
mousePressedKey_ = key;
if (onKeyPressed)
onKeyPressed(key, mousePressVelocity(key, where.y));
}
invalid();
}
return kMouseEventHandled;
}
return CView::onMouseMoved(where, buttons);
}
const SPiano::Dimensions& SPiano::getDimensions(bool forceUpdate) const
{
if (!forceUpdate && dim_.bounds == getViewSize())
return dim_;
Dimensions dim;
dim.bounds = getViewSize();
dim.paddedBounds = CRect(dim.bounds)
.extend(-2 * innerPaddingX_, -2 * innerPaddingY_);
CCoord keyHeight = std::floor(dim.paddedBounds.getHeight());
CCoord fontHeight = font_ ? font_->getSize() : 0.0;
keyHeight -= spacingY_ + fontHeight;
dim.keyBounds = CRect(dim.paddedBounds)
.setHeight(keyHeight);
dim.keyWidth = static_cast<unsigned>(
dim.paddedBounds.getWidth() / octs_ / 7.0);
dim.keyBounds.setWidth(dim.keyWidth * octs_ * 7.0);
dim.keyBounds.offset(
std::floor(0.5 * (dim.paddedBounds.getWidth() - dim.keyBounds.getWidth())), 0.0);
if (!font_)
dim.labelBounds = CRect();
else
dim.labelBounds = CRect(
dim.keyBounds.left, dim.keyBounds.bottom + spacingY_,
dim.keyBounds.right, dim.keyBounds.bottom + spacingY_ + fontHeight);
dim_ = dim;
return dim_;
}
CRect SPiano::keyRect(const Dimensions& dim, unsigned key)
{
unsigned oct = key / 12;
unsigned note = key % 12;
unsigned keyw = dim.keyWidth;
unsigned keyh = static_cast<unsigned>(dim.keyBounds.getHeight());
CCoord octwidth = (keyoffs[11] + 1.0) * keyw;
CCoord octx = octwidth * oct;
CCoord notex = octx + keyoffs[note] * keyw;
CCoord notew = black[note] ? (0.6 * keyw) : keyw;
CCoord noteh = black[note] ? (0.6 * keyh) : keyh;
return CRect(notex, 0.0, notex + notew, noteh).offset(dim.keyBounds.getTopLeft());
}
CRect SPiano::keyRect(unsigned key) const
{
return keyRect(getDimensions(false), key);
}
unsigned SPiano::keyAtPos(CPoint pos) const
{
const unsigned octs = octs_;
for (unsigned key = 0; key < octs * 12; ++key) {
if (black[key % 12]) {
if (keyRect(key).pointInside(pos))
return key;
}
}
for (unsigned key = 0; key < octs * 12; ++key) {
if (!black[key % 12]) {
if (keyRect(key).pointInside(pos))
return key;
}
}
return ~0u;
}
float SPiano::mousePressVelocity(unsigned key, CCoord posY)
{
const CRect rect = keyRect(key);
CCoord value = (posY - rect.top) / rect.getHeight();
return std::max(0.0f, std::min(1.0f, static_cast<float>(value)));
}

View file

@ -0,0 +1,72 @@
// 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
#pragma once
#include "utility/vstgui_before.h"
#include "vstgui/lib/cview.h"
#include "vstgui/lib/ccolor.h"
#include "utility/vstgui_after.h"
#include <functional>
#include <vector>
using namespace VSTGUI;
class SPiano : public CView {
public:
explicit SPiano(CRect bounds);
CFontRef getFont() const { return font_; }
void setFont(CFontRef font);
unsigned getNumOctaves() const { return octs_; }
void setNumOctaves(unsigned octs);
std::function<void(unsigned, float)> onKeyPressed;
std::function<void(unsigned, float)> onKeyReleased;
protected:
void draw(CDrawContext* dc) override;
CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp(CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved(CPoint& where, const CButtonState& buttons) override;
private:
struct Dimensions {
CRect bounds {};
CRect paddedBounds {};
CRect keyBounds {};
unsigned keyWidth {};
CRect labelBounds {};
};
const Dimensions& getDimensions(bool forceUpdate) const;
static CRect keyRect(const Dimensions& dim, unsigned key);
CRect keyRect(unsigned key) const;
unsigned keyAtPos(CPoint pos) const;
float mousePressVelocity(unsigned key, CCoord posY);
private:
unsigned octs_ {};
std::vector<unsigned> keyval_;
unsigned mousePressedKey_ = ~0u;
CCoord innerPaddingX_ = 4.0;
CCoord innerPaddingY_ = 4.0;
CCoord spacingY_ = 4.0;
CColor backgroundFill_ { 0xca, 0xca, 0xca, 0xff };
float backgroundRadius_ = 5.0;
CColor whiteFill_ { 0xee, 0xee, 0xec, 0xff };
CColor blackFill_ { 0x2e, 0x34, 0x36, 0xff };
CColor pressedFill_ { 0xa0, 0xa0, 0xa0, 0xff };
CColor outline_ { 0x00, 0x00, 0x00, 0xff };
CColor shadeOutline_ { 0x80, 0x80, 0x80, 0xff };
CColor labelStroke_ { 0x63, 0x63, 0x63, 0xff };
mutable Dimensions dim_;
SharedPointer<CFontDesc> font_;
};

View file

@ -0,0 +1,50 @@
// 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 "NativeHelpers.h"
#if defined(_WIN32)
#include "ghc/fs_std.hpp"
#include <windows.h>
#include <cstring>
bool openFileInExternalEditor(const char *filename)
{
std::wstring path = fs::u8path(filename).wstring();
SHELLEXECUTEINFOW info;
memset(&info, 0, sizeof(info));
info.cbSize = sizeof(info);
info.fMask = SEE_MASK_CLASSNAME;
info.lpVerb = L"open";
info.lpFile = path.c_str();
info.lpClass = L"txtfile";
info.nShow = SW_SHOW;
return ShellExecuteExW(&info);
}
#elif defined(__APPLE__)
// implemented in NativeHelpers.mm
#else
#include <gio/gio.h>
bool openFileInExternalEditor(const char *filename)
{
GAppInfo* appinfo = g_app_info_get_default_for_type("text/plain", FALSE);
if (!appinfo)
return 1;
GList* files = nullptr;
GFile* file = g_file_new_for_path(filename);
files = g_list_append(files, file);
gboolean success = g_app_info_launch(appinfo, files, nullptr, nullptr);
g_object_unref(file);
g_list_free(files);
g_object_unref(appinfo);
return success == TRUE;
}
#endif

View file

@ -0,0 +1,9 @@
// 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
#pragma once
bool openFileInExternalEditor(const char *filename);

View file

@ -0,0 +1,30 @@
// 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 "NativeHelpers.h"
#if defined(__APPLE__)
#import <AppKit/AppKit.h>
#import <CoreServices/CoreServices.h>
#import <Foundation/Foundation.h>
bool openFileInExternalEditor(const char *fileNameUTF8)
{
BOOL wasOpened = NO;
NSURL* applicationURL = (__bridge_transfer NSURL*)LSCopyDefaultApplicationURLForContentType(
kUTTypePlainText, kLSRolesEditor, nil);
if (!applicationURL)
return false;
if ([applicationURL isFileURL]) {
NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
NSString* fileName = [NSString stringWithUTF8String:fileNameUTF8];
wasOpened = [workspace openFile:fileName withApplication:[applicationURL path]];
}
return wasOpened == YES;
}
#endif

View file

@ -0,0 +1,155 @@
/* This file is generated by the layout maker tool. */
LogicalGroup* const view__0 = createLogicalGroup(CRect(0, 0, 800, 475), -1, "", kCenterText, 14);
mainView = view__0;
Background* const view__1 = createBackground(CRect(190, 110, 790, 390), -1, "", kCenterText, 14);
view__0->addView(view__1);
enterTheme(darkTheme);
LogicalGroup* const view__2 = createLogicalGroup(CRect(0, 0, 800, 110), -1, "", kCenterText, 14);
view__0->addView(view__2);
RoundedGroup* const view__3 = createRoundedGroup(CRect(5, 4, 180, 105), -1, "", kCenterText, 14);
view__2->addView(view__3);
SfizzMainButton* const view__4 = createSfizzMainButton(CRect(30, 5, 150, 65), kTagFirstChangePanel+kPanelGeneral, "", kCenterText, 14);
view__3->addView(view__4);
HomeButton* const view__5 = createHomeButton(CRect(44, 69, 69, 94), kTagFirstChangePanel+kPanelGeneral, "", kCenterText, 24);
view__3->addView(view__5);
CCButton* const view__6 = createCCButton(CRect(76, 69, 101, 94), kTagFirstChangePanel+kPanelControls, "", kCenterText, 24);
view__3->addView(view__6);
SettingsButton* const view__7 = createSettingsButton(CRect(107, 69, 132, 94), kTagFirstChangePanel+kPanelSettings, "", kCenterText, 24);
view__3->addView(view__7);
RoundedGroup* const view__8 = createRoundedGroup(CRect(185, 5, 565, 105), -1, "", kCenterText, 14);
view__2->addView(view__8);
HLine* const view__9 = createHLine(CRect(10, 36, 370, 41), -1, "", kCenterText, 14);
view__8->addView(view__9);
HLine* const view__10 = createHLine(CRect(10, 68, 370, 73), -1, "", kCenterText, 14);
view__8->addView(view__10);
ClickableLabel* const view__11 = createClickableLabel(CRect(10, 6, 260, 37), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20);
sfzFileLabel_ = view__11;
view__8->addView(view__11);
Label* const view__12 = createLabel(CRect(10, 39, 260, 69), -1, "Key switch:", kLeftText, 20);
view__8->addView(view__12);
Label* const view__13 = createLabel(CRect(10, 71, 70, 96), -1, "Voices:", kRightText, 12);
view__8->addView(view__13);
PreviousFileButton* const view__14 = createPreviousFileButton(CRect(295, 9, 320, 34), kTagPreviousSfzFile, "", kCenterText, 24);
view__8->addView(view__14);
NextFileButton* const view__15 = createNextFileButton(CRect(320, 9, 345, 34), kTagNextSfzFile, "", kCenterText, 24);
view__8->addView(view__15);
ChevronDropDown* const view__16 = createChevronDropDown(CRect(345, 9, 370, 34), kTagFileOperations, "", kCenterText, 24);
fileOperationsMenu_ = view__16;
view__8->addView(view__16);
Label* const view__17 = createLabel(CRect(75, 71, 125, 96), -1, "", kCenterText, 12);
infoVoicesLabel_ = view__17;
view__8->addView(view__17);
Label* const view__18 = createLabel(CRect(130, 71, 190, 96), -1, "Max:", kRightText, 12);
view__8->addView(view__18);
Label* const view__19 = createLabel(CRect(195, 71, 245, 96), -1, "", kCenterText, 12);
numVoicesLabel_ = view__19;
view__8->addView(view__19);
Label* const view__20 = createLabel(CRect(250, 71, 310, 96), -1, "Memory:", kRightText, 12);
view__8->addView(view__20);
Label* const view__21 = createLabel(CRect(315, 71, 365, 96), -1, "", kCenterText, 12);
memoryLabel_ = view__21;
view__8->addView(view__21);
RoundedGroup* const view__22 = createRoundedGroup(CRect(570, 5, 795, 105), -1, "", kCenterText, 14);
view__2->addView(view__22);
Knob48* const view__23 = createKnob48(CRect(45, 15, 93, 63), -1, "", kCenterText, 14);
view__22->addView(view__23);
view__23->setVisible(false);
ValueLabel* const view__24 = createValueLabel(CRect(40, 65, 100, 70), -1, "Center", kCenterText, 12);
view__22->addView(view__24);
view__24->setVisible(false);
StyledKnob* const view__25 = createStyledKnob(CRect(110, 15, 158, 63), kTagSetVolume, "", kCenterText, 14);
volumeSlider_ = view__25;
view__22->addView(view__25);
ValueLabel* const view__26 = createValueLabel(CRect(105, 65, 165, 87), -1, "0.0 dB", kCenterText, 12);
volumeLabel_ = view__26;
view__22->addView(view__26);
VMeter* const view__27 = createVMeter(CRect(175, 15, 210, 70), -1, "", kCenterText, 14);
view__22->addView(view__27);
enterTheme(defaultTheme);
LogicalGroup* const view__28 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14);
subPanels_[kPanelGeneral] = view__28;
view__0->addView(view__28);
RoundedGroup* const view__29 = createRoundedGroup(CRect(0, 0, 175, 280), -1, "", kCenterText, 14);
view__28->addView(view__29);
Label* const view__30 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14);
view__29->addView(view__30);
Label* const view__31 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14);
view__29->addView(view__31);
Label* const view__32 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14);
view__29->addView(view__32);
Label* const view__33 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14);
view__29->addView(view__33);
Label* const view__34 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14);
view__29->addView(view__34);
Label* const view__35 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14);
infoCurvesLabel_ = view__35;
view__29->addView(view__35);
Label* const view__36 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14);
infoMastersLabel_ = view__36;
view__29->addView(view__36);
Label* const view__37 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14);
infoGroupsLabel_ = view__37;
view__29->addView(view__37);
Label* const view__38 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14);
infoRegionsLabel_ = view__38;
view__29->addView(view__38);
Label* const view__39 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14);
infoSamplesLabel_ = view__39;
view__29->addView(view__39);
LogicalGroup* const view__40 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
subPanels_[kPanelControls] = view__40;
view__0->addView(view__40);
view__40->setVisible(false);
RoundedGroup* const view__41 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14);
view__40->addView(view__41);
Label* const view__42 = createLabel(CRect(0, 0, 790, 285), -1, "Controls not available", kCenterText, 40);
view__41->addView(view__42);
LogicalGroup* const view__43 = createLogicalGroup(CRect(5, 109, 795, 395), -1, "", kCenterText, 14);
subPanels_[kPanelSettings] = view__43;
view__0->addView(view__43);
view__43->setVisible(false);
TitleGroup* const view__44 = createTitleGroup(CRect(255, 26, 535, 126), -1, "Engine", kCenterText, 12);
view__43->addView(view__44);
ValueMenu* const view__45 = createValueMenu(CRect(25, 60, 85, 85), kTagSetNumVoices, "", kCenterText, 12);
numVoicesSlider_ = view__45;
view__44->addView(view__45);
ValueLabel* const view__46 = createValueLabel(CRect(15, 20, 95, 45), -1, "Polyphony", kCenterText, 12);
view__44->addView(view__46);
ValueMenu* const view__47 = createValueMenu(CRect(110, 60, 170, 85), kTagSetOversampling, "", kCenterText, 12);
oversamplingSlider_ = view__47;
view__44->addView(view__47);
ValueLabel* const view__48 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oversampling", kCenterText, 12);
view__44->addView(view__48);
ValueLabel* const view__49 = createValueLabel(CRect(185, 20, 265, 45), -1, "Preload size", kCenterText, 12);
view__44->addView(view__49);
ValueMenu* const view__50 = createValueMenu(CRect(195, 60, 255, 85), kTagSetPreloadSize, "", kCenterText, 12);
preloadSizeSlider_ = view__50;
view__44->addView(view__50);
TitleGroup* const view__51 = createTitleGroup(CRect(200, 161, 590, 261), -1, "Tuning", kCenterText, 12);
view__43->addView(view__51);
ValueLabel* const view__52 = createValueLabel(CRect(125, 20, 205, 45), -1, "Root key", kCenterText, 12);
view__51->addView(view__52);
ValueMenu* const view__53 = createValueMenu(CRect(220, 60, 280, 85), kTagSetTuningFrequency, "", kCenterText, 12);
tuningFrequencySlider_ = view__53;
view__51->addView(view__53);
ValueLabel* const view__54 = createValueLabel(CRect(210, 20, 290, 45), -1, "Frequency", kCenterText, 12);
view__51->addView(view__54);
StyledKnob* const view__55 = createStyledKnob(CRect(310, 45, 358, 93), kTagSetStretchedTuning, "", kCenterText, 14);
stretchedTuningSlider_ = view__55;
view__51->addView(view__55);
ValueLabel* const view__56 = createValueLabel(CRect(295, 20, 375, 45), -1, "Stretch", kCenterText, 12);
view__51->addView(view__56);
ValueLabel* const view__57 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12);
view__51->addView(view__57);
ValueButton* const view__58 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12);
scalaFileButton_ = view__58;
view__51->addView(view__58);
ValueMenu* const view__59 = createValueMenu(CRect(135, 60, 170, 85), kTagSetScalaRootKey, "", kCenterText, 12);
scalaRootKeySlider_ = view__59;
view__51->addView(view__59);
ValueMenu* const view__60 = createValueMenu(CRect(170, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12);
scalaRootOctaveSlider_ = view__60;
view__51->addView(view__60);
Piano* const view__61 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
piano_ = view__61;
view__0->addView(view__61);

View file

@ -0,0 +1,9 @@
// 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
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

View file

@ -0,0 +1,14 @@
// 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
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#pragma GCC diagnostic ignored "-Wdeprecated-copy"
#pragma GCC diagnostic ignored "-Wmultichar"
#pragma GCC diagnostic ignored "-Wextra"
#endif

View file

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,2 @@
This purpose of this tool is to accept UI designs make with Fluid, the FLTK
design editor, and convert these designs to music plugin interfaces.

View file

@ -0,0 +1,42 @@
#pragma once
#include <vector>
#include <string>
struct LayoutImage {
std::string filepath;
int x = 0;
int y = 0;
int w = 0;
int h = 0;
};
struct LayoutItem {
std::string id;
std::string classname;
std::string label;
int x = 0;
int y = 0;
int w = 0;
int h = 0;
std::string box;
std::string down_box;
int labelfont = 0;
int labelsize = 14;
std::string labeltype;
int textsize = 14;
int align = 0;
double value = 0;
double minimum = 0;
double maximum = 0;
double step = 0;
std::string type;
std::string callback;
LayoutImage image;
bool hidden = false;
std::string comment;
std::vector<LayoutItem> items;
};
struct Layout {
std::vector<LayoutItem> items;
};

View file

@ -0,0 +1,136 @@
#include "layout.h"
#include "reader.h"
#include <absl/strings/ascii.h>
#include <absl/strings/string_view.h>
#include <unordered_map>
#include <iostream>
///
typedef std::unordered_map<std::string, std::string> Metadata;
static Metadata metadata_from_comment(absl::string_view comment)
{
Metadata md;
while (!comment.empty()) {
absl::string_view line;
size_t pos = comment.find_first_of("\r\n");
if (pos != comment.npos) {
line = comment.substr(0, pos);
comment.remove_prefix(pos + 1);
}
else {
line = comment;
comment = {};
}
line = absl::StripAsciiWhitespace(line);
if (line.empty() || line[0] == '#')
continue;
std::string key, value;
pos = line.find_first_of('=');
if (pos != comment.npos) {
key = std::string(line.substr(0, pos));
value = std::string(line.substr(pos + 1));
}
else
key = std::string(line);
md.emplace(std::move(key), std::move(value));
}
return md;
}
///
static void codegen_item(int& idCounter, int parentId, int parentX, int parentY, const LayoutItem& item, absl::string_view oldTheme)
{
const Metadata md = metadata_from_comment(item.comment);
absl::string_view tag = "-1";
absl::string_view newTheme;
Metadata::const_iterator it;
it = md.find("tag");
if (it != md.end())
tag = it->second;
it = md.find("theme");
if (it != md.end())
newTheme = it->second;
absl::string_view currentTheme = newTheme.empty() ? oldTheme : newTheme;
int id = idCounter++;
int myX = item.x;
int myY = item.y;
if (parentId == -1) {
myX = 0;
myY = 0;
}
int relX = myX - parentX;
int relY = myY - parentY;
//std::cout << "// Begin " << id << " " << item.classname << " {" << item.label << "}" << "\n";
if (!newTheme.empty())
std::cout << "enterTheme(" << newTheme << ");\n";
absl::string_view label;
if (!item.label.empty() && item.labeltype != "NO_LABEL")
label = item.label;
absl::string_view align = "kCenterText";
if (item.align & 4)
align = "kLeftText";
else if (item.align & 8)
align = "kRightText";
std::cout << item.classname << "* const view__" << id << " = create" << item.classname << "(CRect(" << relX << ", " << relY << ", " << (relX + item.w) << ", " << (relY + item.h) << "), " << tag << ", \"" << label << "\", " << align << ", " << item.labelsize << ");\n";
if (!item.id.empty())
std::cout << item.id << " = view__" << id << ";\n";
if (parentId != -1)
std::cout << "view__" << parentId << "->addView(view__" << id << ");\n";
if (item.hidden)
std::cout << "view__" << id << "->setVisible(false);\n";
for (const LayoutItem& subItem : item.items)
codegen_item(idCounter, id, myX, myY, subItem, currentTheme);
if (!newTheme.empty())
std::cout << "enterTheme(" << oldTheme << ");\n";
//std::cout << "// End " << id << " " << item.classname << " {" << item.label << "}" << "\n";
}
static void codegen_layout(const LayoutItem& item)
{
int idCounter = 0;
codegen_item(idCounter, -1, 0, 0, item, "defaultTheme");
}
///
int main(int argc, char *argv[])
{
if (argc != 2) {
std::cerr << "Please indicate a fluid design file.\n";
return 1;
}
Layout layout = read_file_layout(argv[1]);
if (layout.items.size() != 1) {
std::cerr << "There must be exactly 1 top level component.";
return 1;
}
std::cout << "/* This file is generated by the layout maker tool. */\n";
codegen_layout(layout.items[0]);
return 0;
}

View file

@ -0,0 +1,298 @@
#include "reader.h"
#include <absl/strings/string_view.h>
#include <iostream>
#include <fstream>
typedef std::vector<std::string> TokenList;
static bool read_file_tokens(const char *filename, TokenList &tokens);
static Layout read_tokens_layout(TokenList::iterator &tok_it, TokenList::iterator tok_end);
Layout read_file_layout(const char *filename)
{
std::vector<std::string> tokens;
if (!read_file_tokens(filename, tokens))
throw std::runtime_error("Cannot read fluid design file.");
TokenList::iterator tok_it = tokens.begin();
TokenList::iterator tok_end = tokens.end();
return read_tokens_layout(tok_it, tok_end);
}
static std::string consume_next_token(TokenList::iterator &tok_it, TokenList::iterator tok_end)
{
if (tok_it == tok_end)
throw file_format_error("Premature end of tokens");
return *tok_it++;
}
static bool try_consume_next_token(const char *text, TokenList::iterator &tok_it, TokenList::iterator tok_end)
{
if (tok_it == tok_end)
return false;
if (*tok_it != text)
return false;
++tok_it;
return true;
}
static void ensure_next_token(const char *text, TokenList::iterator &tok_it, TokenList::iterator tok_end)
{
std::string tok = consume_next_token(tok_it, tok_end);
if (tok != text)
throw file_format_error("Unexpected token: " + tok);
}
static std::string consume_enclosed_string(TokenList::iterator &tok_it, TokenList::iterator tok_end)
{
ensure_next_token("{", tok_it, tok_end);
unsigned depth = 1;
std::string text;
for (;;) {
std::string part = consume_next_token(tok_it, tok_end);
if (part == "}") {
if (--depth == 0)
return text;
}
else if (part == "{")
++depth;
if (!text.empty())
text.push_back(' ');
text.append(part);
}
return text;
}
static std::string consume_any_string(TokenList::iterator &tok_it, TokenList::iterator tok_end)
{
if (tok_it != tok_end && *tok_it == "{")
return consume_enclosed_string(tok_it, tok_end);
else
return consume_next_token(tok_it, tok_end);
}
static int consume_int_token(TokenList::iterator &tok_it, TokenList::iterator tok_end)
{
std::string text = consume_next_token(tok_it, tok_end);
return std::stoi(text);
}
static int consume_real_token(TokenList::iterator &tok_it, TokenList::iterator tok_end)
{
std::string text = consume_next_token(tok_it, tok_end);
return std::stod(text);
}
static void consume_layout_item_properties(LayoutItem &item, TokenList::iterator &tok_it, TokenList::iterator tok_end)
{
ensure_next_token("{", tok_it, tok_end);
for (bool have = true; have;) {
if (try_consume_next_token("open", tok_it, tok_end))
; // skip
else if (try_consume_next_token("selected", tok_it, tok_end))
; // skip
else if (try_consume_next_token("label", tok_it, tok_end))
item.label = consume_any_string(tok_it, tok_end);
else if (try_consume_next_token("xywh", tok_it, tok_end)) {
ensure_next_token("{", tok_it, tok_end);
item.x = consume_int_token(tok_it, tok_end);
item.y = consume_int_token(tok_it, tok_end);
item.w = consume_int_token(tok_it, tok_end);
item.h = consume_int_token(tok_it, tok_end);
ensure_next_token("}", tok_it, tok_end);
}
else if (try_consume_next_token("box", tok_it, tok_end))
item.box = consume_next_token(tok_it, tok_end);
else if (try_consume_next_token("down_box", tok_it, tok_end))
item.down_box = consume_next_token(tok_it, tok_end);
else if (try_consume_next_token("labelfont", tok_it, tok_end))
item.labelfont = consume_int_token(tok_it, tok_end);
else if (try_consume_next_token("labelsize", tok_it, tok_end))
item.labelsize = consume_int_token(tok_it, tok_end);
else if (try_consume_next_token("labeltype", tok_it, tok_end))
item.labeltype = consume_any_string(tok_it, tok_end);
else if (try_consume_next_token("textsize", tok_it, tok_end))
item.textsize = consume_int_token(tok_it, tok_end);
else if (try_consume_next_token("align", tok_it, tok_end))
item.align = consume_int_token(tok_it, tok_end);
else if (try_consume_next_token("type", tok_it, tok_end))
item.type = consume_any_string(tok_it, tok_end);
else if (try_consume_next_token("callback", tok_it, tok_end))
item.callback = consume_any_string(tok_it, tok_end);
else if (try_consume_next_token("class", tok_it, tok_end))
item.classname = consume_any_string(tok_it, tok_end);
else if (try_consume_next_token("value", tok_it, tok_end))
item.value = consume_real_token(tok_it, tok_end);
else if (try_consume_next_token("minimum", tok_it, tok_end))
item.minimum = consume_real_token(tok_it, tok_end);
else if (try_consume_next_token("maximum", tok_it, tok_end))
item.maximum = consume_real_token(tok_it, tok_end);
else if (try_consume_next_token("step", tok_it, tok_end))
item.step = consume_real_token(tok_it, tok_end);
else if (try_consume_next_token("image", tok_it, tok_end))
item.image.filepath = consume_any_string(tok_it, tok_end);
else if (try_consume_next_token("hide", tok_it, tok_end))
item.hidden = true;
else if (try_consume_next_token("visible", tok_it, tok_end))
/* skip */;
else if (try_consume_next_token("comment", tok_it, tok_end))
item.comment = consume_any_string(tok_it, tok_end);
else
have = false;
}
ensure_next_token("}", tok_it, tok_end);
}
static LayoutItem consume_layout_item(const std::string &classname, TokenList::iterator &tok_it, TokenList::iterator tok_end, bool anonymous = false)
{
LayoutItem item;
item.classname = classname;
if (!anonymous)
item.id = consume_any_string(tok_it, tok_end);
consume_layout_item_properties(item, tok_it, tok_end);
if (tok_it != tok_end && *tok_it == "{") {
consume_next_token(tok_it, tok_end);
for (std::string text; (text = consume_next_token(tok_it, tok_end)) != "}";) {
if (text == "decl") {
consume_any_string(tok_it, tok_end);
consume_any_string(tok_it, tok_end);
}
else if (text == "Function") {
consume_any_string(tok_it, tok_end);
consume_any_string(tok_it, tok_end);
consume_any_string(tok_it, tok_end);
}
else
item.items.push_back(consume_layout_item(text, tok_it, tok_end));
}
}
return item;
}
static Layout read_tokens_layout(TokenList::iterator &tok_it, TokenList::iterator tok_end)
{
Layout layout;
std::string version_name;
std::string header_name;
std::string code_name;
while (tok_it != tok_end) {
std::string key = consume_next_token(tok_it, tok_end);
if (key == "version")
version_name = consume_next_token(tok_it, tok_end);
else if (key == "header_name") {
ensure_next_token("{", tok_it, tok_end);
header_name = consume_next_token(tok_it, tok_end);
ensure_next_token("}", tok_it, tok_end);
}
else if (key == "code_name") {
ensure_next_token("{", tok_it, tok_end);
code_name = consume_next_token(tok_it, tok_end);
ensure_next_token("}", tok_it, tok_end);
}
else if (key == "decl") {
consume_any_string(tok_it, tok_end);
consume_any_string(tok_it, tok_end);
}
else if (key == "widget_class") {
key = consume_next_token(tok_it, tok_end);
layout.items.push_back(consume_layout_item(key, tok_it, tok_end, true));
layout.items.back().id = key;
}
else
layout.items.push_back(consume_layout_item(key, tok_it, tok_end));
}
return layout;
}
///
class tokenizer {
public:
tokenizer(
absl::string_view text,
absl::string_view dropped_delims,
absl::string_view kept_delims);
absl::string_view next();
private:
absl::string_view text_;
absl::string_view dropped_delims_;
absl::string_view kept_delims_;
};
tokenizer::tokenizer(
absl::string_view text,
absl::string_view dropped_delims,
absl::string_view kept_delims)
: text_(text), dropped_delims_(dropped_delims), kept_delims_(kept_delims)
{
}
absl::string_view tokenizer::next()
{
auto is_dropped = [this](char c) -> bool {
return dropped_delims_.find(c) != dropped_delims_.npos;
};
auto is_kept = [this](char c) -> bool {
return kept_delims_.find(c) != kept_delims_.npos;
};
auto is_delim = [this](char c) -> bool {
return dropped_delims_.find(c) != dropped_delims_.npos ||
kept_delims_.find(c) != kept_delims_.npos;
};
absl::string_view text = text_;
while (!text.empty() && is_dropped(text[0]))
text.remove_prefix(1);
if (text.empty())
return {};
size_t pos;
{
auto it = std::find_if(text.begin(), text.end(), is_delim);
if (it == text.end())
pos = text.size();
else {
pos = std::distance(text.begin(), it);
pos += is_kept(text[0]);
}
}
absl::string_view token = text.substr(0, pos);
text_ = text.substr(pos);
return token;
}
///
static bool read_file_tokens(const char *filename, TokenList &tokens)
{
std::ifstream stream(filename);
std::string line;
std::string text;
while (std::getline(stream, line)) {
if (!line.empty() && line[0] != '#') {
text.append(line);
text.push_back('\n');
}
}
if (stream.bad())
return false;
tokenizer tok(text, " \t\r\n", "{}");
absl::string_view token;
while (!(token = tok.next()).empty())
tokens.emplace_back(token);
return !stream.bad();
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "layout.h"
#include <string>
#include <stdexcept>
Layout read_file_layout(const char *filename);
///
struct file_format_error : public std::runtime_error {
public:
explicit file_format_error(const std::string &reason = "Format error")
: runtime_error(reason) {}
};

23
external/jsl/LICENSE.md vendored Normal file
View file

@ -0,0 +1,23 @@
# Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

73
external/jsl/include/jsl/allocator vendored Normal file
View file

@ -0,0 +1,73 @@
// -*- C++ -*-
#pragma once
#include <limits>
#include <utility>
namespace jsl {
template <class T, class Traits>
struct ordinary_allocator {
typedef T value_type;
typedef T *pointer;
typedef const T *const_pointer;
typedef T &reference;
typedef const T &const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef std::true_type propagate_on_container_move_assignment;
template <class U> struct rebind { typedef ordinary_allocator<U, Traits> other; };
typedef std::true_type is_always_equal;
ordinary_allocator() noexcept {}
ordinary_allocator(const ordinary_allocator &) noexcept {}
template <class U> ordinary_allocator(const ordinary_allocator<U, Traits>&) noexcept {}
T *address(T &x) const noexcept;
const T *address(const T &x) const noexcept;
T *allocate(std::size_t n, const void * = nullptr);
void deallocate(T *p, std::size_t n) noexcept;
std::size_t max_size() const noexcept;
template <class U, class... Args> void construct(U *p, Args &&...args);
template <class U> void destroy(U *p);
};
template <class T, class Traits>
inline bool operator==(const ordinary_allocator<T, Traits> &, const ordinary_allocator<T, Traits> &) noexcept
{
return true;
}
template <class T, class Traits>
inline bool operator!=(const ordinary_allocator<T, Traits> &, const ordinary_allocator<T, Traits> &) noexcept
{
return false;
}
//------------------------------------------------------------------------------
struct stdc_allocator_traits {
static void *allocate(std::size_t n);
static void deallocate(void *p, std::size_t = 0);
};
template <class T>
using stdc_allocator = ordinary_allocator<T, stdc_allocator_traits>;
//------------------------------------------------------------------------------
template <std::size_t Al>
struct aligned_allocator_traits {
static void *allocate(std::size_t n);
static void deallocate(void *p, std::size_t = 0);
};
template <class T, std::size_t Al>
using aligned_allocator = ordinary_allocator<T, aligned_allocator_traits<Al>>;
} // namespace jsl
#include "bits/allocator/ordinary_allocator.tcc"
#include "bits/allocator/stdc_allocator.tcc"
#include "bits/allocator/aligned_allocator.tcc"

View file

@ -0,0 +1,41 @@
#include "../../allocator"
#include <new>
#if defined(_WIN32)
# include <malloc.h>
#else
# include <stdlib.h>
#endif
namespace jsl {
template <std::size_t Al>
void *aligned_allocator_traits<Al>::allocate(std::size_t n)
{
static_assert(Al % sizeof(void *) == 0,
"alignment must be a multiple of the pointer size");
static_assert((Al & (~Al + 1)) == Al,
"alignment must be a power of two");
#if defined(_WIN32)
void *p = ::_aligned_malloc(n, Al);
if (!p)
throw std::bad_alloc();
return p;
#else
void *p;
if (::posix_memalign(&p, Al, n) != 0)
throw std::bad_alloc();
return p;
#endif
}
template <std::size_t Al>
void aligned_allocator_traits<Al>::deallocate(void *p, std::size_t)
{
#if defined(_WIN32)
::_aligned_free(p);
#else
::free(p);
#endif
}
} // namespace jsl

View file

@ -0,0 +1,53 @@
#include "../../allocator"
#include <new>
namespace jsl {
template <class T, class Traits>
inline T *ordinary_allocator<T, Traits>::address(T &x) const noexcept
{
return &x;
}
template <class T, class Traits>
inline const T *ordinary_allocator<T, Traits>::address(const T &x) const noexcept
{
return &x;
}
template <class T, class Traits>
T *ordinary_allocator<T, Traits>::allocate(std::size_t n, const void *)
{
T *ptr = (T *)Traits::allocate(n * sizeof(T));
if (!ptr)
throw std::bad_alloc();
return ptr;
}
template <class T, class Traits>
void ordinary_allocator<T, Traits>::deallocate(T *p, std::size_t n) noexcept
{
Traits::deallocate(p, n * sizeof(T));
}
template <class T, class Traits>
std::size_t ordinary_allocator<T, Traits>::max_size() const noexcept
{
return std::numeric_limits<std::size_t>::max() / sizeof(T);
}
template <class T, class Traits>
template <class U, class... Args>
void ordinary_allocator<T, Traits>::construct(U *p, Args &&...args)
{
::new((void *)p) U(std::forward<Args>(args)...);
}
template <class T, class Traits>
template <class U>
void ordinary_allocator<T, Traits>::destroy(U *p)
{
p->~U();
}
} // namespace jsl

View file

@ -0,0 +1,16 @@
#include "../../allocator"
#include <stdlib.h>
namespace jsl {
inline void *stdc_allocator_traits::allocate(std::size_t n)
{
return ::malloc(n);
}
inline void stdc_allocator_traits::deallocate(void *p, std::size_t)
{
::free(p);
}
} // namespace jsl

View file

@ -11,6 +11,10 @@ set (LV2PLUGIN_TTL_SRC_FILES
manifest.ttl.in
${PROJECT_NAME}.ttl.in
)
if (SFIZZ_LV2_UI)
list(APPEND LV2PLUGIN_TTL_SRC_FILES
${PROJECT_NAME}_ui.ttl.in)
endif()
source_group("Turtle Files" FILES
${LV2PLUGIN_TTL_SRC_FILES}
)
@ -19,33 +23,63 @@ add_library (${LV2PLUGIN_PRJ_NAME} MODULE
atomic_compat.h
${LV2PLUGIN_TTL_SRC_FILES})
target_link_libraries (${LV2PLUGIN_PRJ_NAME} ${PROJECT_NAME}::${PROJECT_NAME})
if (SFIZZ_LV2_UI)
add_library (${LV2PLUGIN_PRJ_NAME}_ui MODULE
${PROJECT_NAME}_ui.cpp
vstgui_helpers.h
vstgui_helpers.cpp)
target_link_libraries (${LV2PLUGIN_PRJ_NAME}_ui sfizz_editor sfizz-vstgui)
endif()
# Explicitely strip all symbols on Linux but lv2_descriptor()
# MacOS linker does not support this apparently https://bugs.webkit.org/show_bug.cgi?id=144555
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
file(COPY lv2.version DESTINATION ${CMAKE_BINARY_DIR}/lv2)
target_link_libraries(${LV2PLUGIN_PRJ_NAME} "-Wl,--version-script=lv2.version")
# target_link_libraries(${LV2PLUGIN_PRJ_NAME} "-Wl,-u,lv2_descriptor")
if (SFIZZ_LV2_UI)
file(COPY lv2ui.version DESTINATION ${CMAKE_BINARY_DIR}/lv2)
target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui "-Wl,--version-script=lv2ui.version")
# target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui "-Wl,-u,lv2ui_descriptor")
endif()
endif()
target_include_directories(${LV2PLUGIN_PRJ_NAME} PRIVATE . external/ardour)
sfizz_enable_lto_if_needed (${LV2PLUGIN_PRJ_NAME})
if (MINGW)
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES LINK_FLAGS "-static")
endif()
if (SFIZZ_LV2_UI)
target_include_directories(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE . external/ardour)
sfizz_enable_lto_if_needed (${LV2PLUGIN_PRJ_NAME}_ui)
if (MINGW)
set_target_properties (${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES LINK_FLAGS "-static")
endif()
endif()
# 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}")
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Contents/Binary/$<0:>")
if (SFIZZ_LV2_UI)
set_target_properties (${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES PREFIX "")
set_target_properties (${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES OUTPUT_NAME "${PROJECT_NAME}_ui")
set_target_properties (${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Contents/Binary/$<0:>")
endif()
# 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)
if (SFIZZ_LV2_UI)
configure_file (${PROJECT_NAME}_ui.ttl.in ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_ui.ttl)
endif()
configure_file (LICENSE.md.in ${PROJECT_BINARY_DIR}/LICENSE.md)
if (SFIZZ_USE_VCPKG OR SFIZZ_STATIC_LIBSNDFILE OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
if (SFIZZ_USE_VCPKG OR SFIZZ_STATIC_DEPENDENCIES OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
file(COPY "lgpl-3.0.txt" DESTINATION ${PROJECT_BINARY_DIR})
endif()
@ -54,12 +88,21 @@ set(LV2_RESOURCES
DefaultInstrument.sfz
DefaultScale.scl)
execute_process(
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Resources")
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Contents/Resources")
foreach(res ${LV2_RESOURCES})
file (COPY "${CMAKE_CURRENT_SOURCE_DIR}/resources/${res}"
DESTINATION "${PROJECT_BINARY_DIR}/Resources")
DESTINATION "${PROJECT_BINARY_DIR}/Contents/Resources")
endforeach()
# Copy editor resources
if (SFIZZ_LV2_UI)
execute_process (
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Contents/Resources")
copy_editor_resources(
"${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources"
"${PROJECT_BINARY_DIR}/Contents/Resources")
endif()
# Installation
if (NOT MSVC)
install (DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR}

View file

@ -122,13 +122,13 @@ lv2_atom_sequence_next(const LV2_Atom_Event* i)
@endcode
*/
#define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \
for (LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(&(seq)->body); \
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); \
for (LV2_Atom_Event* iter = lv2_atom_sequence_begin(body); \
!lv2_atom_sequence_is_end(body, size, (iter)); \
(iter) = lv2_atom_sequence_next(iter))
@ -219,13 +219,13 @@ lv2_atom_tuple_next(const LV2_Atom* i)
@endcode
*/
#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \
for (LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \
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); \
for (LV2_Atom* iter = (LV2_Atom*)(body); \
!lv2_atom_tuple_is_end(body, size, (iter)); \
(iter) = lv2_atom_tuple_next(iter))
@ -275,13 +275,13 @@ lv2_atom_object_next(const LV2_Atom_Property_Body* i)
@endcode
*/
#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \
for (LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(&(obj)->body); \
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); \
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))

4
lv2/lv2ui.version Normal file
View file

@ -0,0 +1,4 @@
LV2UIABI_1.0 {
global: *lv2ui_descriptor*;
local: *;
};

View file

@ -1,7 +1,13 @@
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
<@LV2PLUGIN_URI@>
a lv2:Plugin ;
lv2:binary <@PROJECT_NAME@@CMAKE_SHARED_MODULE_SUFFIX@> ;
lv2:binary <Contents/Binary/@PROJECT_NAME@@CMAKE_SHARED_MODULE_SUFFIX@> ;
rdfs:seeAlso <@PROJECT_NAME@.ttl> .
@LV2PLUGIN_IF_ENABLE_UI@<@LV2PLUGIN_URI@#ui>
@LV2PLUGIN_IF_ENABLE_UI@ a ui:@LV2_UI_TYPE@ ;
@LV2PLUGIN_IF_ENABLE_UI@ ui:binary <Contents/Binary/@PROJECT_NAME@_ui@CMAKE_SHARED_MODULE_SUFFIX@> ;
@LV2PLUGIN_IF_ENABLE_UI@ rdfs:seeAlso <@PROJECT_NAME@_ui.ttl> .

View file

@ -32,6 +32,9 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "sfizz_lv2.h"
#include <lv2/atom/atom.h>
#include <lv2/atom/forge.h>
#include <lv2/atom/util.h>
#include <lv2/buf-size/buf-size.h>
@ -58,23 +61,11 @@
#include "atomic_compat.h"
#define SFIZZ_URI "http://sfztools.github.io/sfizz"
#define SFIZZ_PREFIX SFIZZ_URI "#"
#define SFIZZ__sfzFile "http://sfztools.github.io/sfizz:sfzfile"
#define SFIZZ__tuningfile "http://sfztools.github.io/sfizz:tuningfile"
#define SFIZZ__numVoices "http://sfztools.github.io/sfizz:numvoices"
#define SFIZZ__preloadSize "http://sfztools.github.io/sfizz:preload_size"
#define SFIZZ__oversampling "http://sfztools.github.io/sfizz:oversampling"
// These ones are just for the worker
#define SFIZZ__logStatus "http://sfztools.github.io/sfizz:log_status"
#define SFIZZ__checkModification "http://sfztools.github.io/sfizz:check_modification"
#define CHANNEL_MASK 0x0F
#define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK)
#define MIDI_STATUS(byte) (byte & ~CHANNEL_MASK)
#define PITCH_BUILD_AND_CENTER(first_byte, last_byte) (int)(((unsigned int)last_byte << 7) + (unsigned int)first_byte) - 8192
#define MAX_BLOCK_SIZE 8192
#define MAX_PATH_SIZE 1024
#define MAX_VOICES 256
#define DEFAULT_VOICES 64
#define DEFAULT_OVERSAMPLING SFIZZ_OVERSAMPLING_X1
@ -82,8 +73,8 @@
#define LOG_SAMPLE_COUNT 48000
#define UNUSED(x) (void)(x)
#define DEFAULT_SCALA_FILE "Resources/DefaultScale.scl"
#define DEFAULT_SFZ_FILE "Resources/DefaultInstrument.sfz"
#define DEFAULT_SCALA_FILE "Contents/Resources/DefaultScale.scl"
#define DEFAULT_SFZ_FILE "Contents/Resources/DefaultInstrument.sfz"
// This assumes that the longest path is the default sfz file; if not, change it
#define MAX_BUNDLE_PATH_SIZE (MAX_PATH_SIZE - sizeof(DEFAULT_SFZ_FILE))
@ -114,6 +105,12 @@ typedef struct
const float *scala_root_key_port;
const float *tuning_frequency_port;
const float *stretch_tuning_port;
float *active_voices_port;
float *num_curves_port;
float *num_masters_port;
float *num_groups_port;
float *num_regions_port;
float *num_samples_port;
// Atom forge
LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread
@ -129,8 +126,11 @@ typedef struct
LV2_URID nominal_block_length_uri;
LV2_URID sample_rate_uri;
LV2_URID atom_object_uri;
LV2_URID atom_blank_uri;
LV2_URID atom_float_uri;
LV2_URID atom_double_uri;
LV2_URID atom_int_uri;
LV2_URID atom_long_uri;
LV2_URID atom_urid_uri;
LV2_URID atom_path_uri;
LV2_URID patch_set_uri;
@ -147,7 +147,14 @@ typedef struct
LV2_URID sfizz_oversampling_uri;
LV2_URID sfizz_log_status_uri;
LV2_URID sfizz_check_modification_uri;
LV2_URID sfizz_active_voices_uri;
LV2_URID time_position_uri;
LV2_URID time_bar_uri;
LV2_URID time_bar_beat_uri;
LV2_URID time_beat_unit_uri;
LV2_URID time_beats_per_bar_uri;
LV2_URID time_beats_per_minute_uri;
LV2_URID time_speed_uri;
// Sfizz related data
sfizz_synth_t *synth;
@ -164,24 +171,24 @@ typedef struct
float sample_rate;
atomic_int must_update_midnam;
// Timing data
int bar;
float bar_beat;
int beats_per_bar;
int beat_unit;
float bpm_tempo;
float speed;
// Paths
char bundle_path[MAX_BUNDLE_PATH_SIZE];
} sfizz_plugin_t;
enum
{
SFIZZ_CONTROL = 0,
SFIZZ_NOTIFY = 1,
SFIZZ_LEFT = 2,
SFIZZ_RIGHT = 3,
SFIZZ_VOLUME = 4,
SFIZZ_POLYPHONY = 5,
SFIZZ_OVERSAMPLING = 6,
SFIZZ_PRELOAD = 7,
SFIZZ_FREEWHEELING = 8,
SFIZZ_SCALA_ROOT_KEY = 9,
SFIZZ_TUNING_FREQUENCY = 10,
SFIZZ_STRETCH_TUNING = 11,
SFIZZ_TIMEINFO_POSITION = 1 << 0,
SFIZZ_TIMEINFO_SIGNATURE = 1 << 1,
SFIZZ_TIMEINFO_TEMPO = 1 << 2,
SFIZZ_TIMEINFO_SPEED = 1 << 3,
};
static void
@ -207,10 +214,13 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
self->nominal_block_length_uri = map->map(map->handle, LV2_BUF_SIZE__nominalBlockLength);
self->sample_rate_uri = map->map(map->handle, LV2_PARAMETERS__sampleRate);
self->atom_float_uri = map->map(map->handle, LV2_ATOM__Float);
self->atom_double_uri = map->map(map->handle, LV2_ATOM__Double);
self->atom_int_uri = map->map(map->handle, LV2_ATOM__Int);
self->atom_long_uri = map->map(map->handle, LV2_ATOM__Long);
self->atom_path_uri = map->map(map->handle, LV2_ATOM__Path);
self->atom_urid_uri = map->map(map->handle, LV2_ATOM__URID);
self->atom_object_uri = map->map(map->handle, LV2_ATOM__Object);
self->atom_blank_uri = map->map(map->handle, LV2_ATOM__Blank);
self->patch_set_uri = map->map(map->handle, LV2_PATCH__Set);
self->patch_get_uri = map->map(map->handle, LV2_PATCH__Get);
self->patch_put_uri = map->map(map->handle, LV2_PATCH__Put);
@ -224,8 +234,71 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize);
self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling);
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification);
self->time_position_uri = map->map(map->handle, LV2_TIME__Position);
self->time_bar_uri = map->map(map->handle, LV2_TIME__bar);
self->time_bar_beat_uri = map->map(map->handle, LV2_TIME__barBeat);
self->time_beat_unit_uri = map->map(map->handle, LV2_TIME__beatUnit);
self->time_beats_per_bar_uri = map->map(map->handle, LV2_TIME__beatsPerBar);
self->time_beats_per_minute_uri = map->map(map->handle, LV2_TIME__beatsPerMinute);
self->time_speed_uri = map->map(map->handle, LV2_TIME__speed);
}
static bool
sfizz_atom_extract_real(sfizz_plugin_t *self, const LV2_Atom *atom, double *real)
{
if (!atom)
return false;
const LV2_URID type = atom->type;
if (type == self->atom_int_uri && atom->size >= sizeof(int32_t)) {
*real = ((const LV2_Atom_Int *)atom)->body;
return true;
}
if (type == self->atom_long_uri && atom->size >= sizeof(int64_t)) {
*real = ((const LV2_Atom_Long *)atom)->body;
return true;
}
if (type == self->atom_float_uri && atom->size >= sizeof(float)) {
*real = ((const LV2_Atom_Float *)atom)->body;
return true;
}
if (type == self->atom_double_uri && atom->size >= sizeof(double)) {
*real = ((const LV2_Atom_Double *)atom)->body;
return true;
}
return false;
}
static bool
sfizz_atom_extract_integer(sfizz_plugin_t *self, const LV2_Atom *atom, int64_t *integer)
{
if (!atom)
return false;
const LV2_URID type = atom->type;
if (type == self->atom_int_uri && atom->size >= sizeof(int32_t)) {
*integer = ((const LV2_Atom_Int *)atom)->body;
return true;
}
if (type == self->atom_long_uri && atom->size >= sizeof(int64_t)) {
*integer = ((const LV2_Atom_Long *)atom)->body;
return true;
}
if (type == self->atom_float_uri && atom->size >= sizeof(float)) {
*integer = (int64_t)((const LV2_Atom_Float *)atom)->body;
return true;
}
if (type == self->atom_double_uri && atom->size >= sizeof(double)) {
*integer = (int64_t)((const LV2_Atom_Double *)atom)->body;
return true;
}
return false;
}
static void
@ -272,6 +345,24 @@ connect_port(LV2_Handle instance,
case SFIZZ_STRETCH_TUNING:
self->stretch_tuning_port = (const float *)data;
break;
case SFIZZ_ACTIVE_VOICES:
self->active_voices_port = (float *)data;
break;
case SFIZZ_NUM_CURVES:
self->num_curves_port = (float *)data;
break;
case SFIZZ_NUM_MASTERS:
self->num_masters_port = (float *)data;
break;
case SFIZZ_NUM_GROUPS:
self->num_groups_port = (float *)data;
break;
case SFIZZ_NUM_REGIONS:
self->num_regions_port = (float *)data;
break;
case SFIZZ_NUM_SAMPLES:
self->num_samples_port = (float *)data;
break;
default:
break;
}
@ -319,6 +410,19 @@ sfizz_lv2_get_default_scala_path(LV2_Handle instance, char *path, size_t size)
snprintf(path, size, "%s/%s", self->bundle_path, DEFAULT_SCALA_FILE);
}
static void
sfizz_lv2_update_timeinfo(sfizz_plugin_t *self, int delay, int updates)
{
if (updates & SFIZZ_TIMEINFO_POSITION)
sfizz_send_time_position(self->synth, delay, self->bar, self->bar_beat);
if (updates & SFIZZ_TIMEINFO_SIGNATURE)
sfizz_send_time_signature(self->synth, delay, self->beats_per_bar, self->beat_unit);
if (updates & SFIZZ_TIMEINFO_TEMPO)
sfizz_send_tempo(self->synth, delay, 60.0f / self->bpm_tempo);
if (updates & SFIZZ_TIMEINFO_SPEED)
sfizz_send_playback_state(self->synth, delay, self->speed > 0);
}
static LV2_Handle
instantiate(const LV2_Descriptor *descriptor,
double rate,
@ -354,6 +458,14 @@ instantiate(const LV2_Descriptor *descriptor,
self->check_modification = false;
self->sample_counter = 0;
// Initial timing
self->bar = 0;
self->bar_beat = 0;
self->beats_per_bar = 4;
self->beat_unit = 4;
self->bpm_tempo = 120;
self->speed = 1;
// Get the features from the host and populate the structure
for (const LV2_Feature *const *f = features; *f; f++)
{
@ -465,6 +577,8 @@ instantiate(const LV2_Descriptor *descriptor,
sfizz_load_file(self->synth, self->sfz_file_path);
sfizz_load_scala_file(self->synth, self->scala_file_path);
sfizz_lv2_update_timeinfo(self, 0, ~0);
return (LV2_Handle)self;
}
@ -509,7 +623,6 @@ sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_URID urid, const char *path)
lv2_atom_forge_pop(&self->forge, &frame);
}
static void
sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, const LV2_Atom_Object *obj)
{
@ -745,8 +858,10 @@ run(LV2_Handle instance, uint32_t sample_count)
LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev)
{
const int delay = (int)ev->time.frames;
// If the received atom is an object/patch message
if (ev->body.type == self->atom_object_uri)
if (ev->body.type == self->atom_object_uri || ev->body.type == self->atom_blank_uri)
{
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
if (obj->body.otype == self->patch_set_uri)
@ -773,7 +888,60 @@ run(LV2_Handle instance, uint32_t sample_count)
}
else if (obj->body.otype == self->time_position_uri)
{
// TODO: Handle time position atom
const LV2_Atom *bar_atom = NULL;
const LV2_Atom *bar_beat_atom = NULL;
const LV2_Atom *beat_unit_atom = NULL;
const LV2_Atom *beats_per_bar_atom = NULL;
const LV2_Atom *beats_per_minute_atom = NULL;
const LV2_Atom *speed_atom = NULL;
lv2_atom_object_get(
obj,
self->time_bar_uri, &bar_atom,
self->time_bar_beat_uri, &bar_beat_atom,
self->time_beats_per_bar_uri, &beats_per_bar_atom,
self->time_beats_per_minute_uri, &beats_per_minute_atom,
self->time_beat_unit_uri, &beat_unit_atom,
self->time_speed_uri, &speed_atom,
0);
int updates = 0;
int64_t bar;
double bar_beat;
if (sfizz_atom_extract_integer(self, bar_atom, &bar)) {
self->bar = (int)bar;
updates |= SFIZZ_TIMEINFO_POSITION;
}
if (sfizz_atom_extract_real(self, bar_beat_atom, &bar_beat)) {
self->bar_beat = (float)bar_beat;
updates |= SFIZZ_TIMEINFO_POSITION;
}
double beats_per_bar;
int64_t beat_unit;
if (sfizz_atom_extract_real(self, beats_per_bar_atom, &beats_per_bar)) {
self->beats_per_bar = (int)beats_per_bar;
updates |= SFIZZ_TIMEINFO_SIGNATURE;
}
if (sfizz_atom_extract_integer(self, beat_unit_atom, &beat_unit)) {
self->beat_unit = (int)beat_unit;
updates |= SFIZZ_TIMEINFO_SIGNATURE;
}
double tempo;
if (sfizz_atom_extract_real(self, beats_per_minute_atom, &tempo)) {
self->bpm_tempo = (float)tempo;
updates |= SFIZZ_TIMEINFO_TEMPO;
}
double speed;
if (sfizz_atom_extract_real(self, speed_atom, &speed)) {
self->speed = (float)speed;
updates |= SFIZZ_TIMEINFO_SPEED;
}
sfizz_lv2_update_timeinfo(self, delay, updates);
}
else
{
@ -802,6 +970,12 @@ run(LV2_Handle instance, uint32_t sample_count)
sfizz_lv2_check_preload_size(self);
sfizz_lv2_check_oversampling(self);
sfizz_lv2_check_num_voices(self);
*(self->active_voices_port) = sfizz_get_num_active_voices(self->synth);
*(self->num_curves_port) = sfizz_get_num_curves(self->synth);
*(self->num_masters_port) = sfizz_get_num_masters(self->synth);
*(self->num_groups_port) = sfizz_get_num_groups(self->synth);
*(self->num_regions_port) = sfizz_get_num_regions(self->synth);
*(self->num_samples_port) = sfizz_get_num_preloaded_samples(self->synth);
// Log the buffer usage
self->sample_counter += (int)sample_count;

View file

@ -13,6 +13,7 @@
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix time: <http://lv2plug.in/ns/ext/time#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix work: <http://lv2plug.in/ns/ext/worker#> .
@ -35,6 +36,12 @@ midnam:update a lv2:Feature .
"Accordage"@fr ,
"Accordatura"@it .
<@LV2PLUGIN_URI@#status>
a pg:Group ;
lv2:symbol "status" ;
lv2:name "status",
"Statut"@fr .
<@LV2PLUGIN_URI@:sfzfile>
a lv2:Parameter ;
rdfs:label "SFZ file",
@ -77,8 +84,10 @@ midnam:update a lv2:Feature .
opts:supportedOption param:sampleRate ;
opts:supportedOption bufsize:maxBlockLength, bufsize:nominalBlockLength ;
patch:writable <@LV2PLUGIN_URI@:sfzfile> ;
patch:writable <@LV2PLUGIN_URI@:tuningfile> ;
@LV2PLUGIN_IF_ENABLE_UI@ui:ui <@LV2PLUGIN_URI@#ui> ;
patch:writable <@LV2PLUGIN_URI@:sfzfile> ,
<@LV2PLUGIN_URI@:tuningfile> ;
lv2:port [
a lv2:InputPort, atom:AtomPort ;
@ -87,7 +96,8 @@ midnam:update a lv2:Feature .
lv2:designation lv2:control ;
lv2:index 0 ;
lv2:symbol "control" ;
lv2:name "Control"
lv2:name "Control",
"Contrôle"@fr ;
] , [
a lv2:OutputPort, atom:AtomPort ;
atom:bufferType atom:Sequence ;
@ -95,7 +105,8 @@ midnam:update a lv2:Feature .
lv2:designation lv2:control ;
lv2:index 1 ;
lv2:symbol "notify" ;
lv2:name "Notify" ;
lv2:name "Notify",
"Notification"@fr ;
] , [
a lv2:AudioPort, lv2:OutputPort ;
lv2:index 2 ;
@ -291,4 +302,70 @@ midnam:update a lv2:Feature .
lv2:minimum 0.0 ;
lv2:maximum 1.0 ;
units:unit units:coef
] , [
a lv2:OutputPort, lv2:ControlPort ;
lv2:index 12 ;
lv2:symbol "active_voices" ;
lv2:name "Active voices",
"Voix utilisées"@fr ;
pg:group <@LV2PLUGIN_URI@#status> ;
lv2:portProperty lv2:integer ;
lv2:default 0 ;
lv2:minimum 0 ;
lv2:maximum 256 ;
] , [
a lv2:OutputPort, lv2:ControlPort ;
lv2:index 13 ;
lv2:symbol "num_curves" ;
lv2:name "Number of curves",
"Nombre de courbes"@fr ;
pg:group <@LV2PLUGIN_URI@#status> ;
lv2:portProperty lv2:integer ;
lv2:default 0 ;
lv2:minimum 0 ;
lv2:maximum 65535 ;
] , [
a lv2:OutputPort, lv2:ControlPort ;
lv2:index 14 ;
lv2:symbol "num_masters" ;
lv2:name "Number of masters",
"Nombre de maîtres"@fr ;
pg:group <@LV2PLUGIN_URI@#status> ;
lv2:portProperty lv2:integer ;
lv2:default 0 ;
lv2:minimum 0 ;
lv2:maximum 65535 ;
] , [
a lv2:OutputPort, lv2:ControlPort ;
lv2:index 15 ;
lv2:symbol "num_groups" ;
lv2:name "Number of groups",
"Nombre de groupes"@fr ;
pg:group <@LV2PLUGIN_URI@#status> ;
lv2:portProperty lv2:integer ;
lv2:default 0 ;
lv2:minimum 0 ;
lv2:maximum 65535 ;
] , [
a lv2:OutputPort, lv2:ControlPort ;
lv2:index 16 ;
lv2:symbol "num_regions" ;
lv2:name "Number of regions",
"Nombre de régions"@fr ;
pg:group <@LV2PLUGIN_URI@#status> ;
lv2:portProperty lv2:integer ;
lv2:default 0 ;
lv2:minimum 0 ;
lv2:maximum 65535 ;
] , [
a lv2:OutputPort, lv2:ControlPort ;
lv2:index 17 ;
lv2:symbol "num_samples" ;
lv2:name "Number of samples",
"Nombre d'échantillons"@fr ;
pg:group <@LV2PLUGIN_URI@#status> ;
lv2:portProperty lv2:integer ;
lv2:default 0 ;
lv2:minimum 0 ;
lv2:maximum 65535 ;
] .

43
lv2/sfizz_lv2.h Normal file
View file

@ -0,0 +1,43 @@
// 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
#pragma once
#define MAX_PATH_SIZE 1024
#define SFIZZ_URI "http://sfztools.github.io/sfizz"
#define SFIZZ_UI_URI "http://sfztools.github.io/sfizz#ui"
#define SFIZZ_PREFIX SFIZZ_URI "#"
#define SFIZZ__sfzFile SFIZZ_URI ":" "sfzfile"
#define SFIZZ__tuningfile SFIZZ_URI ":" "tuningfile"
#define SFIZZ__numVoices SFIZZ_URI ":" "numvoices"
#define SFIZZ__preloadSize SFIZZ_URI ":" "preload_size"
#define SFIZZ__oversampling SFIZZ_URI ":" "oversampling"
// These ones are just for the worker
#define SFIZZ__logStatus SFIZZ_URI ":" "log_status"
#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification"
enum
{
SFIZZ_CONTROL = 0,
SFIZZ_NOTIFY = 1,
SFIZZ_LEFT = 2,
SFIZZ_RIGHT = 3,
SFIZZ_VOLUME = 4,
SFIZZ_POLYPHONY = 5,
SFIZZ_OVERSAMPLING = 6,
SFIZZ_PRELOAD = 7,
SFIZZ_FREEWHEELING = 8,
SFIZZ_SCALA_ROOT_KEY = 9,
SFIZZ_TUNING_FREQUENCY = 10,
SFIZZ_STRETCH_TUNING = 11,
SFIZZ_ACTIVE_VOICES = 12,
SFIZZ_NUM_CURVES = 13,
SFIZZ_NUM_MASTERS = 14,
SFIZZ_NUM_GROUPS = 15,
SFIZZ_NUM_REGIONS = 16,
SFIZZ_NUM_SAMPLES = 17,
};

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