Merge branch 'develop'
This commit is contained in:
commit
1a3b5a9b2d
215 changed files with 9086 additions and 3317 deletions
|
|
@ -42,9 +42,9 @@ for:
|
|||
- ${APPVEYOR_BUILD_FOLDER}/scripts/appveyor/before_build.sh
|
||||
build_script:
|
||||
- cd build
|
||||
- make -j2 sfizz_tests
|
||||
- cmake --build . -j2 --target sfizz_tests
|
||||
- tests/sfizz_tests
|
||||
- make -j2
|
||||
- cmake --build . -j2
|
||||
after_build:
|
||||
- chmod +x ${APPVEYOR_BUILD_FOLDER}/scripts/appveyor/after_build.sh
|
||||
- ${APPVEYOR_BUILD_FOLDER}/scripts/appveyor/after_build.sh
|
||||
|
|
|
|||
183
.github/workflows/build.yml
vendored
183
.github/workflows/build.yml
vendored
|
|
@ -44,6 +44,7 @@ jobs:
|
|||
run: |
|
||||
sudo apt-get update && \
|
||||
sudo apt-get install \
|
||||
ninja-build \
|
||||
libjack-jackd2-dev \
|
||||
libsndfile1-dev \
|
||||
libcairo2-dev \
|
||||
|
|
@ -64,10 +65,12 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
cmake "$GITHUB_WORKSPACE" -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
|
||||
cmake "$GITHUB_WORKSPACE" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
|
||||
-DSFIZZ_JACK=ON \
|
||||
-DSFIZZ_VST=ON \
|
||||
-DSFIZZ_LV2_UI=ON \
|
||||
-DSFIZZ_PUREDATA=ON \
|
||||
-DSFIZZ_TESTS=ON \
|
||||
-DSFIZZ_SHARED=OFF \
|
||||
-DSFIZZ_STATIC_DEPENDENCIES=OFF \
|
||||
|
|
@ -137,15 +140,24 @@ jobs:
|
|||
name: MOD devices tarball
|
||||
path: ${{runner.workspace}}/build/${{env.install_name}}.tar.gz
|
||||
|
||||
build_for_win32:
|
||||
build_for_windows:
|
||||
runs-on: windows-2019
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: x86
|
||||
pkg_platform: Win32
|
||||
release_arch: Win32
|
||||
bits: 32
|
||||
- platform: x64
|
||||
pkg_platform: Win64
|
||||
release_arch: x64
|
||||
bits: 64
|
||||
steps:
|
||||
- name: Set install name
|
||||
run: |
|
||||
echo platform=x86 >> "${Env:GITHUB_ENV}"
|
||||
echo release_arch=Win32 >> "${Env:GITHUB_ENV}"
|
||||
echo "install_ref=$(${Env:GITHUB_REF}.split('/')[-1])" >> "${Env:GITHUB_ENV}"
|
||||
echo "install_name=sfizz-$(${Env:GITHUB_REF}.split('/')[-1])-win32" >> "${Env:GITHUB_ENV}"
|
||||
echo "install_name=sfizz-$(${Env:GITHUB_REF}.split('/')[-1])-win${{matrix.bits}}" >> "${Env:GITHUB_ENV}"
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
|
@ -155,42 +167,7 @@ jobs:
|
|||
- name: Configure CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON
|
||||
- name: Build tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 --target sfizz_tests
|
||||
- name: Test
|
||||
run: ${{runner.workspace}}/build/tests/Release/sfizz_tests
|
||||
- name: Build all
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2
|
||||
- name: Create installer
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: iscc /O"." /F"${Env:install_name}" /dARCH="${Env:platform}" innosetup.iss
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Win32 installer
|
||||
path: ${{runner.workspace}}/build/${{env.install_name}}.exe
|
||||
|
||||
build_for_win64:
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Set install name
|
||||
run: |
|
||||
echo platform=x64 >> "${Env:GITHUB_ENV}"
|
||||
echo release_arch=x64 >> "${Env:GITHUB_ENV}"
|
||||
echo "install_ref=$(${Env:GITHUB_REF}.split('/')[-1])" >> "${Env:GITHUB_ENV}"
|
||||
echo "install_name=sfizz-$(${Env:GITHUB_REF}.split('/')[-1])-win64" >> "${Env:GITHUB_ENV}"
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Create Build Environment
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: cmake -E make_directory build
|
||||
- name: Configure CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON
|
||||
cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${{matrix.release_arch}}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON -DSFIZZ_PUREDATA=ON
|
||||
- name: Build tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 --target sfizz_tests
|
||||
|
|
@ -200,31 +177,42 @@ jobs:
|
|||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2
|
||||
- name: Install pluginval
|
||||
if: ${{ matrix.platform == 'x64' }}
|
||||
run: |
|
||||
Invoke-WebRequest https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_Windows.zip -OutFile pluginval.zip
|
||||
Expand-Archive pluginval.zip -DestinationPath pluginval
|
||||
echo "$(Get-Location)\pluginval" | Out-File -FilePath ${Env:GITHUB_PATH} -Encoding utf8 -Append
|
||||
pluginval\pluginval --version
|
||||
- name: Validate VST3
|
||||
if: ${{ matrix.platform == 'x64' }}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: pluginval --validate-in-process --validate sfizz.vst3
|
||||
- name: Create installer
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: iscc /O"." /F"${Env:install_name}" /dARCH="${Env:platform}" innosetup.iss
|
||||
run: iscc /O"." /F"${Env:install_name}" /dARCH="${{matrix.platform}}" innosetup.iss
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Win64 installer
|
||||
name: ${{matrix.pkg_platform}} installer
|
||||
path: ${{runner.workspace}}/build/${{env.install_name}}.exe
|
||||
|
||||
build_for_mingw32:
|
||||
build_for_mingw:
|
||||
runs-on: ubuntu-18.04
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: i686
|
||||
pkg_platform: Win32
|
||||
bits: 32
|
||||
- platform: x86_64
|
||||
pkg_platform: Win64
|
||||
bits: 64
|
||||
container:
|
||||
image: archlinux
|
||||
steps:
|
||||
- name: Set install name
|
||||
run: |
|
||||
echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
|
||||
echo "install_name=sfizz-${GITHUB_REF##*/}-mingw32" >> "$GITHUB_ENV"
|
||||
echo "install_name=sfizz-${GITHUB_REF##*/}-mingw${{matrix.bits}}" >> "$GITHUB_ENV"
|
||||
- name: Configure pacman repositories
|
||||
shell: bash
|
||||
run: |
|
||||
|
|
@ -239,7 +227,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
pacman -Sqyu --noconfirm
|
||||
pacman -Sq --needed --noconfirm base-devel git wget mingw-w64-cmake mingw-w64-gcc mingw-w64-pkg-config mingw-w64-libsndfile
|
||||
pacman -Sq --needed --noconfirm base-devel git wget ninja mingw-w64-cmake mingw-w64-gcc mingw-w64-pkg-config mingw-w64-libsndfile
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
|
@ -247,14 +235,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
cp -vf "$GITHUB_WORKSPACE"/scripts/mingw_dwrite_3.h \
|
||||
/usr/i686-w64-mingw32/include/dwrite_3.h
|
||||
- name: Fix VST sources
|
||||
shell: bash
|
||||
# need to convert some includes to lower case (as of VST 3.7.1)
|
||||
run: |
|
||||
find "$GITHUB_WORKSPACE"/plugins/vst/external/VST_SDK -type d -name source -exec \
|
||||
find {} -type f -name '*.[hc]' -o -name '*.[hc]pp' -print0 \; | \
|
||||
xargs -0 sed -i 's/<Windows.h>/<windows.h>/'
|
||||
/usr/${{matrix.platform}}-w64-mingw32/include/dwrite_3.h
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
|
@ -263,95 +244,27 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
i686-w64-mingw32-cmake "$GITHUB_WORKSPACE" \
|
||||
${{matrix.platform}}-w64-mingw32-cmake "$GITHUB_WORKSPACE" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
|
||||
-DENABLE_LTO=OFF \
|
||||
-DSFIZZ_JACK=OFF \
|
||||
-DSFIZZ_VST=ON \
|
||||
-DSFIZZ_PUREDATA=ON \
|
||||
-DSFIZZ_STATIC_DEPENDENCIES=ON \
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
- name: Build
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: i686-w64-mingw32-cmake --build . --config "$BUILD_TYPE" -j 2
|
||||
run: ${{matrix.platform}}-w64-mingw32-cmake --build . --config "$BUILD_TYPE" -j 2
|
||||
- name: Install
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: |
|
||||
DESTDIR="$(pwd)/$install_name" i686-w64-mingw32-cmake --build . --config "$BUILD_TYPE" --target install
|
||||
DESTDIR="$(pwd)/$install_name" ${{matrix.platform}}-w64-mingw32-cmake --build . --config "$BUILD_TYPE" --target install
|
||||
tar czvf "$install_name".tar.gz "$install_name"
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Win32 MinGW tarball
|
||||
path: ${{runner.workspace}}/build/${{env.install_name}}.tar.gz
|
||||
|
||||
build_for_mingw64:
|
||||
runs-on: ubuntu-18.04
|
||||
container:
|
||||
image: archlinux
|
||||
steps:
|
||||
- name: Set install name
|
||||
run: |
|
||||
echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
|
||||
echo "install_name=sfizz-${GITHUB_REF##*/}-mingw64" >> "$GITHUB_ENV"
|
||||
- name: Configure pacman repositories
|
||||
shell: bash
|
||||
run: |
|
||||
cat >>/etc/pacman.conf <<EOF
|
||||
[multilib]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
[mingw-w64]
|
||||
SigLevel = Optional TrustAll
|
||||
Server = https://github.com/jpcima/arch-mingw-w64/releases/download/repo.\$arch/
|
||||
EOF
|
||||
- name: Set up dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
pacman -Sqyu --noconfirm
|
||||
pacman -Sq --needed --noconfirm base-devel git wget mingw-w64-cmake mingw-w64-gcc mingw-w64-pkg-config mingw-w64-libsndfile
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Fix MinGW headers
|
||||
shell: bash
|
||||
run: |
|
||||
cp -vf "$GITHUB_WORKSPACE"/scripts/mingw_dwrite_3.h \
|
||||
/usr/x86_64-w64-mingw32/include/dwrite_3.h
|
||||
- name: Fix VST sources
|
||||
shell: bash
|
||||
# need to convert some includes to lower case (as of VST 3.7.1)
|
||||
run: |
|
||||
find "$GITHUB_WORKSPACE"/plugins/vst/external/VST_SDK -type d -name source -exec \
|
||||
find {} -type f -name '*.[hc]' -o -name '*.[hc]pp' -print0 \; | \
|
||||
xargs -0 sed -i 's/<Windows.h>/<windows.h>/'
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: cmake -E make_directory build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
x86_64-w64-mingw32-cmake "$GITHUB_WORKSPACE" \
|
||||
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
|
||||
-DENABLE_LTO=OFF \
|
||||
-DSFIZZ_JACK=OFF \
|
||||
-DSFIZZ_VST=ON \
|
||||
-DSFIZZ_STATIC_DEPENDENCIES=ON \
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
- name: Build
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: x86_64-w64-mingw32-cmake --build . --config "$BUILD_TYPE" -j 2
|
||||
- name: Install
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: |
|
||||
DESTDIR="$(pwd)/$install_name" x86_64-w64-mingw32-cmake --build . --config "$BUILD_TYPE" --target install
|
||||
tar czvf "$install_name".tar.gz "$install_name"
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Win64 MinGW tarball
|
||||
name: ${{matrix.pkg_platform}} MinGW tarball
|
||||
path: ${{runner.workspace}}/build/${{env.install_name}}.tar.gz
|
||||
|
||||
build_with_makefile:
|
||||
|
|
@ -417,16 +330,16 @@ jobs:
|
|||
needs:
|
||||
- build_for_linux
|
||||
- build_for_mod
|
||||
- build_for_mingw32
|
||||
- build_for_mingw64
|
||||
- build_for_mingw
|
||||
- build_for_windows
|
||||
- archive_source_code
|
||||
steps:
|
||||
- name: Set install name
|
||||
run: |
|
||||
echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: Linux tarball
|
||||
# - uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: Linux tarball
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: MOD devices tarball
|
||||
|
|
@ -436,6 +349,12 @@ jobs:
|
|||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: Win64 MinGW tarball
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: Win32 installer
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: Win64 installer
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: Source code tarball
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -15,6 +15,8 @@ compile_commands.json
|
|||
*.autosave
|
||||
/Doxyfile
|
||||
.DS_Store
|
||||
.cache/
|
||||
.cmake/
|
||||
|
||||
clients/sfizz_jack
|
||||
clients/sfzprint
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ option_ex (SFIZZ_LV2_UI "Enable LV2 plug-in user interface" ON)
|
|||
option_ex (SFIZZ_VST "Enable VST plug-in build" ON)
|
||||
option_ex (SFIZZ_AU "Enable AU plug-in build" APPLE)
|
||||
option_ex (SFIZZ_VST2 "Enable VST2 plug-in build (unsupported)" OFF)
|
||||
option_ex (SFIZZ_PUREDATA "Enable Puredata plug-in build" OFF)
|
||||
option_ex (SFIZZ_BENCHMARKS "Enable benchmarks build" OFF)
|
||||
option_ex (SFIZZ_TESTS "Enable tests build" OFF)
|
||||
option_ex (SFIZZ_DEMOS "Enable feature demos build" OFF)
|
||||
|
|
@ -37,8 +38,12 @@ option_ex (SFIZZ_USE_SNDFILE "Enable use of the sndfile library" OFF)
|
|||
option_ex (SFIZZ_USE_VCPKG "Assume that sfizz is build using vcpkg" OFF)
|
||||
option_ex (SFIZZ_USE_SYSTEM_ABSEIL "Use Abseil libraries preinstalled on system" OFF)
|
||||
option_ex (SFIZZ_USE_SYSTEM_SIMDE "Use SIMDe libraries preinstalled on system" OFF)
|
||||
option_ex (SFIZZ_USE_SYSTEM_KISS_FFT "Use KISS FFT libraries preinstalled on system" OFF)
|
||||
option_ex (SFIZZ_USE_SYSTEM_PUGIXML "Use pugixml libraries preinstalled on system" OFF)
|
||||
option_ex (SFIZZ_USE_SYSTEM_CXXOPTS "Use CXXOPTS libraries preinstalled on system" OFF)
|
||||
option_ex (SFIZZ_STATIC_DEPENDENCIES "Link dependencies statically" OFF)
|
||||
option_ex (SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds" OFF)
|
||||
option_ex (SFIZZ_PROFILE_BUILD "Profile the build time" OFF)
|
||||
|
||||
# The fixed number of controller parameters
|
||||
set(SFIZZ_NUM_CCS 512)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
BSD 2-Clause License
|
||||
|
||||
The code is copyrighted by their respective authors, as indicated by the
|
||||
source control mechanism.
|
||||
|
||||
Please refer to AUTHORS.md for the list of contributors.
|
||||
|
||||
Copyright (c) 2021, sfizz contributors (detailed in AUTHORS.md)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
17
README.md
17
README.md
|
|
@ -4,6 +4,10 @@
|
|||
[![AppVeyor Build Status]](https://ci.appveyor.com/project/sfztools/sfizz)
|
||||
|
||||
[![Discord Badge Image]](https://discord.gg/3ArE9Mw)
|
||||
[![SFZv1 Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=1)
|
||||
[![SFZv2 Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=2)
|
||||
[![ARIA Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=aria)
|
||||
[![Cakewalk Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=cakewalk)
|
||||
|
||||
SFZ parser and synth c++ library, providing AU / LV2 / VST3 plugins
|
||||
and JACK standalone client, please check [our website] for more details.
|
||||
|
|
@ -36,9 +40,14 @@ We invite you to check out the [GOVERNANCE](GOVERNANCE.md) file to see how the o
|
|||
|
||||
## Dependencies and licenses
|
||||
|
||||
The sfizz library has the option to be compiled against either the `dr_libs`
|
||||
audio libraries, which is now the default option, or `libsndfile`.
|
||||
|
||||
- [dr_libs] is licensed under the MIT No Attribution license
|
||||
- [libsndfile] is licensed under the GNU Lesser General Public License v2.1
|
||||
|
||||
The sfizz library makes primary use of:
|
||||
|
||||
- [libsndfile], licensed under the GNU Lesser General Public License v2.1
|
||||
- [Abseil], licensed under the Apache License 2.0
|
||||
- [atomic_queue] by Maxim Egorushkin, licensed under the MIT license
|
||||
- [filesystem] by Steffen Schümann, licensed under the BSD 3-Clause license
|
||||
|
|
@ -71,6 +80,7 @@ The sfizz library also uses in some subprojects:
|
|||
[pugixml]: https://pugixml.org/
|
||||
[cephes]: https://www.netlib.org/cephes/
|
||||
[cpuid]: https://github.com/steinwurf/cpuid
|
||||
[dr_libs]: https://github.com/mackron/dr_libs
|
||||
[faust-libraries]: https://github.com/grame-cncm/faustlibraries
|
||||
[hiir]: http://ldesoras.free.fr/prod.html#src_hiir
|
||||
[KISS FFT]: http://kissfft.sourceforge.net/
|
||||
|
|
@ -92,3 +102,8 @@ The sfizz library also uses in some subprojects:
|
|||
[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
|
||||
|
||||
[SFZv1 Status Image]: https://sfz.tools/assets/img/sfizz/badge_sfz1.svg
|
||||
[SFZv2 Status Image]: https://sfz.tools/assets/img/sfizz/badge_sfz2.svg
|
||||
[ARIA Status Image]: https://sfz.tools/assets/img/sfizz/badge_aria.svg
|
||||
[Cakewalk Status Image]: https://sfz.tools/assets/img/sfizz/badge_cakewalk.svg
|
||||
|
|
|
|||
|
|
@ -1,14 +1,22 @@
|
|||
string(TIMESTAMP MAN_TODAY "%Y-%m-%d")
|
||||
|
||||
if(SFIZZ_JACK)
|
||||
add_executable(sfizz_jack MidiHelpers.h jack_client.cpp)
|
||||
target_link_libraries(sfizz_jack PRIVATE sfizz::sfizz sfizz::jack sfizz::spin_mutex absl::flags_parse)
|
||||
target_link_libraries(sfizz_jack PRIVATE sfizz::import sfizz::sfizz sfizz::jack sfizz::spin_mutex absl::flags_parse)
|
||||
sfizz_enable_lto_if_needed(sfizz_jack)
|
||||
configure_file(sfizz_jack.man.in sfizz_jack.man @ONLY)
|
||||
install(TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
COMPONENT "jack" OPTIONAL)
|
||||
install(FILES ${PROJECT_BINARY_DIR}/clients/sfizz_jack.man DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
|
||||
RENAME sfizz_jack COMPONENT "jack" OPTIONAL)
|
||||
endif()
|
||||
|
||||
if(SFIZZ_RENDER)
|
||||
add_executable(sfizz_render MidiHelpers.h sfizz_render.cpp)
|
||||
target_link_libraries(sfizz_render PRIVATE sfizz::internal sfizz::fmidi sfizz::cxxopts st_audiofile_formats)
|
||||
sfizz_enable_lto_if_needed(sfizz_render)
|
||||
configure_file(sfizz_render.man.in sfizz_render.man @ONLY)
|
||||
install(TARGETS sfizz_render DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT "render" OPTIONAL)
|
||||
install(FILES ${PROJECT_BINARY_DIR}/clients/sfizz_render.man DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
|
||||
RENAME sfizz_render COMPONENT "render" OPTIONAL)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "sfizz.hpp"
|
||||
#include "sfizz/import/sfizz_import.h"
|
||||
#include "MidiHelpers.h"
|
||||
#include <absl/flags/parse.h>
|
||||
#include <absl/flags/flag.h>
|
||||
|
|
@ -41,6 +42,9 @@
|
|||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
sfz::Sfizz synth;
|
||||
|
||||
static jack_port_t* midiInputPort;
|
||||
static jack_port_t* outputPort1;
|
||||
|
|
@ -148,30 +152,148 @@ static void done(int sig)
|
|||
// exit(0);
|
||||
}
|
||||
|
||||
bool loadInstrument(const char* fpath)
|
||||
{
|
||||
const char* importFormat = nullptr;
|
||||
if (!sfizz_load_or_import_file(synth.handle(), fpath, &importFormat)) {
|
||||
std::cout << "Could not load the instrument file: " << fpath << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Instrument loaded: " << fpath << '\n';
|
||||
std::cout << "===========================" << '\n';
|
||||
std::cout << "Total:" << '\n';
|
||||
std::cout << "\tMasters: " << synth.getNumMasters() << '\n';
|
||||
std::cout << "\tGroups: " << synth.getNumGroups() << '\n';
|
||||
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())
|
||||
std::cout << '\t' << file << '\n';
|
||||
std::cout << "===========================" << '\n';
|
||||
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())
|
||||
std::cout << opcode << ',';
|
||||
std::cout << '\n';
|
||||
if (importFormat) {
|
||||
std::cout << "===========================" << '\n';
|
||||
std::cout << "Import format: " << importFormat << '\n';
|
||||
}
|
||||
// std::cout << std::flush;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> stringTokenize(const std::string& str)
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
std::string part = "";
|
||||
for (size_t i = 0; i < str.length(); i++) {
|
||||
char c = str[i];
|
||||
if (c == ' ' && part != "") {
|
||||
tokens.push_back(part);
|
||||
part = "";
|
||||
} else if (c == '\"') {
|
||||
i++;
|
||||
while (str[i] != '\"') {
|
||||
part += str[i];
|
||||
i++;
|
||||
}
|
||||
tokens.push_back(part);
|
||||
part = "";
|
||||
} else {
|
||||
part += c;
|
||||
}
|
||||
}
|
||||
if (part != "") {
|
||||
tokens.push_back(part);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
void cliThreadProc()
|
||||
{
|
||||
while (!shouldClose) {
|
||||
std::cout << "\n> ";
|
||||
|
||||
std::string command;
|
||||
std::getline(std::cin, command);
|
||||
std::size_t pos = command.find(" ");
|
||||
std::string kw = command.substr(0, pos);
|
||||
std::string args = command.substr(pos + 1);
|
||||
std::vector<std::string> tokens = stringTokenize(args);
|
||||
|
||||
if (kw == "load_instrument") {
|
||||
try {
|
||||
std::lock_guard<SpinMutex> lock { processMutex };
|
||||
loadInstrument(tokens[0].c_str());
|
||||
} catch (...) {
|
||||
std::cout << "ERROR: Can't load instrument!\n";
|
||||
}
|
||||
} else if (kw == "set_oversampling") {
|
||||
try {
|
||||
std::lock_guard<SpinMutex> lock { processMutex };
|
||||
synth.setOversamplingFactor(stoi(args));
|
||||
} catch (...) {
|
||||
std::cout << "ERROR: Can't set oversampling!\n";
|
||||
}
|
||||
} else if (kw == "set_preload_size") {
|
||||
try {
|
||||
std::lock_guard<SpinMutex> lock { processMutex };
|
||||
synth.setPreloadSize(stoi(args));
|
||||
} catch (...) {
|
||||
std::cout << "ERROR: Can't set preload size!\n";
|
||||
}
|
||||
} else if (kw == "set_voices") {
|
||||
try {
|
||||
std::lock_guard<SpinMutex> lock { processMutex };
|
||||
synth.setNumVoices(stoi(args));
|
||||
} catch (...) {
|
||||
std::cout << "ERROR: Can't set num of voices!\n";
|
||||
}
|
||||
} else if (kw == "quit") {
|
||||
shouldClose = true;
|
||||
} else if (kw.size() > 0) {
|
||||
std::cout << "ERROR: Unknown command '" << kw << "'!\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ABSL_FLAG(std::string, client_name, "sfizz", "Jack client name");
|
||||
ABSL_FLAG(std::string, oversampling, "1x", "Internal oversampling factor (value values are x1, x2, x4, x8)");
|
||||
ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded value");
|
||||
ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded size");
|
||||
ABSL_FLAG(uint32_t, num_voices, 32, "Num of voices");
|
||||
ABSL_FLAG(bool, jack_autoconnect, false, "Autoconnect audio output");
|
||||
ABSL_FLAG(bool, state, false, "Output the synth state in the jack loop");
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// std::ios::sync_with_stdio(false);
|
||||
auto arguments = absl::ParseCommandLine(argc, argv);
|
||||
if (arguments.size() < 2) {
|
||||
std::cout << "You need to specify an SFZ file to load." << '\n';
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto filesToParse = absl::MakeConstSpan(arguments).subspan(1);
|
||||
const std::string clientName = absl::GetFlag(FLAGS_client_name);
|
||||
const std::string oversampling = absl::GetFlag(FLAGS_oversampling);
|
||||
const uint32_t preload_size = absl::GetFlag(FLAGS_preload_size);
|
||||
const uint32_t num_voices = absl::GetFlag(FLAGS_num_voices);
|
||||
const bool jack_autoconnect = absl::GetFlag(FLAGS_jack_autoconnect);
|
||||
const bool verboseState = absl::GetFlag(FLAGS_state);
|
||||
|
||||
std::cout << "Flags" << '\n';
|
||||
std::cout << "- Client name: " << clientName << '\n';
|
||||
std::cout << "- Oversampling: " << oversampling << '\n';
|
||||
std::cout << "- Preloaded Size: " << preload_size << '\n';
|
||||
std::cout << "- Preloaded size: " << preload_size << '\n';
|
||||
std::cout << "- Num of voices: " << num_voices << '\n';
|
||||
std::cout << "- Audio Autoconnect: " << jack_autoconnect << '\n';
|
||||
std::cout << "- Verbose State: " << verboseState << '\n';
|
||||
|
||||
const auto factor = [&]() {
|
||||
if (oversampling == "x1") return 1;
|
||||
if (oversampling == "x2") return 2;
|
||||
|
|
@ -185,33 +307,9 @@ int main(int argc, char** argv)
|
|||
std::cout << " " << file << ',';
|
||||
std::cout << '\n';
|
||||
|
||||
sfz::Sfizz synth;
|
||||
synth.setOversamplingFactor(factor);
|
||||
synth.setPreloadSize(preload_size);
|
||||
synth.loadSfzFile(filesToParse[0]);
|
||||
std::cout << "==========" << '\n';
|
||||
std::cout << "Total:" << '\n';
|
||||
std::cout << "\tMasters: " << synth.getNumMasters() << '\n';
|
||||
std::cout << "\tGroups: " << synth.getNumGroups() << '\n';
|
||||
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())
|
||||
std::cout << '\t' << file << '\n';
|
||||
std::cout << "==========" << '\n';
|
||||
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())
|
||||
std::cout << opcode << ',';
|
||||
std::cout << '\n';
|
||||
// std::cout << std::flush;
|
||||
synth.setNumVoices(num_voices);
|
||||
|
||||
jack_status_t status;
|
||||
client = jack_client_open(clientName.c_str(), JackNullOption, &status);
|
||||
|
|
@ -254,32 +352,40 @@ int main(int argc, char** argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
auto systemPorts = jack_get_ports(client, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput);
|
||||
if (systemPorts == nullptr) {
|
||||
std::cerr << "No physical output ports found" << '\n';
|
||||
return 1;
|
||||
if (jack_autoconnect) {
|
||||
auto systemPorts = jack_get_ports(client, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput);
|
||||
if (systemPorts == nullptr) {
|
||||
std::cerr << "No physical output ports found" << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (jack_connect(client, jack_port_name(outputPort1), systemPorts[0])) {
|
||||
std::cerr << "Cannot connect to physical output ports (0)" << '\n';
|
||||
}
|
||||
|
||||
if (jack_connect(client, jack_port_name(outputPort2), systemPorts[1])) {
|
||||
std::cerr << "Cannot connect to physical output ports (1)" << '\n';
|
||||
}
|
||||
jack_free(systemPorts);
|
||||
}
|
||||
|
||||
if (jack_connect(client, jack_port_name(outputPort1), systemPorts[0])) {
|
||||
std::cerr << "Cannot connect to physical output ports (0)" << '\n';
|
||||
if (!filesToParse.empty() && filesToParse[0]) {
|
||||
loadInstrument(filesToParse[0]);
|
||||
}
|
||||
|
||||
if (jack_connect(client, jack_port_name(outputPort2), systemPorts[1])) {
|
||||
std::cerr << "Cannot connect to physical output ports (1)" << '\n';
|
||||
}
|
||||
jack_free(systemPorts);
|
||||
std::thread cli_thread(cliThreadProc);
|
||||
|
||||
signal(SIGHUP, done);
|
||||
signal(SIGINT, done);
|
||||
signal(SIGTERM, done);
|
||||
signal(SIGQUIT, done);
|
||||
|
||||
while (!shouldClose){
|
||||
while (!shouldClose) {
|
||||
if (verboseState) {
|
||||
std::cout << "Active voices: " << synth.getNumActiveVoices() << '\n';
|
||||
#ifndef NDEBUG
|
||||
std::cout << "Allocated buffers: " << synth.getAllocatedBuffers() << '\n';
|
||||
std::cout << "Total size: " << synth.getAllocatedBytes() << '\n';
|
||||
std::cout << "Allocated buffers: " << synth.getAllocatedBuffers() << '\n';
|
||||
std::cout << "Total size: " << synth.getAllocatedBytes() << '\n';
|
||||
#endif
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
|
|
|||
37
clients/sfizz_jack.man.in
Normal file
37
clients/sfizz_jack.man.in
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
.TH sfizz_jack 1 "@MAN_TODAY@" "@CMAKE_PROJECT_VERSION@" "sfizz_jack man page"
|
||||
.SH NAME
|
||||
sfizz_jack \- Launch a sfizz instance as a JACK client
|
||||
.SH SYNOPSIS
|
||||
sfizz_jack [FILE] [OPTIONS...]
|
||||
.SH DESCRIPTION
|
||||
sfizz_jack wraps the sfizz SFZ library in a JACK client that can be controlled by a text interface.
|
||||
.SH OPTIONS
|
||||
.IP FILE
|
||||
An optional SFZ file name to load at firsts
|
||||
.IP "--client_name"
|
||||
Name for the JACK client
|
||||
.IP "--num_voices NUMBER"
|
||||
Change the maximum polyphony
|
||||
.IP "--preload_size NUMBER"
|
||||
Bytes to preload in cache for samples
|
||||
.IP "--state"
|
||||
Output the state in the JACK loop
|
||||
.IP "--jack_autoconnect"
|
||||
Autoconnect the JACK outputs
|
||||
.SH TEXT INTERFACE
|
||||
It is possible it interact with the JACK client through the standard input.
|
||||
The possible commands are
|
||||
.IP "load_instrument FILE"
|
||||
Load an instrument
|
||||
.IP "set_preload_size NUMBER"
|
||||
Set the number of bytes to preload in cache for samples
|
||||
.IP "set_voices NUMBER"
|
||||
Change the maximum polyphony
|
||||
.IP quit
|
||||
Quit
|
||||
.SH SEE ALSO
|
||||
sfizz_render(1)
|
||||
.SH BUGS
|
||||
See the main repository at @SFIZZ_REPOSITORY@.
|
||||
.SH AUTHOR
|
||||
Contributors can be seen on the main repository at @SFIZZ_REPOSITORY@.
|
||||
|
|
@ -66,6 +66,7 @@ int main(int argc, char** argv)
|
|||
bool help { false };
|
||||
bool useEOT { false };
|
||||
int quality { 2 };
|
||||
int polyphony { 64 };
|
||||
|
||||
options.add_options()
|
||||
("sfz", "SFZ file", cxxopts::value<std::string>())
|
||||
|
|
@ -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))
|
||||
("q,quality", "Resampling quality", cxxopts::value(quality))
|
||||
("p,polyphony", "Polyphony max", cxxopts::value(polyphony))
|
||||
("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))
|
||||
|
|
@ -114,11 +116,13 @@ int main(int argc, char** argv)
|
|||
LOG_INFO("Output file: " << outputPath.string());
|
||||
LOG_INFO("Block size: " << blockSize);
|
||||
LOG_INFO("Sample rate: " << sampleRate);
|
||||
LOG_INFO("Polyphony Max: " << polyphony);
|
||||
|
||||
sfz::Synth synth;
|
||||
synth.setSamplesPerBlock(blockSize);
|
||||
synth.setSampleRate(sampleRate);
|
||||
synth.setSampleQuality(sfz::Synth::ProcessMode::ProcessFreewheeling, quality);
|
||||
synth.setNumVoices(polyphony);
|
||||
synth.enableFreeWheeling();
|
||||
|
||||
if (params.count("log") > 0)
|
||||
|
|
|
|||
30
clients/sfizz_render.man.in
Normal file
30
clients/sfizz_render.man.in
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
.TH sfizz_render 1 "@MAN_TODAY@" "@CMAKE_PROJECT_VERSION@" "sfizz_render man page"
|
||||
.SH NAME
|
||||
sfizz_render \- Render a MIDI file as a WAV file using an SFZ instrument description.
|
||||
.SH SYNOPSIS
|
||||
sfizz_render --sfz FILE --wav FILE --midi FILE [OPTIONS...]
|
||||
.SH DESCRIPTION
|
||||
sfizz_render wraps the sfizz SFZ library and can be used to render midi file as sound files using an SFZ description file and its associated samples.
|
||||
.SH OPTIONS
|
||||
.IP "-b, --blocksize NUMBER"
|
||||
Block size for the sfizz callbacks
|
||||
.IP "-s, --samplerate NUMBER"
|
||||
Output sample rate
|
||||
.IP "-q, --quality NUMBER"
|
||||
Resampling quality, like the SFZ sample_quality opcode. A value of 1 will use a linear interpolation of source samples, while higher value will use increasingly better algorithms.
|
||||
.IP "-p, --polyphony NUMBER"
|
||||
Maximum polyphony
|
||||
.IP "-v, --verbose"
|
||||
Verbose output
|
||||
.IP "--log PREFIX"
|
||||
Produce logs
|
||||
.IP "--use-eot"
|
||||
End the rendering at the last End of Track Midi message
|
||||
.IP "-h, --help"
|
||||
Show help
|
||||
.SH SEE ALSO
|
||||
sfizz_jack(1)
|
||||
.SH BUGS
|
||||
See the main repository at @SFIZZ_REPOSITORY@.
|
||||
.SH AUTHOR
|
||||
Contributors can be seen on the main repository at @SFIZZ_REPOSITORY@.
|
||||
|
|
@ -8,7 +8,7 @@ set(LV2PLUGIN_VERSION_MICRO 0)
|
|||
set(LV2PLUGIN_NAME "sfizz")
|
||||
set(LV2PLUGIN_COMMENT "SFZ sampler")
|
||||
set(LV2PLUGIN_URI "http://sfztools.github.io/sfizz")
|
||||
set(LV2PLUGIN_REPOSITORY "https://github.com/sfztools/sfizz")
|
||||
set(LV2PLUGIN_REPOSITORY SFIZZ_REPOSITORY)
|
||||
set(LV2PLUGIN_AUTHOR "SFZTools")
|
||||
set(LV2PLUGIN_EMAIL "paul@ferrand.cc")
|
||||
if(SFIZZ_USE_VCPKG)
|
||||
|
|
@ -62,7 +62,9 @@ function(sfizz_lv2_generate_controllers_ttl FILE)
|
|||
sfizz:cc${_i}
|
||||
a lv2:Parameter ;
|
||||
rdfs:label \"Controller ${_i}\" ;
|
||||
rdfs:range atom:Float")
|
||||
rdfs:range atom:Float ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 1.0")
|
||||
|
||||
if(_i LESS 128 AND NOT SFIZZ_LV2_PSA)
|
||||
math(EXPR _digit1 "${_i}>>4")
|
||||
|
|
|
|||
70
cmake/PuredataConfig.cmake
Normal file
70
cmake/PuredataConfig.cmake
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
find_path(PD_INCLUDE_BASEDIR "m_pd.h" PATH_SUFFIXES "pd")
|
||||
set(PD_IMP_DEF "${PROJECT_SOURCE_DIR}/plugins/puredata/external/pd/bin/pd.def")
|
||||
|
||||
if(PD_INCLUDE_BASEDIR)
|
||||
message(STATUS "Puredata headers: ${PUREDATA_INCLUDE_DIR}")
|
||||
else()
|
||||
message(STATUS "Puredata headers not found, using our own")
|
||||
set(PD_INCLUDE_BASEDIR "${PROJECT_SOURCE_DIR}/plugins/puredata/external/pd/include")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(PUREDATA_SUFFIX ".dll")
|
||||
elseif(APPLE)
|
||||
set(PUREDATA_SUFFIX ".pd_darwin")
|
||||
else()
|
||||
set(PUREDATA_SUFFIX ".pd_linux")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(PDPLUGIN_INSTALL_DIR "$ENV{HOME}/Library/Pd" CACHE STRING
|
||||
"Install destination for Puredata bundle [default: $ENV{HOME}/Library/Pd]")
|
||||
elseif(MSVC)
|
||||
set(PDPLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/Pd/extra" CACHE STRING
|
||||
"Install destination for Puredata bundle [default: ${CMAKE_INSTALL_PREFIX}/Pd/extra]")
|
||||
else()
|
||||
set(PDPLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/pd/extra" CACHE STRING
|
||||
"Install destination for Puredata bundle [default: ${CMAKE_INSTALL_PREFIX}/lib/pd/extra]")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_library(pdex-implib STATIC IMPORTED)
|
||||
if(MSVC)
|
||||
add_custom_command(
|
||||
OUTPUT "pd.lib"
|
||||
COMMAND "lib" "/out:pd.lib" "/def:${PD_IMP_DEF}" "/machine:${MSVC_C_ARCHITECTURE_ID}"
|
||||
DEPENDS "${PD_IMP_DEF}")
|
||||
add_custom_target(pdex-implib-generated
|
||||
DEPENDS "pd.lib")
|
||||
set_target_properties(pdex-implib PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/pd.lib")
|
||||
else()
|
||||
find_program(PD_DLLTOOL_PROGRAM "dlltool")
|
||||
if(NOT PD_DLLTOOL_PROGRAM)
|
||||
message(FATAL_ERROR "Cannot find dlltool")
|
||||
endif()
|
||||
add_custom_command(
|
||||
OUTPUT "libpd.dll.a"
|
||||
COMMAND "${PD_DLLTOOL_PROGRAM}" "-l" "libpd.dll.a" "-d" "${PD_IMP_DEF}"
|
||||
DEPENDS "${PD_IMP_DEF}")
|
||||
add_custom_target(pdex-implib-generated
|
||||
DEPENDS "libpd.dll.a")
|
||||
set_target_properties(pdex-implib PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/libpd.dll.a")
|
||||
endif()
|
||||
add_dependencies(pdex-implib pdex-implib-generated)
|
||||
endif()
|
||||
|
||||
function(add_pd_external TARGET)
|
||||
add_library("${TARGET}" MODULE ${ARGN})
|
||||
target_include_directories("${TARGET}" PRIVATE "${PD_INCLUDE_BASEDIR}")
|
||||
set_target_properties("${TARGET}" PROPERTIES
|
||||
PREFIX ""
|
||||
SUFFIX "${PUREDATA_SUFFIX}")
|
||||
if(APPLE)
|
||||
set_property(TARGET "${TARGET}" APPEND_STRING
|
||||
PROPERTY LINK_FLAGS " -Wl,-undefined,suppress,-flat_namespace")
|
||||
elseif(WIN32)
|
||||
target_link_libraries("${TARGET}" PRIVATE pdex-implib)
|
||||
endif()
|
||||
endfunction()
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
include(CMakeDependentOption)
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(GNUWarnings)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to be used")
|
||||
|
|
@ -17,9 +19,21 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
|
|||
# Set C++ compatibility level
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND CMAKE_CXX_STANDARD LESS 17)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
elseif((SFIZZ_LV2_UI OR SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) AND CMAKE_CXX_STANDARD LESS 14)
|
||||
# if the UI is part of the build, make it 14
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
elseif((SFIZZ_LV2_UI OR SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) AND CMAKE_CXX_STANDARD LESS 17)
|
||||
# if the UI is part of the build, make it 17
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
|
||||
# Set build profiling options
|
||||
if(SFIZZ_PROFILE_BUILD)
|
||||
check_c_compiler_flag("-ftime-trace" SFIZZ_HAVE_CFLAG_FTIME_TRACE)
|
||||
check_cxx_compiler_flag("-ftime-trace" SFIZZ_HAVE_CXXFLAG_FTIME_TRACE)
|
||||
if(SFIZZ_HAVE_CFLAG_FTIME_TRACE)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:C>:-ftime-trace>")
|
||||
endif()
|
||||
if(SFIZZ_HAVE_CXXFLAG_FTIME_TRACE)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-ftime-trace>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Process sources as UTF-8
|
||||
|
|
@ -32,11 +46,39 @@ if(WIN32)
|
|||
add_compile_definitions(_WIN32_WINNT=0x601)
|
||||
endif()
|
||||
|
||||
# Define the math constants everywhere
|
||||
if(WIN32)
|
||||
add_compile_definitions(_USE_MATH_DEFINES)
|
||||
endif()
|
||||
|
||||
# Set macOS compatibility level
|
||||
if(APPLE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9")
|
||||
endif()
|
||||
|
||||
# If using C++17, check if aligned-new has runtime support on the platform;
|
||||
# on macOS, this depends on the deployment target.
|
||||
if(CMAKE_CXX_STANDARD LESS 17)
|
||||
# not necessary on older C++, it will call ordinary new and delete
|
||||
set(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT FALSE)
|
||||
else()
|
||||
check_cxx_source_compiles("
|
||||
struct Test { alignas(1024) int z; };
|
||||
int main() { new Test; return 0; }
|
||||
" SFIZZ_HAVE_CXX17_ALIGNED_NEW)
|
||||
# if support is absent, sfizz will provide a substitute implementation
|
||||
if(SFIZZ_HAVE_CXX17_ALIGNED_NEW)
|
||||
set(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT FALSE)
|
||||
else()
|
||||
# on macOS, this mandatory flag tells that allocation functions are user-provided
|
||||
check_cxx_compiler_flag("-faligned-allocation" SFIZZ_HAVE_CXXFLAG_FALIGNED_ALLOCATION)
|
||||
if(SFIZZ_HAVE_CXXFLAG_FALIGNED_ALLOCATION)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-faligned-allocation>")
|
||||
endif()
|
||||
set(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Do not define macros `min` and `max`
|
||||
if(WIN32)
|
||||
add_compile_definitions(NOMINMAX)
|
||||
|
|
@ -87,6 +129,9 @@ if(USE_LIBCPP)
|
|||
add_link_options(-lc++abi) # New command on CMake master, not in 3.12 release
|
||||
endif()
|
||||
|
||||
set(SFIZZ_REPOSITORY https://github.com/sfztools/sfizz)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Don't show build information when building a different project
|
||||
function(show_build_info_if_needed)
|
||||
if(CMAKE_PROJECT_NAME STREQUAL "sfizz")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
include(CheckCXXSourceCompiles)
|
||||
|
||||
# Find system threads
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
|
|
@ -65,9 +67,19 @@ add_library(sfizz::jsl ALIAS sfizz_jsl)
|
|||
target_include_directories(sfizz_jsl INTERFACE "external/jsl/include")
|
||||
|
||||
# The cxxopts library
|
||||
add_library(sfizz_cxxopts INTERFACE)
|
||||
if(SFIZZ_USE_SYSTEM_CXXOPTS)
|
||||
find_path(CXXOPTS_INCLUDE_DIR "cxxopts.hpp")
|
||||
if(NOT CXXOPTS_INCLUDE_DIR)
|
||||
message(FATAL_ERROR "Cannot find cxxopts")
|
||||
endif()
|
||||
add_library(sfizz_cxxopts INTERFACE)
|
||||
target_include_directories(sfizz_cxxopts INTERFACE "${CXXOPTS_INCLUDE_DIR}")
|
||||
else()
|
||||
add_library(sfizz_cxxopts INTERFACE)
|
||||
add_library(sfizz::cxxopts ALIAS sfizz_cxxopts)
|
||||
target_include_directories(sfizz_cxxopts INTERFACE "external/cxxopts")
|
||||
endif()
|
||||
add_library(sfizz::cxxopts ALIAS sfizz_cxxopts)
|
||||
target_include_directories(sfizz_cxxopts INTERFACE "external/cxxopts")
|
||||
|
||||
# The sndfile library
|
||||
if(SFIZZ_USE_SNDFILE OR SFIZZ_DEMOS OR SFIZZ_DEVTOOLS OR SFIZZ_BENCHMARKS)
|
||||
|
|
@ -105,10 +117,25 @@ add_subdirectory("external/st_audiofile" EXCLUDE_FROM_ALL)
|
|||
add_library(sfizz_simde INTERFACE)
|
||||
add_library(sfizz::simde ALIAS sfizz_simde)
|
||||
if(SFIZZ_USE_SYSTEM_SIMDE)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(SIMDE "simde" REQUIRED)
|
||||
target_include_directories(sfizz_simde INTERFACE "${SIMDE_INCLUDE_DIRS}")
|
||||
if(NOT SIMDE_VERSION OR SIMDE_VERSION VERSION_LESS_EQUAL "0.7.2")
|
||||
find_path(SIMDE_INCLUDE_DIR "simde/simde-features.h")
|
||||
if(NOT SIMDE_INCLUDE_DIR)
|
||||
message(FATAL_ERROR "Cannot find simde")
|
||||
endif()
|
||||
target_include_directories(sfizz_simde INTERFACE "${SIMDE_INCLUDE_DIR}")
|
||||
|
||||
function(sfizz_ensure_simde_version result major minor micro)
|
||||
set(CMAKE_REQUIRED_INCLUDES "${SIMDE_INCLUDE_DIR}")
|
||||
check_cxx_source_compiles(
|
||||
"#include <simde/simde-common.h>
|
||||
#if SIMDE_VERSION < HEDLEY_VERSION_ENCODE(${major}, ${minor}, ${micro})
|
||||
# error Version check failed
|
||||
#endif
|
||||
int main() { return 0; }"
|
||||
"${result}")
|
||||
endfunction()
|
||||
|
||||
sfizz_ensure_simde_version(SFIZZ_SIMDE_AT_LEAST_0_7_3 0 7 3)
|
||||
if(NOT SFIZZ_SIMDE_AT_LEAST_0_7_3)
|
||||
message(WARNING "The version of SIMDe on this system has known issues. \
|
||||
It is recommended to either update if a newer version is available, or use the \
|
||||
version bundled with this package. Refer to following issues: \
|
||||
|
|
@ -122,9 +149,18 @@ if(TARGET sfizz::openmp)
|
|||
endif()
|
||||
|
||||
# The pugixml library
|
||||
add_library(sfizz_pugixml STATIC "src/external/pugixml/src/pugixml.cpp")
|
||||
if(SFIZZ_USE_SYSTEM_PUGIXML)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(PUGIXML "pugixml" REQUIRED)
|
||||
add_library(sfizz_pugixml INTERFACE)
|
||||
target_include_directories(sfizz_pugixml INTERFACE ${PUGIXML_INCLUDE_DIRS})
|
||||
target_link_libraries(sfizz_pugixml INTERFACE ${PUGIXML_LIBRARIES})
|
||||
link_directories(${PUGIXML_LIBRARY_DIRS})
|
||||
else()
|
||||
add_library(sfizz_pugixml STATIC "src/external/pugixml/src/pugixml.cpp")
|
||||
target_include_directories(sfizz_pugixml PUBLIC "src/external/pugixml/src")
|
||||
endif()
|
||||
add_library(sfizz::pugixml ALIAS sfizz_pugixml)
|
||||
target_include_directories(sfizz_pugixml PUBLIC "src/external/pugixml/src")
|
||||
|
||||
# The spline library
|
||||
add_library(sfizz_spline STATIC "src/external/spline/spline/spline.cpp")
|
||||
|
|
@ -148,13 +184,31 @@ add_library(sfizz::hiir_polyphase_iir2designer ALIAS sfizz_hiir_polyphase_iir2de
|
|||
target_link_libraries(sfizz_hiir_polyphase_iir2designer PUBLIC sfizz::hiir)
|
||||
|
||||
# The kissfft library
|
||||
add_library(sfizz_kissfft STATIC
|
||||
"src/external/kiss_fft/kiss_fft.c"
|
||||
"src/external/kiss_fft/tools/kiss_fftr.c")
|
||||
if (SFIZZ_USE_SYSTEM_KISS_FFT)
|
||||
find_path(KISSFFT_INCLUDE_DIR "kiss_fft.h" PATH_SUFFIXES "kissfft")
|
||||
find_path(KISSFFTR_INCLUDE_DIR "kiss_fftr.h" PATH_SUFFIXES "kissfft")
|
||||
find_library(KISSFFT_FFTR_LIBRARY "kiss_fftr_float" KISSFFTR_INCLUDE_DIR)
|
||||
find_library(KISSFFT_FFT_LIBRARY "kiss_fft_float" KISSFFT_INCLUDE_DIR)
|
||||
add_library(sfizz_kissfft INTERFACE)
|
||||
if(NOT KISSFFT_FFT_LIBRARY)
|
||||
message(FATAL_ERROR "Cannot find kiss fft")
|
||||
endif()
|
||||
if(NOT KISSFFT_FFTR_LIBRARY)
|
||||
message(FATAL_ERROR "Cannot find kiss fftr")
|
||||
endif()
|
||||
target_include_directories(sfizz_kissfft INTERFACE "${KISSFFTR_INCLUDE_DIR}")
|
||||
target_include_directories(sfizz_kissfft INTERFACE "${KISSFFT_INCLUDE_DIR}")
|
||||
target_link_libraries(sfizz_kissfft INTERFACE "${KISSFFT_FFTR_LIBRARY}")
|
||||
target_link_libraries(sfizz_kissfft INTERFACE "${KISSFFT_FFT_LIBRARY}")
|
||||
else()
|
||||
add_library(sfizz_kissfft STATIC
|
||||
"src/external/kiss_fft/kiss_fft.c"
|
||||
"src/external/kiss_fft/tools/kiss_fftr.c")
|
||||
target_include_directories(sfizz_kissfft
|
||||
PUBLIC "src/external/kiss_fft"
|
||||
PUBLIC "src/external/kiss_fft/tools")
|
||||
endif()
|
||||
add_library(sfizz::kissfft ALIAS sfizz_kissfft)
|
||||
target_include_directories(sfizz_kissfft
|
||||
PUBLIC "src/external/kiss_fft"
|
||||
PUBLIC "src/external/kiss_fft/tools")
|
||||
|
||||
# The cephes library
|
||||
add_library(sfizz_cephes STATIC
|
||||
|
|
@ -183,9 +237,27 @@ add_library(sfizz::atomic_queue ALIAS sfizz_atomic_queue)
|
|||
target_include_directories(sfizz_atomic_queue INTERFACE "external/atomic_queue/include")
|
||||
|
||||
# The ghc::filesystem library
|
||||
add_library(sfizz_filesystem INTERFACE)
|
||||
if(FALSE)
|
||||
# header-only
|
||||
add_library(sfizz_filesystem INTERFACE)
|
||||
target_include_directories(sfizz_filesystem INTERFACE "external/filesystem/include")
|
||||
else()
|
||||
# static library
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/fs_std_impl.cpp" "#include <ghc/fs_std_impl.hpp>")
|
||||
add_library(sfizz_filesystem_impl STATIC "${CMAKE_CURRENT_BINARY_DIR}/fs_std_impl.cpp")
|
||||
target_include_directories(sfizz_filesystem_impl PUBLIC "external/filesystem/include")
|
||||
# Add the needed linker option for GCC 8
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU"
|
||||
AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0
|
||||
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
|
||||
target_link_libraries(sfizz_filesystem_impl PUBLIC stdc++fs)
|
||||
endif()
|
||||
#
|
||||
add_library(sfizz_filesystem INTERFACE)
|
||||
target_compile_definitions(sfizz_filesystem INTERFACE "GHC_FILESYSTEM_FWD")
|
||||
target_link_libraries(sfizz_filesystem INTERFACE sfizz_filesystem_impl)
|
||||
endif()
|
||||
add_library(sfizz::filesystem ALIAS sfizz_filesystem)
|
||||
target_include_directories(sfizz_filesystem INTERFACE "external/filesystem/include")
|
||||
|
||||
# The atomic library
|
||||
add_library(sfizz_atomic INTERFACE)
|
||||
|
|
|
|||
|
|
@ -107,6 +107,8 @@ SFIZZ_SOURCES = \
|
|||
src/sfizz/PowerFollower.cpp \
|
||||
src/sfizz/Region.cpp \
|
||||
src/sfizz/RegionSet.cpp \
|
||||
src/sfizz/RegionStateful.cpp \
|
||||
src/sfizz/Resources.cpp \
|
||||
src/sfizz/RTSemaphore.cpp \
|
||||
src/sfizz/ScopedFTZ.cpp \
|
||||
src/sfizz/sfizz.cpp \
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "ui_DemoParser.h"
|
||||
#include "parser/Parser.h"
|
||||
#include "parser/ParserListener.h"
|
||||
#include <QApplication>
|
||||
#include <QTimer>
|
||||
#include <QFileInfo>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "sfizz/Curve.h"
|
||||
#include "sfizz/parser/Parser.h"
|
||||
#include "sfizz/parser/ParserListener.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/types/span.h"
|
||||
#include <iostream>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
*/
|
||||
|
||||
#include "parser/Parser.h"
|
||||
#include "parser/ParserListener.h"
|
||||
#include <pugixml.hpp>
|
||||
#include <cxxopts.hpp>
|
||||
#include <absl/strings/string_view.h>
|
||||
|
|
|
|||
2
external/filesystem
vendored
2
external/filesystem
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 2a8b380f8d4e77b389c42a194ab9c70d8e3a0f1e
|
||||
Subproject commit 7bc5c17305fe70518ca72162e244af1d12455a91
|
||||
39
external/jsl/include/jsl/bits/memory/aligned_unique_ptr.tcc
vendored
Normal file
39
external/jsl/include/jsl/bits/memory/aligned_unique_ptr.tcc
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// -*- C++ -*-
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
// Copyright Jean Pierre Cimalando 2018-2020.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#pragma once
|
||||
#include "../../memory"
|
||||
|
||||
namespace jsl {
|
||||
|
||||
template <class T, std::size_t Al>
|
||||
void aligned_ptr_delete<T, Al>::operator()(T *p) const noexcept
|
||||
{
|
||||
aligned_allocator<T, Al> allocator;
|
||||
allocator.destroy(p);
|
||||
allocator.deallocate(p, 0);
|
||||
}
|
||||
|
||||
template <class T, std::size_t Al, class... Args>
|
||||
aligned_unique_ptr<typename make_aligned_traits<T>::single_element, Al>
|
||||
make_aligned(Args &&... args)
|
||||
{
|
||||
struct allocation_delete {
|
||||
void operator()(T *p) const noexcept
|
||||
{
|
||||
aligned_allocator<T, Al> allocator;
|
||||
allocator.destroy(p);
|
||||
}
|
||||
};
|
||||
aligned_allocator<T, Al> allocator;
|
||||
std::unique_ptr<T, allocation_delete> allocation(allocator.allocate(1, nullptr));
|
||||
allocator.construct(allocation.get(), std::forward<Args>(args)...);
|
||||
return aligned_unique_ptr<T, Al>(allocation.release());
|
||||
}
|
||||
|
||||
} // namespace jsl
|
||||
42
external/jsl/include/jsl/memory
vendored
Normal file
42
external/jsl/include/jsl/memory
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// -*- C++ -*-
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
// Copyright Jean Pierre Cimalando 2018-2020.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#pragma once
|
||||
#include "./allocator"
|
||||
#include <memory>
|
||||
|
||||
namespace jsl {
|
||||
|
||||
template <class T, std::size_t Al>
|
||||
struct aligned_ptr_delete {
|
||||
void operator()(T *p) const noexcept;
|
||||
};
|
||||
|
||||
template <class T, std::size_t Al>
|
||||
using aligned_unique_ptr = std::unique_ptr<T, aligned_ptr_delete<T, Al>>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T> struct make_aligned_traits;
|
||||
template <class T, size_t N> struct make_aligned_traits<T[N]> { struct invalid {}; };
|
||||
template <class T> struct make_aligned_traits<T[]> { struct invalid {}; };
|
||||
template <class T> struct make_aligned_traits { using single_element = T; };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T, std::size_t Al, class... Args>
|
||||
aligned_unique_ptr<typename make_aligned_traits<T>::single_element, Al>
|
||||
make_aligned(Args &&... args);
|
||||
|
||||
template <class T, std::size_t Al, class... Args>
|
||||
typename make_aligned_traits<T>::invalid
|
||||
make_aligned(Args &&... args) = delete;
|
||||
|
||||
} // namespace jsl
|
||||
|
||||
#include "bits/memory/aligned_unique_ptr.tcc"
|
||||
2
external/simde
vendored
2
external/simde
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 5c2f423b41c06228e4be0cce0010a252297da4e7
|
||||
Subproject commit 98075d0593f539762125dbb215d95e782a6ae344
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
add_library(plugins-common STATIC EXCLUDE_FROM_ALL
|
||||
"common/plugin/RMSFollower.h"
|
||||
"common/plugin/MessageUtils.h"
|
||||
"common/plugin/MessageUtils.cpp"
|
||||
"common/plugin/InstrumentDescription.h"
|
||||
|
|
@ -28,7 +29,7 @@ endif()
|
|||
target_include_directories(plugins-common PUBLIC "common")
|
||||
target_link_libraries(plugins-common
|
||||
PUBLIC sfizz::spin_mutex
|
||||
PUBLIC sfizz::filesystem absl::strings
|
||||
PUBLIC sfizz::simde sfizz::filesystem absl::strings
|
||||
PRIVATE sfizz::pugixml
|
||||
PRIVATE sfizz::internal sfizz::sfizz)
|
||||
add_library(sfizz::plugins-common ALIAS plugins-common)
|
||||
|
|
@ -40,10 +41,6 @@ elseif(APPLE)
|
|||
target_link_libraries(plugins-common PRIVATE
|
||||
"${APPLE_FOUNDATION_LIBRARY}")
|
||||
else()
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GLIB REQUIRED glib-2.0)
|
||||
target_include_directories(plugins-common PRIVATE ${GLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(plugins-common PRIVATE ${GLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if((SFIZZ_LV2 AND SFIZZ_LV2_UI) OR SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2)
|
||||
|
|
@ -54,6 +51,10 @@ if(SFIZZ_LV2)
|
|||
add_subdirectory(lv2)
|
||||
endif()
|
||||
|
||||
if(SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2)
|
||||
if((SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) AND NOT (SFIZZ_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc).*"))
|
||||
add_subdirectory(vst)
|
||||
endif()
|
||||
|
||||
if(SFIZZ_PUREDATA)
|
||||
add_subdirectory(puredata)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ std::string getDescriptionBlob(sfizz_synth_t* handle)
|
|||
synth.sendMessage(*client, 0, "/key/slots", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/cc/slots", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/sustain_or_sostenuto/slots", "", nullptr);
|
||||
|
||||
blob.shrink_to_fit();
|
||||
return blob;
|
||||
|
|
@ -152,6 +153,8 @@ InstrumentDescription parseDescriptionBlob(absl::string_view blob)
|
|||
copyArgToBitSpan(args[0], desc.keyswitchUsed.span());
|
||||
else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b"))
|
||||
copyArgToBitSpan(args[0], desc.ccUsed.span());
|
||||
else if (Messages::matchOSC("/sustain_or_sostenuto/slots", path, indices) && !strcmp(sig, "b"))
|
||||
copyArgToBitSpan(args[0], desc.sustainOrSostenuto.span());
|
||||
else if (Messages::matchOSC("/key&/label", path, indices) && !strcmp(sig, "s"))
|
||||
desc.keyLabel[indices[0]] = args[0].s;
|
||||
else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s"))
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ struct InstrumentDescription {
|
|||
std::string image;
|
||||
BitArray<128> keyUsed {};
|
||||
BitArray<128> keyswitchUsed {};
|
||||
BitArray<128> sustainOrSostenuto {};
|
||||
BitArray<sfz::config::numCCs> ccUsed {};
|
||||
std::array<std::string, 128> keyLabel {};
|
||||
std::array<std::string, 128> keyswitchLabel {};
|
||||
|
|
|
|||
|
|
@ -5,8 +5,12 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "NativeHelpers.h"
|
||||
#include <absl/strings/match.h>
|
||||
#include <absl/strings/strip.h>
|
||||
#include <absl/strings/string_view.h>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
|
|
@ -22,24 +26,119 @@ const fs::path& getUserDocumentsDirectory()
|
|||
}();
|
||||
return directory;
|
||||
}
|
||||
|
||||
wchar_t *stringToWideChar(const char *str, int strCch)
|
||||
{
|
||||
unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0);
|
||||
if (strSize == 0)
|
||||
return {};
|
||||
std::unique_ptr<wchar_t[]> strW(new wchar_t[strSize]);
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0)
|
||||
return {};
|
||||
return strW.release();
|
||||
}
|
||||
|
||||
char* stringToUTF8(const wchar_t *strW, int strWCch)
|
||||
{
|
||||
unsigned strSize = WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, nullptr, 0, nullptr, nullptr);
|
||||
if (strSize == 0)
|
||||
return {};
|
||||
std::unique_ptr<char[]> str(new char[strSize]);
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, str.get(), strSize, nullptr, nullptr) == 0)
|
||||
return {};
|
||||
return str.release();
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
// implemented in NativeHelpers.mm
|
||||
#else
|
||||
#include <glib.h>
|
||||
|
||||
const fs::path& getUserDocumentsDirectory()
|
||||
{
|
||||
static const fs::path directory = []() -> fs::path {
|
||||
const gchar* path = g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS);
|
||||
if (path)
|
||||
return fs::path(path);
|
||||
else {
|
||||
const char* home = getenv("HOME");
|
||||
if (home && home[0] == '/')
|
||||
return fs::path(home) / "Documents";
|
||||
throw std::runtime_error("Cannot get the document directory.");
|
||||
for (const XdgUserDirsEntry& ent :
|
||||
parseXdgUserDirs(getXdgConfigHome() / "user-dirs.dirs")) {
|
||||
if (ent.name == "XDG_DOCUMENTS_DIR")
|
||||
return ent.value;
|
||||
}
|
||||
return getUserHomeDirectory() / "Documents";
|
||||
}();
|
||||
return directory;
|
||||
}
|
||||
|
||||
const fs::path& getUserHomeDirectory()
|
||||
{
|
||||
static const fs::path directory = []() -> fs::path {
|
||||
const char* home = getenv("HOME");
|
||||
if (home && home[0] == '/')
|
||||
return fs::u8path(home);
|
||||
else
|
||||
throw std::runtime_error("Cannot get the home directory.");
|
||||
}();
|
||||
return directory;
|
||||
}
|
||||
|
||||
const fs::path& getXdgConfigHome()
|
||||
{
|
||||
static const fs::path directory = []() -> fs::path {
|
||||
const char* config = getenv("XDG_CONFIG_HOME");
|
||||
if (config && config[0] == '/')
|
||||
return fs::u8path(config);
|
||||
else
|
||||
return getUserHomeDirectory() / ".config";
|
||||
|
||||
}();
|
||||
return directory;
|
||||
}
|
||||
|
||||
std::vector<XdgUserDirsEntry> parseXdgUserDirs(const fs::path& userDirsPath)
|
||||
{
|
||||
// from user-dirs.dirs(5)
|
||||
// This file contains lines of the form `XDG_NAME_DIR=VALUE`
|
||||
// VALUE must be of the form "$HOME/Path" or "/Path".
|
||||
// Lines beginning with a # character are ignored.
|
||||
|
||||
std::vector<XdgUserDirsEntry> ents;
|
||||
const fs::path& home = getUserHomeDirectory();
|
||||
|
||||
fs::ifstream in(userDirsPath);
|
||||
std::string lineBuf;
|
||||
|
||||
lineBuf.reserve(256);
|
||||
while (std::getline(in, lineBuf)) {
|
||||
absl::string_view line(lineBuf);
|
||||
|
||||
line = absl::StripLeadingAsciiWhitespace(line);
|
||||
if (line.empty() || line.front() == '#')
|
||||
continue;
|
||||
|
||||
size_t pos = line.find('=');
|
||||
if (pos == line.npos)
|
||||
continue;
|
||||
|
||||
XdgUserDirsEntry ent;
|
||||
ent.name = std::string(line.substr(0, pos));
|
||||
|
||||
absl::string_view rawValue = line.substr(pos + 1);
|
||||
|
||||
rawValue = absl::StripTrailingAsciiWhitespace(rawValue);
|
||||
|
||||
if (rawValue.size() < 2 || rawValue.front() != '"' || rawValue.back() != '"')
|
||||
continue;
|
||||
|
||||
rawValue.remove_prefix(1);
|
||||
rawValue.remove_suffix(1);
|
||||
|
||||
if (!rawValue.empty() && rawValue.front() == '/')
|
||||
ent.value = fs::u8path(rawValue.begin(), rawValue.end());
|
||||
else if (absl::StartsWith(rawValue, "$HOME")) {
|
||||
absl::string_view part = rawValue.substr(5);
|
||||
ent.value = home / fs::u8path(part.begin(), part.end()).relative_path();
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
ents.push_back(std::move(ent));
|
||||
}
|
||||
|
||||
return ents;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,5 +6,21 @@
|
|||
|
||||
#pragma once
|
||||
#include <ghc/fs_std.hpp>
|
||||
#include <vector>
|
||||
#if defined(_WIN32)
|
||||
#include <cwchar>
|
||||
#endif
|
||||
|
||||
const fs::path& getUserDocumentsDirectory();
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
const fs::path& getUserHomeDirectory();
|
||||
const fs::path& getXdgConfigHome();
|
||||
struct XdgUserDirsEntry { std::string name; fs::path value; };
|
||||
std::vector<XdgUserDirsEntry> parseXdgUserDirs(const fs::path& userDirsPath);
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
wchar_t *stringToWideChar(const char *str, int strCch = -1);
|
||||
char* stringToUTF8(const wchar_t *strW, int strWCch = -1);
|
||||
#endif
|
||||
|
|
|
|||
73
plugins/common/plugin/RMSFollower.h
Normal file
73
plugins/common/plugin/RMSFollower.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// 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 <simde/x86/sse.h>
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
|
||||
class RMSFollower {
|
||||
public:
|
||||
RMSFollower()
|
||||
{
|
||||
updatePole();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
mem_ = simde_mm_setzero_ps();
|
||||
}
|
||||
|
||||
void init(float sampleRate)
|
||||
{
|
||||
sampleRate_ = sampleRate;
|
||||
updatePole();
|
||||
clear();
|
||||
}
|
||||
|
||||
void setT60(float t60)
|
||||
{
|
||||
t60_ = t60;
|
||||
updatePole();
|
||||
}
|
||||
|
||||
void process(const float* leftBlock, const float* rightBlock, size_t numFrames)
|
||||
{
|
||||
simde__m128 mem = mem_;
|
||||
const simde__m128 pole = simde_mm_load1_ps(&pole_);
|
||||
for (size_t i = 0; i < numFrames; ++i) {
|
||||
float left = leftBlock[i];
|
||||
float right = rightBlock[i];
|
||||
simde__m128 input = simde_mm_setr_ps(left, right, 0.0f, 0.0f);
|
||||
input = simde_mm_mul_ps(input, input);
|
||||
simde__m128 output = simde_mm_add_ps(input, simde_mm_mul_ps(pole, simde_mm_sub_ps(mem, input)));
|
||||
mem = output;
|
||||
}
|
||||
mem_ = mem;
|
||||
}
|
||||
|
||||
simde__m128 getMS() const
|
||||
{
|
||||
return mem_;
|
||||
}
|
||||
|
||||
simde__m128 getRMS() const
|
||||
{
|
||||
return simde_mm_sqrt_ps(mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
void updatePole()
|
||||
{
|
||||
pole_ = std::exp(float(-2.0 * M_PI) / (t60_ * sampleRate_));
|
||||
}
|
||||
|
||||
private:
|
||||
simde__m128 mem_ {};
|
||||
float pole_ {};
|
||||
float t60_ = 300e-3;
|
||||
float sampleRate_ = 44100;
|
||||
};
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "SfizzSettings.h"
|
||||
#include "NativeHelpers.h"
|
||||
#include <memory>
|
||||
#include <cstdlib>
|
||||
|
||||
|
|
@ -36,28 +37,6 @@ static HKEY openRegistryKey()
|
|||
return key;
|
||||
}
|
||||
|
||||
static WCHAR* stringToWideChar(const char *str, int strCch = -1)
|
||||
{
|
||||
unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0);
|
||||
if (strSize == 0)
|
||||
return {};
|
||||
std::unique_ptr<WCHAR[]> strW(new WCHAR[strSize]);
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0)
|
||||
return {};
|
||||
return strW.release();
|
||||
}
|
||||
|
||||
static char* stringToUTF8(const wchar_t *strW, int strWCch = -1)
|
||||
{
|
||||
unsigned strSize = WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, nullptr, 0, nullptr, nullptr);
|
||||
if (strSize == 0)
|
||||
return {};
|
||||
std::unique_ptr<char[]> str(new char[strSize]);
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, str.get(), strSize, nullptr, nullptr) == 0)
|
||||
return {};
|
||||
return str.release();
|
||||
}
|
||||
|
||||
absl::optional<std::string> SfizzSettings::load(const char* name)
|
||||
{
|
||||
std::unique_ptr<WCHAR[]> nameW { stringToWideChar(name) };
|
||||
|
|
@ -112,16 +91,7 @@ bool SfizzSettings::store(const char* name, absl::string_view value)
|
|||
|
||||
static const fs::path getSettingsPath()
|
||||
{
|
||||
fs::path dirPath;
|
||||
const char* env;
|
||||
if ((env = getenv("XDG_CONFIG_HOME")) && env[0] == '/')
|
||||
dirPath = fs::path(env);
|
||||
else if ((env = getenv("HOME")) && env[0] == '/')
|
||||
dirPath = fs::path(env) / ".config";
|
||||
else
|
||||
return {};
|
||||
dirPath /= "SFZTools";
|
||||
dirPath /= "sfizz";
|
||||
const fs::path dirPath = getXdgConfigHome() / "SFZTools" / "sfizz";
|
||||
std::error_code ec;
|
||||
fs::create_directories(dirPath, ec);
|
||||
if (ec)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ set(EDITOR_RESOURCES
|
|||
Fonts/sfizz-misc-icons.ttf
|
||||
Fonts/Roboto-Regular.ttf
|
||||
Themes/Default/theme.xml
|
||||
Themes/Dark/theme.xml
|
||||
PARENT_SCOPE)
|
||||
|
||||
function(copy_editor_resources TARGET SOURCE_DIR DESTINATION_DIR)
|
||||
|
|
@ -73,9 +74,7 @@ add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
|
|||
src/editor/layout/about.hpp
|
||||
src/editor/utility/vstgui_after.h
|
||||
src/editor/utility/vstgui_before.h
|
||||
src/editor/GitBuildId.h
|
||||
${UI_FILES}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/git-build-id/GitBuildId.c")
|
||||
${UI_FILES})
|
||||
add_library(sfizz::editor ALIAS sfizz_editor)
|
||||
target_include_directories(sfizz_editor PUBLIC "src")
|
||||
target_link_libraries(sfizz_editor PUBLIC sfizz::messaging sfizz::plugins-common)
|
||||
|
|
@ -137,11 +136,5 @@ if(NOT CMAKE_CROSSCOMPILING)
|
|||
endforeach()
|
||||
endif()
|
||||
|
||||
# git build identifier
|
||||
add_custom_target(sfizz_editor_git_build_id
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
"-DSOURCE_DIR=${PROJECT_SOURCE_DIR}"
|
||||
"-DOUTPUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/git-build-id/GitBuildId.c"
|
||||
"-P" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/GitBuildID.cmake"
|
||||
BYPRODUCTS "git-build-id/GitBuildId.c")
|
||||
add_dependencies(sfizz_editor sfizz_editor_git_build_id)
|
||||
# Git build identifier
|
||||
target_link_libraries(sfizz_editor PRIVATE sfizz-git-build-id)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ add_library(sfizz_vstgui STATIC EXCLUDE_FROM_ALL
|
|||
"${VSTGUI_BASEDIR}/vstgui/lib/cfileselector.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/cfont.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/cframe.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/cgradient.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/cgradientview.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/cgraphicspath.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/clayeredviewcontainer.cpp"
|
||||
|
|
@ -57,6 +58,7 @@ add_library(sfizz_vstgui STATIC EXCLUDE_FROM_ALL
|
|||
"${VSTGUI_BASEDIR}/vstgui/lib/cview.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/cviewcontainer.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/cvstguitimer.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/events.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/genericstringlistdatabrowsersource.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/pixelbuffer.cpp"
|
||||
"${VSTGUI_BASEDIR}/vstgui/lib/vstguidebug.cpp"
|
||||
|
|
@ -69,6 +71,7 @@ if(WIN32)
|
|||
"${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/d2dgradient.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"
|
||||
|
|
|
|||
2
plugins/editor/external/vstgui4
vendored
2
plugins/editor/external/vstgui4
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 9a116d1812aa274ccf61d68f4c2bae612b3871ad
|
||||
Subproject commit 0db8738ae73ff76842eb0bfe90cdf2039690a5b5
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
# data file for the Fltk User Interface Designer (fluid)
|
||||
version 1.0305
|
||||
version 1.0306
|
||||
header_name {.h}
|
||||
code_name {.cxx}
|
||||
widget_class mainView {open
|
||||
xywh {439 94 800 475} type Double
|
||||
xywh {108 91 800 475} type Double
|
||||
class LogicalGroup visible
|
||||
} {
|
||||
Fl_Box imageContainer_ {
|
||||
image {../resources/background.png} xywh {190 110 600 280}
|
||||
image {../resources/background.png} xywh {5 110 790 285}
|
||||
class Background
|
||||
}
|
||||
Fl_Group {} {
|
||||
|
|
@ -15,53 +15,58 @@ widget_class mainView {open
|
|||
xywh {0 0 800 110}
|
||||
class LogicalGroup
|
||||
} {
|
||||
Fl_Group {} {open
|
||||
Fl_Group {} {
|
||||
xywh {5 4 175 101} box ROUNDED_BOX align 0
|
||||
class RoundedGroup
|
||||
} {
|
||||
Fl_Box {} {
|
||||
comment {tag=kTagAbout} selected
|
||||
comment {tag=kTagAbout}
|
||||
image {../resources/logo_text_shaded.png} xywh {32 9 120 60}
|
||||
class AboutButton
|
||||
}
|
||||
Fl_Button {panelButtons_[kPanelGeneral]} {
|
||||
comment {tag=kTagFirstChangePanel+kPanelGeneral}
|
||||
xywh {36 73 32 32} labelsize 30
|
||||
class HomeButton
|
||||
Fl_Button {panelButtons_[kPanelInfo]} {
|
||||
comment {tag=kTagFirstChangePanel+kPanelInfo}
|
||||
xywh {56 73 32 32} labelsize 30
|
||||
class InfoButton
|
||||
}
|
||||
Fl_Button {panelButtons_[kPanelControls]} {
|
||||
comment {tag=kTagFirstChangePanel+kPanelControls}
|
||||
xywh {76 73 32 32} labelsize 30
|
||||
xywh {97 73 32 32} labelsize 30
|
||||
class CCButton
|
||||
}
|
||||
Fl_Button {panelButtons_[kPanelSettings]} {
|
||||
comment {tag=kTagFirstChangePanel+kPanelSettings}
|
||||
xywh {116 73 32 32} labelsize 30
|
||||
xywh {137 73 32 32} labelsize 30
|
||||
class SettingsButton
|
||||
}
|
||||
Fl_Button {panelButtons_[kPanelGeneral]} {
|
||||
comment {tag=kTagFirstChangePanel+kPanelGeneral}
|
||||
xywh {16 73 32 32} labelsize 30
|
||||
class HomeButton
|
||||
}
|
||||
}
|
||||
Fl_Group {} {open
|
||||
xywh {185 5 380 100} box ROUNDED_BOX
|
||||
Fl_Group {} {
|
||||
xywh {185 5 418 100} box ROUNDED_BOX
|
||||
class RoundedGroup
|
||||
} {
|
||||
Fl_Box {} {
|
||||
label {Separator 1}
|
||||
xywh {195 41 360 5} box BORDER_BOX labeltype NO_LABEL
|
||||
xywh {195 41 395 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
|
||||
xywh {195 73 395 5} box BORDER_BOX labeltype NO_LABEL
|
||||
class HLine
|
||||
}
|
||||
Fl_Box sfzFileLabel_ {
|
||||
label {DefaultInstrument.sfz}
|
||||
comment {tag=kTagLoadSfzFile}
|
||||
xywh {195 13 250 30} labelsize 20 align 20
|
||||
xywh {195 13 320 30} labelsize 20 align 20
|
||||
class ClickableLabel
|
||||
}
|
||||
Fl_Box keyswitchLabel_ {
|
||||
xywh {265 45 290 30} labelsize 20 align 20
|
||||
xywh {265 45 325 35} labelsize 20 align 20
|
||||
class Label
|
||||
}
|
||||
Fl_Box keyswitchBadge_ {
|
||||
|
|
@ -80,17 +85,17 @@ widget_class mainView {open
|
|||
}
|
||||
Fl_Button {} {
|
||||
comment {tag=kTagPreviousSfzFile}
|
||||
xywh {480 18 25 25} labelsize 24
|
||||
xywh {515 18 25 25} labelsize 24
|
||||
class PreviousFileButton
|
||||
}
|
||||
Fl_Button {} {
|
||||
comment {tag=kTagNextSfzFile}
|
||||
xywh {505 18 25 25} labelsize 24
|
||||
xywh {540 18 25 25} labelsize 24
|
||||
class NextFileButton
|
||||
}
|
||||
Fl_Button fileOperationsMenu_ {
|
||||
comment {tag=kTagFileOperations}
|
||||
xywh {530 18 25 25} labelsize 24
|
||||
xywh {565 18 25 25} labelsize 24
|
||||
class ChevronDropDown
|
||||
}
|
||||
Fl_Box infoVoicesLabel_ {
|
||||
|
|
@ -99,30 +104,30 @@ widget_class mainView {open
|
|||
}
|
||||
Fl_Box {} {
|
||||
label {Max:}
|
||||
xywh {315 78 40 25} labelsize 12 align 24
|
||||
xywh {332 78 40 25} labelsize 12 align 24
|
||||
class Label
|
||||
}
|
||||
Fl_Box numVoicesLabel_ {
|
||||
xywh {360 78 35 25} labelsize 12 align 16
|
||||
xywh {378 78 35 25} labelsize 12 align 16
|
||||
class Label
|
||||
}
|
||||
Fl_Box {} {
|
||||
label {Memory:}
|
||||
xywh {425 78 60 25} labelsize 12 align 24
|
||||
xywh {459 78 60 25} labelsize 12 align 24
|
||||
class Label
|
||||
}
|
||||
Fl_Box memoryLabel_ {
|
||||
xywh {490 78 60 25} labelsize 12 align 16
|
||||
xywh {525 78 60 25} labelsize 12 align 16
|
||||
class Label
|
||||
}
|
||||
Fl_Button numVoicesSlider_ {
|
||||
comment {tag=kTagSetNumVoices}
|
||||
xywh {395 82 20 20} labelsize 16
|
||||
xywh {420 82 20 20} labelsize 16
|
||||
class ChevronValueDropDown
|
||||
}
|
||||
}
|
||||
Fl_Group {} {open
|
||||
xywh {570 5 225 100} box ROUNDED_BOX
|
||||
Fl_Group {} {
|
||||
xywh {608 5 187 100} box ROUNDED_BOX
|
||||
class RoundedGroup
|
||||
} {
|
||||
Fl_Dial {} {
|
||||
|
|
@ -134,30 +139,34 @@ widget_class mainView {open
|
|||
xywh {610 70 60 5} labelsize 12 hide
|
||||
class ValueLabel
|
||||
}
|
||||
Fl_Box {} {
|
||||
xywh {745 20 35 55} box BORDER_BOX
|
||||
class VMeter
|
||||
}
|
||||
Fl_Box volumeCCKnob_ {
|
||||
label Volume
|
||||
comment {tag=kTagSetCCVolume}
|
||||
xywh {580 10 70 90} box BORDER_BOX labelsize 12 align 17
|
||||
xywh {614 10 70 90} box BORDER_BOX labelsize 12 align 17
|
||||
class KnobCCBox
|
||||
}
|
||||
Fl_Box panCCKnob_ {
|
||||
label Pan
|
||||
comment {tag=kTagSetCCPan}
|
||||
xywh {655 10 70 90} box BORDER_BOX labelsize 12 align 17
|
||||
xywh {691 10 70 90} box BORDER_BOX labelsize 12 align 17
|
||||
class KnobCCBox
|
||||
}
|
||||
Fl_Box leftMeter_ {
|
||||
xywh {767 10 9 90} box BORDER_BOX
|
||||
class VMeter
|
||||
}
|
||||
Fl_Box rightMeter_ {
|
||||
xywh {779 10 9 90} box BORDER_BOX
|
||||
class VMeter
|
||||
}
|
||||
}
|
||||
}
|
||||
Fl_Group {subPanels_[kPanelGeneral]} {
|
||||
xywh {5 110 791 285} hide
|
||||
Fl_Group {subPanels_[kPanelInfo]} {open
|
||||
xywh {5 110 790 285} hide
|
||||
class LogicalGroup
|
||||
} {
|
||||
Fl_Group {} {open
|
||||
xywh {5 110 175 280} box ROUNDED_BOX
|
||||
xywh {5 110 790 285} box ROUNDED_BOX
|
||||
class RoundedGroup
|
||||
} {
|
||||
Fl_Box {} {
|
||||
|
|
@ -226,8 +235,8 @@ widget_class mainView {open
|
|||
} {}
|
||||
}
|
||||
}
|
||||
Fl_Group {subPanels_[kPanelSettings]} {open
|
||||
xywh {5 109 790 316}
|
||||
Fl_Group {subPanels_[kPanelSettings]} {
|
||||
xywh {5 110 790 285}
|
||||
class LogicalGroup
|
||||
} {
|
||||
Fl_Group {} {
|
||||
|
|
@ -357,8 +366,8 @@ widget_class mainView {open
|
|||
}
|
||||
}
|
||||
Fl_Group {} {
|
||||
label Theme open
|
||||
xywh {40 270 105 100} box ROUNDED_BOX labelsize 12 align 17 hide
|
||||
label Appearance open
|
||||
xywh {40 270 105 100} box ROUNDED_BOX labelsize 12 align 17
|
||||
class TitleGroup
|
||||
} {
|
||||
Fl_Spinner themeMenu_ {
|
||||
|
|
@ -366,10 +375,19 @@ widget_class mainView {open
|
|||
xywh {60 330 65 25} labelsize 12 textsize 12
|
||||
class OptionMenu
|
||||
}
|
||||
Fl_Box {} {
|
||||
label Theme
|
||||
xywh {50 295 80 25} labelsize 12
|
||||
class ValueLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
Fl_Box piano_ {
|
||||
xywh {5 400 790 70} labelsize 12
|
||||
class Piano
|
||||
}
|
||||
Fl_Group {subPanels_[kPanelGeneral]} {open selected
|
||||
xywh {5 110 790 285} hide
|
||||
class LogicalGroup
|
||||
} {}
|
||||
}
|
||||
|
|
|
|||
40
plugins/editor/resources/Themes/Dark/theme.xml
Normal file
40
plugins/editor/resources/Themes/Dark/theme.xml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0"?>
|
||||
<sfizz-theme author="Andrea Zanellato" version="1.0.0">
|
||||
<color name="frameBackground">#121212</color>
|
||||
<palette name="normal">
|
||||
<color name="boxBackground">#1d1d1d</color>
|
||||
<color name="text">#e3e3e3</color>
|
||||
<color name="inactiveText">#a2a2a2</color>
|
||||
<color name="highlightedText">#e9e9e9</color>
|
||||
<color name="titleBoxText">#e3e3e3</color>
|
||||
<color name="titleBoxBackground">#2d2d2d</color>
|
||||
<color name="valueText">#e3e3e3</color>
|
||||
<color name="valueBackground">#121212</color>
|
||||
<color name="icon">#e3e3e3</color>
|
||||
<color name="iconHighlight">#ff6000</color>
|
||||
<color name="knobActiveTrack">#006b0b</color>
|
||||
<color name="knobInactiveTrack">#6f6f6f</color>
|
||||
<color name="knobLineIndicator">#ffffff</color>
|
||||
<color name="knobText">#ffffff</color>
|
||||
<color name="knobLabelText">#ffffff</color>
|
||||
<color name="knobLabelBackground">#006b0b</color>
|
||||
</palette>
|
||||
<palette name="inverted">
|
||||
<color name="boxBackground">#2d2d2d</color>
|
||||
<color name="text">#9e9e9e</color>
|
||||
<color name="inactiveText">#a2a2a2</color>
|
||||
<color name="highlightedText">#e9e9e9</color>
|
||||
<color name="titleBoxText">#9e9e9e</color>
|
||||
<color name="titleBoxBackground">#2d2d2d</color>
|
||||
<color name="valueText">#9e9e9e</color>
|
||||
<color name="valueBackground">#121212</color>
|
||||
<color name="icon">#9e9e9e</color>
|
||||
<color name="iconHighlight">#ff6000</color>
|
||||
<color name="knobActiveTrack">#006b0b</color>
|
||||
<color name="knobInactiveTrack">#6f6f6f</color>
|
||||
<color name="knobLineIndicator">#ffffff</color>
|
||||
<color name="knobText">#ffffff</color>
|
||||
<color name="knobLabelText">#ffffff</color>
|
||||
<color name="knobLabelBackground">#006b0b</color>
|
||||
</palette>
|
||||
</sfizz-theme>
|
||||
|
|
@ -38,6 +38,9 @@ enum class EditId : int {
|
|||
CC_RANGE(ControllerDefault),
|
||||
CC_RANGE(ControllerLabel),
|
||||
//
|
||||
LeftLevel,
|
||||
RightLevel,
|
||||
//
|
||||
UINumCurves,
|
||||
UINumMasters,
|
||||
UINumGroups,
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ struct Editor::Impl : EditorController::Receiver,
|
|||
|
||||
enum {
|
||||
kPanelGeneral,
|
||||
kPanelInfo,
|
||||
kPanelControls,
|
||||
kPanelSettings,
|
||||
kNumPanels,
|
||||
|
|
@ -153,16 +154,19 @@ struct Editor::Impl : EditorController::Receiver,
|
|||
SKnobCCBox* volumeCCKnob_ = nullptr;
|
||||
SKnobCCBox* panCCKnob_ = nullptr;
|
||||
|
||||
SLevelMeter* leftMeter_ = nullptr;
|
||||
SLevelMeter* rightMeter_ = nullptr;
|
||||
|
||||
SAboutDialog* aboutDialog_ = nullptr;
|
||||
|
||||
SharedPointer<CBitmap> backgroundBitmap_;
|
||||
SharedPointer<CBitmap> defaultBackgroundBitmap_;
|
||||
|
||||
CControl* getSecondaryCCControl(unsigned cc)
|
||||
SKnobCCBox* getSecondaryCCKnob(unsigned cc)
|
||||
{
|
||||
switch (cc) {
|
||||
case 7: return volumeCCKnob_ ? volumeCCKnob_->getControl() : nullptr;
|
||||
case 10: return panCCKnob_ ? panCCKnob_->getControl() : nullptr;
|
||||
case 7: return volumeCCKnob_ ? volumeCCKnob_ : nullptr;
|
||||
case 10: return panCCKnob_ ? panCCKnob_ : nullptr;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -504,6 +508,20 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
|||
updateBackgroundImage(value.c_str());
|
||||
}
|
||||
break;
|
||||
case EditId::LeftLevel:
|
||||
{
|
||||
const float value = v.to_float();
|
||||
if (SLevelMeter* meter = leftMeter_)
|
||||
meter->setValue(value);
|
||||
}
|
||||
break;
|
||||
case EditId::RightLevel:
|
||||
{
|
||||
const float value = v.to_float();
|
||||
if (SLevelMeter* meter = rightMeter_)
|
||||
meter->setValue(value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (editIdIsKey(id)) {
|
||||
const int key = keyForEditId(id);
|
||||
|
|
@ -703,11 +721,15 @@ void Editor::Impl::createFrameContents()
|
|||
lbl->setFont(font);
|
||||
return lbl;
|
||||
};
|
||||
auto createVMeter = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
|
||||
// TODO the volume meter...
|
||||
CViewContainer* container = new CViewContainer(bounds);
|
||||
container->setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
return container;
|
||||
auto createVMeter = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
|
||||
SLevelMeter* meter = new SLevelMeter(bounds);
|
||||
meter->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
meter->setNormalFillColor(CColor(0x00, 0xaa, 0x11));
|
||||
meter->setDangerFillColor(CColor(0xaa, 0x00, 0x00));
|
||||
OnThemeChanged.push_back([meter, palette]() {
|
||||
meter->setBackColor(palette->knobInactiveTrack);
|
||||
});
|
||||
return meter;
|
||||
};
|
||||
#if 0
|
||||
auto createButton = [this](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) {
|
||||
|
|
@ -796,6 +818,9 @@ void Editor::Impl::createFrameContents()
|
|||
auto createHomeButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) {
|
||||
return createGlyphButton(u8"\ue1d6", bounds, tag, fontsize);
|
||||
};
|
||||
auto createInfoButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) {
|
||||
return createGlyphButton(u8"\ue1e7", bounds, tag, fontsize);
|
||||
};
|
||||
auto createCCButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) {
|
||||
// return createGlyphButton(u8"\ue240", bounds, tag, fontsize);
|
||||
return createGlyphButton(u8"\ue253", bounds, tag, fontsize);
|
||||
|
|
@ -881,6 +906,10 @@ void Editor::Impl::createFrameContents()
|
|||
box->setCCLabelFont(font);
|
||||
OnThemeChanged.push_back([box, palette]() {
|
||||
box->setNameLabelFontColor(palette->knobText);
|
||||
box->setValueEditFontColor(palette->knobText);
|
||||
auto shadingColor = palette->knobText;
|
||||
shadingColor.alpha = 70;
|
||||
box->setShadingRectangleColor(shadingColor);
|
||||
box->setCCLabelFontColor(palette->knobLabelText);
|
||||
box->setCCLabelBackColor(palette->knobLabelBackground);
|
||||
box->setKnobFontColor(palette->knobText);
|
||||
|
|
@ -888,10 +917,6 @@ void Editor::Impl::createFrameContents()
|
|||
box->setKnobActiveTrackColor(palette->knobActiveTrack);
|
||||
box->setKnobInactiveTrackColor(palette->knobInactiveTrack);
|
||||
});
|
||||
box->setValueToStringFunction([](float value, std::string& text) -> bool {
|
||||
text = std::to_string(std::lround(value * 127));
|
||||
return true;
|
||||
});
|
||||
return box;
|
||||
};
|
||||
auto createBackground = [&background](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
|
||||
|
|
@ -907,6 +932,10 @@ void Editor::Impl::createFrameContents()
|
|||
panel->setCCLabelFont(font);
|
||||
OnThemeChanged.push_back([panel, palette]() {
|
||||
panel->setNameLabelFontColor(palette->knobText);
|
||||
panel->setValueEditFontColor(palette->knobText);
|
||||
auto shadingColor = palette->knobText;
|
||||
shadingColor.alpha = 70;
|
||||
panel->setShadingRectangleColor(shadingColor);
|
||||
panel->setCCLabelFontColor(palette->knobLabelText);
|
||||
panel->setCCLabelBackColor(palette->knobLabelBackground);
|
||||
panel->setKnobFontColor(palette->knobText);
|
||||
|
|
@ -1640,7 +1669,7 @@ void Editor::Impl::updateCCValue(unsigned cc, float value)
|
|||
if (SControlsPanel* panel = controlsPanel_)
|
||||
panel->setControlValue(cc, value);
|
||||
|
||||
if (CControl* other = getSecondaryCCControl(cc)) {
|
||||
if (SKnobCCBox* other = getSecondaryCCKnob(cc)) {
|
||||
other->setValue(value);
|
||||
other->invalid();
|
||||
}
|
||||
|
|
@ -1651,7 +1680,7 @@ void Editor::Impl::updateCCDefaultValue(unsigned cc, float value)
|
|||
if (SControlsPanel* panel = controlsPanel_)
|
||||
panel->setControlDefaultValue(cc, value);
|
||||
|
||||
if (CControl* other = getSecondaryCCControl(cc))
|
||||
if (SKnobCCBox* other = getSecondaryCCKnob(cc))
|
||||
other->setDefaultValue(value);
|
||||
}
|
||||
|
||||
|
|
@ -1977,4 +2006,6 @@ void Editor::Impl::onThemeChanged()
|
|||
if (function)
|
||||
function();
|
||||
}
|
||||
if (CFrame* frame = frame_)
|
||||
frame->invalid();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "vstgui/lib/cvstguitimer.h"
|
||||
#include "vstgui/lib/cframe.h"
|
||||
#include "utility/vstgui_after.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
|
||||
///
|
||||
SBoxContainer::SBoxContainer(const CRect& size)
|
||||
|
|
@ -582,7 +583,7 @@ void SStyledKnob::draw(CDrawContext* dc)
|
|||
dc->drawLine(p1, p2);
|
||||
}
|
||||
|
||||
if (valueToStringFunction_ && fontColor_.alpha > 0) {
|
||||
if (valueToStringFunction_ && fontColor_.alpha > 0 && !hideValue_) {
|
||||
std::string text;
|
||||
if (valueToStringFunction_(getValue(), text)) {
|
||||
dc->setFont(font_);
|
||||
|
|
@ -592,12 +593,29 @@ void SStyledKnob::draw(CDrawContext* dc)
|
|||
}
|
||||
}
|
||||
|
||||
void CFilledRect::draw(CDrawContext* dc)
|
||||
{
|
||||
CRect bounds = getViewSize();
|
||||
dc->setFillColor(color_);
|
||||
bool isRounded = radius_ > 0.0;
|
||||
if (isRounded) {
|
||||
auto roundRect = owned(dc->createRoundRectGraphicsPath(bounds, radius_));
|
||||
dc->drawGraphicsPath(roundRect, CDrawContext::kPathFilled);
|
||||
} else {
|
||||
dc->drawRect(bounds, kDrawFilled);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
SKnobCCBox::SKnobCCBox(const CRect& size, IControlListener* listener, int32_t tag)
|
||||
: CViewContainer(size),
|
||||
label_(makeOwned<CTextLabel>(CRect())),
|
||||
valueEdit_(makeOwned<CTextEdit>(CRect(), listener, tag)),
|
||||
knob_(makeOwned<SStyledKnob>(CRect(), listener, tag)),
|
||||
ccLabel_(makeOwned<CTextLabel>(CRect()))
|
||||
ccLabel_(makeOwned<CTextLabel>(CRect())),
|
||||
shadingRectangle_(makeOwned<CFilledRect>(CRect())),
|
||||
menuEntry_(makeOwned<CMenuItem>("Use HDCC", tag)),
|
||||
menuListener_(owned(new MenuListener(*this)))
|
||||
{
|
||||
setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
|
||||
|
|
@ -614,17 +632,129 @@ SKnobCCBox::SKnobCCBox(const CRect& size, IControlListener* listener, int32_t ta
|
|||
ccLabel_->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
ccLabel_->setFontColor(CColor(0xff, 0xff, 0xff));
|
||||
|
||||
valueEdit_->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
valueEdit_->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
valueEdit_->setFontColor(CColor(0x00, 0x00, 0x00, 0xff));
|
||||
valueEdit_->registerViewListener(this);
|
||||
setHDMode(false);
|
||||
valueEdit_->setVisible(false);
|
||||
|
||||
shadingRectangle_->setVisible(false);
|
||||
|
||||
addView(label_);
|
||||
label_->remember();
|
||||
addView(knob_);
|
||||
knob_->remember();
|
||||
addView(shadingRectangle_);
|
||||
shadingRectangle_->remember();
|
||||
addView(valueEdit_);
|
||||
valueEdit_->remember();
|
||||
addView(ccLabel_);
|
||||
ccLabel_->remember();
|
||||
|
||||
updateViewColors();
|
||||
updateViewSizes();
|
||||
}
|
||||
|
||||
SKnobCCBox::~SKnobCCBox()
|
||||
{
|
||||
valueEdit_->unregisterViewListener(this);
|
||||
}
|
||||
|
||||
void SKnobCCBox::setHDMode(bool mode)
|
||||
{
|
||||
if (mode) {
|
||||
auto valueToString = [](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool {
|
||||
std::string s = std::to_string(value + 0.005f);
|
||||
text = s.substr(0, 4);
|
||||
return true;
|
||||
};
|
||||
knob_->setValueToStringFunction([valueToString](float value, std::string& text) {
|
||||
return valueToString(value, text, nullptr);
|
||||
});
|
||||
valueEdit_->setValueToStringFunction2(valueToString);
|
||||
|
||||
valueEdit_->setStringToValueFunction([](UTF8StringPtr txt, float& result, CTextEdit*) -> bool {
|
||||
float value;
|
||||
if (absl::SimpleAtof(txt, &value)) {
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
menuEntry_->setTitle("Use low-res. CC");
|
||||
} else {
|
||||
auto valueToString = [](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool {
|
||||
text = std::to_string(std::lround(value * 127));
|
||||
return true;
|
||||
};
|
||||
knob_->setValueToStringFunction([valueToString](float value, std::string& text) {
|
||||
return valueToString(value, text, nullptr);
|
||||
});
|
||||
valueEdit_->setValueToStringFunction2(valueToString);
|
||||
|
||||
valueEdit_->setStringToValueFunction([](UTF8StringPtr txt, float& result, CTextEdit*) -> bool {
|
||||
float value;
|
||||
if (absl::SimpleAtof(txt, &value)) {
|
||||
result = value / 127.0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
menuEntry_->setTitle("Use high-res. CC");
|
||||
}
|
||||
|
||||
hdMode_ = mode;
|
||||
valueEdit_->setValue(valueEdit_->getValue());
|
||||
invalid();
|
||||
}
|
||||
|
||||
CMouseEventResult SKnobCCBox::onMouseDown(CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (buttons.isRightButton()) {
|
||||
CFrame* frame = getFrame();
|
||||
CPoint frameWhere = where;
|
||||
frameWhere.offset(-getViewSize().left, -getViewSize().top);
|
||||
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));
|
||||
menu->addEntry(self->menuEntry_);
|
||||
self->menuEntry_->remember(); // above call does not increment refcount
|
||||
|
||||
menu->setFont(self->getValueEditFont());
|
||||
menu->setFontColor(self->getValueEditFontColor());
|
||||
menu->setBackColor(self->getValueEditBackColor());
|
||||
menu->popup(frame, frameWhere);
|
||||
}
|
||||
});
|
||||
return kMouseEventHandled;
|
||||
} else if (buttons.isDoubleClick() && !valueEdit_->isVisible()) {
|
||||
valueEdit_->setVisible(true);
|
||||
shadingRectangle_->setVisible(true);
|
||||
knob_->setHideValue(true);
|
||||
valueEdit_->takeFocus();
|
||||
invalid();
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
return CViewContainer::onMouseDown(where, buttons);
|
||||
}
|
||||
|
||||
void SKnobCCBox::viewLostFocus (CView* view)
|
||||
{
|
||||
if (view == valueEdit_.get()) {
|
||||
shadingRectangle_->setVisible(false);
|
||||
valueEdit_->setVisible(false);
|
||||
knob_->setHideValue(false);
|
||||
invalid();
|
||||
}
|
||||
}
|
||||
|
||||
void SKnobCCBox::setHue(float hue)
|
||||
{
|
||||
hue_ = hue;
|
||||
|
|
@ -637,12 +767,33 @@ void SKnobCCBox::setNameLabelFont(CFontRef font)
|
|||
updateViewSizes();
|
||||
}
|
||||
|
||||
void SKnobCCBox::setValueEditFont(CFontRef font)
|
||||
{
|
||||
label_->setFont(font);
|
||||
updateViewSizes();
|
||||
}
|
||||
|
||||
void SKnobCCBox::setCCLabelFont(CFontRef font)
|
||||
{
|
||||
ccLabel_->setFont(font);
|
||||
updateViewSizes();
|
||||
}
|
||||
|
||||
void SKnobCCBox::setValue(float value)
|
||||
{
|
||||
float oldValue = knob_->getValue();
|
||||
knob_->setValue(value);
|
||||
valueEdit_->setValue(value);
|
||||
if (value != oldValue)
|
||||
invalid();
|
||||
}
|
||||
|
||||
void SKnobCCBox::setDefaultValue(float value)
|
||||
{
|
||||
knob_->setDefaultValue(value);
|
||||
valueEdit_->setDefaultValue(value);
|
||||
}
|
||||
|
||||
void SKnobCCBox::updateViewSizes()
|
||||
{
|
||||
const CRect size = getViewSize();
|
||||
|
|
@ -650,19 +801,30 @@ void SKnobCCBox::updateViewSizes()
|
|||
|
||||
const CFontRef nameFont = label_->getFont();
|
||||
const CFontRef ccFont = ccLabel_->getFont();
|
||||
const CFontRef valueFont = valueEdit_->getFont();
|
||||
|
||||
nameLabelSize_ = CRect(0.0, 0.0, size.getWidth(), nameFont->getSize() + 2 * ypad);
|
||||
ccLabelSize_ = CRect(0.0, size.getHeight() - ccFont->getSize() - 2 * ypad, size.getWidth(), size.getHeight());
|
||||
knobSize_ = CRect(0.0, nameLabelSize_.bottom, size.getWidth(), ccLabelSize_.top);
|
||||
valueEditSize_ = CRect(
|
||||
size.getWidth() / 2 - valueFont->getSize(),
|
||||
size.getHeight() / 2 - valueFont->getSize() / 2,
|
||||
size.getWidth() / 2 + valueFont->getSize(),
|
||||
size.getHeight() / 2 + valueFont->getSize() / 2
|
||||
);
|
||||
|
||||
// remove knob side areas
|
||||
CCoord side = std::max(0.0, knobSize_.getWidth() - knobSize_.getHeight());
|
||||
knobSize_.extend(-0.5 * side, 0.0);
|
||||
shadingRectangleSize_ = knobSize_;
|
||||
shadingRectangleSize_.bottom -= ypad;
|
||||
|
||||
//
|
||||
label_->setViewSize(nameLabelSize_);
|
||||
knob_->setViewSize(knobSize_);
|
||||
ccLabel_->setViewSize(ccLabelSize_);
|
||||
valueEdit_->setViewSize(valueEditSize_);
|
||||
shadingRectangle_->setViewSize(shadingRectangleSize_);
|
||||
|
||||
invalid();
|
||||
}
|
||||
|
|
@ -746,11 +908,6 @@ SControlsPanel::ControlSlot* SControlsPanel::getOrCreateSlot(uint32_t index)
|
|||
slot->box = box;
|
||||
slot->box->setCCLabelText(("CC " + std::to_string(index)).c_str());
|
||||
|
||||
slot->box->setValueToStringFunction([](float value, std::string& text) -> bool {
|
||||
text = std::to_string(std::lround(value * 127));
|
||||
return true;
|
||||
});
|
||||
|
||||
syncSlotStyle(index);
|
||||
|
||||
return slot;
|
||||
|
|
@ -760,10 +917,9 @@ void SControlsPanel::setControlValue(uint32_t index, float value)
|
|||
{
|
||||
ControlSlot* slot = getOrCreateSlot(index);
|
||||
SKnobCCBox* box = slot->box;
|
||||
auto* control = box->getControl();
|
||||
float oldValue = control->getValue();
|
||||
control->setValue(value);
|
||||
if (control->getValue() != oldValue)
|
||||
float oldValue = box->getValue();
|
||||
box->setValue(value);
|
||||
if (box->getValue() != oldValue)
|
||||
box->invalid();
|
||||
}
|
||||
|
||||
|
|
@ -771,7 +927,7 @@ void SControlsPanel::setControlDefaultValue(uint32_t index, float value)
|
|||
{
|
||||
ControlSlot* slot = getOrCreateSlot(index);
|
||||
SKnobCCBox* box = slot->box;
|
||||
box->getControl()->setDefaultValue(value);
|
||||
box->setDefaultValue(value);
|
||||
}
|
||||
|
||||
void SControlsPanel::setControlLabelText(uint32_t index, UTF8StringPtr text)
|
||||
|
|
@ -788,12 +944,14 @@ void SControlsPanel::setControlLabelText(uint32_t index, UTF8StringPtr text)
|
|||
void SControlsPanel::setNameLabelFont(CFontRef font)
|
||||
{
|
||||
slots_[0]->box->setNameLabelFont(font);
|
||||
slots_[0]->box->setValueEditFont(font);
|
||||
syncAllSlotStyles();
|
||||
}
|
||||
|
||||
void SControlsPanel::setNameLabelFontColor(CColor color)
|
||||
{
|
||||
slots_[0]->box->setNameLabelFontColor(color);
|
||||
slots_[0]->box->setValueEditFontColor(color);
|
||||
syncAllSlotStyles();
|
||||
}
|
||||
|
||||
|
|
@ -815,6 +973,24 @@ void SControlsPanel::setCCLabelFontColor(CColor color)
|
|||
syncAllSlotStyles();
|
||||
}
|
||||
|
||||
void SControlsPanel::setValueEditBackColor(CColor color)
|
||||
{
|
||||
slots_[0]->box->setValueEditBackColor(color);
|
||||
syncAllSlotStyles();
|
||||
}
|
||||
|
||||
void SControlsPanel::setShadingRectangleColor(CColor color)
|
||||
{
|
||||
slots_[0]->box->setShadingRectangleColor(color);
|
||||
syncAllSlotStyles();
|
||||
}
|
||||
|
||||
void SControlsPanel::setValueEditFontColor(CColor color)
|
||||
{
|
||||
slots_[0]->box->setValueEditFontColor(color);
|
||||
syncAllSlotStyles();
|
||||
}
|
||||
|
||||
void SControlsPanel::setKnobActiveTrackColor(CColor color)
|
||||
{
|
||||
slots_[0]->box->setKnobActiveTrackColor(color);
|
||||
|
|
@ -947,6 +1123,11 @@ void SControlsPanel::syncSlotStyle(uint32_t index)
|
|||
cur->setNameLabelFont(ref->getNameLabelFont());
|
||||
cur->setNameLabelFontColor(ref->getNameLabelFontColor());
|
||||
|
||||
cur->setValueEditFont(ref->getValueEditFont());
|
||||
cur->setValueEditFontColor(ref->getValueEditFontColor());
|
||||
|
||||
cur->setShadingRectangleColor(ref->getShadingRectangleColor());
|
||||
|
||||
cur->setCCLabelFont(ref->getCCLabelFont());
|
||||
cur->setCCLabelFontColor(ref->getCCLabelFontColor());
|
||||
cur->setCCLabelBackColor(ref->getCCLabelBackColor());
|
||||
|
|
@ -979,6 +1160,113 @@ void SControlsPanel::ControlSlotListener::controlEndEdit(CControl* pControl)
|
|||
panel_->EndEditFunction(pControl->getTag());
|
||||
}
|
||||
|
||||
///
|
||||
SLevelMeter::SLevelMeter(const CRect& size)
|
||||
: CView(size)
|
||||
{
|
||||
}
|
||||
|
||||
void SLevelMeter::setValue(float value)
|
||||
{
|
||||
if (value_ == value)
|
||||
return;
|
||||
|
||||
value_ = value;
|
||||
|
||||
// instantiate the timer lazily
|
||||
if (!timer_) {
|
||||
const uint32_t interval = 10;
|
||||
timer_ = makeOwned<CVSTGUITimer>(
|
||||
[this](CVSTGUITimer* timer) {
|
||||
timer->stop();
|
||||
timerArmed_ = false;
|
||||
invalid();
|
||||
}, interval, false);
|
||||
}
|
||||
|
||||
// defer the update, but do not rearm the timer
|
||||
if (!timerArmed_) {
|
||||
timerArmed_ = true;
|
||||
timer_->start();
|
||||
}
|
||||
}
|
||||
|
||||
void SLevelMeter::draw(CDrawContext* dc)
|
||||
{
|
||||
float dbValue = 20.0f * std::log10(value_);
|
||||
float fill = (dbValue - dbMin_) / (dbMax_ - dbMin_);
|
||||
fill = (fill < 0.0f) ? 0.0f : fill;
|
||||
fill = (fill > 1.0f) ? 1.0f : fill;
|
||||
|
||||
CRect largeBounds = getViewSize();
|
||||
CRect fillBounds = largeBounds;
|
||||
fillBounds.top = largeBounds.bottom - fill * largeBounds.getHeight();
|
||||
|
||||
const CColor safeColor = safeFillColor_;
|
||||
const CColor dangerColor = dangerFillColor_;
|
||||
|
||||
CColor fillColor;
|
||||
if (safeColor == dangerColor) {
|
||||
fillColor = safeColor;
|
||||
}
|
||||
else {
|
||||
float thres = dangerThreshold_;
|
||||
float mix = (fill - thres) / (1.0f - thres);
|
||||
mix = (mix < 0.0f) ? 0.0f : mix;
|
||||
|
||||
CCoord safeH, safeS, safeV, safeA;
|
||||
CCoord dangerH, dangerS, dangerV, dangerA;
|
||||
safeColor.toHSV(safeH, safeS, safeV);
|
||||
dangerColor.toHSV(dangerH, dangerS, dangerV);
|
||||
safeA = safeColor.alpha / 255.0;
|
||||
dangerA = dangerColor.alpha / 255.0;
|
||||
|
||||
CCoord H, S, V, A;
|
||||
H = safeH + mix * (dangerH - safeH);
|
||||
S = safeS + mix * (dangerS - safeS);
|
||||
V = safeV + mix * (dangerV - safeV);
|
||||
A = safeA + mix * (dangerA - safeA);
|
||||
|
||||
fillColor.fromHSV(H, S, V);
|
||||
fillColor.alpha = static_cast<uint8_t>(A * 255.0);
|
||||
}
|
||||
|
||||
CCoord radius = radius_;
|
||||
bool isRounded = radius > 0.0;
|
||||
|
||||
dc->setDrawMode(isRounded ? kAntiAliasing : kAliasing);
|
||||
|
||||
SharedPointer<CGraphicsPath> largeRoundRect;
|
||||
SharedPointer<CGraphicsPath> fillRoundRect;
|
||||
|
||||
if (isRounded) {
|
||||
largeRoundRect = owned(dc->createRoundRectGraphicsPath(largeBounds, radius));
|
||||
fillRoundRect = owned(dc->createRoundRectGraphicsPath(fillBounds, radius));
|
||||
}
|
||||
|
||||
if (backColor_.alpha > 0) {
|
||||
dc->setFillColor(backColor_);
|
||||
if (!isRounded)
|
||||
dc->drawRect(largeBounds, kDrawFilled);
|
||||
else
|
||||
dc->drawGraphicsPath(largeRoundRect, CDrawContext::kPathFilled);
|
||||
}
|
||||
|
||||
dc->setFrameColor(frameColor_);
|
||||
dc->setFillColor(fillColor);
|
||||
|
||||
if (!isRounded) {
|
||||
if (fill > 0)
|
||||
dc->drawRect(fillBounds, kDrawFilled);
|
||||
dc->drawRect(largeBounds);
|
||||
}
|
||||
else {
|
||||
if (fill > 0 && fillBounds.getHeight() >= radius)
|
||||
dc->drawGraphicsPath(fillRoundRect, CDrawContext::kPathFilled);
|
||||
dc->drawGraphicsPath(largeRoundRect, CDrawContext::kPathStroked);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
SPlaceHolder::SPlaceHolder(const CRect& size, const CColor& color)
|
||||
: CView(size), color_(color)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "vstgui/lib/controls/cslider.h"
|
||||
#include "vstgui/lib/controls/cknob.h"
|
||||
#include "vstgui/lib/controls/ctextlabel.h"
|
||||
#include "vstgui/lib/controls/ctextedit.h"
|
||||
#include "vstgui/lib/controls/cbuttons.h"
|
||||
#include "vstgui/lib/controls/coptionmenu.h"
|
||||
#include "vstgui/lib/controls/cscrollbar.h"
|
||||
|
|
@ -233,6 +234,9 @@ public:
|
|||
using ValueToStringFunction = std::function<bool(float value, std::string& result)>;
|
||||
void setValueToStringFunction(ValueToStringFunction func);
|
||||
|
||||
void setHideValue(bool hide) { hideValue_ = hide; invalid(); }
|
||||
bool getHideValue() const { return hideValue_; }
|
||||
|
||||
CLASS_METHODS(SStyledKnob, CKnobBase)
|
||||
protected:
|
||||
void draw(CDrawContext* dc) override;
|
||||
|
|
@ -241,6 +245,7 @@ private:
|
|||
CColor activeTrackColor_;
|
||||
CColor inactiveTrackColor_;
|
||||
CColor lineIndicatorColor_;
|
||||
bool hideValue_ { false };
|
||||
|
||||
SharedPointer<CFontDesc> font_ = kNormalFont;
|
||||
CColor fontColor_ { 0x00, 0x00, 0x00 };
|
||||
|
|
@ -248,12 +253,35 @@ private:
|
|||
ValueToStringFunction valueToStringFunction_;
|
||||
};
|
||||
|
||||
class CFilledRect : public CView
|
||||
{
|
||||
public:
|
||||
explicit CFilledRect(const CRect& size)
|
||||
: CView(size) {}
|
||||
|
||||
void setRadius(CCoord radius) { radius_ = radius; invalid(); }
|
||||
CCoord getRadius() const { return radius_; }
|
||||
|
||||
void setColor(CColor color){ color_ = color; invalid(); }
|
||||
CColor getColor() { return color_; }
|
||||
protected:
|
||||
void draw(CDrawContext* dc) override;
|
||||
private:
|
||||
CCoord radius_ { 5.0 };
|
||||
CColor color_ { 0, 0, 0, 70 };
|
||||
};
|
||||
|
||||
///
|
||||
class SKnobCCBox : public CViewContainer {
|
||||
class SKnobCCBox : public CViewContainer, ViewListenerAdapter {
|
||||
public:
|
||||
SKnobCCBox(const CRect& size, IControlListener* listener, int32_t tag);
|
||||
~SKnobCCBox();
|
||||
void setHue(float hue);
|
||||
SStyledKnob* getControl() const { return knob_; }
|
||||
|
||||
float getValue() const { return knob_->getValue(); }
|
||||
float getDefaultValue() const { return knob_->getDefaultValue(); }
|
||||
void setValue(float value);
|
||||
void setDefaultValue(float value);
|
||||
|
||||
void setNameLabelText(const UTF8String& name) { label_->setText(name); label_->invalid(); }
|
||||
void setCCLabelText(const UTF8String& name) { ccLabel_->setText(name); ccLabel_->invalid(); }
|
||||
|
|
@ -264,6 +292,18 @@ public:
|
|||
void setNameLabelFontColor(CColor color) { label_->setFontColor(color); label_->invalid(); }
|
||||
CColor getNameLabelFontColor() const { return label_->getFontColor(); }
|
||||
|
||||
void setValueEditFont(CFontRef font);
|
||||
CFontRef getValueEditFont() const { return label_->getFont(); }
|
||||
|
||||
void setValueEditFontColor(CColor color) { valueEdit_->setFontColor(color); valueEdit_->invalid(); }
|
||||
CColor getValueEditFontColor() const { return valueEdit_->getFontColor(); }
|
||||
|
||||
void setValueEditBackColor(CColor color) { valueEdit_->setBackColor(color); valueEdit_->invalid(); }
|
||||
CColor getValueEditBackColor() const { return valueEdit_->getBackColor(); }
|
||||
|
||||
void setShadingRectangleColor(CColor color) { shadingRectangle_->setColor(color); shadingRectangle_->invalid(); }
|
||||
CColor getShadingRectangleColor() const { return shadingRectangle_->getColor(); }
|
||||
|
||||
void setCCLabelFont(CFontRef font);
|
||||
CFontRef getCCLabelFont() const { return ccLabel_->getFont(); }
|
||||
|
||||
|
|
@ -288,21 +328,44 @@ public:
|
|||
void setKnobFontColor(CColor color) { knob_->setFontColor(color); knob_->invalid(); }
|
||||
CColor getKnobFontColor() const { return knob_->getFontColor(); }
|
||||
|
||||
using ValueToStringFunction = SStyledKnob::ValueToStringFunction;
|
||||
void setValueToStringFunction(ValueToStringFunction f) { knob_->setValueToStringFunction(std::move(f)); knob_->invalid(); }
|
||||
// Edit box listener
|
||||
void viewLostFocus (CView* view) override;
|
||||
|
||||
bool isHD() const noexcept { return hdMode_; }
|
||||
void setHDMode(bool mode);
|
||||
|
||||
protected:
|
||||
CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override;
|
||||
|
||||
private:
|
||||
void updateViewSizes();
|
||||
void updateViewColors();
|
||||
|
||||
private:
|
||||
SharedPointer<CTextLabel> label_;
|
||||
SharedPointer<CTextEdit> valueEdit_;
|
||||
SharedPointer<SStyledKnob> knob_;
|
||||
SharedPointer<CTextLabel> ccLabel_;
|
||||
SharedPointer<CFilledRect> shadingRectangle_;
|
||||
SharedPointer<CMenuItem> menuEntry_;
|
||||
CRect nameLabelSize_;
|
||||
CRect knobSize_;
|
||||
CRect shadingRectangleSize_;
|
||||
CRect ccLabelSize_;
|
||||
CRect valueEditSize_;
|
||||
CRect rectangleSize_;
|
||||
float hue_ = 0.35;
|
||||
|
||||
class MenuListener : public IControlListener, public NonAtomicReferenceCounted {
|
||||
public:
|
||||
explicit MenuListener(SKnobCCBox& box) : box_(box) {}
|
||||
void valueChanged(CControl*) override
|
||||
{
|
||||
box_.setHDMode(!box_.isHD());
|
||||
}
|
||||
private:
|
||||
SKnobCCBox& box_;
|
||||
};
|
||||
SharedPointer<MenuListener> menuListener_;
|
||||
bool hdMode_ { false };
|
||||
};
|
||||
|
||||
///
|
||||
|
|
@ -320,6 +383,9 @@ public:
|
|||
void setCCLabelFont(CFontRef font);
|
||||
void setCCLabelBackColor(CColor color);
|
||||
void setCCLabelFontColor(CColor color);
|
||||
void setValueEditBackColor(CColor color);
|
||||
void setValueEditFontColor(CColor color);
|
||||
void setShadingRectangleColor(CColor color);
|
||||
void setKnobActiveTrackColor(CColor color);
|
||||
void setKnobInactiveTrackColor(CColor color);
|
||||
void setKnobLineIndicatorColor(CColor color);
|
||||
|
|
@ -365,6 +431,47 @@ private:
|
|||
SharedPointer<CVSTGUITimer> relayoutTrigger_;
|
||||
};
|
||||
|
||||
///
|
||||
class SLevelMeter : public CView {
|
||||
public:
|
||||
explicit SLevelMeter(const CRect& size);
|
||||
|
||||
float getValue() const { return value_; }
|
||||
void setValue(float value);
|
||||
|
||||
float getDangerThreshold() const { return dangerThreshold_; }
|
||||
void setDangerThreshold(float thres) { dangerThreshold_ = thres; invalid(); }
|
||||
|
||||
CColor getFrameColor() const { return frameColor_; }
|
||||
void setFrameColor(CColor color) { frameColor_ = color; invalid(); }
|
||||
CColor getBackColor() const { return backColor_; }
|
||||
void setBackColor(CColor color) { backColor_ = color; invalid(); }
|
||||
|
||||
CColor getNormalFillColor() const { return safeFillColor_; }
|
||||
void setNormalFillColor(CColor color) { safeFillColor_ = color; invalid(); }
|
||||
CColor getDangerFillColor() const { return dangerFillColor_; }
|
||||
void setDangerFillColor(CColor color) { dangerFillColor_ = color; invalid(); }
|
||||
|
||||
CCoord getRoundRectRadius() const { return radius_; }
|
||||
void setRoundRectRadius(CCoord radius) { radius_ = radius; invalid(); }
|
||||
|
||||
protected:
|
||||
void draw(CDrawContext* dc) override;
|
||||
|
||||
private:
|
||||
float value_ = 0;
|
||||
float dangerThreshold_ = 0.5;
|
||||
float dbMin_ = -40;
|
||||
float dbMax_ = 0;
|
||||
CColor frameColor_;
|
||||
CColor safeFillColor_;
|
||||
CColor dangerFillColor_;
|
||||
CColor backColor_;
|
||||
CCoord radius_ = 5.0;
|
||||
SharedPointer<CVSTGUITimer> timer_;
|
||||
bool timerArmed_ = false;
|
||||
};
|
||||
|
||||
///
|
||||
class SPlaceHolder : public CView {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "GUIHelpers.h"
|
||||
#include "utility/vstgui_before.h"
|
||||
#include <vstgui/lib/events.h>
|
||||
#include "utility/vstgui_after.h"
|
||||
|
||||
class SFrameDisabler::KeyAndMouseHook : public CBaseObject,
|
||||
public IKeyboardHook,
|
||||
|
|
@ -13,12 +16,10 @@ public:
|
|||
void setEnabled(bool value) { enabled_ = value; }
|
||||
|
||||
protected:
|
||||
int32_t onKeyDown(const VstKeyCode&, CFrame*) { return enabled_ ? -1 : 1; }
|
||||
int32_t onKeyUp(const VstKeyCode&, CFrame*) { return enabled_ ? -1 : 1; }
|
||||
void onMouseEntered(CView*, CFrame*) {}
|
||||
void onMouseExited(CView*, CFrame*) {}
|
||||
CMouseEventResult onMouseMoved(CFrame*, const CPoint&, const CButtonState&) { return enabled_ ? kMouseEventNotHandled : kMouseEventHandled; }
|
||||
CMouseEventResult onMouseDown(CFrame*, const CPoint&, const CButtonState&) { return enabled_ ? kMouseEventNotHandled : kMouseEventHandled; }
|
||||
void onKeyboardEvent(KeyboardEvent& event, CFrame* frame) override;
|
||||
void onMouseEntered(CView* view, CFrame* frame) override {}
|
||||
void onMouseExited(CView* view, CFrame* frame) override {}
|
||||
void onMouseEvent(MouseEvent& event, CFrame* frame) override;
|
||||
|
||||
private:
|
||||
bool enabled_ = true;
|
||||
|
|
@ -51,3 +52,15 @@ void SFrameDisabler::disable()
|
|||
hook_->setEnabled(false);
|
||||
delayedEnabler_->stop();
|
||||
}
|
||||
|
||||
void SFrameDisabler::KeyAndMouseHook::onKeyboardEvent(KeyboardEvent& event, CFrame* frame)
|
||||
{
|
||||
if (!enabled_)
|
||||
event.consumed = true;
|
||||
}
|
||||
|
||||
void SFrameDisabler::KeyAndMouseHook::onMouseEvent(MouseEvent& event, CFrame* frame)
|
||||
{
|
||||
if (!enabled_)
|
||||
event.consumed = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,7 +164,6 @@ void SPiano::draw(CDrawContext* dc)
|
|||
const Dimensions dim = getDimensions(false);
|
||||
const unsigned octs = impl.octs_;
|
||||
const unsigned keyCount = octs * 12;
|
||||
const bool allKeysUsed = impl.keyUsed_.all();
|
||||
|
||||
dc->setDrawMode(kAntiAliasing);
|
||||
|
||||
|
|
@ -184,14 +183,12 @@ void SPiano::draw(CDrawContext* dc)
|
|||
|
||||
switch (getKeyRole(key)) {
|
||||
case KeyRole::Note:
|
||||
if (allKeysUsed)
|
||||
goto whiteKeyDefault;
|
||||
hcy.h = impl.keyUsedHue_;
|
||||
break;
|
||||
case KeyRole::Switch:
|
||||
hcy.h = impl.keySwitchHue_;
|
||||
break;
|
||||
default: whiteKeyDefault:
|
||||
default:
|
||||
hcy.y = 1.0;
|
||||
if (impl.keyval_[key])
|
||||
hcy.c = 0.0;
|
||||
|
|
@ -224,14 +221,12 @@ void SPiano::draw(CDrawContext* dc)
|
|||
|
||||
switch (getKeyRole(key)) {
|
||||
case KeyRole::Note:
|
||||
if (allKeysUsed)
|
||||
goto blackKeyDefault;
|
||||
hcy.h = impl.keyUsedHue_;
|
||||
break;
|
||||
case KeyRole::Switch:
|
||||
hcy.h = impl.keySwitchHue_;
|
||||
break;
|
||||
default: blackKeyDefault:
|
||||
default:
|
||||
hcy.c = 0.0;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,34 +5,13 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "NativeHelpers.h"
|
||||
#include "plugin/NativeHelpers.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "ghc/fs_std.hpp"
|
||||
#include <windows.h>
|
||||
#include <cstring>
|
||||
|
||||
static WCHAR *stringToWideChar(const char *str, int strCch = -1)
|
||||
{
|
||||
unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0);
|
||||
if (strSize == 0)
|
||||
return {};
|
||||
std::unique_ptr<WCHAR[]> strW(new WCHAR[strSize]);
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0)
|
||||
return {};
|
||||
return strW.release();
|
||||
}
|
||||
|
||||
static char* stringToUTF8(const wchar_t *strW, int strWCch = -1)
|
||||
{
|
||||
unsigned strSize = WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, nullptr, 0, nullptr, nullptr);
|
||||
if (strSize == 0)
|
||||
return {};
|
||||
std::unique_ptr<char[]> str(new char[strSize]);
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, str.get(), strSize, nullptr, nullptr) == 0)
|
||||
return {};
|
||||
return str.release();
|
||||
}
|
||||
|
||||
bool openFileInExternalEditor(const char *filename)
|
||||
{
|
||||
std::wstring path = stringToWideChar(filename);
|
||||
|
|
@ -163,8 +142,9 @@ std::string getCurrentProcessName()
|
|||
#include <sys/wait.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#include <absl/strings/strip.h>
|
||||
#include <absl/strings/string_view.h>
|
||||
#include <vector>
|
||||
#include <regex>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
|
|
@ -218,12 +198,21 @@ static std::vector<char *> createForkEnviron()
|
|||
return newEnv;
|
||||
}
|
||||
|
||||
static constexpr char zenityPath[] = "/usr/bin/zenity";
|
||||
static const std::string zenityPath = [] {
|
||||
auto glibPath = g_find_program_in_path("zenity");
|
||||
if (glibPath) {
|
||||
std::string s { glibPath };
|
||||
g_free(glibPath);
|
||||
return s;
|
||||
} else {
|
||||
return std::string("/usr/bin/zenity");
|
||||
}
|
||||
}();
|
||||
|
||||
bool askQuestion(const char *text)
|
||||
{
|
||||
char *argv[] = {
|
||||
const_cast<char *>(zenityPath),
|
||||
const_cast<char *>(zenityPath.c_str()),
|
||||
const_cast<char *>("--question"),
|
||||
const_cast<char *>("--text"),
|
||||
const_cast<char *>(text),
|
||||
|
|
@ -256,7 +245,7 @@ bool askQuestion(const char *text)
|
|||
|
||||
bool isZenityAvailable()
|
||||
{
|
||||
return access(zenityPath, X_OK) == 0;
|
||||
return access(zenityPath.c_str(), X_OK) == 0;
|
||||
}
|
||||
|
||||
std::string getOperatingSystemName()
|
||||
|
|
@ -305,14 +294,22 @@ std::string getProcessorName()
|
|||
std::string name;
|
||||
std::string line;
|
||||
std::ifstream in("/proc/cpuinfo", std::ios::binary);
|
||||
std::regex re("^model name\\s*:\\s*(.*)");
|
||||
|
||||
line.reserve(256);
|
||||
|
||||
while (name.empty() && std::getline(in, line) && !line.empty()) {
|
||||
std::smatch match;
|
||||
if (std::regex_match(line, match, re))
|
||||
name = match[1];
|
||||
size_t pos = line.find(':');
|
||||
if (pos == line.npos)
|
||||
continue;
|
||||
|
||||
absl::string_view left = absl::string_view(line).substr(0, pos);
|
||||
absl::string_view right = absl::string_view(line).substr(pos + 1);
|
||||
|
||||
left = absl::StripAsciiWhitespace(left);
|
||||
right = absl::StripAsciiWhitespace(right);
|
||||
|
||||
if (left == "model name")
|
||||
name = std::string(right);
|
||||
}
|
||||
|
||||
if (name.empty())
|
||||
|
|
|
|||
|
|
@ -24,11 +24,30 @@ static bool openFileWithApplication(const char *fileName, NSString *application)
|
|||
|
||||
bool openFileInExternalEditor(const char *fileName)
|
||||
{
|
||||
NSURL* applicationURL = (__bridge_transfer NSURL*)LSCopyDefaultApplicationURLForContentType(
|
||||
kUTTypePlainText, kLSRolesEditor, nil);
|
||||
if (!applicationURL || ![applicationURL isFileURL])
|
||||
NSURL* appURL = nil;
|
||||
NSURL* fileURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:fileName]];
|
||||
const LSRolesMask roles = kLSRolesEditor;
|
||||
|
||||
NSArray* editorApps = (__bridge_transfer NSArray*)LSCopyApplicationURLsForURL(
|
||||
(__bridge CFURLRef)fileURL, roles);
|
||||
|
||||
for (NSUInteger i = 0, n = [editorApps count]; i < n && !appURL; ++i) {
|
||||
NSURL* url = [editorApps objectAtIndex:i];
|
||||
if (url && [url isFileURL])
|
||||
appURL = url;
|
||||
}
|
||||
|
||||
if (!appURL) {
|
||||
NSURL* url = (__bridge_transfer NSURL*)LSCopyDefaultApplicationURLForContentType(
|
||||
kUTTypePlainText, roles, nil);
|
||||
if (url && [url isFileURL])
|
||||
appURL = url;
|
||||
}
|
||||
|
||||
if (!appURL)
|
||||
return false;
|
||||
return openFileWithApplication(fileName, [applicationURL path]);
|
||||
|
||||
return openFileWithApplication(fileName, [appURL path]);
|
||||
}
|
||||
|
||||
bool openDirectoryInExplorer(const char *fileName)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* This file is generated by the layout maker tool. */
|
||||
auto* const view__0 = createLogicalGroup(CRect(0, 0, 800, 475), -1, "", kCenterText, 14);
|
||||
mainView = view__0;
|
||||
auto* const view__1 = createBackground(CRect(190, 110, 790, 390), -1, "", kCenterText, 14);
|
||||
auto* const view__1 = createBackground(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
|
||||
imageContainer_ = view__1;
|
||||
view__0->addView(view__1);
|
||||
enterPalette(invertedPalette);
|
||||
|
|
@ -11,185 +11,197 @@ auto* const view__3 = createRoundedGroup(CRect(5, 4, 180, 105), -1, "", kCenterT
|
|||
view__2->addView(view__3);
|
||||
auto* const view__4 = createAboutButton(CRect(27, 5, 147, 65), kTagAbout, "", kCenterText, 14);
|
||||
view__3->addView(view__4);
|
||||
auto* const view__5 = createHomeButton(CRect(31, 69, 63, 101), kTagFirstChangePanel+kPanelGeneral, "", kCenterText, 30);
|
||||
panelButtons_[kPanelGeneral] = view__5;
|
||||
auto* const view__5 = createInfoButton(CRect(51, 69, 83, 101), kTagFirstChangePanel+kPanelInfo, "", kCenterText, 30);
|
||||
panelButtons_[kPanelInfo] = view__5;
|
||||
view__3->addView(view__5);
|
||||
auto* const view__6 = createCCButton(CRect(71, 69, 103, 101), kTagFirstChangePanel+kPanelControls, "", kCenterText, 30);
|
||||
auto* const view__6 = createCCButton(CRect(92, 69, 124, 101), kTagFirstChangePanel+kPanelControls, "", kCenterText, 30);
|
||||
panelButtons_[kPanelControls] = view__6;
|
||||
view__3->addView(view__6);
|
||||
auto* const view__7 = createSettingsButton(CRect(111, 69, 143, 101), kTagFirstChangePanel+kPanelSettings, "", kCenterText, 30);
|
||||
auto* const view__7 = createSettingsButton(CRect(132, 69, 164, 101), kTagFirstChangePanel+kPanelSettings, "", kCenterText, 30);
|
||||
panelButtons_[kPanelSettings] = view__7;
|
||||
view__3->addView(view__7);
|
||||
auto* const view__8 = createRoundedGroup(CRect(185, 5, 565, 105), -1, "", kCenterText, 14);
|
||||
view__2->addView(view__8);
|
||||
auto* const view__9 = createHLine(CRect(10, 36, 370, 41), -1, "", kCenterText, 14);
|
||||
view__8->addView(view__9);
|
||||
auto* const view__10 = createHLine(CRect(10, 68, 370, 73), -1, "", kCenterText, 14);
|
||||
view__8->addView(view__10);
|
||||
auto* const view__11 = createClickableLabel(CRect(10, 8, 260, 38), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20);
|
||||
sfzFileLabel_ = view__11;
|
||||
view__8->addView(view__11);
|
||||
auto* const view__12 = createLabel(CRect(80, 40, 370, 70), -1, "", kLeftText, 20);
|
||||
keyswitchLabel_ = view__12;
|
||||
view__8->addView(view__12);
|
||||
auto* const view__13 = createBadge(CRect(10, 42, 70, 68), -1, "", kCenterText, 20);
|
||||
keyswitchBadge_ = view__13;
|
||||
view__8->addView(view__13);
|
||||
auto* const view__14 = createInactiveLabel(CRect(10, 40, 370, 70), -1, "No key switch", kLeftText, 20);
|
||||
keyswitchInactiveLabel_ = view__14;
|
||||
view__8->addView(view__14);
|
||||
view__14->setVisible(false);
|
||||
auto* const view__15 = createLabel(CRect(10, 73, 70, 98), -1, "Voices:", kRightText, 12);
|
||||
view__8->addView(view__15);
|
||||
auto* const view__16 = createPreviousFileButton(CRect(295, 13, 320, 38), kTagPreviousSfzFile, "", kCenterText, 24);
|
||||
view__8->addView(view__16);
|
||||
auto* const view__17 = createNextFileButton(CRect(320, 13, 345, 38), kTagNextSfzFile, "", kCenterText, 24);
|
||||
view__8->addView(view__17);
|
||||
auto* const view__18 = createChevronDropDown(CRect(345, 13, 370, 38), kTagFileOperations, "", kCenterText, 24);
|
||||
fileOperationsMenu_ = view__18;
|
||||
view__8->addView(view__18);
|
||||
auto* const view__19 = createLabel(CRect(75, 73, 115, 98), -1, "", kCenterText, 12);
|
||||
infoVoicesLabel_ = view__19;
|
||||
view__8->addView(view__19);
|
||||
auto* const view__20 = createLabel(CRect(130, 73, 170, 98), -1, "Max:", kRightText, 12);
|
||||
view__8->addView(view__20);
|
||||
auto* const view__21 = createLabel(CRect(175, 73, 210, 98), -1, "", kCenterText, 12);
|
||||
numVoicesLabel_ = view__21;
|
||||
view__8->addView(view__21);
|
||||
auto* const view__22 = createLabel(CRect(240, 73, 300, 98), -1, "Memory:", kRightText, 12);
|
||||
view__8->addView(view__22);
|
||||
auto* const view__23 = createLabel(CRect(305, 73, 365, 98), -1, "", kCenterText, 12);
|
||||
memoryLabel_ = view__23;
|
||||
view__8->addView(view__23);
|
||||
auto* const view__24 = createChevronValueDropDown(CRect(210, 77, 230, 97), kTagSetNumVoices, "", kCenterText, 16);
|
||||
numVoicesSlider_ = view__24;
|
||||
view__8->addView(view__24);
|
||||
auto* const view__25 = createRoundedGroup(CRect(570, 5, 795, 105), -1, "", kCenterText, 14);
|
||||
view__2->addView(view__25);
|
||||
auto* const view__26 = createKnob48(CRect(45, 15, 93, 63), -1, "", kCenterText, 14);
|
||||
view__25->addView(view__26);
|
||||
view__26->setVisible(false);
|
||||
auto* const view__27 = createValueLabel(CRect(40, 65, 100, 70), -1, "Center", kCenterText, 12);
|
||||
view__25->addView(view__27);
|
||||
auto* const view__8 = createHomeButton(CRect(11, 69, 43, 101), kTagFirstChangePanel+kPanelGeneral, "", kCenterText, 30);
|
||||
panelButtons_[kPanelGeneral] = view__8;
|
||||
view__3->addView(view__8);
|
||||
auto* const view__9 = createRoundedGroup(CRect(185, 5, 603, 105), -1, "", kCenterText, 14);
|
||||
view__2->addView(view__9);
|
||||
auto* const view__10 = createHLine(CRect(10, 36, 405, 41), -1, "", kCenterText, 14);
|
||||
view__9->addView(view__10);
|
||||
auto* const view__11 = createHLine(CRect(10, 68, 405, 73), -1, "", kCenterText, 14);
|
||||
view__9->addView(view__11);
|
||||
auto* const view__12 = createClickableLabel(CRect(10, 8, 330, 38), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20);
|
||||
sfzFileLabel_ = view__12;
|
||||
view__9->addView(view__12);
|
||||
auto* const view__13 = createLabel(CRect(80, 40, 405, 75), -1, "", kLeftText, 20);
|
||||
keyswitchLabel_ = view__13;
|
||||
view__9->addView(view__13);
|
||||
auto* const view__14 = createBadge(CRect(10, 42, 70, 68), -1, "", kCenterText, 20);
|
||||
keyswitchBadge_ = view__14;
|
||||
view__9->addView(view__14);
|
||||
auto* const view__15 = createInactiveLabel(CRect(10, 40, 370, 70), -1, "No key switch", kLeftText, 20);
|
||||
keyswitchInactiveLabel_ = view__15;
|
||||
view__9->addView(view__15);
|
||||
view__15->setVisible(false);
|
||||
auto* const view__16 = createLabel(CRect(10, 73, 70, 98), -1, "Voices:", kRightText, 12);
|
||||
view__9->addView(view__16);
|
||||
auto* const view__17 = createPreviousFileButton(CRect(330, 13, 355, 38), kTagPreviousSfzFile, "", kCenterText, 24);
|
||||
view__9->addView(view__17);
|
||||
auto* const view__18 = createNextFileButton(CRect(355, 13, 380, 38), kTagNextSfzFile, "", kCenterText, 24);
|
||||
view__9->addView(view__18);
|
||||
auto* const view__19 = createChevronDropDown(CRect(380, 13, 405, 38), kTagFileOperations, "", kCenterText, 24);
|
||||
fileOperationsMenu_ = view__19;
|
||||
view__9->addView(view__19);
|
||||
auto* const view__20 = createLabel(CRect(75, 73, 115, 98), -1, "", kCenterText, 12);
|
||||
infoVoicesLabel_ = view__20;
|
||||
view__9->addView(view__20);
|
||||
auto* const view__21 = createLabel(CRect(147, 73, 187, 98), -1, "Max:", kRightText, 12);
|
||||
view__9->addView(view__21);
|
||||
auto* const view__22 = createLabel(CRect(193, 73, 228, 98), -1, "", kCenterText, 12);
|
||||
numVoicesLabel_ = view__22;
|
||||
view__9->addView(view__22);
|
||||
auto* const view__23 = createLabel(CRect(274, 73, 334, 98), -1, "Memory:", kRightText, 12);
|
||||
view__9->addView(view__23);
|
||||
auto* const view__24 = createLabel(CRect(340, 73, 400, 98), -1, "", kCenterText, 12);
|
||||
memoryLabel_ = view__24;
|
||||
view__9->addView(view__24);
|
||||
auto* const view__25 = createChevronValueDropDown(CRect(235, 77, 255, 97), kTagSetNumVoices, "", kCenterText, 16);
|
||||
numVoicesSlider_ = view__25;
|
||||
view__9->addView(view__25);
|
||||
auto* const view__26 = createRoundedGroup(CRect(608, 5, 795, 105), -1, "", kCenterText, 14);
|
||||
view__2->addView(view__26);
|
||||
auto* const view__27 = createKnob48(CRect(7, 15, 55, 63), -1, "", kCenterText, 14);
|
||||
view__26->addView(view__27);
|
||||
view__27->setVisible(false);
|
||||
auto* const view__28 = createVMeter(CRect(175, 15, 210, 70), -1, "", kCenterText, 14);
|
||||
view__25->addView(view__28);
|
||||
auto* const view__29 = createKnobCCBox(CRect(10, 5, 80, 95), kTagSetCCVolume, "Volume", kCenterText, 12);
|
||||
auto* const view__28 = createValueLabel(CRect(2, 65, 62, 70), -1, "Center", kCenterText, 12);
|
||||
view__26->addView(view__28);
|
||||
view__28->setVisible(false);
|
||||
auto* const view__29 = createKnobCCBox(CRect(6, 5, 76, 95), kTagSetCCVolume, "Volume", kCenterText, 12);
|
||||
volumeCCKnob_ = view__29;
|
||||
view__25->addView(view__29);
|
||||
auto* const view__30 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12);
|
||||
view__26->addView(view__29);
|
||||
auto* const view__30 = createKnobCCBox(CRect(83, 5, 153, 95), kTagSetCCPan, "Pan", kCenterText, 12);
|
||||
panCCKnob_ = view__30;
|
||||
view__25->addView(view__30);
|
||||
view__26->addView(view__30);
|
||||
auto* const view__31 = createVMeter(CRect(159, 5, 168, 95), -1, "", kCenterText, 14);
|
||||
leftMeter_ = view__31;
|
||||
view__26->addView(view__31);
|
||||
auto* const view__32 = createVMeter(CRect(171, 5, 180, 95), -1, "", kCenterText, 14);
|
||||
rightMeter_ = view__32;
|
||||
view__26->addView(view__32);
|
||||
enterPalette(defaultPalette);
|
||||
auto* const view__31 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelGeneral] = view__31;
|
||||
view__0->addView(view__31);
|
||||
view__31->setVisible(false);
|
||||
auto* const view__32 = createRoundedGroup(CRect(0, 0, 175, 280), -1, "", kCenterText, 14);
|
||||
view__31->addView(view__32);
|
||||
auto* const view__33 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14);
|
||||
view__32->addView(view__33);
|
||||
auto* const view__34 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14);
|
||||
view__32->addView(view__34);
|
||||
auto* const view__35 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14);
|
||||
view__32->addView(view__35);
|
||||
auto* const view__36 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14);
|
||||
view__32->addView(view__36);
|
||||
auto* const view__37 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14);
|
||||
view__32->addView(view__37);
|
||||
auto* const view__38 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14);
|
||||
infoCurvesLabel_ = view__38;
|
||||
view__32->addView(view__38);
|
||||
auto* const view__39 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14);
|
||||
infoMastersLabel_ = view__39;
|
||||
view__32->addView(view__39);
|
||||
auto* const view__40 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14);
|
||||
infoGroupsLabel_ = view__40;
|
||||
view__32->addView(view__40);
|
||||
auto* const view__41 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14);
|
||||
infoRegionsLabel_ = view__41;
|
||||
view__32->addView(view__41);
|
||||
auto* const view__42 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14);
|
||||
infoSamplesLabel_ = view__42;
|
||||
view__32->addView(view__42);
|
||||
auto* const view__43 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelControls] = view__43;
|
||||
view__0->addView(view__43);
|
||||
view__43->setVisible(false);
|
||||
auto* const view__44 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14);
|
||||
view__43->addView(view__44);
|
||||
auto* const view__45 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12);
|
||||
controlsPanel_ = view__45;
|
||||
view__44->addView(view__45);
|
||||
auto* const view__46 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelSettings] = view__46;
|
||||
view__0->addView(view__46);
|
||||
auto* const view__47 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12);
|
||||
auto* const view__33 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelInfo] = view__33;
|
||||
view__0->addView(view__33);
|
||||
view__33->setVisible(false);
|
||||
auto* const view__34 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14);
|
||||
view__33->addView(view__34);
|
||||
auto* const view__35 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14);
|
||||
view__34->addView(view__35);
|
||||
auto* const view__36 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14);
|
||||
view__34->addView(view__36);
|
||||
auto* const view__37 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14);
|
||||
view__34->addView(view__37);
|
||||
auto* const view__38 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14);
|
||||
view__34->addView(view__38);
|
||||
auto* const view__39 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14);
|
||||
view__34->addView(view__39);
|
||||
auto* const view__40 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14);
|
||||
infoCurvesLabel_ = view__40;
|
||||
view__34->addView(view__40);
|
||||
auto* const view__41 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14);
|
||||
infoMastersLabel_ = view__41;
|
||||
view__34->addView(view__41);
|
||||
auto* const view__42 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14);
|
||||
infoGroupsLabel_ = view__42;
|
||||
view__34->addView(view__42);
|
||||
auto* const view__43 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14);
|
||||
infoRegionsLabel_ = view__43;
|
||||
view__34->addView(view__43);
|
||||
auto* const view__44 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14);
|
||||
infoSamplesLabel_ = view__44;
|
||||
view__34->addView(view__44);
|
||||
auto* const view__45 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelControls] = view__45;
|
||||
view__0->addView(view__45);
|
||||
view__45->setVisible(false);
|
||||
auto* const view__46 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14);
|
||||
view__45->addView(view__46);
|
||||
auto* const view__47 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12);
|
||||
controlsPanel_ = view__47;
|
||||
view__46->addView(view__47);
|
||||
auto* const view__48 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12);
|
||||
oversamplingSlider_ = view__48;
|
||||
view__47->addView(view__48);
|
||||
auto* const view__49 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12);
|
||||
view__47->addView(view__49);
|
||||
auto* const view__50 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12);
|
||||
view__47->addView(view__50);
|
||||
auto* const view__51 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12);
|
||||
preloadSizeSlider_ = view__51;
|
||||
view__47->addView(view__51);
|
||||
auto* const view__52 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12);
|
||||
view__46->addView(view__52);
|
||||
auto* const view__53 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12);
|
||||
view__52->addView(view__53);
|
||||
auto* const view__54 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12);
|
||||
tuningFrequencySlider_ = view__54;
|
||||
view__52->addView(view__54);
|
||||
auto* const view__55 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12);
|
||||
view__52->addView(view__55);
|
||||
auto* const view__56 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14);
|
||||
stretchedTuningSlider_ = view__56;
|
||||
view__52->addView(view__56);
|
||||
auto* const view__57 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12);
|
||||
view__52->addView(view__57);
|
||||
auto* const view__58 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12);
|
||||
view__52->addView(view__58);
|
||||
auto* const view__59 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12);
|
||||
scalaFileButton_ = view__59;
|
||||
view__52->addView(view__59);
|
||||
auto* const view__60 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12);
|
||||
scalaRootKeySlider_ = view__60;
|
||||
view__52->addView(view__60);
|
||||
auto* const view__61 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12);
|
||||
scalaRootOctaveSlider_ = view__61;
|
||||
view__52->addView(view__61);
|
||||
auto* const view__62 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12);
|
||||
scalaResetButton_ = view__62;
|
||||
view__52->addView(view__62);
|
||||
auto* const view__63 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12);
|
||||
userFilesGroup_ = view__63;
|
||||
view__46->addView(view__63);
|
||||
auto* const view__64 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12);
|
||||
view__63->addView(view__64);
|
||||
auto* const view__65 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12);
|
||||
userFilesDirButton_ = view__65;
|
||||
view__63->addView(view__65);
|
||||
auto* const view__66 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12);
|
||||
view__46->addView(view__66);
|
||||
auto* const view__67 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12);
|
||||
sampleQualitySlider_ = view__67;
|
||||
view__66->addView(view__67);
|
||||
auto* const view__68 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12);
|
||||
view__66->addView(view__68);
|
||||
auto* const view__69 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12);
|
||||
view__66->addView(view__69);
|
||||
auto* const view__70 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12);
|
||||
oscillatorQualitySlider_ = view__70;
|
||||
view__66->addView(view__70);
|
||||
auto* const view__71 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12);
|
||||
view__46->addView(view__71);
|
||||
view__71->setVisible(false);
|
||||
auto* const view__72 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12);
|
||||
themeMenu_ = view__72;
|
||||
view__71->addView(view__72);
|
||||
auto* const view__73 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
|
||||
piano_ = view__73;
|
||||
view__0->addView(view__73);
|
||||
auto* const view__48 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelSettings] = view__48;
|
||||
view__0->addView(view__48);
|
||||
auto* const view__49 = createTitleGroup(CRect(300, 25, 495, 125), -1, "Engine", kCenterText, 12);
|
||||
view__48->addView(view__49);
|
||||
auto* const view__50 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12);
|
||||
oversamplingSlider_ = view__50;
|
||||
view__49->addView(view__50);
|
||||
auto* const view__51 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12);
|
||||
view__49->addView(view__51);
|
||||
auto* const view__52 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12);
|
||||
view__49->addView(view__52);
|
||||
auto* const view__53 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12);
|
||||
preloadSizeSlider_ = view__53;
|
||||
view__49->addView(view__53);
|
||||
auto* const view__54 = createTitleGroup(CRect(170, 160, 585, 260), -1, "Tuning", kCenterText, 12);
|
||||
view__48->addView(view__54);
|
||||
auto* const view__55 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12);
|
||||
view__54->addView(view__55);
|
||||
auto* const view__56 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12);
|
||||
tuningFrequencySlider_ = view__56;
|
||||
view__54->addView(view__56);
|
||||
auto* const view__57 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12);
|
||||
view__54->addView(view__57);
|
||||
auto* const view__58 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14);
|
||||
stretchedTuningSlider_ = view__58;
|
||||
view__54->addView(view__58);
|
||||
auto* const view__59 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12);
|
||||
view__54->addView(view__59);
|
||||
auto* const view__60 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12);
|
||||
view__54->addView(view__60);
|
||||
auto* const view__61 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12);
|
||||
scalaFileButton_ = view__61;
|
||||
view__54->addView(view__61);
|
||||
auto* const view__62 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12);
|
||||
scalaRootKeySlider_ = view__62;
|
||||
view__54->addView(view__62);
|
||||
auto* const view__63 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12);
|
||||
scalaRootOctaveSlider_ = view__63;
|
||||
view__54->addView(view__63);
|
||||
auto* const view__64 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12);
|
||||
scalaResetButton_ = view__64;
|
||||
view__54->addView(view__64);
|
||||
auto* const view__65 = createTitleGroup(CRect(615, 160, 754, 260), -1, "Files", kCenterText, 12);
|
||||
userFilesGroup_ = view__65;
|
||||
view__48->addView(view__65);
|
||||
auto* const view__66 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12);
|
||||
view__65->addView(view__66);
|
||||
auto* const view__67 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12);
|
||||
userFilesDirButton_ = view__67;
|
||||
view__65->addView(view__67);
|
||||
auto* const view__68 = createTitleGroup(CRect(525, 25, 720, 125), -1, "Quality", kCenterText, 12);
|
||||
view__48->addView(view__68);
|
||||
auto* const view__69 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12);
|
||||
sampleQualitySlider_ = view__69;
|
||||
view__68->addView(view__69);
|
||||
auto* const view__70 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12);
|
||||
view__68->addView(view__70);
|
||||
auto* const view__71 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12);
|
||||
view__68->addView(view__71);
|
||||
auto* const view__72 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12);
|
||||
oscillatorQualitySlider_ = view__72;
|
||||
view__68->addView(view__72);
|
||||
auto* const view__73 = createTitleGroup(CRect(35, 160, 140, 260), -1, "Appearance", kCenterText, 12);
|
||||
view__48->addView(view__73);
|
||||
auto* const view__74 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12);
|
||||
themeMenu_ = view__74;
|
||||
view__73->addView(view__74);
|
||||
auto* const view__75 = createValueLabel(CRect(10, 25, 90, 50), -1, "Theme", kCenterText, 12);
|
||||
view__73->addView(view__75);
|
||||
auto* const view__76 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
|
||||
piano_ = view__76;
|
||||
view__0->addView(view__76);
|
||||
auto* const view__77 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelGeneral] = view__77;
|
||||
view__0->addView(view__77);
|
||||
view__77->setVisible(false);
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ if(SFIZZ_LV2_UI)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Define a preprocessor variable to indicate a build with UI enabled
|
||||
if(SFIZZ_LV2_UI)
|
||||
target_compile_definitions(${LV2PLUGIN_PRJ_NAME} PRIVATE "SFIZZ_LV2_UI=1")
|
||||
target_compile_definitions(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE "SFIZZ_LV2_UI=1")
|
||||
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>
|
||||
|
|
@ -83,9 +89,6 @@ 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_DEPENDENCIES OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
file(COPY "lgpl-3.0.txt" DESTINATION ${PROJECT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
# Generate controllers.ttl
|
||||
sfizz_lv2_generate_controllers_ttl("${PROJECT_BINARY_DIR}/controllers.ttl")
|
||||
|
|
@ -114,7 +117,8 @@ endif()
|
|||
# Installation
|
||||
if(NOT MSVC)
|
||||
install(DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR}
|
||||
COMPONENT "lv2")
|
||||
COMPONENT "lv2"
|
||||
USE_SOURCE_PERMISSIONS)
|
||||
bundle_dylibs(lv2
|
||||
"${LV2PLUGIN_INSTALL_DIR}/${PROJECT_NAME}.lv2/Contents/Binary/sfizz.so"
|
||||
COMPONENT "lv2")
|
||||
|
|
|
|||
|
|
@ -1,19 +1,12 @@
|
|||
The `sfizz` LV2 plugin is able to be built statically against all its components,
|
||||
which is a desirable property for audio plugin hosts. These components include
|
||||
`libsndfile` from Erik de Castro Lopo (http://www.mega-nerd.com/libsndfile).
|
||||
`libsndfile` is distributed under the terms of the LGPL v2 or v3. As such,
|
||||
statically-linked LV2 version of `sfizz` are to be distributed under the terms
|
||||
of the LGPL-v3.0 license. You should be able to rebuild from source the sfizz LV2
|
||||
plugin using any `libsndfile` version you wish to use.
|
||||
|
||||
See the file `lgpl-3.0.txt` or (https://www.gnu.org/licenses/lgpl-3.0.txt) for more
|
||||
information about the LGPL v3 license.
|
||||
|
||||
Most of the code in the plug comes from various examples in the LV2 distribution and
|
||||
example plugins. As such, the source code of the LV2 plugin version of `sfizz` is
|
||||
distributed under the same terms as the LV2 specification, which is the ISC license.
|
||||
Binaries of this LV2 plugin dynamically linked against e.g. a system installation
|
||||
of `libsndfile` thus have to respect the terms of the ISC license and BSD 2-clause
|
||||
license. Check the LICENSE.md at (@LV2PLUGIN_REPOSITORY@) file for more
|
||||
Check the LICENSE.md at (@LV2PLUGIN_REPOSITORY@) file for more
|
||||
information about the license of the source code of the `sfizz` library and its
|
||||
contributors.
|
||||
|
||||
The `sfizz` LV2 plugin can be optionally statically built against `libsndfile`
|
||||
from Erik de Castro Lopo (http://www.mega-nerd.com/libsndfile). `libsndfile`
|
||||
is distributed under the terms of the LGPL v2 or v3. If you distribute a
|
||||
statically-linked LV2 version of `sfizz` built against `libsndfile`, you need
|
||||
to respect the terms of the LGPL v2 or v3 license.
|
||||
|
|
|
|||
|
|
@ -1,165 +0,0 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
#include "sfizz_lv2.h"
|
||||
#include "sfizz_lv2_plugin.h"
|
||||
|
||||
#include "sfizz/import/ForeignInstrument.h"
|
||||
#include "sfizz/import/sfizz_import.h"
|
||||
#include "plugin/InstrumentDescription.h"
|
||||
|
||||
#include <math.h>
|
||||
|
|
@ -120,10 +120,12 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
|
|||
self->sfizz_num_voices_uri = map->map(map->handle, SFIZZ__numVoices);
|
||||
self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize);
|
||||
self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling);
|
||||
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
|
||||
self->sfizz_last_keyswitch_uri = map->map(map->handle, SFIZZ__lastKeyswitch);
|
||||
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
|
||||
self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification);
|
||||
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob);
|
||||
self->sfizz_notify_uri = map->map(map->handle, SFIZZ__Notify);
|
||||
self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel);
|
||||
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);
|
||||
|
|
@ -200,9 +202,6 @@ connect_port(LV2_Handle instance,
|
|||
case SFIZZ_CONTROL:
|
||||
self->control_port = (const LV2_Atom_Sequence *)data;
|
||||
break;
|
||||
case SFIZZ_NOTIFY:
|
||||
self->notify_port = (LV2_Atom_Sequence *)data;
|
||||
break;
|
||||
case SFIZZ_AUTOMATE:
|
||||
self->automate_port = (LV2_Atom_Sequence *)data;
|
||||
break;
|
||||
|
|
@ -325,13 +324,21 @@ sfizz_lv2_receive_message(void* data, int delay, const char* path, const char* s
|
|||
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)data;
|
||||
|
||||
if (!strcmp(path, "/sw/last/current") && sig)
|
||||
{
|
||||
if (sig[0] == 'i')
|
||||
self->last_keyswitch = args[0].i;
|
||||
else if (sig[0] == 'N')
|
||||
self->last_keyswitch = -1;
|
||||
}
|
||||
|
||||
// transmit to UI as OSC blob
|
||||
uint8_t *osc_temp = self->osc_temp;
|
||||
uint32_t osc_size = sfizz_prepare_message(osc_temp, OSC_TEMP_SIZE, path, sig, args);
|
||||
if (osc_size > OSC_TEMP_SIZE)
|
||||
return;
|
||||
|
||||
LV2_Atom_Forge* forge = &self->forge_notify;
|
||||
LV2_Atom_Forge* forge = &self->forge_automate;
|
||||
bool write_ok =
|
||||
lv2_atom_forge_frame_time(forge, 0) &&
|
||||
lv2_atom_forge_atom(forge, osc_size, self->sfizz_osc_blob_uri) &&
|
||||
|
|
@ -439,7 +446,6 @@ instantiate(const LV2_Descriptor *descriptor,
|
|||
sfizz_lv2_map_required_uris(self);
|
||||
|
||||
// Initialize the forge
|
||||
lv2_atom_forge_init(&self->forge_notify, self->map);
|
||||
lv2_atom_forge_init(&self->forge_automate, self->map);
|
||||
lv2_atom_forge_init(&self->forge_secondary, self->map);
|
||||
|
||||
|
|
@ -478,7 +484,7 @@ instantiate(const LV2_Descriptor *descriptor,
|
|||
else
|
||||
{
|
||||
lv2_log_warning(&self->logger,
|
||||
"No option array was given upon instantiation; will use default values\n.");
|
||||
"No option array was given upon instantiation; will use default values.\n");
|
||||
}
|
||||
|
||||
// We need _some_ information on the block size
|
||||
|
|
@ -532,6 +538,9 @@ activate(LV2_Handle instance)
|
|||
sfizz_set_samples_per_block(self->synth, self->max_block_size);
|
||||
sfizz_set_sample_rate(self->synth, self->sample_rate);
|
||||
self->must_update_midnam.store(1);
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
self->rms_follower.init(self->sample_rate);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -542,13 +551,14 @@ deactivate(LV2_Handle instance)
|
|||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_Atom_Forge* forge, LV2_URID urid, const char *path)
|
||||
sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_URID verb_uri, LV2_URID urid, const char *path)
|
||||
{
|
||||
LV2_Atom_Forge_Frame frame;
|
||||
|
||||
LV2_Atom_Forge* forge = &self->forge_automate;
|
||||
bool write_ok =
|
||||
lv2_atom_forge_frame_time(forge, 0) &&
|
||||
lv2_atom_forge_object(forge, &frame, 0, self->patch_set_uri) &&
|
||||
lv2_atom_forge_object(forge, &frame, 0, verb_uri) &&
|
||||
lv2_atom_forge_key(forge, self->patch_property_uri) &&
|
||||
lv2_atom_forge_urid(forge, urid) &&
|
||||
lv2_atom_forge_key(forge, self->patch_value_uri) &&
|
||||
|
|
@ -559,14 +569,15 @@ sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_Atom_Forge* forge, LV2_URID u
|
|||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_Atom_Forge* forge, unsigned cc, float value)
|
||||
sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_URID verb_uri, unsigned cc, float value)
|
||||
{
|
||||
LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc));
|
||||
LV2_Atom_Forge_Frame frame;
|
||||
|
||||
LV2_Atom_Forge* forge = &self->forge_automate;
|
||||
bool write_ok =
|
||||
lv2_atom_forge_frame_time(forge, 0) &&
|
||||
lv2_atom_forge_object(forge, &frame, 0, self->patch_set_uri) &&
|
||||
lv2_atom_forge_object(forge, &frame, 0, verb_uri) &&
|
||||
lv2_atom_forge_key(forge, self->patch_property_uri) &&
|
||||
lv2_atom_forge_urid(forge, urid) &&
|
||||
lv2_atom_forge_key(forge, self->patch_value_uri) &&
|
||||
|
|
@ -576,6 +587,31 @@ sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_Atom_Forge* forge, unsigned
|
|||
lv2_atom_forge_pop(forge, &frame);
|
||||
}
|
||||
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
static void
|
||||
sfizz_lv2_send_levels(sfizz_plugin_t *self, float left, float right)
|
||||
{
|
||||
const float levels[] = {left, right};
|
||||
uint32_t num_levels = sizeof(levels) / sizeof(levels[0]);
|
||||
|
||||
LV2_Atom_Forge* forge = &self->forge_automate;
|
||||
bool write_ok = lv2_atom_forge_frame_time(forge, 0);
|
||||
|
||||
LV2_Atom_Vector *vector = nullptr;
|
||||
if (write_ok) {
|
||||
LV2_Atom_Forge_Ref ref = lv2_atom_forge_vector(forge, sizeof(float), self->atom_float_uri, num_levels, levels);
|
||||
write_ok = ref;
|
||||
if (write_ok)
|
||||
vector = (LV2_Atom_Vector *)lv2_atom_forge_deref(forge, ref);
|
||||
}
|
||||
|
||||
if (write_ok)
|
||||
vector->atom.type = self->sfizz_audio_level_uri;
|
||||
|
||||
(void)write_ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Object *obj)
|
||||
{
|
||||
|
|
@ -618,7 +654,7 @@ sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Obj
|
|||
if (cc != -1) {
|
||||
if (atom->type == self->atom_float_uri && atom->size == sizeof(float)) {
|
||||
float value = *(const float *)LV2_ATOM_BODY_CONST(atom);
|
||||
sfizz_send_hdcc(self->synth, delay, cc, value);
|
||||
sfizz_automate_hdcc(self->synth, delay, cc, value);
|
||||
self->cc_current[cc] = value;
|
||||
self->ccauto[cc] = absl::nullopt;
|
||||
}
|
||||
|
|
@ -656,6 +692,12 @@ sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Obj
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
sfizz_is_sustain_or_sostenuto(sfizz_plugin_t* self, unsigned cc)
|
||||
{
|
||||
return (self->sustain_or_sostenuto[cc / 8] & (1 << (cc % 8)));
|
||||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
||||
{
|
||||
|
|
@ -677,23 +719,47 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
|||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
// Note(jpc) CC must be mapped by host, not handled here.
|
||||
// See LV2 midi:binding.
|
||||
#if defined(SFIZZ_LV2_PSA)
|
||||
case LV2_MIDI_MSG_CONTROLLER:
|
||||
{
|
||||
unsigned cc = msg[1];
|
||||
float value = float(msg[2]) * (1.0f / 127.0f);
|
||||
sfizz_send_hdcc(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)cc,
|
||||
value);
|
||||
self->cc_current[cc] = value;
|
||||
self->ccauto[cc] = value;
|
||||
self->have_ccauto = true;
|
||||
|
||||
// Send
|
||||
if (sfizz_is_sustain_or_sostenuto(self, cc)) {
|
||||
sfizz_automate_hdcc(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)cc,
|
||||
value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Note(jpc) CC must be mapped by host, not handled here.
|
||||
// See LV2 midi:binding.
|
||||
#if defined(SFIZZ_LV2_PSA)
|
||||
switch (cc)
|
||||
{
|
||||
default:
|
||||
{
|
||||
sfizz_automate_hdcc(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)cc,
|
||||
value);
|
||||
self->cc_current[cc] = value;
|
||||
self->ccauto[cc] = value;
|
||||
self->have_ccauto = true;
|
||||
}
|
||||
break;
|
||||
case LV2_MIDI_CTL_ALL_NOTES_OFF:
|
||||
// TODO implemented as all-sound-off
|
||||
sfizz_all_sound_off(self->synth);
|
||||
break;
|
||||
case LV2_MIDI_CTL_ALL_SOUNDS_OFF:
|
||||
sfizz_all_sound_off(self->synth);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case LV2_MIDI_MSG_CHANNEL_PRESSURE:
|
||||
sfizz_send_channel_aftertouch(self->synth,
|
||||
(int)ev->time.frames,
|
||||
|
|
@ -829,7 +895,7 @@ static void
|
|||
run(LV2_Handle instance, uint32_t sample_count)
|
||||
{
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
assert(self->control_port && self->notify_port && self->automate_port);
|
||||
assert(self->control_port && self->automate_port);
|
||||
|
||||
if (!spin_mutex_trylock(self->synth_mutex))
|
||||
{
|
||||
|
|
@ -839,15 +905,10 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
}
|
||||
|
||||
// Set up dedicated forges to write on their respective ports.
|
||||
const size_t notify_capacity = self->notify_port->atom.size;
|
||||
lv2_atom_forge_set_buffer(&self->forge_notify, (uint8_t *)self->notify_port, notify_capacity);
|
||||
const size_t automate_capacity = self->automate_port->atom.size;
|
||||
lv2_atom_forge_set_buffer(&self->forge_automate, (uint8_t *)self->automate_port, automate_capacity);
|
||||
|
||||
// Start sequences in the respective output ports.
|
||||
LV2_Atom_Forge_Frame notify_frame;
|
||||
if (!lv2_atom_forge_sequence_head(&self->forge_notify, ¬ify_frame, 0))
|
||||
assert(false);
|
||||
LV2_Atom_Forge_Frame automate_frame;
|
||||
if (!lv2_atom_forge_sequence_head(&self->forge_automate, &automate_frame, 0))
|
||||
assert(false);
|
||||
|
|
@ -871,25 +932,25 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
lv2_atom_object_get(obj, self->patch_property_uri, &property, 0);
|
||||
if (!property) // Send the full state
|
||||
{
|
||||
sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||
sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||
sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||
sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc)
|
||||
sfizz_lv2_send_controller(self, &self->forge_notify, cc, self->cc_current[cc]);
|
||||
sfizz_lv2_send_controller(self, self->sfizz_notify_uri, cc, self->cc_current[cc]);
|
||||
}
|
||||
else if (property->body == self->sfizz_sfz_file_uri)
|
||||
{
|
||||
sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||
sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||
}
|
||||
else if (property->body == self->sfizz_scala_file_uri)
|
||||
{
|
||||
sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||
sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
int cc = sfizz_lv2_ccmap_unmap(self->ccmap, property->body);
|
||||
if (cc != -1)
|
||||
sfizz_lv2_send_controller(self, &self->forge_notify, unsigned(cc), self->cc_current[cc]);
|
||||
sfizz_lv2_send_controller(self, self->sfizz_notify_uri, unsigned(cc), self->cc_current[cc]);
|
||||
}
|
||||
}
|
||||
else if (obj->body.otype == self->time_position_uri)
|
||||
|
|
@ -1037,7 +1098,7 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
absl::optional<float> value = self->ccauto[cc];
|
||||
if (value) {
|
||||
sfizz_lv2_send_controller(self, &self->forge_automate, cc, *value);
|
||||
sfizz_lv2_send_controller(self, self->patch_set_uri, cc, *value);
|
||||
self->ccauto[cc] = absl::nullopt;
|
||||
}
|
||||
}
|
||||
|
|
@ -1046,7 +1107,20 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
|
||||
spin_mutex_unlock(self->synth_mutex);
|
||||
|
||||
lv2_atom_forge_pop(&self->forge_notify, ¬ify_frame);
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
if (self->ui_active)
|
||||
{
|
||||
self->rms_follower.process(self->output_buffers[0], self->output_buffers[1], sample_count);
|
||||
const simde__m128 rms = self->rms_follower.getRMS();
|
||||
const float *levels = (const float *)&rms;
|
||||
sfizz_lv2_send_levels(self, levels[0], levels[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->rms_follower.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
lv2_atom_forge_pop(&self->forge_automate, &automate_frame);
|
||||
}
|
||||
|
||||
|
|
@ -1168,7 +1242,7 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self)
|
|||
//
|
||||
const InstrumentDescription desc = parseDescriptionBlob(blob);
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
if (desc.ccUsed.test(cc)) {
|
||||
if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) {
|
||||
// Mark all the used CCs for automation with default values
|
||||
self->ccauto[cc] = desc.ccDefault[cc];
|
||||
self->have_ccauto = true;
|
||||
|
|
@ -1176,13 +1250,14 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self)
|
|||
self->cc_current[cc] = desc.ccDefault[cc];
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(self->sustain_or_sostenuto, desc.sustainOrSostenuto.data(),
|
||||
sizeof(self->sustain_or_sostenuto));
|
||||
}
|
||||
|
||||
static bool
|
||||
sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path)
|
||||
{
|
||||
bool status;
|
||||
|
||||
char buf[MAX_PATH_SIZE];
|
||||
if (file_path[0] == '\0')
|
||||
{
|
||||
|
|
@ -1191,18 +1266,7 @@ sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path)
|
|||
}
|
||||
|
||||
///
|
||||
const sfz::InstrumentFormatRegistry& formatRegistry = sfz::InstrumentFormatRegistry::getInstance();
|
||||
const sfz::InstrumentFormat* format = formatRegistry.getMatchingFormat(file_path);
|
||||
|
||||
if (!format)
|
||||
status = sfizz_load_file(self->synth, file_path);
|
||||
else {
|
||||
auto importer = format->createImporter();
|
||||
std::string virtual_path = std::string(file_path) + ".sfz";
|
||||
std::string sfz_text = importer->convertToSfz(file_path);
|
||||
status = sfizz_load_string(self->synth, virtual_path.c_str(), sfz_text.c_str());
|
||||
}
|
||||
|
||||
bool status = sfizz_load_or_import_file(self->synth, file_path, nullptr);
|
||||
sfizz_lv2_update_sfz_info(self);
|
||||
sfizz_lv2_update_file_info(self, file_path);
|
||||
return status;
|
||||
|
|
@ -1246,11 +1310,9 @@ restore(LV2_Handle instance,
|
|||
}
|
||||
|
||||
// Set default values
|
||||
self->last_keyswitch = -1;
|
||||
sfizz_lv2_get_default_sfz_path(self, self->sfz_file_path, MAX_PATH_SIZE);
|
||||
sfizz_lv2_get_default_scala_path(self, self->scala_file_path, MAX_PATH_SIZE);
|
||||
self->num_voices = DEFAULT_VOICES;
|
||||
self->preload_size = DEFAULT_PRELOAD;
|
||||
self->oversampling = DEFAULT_OVERSAMPLING;
|
||||
|
||||
// Fetch back the saved file path, if any
|
||||
size_t size;
|
||||
|
|
@ -1299,26 +1361,11 @@ restore(LV2_Handle instance,
|
|||
}
|
||||
}
|
||||
|
||||
value = retrieve(handle, self->sfizz_num_voices_uri, &size, &type, &val_flags);
|
||||
value = retrieve(handle, self->sfizz_last_keyswitch_uri, &size, &type, &val_flags);
|
||||
if (value)
|
||||
{
|
||||
int num_voices = *(const int *)value;
|
||||
if (num_voices > 0 && num_voices <= MAX_VOICES)
|
||||
self->num_voices = num_voices;
|
||||
}
|
||||
|
||||
value = retrieve(handle, self->sfizz_preload_size_uri, &size, &type, &val_flags);
|
||||
if (value)
|
||||
{
|
||||
unsigned int preload_size = *(const unsigned int *)value;
|
||||
self->preload_size = preload_size;
|
||||
}
|
||||
|
||||
value = retrieve(handle, self->sfizz_oversampling_uri, &size, &type, &val_flags);
|
||||
if (value)
|
||||
{
|
||||
sfizz_oversampling_factor_t oversampling = *(const sfizz_oversampling_factor_t *)value;
|
||||
self->oversampling = oversampling;
|
||||
int last_keyswitch = *(const int*)value;
|
||||
self->last_keyswitch = last_keyswitch;
|
||||
}
|
||||
|
||||
// Collect all CC values present in the state
|
||||
|
|
@ -1361,21 +1408,12 @@ restore(LV2_Handle instance,
|
|||
"[sfizz] Error while restoring the scale %s\n", self->scala_file_path);
|
||||
}
|
||||
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the number of voices to %d\n", self->num_voices);
|
||||
sfizz_set_num_voices(self->synth, self->num_voices);
|
||||
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the preload size to %d\n", self->preload_size);
|
||||
sfizz_set_preload_size(self->synth, self->preload_size);
|
||||
|
||||
lv2_log_note(&self->logger, "[sfizz] Restoring the oversampling to %d\n", self->oversampling);
|
||||
sfizz_set_oversampling_factor(self->synth, self->oversampling);
|
||||
|
||||
// Override default automation values with these from the state file
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
absl::optional<float> value = cc_values[cc];
|
||||
if (value) {
|
||||
// Set CC in the synth
|
||||
sfizz_send_hdcc(self->synth, 0, int(cc), *value);
|
||||
sfizz_automate_hdcc(self->synth, 0, int(cc), *value);
|
||||
// Mark CCs for automation with state values
|
||||
self->ccauto[cc] = *value;
|
||||
self->have_ccauto = true;
|
||||
|
|
@ -1384,6 +1422,11 @@ restore(LV2_Handle instance,
|
|||
}
|
||||
}
|
||||
|
||||
if (self->last_keyswitch >= 0 && self->last_keyswitch <= 127) {
|
||||
sfizz_send_hd_note_on(self->synth, 0, self->last_keyswitch, 1.0f);
|
||||
sfizz_send_hd_note_off(self->synth, 1, self->last_keyswitch, 0.0f);
|
||||
}
|
||||
|
||||
spin_mutex_unlock(self->synth_mutex);
|
||||
|
||||
return status;
|
||||
|
|
@ -1447,38 +1490,23 @@ save(LV2_Handle instance,
|
|||
if (map_path)
|
||||
free_path->free_path(free_path->handle, (char *)path);
|
||||
|
||||
// Save the number of voices
|
||||
store(handle,
|
||||
self->sfizz_num_voices_uri,
|
||||
&self->num_voices,
|
||||
sizeof(int),
|
||||
self->atom_int_uri,
|
||||
LV2_STATE_IS_POD);
|
||||
|
||||
// Save the preload size
|
||||
store(handle,
|
||||
self->sfizz_preload_size_uri,
|
||||
&self->preload_size,
|
||||
sizeof(unsigned int),
|
||||
self->atom_int_uri,
|
||||
LV2_STATE_IS_POD);
|
||||
|
||||
// Save the preload size
|
||||
store(handle,
|
||||
self->sfizz_oversampling_uri,
|
||||
&self->oversampling,
|
||||
sizeof(int),
|
||||
self->atom_int_uri,
|
||||
LV2_STATE_IS_POD);
|
||||
|
||||
// Save the CCs (used only)
|
||||
self->sfz_blob_mutex->lock();
|
||||
const InstrumentDescription desc = parseDescriptionBlob(
|
||||
absl::string_view((const char*)self->sfz_blob_data, self->sfz_blob_size));
|
||||
self->sfz_blob_mutex->unlock();
|
||||
|
||||
if (self->last_keyswitch >= 0 && self->last_keyswitch <= 127) {
|
||||
store(handle,
|
||||
self->sfizz_last_keyswitch_uri,
|
||||
&self->last_keyswitch,
|
||||
sizeof(int),
|
||||
self->atom_int_uri,
|
||||
LV2_STATE_IS_POD);
|
||||
}
|
||||
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
if (desc.ccUsed.test(cc)) {
|
||||
if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) {
|
||||
LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc));
|
||||
store(handle,
|
||||
urid,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,13 @@
|
|||
midnam:interface a lv2:ExtensionData .
|
||||
midnam:update a lv2:Feature .
|
||||
|
||||
<@LV2PLUGIN_URI@#stereo_output>
|
||||
a pg:StereoGroup, pg:OutputGroup ;
|
||||
lv2:symbol "stereo_output" ;
|
||||
lv2:name "Stereo output",
|
||||
"Sortie stéréo"@fr ,
|
||||
"Uscita stereo"@it .
|
||||
|
||||
<@LV2PLUGIN_URI@#config>
|
||||
a pg:Group ;
|
||||
lv2:symbol "config" ;
|
||||
|
|
@ -90,10 +97,12 @@ midnam:update a lv2:Feature .
|
|||
patch:writable <@LV2PLUGIN_URI@:sfzfile> ,
|
||||
<@LV2PLUGIN_URI@:tuningfile> ;
|
||||
|
||||
pg:mainOutput <@LV2PLUGIN_URI@#stereo_output> ;
|
||||
|
||||
lv2:port [
|
||||
a lv2:InputPort, atom:AtomPort ;
|
||||
atom:bufferType atom:Sequence ;
|
||||
atom:supports patch:Message, midi:MidiEvent, time:Position, <@LV2PLUGIN_URI@:OSCBlob> ;
|
||||
atom:supports patch:Message, midi:MidiEvent, time:Position, <@LV2PLUGIN_URI@:OSCBlob>, <@LV2PLUGIN_URI@:Notify> ;
|
||||
lv2:designation lv2:control ;
|
||||
lv2:index 0 ;
|
||||
lv2:symbol "control" ;
|
||||
|
|
@ -104,38 +113,33 @@ midnam:update a lv2:Feature .
|
|||
a lv2:OutputPort, atom:AtomPort ;
|
||||
atom:bufferType atom:Sequence ;
|
||||
atom:supports patch:Message, <@LV2PLUGIN_URI@:OSCBlob> ;
|
||||
lv2:index 1 ;
|
||||
lv2:symbol "notify" ;
|
||||
lv2:name "Notify",
|
||||
"Notification"@fr ;
|
||||
rsz:minimumSize 524288 ;
|
||||
] , [
|
||||
a lv2:OutputPort, atom:AtomPort ;
|
||||
atom:bufferType atom:Sequence ;
|
||||
atom:supports patch:Message ;
|
||||
lv2:designation lv2:control ;
|
||||
lv2:index 2 ;
|
||||
lv2:index 1 ;
|
||||
lv2:symbol "automate" ;
|
||||
lv2:name "Automate",
|
||||
"Automatisation"@fr ;
|
||||
rsz:minimumSize 524288 ;
|
||||
] , [
|
||||
a lv2:AudioPort, lv2:OutputPort ;
|
||||
lv2:index 3 ;
|
||||
lv2:index 2 ;
|
||||
lv2:symbol "out_left" ;
|
||||
lv2:name "Left Output",
|
||||
"Sortie gauche"@fr ,
|
||||
"Uscita Sinistra"@it
|
||||
"Uscita Sinistra"@it ;
|
||||
pg:group <@LV2PLUGIN_URI@#stereo_output> ;
|
||||
lv2:designation pg:left
|
||||
] , [
|
||||
a lv2:AudioPort, lv2:OutputPort ;
|
||||
lv2:index 4 ;
|
||||
lv2:index 3 ;
|
||||
lv2:symbol "out_right" ;
|
||||
lv2:name "Right Output",
|
||||
"Sortie droite"@fr ,
|
||||
"Uscita Destra"@it
|
||||
"Uscita Destra"@it ;
|
||||
pg:group <@LV2PLUGIN_URI@#stereo_output> ;
|
||||
lv2:designation pg:right
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 5 ;
|
||||
lv2:index 4 ;
|
||||
lv2:symbol "volume" ;
|
||||
lv2:name "Volume" ;
|
||||
lv2:default 0.0 ;
|
||||
|
|
@ -144,7 +148,7 @@ midnam:update a lv2:Feature .
|
|||
units:unit units:db
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 6 ;
|
||||
lv2:index 5 ;
|
||||
lv2:symbol "num_voices" ;
|
||||
lv2:name "Polyphony",
|
||||
"Polyphonie"@fr ,
|
||||
|
|
@ -189,7 +193,7 @@ midnam:update a lv2:Feature .
|
|||
] ;
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 7 ;
|
||||
lv2:index 6 ;
|
||||
lv2:symbol "oversampling" ;
|
||||
lv2:name "Oversampling factor",
|
||||
"Facteur de suréchantillonnage"@fr ,
|
||||
|
|
@ -224,7 +228,7 @@ midnam:update a lv2:Feature .
|
|||
] ;
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 8 ;
|
||||
lv2:index 7 ;
|
||||
lv2:symbol "preload_size" ;
|
||||
lv2:name "Preload size",
|
||||
"Taille préchargée"@fr ,
|
||||
|
|
@ -267,7 +271,7 @@ midnam:update a lv2:Feature .
|
|||
] ;
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 9 ;
|
||||
lv2:index 8 ;
|
||||
lv2:symbol "freewheeling" ;
|
||||
lv2:name "Freewheeling",
|
||||
"En roue libre (freewheeling)"@fr ,
|
||||
|
|
@ -279,7 +283,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 1 ;
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 10 ;
|
||||
lv2:index 9 ;
|
||||
lv2:symbol "scala_root_key" ;
|
||||
lv2:name "Scala root key",
|
||||
"Tonalité de base Scala"@fr ,
|
||||
|
|
@ -292,7 +296,7 @@ midnam:update a lv2:Feature .
|
|||
units:unit units:midiNote
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 11 ;
|
||||
lv2:index 10 ;
|
||||
lv2:symbol "tuning_frequency" ;
|
||||
lv2:name "Tuning frequency",
|
||||
"Fréquence d'accordage"@fr ,
|
||||
|
|
@ -304,7 +308,7 @@ midnam:update a lv2:Feature .
|
|||
units:unit units:hz
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 12 ;
|
||||
lv2:index 11 ;
|
||||
lv2:symbol "stretched_tuning" ;
|
||||
lv2:name "Stretched tuning",
|
||||
"Accordage étiré"@fr ,
|
||||
|
|
@ -316,7 +320,7 @@ midnam:update a lv2:Feature .
|
|||
units:unit units:coef
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 13 ;
|
||||
lv2:index 12 ;
|
||||
lv2:symbol "sample_quality" ;
|
||||
lv2:name "Sample quality",
|
||||
"Qualité des échantillons"@fr ,
|
||||
|
|
@ -327,7 +331,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 10.0
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 14 ;
|
||||
lv2:index 13 ;
|
||||
lv2:symbol "oscillator_quality" ;
|
||||
lv2:name "Oscillator quality",
|
||||
"Qualité des oscillateurs"@fr ,
|
||||
|
|
@ -338,7 +342,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 3.0
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 15 ;
|
||||
lv2:index 14 ;
|
||||
lv2:symbol "active_voices" ;
|
||||
lv2:name "Active voices",
|
||||
"Voix utilisées"@fr ;
|
||||
|
|
@ -349,7 +353,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 256 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 16 ;
|
||||
lv2:index 15 ;
|
||||
lv2:symbol "num_curves" ;
|
||||
lv2:name "Number of curves",
|
||||
"Nombre de courbes"@fr ;
|
||||
|
|
@ -360,7 +364,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 17 ;
|
||||
lv2:index 16 ;
|
||||
lv2:symbol "num_masters" ;
|
||||
lv2:name "Number of masters",
|
||||
"Nombre de maîtres"@fr ;
|
||||
|
|
@ -371,7 +375,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 18 ;
|
||||
lv2:index 17 ;
|
||||
lv2:symbol "num_groups" ;
|
||||
lv2:name "Number of groups",
|
||||
"Nombre de groupes"@fr ;
|
||||
|
|
@ -382,7 +386,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 19 ;
|
||||
lv2:index 18 ;
|
||||
lv2:symbol "num_regions" ;
|
||||
lv2:name "Number of regions",
|
||||
"Nombre de régions"@fr ;
|
||||
|
|
@ -393,7 +397,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 20 ;
|
||||
lv2:index 19 ;
|
||||
lv2:symbol "num_samples" ;
|
||||
lv2:name "Number of samples",
|
||||
"Nombre d'échantillons"@fr ;
|
||||
|
|
|
|||
|
|
@ -39,35 +39,38 @@
|
|||
#define SFIZZ__numVoices SFIZZ_URI ":" "numvoices"
|
||||
#define SFIZZ__preloadSize SFIZZ_URI ":" "preload_size"
|
||||
#define SFIZZ__oversampling SFIZZ_URI ":" "oversampling"
|
||||
#define SFIZZ__lastKeyswitch SFIZZ_URI ":" "last_keyswitch"
|
||||
// These ones are just for the worker
|
||||
#define SFIZZ__logStatus SFIZZ_URI ":" "log_status"
|
||||
#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification"
|
||||
// OSC atoms
|
||||
#define SFIZZ__OSCBlob SFIZZ_URI ":" "OSCBlob"
|
||||
#define SFIZZ__Notify SFIZZ_URI ":" "Notify"
|
||||
// Level atoms
|
||||
#define SFIZZ__AudioLevel SFIZZ_URI ":" "AudioLevel"
|
||||
|
||||
enum
|
||||
{
|
||||
SFIZZ_CONTROL = 0,
|
||||
SFIZZ_NOTIFY = 1,
|
||||
SFIZZ_AUTOMATE = 2,
|
||||
SFIZZ_LEFT = 3,
|
||||
SFIZZ_RIGHT = 4,
|
||||
SFIZZ_VOLUME = 5,
|
||||
SFIZZ_POLYPHONY = 6,
|
||||
SFIZZ_OVERSAMPLING = 7,
|
||||
SFIZZ_PRELOAD = 8,
|
||||
SFIZZ_FREEWHEELING = 9,
|
||||
SFIZZ_SCALA_ROOT_KEY = 10,
|
||||
SFIZZ_TUNING_FREQUENCY = 11,
|
||||
SFIZZ_STRETCH_TUNING = 12,
|
||||
SFIZZ_SAMPLE_QUALITY = 13,
|
||||
SFIZZ_OSCILLATOR_QUALITY = 14,
|
||||
SFIZZ_ACTIVE_VOICES = 15,
|
||||
SFIZZ_NUM_CURVES = 16,
|
||||
SFIZZ_NUM_MASTERS = 17,
|
||||
SFIZZ_NUM_GROUPS = 18,
|
||||
SFIZZ_NUM_REGIONS = 19,
|
||||
SFIZZ_NUM_SAMPLES = 20,
|
||||
SFIZZ_AUTOMATE = 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_SAMPLE_QUALITY = 12,
|
||||
SFIZZ_OSCILLATOR_QUALITY = 13,
|
||||
SFIZZ_ACTIVE_VOICES = 14,
|
||||
SFIZZ_NUM_CURVES = 15,
|
||||
SFIZZ_NUM_MASTERS = 16,
|
||||
SFIZZ_NUM_GROUPS = 17,
|
||||
SFIZZ_NUM_REGIONS = 18,
|
||||
SFIZZ_NUM_SAMPLES = 19,
|
||||
};
|
||||
|
||||
// For use with instance-access
|
||||
|
|
@ -89,6 +92,10 @@ bool sfizz_lv2_fetch_description(
|
|||
sfizz_plugin_t *self, const int *serial,
|
||||
uint8_t **descp, uint32_t *sizep, int *serialp);
|
||||
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
void sfizz_lv2_set_ui_active(sfizz_plugin_t *self, bool ui_active);
|
||||
#endif
|
||||
|
||||
// Mapping URID to CC and vice-versa
|
||||
struct sfizz_lv2_ccmap;
|
||||
sfizz_lv2_ccmap *sfizz_lv2_ccmap_create(LV2_URID_Map* map);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,13 @@ bool sfizz_lv2_fetch_description(
|
|||
return true;
|
||||
}
|
||||
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
void sfizz_lv2_set_ui_active(sfizz_plugin_t *self, bool ui_active)
|
||||
{
|
||||
self->ui_active = ui_active;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct sfizz_lv2_ccmap {
|
||||
LV2_URID *cc_to_urid;
|
||||
int *urid_to_cc;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "sfizz_lv2.h"
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
#include "plugin/RMSFollower.h"
|
||||
#endif
|
||||
#include <spin_mutex.h>
|
||||
#include <sfizz.h>
|
||||
#include <stdbool.h>
|
||||
|
|
@ -31,7 +34,6 @@ struct sfizz_plugin_t
|
|||
|
||||
// Ports
|
||||
const LV2_Atom_Sequence *control_port {};
|
||||
LV2_Atom_Sequence *notify_port {};
|
||||
LV2_Atom_Sequence *automate_port {};
|
||||
float *output_buffers[2] {};
|
||||
const float *volume_port {};
|
||||
|
|
@ -52,7 +54,6 @@ struct sfizz_plugin_t
|
|||
float *num_samples_port {};
|
||||
|
||||
// Atom forge
|
||||
LV2_Atom_Forge forge_notify {}; ///< Forge for writing notification atoms in run thread
|
||||
LV2_Atom_Forge forge_automate {}; ///< Forge for writing automation atoms in run thread
|
||||
LV2_Atom_Forge forge_secondary {}; ///< Forge for writing into other buffers
|
||||
|
||||
|
|
@ -85,10 +86,13 @@ struct sfizz_plugin_t
|
|||
LV2_URID sfizz_num_voices_uri {};
|
||||
LV2_URID sfizz_preload_size_uri {};
|
||||
LV2_URID sfizz_oversampling_uri {};
|
||||
LV2_URID sfizz_last_keyswitch_uri {};
|
||||
LV2_URID sfizz_log_status_uri {};
|
||||
LV2_URID sfizz_check_modification_uri {};
|
||||
LV2_URID sfizz_active_voices_uri {};
|
||||
LV2_URID sfizz_osc_blob_uri {};
|
||||
LV2_URID sfizz_notify_uri {};
|
||||
LV2_URID sfizz_audio_level_uri {};
|
||||
LV2_URID time_position_uri {};
|
||||
LV2_URID time_bar_uri {};
|
||||
LV2_URID time_bar_beat_uri {};
|
||||
|
|
@ -117,6 +121,7 @@ struct sfizz_plugin_t
|
|||
float sample_rate {};
|
||||
std::atomic<int> must_update_midnam {};
|
||||
volatile bool must_automate_cc {};
|
||||
int last_keyswitch { -1 };
|
||||
|
||||
// Current instrument description
|
||||
std::mutex *sfz_blob_mutex {};
|
||||
|
|
@ -124,6 +129,9 @@ struct sfizz_plugin_t
|
|||
const uint8_t *volatile sfz_blob_data {};
|
||||
volatile uint32_t sfz_blob_size {};
|
||||
|
||||
// Sostenuto or sustain
|
||||
char sustain_or_sostenuto[16] {};
|
||||
|
||||
// Current CC values in the synth (synchronized by `synth_mutex`)
|
||||
// updated by hdcc or file load
|
||||
float *cc_current {};
|
||||
|
|
@ -145,4 +153,10 @@ struct sfizz_plugin_t
|
|||
|
||||
// OSC
|
||||
uint8_t osc_temp[OSC_TEMP_SIZE] {};
|
||||
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
// UI
|
||||
volatile bool ui_active = false;
|
||||
RMSFollower rms_follower;
|
||||
#endif
|
||||
};
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
|||
|
||||
/// VSTGUIEditorInterface
|
||||
CFrame* getFrame() const override { return uiFrame.get(); }
|
||||
|
||||
int32_t getKnobMode () const override { return kLinearMode; }
|
||||
LV2_Atom_Forge atom_forge;
|
||||
LV2_URID atom_event_transfer_uri;
|
||||
LV2_URID atom_object_uri;
|
||||
|
|
@ -116,6 +116,8 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
|||
LV2_URID sfizz_sfz_file_uri;
|
||||
LV2_URID sfizz_scala_file_uri;
|
||||
LV2_URID sfizz_osc_blob_uri;
|
||||
LV2_URID sfizz_notify_uri;
|
||||
LV2_URID sfizz_audio_level_uri;
|
||||
std::unique_ptr<sfizz_lv2_ccmap, sfizz_lv2_ccmap_delete> ccmap;
|
||||
|
||||
uint8_t osc_temp[OSC_TEMP_SIZE];
|
||||
|
|
@ -217,6 +219,8 @@ instantiate(const LV2UI_Descriptor *descriptor,
|
|||
self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
|
||||
self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile);
|
||||
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob);
|
||||
self->sfizz_notify_uri = map->map(map->handle, SFIZZ__Notify);
|
||||
self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel);
|
||||
self->ccmap.reset(sfizz_lv2_ccmap_create(map));
|
||||
|
||||
// set up the resource path
|
||||
|
|
@ -287,6 +291,7 @@ static void
|
|||
cleanup(LV2UI_Handle ui)
|
||||
{
|
||||
sfizz_ui_t *self = (sfizz_ui_t *)ui;
|
||||
sfizz_lv2_set_ui_active(self->plugin, false);
|
||||
delete self;
|
||||
}
|
||||
|
||||
|
|
@ -391,6 +396,27 @@ port_event(LV2UI_Handle ui,
|
|||
if (sfizz_extract_message(LV2_ATOM_BODY_CONST(atom), atom->size, buffer, sizeof(buffer), &path, &sig, &args) > 0)
|
||||
self->uiReceiveMessage(path, sig, args);
|
||||
}
|
||||
else if (atom->type == self->sfizz_audio_level_uri) {
|
||||
const LV2_Atom_Vector *vector = reinterpret_cast<const LV2_Atom_Vector *>(atom);
|
||||
|
||||
if (vector->body.child_type == self->atom_float_uri &&
|
||||
vector->body.child_size == sizeof(float))
|
||||
{
|
||||
const uint8_t *vec_body = reinterpret_cast<const uint8_t*>(
|
||||
LV2_ATOM_CONTENTS_CONST(LV2_Atom_Vector, vector));
|
||||
const uint8_t *vec_end = reinterpret_cast<const uint8_t*>(
|
||||
LV2_ATOM_BODY_CONST(&vector->atom)) + vector->atom.size;
|
||||
|
||||
const float *levels = reinterpret_cast<const float *>(vec_body);
|
||||
uint32_t count = static_cast<uint32_t>((vec_end - vec_body) / sizeof(float));
|
||||
|
||||
float left = (count > 0) ? levels[0] : 0.0f;
|
||||
float right = (count > 1) ? levels[1] : 0.0f;
|
||||
|
||||
self->uiReceiveValue(EditId::LeftLevel, left);
|
||||
self->uiReceiveValue(EditId::RightLevel, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void)buffer_size;
|
||||
|
|
@ -421,7 +447,7 @@ sfizz_ui_update_description(sfizz_ui_t *self, const InstrumentDescription& desc)
|
|||
}
|
||||
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
bool ccUsed = desc.ccUsed.test(cc);
|
||||
bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc);
|
||||
self->uiReceiveValue(editIdForCCUsed(cc), float(ccUsed));
|
||||
if (ccUsed) {
|
||||
self->uiReceiveValue(editIdForCCDefault(cc), desc.ccDefault[cc]);
|
||||
|
|
@ -466,6 +492,8 @@ idle(LV2UI_Handle ui)
|
|||
(void)self;
|
||||
#endif
|
||||
|
||||
sfizz_lv2_set_ui_active(self->plugin, self->uiFrame->isVisible());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@
|
|||
lv2:optionalFeature ui:touch ;
|
||||
lv2:requiredFeature urid:map ;
|
||||
lv2:requiredFeature urid:unmap ;
|
||||
lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> .
|
||||
lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> .
|
||||
|
|
|
|||
57
plugins/puredata/CMakeLists.txt
Normal file
57
plugins/puredata/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Puredata plugin specific settings
|
||||
include(PuredataConfig)
|
||||
|
||||
set(PUREDATA_BINARY_DIR "${PROJECT_BINARY_DIR}/pd/sfizz")
|
||||
|
||||
set(PUREDATA_RESOURCES
|
||||
"sfizz~-help.pd"
|
||||
"example.sfz")
|
||||
|
||||
function(copy_puredata_resources TARGET SOURCE_DIR DESTINATION_DIR)
|
||||
set(_deps)
|
||||
foreach(res ${PUREDATA_RESOURCES})
|
||||
get_filename_component(_dir "${res}" DIRECTORY)
|
||||
file(MAKE_DIRECTORY "${DESTINATION_DIR}/${_dir}")
|
||||
add_custom_command(
|
||||
OUTPUT "${DESTINATION_DIR}/${res}"
|
||||
COMMAND "${CMAKE_COMMAND}" "-E" "copy"
|
||||
"${SOURCE_DIR}/${res}" "${DESTINATION_DIR}/${res}"
|
||||
DEPENDS "${SOURCE_DIR}/${res}")
|
||||
list(APPEND _deps "${DESTINATION_DIR}/${res}")
|
||||
endforeach()
|
||||
add_custom_target("${TARGET}_puredata_resources" DEPENDS ${_deps})
|
||||
add_dependencies("${TARGET}" "${TARGET}_puredata_resources")
|
||||
endfunction()
|
||||
|
||||
add_pd_external(sfizz_puredata "sfizz_puredata.c")
|
||||
target_compile_definitions(sfizz_puredata PRIVATE
|
||||
"SFIZZ_NUM_CCS=${SFIZZ_NUM_CCS}"
|
||||
"SFIZZ_VERSION=\"${CMAKE_PROJECT_VERSION}\"")
|
||||
target_link_libraries(sfizz_puredata PRIVATE sfizz::import sfizz::sfizz)
|
||||
|
||||
set_target_properties(sfizz_puredata PROPERTIES
|
||||
OUTPUT_NAME "sfizz"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${PUREDATA_BINARY_DIR}/$<0:>")
|
||||
|
||||
if(MINGW)
|
||||
set_property(TARGET sfizz_puredata APPEND_STRING
|
||||
PROPERTY LINK_FLAGS " -static")
|
||||
endif()
|
||||
|
||||
# Git build identifier
|
||||
target_link_libraries(sfizz_puredata PRIVATE sfizz-git-build-id)
|
||||
|
||||
# Copy resources
|
||||
copy_puredata_resources(sfizz_puredata
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${PUREDATA_BINARY_DIR}")
|
||||
|
||||
# Installation
|
||||
if(NOT MSVC)
|
||||
install(DIRECTORY "${PUREDATA_BINARY_DIR}" DESTINATION "${PDPLUGIN_INSTALL_DIR}"
|
||||
COMPONENT "puredata"
|
||||
USE_SOURCE_PERMISSIONS)
|
||||
bundle_dylibs(puredata
|
||||
"${PDPLUGIN_INSTALL_DIR}/sfizz/sfizz${PUREDATA_SUFFIX}"
|
||||
COMPONENT "puredata")
|
||||
endif()
|
||||
22
plugins/puredata/example.sfz
Normal file
22
plugins/puredata/example.sfz
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<group>
|
||||
ampeg_attack=0.25
|
||||
ampeg_decay=5
|
||||
ampeg_sustain=50
|
||||
ampeg_release=2
|
||||
fil_type=lpf_4p
|
||||
resonance=3
|
||||
cutoff=3000
|
||||
oscillator_phase=-1
|
||||
cutoff_oncc300=3600
|
||||
cutoff_curvecc300=1
|
||||
|
||||
<region>
|
||||
sample=*saw
|
||||
oscillator_multi=3
|
||||
oscillator_detune=10
|
||||
lfo1_freq=5
|
||||
lfo1_pitch=30
|
||||
|
||||
<region>
|
||||
sample=*triangle
|
||||
transpose=-12
|
||||
30
plugins/puredata/external/pd/LICENSE.txt
vendored
Normal file
30
plugins/puredata/external/pd/LICENSE.txt
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
This software is copyrighted by Miller Puckette and others. The following
|
||||
terms (the "Standard Improved BSD License") apply to all files associated with
|
||||
the software unless explicitly disclaimed in individual files:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote
|
||||
products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
601
plugins/puredata/external/pd/bin/pd.def
vendored
Normal file
601
plugins/puredata/external/pd/bin/pd.def
vendored
Normal file
|
|
@ -0,0 +1,601 @@
|
|||
LIBRARY pd
|
||||
EXPORTS
|
||||
ABORT
|
||||
array_free
|
||||
array_getcoordinate
|
||||
array_getfields
|
||||
array_new
|
||||
array_redraw
|
||||
array_resize
|
||||
array_resize_and_redraw
|
||||
atom_gensym
|
||||
atom_getfloat
|
||||
atom_getfloatarg
|
||||
atom_getint
|
||||
atom_getintarg
|
||||
atom_getsymbol
|
||||
atom_getsymbolarg
|
||||
atom_string
|
||||
audio_isopen
|
||||
audio_shouldkeepopen
|
||||
binbuf_add
|
||||
binbuf_addbinbuf
|
||||
binbuf_addsemi
|
||||
binbuf_addv
|
||||
binbuf_clear
|
||||
binbuf_duplicate
|
||||
binbuf_eval
|
||||
binbuf_evalfile
|
||||
binbuf_free
|
||||
binbuf_getnatom
|
||||
binbuf_gettext
|
||||
binbuf_getvec
|
||||
binbuf_new
|
||||
binbuf_print
|
||||
binbuf_read
|
||||
binbuf_read_via_canvas
|
||||
binbuf_read_via_path
|
||||
binbuf_realizedollsym
|
||||
binbuf_resize
|
||||
binbuf_restore
|
||||
binbuf_text
|
||||
binbuf_write
|
||||
bug
|
||||
canvas_addinlet
|
||||
canvas_addoutlet
|
||||
canvas_class DATA
|
||||
canvas_closebang
|
||||
canvas_connect
|
||||
canvas_create_editor
|
||||
canvas_dataproperties
|
||||
canvas_deletelinesfor
|
||||
canvas_deletelinesforio
|
||||
canvas_destroy_editor
|
||||
canvas_dirty
|
||||
canvas_disconnect
|
||||
canvas_dspstate DATA
|
||||
canvas_editmode
|
||||
canvas_fixlinesfor
|
||||
canvas_free
|
||||
canvas_getargs
|
||||
canvas_getcurrent
|
||||
canvas_getcurrentdir
|
||||
canvas_getdir
|
||||
canvas_getdollarzero
|
||||
canvas_getenv
|
||||
canvas_getindex
|
||||
canvas_getrootfor
|
||||
canvas_hitbox
|
||||
canvas_initbang
|
||||
canvas_isabstraction
|
||||
canvas_isconnected
|
||||
canvas_istable
|
||||
canvas_loadbang
|
||||
canvas_makebindsym
|
||||
canvas_makefilename
|
||||
canvas_new
|
||||
canvas_noundo
|
||||
canvas_open
|
||||
canvas_path_iterate
|
||||
canvas_readscalar
|
||||
canvas_realizedollar
|
||||
canvas_redraw
|
||||
canvas_redrawallfortemplate
|
||||
canvas_redrawallfortemplatecanvas
|
||||
canvas_rename
|
||||
canvas_resortinlets
|
||||
canvas_resortoutlets
|
||||
canvas_restoreconnections
|
||||
canvas_resume_dsp
|
||||
canvas_rminlet
|
||||
canvas_rmoutlet
|
||||
canvas_selectinrect
|
||||
canvas_setargs
|
||||
canvas_setcurrent
|
||||
canvas_setcursor
|
||||
canvas_setdeleting
|
||||
canvas_setundo
|
||||
canvas_showtext
|
||||
canvas_stowconnections
|
||||
canvas_suspend_dsp
|
||||
canvas_unsetcurrent
|
||||
canvas_update_dsp
|
||||
canvas_updatewindowlist
|
||||
canvas_vis
|
||||
canvas_whichfind DATA
|
||||
canvas_writescalar
|
||||
class_addanything
|
||||
class_addbang
|
||||
class_addcreator
|
||||
class_addlist
|
||||
class_addmethod
|
||||
class_addpointer
|
||||
class_addsymbol
|
||||
class_doaddfloat
|
||||
class_domainsignalin
|
||||
class_gethelpdir
|
||||
class_gethelpname
|
||||
class_getname
|
||||
class_getpropertiesfn
|
||||
class_getsavefn
|
||||
class_isdrawcommand
|
||||
class_new
|
||||
class_new64
|
||||
class_set_extern_dir
|
||||
class_setdrawcommand
|
||||
class_sethelpsymbol
|
||||
class_setparentwidget
|
||||
class_setpropertiesfn
|
||||
class_setsavefn
|
||||
class_setwidget
|
||||
clock_delay
|
||||
clock_free
|
||||
clock_getlogicaltime
|
||||
clock_getsystime
|
||||
clock_getsystimeafter
|
||||
clock_gettimesince
|
||||
clock_gettimesincewithunits
|
||||
clock_new
|
||||
clock_set
|
||||
clock_setunit
|
||||
clock_unset
|
||||
clone_class DATA
|
||||
copy_perform
|
||||
copybytes
|
||||
cos_table DATA
|
||||
dbtopow
|
||||
dbtorms
|
||||
dsp_add
|
||||
dsp_add_copy
|
||||
dsp_add_plus
|
||||
dsp_add_scalarcopy
|
||||
dsp_add_zero
|
||||
dsp_addv
|
||||
endpost
|
||||
error
|
||||
ex_Avg
|
||||
ex_Sum
|
||||
ex_avg
|
||||
ex_funcs DATA
|
||||
ex_getsym
|
||||
ex_mkvector
|
||||
ex_size
|
||||
ex_store
|
||||
ex_sum
|
||||
ex_symname
|
||||
fielddesc_cvtfromcoord
|
||||
fielddesc_cvttocoord
|
||||
fielddesc_getcoord
|
||||
fielddesc_setcoord
|
||||
floatinlet_new
|
||||
freebytes
|
||||
ftom
|
||||
g_editor_freepdinstance
|
||||
g_editor_newpdinstance
|
||||
g_template_freepdinstance
|
||||
g_template_newpdinstance
|
||||
garray_class DATA
|
||||
garray_getarray
|
||||
garray_getfloatarray
|
||||
garray_getfloatwords
|
||||
garray_getglist
|
||||
garray_npoints
|
||||
garray_redraw
|
||||
garray_resize
|
||||
garray_resize_long
|
||||
garray_setsaveit
|
||||
garray_template
|
||||
garray_usedindsp
|
||||
garray_vec
|
||||
gensym
|
||||
get_sys_dacsr
|
||||
get_sys_main_advance
|
||||
get_sys_schedadvance
|
||||
get_sys_schedblocksize
|
||||
get_sys_sleepgrain
|
||||
get_sys_soundin
|
||||
get_sys_soundout
|
||||
get_sys_time
|
||||
get_sys_time_per_dsp_tick
|
||||
getbytes
|
||||
getfn
|
||||
getzbytes
|
||||
gfxstub_deleteforkey
|
||||
gfxstub_new
|
||||
glist_add
|
||||
glist_addglist
|
||||
glist_arraydialog
|
||||
glist_clear
|
||||
glist_delete
|
||||
glist_deselect
|
||||
glist_dpixtodx
|
||||
glist_dpixtody
|
||||
glist_drawiofor
|
||||
glist_eraseiofor
|
||||
glist_findgraph
|
||||
glist_findrtext
|
||||
glist_fontheight
|
||||
glist_fontwidth
|
||||
glist_getcanvas
|
||||
glist_getfont
|
||||
glist_getnextxy
|
||||
glist_getzoom
|
||||
glist_glist
|
||||
glist_grab
|
||||
glist_init
|
||||
glist_isgraph
|
||||
glist_isselected
|
||||
glist_istoplevel
|
||||
glist_isvisible
|
||||
glist_mergefile
|
||||
glist_noselect
|
||||
glist_pixelstox
|
||||
glist_pixelstoy
|
||||
glist_read
|
||||
glist_redraw
|
||||
glist_retext
|
||||
glist_select
|
||||
glist_selectall
|
||||
glist_sort
|
||||
glist_valid DATA
|
||||
glist_writetobinbuf
|
||||
glist_xtopixels
|
||||
glist_ytopixels
|
||||
glob_evalfile
|
||||
glob_initfromgui
|
||||
glob_pdobject DATA
|
||||
glob_quit
|
||||
glob_setfilename
|
||||
gobj_activate
|
||||
gobj_click
|
||||
gobj_delete
|
||||
gobj_displace
|
||||
gobj_getrect
|
||||
gobj_save
|
||||
gobj_select
|
||||
gobj_shouldvis
|
||||
gobj_vis
|
||||
gpointer_check
|
||||
gpointer_copy
|
||||
gpointer_init
|
||||
gpointer_setarray
|
||||
gpointer_setglist
|
||||
gpointer_unset
|
||||
graph_array
|
||||
gstub_cutoff
|
||||
gstub_new
|
||||
gtemplate_get
|
||||
guiconnect_new
|
||||
guiconnect_notarget
|
||||
iem_fstyletoint
|
||||
iem_inttofstyle
|
||||
iem_inttosymargs
|
||||
iem_symargstoint
|
||||
iemgui_all_dollar2raute
|
||||
iemgui_all_dollararg2sym
|
||||
iemgui_all_loadcolors
|
||||
iemgui_all_raute2dollar
|
||||
iemgui_all_sym2dollararg
|
||||
iemgui_clip_font
|
||||
iemgui_clip_size
|
||||
iemgui_color
|
||||
iemgui_color_hex DATA
|
||||
iemgui_delete
|
||||
iemgui_delta
|
||||
iemgui_dialog
|
||||
iemgui_displace
|
||||
iemgui_dollar2raute
|
||||
iemgui_label
|
||||
iemgui_label_font
|
||||
iemgui_label_pos
|
||||
iemgui_new_dogetname
|
||||
iemgui_new_getnames
|
||||
iemgui_newzoom
|
||||
iemgui_pos
|
||||
iemgui_properties
|
||||
iemgui_raute2dollar
|
||||
iemgui_receive
|
||||
iemgui_save
|
||||
iemgui_select
|
||||
iemgui_send
|
||||
iemgui_size
|
||||
iemgui_verify_snd_ne_rcv
|
||||
iemgui_vis
|
||||
iemgui_vu_col DATA
|
||||
iemgui_vu_db2i DATA
|
||||
iemgui_vu_scale_str DATA
|
||||
iemgui_zoom
|
||||
ilog2
|
||||
inlet_free
|
||||
inlet_new
|
||||
inmidi_aftertouch
|
||||
inmidi_byte
|
||||
inmidi_controlchange
|
||||
inmidi_noteon
|
||||
inmidi_pitchbend
|
||||
inmidi_polyaftertouch
|
||||
inmidi_programchange
|
||||
inmidi_realtimein
|
||||
inmidi_sysex
|
||||
linetraverser_next
|
||||
linetraverser_skipobject
|
||||
linetraverser_start
|
||||
logpost
|
||||
max_ex_tab
|
||||
max_ex_tab_store
|
||||
max_ex_var
|
||||
max_ex_var_store
|
||||
mayer_fft
|
||||
mayer_fht
|
||||
mayer_ifft
|
||||
mayer_realfft
|
||||
mayer_realifft
|
||||
midi_getdevs
|
||||
mmio_close_audio
|
||||
mmio_getdevs
|
||||
mmio_open_audio
|
||||
mmio_reportidle
|
||||
mmio_send_dacs
|
||||
mtof
|
||||
namelist_append
|
||||
namelist_append_files
|
||||
namelist_free
|
||||
namelist_get
|
||||
nullfn
|
||||
obj_connect
|
||||
obj_disconnect
|
||||
obj_issignalinlet
|
||||
obj_issignaloutlet
|
||||
obj_list
|
||||
obj_nexttraverseoutlet
|
||||
obj_ninlets
|
||||
obj_noutlets
|
||||
obj_nsiginlets
|
||||
obj_nsigoutlets
|
||||
obj_saveformat
|
||||
obj_siginletindex
|
||||
obj_sigoutletindex
|
||||
obj_starttraverseoutlet
|
||||
open_via_helppath
|
||||
open_via_path
|
||||
outlet_anything
|
||||
outlet_bang
|
||||
outlet_float
|
||||
outlet_free
|
||||
outlet_getsymbol
|
||||
outlet_list
|
||||
outlet_new
|
||||
outlet_pointer
|
||||
outlet_setstacklim
|
||||
outlet_symbol
|
||||
pa_close_audio
|
||||
pa_getdevs
|
||||
pa_open_audio
|
||||
pa_send_dacs
|
||||
pd_bang
|
||||
pd_bind
|
||||
pd_canvasmaker DATA
|
||||
pd_checkglist
|
||||
pd_checkobject
|
||||
pd_compatibilitylevel DATA
|
||||
pd_emptylist
|
||||
pd_error
|
||||
pd_fft
|
||||
pd_findbyclass
|
||||
pd_float
|
||||
pd_forwardmess
|
||||
pd_free
|
||||
pd_getcanvaslist
|
||||
pd_getdspstate
|
||||
pd_getparentwidget
|
||||
pd_globallock
|
||||
pd_globalunlock
|
||||
pd_list
|
||||
pd_maininstance DATA
|
||||
pd_new
|
||||
pd_newest
|
||||
pd_objectmaker DATA
|
||||
pd_pointer
|
||||
pd_popsym
|
||||
pd_pushsym
|
||||
pd_symbol
|
||||
pd_typedmess
|
||||
pd_unbind
|
||||
pd_vmess
|
||||
plus_perform
|
||||
pointerinlet_new
|
||||
post
|
||||
postatom
|
||||
postfloat
|
||||
poststring
|
||||
powtodb
|
||||
q8_rsqrt
|
||||
q8_sqrt
|
||||
qrsqrt
|
||||
qsqrt
|
||||
resample_dsp
|
||||
resample_free
|
||||
resample_init
|
||||
resamplefrom_dsp
|
||||
resampleto_dsp
|
||||
resizebytes
|
||||
rmstodb
|
||||
rtext_activate
|
||||
rtext_displace
|
||||
rtext_draw
|
||||
rtext_erase
|
||||
rtext_free
|
||||
rtext_getseltext
|
||||
rtext_gettag
|
||||
rtext_gettext
|
||||
rtext_height
|
||||
rtext_key
|
||||
rtext_mouse
|
||||
rtext_new
|
||||
rtext_retext
|
||||
rtext_select
|
||||
rtext_width
|
||||
s_ DATA
|
||||
s__N DATA
|
||||
s__X DATA
|
||||
s_anything DATA
|
||||
s_bang DATA
|
||||
s_float DATA
|
||||
s_list DATA
|
||||
s_pointer DATA
|
||||
s_signal DATA
|
||||
s_symbol DATA
|
||||
s_x DATA
|
||||
s_y DATA
|
||||
scalar_class DATA
|
||||
scalar_getbasexy
|
||||
scalar_new
|
||||
scalar_redraw
|
||||
sched_geteventno
|
||||
sched_set_using_audio
|
||||
sched_tick
|
||||
signalinlet_new
|
||||
socketreceiver_new
|
||||
socketreceiver_read
|
||||
startpost
|
||||
symbolinlet_new
|
||||
sys_addhist
|
||||
sys_addpollfn
|
||||
sys_advance_samples DATA
|
||||
sys_audioapi DATA
|
||||
sys_audiodevnametonumber
|
||||
sys_audiodevnumbertoname
|
||||
sys_bail
|
||||
sys_bashfilename
|
||||
sys_clearhist
|
||||
sys_close
|
||||
sys_close_audio
|
||||
sys_close_midi
|
||||
sys_closesocket
|
||||
sys_debuglevel DATA
|
||||
sys_decodedialog
|
||||
sys_defaultfont DATA
|
||||
sys_defeatrt DATA
|
||||
sys_do_open_midi
|
||||
sys_externalschedlib DATA
|
||||
sys_fclose
|
||||
sys_flags DATA
|
||||
sys_font DATA
|
||||
sys_fontheight
|
||||
sys_fontweight DATA
|
||||
sys_fontwidth
|
||||
sys_fopen
|
||||
sys_get_audio_apis
|
||||
sys_get_audio_devs
|
||||
sys_get_audio_params
|
||||
sys_get_inchannels
|
||||
sys_get_midi_apis
|
||||
sys_get_midi_devs
|
||||
sys_get_midi_params
|
||||
sys_get_outchannels
|
||||
sys_getblksize
|
||||
sys_getmeters
|
||||
sys_getrealtime
|
||||
sys_getsr
|
||||
sys_getversion
|
||||
sys_gui
|
||||
sys_guicmd DATA
|
||||
sys_havegui
|
||||
sys_hipriority DATA
|
||||
sys_hostfontsize
|
||||
sys_idlehook DATA
|
||||
sys_init_fdpoll
|
||||
sys_initmidiqueue
|
||||
sys_isabsolutepath
|
||||
sys_libdir DATA
|
||||
sys_listdevs
|
||||
sys_listmididevs
|
||||
sys_load_lib
|
||||
sys_loadpreferences
|
||||
sys_lock
|
||||
sys_log_error
|
||||
sys_logerror
|
||||
sys_microsleep
|
||||
sys_midiapi DATA
|
||||
sys_midibytein
|
||||
sys_mididevnametonumber
|
||||
sys_mididevnumbertoname
|
||||
sys_midiindevlist DATA
|
||||
sys_midioutdevlist DATA
|
||||
sys_nearestfontsize
|
||||
sys_nmidiin DATA
|
||||
sys_nmidiout DATA
|
||||
sys_noloadbang DATA
|
||||
sys_open
|
||||
sys_open_absolute
|
||||
sys_open_midi
|
||||
sys_ouch
|
||||
sys_poll_midi
|
||||
sys_pollgui
|
||||
sys_pollmidiqueue
|
||||
sys_pretendguibytes
|
||||
sys_printhook DATA
|
||||
sys_printtostderr DATA
|
||||
sys_putmidibyte
|
||||
sys_putmidimess
|
||||
sys_queuegui
|
||||
sys_register_loader
|
||||
sys_reopen_audio
|
||||
sys_reopen_midi
|
||||
sys_reportidle
|
||||
sys_rmpollfn
|
||||
sys_save_audio_params
|
||||
sys_savepreferences
|
||||
sys_schedadvance DATA
|
||||
sys_send_dacs
|
||||
sys_set_audio_api
|
||||
sys_set_audio_settings
|
||||
sys_set_audio_settings_reopen
|
||||
sys_set_audio_state
|
||||
sys_set_midi_api
|
||||
sys_setchsr
|
||||
sys_setextrapath
|
||||
sys_setmiditimediff
|
||||
sys_sleepgrain DATA
|
||||
sys_sockerror
|
||||
sys_trylock
|
||||
sys_trytoopenone
|
||||
sys_unbashfilename
|
||||
sys_unixerror
|
||||
sys_unlock
|
||||
sys_unqueuegui
|
||||
sys_usestdpath DATA
|
||||
sys_verbose DATA
|
||||
sys_vgui
|
||||
sys_zoom_open DATA
|
||||
sys_zoomfontheight
|
||||
sys_zoomfontwidth
|
||||
template_find_field
|
||||
template_findbyname
|
||||
template_findcanvas
|
||||
template_free
|
||||
template_getfloat
|
||||
template_getsymbol
|
||||
template_match
|
||||
template_new
|
||||
template_notify
|
||||
template_setfloat
|
||||
template_setsymbol
|
||||
text_drawborder
|
||||
text_eraseborder
|
||||
text_setto
|
||||
text_widgetbehavior DATA
|
||||
text_xpix
|
||||
text_ypix
|
||||
value_get
|
||||
value_getfloat
|
||||
value_release
|
||||
value_setfloat
|
||||
verbose
|
||||
vinlet_class DATA
|
||||
voutlet_class DATA
|
||||
word_free
|
||||
word_init
|
||||
word_restore
|
||||
zero_perform
|
||||
zgetfn
|
||||
911
plugins/puredata/external/pd/include/m_pd.h
vendored
Normal file
911
plugins/puredata/external/pd/include/m_pd.h
vendored
Normal file
|
|
@ -0,0 +1,911 @@
|
|||
/* Copyright (c) 1997-1999 Miller Puckette.
|
||||
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
|
||||
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
|
||||
|
||||
#ifndef __m_pd_h_
|
||||
|
||||
#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PD_MAJOR_VERSION 0
|
||||
#define PD_MINOR_VERSION 51
|
||||
#define PD_BUGFIX_VERSION 4
|
||||
#define PD_TEST_VERSION ""
|
||||
extern int pd_compatibilitylevel; /* e.g., 43 for pd 0.43 compatibility */
|
||||
|
||||
/* old name for "MSW" flag -- we have to take it for the sake of many old
|
||||
"nmakefiles" for externs, which will define NT and not MSW */
|
||||
#if defined(NT) && !defined(MSW)
|
||||
#define MSW
|
||||
#endif
|
||||
|
||||
/* These pragmas are only used for MSVC, not MinGW or Cygwin <hans@at.or.at> */
|
||||
#ifdef _MSC_VER
|
||||
/* #pragma warning( disable : 4091 ) */
|
||||
#pragma warning( disable : 4305 ) /* uncast const double to float */
|
||||
#pragma warning( disable : 4244 ) /* uncast float/int conversion etc. */
|
||||
#pragma warning( disable : 4101 ) /* unused automatic variables */
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
/* the external storage class is "extern" in UNIX; in MSW it's ugly. */
|
||||
#ifdef _WIN32
|
||||
#ifdef PD_INTERNAL
|
||||
#define EXTERN __declspec(dllexport) extern
|
||||
#else
|
||||
#define EXTERN __declspec(dllimport) extern
|
||||
#endif /* PD_INTERNAL */
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/* On most c compilers, you can just say "struct foo;" to declare a
|
||||
structure whose elements are defined elsewhere. On MSVC, when compiling
|
||||
C (but not C++) code, you have to say "extern struct foo;". So we make
|
||||
a stupid macro: */
|
||||
#if defined(_MSC_VER) && !defined(_LANGUAGE_C_PLUS_PLUS) \
|
||||
&& !defined(__cplusplus)
|
||||
#define EXTERN_STRUCT extern struct
|
||||
#else
|
||||
#define EXTERN_STRUCT struct
|
||||
#endif
|
||||
|
||||
/* Define some attributes, specific to the compiler */
|
||||
#if defined(__GNUC__)
|
||||
#define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__ ((format (printf, a, b)))
|
||||
#else
|
||||
#define ATTRIBUTE_FORMAT_PRINTF(a, b)
|
||||
#endif
|
||||
|
||||
#if !defined(_SIZE_T) && !defined(_SIZE_T_)
|
||||
#include <stddef.h> /* just for size_t -- how lame! */
|
||||
#endif
|
||||
|
||||
/* Microsoft Visual Studio is not C99, but since VS2015 has included most C99 headers:
|
||||
https://docs.microsoft.com/en-us/previous-versions/hh409293(v=vs.140)#c-runtime-library
|
||||
These definitions recreate stdint.h types, but only in pre-2015 Visual Studio: */
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* for FILE, needed by sys_fopen() and sys_fclose() only */
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAXPDSTRING 1000 /* use this for anything you want */
|
||||
#define MAXPDARG 5 /* max number of args we can typecheck today */
|
||||
|
||||
/* signed and unsigned integer types the size of a pointer: */
|
||||
#if !defined(PD_LONGINTTYPE)
|
||||
#if defined(_WIN32) && defined(_WIN64)
|
||||
#define PD_LONGINTTYPE long long
|
||||
#else
|
||||
#define PD_LONGINTTYPE long
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(PD_FLOATSIZE)
|
||||
/* normally, our floats (t_float, t_sample,...) are 32bit */
|
||||
# define PD_FLOATSIZE 32
|
||||
#endif
|
||||
|
||||
#if PD_FLOATSIZE == 32
|
||||
# define PD_FLOATTYPE float
|
||||
/* an unsigned int of the same size as FLOATTYPE: */
|
||||
# define PD_FLOATUINTTYPE uint32_t
|
||||
|
||||
#elif PD_FLOATSIZE == 64
|
||||
# define PD_FLOATTYPE double
|
||||
# define PD_FLOATUINTTYPE uint64_t
|
||||
#else
|
||||
# error invalid FLOATSIZE: must be 32 or 64
|
||||
#endif
|
||||
|
||||
typedef PD_LONGINTTYPE t_int; /* pointer-size integer */
|
||||
typedef PD_FLOATTYPE t_float; /* a float type at most the same size */
|
||||
typedef PD_FLOATTYPE t_floatarg; /* float type for function calls */
|
||||
|
||||
typedef struct _symbol
|
||||
{
|
||||
const char *s_name;
|
||||
struct _class **s_thing;
|
||||
struct _symbol *s_next;
|
||||
} t_symbol;
|
||||
|
||||
EXTERN_STRUCT _array;
|
||||
#define t_array struct _array /* g_canvas.h */
|
||||
|
||||
/* pointers to glist and array elements go through a "stub" which sticks
|
||||
around after the glist or array is freed. The stub itself is deleted when
|
||||
both the glist/array is gone and the refcount is zero, ensuring that no
|
||||
gpointers are pointing here. */
|
||||
|
||||
#define GP_NONE 0 /* the stub points nowhere (has been cut off) */
|
||||
#define GP_GLIST 1 /* the stub points to a glist element */
|
||||
#define GP_ARRAY 2 /* ... or array */
|
||||
|
||||
typedef struct _gstub
|
||||
{
|
||||
union
|
||||
{
|
||||
struct _glist *gs_glist; /* glist we're in */
|
||||
struct _array *gs_array; /* array we're in */
|
||||
} gs_un;
|
||||
int gs_which; /* GP_GLIST/GP_ARRAY */
|
||||
int gs_refcount; /* number of gpointers pointing here */
|
||||
} t_gstub;
|
||||
|
||||
typedef struct _gpointer /* pointer to a gobj in a glist */
|
||||
{
|
||||
union
|
||||
{
|
||||
struct _scalar *gp_scalar; /* scalar we're in (if glist) */
|
||||
union word *gp_w; /* raw data (if array) */
|
||||
} gp_un;
|
||||
int gp_valid; /* number which must match gpointee */
|
||||
t_gstub *gp_stub; /* stub which points to glist/array */
|
||||
} t_gpointer;
|
||||
|
||||
typedef union word
|
||||
{
|
||||
t_float w_float;
|
||||
t_symbol *w_symbol;
|
||||
t_gpointer *w_gpointer;
|
||||
t_array *w_array;
|
||||
struct _binbuf *w_binbuf;
|
||||
int w_index;
|
||||
} t_word;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
A_NULL,
|
||||
A_FLOAT,
|
||||
A_SYMBOL,
|
||||
A_POINTER,
|
||||
A_SEMI,
|
||||
A_COMMA,
|
||||
A_DEFFLOAT,
|
||||
A_DEFSYM,
|
||||
A_DOLLAR,
|
||||
A_DOLLSYM,
|
||||
A_GIMME,
|
||||
A_CANT
|
||||
} t_atomtype;
|
||||
|
||||
#define A_DEFSYMBOL A_DEFSYM /* better name for this */
|
||||
|
||||
typedef struct _atom
|
||||
{
|
||||
t_atomtype a_type;
|
||||
union word a_w;
|
||||
} t_atom;
|
||||
|
||||
EXTERN_STRUCT _class;
|
||||
#define t_class struct _class
|
||||
|
||||
EXTERN_STRUCT _outlet;
|
||||
#define t_outlet struct _outlet
|
||||
|
||||
EXTERN_STRUCT _inlet;
|
||||
#define t_inlet struct _inlet
|
||||
|
||||
EXTERN_STRUCT _binbuf;
|
||||
#define t_binbuf struct _binbuf
|
||||
|
||||
EXTERN_STRUCT _clock;
|
||||
#define t_clock struct _clock
|
||||
|
||||
EXTERN_STRUCT _outconnect;
|
||||
#define t_outconnect struct _outconnect
|
||||
|
||||
EXTERN_STRUCT _glist;
|
||||
#define t_glist struct _glist
|
||||
#define t_canvas struct _glist /* LATER lose this */
|
||||
|
||||
EXTERN_STRUCT _template;
|
||||
|
||||
typedef t_class *t_pd; /* pure datum: nothing but a class pointer */
|
||||
|
||||
typedef struct _gobj /* a graphical object */
|
||||
{
|
||||
t_pd g_pd; /* pure datum header (class) */
|
||||
struct _gobj *g_next; /* next in list */
|
||||
} t_gobj;
|
||||
|
||||
typedef struct _scalar /* a graphical object holding data */
|
||||
{
|
||||
t_gobj sc_gobj; /* header for graphical object */
|
||||
t_symbol *sc_template; /* template name (LATER replace with pointer) */
|
||||
t_word sc_vec[1]; /* indeterminate-length array of words */
|
||||
} t_scalar;
|
||||
|
||||
typedef struct _text /* patchable object - graphical, with text */
|
||||
{
|
||||
t_gobj te_g; /* header for graphical object */
|
||||
t_binbuf *te_binbuf; /* holder for the text */
|
||||
t_outlet *te_outlet; /* linked list of outlets */
|
||||
t_inlet *te_inlet; /* linked list of inlets */
|
||||
short te_xpix; /* x&y location (within the toplevel) */
|
||||
short te_ypix;
|
||||
short te_width; /* requested width in chars, 0 if auto */
|
||||
unsigned int te_type:2; /* from defs below */
|
||||
} t_text;
|
||||
|
||||
#define T_TEXT 0 /* just a textual comment */
|
||||
#define T_OBJECT 1 /* a MAX style patchable object */
|
||||
#define T_MESSAGE 2 /* a MAX type message */
|
||||
#define T_ATOM 3 /* a cell to display a number or symbol */
|
||||
|
||||
#define te_pd te_g.g_pd
|
||||
|
||||
/* t_object is synonym for t_text (LATER unify them) */
|
||||
|
||||
typedef struct _text t_object;
|
||||
|
||||
#define ob_outlet te_outlet
|
||||
#define ob_inlet te_inlet
|
||||
#define ob_binbuf te_binbuf
|
||||
#define ob_pd te_g.g_pd
|
||||
#define ob_g te_g
|
||||
|
||||
typedef void (*t_method)(void);
|
||||
typedef void *(*t_newmethod)(void);
|
||||
|
||||
/* in ARM 64 a varargs prototype generates a different function call sequence
|
||||
from a fixed one, so in that special case we make a more restrictive
|
||||
definition for t_gotfn. This will break some code in the "chaos" package
|
||||
in Pd extended. (that code will run incorrectly anyhow so why not catch it
|
||||
at compile time anyhow.) */
|
||||
#if defined(__APPLE__) && defined(__aarch64__)
|
||||
typedef void (*t_gotfn)(void *x);
|
||||
#else
|
||||
typedef void (*t_gotfn)(void *x, ...);
|
||||
#endif
|
||||
|
||||
/* ---------------- pre-defined objects and symbols --------------*/
|
||||
EXTERN t_pd pd_objectmaker; /* factory for creating "object" boxes */
|
||||
EXTERN t_pd pd_canvasmaker; /* factory for creating canvases */
|
||||
|
||||
/* --------- prototypes from the central message system ----------- */
|
||||
EXTERN void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv);
|
||||
EXTERN void pd_forwardmess(t_pd *x, int argc, t_atom *argv);
|
||||
EXTERN t_symbol *gensym(const char *s);
|
||||
EXTERN t_gotfn getfn(const t_pd *x, t_symbol *s);
|
||||
EXTERN t_gotfn zgetfn(const t_pd *x, t_symbol *s);
|
||||
EXTERN void nullfn(void);
|
||||
EXTERN void pd_vmess(t_pd *x, t_symbol *s, const char *fmt, ...);
|
||||
|
||||
/* the following macros are for sending non-type-checkable messages, i.e.,
|
||||
using function lookup but circumventing type checking on arguments. Only
|
||||
use for internal messaging protected by A_CANT so that the message can't
|
||||
be generated at patch level. */
|
||||
#define mess0(x, s) ((*getfn((x), (s)))((x)))
|
||||
typedef void (*t_gotfn1)(void *x, void *arg1);
|
||||
#define mess1(x, s, a) ((*(t_gotfn1)getfn((x), (s)))((x), (a)))
|
||||
typedef void (*t_gotfn2)(void *x, void *arg1, void *arg2);
|
||||
#define mess2(x, s, a,b) ((*(t_gotfn2)getfn((x), (s)))((x), (a),(b)))
|
||||
typedef void (*t_gotfn3)(void *x, void *arg1, void *arg2, void *arg3);
|
||||
#define mess3(x, s, a,b,c) ((*(t_gotfn3)getfn((x), (s)))((x), (a),(b),(c)))
|
||||
typedef void (*t_gotfn4)(void *x,
|
||||
void *arg1, void *arg2, void *arg3, void *arg4);
|
||||
#define mess4(x, s, a,b,c,d) \
|
||||
((*(t_gotfn4)getfn((x), (s)))((x), (a),(b),(c),(d)))
|
||||
typedef void (*t_gotfn5)(void *x,
|
||||
void *arg1, void *arg2, void *arg3, void *arg4, void *arg5);
|
||||
#define mess5(x, s, a,b,c,d,e) \
|
||||
((*(t_gotfn5)getfn((x), (s)))((x), (a),(b),(c),(d),(e)))
|
||||
|
||||
EXTERN void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv);
|
||||
EXTERN t_pd *pd_newest(void);
|
||||
|
||||
/* --------------- memory management -------------------- */
|
||||
EXTERN void *getbytes(size_t nbytes);
|
||||
EXTERN void *getzbytes(size_t nbytes);
|
||||
EXTERN void *copybytes(const void *src, size_t nbytes);
|
||||
EXTERN void freebytes(void *x, size_t nbytes);
|
||||
EXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize);
|
||||
|
||||
/* -------------------- atoms ----------------------------- */
|
||||
|
||||
#define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_index = 0)
|
||||
#define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_index = 0)
|
||||
#define SETPOINTER(atom, gp) ((atom)->a_type = A_POINTER, \
|
||||
(atom)->a_w.w_gpointer = (gp))
|
||||
#define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f))
|
||||
#define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \
|
||||
(atom)->a_w.w_symbol = (s))
|
||||
#define SETDOLLAR(atom, n) ((atom)->a_type = A_DOLLAR, \
|
||||
(atom)->a_w.w_index = (n))
|
||||
#define SETDOLLSYM(atom, s) ((atom)->a_type = A_DOLLSYM, \
|
||||
(atom)->a_w.w_symbol= (s))
|
||||
|
||||
EXTERN t_float atom_getfloat(const t_atom *a);
|
||||
EXTERN t_int atom_getint(const t_atom *a);
|
||||
EXTERN t_symbol *atom_getsymbol(const t_atom *a);
|
||||
EXTERN t_symbol *atom_gensym(const t_atom *a);
|
||||
EXTERN t_float atom_getfloatarg(int which, int argc, const t_atom *argv);
|
||||
EXTERN t_int atom_getintarg(int which, int argc, const t_atom *argv);
|
||||
EXTERN t_symbol *atom_getsymbolarg(int which, int argc, const t_atom *argv);
|
||||
|
||||
EXTERN void atom_string(const t_atom *a, char *buf, unsigned int bufsize);
|
||||
|
||||
/* ------------------ binbufs --------------- */
|
||||
|
||||
EXTERN t_binbuf *binbuf_new(void);
|
||||
EXTERN void binbuf_free(t_binbuf *x);
|
||||
EXTERN t_binbuf *binbuf_duplicate(const t_binbuf *y);
|
||||
|
||||
EXTERN void binbuf_text(t_binbuf *x, const char *text, size_t size);
|
||||
EXTERN void binbuf_gettext(const t_binbuf *x, char **bufp, int *lengthp);
|
||||
EXTERN void binbuf_clear(t_binbuf *x);
|
||||
EXTERN void binbuf_add(t_binbuf *x, int argc, const t_atom *argv);
|
||||
EXTERN void binbuf_addv(t_binbuf *x, const char *fmt, ...);
|
||||
EXTERN void binbuf_addbinbuf(t_binbuf *x, const t_binbuf *y);
|
||||
EXTERN void binbuf_addsemi(t_binbuf *x);
|
||||
EXTERN void binbuf_restore(t_binbuf *x, int argc, const t_atom *argv);
|
||||
EXTERN void binbuf_print(const t_binbuf *x);
|
||||
EXTERN int binbuf_getnatom(const t_binbuf *x);
|
||||
EXTERN t_atom *binbuf_getvec(const t_binbuf *x);
|
||||
EXTERN int binbuf_resize(t_binbuf *x, int newsize);
|
||||
EXTERN void binbuf_eval(const t_binbuf *x, t_pd *target, int argc, const t_atom *argv);
|
||||
EXTERN int binbuf_read(t_binbuf *b, const char *filename, const char *dirname,
|
||||
int crflag);
|
||||
EXTERN int binbuf_read_via_canvas(t_binbuf *b, const char *filename, const t_canvas *canvas,
|
||||
int crflag);
|
||||
EXTERN int binbuf_read_via_path(t_binbuf *b, const char *filename, const char *dirname,
|
||||
int crflag);
|
||||
EXTERN int binbuf_write(const t_binbuf *x, const char *filename, const char *dir,
|
||||
int crflag);
|
||||
EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir);
|
||||
EXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, const t_atom *av,
|
||||
int tonew);
|
||||
|
||||
/* ------------------ clocks --------------- */
|
||||
|
||||
EXTERN t_clock *clock_new(void *owner, t_method fn);
|
||||
EXTERN void clock_set(t_clock *x, double systime);
|
||||
EXTERN void clock_delay(t_clock *x, double delaytime);
|
||||
EXTERN void clock_unset(t_clock *x);
|
||||
EXTERN void clock_setunit(t_clock *x, double timeunit, int sampflag);
|
||||
EXTERN double clock_getlogicaltime(void);
|
||||
EXTERN double clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */
|
||||
EXTERN double clock_gettimesince(double prevsystime);
|
||||
EXTERN double clock_gettimesincewithunits(double prevsystime,
|
||||
double units, int sampflag);
|
||||
EXTERN double clock_getsystimeafter(double delaytime);
|
||||
EXTERN void clock_free(t_clock *x);
|
||||
|
||||
/* ----------------- pure data ---------------- */
|
||||
EXTERN t_pd *pd_new(t_class *cls);
|
||||
EXTERN void pd_free(t_pd *x);
|
||||
EXTERN void pd_bind(t_pd *x, t_symbol *s);
|
||||
EXTERN void pd_unbind(t_pd *x, t_symbol *s);
|
||||
EXTERN t_pd *pd_findbyclass(t_symbol *s, const t_class *c);
|
||||
EXTERN void pd_pushsym(t_pd *x);
|
||||
EXTERN void pd_popsym(t_pd *x);
|
||||
EXTERN void pd_bang(t_pd *x);
|
||||
EXTERN void pd_pointer(t_pd *x, t_gpointer *gp);
|
||||
EXTERN void pd_float(t_pd *x, t_float f);
|
||||
EXTERN void pd_symbol(t_pd *x, t_symbol *s);
|
||||
EXTERN void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv);
|
||||
EXTERN void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv);
|
||||
#define pd_class(x) (*(x))
|
||||
|
||||
/* ----------------- pointers ---------------- */
|
||||
EXTERN void gpointer_init(t_gpointer *gp);
|
||||
EXTERN void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto);
|
||||
EXTERN void gpointer_unset(t_gpointer *gp);
|
||||
EXTERN int gpointer_check(const t_gpointer *gp, int headok);
|
||||
|
||||
/* ----------------- patchable "objects" -------------- */
|
||||
EXTERN t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1,
|
||||
t_symbol *s2);
|
||||
EXTERN t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp);
|
||||
EXTERN t_inlet *floatinlet_new(t_object *owner, t_float *fp);
|
||||
EXTERN t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp);
|
||||
EXTERN t_inlet *signalinlet_new(t_object *owner, t_float f);
|
||||
EXTERN void inlet_free(t_inlet *x);
|
||||
|
||||
EXTERN t_outlet *outlet_new(t_object *owner, t_symbol *s);
|
||||
EXTERN void outlet_bang(t_outlet *x);
|
||||
EXTERN void outlet_pointer(t_outlet *x, t_gpointer *gp);
|
||||
EXTERN void outlet_float(t_outlet *x, t_float f);
|
||||
EXTERN void outlet_symbol(t_outlet *x, t_symbol *s);
|
||||
EXTERN void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv);
|
||||
EXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv);
|
||||
EXTERN t_symbol *outlet_getsymbol(t_outlet *x);
|
||||
EXTERN void outlet_free(t_outlet *x);
|
||||
EXTERN t_object *pd_checkobject(t_pd *x);
|
||||
|
||||
|
||||
/* -------------------- canvases -------------- */
|
||||
|
||||
EXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir);
|
||||
|
||||
EXTERN void canvas_setargs(int argc, const t_atom *argv);
|
||||
EXTERN void canvas_getargs(int *argcp, t_atom **argvp);
|
||||
EXTERN t_symbol *canvas_getcurrentdir(void);
|
||||
EXTERN t_glist *canvas_getcurrent(void);
|
||||
EXTERN void canvas_makefilename(const t_glist *c, const char *file,
|
||||
char *result, int resultsize);
|
||||
EXTERN t_symbol *canvas_getdir(const t_glist *x);
|
||||
EXTERN char sys_font[]; /* default typeface set in s_main.c */
|
||||
EXTERN char sys_fontweight[]; /* default font weight set in s_main.c */
|
||||
EXTERN int sys_hostfontsize(int fontsize, int zoom);
|
||||
EXTERN int sys_zoomfontwidth(int fontsize, int zoom, int worstcase);
|
||||
EXTERN int sys_zoomfontheight(int fontsize, int zoom, int worstcase);
|
||||
EXTERN int sys_fontwidth(int fontsize);
|
||||
EXTERN int sys_fontheight(int fontsize);
|
||||
EXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b);
|
||||
EXTERN int canvas_open(const t_canvas *x, const char *name, const char *ext,
|
||||
char *dirresult, char **nameresult, unsigned int size, int bin);
|
||||
|
||||
/* ---------------- widget behaviors ---------------------- */
|
||||
|
||||
EXTERN_STRUCT _widgetbehavior;
|
||||
#define t_widgetbehavior struct _widgetbehavior
|
||||
|
||||
EXTERN_STRUCT _parentwidgetbehavior;
|
||||
#define t_parentwidgetbehavior struct _parentwidgetbehavior
|
||||
EXTERN const t_parentwidgetbehavior *pd_getparentwidget(t_pd *x);
|
||||
|
||||
/* -------------------- classes -------------- */
|
||||
|
||||
#define CLASS_DEFAULT 0 /* flags for new classes below */
|
||||
#define CLASS_PD 1
|
||||
#define CLASS_GOBJ 2
|
||||
#define CLASS_PATCHABLE 3
|
||||
#define CLASS_NOINLET 8
|
||||
|
||||
#define CLASS_TYPEMASK 3
|
||||
|
||||
EXTERN t_class *class_new(t_symbol *name, t_newmethod newmethod,
|
||||
t_method freemethod, size_t size, int flags, t_atomtype arg1, ...);
|
||||
|
||||
EXTERN t_class *class_new64(t_symbol *name, t_newmethod newmethod,
|
||||
t_method freemethod, size_t size, int flags, t_atomtype arg1, ...);
|
||||
|
||||
EXTERN void class_free(t_class *c);
|
||||
|
||||
#ifdef PDINSTANCE
|
||||
EXTERN t_class *class_getfirst(void);
|
||||
#endif
|
||||
|
||||
EXTERN void class_addcreator(t_newmethod newmethod, t_symbol *s,
|
||||
t_atomtype type1, ...);
|
||||
EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel,
|
||||
t_atomtype arg1, ...);
|
||||
EXTERN void class_addbang(t_class *c, t_method fn);
|
||||
EXTERN void class_addpointer(t_class *c, t_method fn);
|
||||
EXTERN void class_doaddfloat(t_class *c, t_method fn);
|
||||
EXTERN void class_addsymbol(t_class *c, t_method fn);
|
||||
EXTERN void class_addlist(t_class *c, t_method fn);
|
||||
EXTERN void class_addanything(t_class *c, t_method fn);
|
||||
EXTERN void class_sethelpsymbol(t_class *c, t_symbol *s);
|
||||
EXTERN void class_setwidget(t_class *c, const t_widgetbehavior *w);
|
||||
EXTERN void class_setparentwidget(t_class *c, const t_parentwidgetbehavior *w);
|
||||
EXTERN const char *class_getname(const t_class *c);
|
||||
EXTERN const char *class_gethelpname(const t_class *c);
|
||||
EXTERN const char *class_gethelpdir(const t_class *c);
|
||||
EXTERN void class_setdrawcommand(t_class *c);
|
||||
EXTERN int class_isdrawcommand(const t_class *c);
|
||||
EXTERN void class_domainsignalin(t_class *c, int onset);
|
||||
EXTERN void class_set_extern_dir(t_symbol *s);
|
||||
#define CLASS_MAINSIGNALIN(c, type, field) \
|
||||
class_domainsignalin(c, (char *)(&((type *)0)->field) - (char *)0)
|
||||
|
||||
/* prototype for functions to save Pd's to a binbuf */
|
||||
typedef void (*t_savefn)(t_gobj *x, t_binbuf *b);
|
||||
EXTERN void class_setsavefn(t_class *c, t_savefn f);
|
||||
EXTERN t_savefn class_getsavefn(const t_class *c);
|
||||
EXTERN void obj_saveformat(const t_object *x, t_binbuf *bb); /* add format to bb */
|
||||
|
||||
/* prototype for functions to open properties dialogs */
|
||||
typedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist);
|
||||
EXTERN void class_setpropertiesfn(t_class *c, t_propertiesfn f);
|
||||
EXTERN t_propertiesfn class_getpropertiesfn(const t_class *c);
|
||||
|
||||
typedef void (*t_classfreefn)(t_class *);
|
||||
EXTERN void class_setfreefn(t_class *c, t_classfreefn fn);
|
||||
|
||||
#ifndef PD_CLASS_DEF
|
||||
#define class_addbang(x, y) class_addbang((x), (t_method)(y))
|
||||
#define class_addpointer(x, y) class_addpointer((x), (t_method)(y))
|
||||
#define class_addfloat(x, y) class_doaddfloat((x), (t_method)(y))
|
||||
#define class_addsymbol(x, y) class_addsymbol((x), (t_method)(y))
|
||||
#define class_addlist(x, y) class_addlist((x), (t_method)(y))
|
||||
#define class_addanything(x, y) class_addanything((x), (t_method)(y))
|
||||
#endif
|
||||
|
||||
#if PD_FLOATSIZE == 64
|
||||
# define class_new class_new64
|
||||
#endif
|
||||
|
||||
/* ------------ printing --------------------------------- */
|
||||
EXTERN void post(const char *fmt, ...);
|
||||
EXTERN void startpost(const char *fmt, ...);
|
||||
EXTERN void poststring(const char *s);
|
||||
EXTERN void postfloat(t_floatarg f);
|
||||
EXTERN void postatom(int argc, const t_atom *argv);
|
||||
EXTERN void endpost(void);
|
||||
EXTERN void error(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
|
||||
EXTERN void verbose(int level, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3);
|
||||
EXTERN void bug(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
|
||||
EXTERN void pd_error(const void *object, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3);
|
||||
EXTERN void logpost(const void *object, const int level, const char *fmt, ...)
|
||||
ATTRIBUTE_FORMAT_PRINTF(3, 4);
|
||||
|
||||
/* ------------ system interface routines ------------------- */
|
||||
EXTERN int sys_isabsolutepath(const char *dir);
|
||||
EXTERN void sys_bashfilename(const char *from, char *to);
|
||||
EXTERN void sys_unbashfilename(const char *from, char *to);
|
||||
EXTERN int open_via_path(const char *dir, const char *name, const char *ext,
|
||||
char *dirresult, char **nameresult, unsigned int size, int bin);
|
||||
EXTERN int sched_geteventno(void);
|
||||
EXTERN double sys_getrealtime(void);
|
||||
EXTERN int (*sys_idlehook)(void); /* hook to add idle time computation */
|
||||
|
||||
/* Win32's open()/fopen() do not handle UTF-8 filenames so we need
|
||||
* these internal versions that handle UTF-8 filenames the same across
|
||||
* all platforms. They are recommended for use in external
|
||||
* objectclasses as well so they work with Unicode filenames on Windows */
|
||||
EXTERN int sys_open(const char *path, int oflag, ...);
|
||||
EXTERN int sys_close(int fd);
|
||||
EXTERN FILE *sys_fopen(const char *filename, const char *mode);
|
||||
EXTERN int sys_fclose(FILE *stream);
|
||||
|
||||
/* ------------ threading ------------------- */
|
||||
EXTERN void sys_lock(void);
|
||||
EXTERN void sys_unlock(void);
|
||||
EXTERN int sys_trylock(void);
|
||||
|
||||
|
||||
/* --------------- signals ----------------------------------- */
|
||||
|
||||
typedef PD_FLOATTYPE t_sample;
|
||||
typedef union _sampleint_union {
|
||||
t_sample f;
|
||||
PD_FLOATUINTTYPE i;
|
||||
} t_sampleint_union;
|
||||
#define MAXLOGSIG 32
|
||||
#define MAXSIGSIZE (1 << MAXLOGSIG)
|
||||
|
||||
typedef struct _signal
|
||||
{
|
||||
int s_n; /* number of points in the array */
|
||||
t_sample *s_vec; /* the array */
|
||||
t_float s_sr; /* sample rate */
|
||||
int s_refcount; /* number of times used */
|
||||
int s_isborrowed; /* whether we're going to borrow our array */
|
||||
struct _signal *s_borrowedfrom; /* signal to borrow it from */
|
||||
struct _signal *s_nextfree; /* next in freelist */
|
||||
struct _signal *s_nextused; /* next in used list */
|
||||
int s_vecsize; /* allocated size of array in points */
|
||||
} t_signal;
|
||||
|
||||
typedef t_int *(*t_perfroutine)(t_int *args);
|
||||
|
||||
EXTERN t_int *plus_perform(t_int *args);
|
||||
EXTERN t_int *zero_perform(t_int *args);
|
||||
EXTERN t_int *copy_perform(t_int *args);
|
||||
|
||||
EXTERN void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n);
|
||||
EXTERN void dsp_add_copy(t_sample *in, t_sample *out, int n);
|
||||
EXTERN void dsp_add_scalarcopy(t_float *in, t_sample *out, int n);
|
||||
EXTERN void dsp_add_zero(t_sample *out, int n);
|
||||
|
||||
EXTERN int sys_getblksize(void);
|
||||
EXTERN t_float sys_getsr(void);
|
||||
EXTERN int sys_get_inchannels(void);
|
||||
EXTERN int sys_get_outchannels(void);
|
||||
|
||||
EXTERN void dsp_add(t_perfroutine f, int n, ...);
|
||||
EXTERN void dsp_addv(t_perfroutine f, int n, t_int *vec);
|
||||
EXTERN void pd_fft(t_float *buf, int npoints, int inverse);
|
||||
EXTERN int ilog2(int n);
|
||||
|
||||
EXTERN void mayer_fht(t_sample *fz, int n);
|
||||
EXTERN void mayer_fft(int n, t_sample *real, t_sample *imag);
|
||||
EXTERN void mayer_ifft(int n, t_sample *real, t_sample *imag);
|
||||
EXTERN void mayer_realfft(int n, t_sample *real);
|
||||
EXTERN void mayer_realifft(int n, t_sample *real);
|
||||
|
||||
EXTERN float *cos_table;
|
||||
#define LOGCOSTABSIZE 9
|
||||
#define COSTABSIZE (1<<LOGCOSTABSIZE)
|
||||
|
||||
EXTERN int canvas_suspend_dsp(void);
|
||||
EXTERN void canvas_resume_dsp(int oldstate);
|
||||
EXTERN void canvas_update_dsp(void);
|
||||
EXTERN int canvas_dspstate;
|
||||
|
||||
/* up/downsampling */
|
||||
typedef struct _resample
|
||||
{
|
||||
int method; /* up/downsampling method ID */
|
||||
|
||||
int downsample; /* downsampling factor */
|
||||
int upsample; /* upsampling factor */
|
||||
|
||||
t_sample *s_vec; /* here we hold the resampled data */
|
||||
int s_n;
|
||||
|
||||
t_sample *coeffs; /* coefficients for filtering... */
|
||||
int coefsize;
|
||||
|
||||
t_sample *buffer; /* buffer for filtering */
|
||||
int bufsize;
|
||||
} t_resample;
|
||||
|
||||
EXTERN void resample_init(t_resample *x);
|
||||
EXTERN void resample_free(t_resample *x);
|
||||
|
||||
EXTERN void resample_dsp(t_resample *x, t_sample *in, int insize, t_sample *out, int outsize, int method);
|
||||
EXTERN void resamplefrom_dsp(t_resample *x, t_sample *in, int insize, int outsize, int method);
|
||||
EXTERN void resampleto_dsp(t_resample *x, t_sample *out, int insize, int outsize, int method);
|
||||
|
||||
/* ----------------------- utility functions for signals -------------- */
|
||||
EXTERN t_float mtof(t_float);
|
||||
EXTERN t_float ftom(t_float);
|
||||
EXTERN t_float rmstodb(t_float);
|
||||
EXTERN t_float powtodb(t_float);
|
||||
EXTERN t_float dbtorms(t_float);
|
||||
EXTERN t_float dbtopow(t_float);
|
||||
|
||||
EXTERN t_float q8_sqrt(t_float);
|
||||
EXTERN t_float q8_rsqrt(t_float);
|
||||
#ifndef N32
|
||||
EXTERN t_float qsqrt(t_float); /* old names kept for extern compatibility */
|
||||
EXTERN t_float qrsqrt(t_float);
|
||||
#endif
|
||||
|
||||
/* --------------------- data --------------------------------- */
|
||||
|
||||
/* graphical arrays */
|
||||
EXTERN_STRUCT _garray;
|
||||
#define t_garray struct _garray
|
||||
|
||||
EXTERN t_class *garray_class;
|
||||
EXTERN int garray_getfloatarray(t_garray *x, int *size, t_float **vec);
|
||||
EXTERN int garray_getfloatwords(t_garray *x, int *size, t_word **vec);
|
||||
EXTERN void garray_redraw(t_garray *x);
|
||||
EXTERN int garray_npoints(t_garray *x);
|
||||
EXTERN char *garray_vec(t_garray *x);
|
||||
EXTERN void garray_resize(t_garray *x, t_floatarg f); /* avoid; use this: */
|
||||
EXTERN void garray_resize_long(t_garray *x, long n); /* better version */
|
||||
EXTERN void garray_usedindsp(t_garray *x);
|
||||
EXTERN void garray_setsaveit(t_garray *x, int saveit);
|
||||
EXTERN t_glist *garray_getglist(t_garray *x);
|
||||
EXTERN t_array *garray_getarray(t_garray *x);
|
||||
EXTERN t_class *scalar_class;
|
||||
|
||||
EXTERN t_float *value_get(t_symbol *s);
|
||||
EXTERN void value_release(t_symbol *s);
|
||||
EXTERN int value_getfloat(t_symbol *s, t_float *f);
|
||||
EXTERN int value_setfloat(t_symbol *s, t_float f);
|
||||
|
||||
/* ------- GUI interface - functions to send strings to TK --------- */
|
||||
typedef void (*t_guicallbackfn)(t_gobj *client, t_glist *glist);
|
||||
|
||||
EXTERN void sys_vgui(const char *fmt, ...);
|
||||
EXTERN void sys_gui(const char *s);
|
||||
EXTERN void sys_pretendguibytes(int n);
|
||||
EXTERN void sys_queuegui(void *client, t_glist *glist, t_guicallbackfn f);
|
||||
EXTERN void sys_unqueuegui(void *client);
|
||||
/* dialog window creation and destruction */
|
||||
EXTERN void gfxstub_new(t_pd *owner, void *key, const char *cmd);
|
||||
EXTERN void gfxstub_deleteforkey(void *key);
|
||||
|
||||
extern t_class *glob_pdobject; /* object to send "pd" messages */
|
||||
|
||||
/*------------- Max 0.26 compatibility --------------------*/
|
||||
|
||||
/* the following reflects the new way classes are laid out, with the class
|
||||
pointing to the messlist and not vice versa. Externs shouldn't feel it. */
|
||||
typedef t_class *t_externclass;
|
||||
|
||||
EXTERN void c_extern(t_externclass *cls, t_newmethod newroutine,
|
||||
t_method freeroutine, t_symbol *name, size_t size, int tiny, \
|
||||
t_atomtype arg1, ...);
|
||||
EXTERN void c_addmess(t_method fn, t_symbol *sel, t_atomtype arg1, ...);
|
||||
|
||||
#define t_getbytes getbytes
|
||||
#define t_freebytes freebytes
|
||||
#define t_resizebytes resizebytes
|
||||
#define typedmess pd_typedmess
|
||||
#define vmess pd_vmess
|
||||
|
||||
/* A definition to help gui objects straddle 0.34-0.35 changes. If this is
|
||||
defined, there is a "te_xpix" field in objects, not a "te_xpos" as before: */
|
||||
|
||||
#define PD_USE_TE_XPIX
|
||||
|
||||
#ifndef _MSC_VER /* Microoft compiler can't handle "inline" function/macros */
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
|
||||
/* a test for NANs and denormals. Should only be necessary on i386. */
|
||||
#if PD_FLOATSIZE == 32
|
||||
|
||||
typedef union
|
||||
{
|
||||
t_float f;
|
||||
unsigned int ui;
|
||||
}t_bigorsmall32;
|
||||
|
||||
static inline int PD_BADFLOAT(t_float f) /* malformed float */
|
||||
{
|
||||
t_bigorsmall32 pun;
|
||||
pun.f = f;
|
||||
pun.ui &= 0x7f800000;
|
||||
return((pun.ui == 0) | (pun.ui == 0x7f800000));
|
||||
}
|
||||
|
||||
static inline int PD_BIGORSMALL(t_float f) /* exponent outside (-64,64) */
|
||||
{
|
||||
t_bigorsmall32 pun;
|
||||
pun.f = f;
|
||||
return((pun.ui & 0x20000000) == ((pun.ui >> 1) & 0x20000000));
|
||||
}
|
||||
|
||||
#elif PD_FLOATSIZE == 64
|
||||
|
||||
typedef union
|
||||
{
|
||||
t_float f;
|
||||
unsigned int ui[2];
|
||||
}t_bigorsmall64;
|
||||
|
||||
static inline int PD_BADFLOAT(t_float f) /* malformed double */
|
||||
{
|
||||
t_bigorsmall64 pun;
|
||||
pun.f = f;
|
||||
pun.ui[1] &= 0x7ff00000;
|
||||
return((pun.ui[1] == 0) | (pun.ui[1] == 0x7ff00000));
|
||||
}
|
||||
|
||||
static inline int PD_BIGORSMALL(t_float f) /* exponent outside (-512,512) */
|
||||
{
|
||||
t_bigorsmall64 pun;
|
||||
pun.f = f;
|
||||
return((pun.ui[1] & 0x20000000) == ((pun.ui[1] >> 1) & 0x20000000));
|
||||
}
|
||||
|
||||
#endif /* PD_FLOATSIZE */
|
||||
#else /* not INTEL or ARM */
|
||||
#define PD_BADFLOAT(f) 0
|
||||
#define PD_BIGORSMALL(f) 0
|
||||
#endif
|
||||
|
||||
#else /* _MSC_VER */
|
||||
#if PD_FLOATSIZE == 32
|
||||
#define PD_BADFLOAT(f) ((((*(unsigned int*)&(f))&0x7f800000)==0) || \
|
||||
(((*(unsigned int*)&(f))&0x7f800000)==0x7f800000))
|
||||
/* more stringent test: anything not between 1e-19 and 1e19 in absolute val */
|
||||
#define PD_BIGORSMALL(f) ((((*(unsigned int*)&(f))&0x60000000)==0) || \
|
||||
(((*(unsigned int*)&(f))&0x60000000)==0x60000000))
|
||||
#else /* 64 bits... don't know what to do here */
|
||||
#define PD_BADFLOAT(f) (!(((f) >= 0) || ((f) <= 0)))
|
||||
#define PD_BIGORSMALL(f) ((f) > 1e150 || (f) < -1e150 \
|
||||
|| (f) > -1e-150 && (f) < 1e-150 )
|
||||
#endif
|
||||
#endif /* _MSC_VER */
|
||||
/* get version number at run time */
|
||||
EXTERN void sys_getversion(int *major, int *minor, int *bugfix);
|
||||
|
||||
EXTERN_STRUCT _instancemidi;
|
||||
#define t_instancemidi struct _instancemidi
|
||||
|
||||
EXTERN_STRUCT _instanceinter;
|
||||
#define t_instanceinter struct _instanceinter
|
||||
|
||||
EXTERN_STRUCT _instancecanvas;
|
||||
#define t_instancecanvas struct _instancecanvas
|
||||
|
||||
EXTERN_STRUCT _instanceugen;
|
||||
#define t_instanceugen struct _instanceugen
|
||||
|
||||
EXTERN_STRUCT _instancestuff;
|
||||
#define t_instancestuff struct _instancestuff
|
||||
|
||||
#ifndef PDTHREADS
|
||||
#define PDTHREADS 1
|
||||
#endif
|
||||
|
||||
struct _pdinstance
|
||||
{
|
||||
double pd_systime; /* global time in Pd ticks */
|
||||
t_clock *pd_clock_setlist; /* list of set clocks */
|
||||
t_canvas *pd_canvaslist; /* list of all root canvases */
|
||||
struct _template *pd_templatelist; /* list of all templates */
|
||||
int pd_instanceno; /* ordinal number of this instance */
|
||||
t_symbol **pd_symhash; /* symbol table hash table */
|
||||
t_instancemidi *pd_midi; /* private stuff for x_midi.c */
|
||||
t_instanceinter *pd_inter; /* private stuff for s_inter.c */
|
||||
t_instanceugen *pd_ugen; /* private stuff for d_ugen.c */
|
||||
t_instancecanvas *pd_gui; /* semi-private stuff in g_canvas.h */
|
||||
t_instancestuff *pd_stuff; /* semi-private stuff in s_stuff.h */
|
||||
t_pd *pd_newest; /* most recently created object */
|
||||
#ifdef PDINSTANCE
|
||||
t_symbol pd_s_pointer;
|
||||
t_symbol pd_s_float;
|
||||
t_symbol pd_s_symbol;
|
||||
t_symbol pd_s_bang;
|
||||
t_symbol pd_s_list;
|
||||
t_symbol pd_s_anything;
|
||||
t_symbol pd_s_signal;
|
||||
t_symbol pd_s__N;
|
||||
t_symbol pd_s__X;
|
||||
t_symbol pd_s_x;
|
||||
t_symbol pd_s_y;
|
||||
t_symbol pd_s_;
|
||||
#endif
|
||||
#if PDTHREADS
|
||||
int pd_islocked;
|
||||
#endif
|
||||
};
|
||||
#define t_pdinstance struct _pdinstance
|
||||
EXTERN t_pdinstance pd_maininstance;
|
||||
|
||||
/* m_pd.c */
|
||||
#ifdef PDINSTANCE
|
||||
EXTERN t_pdinstance *pdinstance_new(void);
|
||||
EXTERN void pd_setinstance(t_pdinstance *x);
|
||||
EXTERN void pdinstance_free(t_pdinstance *x);
|
||||
#endif /* PDINSTANCE */
|
||||
|
||||
#if defined(PDTHREADS) && defined(PDINSTANCE)
|
||||
#ifdef _MSC_VER
|
||||
#define PERTHREAD __declspec(thread)
|
||||
#else
|
||||
#define PERTHREAD __thread
|
||||
#endif /* _MSC_VER */
|
||||
#else
|
||||
#define PERTHREAD
|
||||
#endif
|
||||
|
||||
#ifdef PDINSTANCE
|
||||
extern PERTHREAD t_pdinstance *pd_this;
|
||||
EXTERN t_pdinstance **pd_instances;
|
||||
EXTERN int pd_ninstances;
|
||||
#else
|
||||
#define pd_this (&pd_maininstance)
|
||||
#endif /* PDINSTANCE */
|
||||
|
||||
#ifdef PDINSTANCE
|
||||
#define s_pointer (pd_this->pd_s_pointer)
|
||||
#define s_float (pd_this->pd_s_float)
|
||||
#define s_symbol (pd_this->pd_s_symbol)
|
||||
#define s_bang (pd_this->pd_s_bang)
|
||||
#define s_list (pd_this->pd_s_list)
|
||||
#define s_anything (pd_this->pd_s_anything)
|
||||
#define s_signal (pd_this->pd_s_signal)
|
||||
#define s__N (pd_this->pd_s__N)
|
||||
#define s__X (pd_this->pd_s__X)
|
||||
#define s_x (pd_this->pd_s_x)
|
||||
#define s_y (pd_this->pd_s_y)
|
||||
#define s_ (pd_this->pd_s_)
|
||||
#else
|
||||
EXTERN t_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything,
|
||||
s_signal, s__N, s__X, s_x, s_y, s_;
|
||||
#endif
|
||||
|
||||
EXTERN t_canvas *pd_getcanvaslist(void);
|
||||
EXTERN int pd_getdspstate(void);
|
||||
|
||||
/* x_text.c */
|
||||
EXTERN t_binbuf *text_getbufbyname(t_symbol *s); /* get binbuf from text obj */
|
||||
EXTERN void text_notifybyname(t_symbol *s); /* notify it was modified */
|
||||
|
||||
#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define __m_pd_h_
|
||||
#endif /* __m_pd_h_ */
|
||||
336
plugins/puredata/sfizz_puredata.c
Normal file
336
plugins/puredata/sfizz_puredata.c
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
// 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 "GitBuildId.h"
|
||||
#include <m_pd.h>
|
||||
#include <sfizz.h>
|
||||
#include <sfizz/import/sfizz_import.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static t_class* cls_sfizz_tilde;
|
||||
|
||||
typedef struct _sfizz_tilde {
|
||||
t_object obj;
|
||||
t_outlet* outputs[2];
|
||||
t_inlet* input_cc;
|
||||
t_inlet* input_bend;
|
||||
t_inlet* input_touch;
|
||||
t_inlet* input_polytouch;
|
||||
sfizz_synth_t* synth;
|
||||
int midi[3];
|
||||
int midinum;
|
||||
t_symbol* dir;
|
||||
char* filepath;
|
||||
} t_sfizz_tilde;
|
||||
|
||||
static t_float clamp01(t_float x)
|
||||
{
|
||||
x = (x > 0) ? x : 0;
|
||||
x = (x < 1) ? x : 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
static t_float clampB1(t_float x)
|
||||
{
|
||||
x = (x > -1) ? x : -1;
|
||||
x = (x < 1) ? x : 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
static void sfizz_tilde_set_file(t_sfizz_tilde* self, const char* file)
|
||||
{
|
||||
const char* dir = self->dir->s_name;
|
||||
char* filepath;
|
||||
if (file[0] != '\0') {
|
||||
filepath = malloc(strlen(dir) + 1 + strlen(file) + 1);
|
||||
sprintf(filepath, "%s/%s", dir, file);
|
||||
}
|
||||
else {
|
||||
filepath = malloc(1);
|
||||
*filepath = '\0';
|
||||
}
|
||||
free(self->filepath);
|
||||
self->filepath = filepath;
|
||||
}
|
||||
|
||||
static bool sfizz_tilde_do_load(t_sfizz_tilde* self)
|
||||
{
|
||||
bool loaded;
|
||||
if (self->filepath[0] != '\0')
|
||||
loaded = sfizz_load_or_import_file(self->synth, self->filepath, NULL);
|
||||
else
|
||||
loaded = sfizz_load_string(self->synth, "default.sfz", "<region>sample=*sine");
|
||||
return loaded;
|
||||
}
|
||||
|
||||
static void* sfizz_tilde_new(t_symbol* sym, int argc, t_atom argv[])
|
||||
{
|
||||
(void)sym;
|
||||
t_sfizz_tilde* self = (t_sfizz_tilde*)pd_new(cls_sfizz_tilde);
|
||||
|
||||
const char* file;
|
||||
if (argc == 0)
|
||||
file = "";
|
||||
else if (argc == 1 && argv[0].a_type == A_SYMBOL)
|
||||
file = argv[0].a_w.w_symbol->s_name;
|
||||
else {
|
||||
pd_free((t_pd*)self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->dir = canvas_getcurrentdir();
|
||||
|
||||
self->outputs[0] = outlet_new(&self->obj, &s_signal);
|
||||
self->outputs[1] = outlet_new(&self->obj, &s_signal);
|
||||
|
||||
self->input_cc = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("cc"));
|
||||
self->input_bend = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("bend"));
|
||||
self->input_touch = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("touch"));
|
||||
self->input_polytouch = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("polytouch"));
|
||||
|
||||
sfizz_synth_t* synth = sfizz_create_synth();
|
||||
self->synth = synth;
|
||||
|
||||
sfizz_set_sample_rate(synth, sys_getsr());
|
||||
sfizz_set_samples_per_block(synth, sys_getblksize());
|
||||
|
||||
sfizz_tilde_set_file(self, file);
|
||||
if (!sfizz_tilde_do_load(self)) {
|
||||
pd_free((t_pd*)self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void sfizz_tilde_free(t_sfizz_tilde* self)
|
||||
{
|
||||
if (self->filepath)
|
||||
free(self->filepath);
|
||||
if (self->synth)
|
||||
sfizz_free(self->synth);
|
||||
if (self->outputs[0])
|
||||
outlet_free(self->outputs[0]);
|
||||
if (self->outputs[1])
|
||||
outlet_free(self->outputs[1]);
|
||||
if (self->input_cc)
|
||||
inlet_free(self->input_cc);
|
||||
if (self->input_bend)
|
||||
inlet_free(self->input_bend);
|
||||
if (self->input_touch)
|
||||
inlet_free(self->input_touch);
|
||||
if (self->input_polytouch)
|
||||
inlet_free(self->input_polytouch);
|
||||
}
|
||||
|
||||
static t_int* sfizz_tilde_perform(t_int* w)
|
||||
{
|
||||
t_sfizz_tilde* self;
|
||||
t_sample* outputs[2];
|
||||
t_int nframes;
|
||||
|
||||
w++;
|
||||
self = (t_sfizz_tilde*)*w++;
|
||||
outputs[0] = (t_sample*)*w++;
|
||||
outputs[1] = (t_sample*)*w++;
|
||||
nframes = (t_int)*w++;
|
||||
|
||||
sfizz_render_block(self->synth, outputs, 2, nframes);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
static void sfizz_tilde_dsp(t_sfizz_tilde* self, t_signal** sp)
|
||||
{
|
||||
dsp_add(
|
||||
&sfizz_tilde_perform, 4, (t_int)self,
|
||||
(t_int)sp[0]->s_vec, (t_int)sp[1]->s_vec, (t_int)sp[0]->s_n);
|
||||
}
|
||||
|
||||
static void sfizz_tilde_list(t_sfizz_tilde* self, t_symbol* sym, int argc, t_atom* argv)
|
||||
{
|
||||
(void)sym;
|
||||
|
||||
if (argc == 2 && argv[0].a_type == A_FLOAT && argv[1].a_type == A_FLOAT) {
|
||||
int key = (int)argv[0].a_w.w_float;
|
||||
if (key < 0 || key > 127)
|
||||
return;
|
||||
t_float vel = clamp01(argv[1].a_w.w_float / 127);
|
||||
if (vel > 0)
|
||||
sfizz_send_hd_note_on(self->synth, 0, key, vel);
|
||||
else
|
||||
sfizz_send_hd_note_off(self->synth, 0, key, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void sfizz_tilde_midiin(t_sfizz_tilde* self, t_float f)
|
||||
{
|
||||
int byte = (int)f;
|
||||
bool isstatus = (byte & 0x80) != 0;
|
||||
|
||||
int* midi = self->midi;
|
||||
int midinum = self->midinum;
|
||||
|
||||
//
|
||||
if (isstatus) {
|
||||
midi[0] = byte;
|
||||
midinum = 1;
|
||||
}
|
||||
else if (midinum != -1 && midinum < 3)
|
||||
midi[midinum++] = byte;
|
||||
else
|
||||
midinum = -1;
|
||||
|
||||
//
|
||||
switch (midinum) {
|
||||
case 2:
|
||||
switch (midi[0] & 0xf0) {
|
||||
case 0xd0: // channel aftertouch
|
||||
sfizz_send_channel_aftertouch(self->synth, 0, midi[1]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (midi[0] & 0xf0) {
|
||||
case 0x90: // note on
|
||||
if (midi[2] == 0)
|
||||
goto noteoff;
|
||||
sfizz_send_note_on(self->synth, 0, midi[1], midi[2]);
|
||||
break;
|
||||
case 0x80: // note off
|
||||
noteoff:
|
||||
sfizz_send_note_off(self->synth, 0, midi[1], midi[2]);
|
||||
break;
|
||||
case 0xb0: // controller
|
||||
sfizz_send_cc(self->synth, 0, midi[1], midi[2]);
|
||||
break;
|
||||
case 0xa0: // key aftertouch
|
||||
sfizz_send_poly_aftertouch(self->synth, 0, midi[1], midi[2]);
|
||||
break;
|
||||
case 0xe0: // pitch bend
|
||||
sfizz_send_pitch_wheel(self->synth, 0, (midi[1] + (midi[2] << 7)) - 8192);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
self->midinum = midinum;
|
||||
}
|
||||
|
||||
static void sfizz_tilde_load(t_sfizz_tilde* self, t_symbol* sym)
|
||||
{
|
||||
sfizz_tilde_set_file(self, sym->s_name);
|
||||
sfizz_tilde_do_load(self);
|
||||
}
|
||||
|
||||
static void sfizz_tilde_reload(t_sfizz_tilde* self, t_float value)
|
||||
{
|
||||
(void)value;
|
||||
sfizz_tilde_do_load(self);
|
||||
}
|
||||
|
||||
static void sfizz_tilde_hdcc(t_sfizz_tilde* self, t_float f1, t_float f2)
|
||||
{
|
||||
int cc = (int)f1;
|
||||
if (cc < 0 || cc >= SFIZZ_NUM_CCS)
|
||||
return;
|
||||
sfizz_automate_hdcc(self->synth, 0, (int)cc, clamp01(f2));
|
||||
}
|
||||
|
||||
static void sfizz_tilde_cc(t_sfizz_tilde* self, t_float f1, t_float f2)
|
||||
{
|
||||
sfizz_tilde_hdcc(self, f1, f2 / 127);
|
||||
}
|
||||
|
||||
static void sfizz_tilde_hdbend(t_sfizz_tilde* self, t_float f1)
|
||||
{
|
||||
sfizz_send_hd_pitch_wheel(self->synth, 0, clampB1(f1));
|
||||
}
|
||||
|
||||
static void sfizz_tilde_bend(t_sfizz_tilde* self, t_float f1)
|
||||
{
|
||||
return sfizz_tilde_hdbend(self, f1 / 8191);
|
||||
}
|
||||
|
||||
static void sfizz_tilde_hdtouch(t_sfizz_tilde* self, t_float f1)
|
||||
{
|
||||
sfizz_send_hd_channel_aftertouch(self->synth, 0, clamp01(f1));
|
||||
}
|
||||
|
||||
static void sfizz_tilde_touch(t_sfizz_tilde* self, t_float f1)
|
||||
{
|
||||
sfizz_tilde_hdtouch(self, f1 / 127);
|
||||
}
|
||||
|
||||
static void sfizz_tilde_hdpolytouch(t_sfizz_tilde* self, t_float key, t_float f2)
|
||||
{
|
||||
if (key < 0 || key > 127)
|
||||
return;
|
||||
sfizz_send_hd_poly_aftertouch(self->synth, 0, (int)key, clamp01(f2));
|
||||
}
|
||||
|
||||
static void sfizz_tilde_polytouch(t_sfizz_tilde* self, t_float f1, t_float f2)
|
||||
{
|
||||
sfizz_tilde_hdpolytouch(self, f1, f2 / 127);
|
||||
}
|
||||
|
||||
static void sfizz_tilde_voices(t_sfizz_tilde* self, t_float f1)
|
||||
{
|
||||
int numvoices = (int)f1;
|
||||
numvoices = (numvoices < 1) ? 1 : numvoices;
|
||||
sfizz_set_num_voices(self->synth, numvoices);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
__declspec(dllexport)
|
||||
#else
|
||||
__attribute__((visibility("default")))
|
||||
#endif
|
||||
void sfizz_setup()
|
||||
{
|
||||
if (GitBuildId[0])
|
||||
post("sfizz external for Puredata, version '%s.%s'", SFIZZ_VERSION, GitBuildId);
|
||||
else
|
||||
post("sfizz external for Puredata, version '%s'", SFIZZ_VERSION);
|
||||
|
||||
cls_sfizz_tilde = class_new(
|
||||
gensym("sfizz~"),
|
||||
(t_newmethod)&sfizz_tilde_new,
|
||||
(t_method)&sfizz_tilde_free,
|
||||
sizeof(t_sfizz_tilde),
|
||||
CLASS_DEFAULT, A_GIMME, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_dsp, gensym("dsp"), A_CANT, A_NULL);
|
||||
class_addlist(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_list);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_midiin, &s_float, A_FLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_load, gensym("load"), A_DEFSYM, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_reload, gensym("reload"), A_DEFFLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_cc, gensym("cc"), A_FLOAT, A_FLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_hdcc, gensym("hdcc"), A_FLOAT, A_FLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_bend, gensym("bend"), A_FLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_hdbend, gensym("hdbend"), A_FLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_touch, gensym("touch"), A_FLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_hdtouch, gensym("hdtouch"), A_FLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_polytouch, gensym("polytouch"), A_FLOAT, A_FLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_hdpolytouch, gensym("hdpolytouch"), A_FLOAT, A_FLOAT, A_NULL);
|
||||
class_addmethod(
|
||||
cls_sfizz_tilde, (t_method)&sfizz_tilde_voices, gensym("voices"), A_FLOAT, A_NULL);
|
||||
}
|
||||
60
plugins/puredata/sfizz~-help.pd
Normal file
60
plugins/puredata/sfizz~-help.pd
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#N canvas 614 464 709 426 12;
|
||||
#X declare -lib sfizz;
|
||||
#X obj 4 3 cnv 15 700 80 empty empty sfizz 20 30 0 40 -261682 -66577
|
||||
0;
|
||||
#X text 45 54 A synthesizer for instruments in SFZ format;
|
||||
#X text 33 167 create a SFZ instrument;
|
||||
#X obj 26 323 midiin;
|
||||
#X text 23 300 connect MIDI-in;
|
||||
#X obj 162 223 dac~;
|
||||
#X msg 177 345 voices \$1;
|
||||
#X text 175 301 modify the polyphony;
|
||||
#X obj 376 326 hsl 127 15 0 1 0 1 empty empty empty -2 -8 0 10 -262144
|
||||
-1 -1 6300 1;
|
||||
#X floatatom 466 346 5 0 0 0 - - -;
|
||||
#X text 372 302 modulate a parameter;
|
||||
#X msg 373 346 hdcc 300 \$1;
|
||||
#X obj 27 115 declare -lib sfizz;
|
||||
#X obj 163 192 sfizz~ example.sfz;
|
||||
#X text 18 93 load the sfizz library;
|
||||
#X msg 221 115 \; pd dsp 1;
|
||||
#X text 218 94 click to turn on DSP;
|
||||
#X text 153 247 output stereo audio;
|
||||
#X msg 398 223 reload;
|
||||
#X msg 498 223 load example.sfz;
|
||||
#X text 396 200 reload the instrument or load another;
|
||||
#X obj 241 165 r \$0-input;
|
||||
#X obj 26 348 s \$0-input;
|
||||
#X obj 177 370 s \$0-input;
|
||||
#X obj 373 371 s \$0-input;
|
||||
#X obj 398 248 s \$0-input;
|
||||
#X obj 498 248 s \$0-input;
|
||||
#X obj 177 326 nbx 5 14 1 256 0 1 empty empty empty 0 -8 0 10 -262144
|
||||
-1 -1 64 256;
|
||||
#X text 567 302 send a note;
|
||||
#X obj 568 348 makenote 100 500;
|
||||
#X obj 568 373 list;
|
||||
#X obj 568 398 s \$0-input;
|
||||
#X msg 568 323 60;
|
||||
#X obj 386 92 cnv 15 300 100 empty empty Inlets 20 12 0 14 -233017
|
||||
-66577 0;
|
||||
#X text 395 140 3: pitch bend;
|
||||
#X text 395 154 4: channel aftertouch;
|
||||
#X text 395 168 5: polyphonic aftertouch and key;
|
||||
#X text 395 126 2: controller and value;
|
||||
#X text 395 112 1: note and velocity;
|
||||
#X connect 3 0 22 0;
|
||||
#X connect 6 0 23 0;
|
||||
#X connect 8 0 11 0;
|
||||
#X connect 8 0 9 0;
|
||||
#X connect 11 0 24 0;
|
||||
#X connect 13 0 5 0;
|
||||
#X connect 13 1 5 1;
|
||||
#X connect 18 0 25 0;
|
||||
#X connect 19 0 26 0;
|
||||
#X connect 21 0 13 0;
|
||||
#X connect 27 0 6 0;
|
||||
#X connect 29 0 30 0;
|
||||
#X connect 29 1 30 1;
|
||||
#X connect 30 0 31 0;
|
||||
#X connect 32 0 29 0;
|
||||
|
|
@ -33,6 +33,7 @@ add_library(sfizz-vst3-core STATIC EXCLUDE_FROM_ALL
|
|||
SfizzVstState.h
|
||||
SfizzVstParameters.h
|
||||
SfizzVstUpdates.h
|
||||
SfizzVstUpdates.hpp
|
||||
SfizzVstUpdates.cpp
|
||||
SfizzVstIDs.h
|
||||
OrderedEventProcessor.h
|
||||
|
|
@ -135,7 +136,8 @@ if(SFIZZ_VST)
|
|||
if(NOT MSVC)
|
||||
install(DIRECTORY "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}"
|
||||
DESTINATION "${VSTPLUGIN_INSTALL_DIR}"
|
||||
COMPONENT "vst")
|
||||
COMPONENT "vst"
|
||||
USE_SOURCE_PERMISSIONS)
|
||||
bundle_dylibs(vst
|
||||
"${VSTPLUGIN_INSTALL_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/MacOS/sfizz"
|
||||
COMPONENT "vst")
|
||||
|
|
@ -283,7 +285,8 @@ elseif(SFIZZ_AU)
|
|||
if(AUPLUGIN_INSTALL_DIR)
|
||||
install(DIRECTORY "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}"
|
||||
DESTINATION "${AUPLUGIN_INSTALL_DIR}"
|
||||
COMPONENT "au")
|
||||
COMPONENT "au"
|
||||
USE_SOURCE_PERMISSIONS)
|
||||
bundle_dylibs(au
|
||||
"${AUPLUGIN_INSTALL_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents/MacOS/sfizz"
|
||||
COMPONENT "au")
|
||||
|
|
@ -354,7 +357,8 @@ if(SFIZZ_VST2)
|
|||
if(VST2PLUGIN_INSTALL_DIR)
|
||||
install(DIRECTORY "${PROJECT_BINARY_DIR}/${VST2PLUGIN_BUNDLE_NAME}"
|
||||
DESTINATION "${VST2PLUGIN_INSTALL_DIR}"
|
||||
COMPONENT "vst2")
|
||||
COMPONENT "vst2"
|
||||
USE_SOURCE_PERMISSIONS)
|
||||
if(APPLE)
|
||||
bundle_dylibs(vst2
|
||||
"${VST2PLUGIN_INSTALL_DIR}/${VST2PLUGIN_BUNDLE_NAME}/Contents/Binary/sfizz.${CMAKE_SHARED_MODULE_SUFFIX}"
|
||||
|
|
|
|||
|
|
@ -8,26 +8,38 @@
|
|||
#include "SfizzVstEditor.h"
|
||||
#include "SfizzVstParameters.h"
|
||||
#include "SfizzVstIDs.h"
|
||||
#include "plugin/InstrumentDescription.h"
|
||||
#include "base/source/fstreamer.h"
|
||||
#include "base/source/updatehandler.h"
|
||||
#include <absl/strings/match.h>
|
||||
#include <ghc/fs_std.hpp>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
|
||||
enum { kProgramListID = 0 };
|
||||
|
||||
tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
||||
{
|
||||
tresult result = EditController::initialize(context);
|
||||
tresult result = EditControllerEx1::initialize(context);
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
// initialize the update handler
|
||||
Steinberg::UpdateHandler::instance();
|
||||
|
||||
// initialize the thread checker
|
||||
threadChecker_ = Vst::ThreadChecker::create();
|
||||
|
||||
// create update objects
|
||||
oscUpdate_ = Steinberg::owned(new OSCUpdate);
|
||||
noteUpdate_ = Steinberg::owned(new NoteUpdate);
|
||||
queuedUpdates_ = Steinberg::owned(new QueuedUpdates);
|
||||
sfzUpdate_ = Steinberg::owned(new SfzUpdate);
|
||||
sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate);
|
||||
scalaUpdate_ = Steinberg::owned(new ScalaUpdate);
|
||||
playStateUpdate_ = Steinberg::owned(new PlayStateUpdate);
|
||||
|
||||
// Unit
|
||||
addUnit(new Vst::Unit(Steinberg::String("Root"), Vst::kRootUnitId, Vst::kNoParentUnitId, kProgramListID));
|
||||
|
||||
// Parameters
|
||||
Vst::ParamID pid = 0;
|
||||
|
||||
|
|
@ -92,6 +104,22 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
|||
Vst::kRootUnitId, shortTitle));
|
||||
}
|
||||
|
||||
// Volume levels
|
||||
parameters.addParameter(
|
||||
SfizzRange::getForParameter(kPidLeftLevel).createParameter(
|
||||
Steinberg::String("Left level"), pid++, nullptr,
|
||||
0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId));
|
||||
parameters.addParameter(
|
||||
SfizzRange::getForParameter(kPidRightLevel).createParameter(
|
||||
Steinberg::String("Right level"), pid++, nullptr,
|
||||
0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId));
|
||||
|
||||
// Editor status
|
||||
parameters.addParameter(
|
||||
SfizzRange::getForParameter(kPidEditorOpen).createParameter(
|
||||
Steinberg::String("Editor open"), pid++, nullptr,
|
||||
0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId));
|
||||
|
||||
// Initial MIDI mapping
|
||||
for (int32 i = 0; i < Vst::kCountCtrlNumber; ++i) {
|
||||
Vst::ParamID id = Vst::kNoParamId;
|
||||
|
|
@ -110,12 +138,22 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
|||
midiMapping_[i] = id;
|
||||
}
|
||||
|
||||
// Program list
|
||||
IPtr<Vst::ProgramListWithPitchNames> list = Steinberg::owned(
|
||||
new Vst::ProgramListWithPitchNames(Steinberg::String("Programs"), kProgramListID, Vst::kRootUnitId));
|
||||
list->addProgram(Steinberg::String("Default"));
|
||||
addProgramList(list);
|
||||
list->addRef();
|
||||
|
||||
// Use linear knobs
|
||||
setKnobMode(kLinearMode);
|
||||
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstControllerNoUi::terminate()
|
||||
{
|
||||
return EditController::terminate();
|
||||
return EditControllerEx1::terminate();
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstControllerNoUi::getMidiControllerAssignment(int32 busIndex, int16 channel, Vst::CtrlNumber midiControllerNumber, Vst::ParamID& id)
|
||||
|
|
@ -132,6 +170,44 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getMidiControllerAssignment(int32 bus
|
|||
return kResultTrue;
|
||||
}
|
||||
|
||||
int32 PLUGIN_API SfizzVstControllerNoUi::getKeyswitchCount(int32 busIndex, int16 channel)
|
||||
{
|
||||
(void)channel;
|
||||
|
||||
if (busIndex != 0)
|
||||
return 0;
|
||||
|
||||
return keyswitches_.size();
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstControllerNoUi::getKeyswitchInfo(int32 busIndex, int16 channel, int32 keySwitchIndex, Vst::KeyswitchInfo& info)
|
||||
{
|
||||
(void)channel;
|
||||
|
||||
if (busIndex != 0)
|
||||
return kResultFalse;
|
||||
|
||||
if (keySwitchIndex < 0 || keySwitchIndex >= keyswitches_.size())
|
||||
return kResultFalse;
|
||||
|
||||
info = keyswitches_[keySwitchIndex];
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstControllerNoUi::beginEditFromHost(Vst::ParamID paramID)
|
||||
{
|
||||
// Note(jpc) implementing this interface is a workaround to make
|
||||
// non-automatable parameters editable in Ardour (as of 6.6)
|
||||
(void)paramID;
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstControllerNoUi::endEditFromHost(Vst::ParamID paramID)
|
||||
{
|
||||
(void)paramID;
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstControllerNoUi::getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string)
|
||||
{
|
||||
switch (tag) {
|
||||
|
|
@ -146,7 +222,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getParamStringByValue(Vst::ParamID ta
|
|||
}
|
||||
}
|
||||
|
||||
return EditController::getParamStringByValue(tag, valueNormalized, string);
|
||||
return EditControllerEx1::getParamStringByValue(tag, valueNormalized, string);
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstControllerNoUi::getParamValueByString(Vst::ParamID tag, Vst::TChar* string, Vst::ParamValue& valueNormalized)
|
||||
|
|
@ -164,7 +240,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getParamValueByString(Vst::ParamID ta
|
|||
}
|
||||
}
|
||||
|
||||
return EditController::getParamValueByString(tag, string, valueNormalized);
|
||||
return EditControllerEx1::getParamValueByString(tag, string, valueNormalized);
|
||||
}
|
||||
|
||||
tresult SfizzVstControllerNoUi::setParam(Vst::ParamID tag, float value)
|
||||
|
|
@ -207,109 +283,157 @@ tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream)
|
|||
|
||||
tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
|
||||
{
|
||||
// Note: may be called from any thread (Reaper)
|
||||
// Note: is expected to be called from the controller thread only
|
||||
|
||||
tresult result = EditController::notify(message);
|
||||
tresult result = EditControllerEx1::notify(message);
|
||||
if (result != kResultFalse)
|
||||
return result;
|
||||
|
||||
if (!threadChecker_->test()) {
|
||||
static std::atomic_bool warn_once_flag { false };
|
||||
if (!warn_once_flag.exchange(true))
|
||||
fprintf(stderr, "[sfizz] controller notification arrives from the wrong thread\n");
|
||||
}
|
||||
|
||||
const char* id = message->getMessageID();
|
||||
Vst::IAttributeList* attr = message->getAttributes();
|
||||
|
||||
///
|
||||
auto stringFromBinaryAttribute = [attr](const char* id, absl::string_view& string) -> tresult {
|
||||
const void* data = nullptr;
|
||||
uint32 size = 0;
|
||||
tresult result = attr->getBinary(id, data, size);
|
||||
if (result == kResultTrue)
|
||||
string = absl::string_view(reinterpret_cast<const char*>(data), size);
|
||||
return result;
|
||||
};
|
||||
if (!strcmp(id, SfzUpdate::getFClassID())) {
|
||||
if (!sfzUpdate_->convertFromMessage(*message)) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
|
||||
///
|
||||
if (!strcmp(id, "LoadedSfz")) {
|
||||
absl::string_view sfzFile;
|
||||
absl::string_view sfzDescriptionBlob;
|
||||
// update the program name and notify
|
||||
std::string name = fs::u8path(sfzUpdate_->getPath()).filename().u8string();
|
||||
if (absl::EndsWithIgnoreCase(name, ".sfz"))
|
||||
name.resize(name.size() - 4);
|
||||
setProgramName(kProgramListID, 0, Steinberg::String(name.c_str()));
|
||||
|
||||
result = stringFromBinaryAttribute("File", sfzFile);
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
FUnknownPtr<Vst::IUnitHandler> unitHandler(getComponentHandler());
|
||||
if (unitHandler)
|
||||
unitHandler->notifyProgramListChange(kProgramListID, 0);
|
||||
|
||||
result = stringFromBinaryAttribute("Description", sfzDescriptionBlob);
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
sfzUpdate_->setPath(std::string(sfzFile));
|
||||
//
|
||||
sfzUpdate_->deferUpdate();
|
||||
sfzDescriptionUpdate_->setDescription(std::string(sfzDescriptionBlob));
|
||||
}
|
||||
else if (!strcmp(id, SfzDescriptionUpdate::getFClassID())) {
|
||||
if (!sfzDescriptionUpdate_->convertFromMessage(*message)) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
|
||||
// parse the description blob
|
||||
const InstrumentDescription desc = parseDescriptionBlob(
|
||||
sfzDescriptionUpdate_->getDescription());
|
||||
|
||||
// update pitch names and notify
|
||||
Vst::ProgramListWithPitchNames* list =
|
||||
static_cast<Vst::ProgramListWithPitchNames*>(getProgramList(kProgramListID));
|
||||
for (int16 pitch = 0; pitch < 128; ++pitch) {
|
||||
Steinberg::String pitchName;
|
||||
if (desc.keyUsed.test(pitch) && !desc.keyLabel[pitch].empty())
|
||||
pitchName = Steinberg::String(desc.keyLabel[pitch].c_str());
|
||||
else if (desc.keyswitchUsed.test(pitch) && !desc.keyswitchLabel[pitch].empty())
|
||||
pitchName = Steinberg::String(desc.keyswitchLabel[pitch].c_str());
|
||||
|
||||
list->setPitchName(0, pitch, pitchName);
|
||||
}
|
||||
|
||||
FUnknownPtr<Vst::IUnitHandler> unitHandler(getComponentHandler());
|
||||
if (unitHandler)
|
||||
unitHandler->notifyProgramListChange(kProgramListID, 0);
|
||||
|
||||
// update the key switches and notify
|
||||
size_t idKeyswitch = 0;
|
||||
for (int16 pitch = 0; pitch < 128; ++pitch)
|
||||
idKeyswitch += desc.keyswitchUsed.test(pitch);
|
||||
keyswitches_.resize(idKeyswitch);
|
||||
|
||||
idKeyswitch = 0;
|
||||
for (int16 pitch = 0; pitch < 128; ++pitch) {
|
||||
if (!desc.keyswitchUsed.test(pitch))
|
||||
continue;
|
||||
Vst::KeyswitchInfo info {};
|
||||
info.typeId = Vst::kNoteOnKeyswitchTypeID;
|
||||
Steinberg::String(desc.keyswitchLabel[pitch].c_str()).copyTo(info.title);
|
||||
Steinberg::String(desc.keyswitchLabel[pitch].c_str()).copyTo(info.shortTitle);
|
||||
info.keyswitchMin = pitch; // TODO reexamine this when supporting keyswitch groups
|
||||
info.keyswitchMax = pitch; // TODO reexamine this when supporting keyswitch groups
|
||||
info.keyRemapped = pitch;
|
||||
info.unitId = Vst::kRootUnitId;
|
||||
info.flags = 0;
|
||||
keyswitches_[idKeyswitch++] = info;
|
||||
}
|
||||
|
||||
if (Vst::IComponentHandler* componentHandler = getComponentHandler())
|
||||
componentHandler->restartComponent(Vst::kKeyswitchChanged);
|
||||
|
||||
// update the parameter titles and notify
|
||||
for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
Vst::ParamID pid = kPidCC0 + cc;
|
||||
Vst::Parameter* param = getParameterObject(pid);
|
||||
Vst::ParameterInfo& info = param->getInfo();
|
||||
Steinberg::String title;
|
||||
Steinberg::String shortTitle;
|
||||
if (!desc.ccLabel[cc].empty()) {
|
||||
title = desc.ccLabel[cc].c_str();
|
||||
shortTitle = title;
|
||||
}
|
||||
else {
|
||||
title.printf("Controller %u", cc);
|
||||
shortTitle.printf("CC%u", cc);
|
||||
}
|
||||
title.copyTo(info.title);
|
||||
shortTitle.copyTo(info.shortTitle);
|
||||
}
|
||||
|
||||
if (Vst::IComponentHandler* componentHandler = getComponentHandler())
|
||||
componentHandler->restartComponent(Vst::kParamTitlesChanged);
|
||||
|
||||
//
|
||||
sfzDescriptionUpdate_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, "LoadedScala")) {
|
||||
absl::string_view scalaFile;
|
||||
|
||||
result = stringFromBinaryAttribute("File", scalaFile);
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
scalaUpdate_->setPath(std::string(scalaFile));
|
||||
else if (!strcmp(id, ScalaUpdate::getFClassID())) {
|
||||
if (!scalaUpdate_->convertFromMessage(*message)) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
scalaUpdate_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, "NotifiedPlayState")) {
|
||||
const void* data = nullptr;
|
||||
uint32 size = 0;
|
||||
result = attr->getBinary("PlayState", data, size);
|
||||
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
playStateUpdate_->setState(*static_cast<const SfizzPlayState*>(data));
|
||||
else if (!strcmp(id, PlayStateUpdate::getFClassID())) {
|
||||
if (!playStateUpdate_->convertFromMessage(*message)) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
playStateUpdate_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, "ReceivedMessage")) {
|
||||
const void* data = nullptr;
|
||||
uint32 size = 0;
|
||||
result = attr->getBinary("Message", data, size);
|
||||
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
// this is a synchronous send, because the update object gets reused
|
||||
oscUpdate_->setMessage(data, size, false);
|
||||
oscUpdate_->changed();
|
||||
oscUpdate_->clear();
|
||||
}
|
||||
else if (!strcmp(id, "NoteEvents")) {
|
||||
const void* data = nullptr;
|
||||
uint32 size = 0;
|
||||
result = attr->getBinary("Events", data, size);
|
||||
|
||||
const auto* events = reinterpret_cast<
|
||||
const std::pair<uint32_t, float>*>(data);
|
||||
uint32 numEvents = size / sizeof(events[0]);
|
||||
|
||||
// this is a synchronous send, because the update object gets reused
|
||||
noteUpdate_->setEvents(events, numEvents, false);
|
||||
noteUpdate_->changed();
|
||||
noteUpdate_->clear();
|
||||
}
|
||||
else if (!strcmp(id, "Automate")) {
|
||||
const void* data = nullptr;
|
||||
uint32 size = 0;
|
||||
result = attr->getBinary("Data", data, size);
|
||||
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
const uint8* pos = reinterpret_cast<const uint8*>(data);
|
||||
const uint8* end = pos + size;
|
||||
|
||||
while (static_cast<size_t>(end - pos) >= sizeof(uint32) + sizeof(float)) {
|
||||
Vst::ParamID pid = *reinterpret_cast<const uint32*>(pos);
|
||||
pos += sizeof(uint32);
|
||||
float value = *reinterpret_cast<const float*>(pos);
|
||||
pos += sizeof(float);
|
||||
setParam(pid, value);
|
||||
else if (!strcmp(id, OSCUpdate::getFClassID())) {
|
||||
IPtr<OSCUpdate> update = OSCUpdate::createFromMessage(*message);
|
||||
if (!update) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
queuedUpdates_->enqueue(update);
|
||||
queuedUpdates_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, NoteUpdate::getFClassID())) {
|
||||
IPtr<NoteUpdate> update = NoteUpdate::createFromMessage(*message);
|
||||
if (!update) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
queuedUpdates_->enqueue(update);
|
||||
queuedUpdates_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, AutomationUpdate::getFClassID())) {
|
||||
IPtr<AutomationUpdate> update = AutomationUpdate::createFromMessage(*message);
|
||||
if (!update) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
for (AutomationUpdate::Item item : update->getItems())
|
||||
setParam(item.first, item.second);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -326,20 +450,17 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name)
|
|||
if (name != Vst::ViewType::kEditor)
|
||||
return nullptr;
|
||||
|
||||
std::vector<FObject*> continuousUpdates;
|
||||
continuousUpdates.push_back(sfzUpdate_);
|
||||
continuousUpdates.push_back(sfzDescriptionUpdate_);
|
||||
continuousUpdates.push_back(scalaUpdate_);
|
||||
continuousUpdates.push_back(playStateUpdate_);
|
||||
std::vector<FObject*> updates;
|
||||
updates.push_back(queuedUpdates_);
|
||||
updates.push_back(sfzUpdate_);
|
||||
updates.push_back(sfzDescriptionUpdate_);
|
||||
updates.push_back(scalaUpdate_);
|
||||
updates.push_back(playStateUpdate_);
|
||||
for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i)
|
||||
continuousUpdates.push_back(parameters.getParameterByIndex(i));
|
||||
|
||||
std::vector<FObject*> triggerUpdates;
|
||||
triggerUpdates.push_back(oscUpdate_);
|
||||
triggerUpdates.push_back(noteUpdate_);
|
||||
updates.push_back(parameters.getParameterByIndex(i));
|
||||
|
||||
IPtr<SfizzVstEditor> editor = Steinberg::owned(
|
||||
new SfizzVstEditor(this, absl::MakeSpan(continuousUpdates), absl::MakeSpan(triggerUpdates)));
|
||||
new SfizzVstEditor(this, absl::MakeSpan(updates)));
|
||||
|
||||
editor->remember();
|
||||
return editor;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
#include "SfizzVstUpdates.h"
|
||||
#include "public.sdk/source/vst/vsteditcontroller.h"
|
||||
#include "public.sdk/source/vst/vstparameters.h"
|
||||
#include "public.sdk/source/common/threadchecker.h"
|
||||
#include "pluginterfaces/vst/ivstmidicontrollers.h"
|
||||
#include "pluginterfaces/vst/ivstnoteexpression.h"
|
||||
#include "vstgui/plugin-bindings/vst3editor.h"
|
||||
#include <sfizz_message.h>
|
||||
#include <mutex>
|
||||
|
|
@ -19,8 +21,10 @@ class SfizzVstEditor;
|
|||
using namespace Steinberg;
|
||||
using namespace VSTGUI;
|
||||
|
||||
class SfizzVstControllerNoUi : public Vst::EditController,
|
||||
public Vst::IMidiMapping {
|
||||
class SfizzVstControllerNoUi : public Vst::EditControllerEx1,
|
||||
public Vst::IMidiMapping,
|
||||
public Vst::IKeyswitchController,
|
||||
public Vst::IEditControllerHostEditing {
|
||||
public:
|
||||
virtual ~SfizzVstControllerNoUi() {}
|
||||
|
||||
|
|
@ -29,6 +33,12 @@ public:
|
|||
|
||||
tresult PLUGIN_API getMidiControllerAssignment(int32 busIndex, int16 channel, Vst::CtrlNumber midiControllerNumber, Vst::ParamID& id) override;
|
||||
|
||||
int32 PLUGIN_API getKeyswitchCount (int32 busIndex, int16 channel) override;
|
||||
tresult PLUGIN_API getKeyswitchInfo (int32 busIndex, int16 channel, int32 keySwitchIndex, Vst::KeyswitchInfo& info) override;
|
||||
|
||||
tresult PLUGIN_API beginEditFromHost(Vst::ParamID paramID) override;
|
||||
tresult PLUGIN_API endEditFromHost(Vst::ParamID paramID) override;
|
||||
|
||||
tresult PLUGIN_API getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string) override;
|
||||
tresult PLUGIN_API getParamValueByString(Vst::ParamID tag, Vst::TChar* string, Vst::ParamValue& valueNormalized) override;
|
||||
|
||||
|
|
@ -37,20 +47,23 @@ public:
|
|||
tresult PLUGIN_API notify(Vst::IMessage* message) override;
|
||||
|
||||
// interfaces
|
||||
OBJ_METHODS(SfizzVstControllerNoUi, Vst::EditController)
|
||||
OBJ_METHODS(SfizzVstControllerNoUi, Vst::EditControllerEx1)
|
||||
DEFINE_INTERFACES
|
||||
DEF_INTERFACE(Vst::IMidiMapping)
|
||||
END_DEFINE_INTERFACES(Vst::EditController)
|
||||
REFCOUNT_METHODS(Vst::EditController)
|
||||
DEF_INTERFACE(Vst::IKeyswitchController)
|
||||
DEF_INTERFACE(Vst::IEditControllerHostEditing)
|
||||
END_DEFINE_INTERFACES(Vst::EditControllerEx1)
|
||||
REFCOUNT_METHODS(Vst::EditControllerEx1)
|
||||
|
||||
protected:
|
||||
Steinberg::IPtr<OSCUpdate> oscUpdate_;
|
||||
Steinberg::IPtr<NoteUpdate> noteUpdate_;
|
||||
std::unique_ptr<Vst::ThreadChecker> threadChecker_;
|
||||
Steinberg::IPtr<QueuedUpdates> queuedUpdates_;
|
||||
Steinberg::IPtr<SfzUpdate> sfzUpdate_;
|
||||
Steinberg::IPtr<SfzDescriptionUpdate> sfzDescriptionUpdate_;
|
||||
Steinberg::IPtr<ScalaUpdate> scalaUpdate_;
|
||||
Steinberg::IPtr<PlayStateUpdate> playStateUpdate_;
|
||||
Vst::ParamID midiMapping_[Vst::kCountCtrlNumber] {};
|
||||
std::vector<Vst::KeyswitchInfo> keyswitches_;
|
||||
};
|
||||
|
||||
class SfizzVstController : public SfizzVstControllerNoUi, public VSTGUI::VST3EditorDelegate {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "X11RunLoop.h"
|
||||
#endif
|
||||
#include <ghc/fs_std.hpp>
|
||||
#include <atomic>
|
||||
|
||||
using namespace VSTGUI;
|
||||
|
||||
|
|
@ -29,14 +30,10 @@ enum {
|
|||
kNoteEventQueueSize = 8192,
|
||||
};
|
||||
|
||||
SfizzVstEditor::SfizzVstEditor(
|
||||
SfizzVstController* controller,
|
||||
absl::Span<FObject*> continuousUpdates,
|
||||
absl::Span<FObject*> triggerUpdates)
|
||||
SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller, absl::Span<FObject*> updates)
|
||||
: VSTGUIEditor(controller, &sfizzUiViewRect),
|
||||
oscTemp_(new uint8_t[kOscTempSize]),
|
||||
continuousUpdates_(continuousUpdates.begin(), continuousUpdates.end()),
|
||||
triggerUpdates_(triggerUpdates.begin(), triggerUpdates.end())
|
||||
updates_(updates.begin(), updates.end())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -62,24 +59,8 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
|
|||
config = &x11config;
|
||||
#endif
|
||||
|
||||
Editor* editor = editor_.get();
|
||||
if (!editor) {
|
||||
editor = new Editor(*this);
|
||||
editor_.reset(editor);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(oscQueueMutex_);
|
||||
OscByteVec* queue = new OscByteVec;
|
||||
oscQueue_.reset(queue);
|
||||
queue->reserve(kOscQueueSize);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
|
||||
NoteEventsVec* queue = new NoteEventsVec;
|
||||
noteEventQueue_.reset(queue);
|
||||
queue->reserve(kNoteEventQueueSize);
|
||||
}
|
||||
Editor* editor = new Editor(*this);
|
||||
editor_.reset(editor);
|
||||
|
||||
if (!frame->open(parent, platformType, config)) {
|
||||
fprintf(stderr, "[sfizz] error opening frame\n");
|
||||
|
|
@ -88,14 +69,16 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
|
|||
|
||||
editor->open(*frame);
|
||||
|
||||
for (FObject* update : continuousUpdates_)
|
||||
update->addDependent(this);
|
||||
for (FObject* update : triggerUpdates_)
|
||||
for (FObject* update : updates_)
|
||||
update->addDependent(this);
|
||||
|
||||
threadChecker_ = Vst::ThreadChecker::create();
|
||||
|
||||
parametersToUpdate_.clear();
|
||||
|
||||
Steinberg::IdleUpdateHandler::start();
|
||||
|
||||
for (FObject* update : continuousUpdates_)
|
||||
for (FObject* update : updates_)
|
||||
update->deferUpdate();
|
||||
|
||||
// let the editor know about plugin format
|
||||
|
|
@ -122,6 +105,8 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
|
|||
uiReceiveValue(EditId::UserFilesDir, userFilesDir.value_or(fs::path()).u8string());
|
||||
uiReceiveValue(EditId::FallbackFilesDir, SfizzPaths::getSfzFallbackDefaultPath().u8string());
|
||||
|
||||
updateEditorIsOpenParameter();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -131,28 +116,36 @@ void PLUGIN_API SfizzVstEditor::close()
|
|||
if (frame) {
|
||||
Steinberg::IdleUpdateHandler::stop();
|
||||
|
||||
for (FObject* update : continuousUpdates_)
|
||||
update->removeDependent(this);
|
||||
for (FObject* update : triggerUpdates_)
|
||||
for (FObject* update : updates_)
|
||||
update->removeDependent(this);
|
||||
|
||||
if (editor_)
|
||||
if (editor_) {
|
||||
editor_->close();
|
||||
editor_ = nullptr;
|
||||
}
|
||||
|
||||
if (frame->getNbReference() != 1)
|
||||
frame->forget();
|
||||
else
|
||||
else {
|
||||
frame->close();
|
||||
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||
// if vstgui is done using the runloop, destroy it
|
||||
if (!RunLoop::get())
|
||||
_runLoop = nullptr;
|
||||
#endif
|
||||
}
|
||||
this->frame = nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(oscQueueMutex_);
|
||||
oscQueue_.reset();
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
|
||||
noteEventQueue_.reset();
|
||||
}
|
||||
updateEditorIsOpenParameter();
|
||||
}
|
||||
|
||||
void SfizzVstEditor::updateEditorIsOpenParameter()
|
||||
{
|
||||
SfizzVstController* ctrl = getController();
|
||||
bool editorIsOpen = frame && frame->isVisible();
|
||||
ctrl->setParamNormalized(kPidEditorOpen, editorIsOpen);
|
||||
ctrl->performEdit(kPidEditorOpen, editorIsOpen);
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -171,15 +164,13 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
|
|||
// notifier of X11 events is working. If there is, remove this and
|
||||
// avoid polluting Linux hosts which implement the loop correctly.
|
||||
runLoop->processSomeEvents();
|
||||
|
||||
runLoop->cleanupDeadHandlers();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (message == CVSTGUITimer::kMsgTimer) {
|
||||
processOscQueue();
|
||||
processNoteEventQueue();
|
||||
processParameterUpdates();
|
||||
updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -187,34 +178,51 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
|
|||
|
||||
void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
||||
{
|
||||
if (OSCUpdate* update = FCast<OSCUpdate>(changedUnknown)) {
|
||||
// this update is synchronous: may happen from non-UI thread
|
||||
uint32 size = update->size();
|
||||
if (size > 0) {
|
||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(update->data());
|
||||
std::lock_guard<std::mutex> lock(oscQueueMutex_);
|
||||
if (OscByteVec* queue = oscQueue_.get())
|
||||
std::copy(bytes, bytes + size, std::back_inserter(*queue));
|
||||
}
|
||||
if (processUpdate(changedUnknown, message))
|
||||
return;
|
||||
|
||||
Vst::VSTGUIEditor::update(changedUnknown, message);
|
||||
}
|
||||
|
||||
bool SfizzVstEditor::processUpdate(FUnknown* changedUnknown, int32 message)
|
||||
{
|
||||
if (QueuedUpdates* update = FCast<QueuedUpdates>(changedUnknown)) {
|
||||
for (FObject* queuedUpdate : update->getUpdates(this))
|
||||
processUpdate(queuedUpdate, message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (OSCUpdate* update = FCast<OSCUpdate>(changedUnknown)) {
|
||||
const uint8* oscData = update->data();
|
||||
uint32 oscSize = update->size();
|
||||
|
||||
const char* path;
|
||||
const char* sig;
|
||||
const sfizz_arg_t* args;
|
||||
uint8_t buffer[1024];
|
||||
|
||||
uint32_t msgSize;
|
||||
while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) {
|
||||
uiReceiveMessage(path, sig, args);
|
||||
oscData += msgSize;
|
||||
oscSize -= msgSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NoteUpdate* update = FCast<NoteUpdate>(changedUnknown)) {
|
||||
// this update is synchronous: may happen from non-UI thread
|
||||
const NoteUpdate::Item* events = update->events();
|
||||
uint32 count = update->count();
|
||||
if (count > 0) {
|
||||
const auto* events = update->events();
|
||||
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
|
||||
if (NoteEventsVec* queue = noteEventQueue_.get())
|
||||
std::copy(events, events + count, std::back_inserter(*queue));
|
||||
}
|
||||
return;
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
uiReceiveValue(editIdForKey(events[i].first), events[i].second);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SfzUpdate* update = FCast<SfzUpdate>(changedUnknown)) {
|
||||
const std::string path = update->getPath();
|
||||
uiReceiveValue(EditId::SfzFile, path);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SfzDescriptionUpdate* update = FCast<SfzDescriptionUpdate>(changedUnknown)) {
|
||||
|
|
@ -242,31 +250,70 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
|||
}
|
||||
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
bool ccUsed = desc.ccUsed.test(cc);
|
||||
bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc);
|
||||
uiReceiveValue(editIdForCCUsed(int(cc)), float(ccUsed));
|
||||
if (ccUsed) {
|
||||
uiReceiveValue(editIdForCCDefault(int(cc)), desc.ccDefault[cc]);
|
||||
uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ScalaUpdate* update = FCast<ScalaUpdate>(changedUnknown)) {
|
||||
const std::string path = update->getPath();
|
||||
uiReceiveValue(EditId::ScalaFile, path);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) {
|
||||
const SfizzPlayState playState = update->getState();
|
||||
uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Vst::RangeParameter* param = Steinberg::FCast<Vst::RangeParameter>(changedUnknown)) {
|
||||
const Vst::ParamValue value = param->getNormalized();
|
||||
// Note(jpc) some hosts send us the parameters in the wrong thread...
|
||||
// store these parameters thread-safely and let the idle
|
||||
// callback process them later
|
||||
if (threadChecker_->test())
|
||||
updateParameter(param);
|
||||
else {
|
||||
static std::atomic_bool warn_once_flag { false };
|
||||
if (!warn_once_flag.exchange(true))
|
||||
fprintf(stderr, "[sfizz] using a thread-safety workaround for parameter updates\n");
|
||||
const Vst::ParamID id = param->getInfo().id;
|
||||
std::lock_guard<std::mutex> lock(parametersToUpdateMutex_);
|
||||
parametersToUpdate_.insert(id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SfizzVstEditor::processParameterUpdates()
|
||||
{
|
||||
auto extractNextParamID = [this]() -> Vst::ParamID {
|
||||
Vst::ParamID id = Vst::kNoParamId;
|
||||
std::lock_guard<std::mutex> lock(parametersToUpdateMutex_);
|
||||
auto it = parametersToUpdate_.begin();
|
||||
if (it != parametersToUpdate_.end()) {
|
||||
id = *it;
|
||||
parametersToUpdate_.erase(it);
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
for (Vst::ParamID id; (id = extractNextParamID()) != Vst::kNoParamId; )
|
||||
updateParameter(getController()->getParameterObject(id));
|
||||
}
|
||||
|
||||
void SfizzVstEditor::updateParameter(Vst::Parameter* parameterToUpdate)
|
||||
{
|
||||
if (Vst::RangeParameter* param = FCast<Vst::RangeParameter>(parameterToUpdate)) {
|
||||
const Vst::ParamID id = param->getInfo().id;
|
||||
const Vst::ParamValue value = param->getNormalized();
|
||||
const SfizzRange range = SfizzRange::getForParameter(id);
|
||||
switch (id) {
|
||||
case kPidVolume:
|
||||
|
|
@ -296,6 +343,12 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
|||
case kPidOscillatorQuality:
|
||||
uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value));
|
||||
break;
|
||||
case kPidLeftLevel:
|
||||
uiReceiveValue(EditId::LeftLevel, range.denormalize(value));
|
||||
break;
|
||||
case kPidRightLevel:
|
||||
uiReceiveValue(EditId::RightLevel, range.denormalize(value));
|
||||
break;
|
||||
default:
|
||||
if (id >= kPidCC0 && id <= kPidCCLast) {
|
||||
int cc = int(id - kPidCC0);
|
||||
|
|
@ -303,50 +356,7 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
|||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Vst::VSTGUIEditor::update(changedUnknown, message);
|
||||
}
|
||||
|
||||
void SfizzVstEditor::processOscQueue()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(oscQueueMutex_);
|
||||
|
||||
OscByteVec* queue = oscQueue_.get();
|
||||
if (!queue)
|
||||
return;
|
||||
|
||||
const uint8_t* oscData = queue->data();
|
||||
size_t oscSize = queue->size();
|
||||
|
||||
const char* path;
|
||||
const char* sig;
|
||||
const sfizz_arg_t* args;
|
||||
uint8_t buffer[1024];
|
||||
|
||||
uint32_t msgSize;
|
||||
while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) {
|
||||
uiReceiveMessage(path, sig, args);
|
||||
oscData += msgSize;
|
||||
oscSize -= msgSize;
|
||||
}
|
||||
|
||||
queue->clear();
|
||||
}
|
||||
|
||||
void SfizzVstEditor::processNoteEventQueue()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
|
||||
|
||||
NoteEventsVec* queue = noteEventQueue_.get();
|
||||
if (!queue)
|
||||
return;
|
||||
|
||||
for (std::pair<uint32, float> event : *queue)
|
||||
uiReceiveValue(editIdForKey(event.first), event.second);
|
||||
|
||||
queue->clear();
|
||||
}
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
#include "SfizzVstController.h"
|
||||
#include "editor/EditorController.h"
|
||||
#include "public.sdk/source/vst/vstguieditor.h"
|
||||
#include "public.sdk/source/common/threadchecker.h"
|
||||
#include <absl/types/span.h>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
class Editor;
|
||||
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||
namespace VSTGUI { class RunLoop; }
|
||||
|
|
@ -23,10 +25,7 @@ class SfizzVstEditor : public Vst::VSTGUIEditor,
|
|||
public:
|
||||
using Self = SfizzVstEditor;
|
||||
|
||||
SfizzVstEditor(
|
||||
SfizzVstController* controller,
|
||||
absl::Span<FObject*> continuousUpdates,
|
||||
absl::Span<FObject*> triggerUpdates);
|
||||
SfizzVstEditor(SfizzVstController* controller, absl::Span<FObject*> updates);
|
||||
~SfizzVstEditor();
|
||||
|
||||
bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType) override;
|
||||
|
|
@ -37,18 +36,18 @@ public:
|
|||
return static_cast<SfizzVstController*>(Vst::VSTGUIEditor::getController());
|
||||
}
|
||||
|
||||
void updateEditorIsOpenParameter();
|
||||
|
||||
// VSTGUIEditor
|
||||
CMessageResult notify(CBaseObject* sender, const char* message) override;
|
||||
// FObject
|
||||
void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override;
|
||||
|
||||
//
|
||||
void updateState(const SfizzVstState& state);
|
||||
void updatePlayState(const SfizzPlayState& playState);
|
||||
|
||||
private:
|
||||
void processOscQueue();
|
||||
void processNoteEventQueue();
|
||||
bool processUpdate(FUnknown* changedUnknown, int32 message);
|
||||
void processParameterUpdates();
|
||||
void updateParameter(Vst::Parameter* parameterToUpdate);
|
||||
|
||||
protected:
|
||||
// EditorController
|
||||
|
|
@ -73,16 +72,14 @@ private:
|
|||
// messaging
|
||||
std::unique_ptr<uint8[]> oscTemp_;
|
||||
|
||||
// editor state
|
||||
// note: might be updated from a non-UI thread
|
||||
typedef std::vector<uint8_t> OscByteVec;
|
||||
std::unique_ptr<OscByteVec> oscQueue_;
|
||||
std::mutex oscQueueMutex_;
|
||||
typedef std::vector<std::pair<uint32, float>> NoteEventsVec;
|
||||
std::unique_ptr<NoteEventsVec> noteEventQueue_;
|
||||
std::mutex noteEventQueueMutex_;
|
||||
|
||||
// subscribed updates
|
||||
std::vector<IPtr<FObject>> continuousUpdates_;
|
||||
std::vector<IPtr<FObject>> triggerUpdates_;
|
||||
std::vector<IPtr<FObject>> updates_;
|
||||
|
||||
// thread safety
|
||||
std::unique_ptr<Vst::ThreadChecker> threadChecker_;
|
||||
|
||||
// parameters to process, whose values have received changes
|
||||
// Note(jpc) it's because hosts send us parameter updates in the wrong thread..
|
||||
std::set<Vst::ParamID> parametersToUpdate_;
|
||||
std::mutex parametersToUpdateMutex_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ enum {
|
|||
kPidPitchBend,
|
||||
kPidCC0,
|
||||
kPidCCLast = kPidCC0 + sfz::config::numCCs - 1,
|
||||
kPidLeftLevel,
|
||||
kPidRightLevel,
|
||||
kPidEditorOpen,
|
||||
/* Reserved */
|
||||
kNumParameters,
|
||||
};
|
||||
|
|
@ -78,6 +81,12 @@ struct SfizzRange {
|
|||
return {0.0, 0.0, 1.0};
|
||||
case kPidPitchBend:
|
||||
return {0.0, -1.0, 1.0};
|
||||
case kPidLeftLevel:
|
||||
return {0.0, 0.0, 1.0};
|
||||
case kPidRightLevel:
|
||||
return {0.0, 0.0, 1.0};
|
||||
case kPidEditorOpen:
|
||||
return {0.0, 0.0, 1.0};
|
||||
default:
|
||||
if (id >= kPidCC0 && id <= kPidCCLast)
|
||||
return {0.0, 0.0, 1.0};
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@
|
|||
#include "SfizzVstState.h"
|
||||
#include "SfizzVstParameters.h"
|
||||
#include "SfizzVstIDs.h"
|
||||
#include "sfizz/import/ForeignInstrument.h"
|
||||
#include "sfizz/import/sfizz_import.h"
|
||||
#include "plugin/SfizzFileScan.h"
|
||||
#include "plugin/InstrumentDescription.h"
|
||||
#include "base/source/fstreamer.h"
|
||||
#include "base/source/updatehandler.h"
|
||||
#include "pluginterfaces/vst/ivstevents.h"
|
||||
#include "pluginterfaces/vst/ivstparameterchanges.h"
|
||||
#include <ghc/fs_std.hpp>
|
||||
|
|
@ -34,10 +35,10 @@ static const char* kRingIdOsc = "Osc";
|
|||
static const char* kMsgIdSetNumVoices = "SetNumVoices";
|
||||
static const char* kMsgIdSetOversampling = "SetOversampling";
|
||||
static const char* kMsgIdSetPreloadSize = "SetPreloadSize";
|
||||
static const char* kMsgIdReceiveMessage = "ReceiveMessage";
|
||||
static const char* kMsgIdReceiveOSC = "ReceiveOSC";
|
||||
static const char* kMsgIdNoteEvents = "NoteEvents";
|
||||
|
||||
static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 50 };
|
||||
static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 20 };
|
||||
|
||||
SfizzVstProcessor::SfizzVstProcessor()
|
||||
: _oscTemp(new uint8_t[kOscTempSize]),
|
||||
|
|
@ -74,6 +75,23 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
|||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
// initialize the update handler
|
||||
Steinberg::UpdateHandler::instance();
|
||||
|
||||
_queuedMessages = Steinberg::owned(new QueuedUpdates);
|
||||
_playStateUpdate = Steinberg::owned(new PlayStateUpdate);
|
||||
_sfzUpdate = Steinberg::owned(new SfzUpdate);
|
||||
_sfzDescriptionUpdate = Steinberg::owned(new SfzDescriptionUpdate);
|
||||
_scalaUpdate = Steinberg::owned(new ScalaUpdate);
|
||||
_automationUpdate = Steinberg::owned(new AutomationUpdate);
|
||||
|
||||
_queuedMessages->addDependent(this);
|
||||
_playStateUpdate->addDependent(this);
|
||||
_sfzUpdate->addDependent(this);
|
||||
_sfzDescriptionUpdate->addDependent(this);
|
||||
_scalaUpdate->addDependent(this);
|
||||
_automationUpdate->addDependent(this);
|
||||
|
||||
addAudioOutput(STR16("Audio Output"), Vst::SpeakerArr::kStereo);
|
||||
addEventInput(STR16("Event Input"), 1);
|
||||
|
||||
|
|
@ -88,7 +106,7 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
|||
auto onMessage = +[](void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args)
|
||||
{
|
||||
auto *self = reinterpret_cast<SfizzVstProcessor*>(data);
|
||||
self->receiveMessage(delay, path, sig, args);
|
||||
self->receiveOSC(delay, path, sig, args);
|
||||
};
|
||||
_client = _synth->createClient(this);
|
||||
_synth->setReceiveCallback(*_client, onMessage);
|
||||
|
|
@ -106,9 +124,23 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
|||
|
||||
_noteEventsCurrentCycle.fill(-1.0f);
|
||||
|
||||
_editorIsOpen = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstProcessor::terminate()
|
||||
{
|
||||
_queuedMessages->removeDependent(this);
|
||||
_playStateUpdate->removeDependent(this);
|
||||
_sfzUpdate->removeDependent(this);
|
||||
_sfzDescriptionUpdate->removeDependent(this);
|
||||
_scalaUpdate->removeDependent(this);
|
||||
_automationUpdate->removeDependent(this);
|
||||
|
||||
return AudioEffect::terminate();
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstProcessor::setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts)
|
||||
{
|
||||
bool isStereo = numIns == 0 && numOuts == 1 && outputs[0] == Vst::SpeakerArr::kStereo;
|
||||
|
|
@ -126,10 +158,7 @@ tresult PLUGIN_API SfizzVstProcessor::connect(IConnectionPoint* other)
|
|||
return result;
|
||||
|
||||
// when controller connects, send these messages that we couldn't earlier
|
||||
if (_loadedSfzMessage)
|
||||
sendMessage(_loadedSfzMessage);
|
||||
if (_automateMessage)
|
||||
sendMessage(_automateMessage);
|
||||
_queuedMessages->deferUpdate();
|
||||
|
||||
return kResultTrue;
|
||||
}
|
||||
|
|
@ -198,6 +227,10 @@ void SfizzVstProcessor::syncStateToSynth()
|
|||
synth->setScalaRootKey(_state.scalaRootKey);
|
||||
synth->setTuningFrequency(_state.tuningFrequency);
|
||||
synth->loadStretchTuningByRatio(_state.stretchedTuning);
|
||||
if (_state.lastKeyswitch >= 0 && _state.lastKeyswitch <= 127) {
|
||||
synth->hdNoteOn(0, _state.lastKeyswitch, 1.0f);
|
||||
synth->hdNoteOff(1, _state.lastKeyswitch, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstProcessor::canProcessSampleSize(int32 symbolicSampleSize)
|
||||
|
|
@ -221,6 +254,7 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state)
|
|||
if (state) {
|
||||
synth->setSampleRate(processSetup.sampleRate);
|
||||
synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock);
|
||||
_rmsFollower.init(processSetup.sampleRate);
|
||||
initializeEventProcessor(processSetup, kNumParameters);
|
||||
startBackgroundWork();
|
||||
} else {
|
||||
|
|
@ -286,12 +320,30 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
|
||||
synth.renderBlock(outputs, numFrames, numChannels);
|
||||
|
||||
// Update levels, if editor is open, otherwise skip
|
||||
RMSFollower& rmsFollower = _rmsFollower;
|
||||
if (_editorIsOpen) {
|
||||
rmsFollower.process(outputs[0], outputs[1], numFrames);
|
||||
simde__m128 rms = _rmsFollower.getRMS();
|
||||
float left = reinterpret_cast<float*>(&rms)[0];
|
||||
float right = reinterpret_cast<float*>(&rms)[1];
|
||||
if (Vst::IParameterChanges* pcs = data.outputParameterChanges) {
|
||||
int32 index;
|
||||
if (Vst::IParamValueQueue* vq = pcs->addParameterData(kPidLeftLevel, index))
|
||||
vq->addPoint(0, left, index);
|
||||
if (Vst::IParamValueQueue* vq = pcs->addParameterData(kPidRightLevel, index))
|
||||
vq->addPoint(0, right, index);
|
||||
}
|
||||
}
|
||||
else
|
||||
rmsFollower.clear();
|
||||
|
||||
// Request OSC updates
|
||||
sfz::Client& client = *_client;
|
||||
synth.sendMessage(client, 0, "/sw/last/current", "", nullptr);
|
||||
|
||||
//
|
||||
std::pair<uint32, float> noteEvents[128];
|
||||
NoteUpdate::Item noteEvents[128];
|
||||
size_t numNoteEvents = 0;
|
||||
for (uint32 key = 0; key < 128; ++key) {
|
||||
float value = _noteEventsCurrentCycle[key];
|
||||
|
|
@ -388,10 +440,13 @@ void SfizzVstProcessor::playOrderedParameter(int32 sampleOffset, Vst::ParamID id
|
|||
case kPidPitchBend:
|
||||
synth.hdPitchWheel(sampleOffset, range.denormalize(value));
|
||||
break;
|
||||
case kPidEditorOpen:
|
||||
_editorIsOpen = value != 0;
|
||||
break;
|
||||
default:
|
||||
if (id >= kPidCC0 && id <= kPidCCLast) {
|
||||
int32 ccNumber = static_cast<int32>(id - kPidCC0);
|
||||
synth.hdcc(sampleOffset, ccNumber, value);
|
||||
synth.automateHdcc(sampleOffset, ccNumber, value);
|
||||
_state.controllers[ccNumber] = value;
|
||||
}
|
||||
break;
|
||||
|
|
@ -467,7 +522,7 @@ void SfizzVstProcessor::processMessagesFromUi()
|
|||
synth.noteOn(0, data[1] & 0x7f, data[2] & 0x7f);
|
||||
break;
|
||||
case 0xb0:
|
||||
synth.cc(0, data[1] & 0x7f, data[2] & 0x7f);
|
||||
synth.automateHdcc(0, data[1] & 0x7f, static_cast<float>(data[2] & 0x7f) / 127.0f);
|
||||
break;
|
||||
case 0xe0:
|
||||
synth.pitchWheel(0, (data[2] << 7) + data[1] - 8192);
|
||||
|
|
@ -500,7 +555,7 @@ void SfizzVstProcessor::processMessagesFromUi()
|
|||
|
||||
tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
||||
{
|
||||
// Note(jpc) this notification is not necessarily handled by the RT thread
|
||||
// Note(jpc) this notification is not handled by the RT thread
|
||||
|
||||
tresult result = AudioEffect::notify(message);
|
||||
if (result != kResultFalse)
|
||||
|
|
@ -535,10 +590,8 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
|||
_synth->loadScalaFile(_state.scalaFile);
|
||||
lock.unlock();
|
||||
|
||||
Steinberg::OPtr<Vst::IMessage> reply { allocateMessage() };
|
||||
reply->setMessageID("LoadedScala");
|
||||
reply->getAttributes()->setBinary("File", _state.scalaFile.data(), _state.scalaFile.size());
|
||||
sendMessage(reply);
|
||||
_scalaUpdate->setPath(_state.scalaFile);
|
||||
_scalaUpdate->deferUpdate();
|
||||
}
|
||||
else if (!std::strcmp(id, "MidiMessage")) {
|
||||
const void* data = nullptr;
|
||||
|
|
@ -557,12 +610,81 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
|||
return result;
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args)
|
||||
void PLUGIN_API SfizzVstProcessor::update(FUnknown* changedUnknown, int32 message)
|
||||
{
|
||||
if (processUpdate(changedUnknown, message))
|
||||
return;
|
||||
|
||||
AudioEffect::update(changedUnknown, message);
|
||||
}
|
||||
|
||||
bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message)
|
||||
{
|
||||
if (QueuedUpdates* update = FCast<QueuedUpdates>(changedUnknown)) {
|
||||
for (FObject* queuedUpdate : update->getUpdates(this))
|
||||
processUpdate(queuedUpdate, message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (OSCUpdate* update = FCast<OSCUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NoteUpdate* update = FCast<NoteUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SfzUpdate* update = FCast<SfzUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SfzDescriptionUpdate* update = FCast<SfzDescriptionUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ScalaUpdate* update = FCast<ScalaUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AutomationUpdate* update = FCast<AutomationUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::receiveOSC(int delay, const char* path, const char* sig, const sfizz_arg_t* args)
|
||||
{
|
||||
if (!strcmp(path, "/sw/last/current") && sig)
|
||||
{
|
||||
if (sig[0] == 'i')
|
||||
_state.lastKeyswitch = args[0].i;
|
||||
else if (sig[0] == 'N')
|
||||
_state.lastKeyswitch = -1;
|
||||
}
|
||||
|
||||
uint8_t* oscTemp = _oscTemp.get();
|
||||
uint32 oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args);
|
||||
if (oscSize <= kOscTempSize) {
|
||||
if (writeWorkerMessage(kMsgIdReceiveMessage, oscTemp, oscSize))
|
||||
if (writeWorkerMessage(kMsgIdReceiveOSC, oscTemp, oscSize))
|
||||
_semaToWorker.post();
|
||||
}
|
||||
}
|
||||
|
|
@ -572,16 +694,7 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i
|
|||
sfz::Sfizz& synth = *_synth;
|
||||
|
||||
if (!filePath.empty()) {
|
||||
const sfz::InstrumentFormatRegistry& formatRegistry = sfz::InstrumentFormatRegistry::getInstance();
|
||||
const sfz::InstrumentFormat* format = formatRegistry.getMatchingFormat(filePath);
|
||||
if (!format)
|
||||
synth.loadSfzFile(filePath);
|
||||
else {
|
||||
auto importer = format->createImporter();
|
||||
std::string virtualPath = filePath + ".sfz";
|
||||
std::string sfzText = importer->convertToSfz(filePath);
|
||||
synth.loadSfzString(virtualPath, sfzText);
|
||||
}
|
||||
sfizz_load_or_import_file(synth.handle(), filePath.c_str(), nullptr);
|
||||
}
|
||||
else {
|
||||
synth.loadSfzString("default.sfz", defaultSfzText);
|
||||
|
|
@ -589,12 +702,6 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i
|
|||
|
||||
const std::string descBlob = getDescriptionBlob(synth.handle());
|
||||
|
||||
Steinberg::OPtr<Vst::IMessage> loadMessage { allocateMessage() };
|
||||
loadMessage->setMessageID("LoadedSfz");
|
||||
Vst::IAttributeList* loadAttrs = loadMessage->getAttributes();
|
||||
loadAttrs->setBinary("File", filePath.data(), filePath.size());
|
||||
loadAttrs->setBinary("Description", descBlob.data(), descBlob.size());
|
||||
|
||||
{
|
||||
std::vector<absl::optional<float>> newControllers(sfz::config::numCCs);
|
||||
const std::vector<absl::optional<float>> oldControllers = std::move(_state.controllers);
|
||||
|
|
@ -609,7 +716,7 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i
|
|||
for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
if (absl::optional<float> value = oldControllers[cc]) {
|
||||
newControllers[cc] = *value;
|
||||
synth.hdcc(0, int(cc), *value);
|
||||
synth.automateHdcc(0, int(cc), *value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -617,26 +724,21 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i
|
|||
}
|
||||
|
||||
// create a message which requests the controller to automate initial parameters
|
||||
Steinberg::OPtr<Vst::IMessage> automateMessage { allocateMessage() };
|
||||
automateMessage->setMessageID("Automate");
|
||||
Vst::IAttributeList* automateAttrs = automateMessage->getAttributes();
|
||||
std::string automateBlob;
|
||||
automateBlob.reserve(sfz::config::numCCs * (sizeof(uint32) + sizeof(float)));
|
||||
std::vector<AutomationUpdate::Item> automationItems;
|
||||
automationItems.reserve(sfz::config::numCCs);
|
||||
for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
uint32 pid = kPidCC0 + cc;
|
||||
Vst::ParamID pid = kPidCC0 + cc;
|
||||
float value = _state.controllers[cc].value_or(0.0f);
|
||||
automateBlob.append(reinterpret_cast<const char*>(&pid), sizeof(uint32));
|
||||
automateBlob.append(reinterpret_cast<const char*>(&value), sizeof(float));
|
||||
automationItems.emplace_back(pid, value);
|
||||
}
|
||||
automateAttrs->setBinary("Data", automateBlob.data(), uint32(automateBlob.size()));
|
||||
|
||||
// sending can fail if controller is not connected yet, so keep it around
|
||||
_loadedSfzMessage = loadMessage;
|
||||
_automateMessage = automateMessage;
|
||||
|
||||
// send message
|
||||
sendMessage(loadMessage);
|
||||
sendMessage(automateMessage);
|
||||
_sfzUpdate->setPath(filePath);
|
||||
_sfzUpdate->deferUpdate();
|
||||
_sfzDescriptionUpdate->setDescription(descBlob);
|
||||
_sfzDescriptionUpdate->deferUpdate();
|
||||
_automationUpdate->setItems(std::move(automationItems));
|
||||
_automationUpdate->deferUpdate();
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::doBackgroundWork()
|
||||
|
|
@ -650,8 +752,13 @@ void SfizzVstProcessor::doBackgroundWork()
|
|||
for (;;) {
|
||||
bool isNotified = _semaToWorker.timed_wait(kBackgroundIdleInterval.count());
|
||||
|
||||
if (!_workRunning)
|
||||
if (!_workRunning) {
|
||||
// if the quit signal is sent, the semaphore is also signaled
|
||||
// make sure the count is kept consistent
|
||||
if (!isNotified)
|
||||
_semaToWorker.wait();
|
||||
break;
|
||||
}
|
||||
|
||||
const char* id = nullptr;
|
||||
RTMessagePtr msg;
|
||||
|
|
@ -680,17 +787,17 @@ void SfizzVstProcessor::doBackgroundWork()
|
|||
std::lock_guard<SpinMutex> lock(_processMutex);
|
||||
_synth->setPreloadSize(value);
|
||||
}
|
||||
else if (id == kMsgIdReceiveMessage) {
|
||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
||||
notification->setMessageID("ReceivedMessage");
|
||||
notification->getAttributes()->setBinary("Message", msg->payload<uint8_t>(), msg->size);
|
||||
sendMessage(notification);
|
||||
else if (id == kMsgIdReceiveOSC) {
|
||||
IPtr<OSCUpdate> update = Steinberg::owned(
|
||||
new OSCUpdate(msg->payload<uint8>(), msg->size));
|
||||
_queuedMessages->enqueue(update);
|
||||
_queuedMessages->deferUpdate();
|
||||
}
|
||||
else if (id == kMsgIdNoteEvents) {
|
||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
||||
notification->setMessageID("NoteEvents");
|
||||
notification->getAttributes()->setBinary("Events", msg->payload<uint8_t>(), msg->size);
|
||||
sendMessage(notification);
|
||||
IPtr<NoteUpdate> update = Steinberg::owned(
|
||||
new NoteUpdate(msg->payload<NoteUpdate::Item>(), msg->size / sizeof(NoteUpdate::Item)));
|
||||
_queuedMessages->enqueue(update);
|
||||
_queuedMessages->deferUpdate();
|
||||
}
|
||||
|
||||
Clock::time_point currentTime = Clock::now();
|
||||
|
|
@ -707,13 +814,11 @@ void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter)
|
|||
{
|
||||
SfizzPlayState ps;
|
||||
ps.activeVoices = _synth->getNumActiveVoices();
|
||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
||||
notification->setMessageID("NotifiedPlayState");
|
||||
notification->getAttributes()->setBinary("PlayState", &ps, sizeof(ps));
|
||||
sendMessage(notification);
|
||||
_playStateUpdate->setState(ps);
|
||||
_playStateUpdate->deferUpdate();
|
||||
}
|
||||
|
||||
if (idleCounter % 10 == 0) {
|
||||
if (idleCounter % 25 == 0) {
|
||||
if (_synth->shouldReloadFile()) {
|
||||
fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n");
|
||||
std::lock_guard<SpinMutex> lock(_processMutex);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
#pragma once
|
||||
#include "SfizzVstState.h"
|
||||
#include "SfizzVstUpdates.h"
|
||||
#include "OrderedEventProcessor.h"
|
||||
#include "plugin/RMSFollower.h"
|
||||
#include "sfizz/RTSemaphore.h"
|
||||
#include "ring_buffer/ring_buffer.h"
|
||||
#include "public.sdk/source/vst/vstaudioeffect.h"
|
||||
|
|
@ -26,6 +28,7 @@ public:
|
|||
~SfizzVstProcessor();
|
||||
|
||||
tresult PLUGIN_API initialize(FUnknown* context) override;
|
||||
tresult PLUGIN_API terminate() override;
|
||||
tresult PLUGIN_API setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) override;
|
||||
|
||||
tresult PLUGIN_API connect(IConnectionPoint* other) override;
|
||||
|
|
@ -45,6 +48,7 @@ public:
|
|||
void processMessagesFromUi();
|
||||
|
||||
tresult PLUGIN_API notify(Vst::IMessage* message) override;
|
||||
void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override;
|
||||
|
||||
static FUnknown* createInstance(void*);
|
||||
|
||||
|
|
@ -54,8 +58,6 @@ public:
|
|||
private:
|
||||
// synth state. acquire processMutex before accessing
|
||||
std::unique_ptr<sfz::Sfizz> _synth;
|
||||
Steinberg::IPtr<Vst::IMessage> _loadedSfzMessage;
|
||||
Steinberg::IPtr<Vst::IMessage> _automateMessage;
|
||||
bool _isActive = false;
|
||||
SfizzVstState _state;
|
||||
float _currentStretchedTuning = 0;
|
||||
|
|
@ -63,10 +65,23 @@ private:
|
|||
// whether allowed to perform events (owns the processing lock)
|
||||
bool _canPerformEventsAndParameters {};
|
||||
|
||||
// level meters
|
||||
RMSFollower _rmsFollower;
|
||||
bool _editorIsOpen = false;
|
||||
|
||||
// updates
|
||||
IPtr<QueuedUpdates> _queuedMessages;
|
||||
IPtr<PlayStateUpdate> _playStateUpdate;
|
||||
IPtr<SfzUpdate> _sfzUpdate;
|
||||
IPtr<SfzDescriptionUpdate> _sfzDescriptionUpdate;
|
||||
IPtr<ScalaUpdate> _scalaUpdate;
|
||||
IPtr<AutomationUpdate> _automationUpdate;
|
||||
bool processUpdate(FUnknown* changedUnknown, int32 message);
|
||||
|
||||
// client
|
||||
sfz::ClientPtr _client;
|
||||
std::unique_ptr<uint8_t[]> _oscTemp;
|
||||
void receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args);
|
||||
void receiveOSC(int delay, const char* path, const char* sig, const sfizz_arg_t* args);
|
||||
|
||||
// misc
|
||||
void loadSfzFileOrDefault(const std::string& filePath, bool initParametersFromState);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,14 @@ tresult SfizzVstState::load(IBStream* state)
|
|||
oscillatorQuality = defaults.oscillatorQuality;
|
||||
}
|
||||
|
||||
if (version >= 4) {
|
||||
if (!s.readInt32(lastKeyswitch))
|
||||
return kResultFalse;
|
||||
}
|
||||
else {
|
||||
lastKeyswitch = -1;
|
||||
}
|
||||
|
||||
controllers.clear();
|
||||
if (version >= 2) {
|
||||
uint32 count;
|
||||
|
|
@ -135,6 +143,9 @@ tresult SfizzVstState::store(IBStream* state) const
|
|||
if (!s.writeInt32(oscillatorQuality))
|
||||
return kResultFalse;
|
||||
|
||||
if (!s.writeInt32(lastKeyswitch))
|
||||
return kResultFalse;
|
||||
|
||||
{
|
||||
uint32 ccCount = 0;
|
||||
uint32 ccLimit = uint32(std::min(controllers.size(), size_t(0x10000)));
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@ public:
|
|||
float stretchedTuning = 0.0;
|
||||
int32 sampleQuality = 2;
|
||||
int32 oscillatorQuality = 1;
|
||||
int32 lastKeyswitch = -1;
|
||||
std::vector<absl::optional<float>> controllers;
|
||||
|
||||
static constexpr uint64 currentStateVersion = 3;
|
||||
static constexpr uint64 currentStateVersion = 4;
|
||||
|
||||
tresult load(IBStream* state);
|
||||
tresult store(IBStream* state) const;
|
||||
|
|
|
|||
|
|
@ -8,61 +8,167 @@
|
|||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
OSCUpdate::~OSCUpdate()
|
||||
void QueuedUpdates::enqueue(IPtr<FObject> update)
|
||||
{
|
||||
clear();
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (std::pair<IDependent* const, List>& item : updates_)
|
||||
item.second.push_back(update);
|
||||
}
|
||||
|
||||
void OSCUpdate::clear()
|
||||
auto QueuedUpdates::getUpdates(IDependent* dep) -> List
|
||||
{
|
||||
if (allocated_)
|
||||
delete[] reinterpret_cast<const uint8_t*>(data_);
|
||||
data_ = nullptr;
|
||||
size_ = 0;
|
||||
allocated_ = false;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
List list;
|
||||
auto it = updates_.find(dep);
|
||||
if (it != updates_.end())
|
||||
std::swap(list, it->second);
|
||||
return list;
|
||||
}
|
||||
|
||||
void OSCUpdate::setMessage(const void* data, uint32_t size, bool copy)
|
||||
void QueuedUpdates::addDependent(IDependent* dep)
|
||||
{
|
||||
clear();
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
FObject::addDependent(dep);
|
||||
updates_.emplace(dep, List());
|
||||
}
|
||||
|
||||
if (copy) {
|
||||
uint8_t *buffer = new uint8_t[size];
|
||||
std::memcpy(buffer, data, size);
|
||||
data = buffer;
|
||||
}
|
||||
|
||||
data_ = data;
|
||||
size_ = size;
|
||||
allocated_ = copy;
|
||||
void QueuedUpdates::removeDependent(IDependent* dep)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
FObject::removeDependent(dep);
|
||||
updates_.erase(dep);
|
||||
}
|
||||
|
||||
///
|
||||
NoteUpdate::~NoteUpdate()
|
||||
bool OSCUpdate::saveToAttributes(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
clear();
|
||||
return attrs->setBinary("Data", data(), size()) == kResultTrue;
|
||||
}
|
||||
|
||||
void NoteUpdate::clear()
|
||||
bool OSCUpdate::loadFromAttributes(Vst::IAttributeList* attrs)
|
||||
{
|
||||
if (allocated_)
|
||||
delete[] events_;
|
||||
events_ = nullptr;
|
||||
count_ = 0;
|
||||
allocated_ = false;
|
||||
const void* data;
|
||||
uint32 size;
|
||||
if (attrs->getBinary("Data", data, size) != kResultTrue)
|
||||
return false;
|
||||
const uint8* data8 = reinterpret_cast<const uint8*>(data);
|
||||
data_.assign(data8, data8 + size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NoteUpdate::setEvents(const std::pair<uint32_t, float>* events, uint32_t count, bool copy)
|
||||
///
|
||||
bool NoteUpdate::saveToAttributes(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
clear();
|
||||
|
||||
if (copy) {
|
||||
auto *buffer = new std::pair<uint32_t, float>[count];
|
||||
std::copy_n(events, count, buffer);
|
||||
events = buffer;
|
||||
}
|
||||
|
||||
events_ = events;
|
||||
count_ = count;
|
||||
allocated_ = copy;
|
||||
return attrs->setBinary("Events", events_.data(), events_.size() * sizeof(Item)) == kResultTrue;
|
||||
}
|
||||
|
||||
bool NoteUpdate::loadFromAttributes(Vst::IAttributeList* attrs)
|
||||
{
|
||||
const void* binData = nullptr;
|
||||
uint32 binSize = 0;
|
||||
if (attrs->getBinary("Events", binData, binSize) != kResultTrue)
|
||||
return false;
|
||||
|
||||
const Item* events = reinterpret_cast<const Item*>(binData);
|
||||
uint32 numEvents = binSize / sizeof(Item);
|
||||
|
||||
events_.assign(events, events + numEvents);
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
bool FilePathUpdate::saveFilePathAttributes_(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return attrs->setBinary("Path", path_.data(), path_.size()) == kResultTrue;
|
||||
}
|
||||
|
||||
bool FilePathUpdate::loadFilePathAttributes_(Vst::IAttributeList* attrs)
|
||||
{
|
||||
const void* binData = nullptr;
|
||||
uint32 binSize = 0;
|
||||
if (attrs->getBinary("Path", binData, binSize) != kResultTrue)
|
||||
return false;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
path_.assign(reinterpret_cast<const char *>(binData), binSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
bool SfzUpdate::saveToAttributes(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
return saveFilePathAttributes_(attrs);
|
||||
}
|
||||
|
||||
bool SfzUpdate::loadFromAttributes(Vst::IAttributeList* attrs)
|
||||
{
|
||||
return loadFilePathAttributes_(attrs);
|
||||
}
|
||||
|
||||
///
|
||||
bool ScalaUpdate::saveToAttributes(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
return saveFilePathAttributes_(attrs);
|
||||
}
|
||||
|
||||
bool ScalaUpdate::loadFromAttributes(Vst::IAttributeList* attrs)
|
||||
{
|
||||
return loadFilePathAttributes_(attrs);
|
||||
}
|
||||
|
||||
///
|
||||
bool SfzDescriptionUpdate::saveToAttributes(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return attrs->setBinary("Blob", description_.data(), description_.size()) == kResultTrue;
|
||||
}
|
||||
|
||||
bool SfzDescriptionUpdate::loadFromAttributes(Vst::IAttributeList* attrs)
|
||||
{
|
||||
const void* binData = nullptr;
|
||||
uint32 binSize = 0;
|
||||
if (attrs->getBinary("Blob", binData, binSize) != kResultTrue)
|
||||
return false;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
description_.assign(reinterpret_cast<const char *>(binData), binSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
bool PlayStateUpdate::saveToAttributes(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return attrs->setInt("ActiveVoices", state_.activeVoices) == kResultTrue;
|
||||
}
|
||||
|
||||
bool PlayStateUpdate::loadFromAttributes(Vst::IAttributeList* attrs)
|
||||
{
|
||||
int64 activeVoices;
|
||||
if (attrs->getInt("ActiveVoices", activeVoices) != kResultTrue)
|
||||
return false;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
state_.activeVoices = static_cast<uint32>(activeVoices);
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
bool AutomationUpdate::saveToAttributes(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return attrs->setBinary("Items", items_.data(), items_.size() * sizeof(Item)) == kResultTrue;
|
||||
}
|
||||
|
||||
bool AutomationUpdate::loadFromAttributes(Vst::IAttributeList* attrs)
|
||||
{
|
||||
const void* binData = nullptr;
|
||||
uint32 binSize = 0;
|
||||
if (attrs->getBinary("Items", binData, binSize) != kResultTrue)
|
||||
return false;
|
||||
|
||||
const Item* events = reinterpret_cast<const Item*>(binData);
|
||||
uint32 numEvents = binSize / sizeof(Item);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
items_.assign(events, events + numEvents);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,70 +6,102 @@
|
|||
|
||||
#pragma once
|
||||
#include "SfizzVstState.h"
|
||||
#include <public.sdk/source/vst/vstcomponentbase.h>
|
||||
#include <pluginterfaces/vst/ivstmessage.h>
|
||||
#include <base/source/fobject.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a single OSC message
|
||||
* Is is supposed to be used synchronously.
|
||||
* (ie. FObject::changed or UpdateHandler::triggerUpdates)
|
||||
* @brief Update which is convertible with Vst::IMessage back and forth.
|
||||
*/
|
||||
class OSCUpdate : public Steinberg::FObject {
|
||||
template <class T> class IConvertibleToMessage {
|
||||
public:
|
||||
virtual ~IConvertibleToMessage() {}
|
||||
|
||||
IPtr<Vst::IMessage> convertToMessage(Vst::ComponentBase* sender) const;
|
||||
bool convertFromMessage(Vst::IMessage& message);
|
||||
static IPtr<T> createFromMessage(Vst::IMessage& message);
|
||||
|
||||
protected:
|
||||
virtual bool saveToAttributes(Vst::IAttributeList* attrs) const = 0;
|
||||
virtual bool loadFromAttributes(Vst::IAttributeList* attrs) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a FIFO queue of one-time updates
|
||||
*/
|
||||
class QueuedUpdates : public Steinberg::FObject {
|
||||
public:
|
||||
using List = std::vector<IPtr<FObject>>;
|
||||
|
||||
void enqueue(IPtr<FObject> update);
|
||||
List getUpdates(IDependent* dep);
|
||||
|
||||
void addDependent(IDependent* dep) override;
|
||||
void removeDependent(IDependent* dep) override;
|
||||
|
||||
OBJ_METHODS(QueuedUpdates, FObject)
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::map<IDependent*, List> updates_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a single OSC message
|
||||
*/
|
||||
class OSCUpdate : public Steinberg::FObject,
|
||||
public IConvertibleToMessage<OSCUpdate> {
|
||||
public:
|
||||
OSCUpdate() = default;
|
||||
~OSCUpdate();
|
||||
void clear();
|
||||
void setMessage(const void* data, uint32_t size, bool copy);
|
||||
OSCUpdate(const uint8* data, uint32 size) : data_(data, data + size) {}
|
||||
const uint8* data() const noexcept { return data_.data(); }
|
||||
uint32_t size() const noexcept { return static_cast<uint32_t>(data_.size()); }
|
||||
|
||||
const void* data() const noexcept { return data_; }
|
||||
const uint32_t size() const noexcept { return size_; }
|
||||
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||
bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||
|
||||
OBJ_METHODS(OSCUpdate, FObject)
|
||||
|
||||
private:
|
||||
const void* data_ = nullptr;
|
||||
uint32_t size_ = 0;
|
||||
bool allocated_ = false;
|
||||
|
||||
private:
|
||||
OSCUpdate(const OSCUpdate&) = delete;
|
||||
OSCUpdate& operator=(const OSCUpdate&) = delete;
|
||||
std::vector<uint8> data_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies one or more note on/off events
|
||||
* Is is supposed to be used synchronously.
|
||||
* (ie. FObject::changed or UpdateHandler::triggerUpdates)
|
||||
*/
|
||||
class NoteUpdate : public Steinberg::FObject {
|
||||
class NoteUpdate : public Steinberg::FObject,
|
||||
public IConvertibleToMessage<NoteUpdate> {
|
||||
public:
|
||||
NoteUpdate() = default;
|
||||
~NoteUpdate();
|
||||
void clear();
|
||||
void setEvents(const std::pair<uint32_t, float>* events, uint32_t count, bool copy);
|
||||
using Item = std::pair<uint32, float>;
|
||||
|
||||
const std::pair<uint32_t, float>* events() const noexcept { return events_; }
|
||||
const uint32_t count() const noexcept { return count_; }
|
||||
NoteUpdate() = default;
|
||||
NoteUpdate(const Item* items, uint32 count) : events_(items, items + count) {}
|
||||
const Item* events() const noexcept { return events_.data(); }
|
||||
const uint32 count() const noexcept { return static_cast<uint32>(events_.size()); }
|
||||
|
||||
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||
bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||
|
||||
OBJ_METHODS(NoteUpdate, FObject)
|
||||
|
||||
private:
|
||||
const std::pair<uint32_t, float>* events_ = nullptr;
|
||||
uint32_t count_ = 0;
|
||||
bool allocated_ = false;
|
||||
|
||||
private:
|
||||
NoteUpdate(const NoteUpdate&) = delete;
|
||||
NoteUpdate& operator=(const NoteUpdate&) = delete;
|
||||
std::vector<Item> events_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a change of SFZ file.
|
||||
* @brief Abstract update which notifies change of a certain file path.
|
||||
*/
|
||||
class SfzUpdate : public Steinberg::FObject {
|
||||
class FilePathUpdate : public Steinberg::FObject {
|
||||
protected:
|
||||
FilePathUpdate() = default;
|
||||
|
||||
public:
|
||||
void setPath(std::string newPath)
|
||||
{
|
||||
|
|
@ -83,17 +115,46 @@ public:
|
|||
return path_;
|
||||
}
|
||||
|
||||
OBJ_METHODS(SfzUpdate, FObject)
|
||||
OBJ_METHODS(FilePathUpdate, FObject)
|
||||
|
||||
protected:
|
||||
bool saveFilePathAttributes_(Vst::IAttributeList* attrs) const;
|
||||
bool loadFilePathAttributes_(Vst::IAttributeList* attrs);
|
||||
|
||||
private:
|
||||
std::string path_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a change of SFZ file.
|
||||
*/
|
||||
class SfzUpdate : public FilePathUpdate,
|
||||
public IConvertibleToMessage<SfzUpdate> {
|
||||
public:
|
||||
OBJ_METHODS(SfzUpdate, FilePathUpdate)
|
||||
|
||||
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||
bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a change of scala file.
|
||||
*/
|
||||
class ScalaUpdate : public FilePathUpdate,
|
||||
public IConvertibleToMessage<ScalaUpdate> {
|
||||
public:
|
||||
OBJ_METHODS(ScalaUpdate, FilePathUpdate)
|
||||
|
||||
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||
bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a change of SFZ description.
|
||||
*/
|
||||
class SfzDescriptionUpdate : public Steinberg::FObject {
|
||||
class SfzDescriptionUpdate : public Steinberg::FObject,
|
||||
public IConvertibleToMessage<SfzDescriptionUpdate> {
|
||||
public:
|
||||
void setDescription(std::string newDescription)
|
||||
{
|
||||
|
|
@ -107,6 +168,9 @@ public:
|
|||
return description_;
|
||||
}
|
||||
|
||||
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||
bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||
|
||||
OBJ_METHODS(SfzDescriptionUpdate, FObject)
|
||||
|
||||
private:
|
||||
|
|
@ -114,34 +178,11 @@ private:
|
|||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a change of scala file.
|
||||
*/
|
||||
class ScalaUpdate : public Steinberg::FObject {
|
||||
public:
|
||||
void setPath(std::string newPath)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
path_ = std::move(newPath);
|
||||
}
|
||||
|
||||
std::string getPath() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return path_;
|
||||
}
|
||||
|
||||
OBJ_METHODS(ScalaUpdate, FObject)
|
||||
|
||||
private:
|
||||
std::string path_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which indicates the playing SFZ status.
|
||||
*/
|
||||
class PlayStateUpdate : public Steinberg::FObject {
|
||||
class PlayStateUpdate : public Steinberg::FObject,
|
||||
public IConvertibleToMessage<PlayStateUpdate> {
|
||||
public:
|
||||
void setState(SfizzPlayState newState)
|
||||
{
|
||||
|
|
@ -155,9 +196,46 @@ public:
|
|||
return state_;
|
||||
}
|
||||
|
||||
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||
virtual bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||
|
||||
OBJ_METHODS(PlayStateUpdate, FObject)
|
||||
|
||||
private:
|
||||
SfizzPlayState state_ {};
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which automates a pack of parameters
|
||||
*/
|
||||
class AutomationUpdate : public Steinberg::FObject,
|
||||
public IConvertibleToMessage<AutomationUpdate> {
|
||||
public:
|
||||
using Item = std::pair<Vst::ParamID, float>;
|
||||
|
||||
AutomationUpdate() = default;
|
||||
|
||||
void setItems(std::vector<Item> newItems)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
items_ = std::move(newItems);
|
||||
}
|
||||
|
||||
std::vector<Item> getItems() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return items_;
|
||||
}
|
||||
|
||||
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||
bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||
|
||||
OBJ_METHODS(AutomationUpdate, FObject)
|
||||
|
||||
private:
|
||||
std::vector<Item> items_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
#include "SfizzVstUpdates.hpp"
|
||||
|
|
|
|||
41
plugins/vst/SfizzVstUpdates.hpp
Normal file
41
plugins/vst/SfizzVstUpdates.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// 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 "SfizzVstUpdates.h"
|
||||
|
||||
template <class T>
|
||||
IPtr<Vst::IMessage> IConvertibleToMessage<T>::convertToMessage(Vst::ComponentBase* sender) const
|
||||
{
|
||||
IPtr<Vst::IMessage> message = Steinberg::owned(sender->allocateMessage());
|
||||
if (!message)
|
||||
return nullptr;
|
||||
message->setMessageID(static_cast<const T*>(this)->isA());
|
||||
if (!saveToAttributes(message->getAttributes()))
|
||||
return nullptr;
|
||||
return message;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
IPtr<T> IConvertibleToMessage<T>::createFromMessage(Vst::IMessage& message)
|
||||
{
|
||||
IPtr<T> object;
|
||||
if (!strcmp(T::getFClassID(), message.getMessageID())) {
|
||||
object = Steinberg::owned(new T);
|
||||
if (!object->loadFromAttributes(message.getAttributes()))
|
||||
object = nullptr;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool IConvertibleToMessage<T>::convertFromMessage(Vst::IMessage& message)
|
||||
{
|
||||
bool success = false;
|
||||
if (!strcmp(T::getFClassID(), message.getMessageID()))
|
||||
success = loadFromAttributes(message.getAttributes());
|
||||
return success;
|
||||
}
|
||||
|
|
@ -1,33 +1,38 @@
|
|||
// SPDX-License-Identifier: GPL-3.0
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||
#include "X11RunLoop.h"
|
||||
#include "vstgui/lib/platform/linux/x11platform.h"
|
||||
#include "base/source/fobject.h"
|
||||
#include <vector>
|
||||
#include <typeinfo>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
RunLoop::RunLoop(Steinberg::FUnknown* runLoop)
|
||||
: runLoop(runLoop)
|
||||
{
|
||||
}
|
||||
struct RunLoop::Impl {
|
||||
struct EventHandler;
|
||||
struct TimerHandler;
|
||||
|
||||
RunLoop::~RunLoop() {}
|
||||
using EventHandlers = std::vector<Steinberg::IPtr<EventHandler>>;
|
||||
using TimerHandlers = std::vector<Steinberg::IPtr<TimerHandler>>;
|
||||
|
||||
SharedPointer<RunLoop> RunLoop::get()
|
||||
{
|
||||
return X11::RunLoop::get().cast<VSTGUI::RunLoop>();
|
||||
}
|
||||
EventHandlers eventHandlers;
|
||||
TimerHandlers timerHandlers;
|
||||
Steinberg::FUnknownPtr<Steinberg::Linux::IRunLoop> runLoop;
|
||||
};
|
||||
|
||||
struct RunLoop::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject {
|
||||
//------------------------------------------------------------------------------
|
||||
struct RunLoop::Impl::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject {
|
||||
X11::IEventHandler* handler { nullptr };
|
||||
bool alive { false };
|
||||
|
||||
void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override
|
||||
{
|
||||
if (alive && handler)
|
||||
handler->onEvent();
|
||||
}
|
||||
void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override;
|
||||
|
||||
DELEGATE_REFCOUNT(Steinberg::FObject)
|
||||
DEFINE_INTERFACES
|
||||
|
|
@ -35,15 +40,11 @@ struct RunLoop::EventHandler final : Steinberg::Linux::IEventHandler, public Ste
|
|||
END_DEFINE_INTERFACES(Steinberg::FObject)
|
||||
};
|
||||
|
||||
struct RunLoop::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject {
|
||||
struct RunLoop::Impl::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject {
|
||||
X11::ITimerHandler* handler { nullptr };
|
||||
bool alive { false };
|
||||
|
||||
void PLUGIN_API onTimer() override
|
||||
{
|
||||
if (alive && handler)
|
||||
handler->onTimer();
|
||||
}
|
||||
void PLUGIN_API onTimer() override;
|
||||
|
||||
DELEGATE_REFCOUNT(Steinberg::FObject)
|
||||
DEFINE_INTERFACES
|
||||
|
|
@ -51,45 +52,126 @@ struct RunLoop::TimerHandler final : Steinberg::Linux::ITimerHandler, public Ste
|
|||
END_DEFINE_INTERFACES(Steinberg::FObject)
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void PLUGIN_API RunLoop::Impl::EventHandler::onFDIsSet(Steinberg::Linux::FileDescriptor)
|
||||
{
|
||||
SharedPointer<RunLoop> runLoop = RunLoop::get();
|
||||
if (!runLoop) {
|
||||
fprintf(stderr, "[x11] event has fired without active runloop\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (alive && handler)
|
||||
handler->onEvent();
|
||||
}
|
||||
|
||||
void PLUGIN_API RunLoop::Impl::TimerHandler::onTimer()
|
||||
{
|
||||
SharedPointer<RunLoop> runLoop = RunLoop::get();
|
||||
if (!runLoop) {
|
||||
fprintf(stderr, "[x11] timer has fired without active runloop\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (alive && handler)
|
||||
handler->onTimer();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
RunLoop::RunLoop(Steinberg::FUnknown* runLoop)
|
||||
: impl(new Impl)
|
||||
{
|
||||
impl->runLoop = runLoop;
|
||||
}
|
||||
|
||||
RunLoop::~RunLoop()
|
||||
{
|
||||
//dumpCurrentState();
|
||||
|
||||
if (0) {
|
||||
// remove any leftover handlers
|
||||
for (size_t i = 0; i < impl->eventHandlers.size(); ++i) {
|
||||
const auto& eh = impl->eventHandlers[i];
|
||||
if (eh->alive && eh->handler) {
|
||||
impl->runLoop->unregisterEventHandler(eh.get());
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < impl->timerHandlers.size(); ++i) {
|
||||
const auto& th = impl->timerHandlers[i];
|
||||
if (th->alive && th->handler) {
|
||||
impl->runLoop->unregisterTimer(th.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SharedPointer<RunLoop> RunLoop::get()
|
||||
{
|
||||
return X11::RunLoop::get().cast<VSTGUI::RunLoop>();
|
||||
}
|
||||
|
||||
void RunLoop::processSomeEvents()
|
||||
{
|
||||
for (size_t i = 0; i < eventHandlers.size(); ++i) {
|
||||
const auto& eh = eventHandlers[i];
|
||||
for (size_t i = 0; i < impl->eventHandlers.size(); ++i) {
|
||||
const auto& eh = impl->eventHandlers[i];
|
||||
if (eh->alive && eh->handler) {
|
||||
eh->handler->onEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunLoop::cleanupDeadHandlers()
|
||||
void RunLoop::dumpCurrentState()
|
||||
{
|
||||
for (size_t i = 0; i < eventHandlers.size(); ++i) {
|
||||
const auto& eh = eventHandlers[i];
|
||||
if (!eh->alive) {
|
||||
runLoop->unregisterEventHandler(eh);
|
||||
eventHandlers.erase(eventHandlers.begin() + i--);
|
||||
}
|
||||
fprintf(stderr, "=== X11 runloop ===\n");
|
||||
|
||||
fprintf(stderr, "\t" "Event slots:\n");
|
||||
for (size_t i = 0, n = impl->eventHandlers.size(); i < n; ++i) {
|
||||
Impl::EventHandler *eh = impl->eventHandlers[i].get();
|
||||
fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, eh->alive, eh->handler, (eh->alive && eh->handler) ? typeid(*eh->handler).name() : "");
|
||||
}
|
||||
for (size_t i = 0; i < timerHandlers.size(); ++i) {
|
||||
const auto& th = timerHandlers[i];
|
||||
if (!th->alive) {
|
||||
runLoop->unregisterTimer(th);
|
||||
timerHandlers.erase(timerHandlers.begin() + i--);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\t" "Timer slots:\n");
|
||||
for (size_t i = 0, n = impl->timerHandlers.size(); i < n; ++i) {
|
||||
Impl::TimerHandler *th = impl->timerHandlers[i].get();
|
||||
fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, th->alive, th->handler, (th->alive && th->handler) ? typeid(*th->handler).name() : "");
|
||||
}
|
||||
|
||||
fprintf(stderr, "===/X11 runloop ===\n");
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void insertHandler(std::vector<Steinberg::IPtr<T>>& list, Steinberg::IPtr<T> handler)
|
||||
{
|
||||
size_t i = 0;
|
||||
size_t n = list.size();
|
||||
while (i < n && list[i]->alive)
|
||||
++i;
|
||||
if (i < n)
|
||||
list[i] = handler;
|
||||
else
|
||||
list.emplace_back(handler);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
static size_t findHandler(const std::vector<Steinberg::IPtr<T>>& list, U* handler)
|
||||
{
|
||||
for (size_t i = 0, n = list.size(); i < n; ++i) {
|
||||
if (list[i]->alive && list[i]->handler == handler)
|
||||
return i;
|
||||
}
|
||||
return ~size_t(0);
|
||||
}
|
||||
|
||||
bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler)
|
||||
{
|
||||
if (!runLoop)
|
||||
if (!impl->runLoop)
|
||||
return false;
|
||||
|
||||
auto smtgHandler = Steinberg::owned(new EventHandler());
|
||||
auto smtgHandler = Steinberg::owned(new Impl::EventHandler);
|
||||
smtgHandler->handler = handler;
|
||||
smtgHandler->alive = true;
|
||||
if (runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) {
|
||||
eventHandlers.push_back(smtgHandler);
|
||||
if (impl->runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) {
|
||||
insertHandler(impl->eventHandlers, smtgHandler);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -97,29 +179,31 @@ bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler)
|
|||
|
||||
bool RunLoop::unregisterEventHandler(X11::IEventHandler* handler)
|
||||
{
|
||||
if (!runLoop)
|
||||
if (!impl->runLoop)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < eventHandlers.size(); ++i) {
|
||||
const auto& eh = eventHandlers[i];
|
||||
if (eh->alive && eh->handler == handler) {
|
||||
eh->alive = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
size_t index = findHandler(impl->eventHandlers, handler);
|
||||
if (index == ~size_t(0))
|
||||
return false;
|
||||
|
||||
Impl::EventHandler *eh = impl->eventHandlers[index].get();
|
||||
if (impl->runLoop->unregisterEventHandler(eh) != Steinberg::kResultTrue)
|
||||
return false;
|
||||
|
||||
eh->alive = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler)
|
||||
{
|
||||
if (!runLoop)
|
||||
if (!impl->runLoop)
|
||||
return false;
|
||||
|
||||
auto smtgHandler = Steinberg::owned(new TimerHandler());
|
||||
auto smtgHandler = Steinberg::owned(new Impl::TimerHandler);
|
||||
smtgHandler->handler = handler;
|
||||
smtgHandler->alive = true;
|
||||
if (runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) {
|
||||
timerHandlers.push_back(smtgHandler);
|
||||
if (impl->runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) {
|
||||
insertHandler(impl->timerHandlers, smtgHandler);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -127,17 +211,19 @@ bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler)
|
|||
|
||||
bool RunLoop::unregisterTimer(X11::ITimerHandler* handler)
|
||||
{
|
||||
if (!runLoop)
|
||||
if (!impl->runLoop)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < timerHandlers.size(); ++i) {
|
||||
const auto& th = timerHandlers[i];
|
||||
if (th->alive && th->handler == handler) {
|
||||
th->alive = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
size_t index = findHandler(impl->timerHandlers, handler);
|
||||
if (index == ~size_t(0))
|
||||
return false;
|
||||
|
||||
Impl::TimerHandler *th = impl->timerHandlers[index].get();
|
||||
if (impl->runLoop->unregisterTimer(th) != Steinberg::kResultTrue)
|
||||
return false;
|
||||
|
||||
th->alive = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace VSTGUI
|
||||
|
|
|
|||
|
|
@ -1,16 +1,28 @@
|
|||
// SPDX-License-Identifier: GPL-3.0
|
||||
/*
|
||||
This is a modified version the X11 run loop from vst3editor.cpp.
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
This version is edited to add more safeguards to protect against host bugs.
|
||||
It also permits to call event processing externally in case the host has a
|
||||
defective X11 event loop notifier.
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
/*
|
||||
This runloop connects to X11 VSTGUI, it connects VST3 and VSTGUI together.
|
||||
The Windows and macOS runloops do not need this, the OS-provided
|
||||
functionality is used instead.
|
||||
|
||||
Previously, this was based on VSTGUI code provided by Steinberg.
|
||||
This is replaced with a rewrite, because the original code has too many
|
||||
issues. For example, it has no robustness in case handlers get added or
|
||||
removed within the execution of the handler.
|
||||
|
||||
This version allows to call event processing externally, in case the host
|
||||
has a defective X11 event loop notifier. (some versions of Bitwig do)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||
#include "vstgui/lib/platform/linux/x11frame.h"
|
||||
#include "pluginterfaces/gui/iplugview.h"
|
||||
#include <memory>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
|
|
@ -22,24 +34,16 @@ public:
|
|||
static SharedPointer<RunLoop> get();
|
||||
|
||||
void processSomeEvents();
|
||||
void cleanupDeadHandlers();
|
||||
void dumpCurrentState();
|
||||
|
||||
// X11::IRunLoop
|
||||
bool registerEventHandler(int fd, X11::IEventHandler* handler);
|
||||
bool unregisterEventHandler(X11::IEventHandler* handler);
|
||||
bool registerTimer(uint64_t interval, X11::ITimerHandler* handler);
|
||||
bool unregisterTimer(X11::ITimerHandler* handler);
|
||||
bool registerEventHandler(int fd, X11::IEventHandler* handler) override;
|
||||
bool unregisterEventHandler(X11::IEventHandler* handler) override;
|
||||
bool registerTimer(uint64_t interval, X11::ITimerHandler* handler) override;
|
||||
bool unregisterTimer(X11::ITimerHandler* handler) override;
|
||||
|
||||
private:
|
||||
struct EventHandler;
|
||||
struct TimerHandler;
|
||||
|
||||
private:
|
||||
using EventHandlers = std::vector<Steinberg::IPtr<EventHandler>>;
|
||||
using TimerHandlers = std::vector<Steinberg::IPtr<TimerHandler>>;
|
||||
EventHandlers eventHandlers;
|
||||
TimerHandlers timerHandlers;
|
||||
Steinberg::FUnknownPtr<Steinberg::Linux::IRunLoop> runLoop;
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
} // namespace VSTGUI
|
||||
|
|
|
|||
|
|
@ -30,11 +30,14 @@ add_library(vst3sdk STATIC EXCLUDE_FROM_ALL
|
|||
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstnoteexpressiontypes.cpp"
|
||||
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstparameters.cpp"
|
||||
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstpresetfile.cpp"
|
||||
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstrepresentation.cpp")
|
||||
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstrepresentation.cpp"
|
||||
"${VST3SDK_BASEDIR}/public.sdk/source/vst/utility/stringconvert.cpp")
|
||||
if(WIN32)
|
||||
target_sources(vst3sdk PRIVATE
|
||||
"${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_win32.cpp")
|
||||
elseif(APPLE)
|
||||
target_sources(vst3sdk PRIVATE
|
||||
"${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_mac.mm")
|
||||
else()
|
||||
target_sources(vst3sdk PRIVATE
|
||||
"${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_linux.cpp")
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit b8566ef3b2a0cba60a96e3ef2001224c865c8b36
|
||||
Subproject commit 93cef1afb7061e488625045ba5a82abaa83d27fe
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit c3948deb407bdbff89de8fb6ab8500ea4df9d6d9
|
||||
Subproject commit 9589800ed94573354bc29de45eec5744523fbfcb
|
||||
|
|
@ -31,6 +31,13 @@ else
|
|||
codesign --sign "${CODESIGN_IDENTITY}" --keychain build.keychain --force --verbose \
|
||||
"${INSTALL_DIR}"/sfizz.lv2/Contents/Frameworks/*.dylib
|
||||
fi
|
||||
# code-sign Puredata and dylibs
|
||||
codesign --sign "${CODESIGN_IDENTITY}" --keychain build.keychain --force --verbose \
|
||||
"${INSTALL_DIR}"/Puredata/*/*.pd_darwin
|
||||
if ls "${INSTALL_DIR}"/Puredata/*/*.dylib &> /dev/null; then
|
||||
codesign --sign "${CODESIGN_IDENTITY}" --keychain build.keychain --force --verbose \
|
||||
"${INSTALL_DIR}"/Puredata/*/*.dylib
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create the DMG
|
||||
|
|
@ -39,12 +46,13 @@ cat > sfizz-dmg.json << EOF
|
|||
"title": "sfizz",
|
||||
"background": "${APPVEYOR_BUILD_FOLDER}/mac/dmg-back.png",
|
||||
"window": {
|
||||
"size": { "width": 500, "height": 500 }
|
||||
"size": { "width": 650, "height": 500 }
|
||||
},
|
||||
"contents": [
|
||||
{ "x": 100, "y": 50, "type": "file", "path": "${INSTALL_DIR}/sfizz.vst3" },
|
||||
{ "x": 250, "y": 50, "type": "file", "path": "${INSTALL_DIR}/sfizz.component" },
|
||||
{ "x": 400, "y": 50, "type": "file", "path": "${INSTALL_DIR}/sfizz.lv2" },
|
||||
{ "x": 550, "y": 50, "type": "file", "path": "${INSTALL_DIR}/Puredata" },
|
||||
{ "x": 100, "y": 400, "type": "link", "path": "/Library/Audio/Plug-Ins/VST3" },
|
||||
{ "x": 250, "y": 400, "type": "link", "path": "/Library/Audio/Plug-Ins/Components" },
|
||||
{ "x": 400, "y": 400, "type": "link", "path": "/Library/Audio/Plug-Ins/LV2" }
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ cmake -DCMAKE_BUILD_TYPE=Release \
|
|||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
-DSFIZZ_VST=ON \
|
||||
-DSFIZZ_AU=ON \
|
||||
-DSFIZZ_PUREDATA=ON \
|
||||
-DSFIZZ_RENDER=OFF \
|
||||
-DSFIZZ_SHARED=OFF \
|
||||
-DSFIZZ_TESTS=ON \
|
||||
|
|
@ -14,4 +15,5 @@ cmake -DCMAKE_BUILD_TYPE=Release \
|
|||
-DLV2PLUGIN_INSTALL_DIR=/ \
|
||||
-DVSTPLUGIN_INSTALL_DIR=/ \
|
||||
-DAUPLUGIN_INSTALL_DIR=/ \
|
||||
-DPDPLUGIN_INSTALL_DIR=/Puredata \
|
||||
..
|
||||
|
|
|
|||
|
|
@ -259,9 +259,9 @@ TAB_SIZE = 4
|
|||
# commands \{ and \} for these it is advised to use the version @{ and @} or use
|
||||
# a double escape (\\{ and \\})
|
||||
|
||||
ALIASES = "true=<b style=\"color:blue\">true</b>" \
|
||||
"false=<b style=\"color:blue\">false</b>" \
|
||||
"null=<b>NULL</b>"
|
||||
ALIASES = "true=\<b style='color:blue'\>true\</b\>" \
|
||||
"false=\<b style='color:blue'\>false\</b\>" \
|
||||
"null=\<b\>nullptr\</b\>"
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@
|
|||
|
||||
- Tags in ALIASES list needs to be escaped (parsed by tag_list_as_string())
|
||||
to avoid Doxygen convert them in its own tag structure, by loosing some
|
||||
details (e.g.: <b style=\"color:blue\">true</b> becomes <bold>true</bold>).
|
||||
details (e.g.: <b style=\"color:blue\">true</b> becomes <bold>true</bold>,
|
||||
use \<b\> as escaping and replace double quotes with apostrophe instead).
|
||||
|
||||
- Some link references are lost like in
|
||||
`sfizz_export_midnam` -> `sfizz_free_memory`
|
||||
|
||||
- Merge the work done previously to be able to fully automate the process
|
||||
to be used in various CIs.
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ LicenseFile="sfizz.lv2\LICENSE.md"
|
|||
OutputBaseFileName={#MyAppName}-{#MyAppVersion}-msvc-{#Arch}-setup
|
||||
OutputDir=.
|
||||
UninstallFilesDir={app}
|
||||
WizardImageFile="C:\Program Files (x86)\Inno Setup 6\WizModernImage-IS.bmp"
|
||||
WizardSmallImageFile="C:\Program Files (x86)\Inno Setup 6\WizModernSmallImage-IS.bmp"
|
||||
WizardImageFile=compiler:WizClassicImage-IS.bmp
|
||||
WizardSmallImageFile=compiler:WizClassicSmallImage-IS.bmp
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
|
@ -46,6 +46,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
|
|||
Name: "main"; Description: "Shared files"; Types: full custom; Flags: fixed
|
||||
Name: "lv2"; Description: "LV2 plugin"; Types: full custom;
|
||||
Name: "vst3"; Description: "VST3 plugin"; Types: full custom;
|
||||
Name: "puredata"; Description: "Puredata external"; Types: full custom;
|
||||
|
||||
[Files]
|
||||
Source: "sfizz.lv2\Contents\Binary\sfizz.dll"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2\Contents\Binary"; Flags: ignoreversion
|
||||
|
|
@ -54,13 +55,13 @@ Source: "sfizz.lv2\Contents\Resources\*"; Components: lv2; DestDir: "{commoncf}\
|
|||
Source: "sfizz.lv2\manifest.ttl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2"
|
||||
Source: "sfizz.lv2\sfizz.ttl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2"
|
||||
Source: "sfizz.lv2\sfizz_ui.ttl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2"
|
||||
Source: "sfizz.lv2\lgpl-3.0.txt"; Components: main; DestDir: "{app}"
|
||||
Source: "sfizz.lv2\LICENSE.md"; Components: main; DestDir: "{app}"
|
||||
Source: "sfizz.vst3\desktop.ini"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3"
|
||||
Source: "sfizz.vst3\Contents\@VST3_PACKAGE_ARCHITECTURE@-win\sfizz.vst3"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3\Contents\@VST3_PACKAGE_ARCHITECTURE@-win"; Flags: ignoreversion
|
||||
Source: "sfizz.vst3\Contents\Resources\*"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3\Contents\Resources"; Flags: recursesubdirs
|
||||
Source: "sfizz.vst3\Plugin.ico"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3"
|
||||
Source: "sfizz.vst3\gpl-3.0.txt"; Components: main; DestDir: "{app}"
|
||||
Source: "pd\*"; Components: puredata; DestDir: "{commoncf}\Pd"; Flags: recursesubdirs
|
||||
; Note(sfizz): OS older than Windows 10 require UI fonts to be installed system-wide
|
||||
Source: "sfizz.vst3\Contents\Resources\Fonts\sfizz-fluentui-system-r20.ttf"; DestDir: "{fonts}"; FontInstall: "Sfizz Fluent System R20"; Flags: uninsneveruninstall; OnlyBelowVersion: 6.4
|
||||
Source: "sfizz.vst3\Contents\Resources\Fonts\sfizz-fluentui-system-f20.ttf"; DestDir: "{fonts}"; FontInstall: "Sfizz Fluent System F20"; Flags: uninsneveruninstall; OnlyBelowVersion: 6.4
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ clang-tidy \
|
|||
src/sfizz/Panning.cpp \
|
||||
src/sfizz/sfizz.cpp \
|
||||
src/sfizz/Region.cpp \
|
||||
src/sfizz/RegionStateful.cpp \
|
||||
src/sfizz/SIMDHelpers.cpp \
|
||||
src/sfizz/simd/HelpersSSE.cpp \
|
||||
src/sfizz/simd/HelpersAVX.cpp \
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: @PROJECT_DESCRIPTION@
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
include(GNUInstallDirs)
|
||||
|
||||
set(FAUST_FILES
|
||||
sfizz/dsp/filters/filters_modulable.dsp
|
||||
sfizz/dsp/filters/rbj_filters.dsp
|
||||
|
|
@ -96,6 +94,7 @@ set(SFIZZ_HEADERS
|
|||
sfizz/railsback/4-1.h
|
||||
sfizz/railsback/4-2.h
|
||||
sfizz/Region.h
|
||||
sfizz/RegionStateful.h
|
||||
sfizz/RegionSet.h
|
||||
sfizz/Resources.h
|
||||
sfizz/RTSemaphore.h
|
||||
|
|
@ -131,6 +130,7 @@ set(SFIZZ_SOURCES
|
|||
sfizz/AudioReader.cpp
|
||||
sfizz/FilterPool.cpp
|
||||
sfizz/EQPool.cpp
|
||||
sfizz/RegionStateful.cpp
|
||||
sfizz/Region.cpp
|
||||
sfizz/Voice.cpp
|
||||
sfizz/ScopedFTZ.cpp
|
||||
|
|
@ -161,6 +161,7 @@ set(SFIZZ_SOURCES
|
|||
sfizz/WindowedSinc.cpp
|
||||
sfizz/Interpolators.cpp
|
||||
sfizz/Layer.cpp
|
||||
sfizz/Resources.cpp
|
||||
sfizz/modulations/ModId.cpp
|
||||
sfizz/modulations/ModKey.cpp
|
||||
sfizz/modulations/ModKeyHash.cpp
|
||||
|
|
@ -190,7 +191,8 @@ set(SFIZZ_SOURCES
|
|||
sfizz/effects/impl/ResonantStringAVX.cpp
|
||||
sfizz/effects/impl/ResonantArray.cpp
|
||||
sfizz/effects/impl/ResonantArraySSE.cpp
|
||||
sfizz/effects/impl/ResonantArrayAVX.cpp)
|
||||
sfizz/effects/impl/ResonantArrayAVX.cpp
|
||||
sfizz/utility/c++17/AlignedMemorySupport.cpp)
|
||||
|
||||
include(SfizzSIMDSourceFiles)
|
||||
sfizz_add_simd_sources(SFIZZ_SOURCES ".")
|
||||
|
|
@ -201,6 +203,7 @@ set(SFIZZ_PARSER_HEADERS
|
|||
sfizz/Range.h
|
||||
sfizz/Opcode.h
|
||||
sfizz/parser/Parser.h
|
||||
sfizz/parser/ParserListener.h
|
||||
sfizz/parser/ParserPrivate.h
|
||||
sfizz/parser/ParserPrivate.hpp
|
||||
sfizz/SfzHelpers.h)
|
||||
|
|
@ -249,11 +252,13 @@ endif()
|
|||
|
||||
# Import library
|
||||
set(SFIZZ_IMPORT_HEADERS
|
||||
sfizz/import/sfizz_import.h
|
||||
sfizz/import/ForeignInstrument.h
|
||||
sfizz/import/foreign_instruments/AudioFile.h
|
||||
sfizz/import/foreign_instruments/DecentSampler.h)
|
||||
|
||||
set(SFIZZ_IMPORT_SOURCES
|
||||
sfizz/import/sfizz_import.cpp
|
||||
sfizz/import/ForeignInstrument.cpp
|
||||
sfizz/import/foreign_instruments/AudioFile.cpp
|
||||
sfizz/import/foreign_instruments/DecentSampler.cpp)
|
||||
|
|
@ -294,12 +299,12 @@ if(SFIZZ_USE_SNDFILE)
|
|||
target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_USE_SNDFILE=1")
|
||||
target_link_libraries(sfizz_internal PUBLIC st_audiofile)
|
||||
endif()
|
||||
if(WIN32)
|
||||
target_compile_definitions(sfizz_internal PRIVATE _USE_MATH_DEFINES)
|
||||
endif()
|
||||
if(SFIZZ_RELEASE_ASSERTS)
|
||||
target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1")
|
||||
endif()
|
||||
if(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT)
|
||||
target_compile_definitions(sfizz_internal PRIVATE "SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT=1")
|
||||
endif()
|
||||
sfizz_enable_fast_math(sfizz_internal)
|
||||
|
||||
# Check that sfizz and cmake-side definitions are matching
|
||||
|
|
@ -315,7 +320,12 @@ add_library(sfizz_static STATIC sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp sfizz/sf
|
|||
add_library(sfizz::static ALIAS sfizz_static)
|
||||
target_include_directories(sfizz_static PUBLIC .)
|
||||
target_link_libraries(sfizz_static PRIVATE sfizz::internal)
|
||||
set_target_properties(sfizz_static PROPERTIES OUTPUT_NAME "sfizz" PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h")
|
||||
set_target_properties(sfizz_static PROPERTIES PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h")
|
||||
if(MSVC)
|
||||
set_target_properties(sfizz_static PROPERTIES OUTPUT_NAME "sfizz_static")
|
||||
else()
|
||||
set_target_properties(sfizz_static PROPERTIES OUTPUT_NAME "sfizz")
|
||||
endif()
|
||||
|
||||
# Shared library and installation target
|
||||
if(SFIZZ_SHARED)
|
||||
|
|
@ -324,7 +334,7 @@ if(SFIZZ_SHARED)
|
|||
target_include_directories(sfizz_shared PUBLIC .)
|
||||
target_link_libraries(sfizz_shared PRIVATE sfizz::internal)
|
||||
target_compile_definitions(sfizz_shared PRIVATE SFIZZ_EXPORT_SYMBOLS)
|
||||
set_target_properties(sfizz_shared PROPERTIES SOVERSION "${PROJECT_VERSION_MAJOR}" OUTPUT_NAME "sfizz" PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h")
|
||||
set_target_properties(sfizz_shared PROPERTIES VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}" OUTPUT_NAME "sfizz" PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h")
|
||||
sfizz_enable_lto_if_needed(sfizz_shared)
|
||||
|
||||
if(NOT MSVC)
|
||||
|
|
@ -347,6 +357,19 @@ endif()
|
|||
# Generic library alias
|
||||
add_library(sfizz::sfizz ALIAS sfizz_static)
|
||||
|
||||
# Git build identifier
|
||||
add_custom_target(sfizz-generate-git-build-id
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
"-DSOURCE_DIR=${PROJECT_SOURCE_DIR}"
|
||||
"-DOUTPUT_FILE=${PROJECT_BINARY_DIR}/git-build-id/GitBuildId.c"
|
||||
"-P" "${PROJECT_SOURCE_DIR}/cmake/GitBuildID.cmake"
|
||||
BYPRODUCTS "${PROJECT_BINARY_DIR}/git-build-id/GitBuildId.c")
|
||||
add_library(sfizz-git-build-id STATIC EXCLUDE_FROM_ALL
|
||||
"sfizz/git-build-id/GitBuildId.h"
|
||||
"${PROJECT_BINARY_DIR}/git-build-id/GitBuildId.c")
|
||||
target_include_directories(sfizz-git-build-id PUBLIC "sfizz/git-build-id")
|
||||
add_dependencies(sfizz-git-build-id sfizz-generate-git-build-id)
|
||||
|
||||
# Preserve generated files (Faust)
|
||||
set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM TRUE)
|
||||
|
||||
|
|
|
|||
30
src/sfizz.h
30
src/sfizz.h
|
|
@ -510,7 +510,7 @@ SFIZZ_EXPORTED_API void sfizz_send_hd_pitch_wheel(sfizz_synth_t* synth, int dela
|
|||
*
|
||||
* @param synth The synth.
|
||||
* @param delay The delay at which the event occurs; this should be lower
|
||||
* than the size of the block in the next call to renderBlock().
|
||||
* than the size of the block in the next call to sfizz_render_block().
|
||||
* @param aftertouch The aftertouch value, in domain 0 to 127.
|
||||
*
|
||||
* @par Thread-safety constraints
|
||||
|
|
@ -528,7 +528,7 @@ SFIZZ_EXPORTED_API SFIZZ_DEPRECATED_API void sfizz_send_aftertouch(sfizz_synth_t
|
|||
*
|
||||
* @param synth The synth.
|
||||
* @param delay The delay at which the event occurs; this should be lower
|
||||
* than the size of the block in the next call to renderBlock().
|
||||
* than the size of the block in the next call to sfizz_render_block().
|
||||
* @param aftertouch The aftertouch value, in domain 0 to 127.
|
||||
*
|
||||
* @par Thread-safety constraints
|
||||
|
|
@ -546,7 +546,7 @@ SFIZZ_EXPORTED_API void sfizz_send_channel_aftertouch(sfizz_synth_t* synth, int
|
|||
*
|
||||
* @param synth The synth.
|
||||
* @param delay The delay at which the event occurs; this should be lower
|
||||
* than the size of the block in the next call to renderBlock().
|
||||
* than the size of the block in the next call to sfizz_render_block().
|
||||
* @param aftertouch The normalized aftertouch value, in domain 0 to 1.
|
||||
*
|
||||
* @par Thread-safety constraints
|
||||
|
|
@ -565,7 +565,7 @@ SFIZZ_EXPORTED_API void sfizz_send_hd_channel_aftertouch(sfizz_synth_t* synth, i
|
|||
*
|
||||
* @param synth The synth.
|
||||
* @param delay The delay at which the event occurs; this should be lower
|
||||
* than the size of the block in the next call to renderBlock().
|
||||
* than the size of the block in the next call to sfizz_render_block().
|
||||
* @param note_number The note number, in domain 0 to 127.
|
||||
* @param aftertouch The aftertouch value, in domain 0 to 127.
|
||||
*
|
||||
|
|
@ -585,7 +585,7 @@ SFIZZ_EXPORTED_API void sfizz_send_poly_aftertouch(sfizz_synth_t* synth, int del
|
|||
*
|
||||
* @param synth The synth.
|
||||
* @param delay The delay at which the event occurs; this should be lower
|
||||
* than the size of the block in the next call to renderBlock().
|
||||
* than the size of the block in the next call to sfizz_render_block().
|
||||
* @param note_number The note number, in domain 0 to 127.
|
||||
* @param aftertouch The normalized aftertouch value, in domain 0 to 1.
|
||||
*
|
||||
|
|
@ -598,8 +598,8 @@ SFIZZ_EXPORTED_API void sfizz_send_hd_poly_aftertouch(sfizz_synth_t* synth, int
|
|||
* @brief Send a tempo event.
|
||||
*
|
||||
* This command should be delay-ordered with all other time/signature commands, namely
|
||||
* tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior
|
||||
* of the synth is undefined.
|
||||
* sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(),
|
||||
* and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*
|
||||
|
|
@ -616,8 +616,8 @@ SFIZZ_EXPORTED_API SFIZZ_DEPRECATED_API void sfizz_send_tempo(sfizz_synth_t* syn
|
|||
* @brief Send a tempo event.
|
||||
*
|
||||
* This command should be delay-ordered with all other time/signature commands, namely
|
||||
* tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior
|
||||
* of the synth is undefined.
|
||||
* sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(),
|
||||
* and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
|
|
@ -634,8 +634,8 @@ SFIZZ_EXPORTED_API void sfizz_send_bpm_tempo(sfizz_synth_t* synth, int delay, fl
|
|||
* @brief Send the time signature.
|
||||
*
|
||||
* This command should be delay-ordered with all other time/signature commands, namely
|
||||
* tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior
|
||||
* of the synth is undefined.
|
||||
* sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(),
|
||||
* and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined.
|
||||
*
|
||||
* @since 0.5.0
|
||||
*
|
||||
|
|
@ -653,8 +653,8 @@ SFIZZ_EXPORTED_API void sfizz_send_time_signature(sfizz_synth_t* synth, int dela
|
|||
* @brief Send the time position.
|
||||
*
|
||||
* This command should be delay-ordered with all other time/signature commands, namely
|
||||
* tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior
|
||||
* of the synth is undefined.
|
||||
* sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(),
|
||||
* and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined.
|
||||
*
|
||||
* @since 0.5.0
|
||||
*
|
||||
|
|
@ -672,8 +672,8 @@ SFIZZ_EXPORTED_API void sfizz_send_time_position(sfizz_synth_t* synth, int delay
|
|||
* @brief Send the playback state.
|
||||
*
|
||||
* This command should be delay-ordered with all other time/signature commands, namely
|
||||
* tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior
|
||||
* of the synth is undefined.
|
||||
* sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(),
|
||||
* and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined.
|
||||
*
|
||||
* @since 0.5.0
|
||||
*
|
||||
|
|
|
|||
|
|
@ -38,30 +38,54 @@ Float ADSREnvelope::secondsToExpRate(Float timeInSeconds) const noexcept
|
|||
return std::exp(Float(-9.0) / (timeInSeconds * sampleRate));
|
||||
};
|
||||
|
||||
void ADSREnvelope::reset(const EGDescription& desc, const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept
|
||||
void ADSREnvelope::reset(const EGDescription& desc, const Region& region, int delay, float velocity, float sampleRate) noexcept
|
||||
{
|
||||
this->sampleRate = sampleRate;
|
||||
|
||||
this->delay = delay + secondsToSamples(desc.getDelay(state, velocity));
|
||||
this->attackStep = secondsToLinRate(desc.getAttack(state, velocity));
|
||||
this->decayRate = secondsToExpRate(desc.getDecay(state, velocity));
|
||||
this->releaseRate = secondsToExpRate(desc.getRelease(state, velocity));
|
||||
this->hold = secondsToSamples(desc.getHold(state, velocity));
|
||||
this->sustain = clamp(desc.getSustain(state, velocity), 0.0f, 1.0f);
|
||||
this->start = clamp(desc.getStart(state, velocity), 0.0f, 1.0f);
|
||||
|
||||
desc_ = &desc;
|
||||
triggerVelocity_ = velocity;
|
||||
currentState = State::Delay; // Has to be before the update
|
||||
updateValues(delay);
|
||||
releaseDelay = 0;
|
||||
sustainThreshold = this->sustain + config::virtuallyZero;
|
||||
shouldRelease = false;
|
||||
freeRunning = (
|
||||
(this->sustain <= Float(config::sustainFreeRunningThreshold))
|
||||
|| (region.loopMode == LoopMode::one_shot && region.isOscillator())
|
||||
);
|
||||
currentValue = this->start;
|
||||
currentState = State::Delay;
|
||||
}
|
||||
|
||||
void ADSREnvelope::updateValues(int delay) noexcept
|
||||
{
|
||||
if (currentState == State::Delay)
|
||||
this->delay = delay + secondsToSamples(desc_->getDelay(midiState_, triggerVelocity_, delay));
|
||||
|
||||
this->attackStep = secondsToLinRate(desc_->getAttack(midiState_, triggerVelocity_, delay));
|
||||
this->decayRate = secondsToExpRate(desc_->getDecay(midiState_, triggerVelocity_, delay));
|
||||
this->releaseRate = secondsToExpRate(desc_->getRelease(midiState_, triggerVelocity_, delay));
|
||||
this->hold = secondsToSamples(desc_->getHold(midiState_, triggerVelocity_, delay));
|
||||
this->sustain = clamp(desc_->getSustain(midiState_, triggerVelocity_, delay), 0.0f, 1.0f);
|
||||
this->start = clamp(desc_->getStart(midiState_, triggerVelocity_, delay), 0.0f, 1.0f);
|
||||
sustainThreshold = this->sustain + config::virtuallyZero;
|
||||
}
|
||||
|
||||
void ADSREnvelope::getBlock(absl::Span<Float> output) noexcept
|
||||
{
|
||||
if (desc_ && desc_->dynamic) {
|
||||
int processed = 0;
|
||||
int remaining = static_cast<int>(output.size());
|
||||
while(remaining > 0) {
|
||||
updateValues(processed);
|
||||
int chunkSize = min(config::processChunkSize, remaining);
|
||||
getBlockInternal(output.subspan(processed, chunkSize));
|
||||
processed += chunkSize;
|
||||
remaining -= chunkSize;
|
||||
}
|
||||
} else {
|
||||
getBlockInternal(output);
|
||||
}
|
||||
}
|
||||
|
||||
void ADSREnvelope::getBlockInternal(absl::Span<Float> output) noexcept
|
||||
{
|
||||
State currentState = this->currentState;
|
||||
Float currentValue = this->currentValue;
|
||||
|
|
@ -122,7 +146,8 @@ void ADSREnvelope::getBlock(absl::Span<Float> output) noexcept
|
|||
break;
|
||||
}
|
||||
while (count < size) {
|
||||
currentValue = std::max(sustain, currentValue + transitionDelta);
|
||||
if (currentValue > sustain)
|
||||
currentValue += transitionDelta;
|
||||
output[count++] = currentValue;
|
||||
}
|
||||
break;
|
||||
|
|
@ -173,6 +198,14 @@ void ADSREnvelope::startRelease(int releaseDelay) noexcept
|
|||
this->releaseDelay = releaseDelay;
|
||||
}
|
||||
|
||||
void ADSREnvelope::cancelRelease(int delay) noexcept
|
||||
{
|
||||
(void)delay;
|
||||
currentState = State::Sustain;
|
||||
shouldRelease = false;
|
||||
this->releaseDelay = -1;
|
||||
}
|
||||
|
||||
void ADSREnvelope::setReleaseTime(Float timeInSeconds) noexcept
|
||||
{
|
||||
releaseRate = secondsToExpRate(timeInSeconds);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ class ADSREnvelope {
|
|||
public:
|
||||
using Float = float;
|
||||
|
||||
ADSREnvelope() = default;
|
||||
ADSREnvelope(const MidiState& state)
|
||||
: midiState_(state) {}
|
||||
/**
|
||||
* @brief Resets the ADSR envelope given a Region, the current midi state, and a delay and
|
||||
* trigger velocity
|
||||
|
|
@ -29,10 +30,9 @@ public:
|
|||
* @param delay
|
||||
* @param velocity
|
||||
*/
|
||||
void reset(const EGDescription& desc, const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept;
|
||||
void reset(const EGDescription& desc, const Region& region, int delay, float velocity, float sampleRate) noexcept;
|
||||
/**
|
||||
* @brief Get a block of values for the envelope. This method tries hard to be efficient
|
||||
* and hopefully it is.
|
||||
* @brief Get the next block of values for the envelope.
|
||||
*
|
||||
* @param output
|
||||
*/
|
||||
|
|
@ -49,6 +49,12 @@ public:
|
|||
* @param releaseDelay the delay before releasing in samples
|
||||
*/
|
||||
void startRelease(int releaseDelay) noexcept;
|
||||
/**
|
||||
* @brief Cancel a release and get back into sustain.
|
||||
*
|
||||
* @param delay
|
||||
*/
|
||||
void cancelRelease(int delay) noexcept;
|
||||
/**
|
||||
* @brief Is the envelope smoothing?
|
||||
*
|
||||
|
|
@ -75,6 +81,8 @@ private:
|
|||
int secondsToSamples(Float timeInSeconds) const noexcept;
|
||||
Float secondsToLinRate(Float timeInSeconds) const noexcept;
|
||||
Float secondsToExpRate(Float timeInSeconds) const noexcept;
|
||||
void updateValues(int delay = 0) noexcept;
|
||||
void getBlockInternal(absl::Span<Float> output) noexcept;
|
||||
|
||||
enum class State {
|
||||
Delay,
|
||||
|
|
@ -88,6 +96,9 @@ private:
|
|||
};
|
||||
State currentState { State::Done };
|
||||
Float currentValue { 0.0 };
|
||||
const EGDescription* desc_ { nullptr };
|
||||
const MidiState& midiState_;
|
||||
float triggerVelocity_ { 0.0f };
|
||||
int delay { 0 };
|
||||
Float attackStep { 0 };
|
||||
Float decayRate { 0 };
|
||||
|
|
|
|||
|
|
@ -26,10 +26,12 @@
|
|||
#pragma once
|
||||
#include "Config.h"
|
||||
#include "utility/LeakDetector.h"
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <atomic>
|
||||
|
||||
|
|
@ -230,7 +232,7 @@ public:
|
|||
*
|
||||
* @param other
|
||||
*/
|
||||
Buffer(const Buffer<Type>& other)
|
||||
Buffer(const Buffer<Type, Alignment>& other)
|
||||
{
|
||||
resize(other.size());
|
||||
std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type));
|
||||
|
|
@ -241,7 +243,7 @@ public:
|
|||
*
|
||||
* @param other
|
||||
*/
|
||||
Buffer(Buffer<Type>&& other) noexcept
|
||||
Buffer(Buffer<Type, Alignment>&& other) noexcept
|
||||
: largerSize(other.largerSize),
|
||||
alignedSize(other.alignedSize),
|
||||
normalData(other.normalData),
|
||||
|
|
@ -252,7 +254,7 @@ public:
|
|||
other._clear();
|
||||
}
|
||||
|
||||
Buffer<Type>& operator=(const Buffer<Type>& other)
|
||||
Buffer<Type, Alignment>& operator=(const Buffer<Type, Alignment>& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
resize(other.size());
|
||||
|
|
@ -261,7 +263,7 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
Buffer<Type>& operator=(Buffer<Type>&& other) noexcept
|
||||
Buffer<Type, Alignment>& operator=(Buffer<Type, Alignment>&& other) noexcept
|
||||
{
|
||||
if (this != &other) {
|
||||
if (largerSize > 0)
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@ namespace config {
|
|||
constexpr float powerFollowerReleaseTime { 200e-3f };
|
||||
constexpr uint16_t numCCs { 512 };
|
||||
constexpr int maxCurves { 256 };
|
||||
constexpr int chunkSize { 1024 };
|
||||
constexpr int fileChunkSize { 1024 };
|
||||
constexpr int processChunkSize { 16 };
|
||||
constexpr unsigned int defaultAlignment { 16 };
|
||||
constexpr int filtersInPool { maxVoices * 2 };
|
||||
constexpr int excessFileFrames { 64 };
|
||||
|
|
|
|||
|
|
@ -41,23 +41,27 @@ FloatSpec oscillatorDetuneMod { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }
|
|||
FloatSpec oscillatorModDepth { 0.0f, {0.0f, 10000.0f}, kNormalizePercent|kPermissiveBounds };
|
||||
FloatSpec oscillatorModDepthMod { 0.0f, {0.0f, 10000.0f}, kNormalizePercent|kPermissiveBounds };
|
||||
Int32Spec oscillatorQuality { 1, {0, 3}, 0 };
|
||||
UInt32Spec group { 0, {0, uint32_t_max}, 0 };
|
||||
Int64Spec group { 0, {-int32_t_max, uint32_t_max}, 0 };
|
||||
FloatSpec offTime { 6e-3f, {0.0f, 100.0f}, kPermissiveBounds };
|
||||
UInt32Spec polyphony { config::maxVoices, {0, config::maxVoices}, kEnforceBounds };
|
||||
UInt32Spec notePolyphony { config::maxVoices, {0, config::maxVoices}, kEnforceBounds };
|
||||
UInt32Spec notePolyphony { config::maxVoices, {1, config::maxVoices}, kEnforceBounds };
|
||||
UInt8Spec key { 60, {0, 127}, kCanBeNote };
|
||||
UInt8Spec loKey { 0, {0, 127}, kCanBeNote };
|
||||
UInt8Spec hiKey { 127, {0, 127}, kCanBeNote };
|
||||
FloatSpec loCC { 0, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec hiCC { 127, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec hiCC { 127, {0.0f, 127.0f}, kNormalizeMidi|kFillGap|kPermissiveBounds };
|
||||
FloatSpec xfoutLo { 127.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec xfoutHi { 127.0f, {0.0f, 127.0f}, kNormalizeMidi|kFillGap|kPermissiveBounds };
|
||||
FloatSpec xfinLo { 0.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec xfinHi { 0.0f, {0.0f, 127.0f}, kNormalizeMidi|kFillGap|kPermissiveBounds };
|
||||
FloatSpec loVel { 0, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec hiVel { 127, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec hiVel { 127, {0.0f, 127.0f}, kNormalizeMidi|kFillGap|kPermissiveBounds };
|
||||
FloatSpec loChannelAftertouch { 0, {0, 127}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec hiChannelAftertouch { 127, {0, 127}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec hiChannelAftertouch { 127, {0, 127}, kNormalizeMidi|kFillGap|kPermissiveBounds };
|
||||
FloatSpec loPolyAftertouch { 0, {0, 127}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec hiPolyAftertouch { 127, {0, 127}, kNormalizeMidi|kPermissiveBounds };
|
||||
FloatSpec hiPolyAftertouch { 127, {0, 127}, kNormalizeMidi|kFillGap|kPermissiveBounds };
|
||||
FloatSpec loBend { -8191, {-8192.0f, 8191.0f}, kNormalizeBend|kPermissiveBounds };
|
||||
FloatSpec hiBend { 8191, {-8192.0f, 8191.0f}, kNormalizeBend|kPermissiveBounds };
|
||||
FloatSpec hiBend { 8191, {-8192.0f, 8191.0f}, kNormalizeBend|kFillGap|kPermissiveBounds };
|
||||
FloatSpec loNormalized { 0.0f, {0.0f, 1.0f}, kPermissiveBounds };
|
||||
FloatSpec hiNormalized { 1.0f, {0.0f, 1.0f}, kPermissiveBounds };
|
||||
FloatSpec loBipolar { -1.0f, {-1.0f, 1.0f}, kPermissiveBounds };
|
||||
|
|
@ -86,6 +90,7 @@ FloatSpec width { 100.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds
|
|||
FloatSpec widthMod { 0.0f, {-200.0f, 200.0f}, kNormalizePercent|kPermissiveBounds };
|
||||
FloatSpec ampKeytrack { 0.0f, {-96.0f, 12.0f}, kPermissiveBounds };
|
||||
FloatSpec ampVeltrack { 100.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds };
|
||||
FloatSpec ampVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds };
|
||||
FloatSpec ampVelcurve { 0.0f, {0.0f, 1.0f}, kPermissiveBounds };
|
||||
FloatSpec ampRandom { 0.0f, {-24.0f, 24.0f}, kPermissiveBounds };
|
||||
BoolSpec rtDead { false, {false, true}, kEnforceBounds };
|
||||
|
|
@ -99,6 +104,7 @@ FloatSpec filterGainMod { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds };
|
|||
FloatSpec filterRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds };
|
||||
FloatSpec filterKeytrack { 0, {0, 1200}, kPermissiveBounds };
|
||||
FloatSpec filterVeltrack { 0, {-12000, 12000}, kPermissiveBounds };
|
||||
FloatSpec filterVeltrackMod { 0.0f, {-12000, 12000}, kPermissiveBounds };
|
||||
FloatSpec eqBandwidth { 1.0f, {0.001f, 4.0f}, kPermissiveBounds };
|
||||
FloatSpec eqBandwidthMod { 0.0f, {-4.0f, 4.0f}, kPermissiveBounds };
|
||||
FloatSpec eqFrequency { 0.0f, {0.0f, 20000.0f}, kPermissiveBounds };
|
||||
|
|
@ -110,6 +116,7 @@ FloatSpec eqVel2Gain { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds };
|
|||
FloatSpec pitchKeytrack { 100, {-1200, 1200}, kPermissiveBounds };
|
||||
FloatSpec pitchRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds };
|
||||
FloatSpec pitchVeltrack { 0, {-12000, 12000}, kPermissiveBounds };
|
||||
FloatSpec pitchVeltrackMod { 0.0f, {-12000, 12000}, kPermissiveBounds };
|
||||
FloatSpec transpose { 0, {-127, 127}, kPermissiveBounds };
|
||||
FloatSpec pitch { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds };
|
||||
FloatSpec pitchMod { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds };
|
||||
|
|
@ -144,11 +151,14 @@ FloatSpec egPercent { 0.0f, {0.0f, 100.0f}, kNormalizePercent|kPermissiveBounds
|
|||
FloatSpec egPercentMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds };
|
||||
FloatSpec egDepth { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds };
|
||||
FloatSpec egVel2Depth { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds };
|
||||
BoolSpec egDynamic { 0, {0, 1}, kEnforceBounds };
|
||||
BoolSpec flexEGAmpeg { false, {0, 1}, kEnforceBounds };
|
||||
BoolSpec flexEGDynamic { 0, {0, 1}, kEnforceBounds };
|
||||
Int32Spec flexEGSustain { 0, {0, 100}, kEnforceLowerBound|kPermissiveUpperBound };
|
||||
FloatSpec flexEGPointTime { 0.0f, {0.0f, 100.0f}, kPermissiveBounds };
|
||||
FloatSpec flexEGPointTimeMod { 0.0f, {-100.0f, 100.0f}, kPermissiveBounds };
|
||||
FloatSpec flexEGPointLevel { 0.0f, {-1.0f, 1.0f}, kPermissiveBounds };
|
||||
FloatSpec flexEGPointLevelMod { 0.0f, {-1.0f, 1.0f}, kPermissiveBounds };
|
||||
FloatSpec flexEGPointShape { 0.0f, {-100.0f, 100.0f}, kPermissiveBounds };
|
||||
Int32Spec sampleQuality { 2, {0, 10}, 0 };
|
||||
Int32Spec octaveOffset { 0, {-10, 10}, kPermissiveBounds };
|
||||
|
|
@ -183,6 +193,7 @@ FloatSpec lofiBitred { 0.0f, {0.0f, 100.0f}, 0 };
|
|||
FloatSpec lofiDecim { 0.0f, {0.0f, 100.0f}, 0 };
|
||||
FloatSpec rectify { 0.0f, {0.0f, 100.0f}, 0 };
|
||||
UInt32Spec stringsNumber { maxStrings, {0, maxStrings}, 0 };
|
||||
BoolSpec sustainCancelsRelease { false, {0, 1}, kEnforceBounds };
|
||||
|
||||
ESpec<Trigger> trigger { Trigger::attack, {Trigger::attack, Trigger::release_key}, 0};
|
||||
ESpec<CrossfadeCurve> crossfadeCurve { CrossfadeCurve::power, {CrossfadeCurve::gain, CrossfadeCurve::power}, 0};
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ enum OpcodeFlags : int {
|
|||
kNormalizeBend = 1 << 7,
|
||||
kWrapPhase = 1 << 8,
|
||||
kDb2Mag = 1 << 9,
|
||||
kFillGap = 1 << 10, // Fill in the gap when converting from discrete midi values to float, so that 13 is actually 13.999999...
|
||||
};
|
||||
|
||||
template<class T>
|
||||
|
|
@ -97,8 +98,12 @@ struct OpcodeSpec
|
|||
return input;
|
||||
else if (flags & kNormalizePercent)
|
||||
return static_cast<U>(input / U(100));
|
||||
else if (flags & kNormalizeMidi)
|
||||
return static_cast<U>(input / U(127));
|
||||
else if (flags & kNormalizeMidi) {
|
||||
if ((flags & kFillGap) && (input <= U(126)) && input >= 0)
|
||||
return std::nextafter(static_cast<U>((input + 1.0f) / U(127)), 0.0f);
|
||||
else
|
||||
return static_cast<U>(input / U(127));
|
||||
}
|
||||
else if (flags & kNormalizeBend)
|
||||
return static_cast<U>(input / U(8191));
|
||||
else if (flags & kDb2Mag)
|
||||
|
|
@ -148,7 +153,7 @@ namespace Default
|
|||
extern const OpcodeSpec<float> oscillatorModDepth;
|
||||
extern const OpcodeSpec<float> oscillatorModDepthMod;
|
||||
extern const OpcodeSpec<int32_t> oscillatorQuality;
|
||||
extern const OpcodeSpec<uint32_t> group;
|
||||
extern const OpcodeSpec<int64_t> group;
|
||||
extern const OpcodeSpec<float> offTime;
|
||||
extern const OpcodeSpec<uint32_t> polyphony;
|
||||
extern const OpcodeSpec<uint32_t> notePolyphony;
|
||||
|
|
@ -159,6 +164,10 @@ namespace Default
|
|||
extern const OpcodeSpec<float> hiVel;
|
||||
extern const OpcodeSpec<float> loCC;
|
||||
extern const OpcodeSpec<float> hiCC;
|
||||
extern const OpcodeSpec<float> xfoutLo;
|
||||
extern const OpcodeSpec<float> xfoutHi;
|
||||
extern const OpcodeSpec<float> xfinHi;
|
||||
extern const OpcodeSpec<float> xfinLo;
|
||||
extern const OpcodeSpec<float> loBend;
|
||||
extern const OpcodeSpec<float> hiBend;
|
||||
extern const OpcodeSpec<float> loNormalized;
|
||||
|
|
@ -193,6 +202,7 @@ namespace Default
|
|||
extern const OpcodeSpec<float> widthMod;
|
||||
extern const OpcodeSpec<float> ampKeytrack;
|
||||
extern const OpcodeSpec<float> ampVeltrack;
|
||||
extern const OpcodeSpec<float> ampVeltrackMod;
|
||||
extern const OpcodeSpec<float> ampVelcurve;
|
||||
extern const OpcodeSpec<float> ampRandom;
|
||||
extern const OpcodeSpec<bool> rtDead;
|
||||
|
|
@ -206,6 +216,7 @@ namespace Default
|
|||
extern const OpcodeSpec<float> filterRandom;
|
||||
extern const OpcodeSpec<float> filterKeytrack;
|
||||
extern const OpcodeSpec<float> filterVeltrack;
|
||||
extern const OpcodeSpec<float> filterVeltrackMod;
|
||||
extern const OpcodeSpec<float> eqBandwidth;
|
||||
extern const OpcodeSpec<float> eqBandwidthMod;
|
||||
extern const OpcodeSpec<float> eqFrequency;
|
||||
|
|
@ -217,6 +228,7 @@ namespace Default
|
|||
extern const OpcodeSpec<float> pitchKeytrack;
|
||||
extern const OpcodeSpec<float> pitchRandom;
|
||||
extern const OpcodeSpec<float> pitchVeltrack;
|
||||
extern const OpcodeSpec<float> pitchVeltrackMod;
|
||||
extern const OpcodeSpec<float> transpose;
|
||||
extern const OpcodeSpec<float> pitch;
|
||||
extern const OpcodeSpec<float> pitchMod;
|
||||
|
|
@ -251,11 +263,14 @@ namespace Default
|
|||
extern const OpcodeSpec<float> egPercentMod;
|
||||
extern const OpcodeSpec<float> egDepth;
|
||||
extern const OpcodeSpec<float> egVel2Depth;
|
||||
extern const OpcodeSpec<bool> egDynamic;
|
||||
extern const OpcodeSpec<bool> flexEGAmpeg;
|
||||
extern const OpcodeSpec<bool> flexEGDynamic;
|
||||
extern const OpcodeSpec<int32_t> flexEGSustain;
|
||||
extern const OpcodeSpec<float> flexEGPointTime;
|
||||
extern const OpcodeSpec<float> flexEGPointTimeMod;
|
||||
extern const OpcodeSpec<float> flexEGPointLevel;
|
||||
extern const OpcodeSpec<float> flexEGPointLevelMod;
|
||||
extern const OpcodeSpec<float> flexEGPointShape;
|
||||
extern const OpcodeSpec<int32_t> sampleQuality;
|
||||
extern const OpcodeSpec<int32_t> octaveOffset;
|
||||
|
|
@ -296,6 +311,7 @@ namespace Default
|
|||
extern const OpcodeSpec<SelfMask> selfMask;
|
||||
extern const OpcodeSpec<FilterType> filter;
|
||||
extern const OpcodeSpec<EqType> eq;
|
||||
extern const OpcodeSpec<bool> sustainCancelsRelease;
|
||||
|
||||
// Default/max count for objects
|
||||
constexpr int numEQs { 3 };
|
||||
|
|
|
|||
|
|
@ -42,22 +42,6 @@ namespace sfz {
|
|||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief If a cc switch exists for the value, returns the value with the CC modifier, otherwise returns the value alone.
|
||||
*
|
||||
* @param ccValues
|
||||
* @param ccSwitch
|
||||
* @param value
|
||||
* @return float
|
||||
*/
|
||||
inline float ccSwitchedValue(const MidiState& state, const absl::optional<CCData<float>>& ccSwitch, float value) noexcept
|
||||
{
|
||||
if (ccSwitch)
|
||||
return value + ccSwitch->data * state.getCCValue(ccSwitch->cc);
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
struct EGDescription {
|
||||
EGDescription() = default;
|
||||
EGDescription(const EGDescription&) = default;
|
||||
|
|
@ -89,6 +73,7 @@ struct EGDescription {
|
|||
CCMap<float> ccRelease;
|
||||
CCMap<float> ccStart;
|
||||
CCMap<float> ccSustain;
|
||||
bool dynamic { false };
|
||||
|
||||
/**
|
||||
* @brief Get the attack with possibly a CC modifier and a velocity modifier
|
||||
|
|
@ -97,12 +82,12 @@ struct EGDescription {
|
|||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getAttack(const MidiState& state, float velocity) const noexcept
|
||||
float getAttack(const MidiState& state, float velocity, int delay = 0) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
float returnedValue { attack + velocity * vel2attack };
|
||||
for (auto& mod: ccAttack) {
|
||||
returnedValue += state.getCCValue(mod.cc) * mod.data;
|
||||
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
|
||||
}
|
||||
return returnedValue;
|
||||
}
|
||||
|
|
@ -113,12 +98,12 @@ struct EGDescription {
|
|||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getDecay(const MidiState& state, float velocity) const noexcept
|
||||
float getDecay(const MidiState& state, float velocity, int delay = 0) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
float returnedValue { decay + velocity * vel2decay };
|
||||
for (auto& mod: ccDecay) {
|
||||
returnedValue += state.getCCValue(mod.cc) * mod.data;
|
||||
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
|
||||
}
|
||||
return returnedValue;
|
||||
}
|
||||
|
|
@ -129,12 +114,12 @@ struct EGDescription {
|
|||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getDelay(const MidiState& state, float velocity) const noexcept
|
||||
float getDelay(const MidiState& state, float velocity, int delay = 0) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
float returnedValue { delay + velocity * vel2delay };
|
||||
float returnedValue { this->delay + velocity * vel2delay };
|
||||
for (auto& mod: ccDelay) {
|
||||
returnedValue += state.getCCValue(mod.cc) * mod.data;
|
||||
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
|
||||
}
|
||||
return returnedValue;
|
||||
}
|
||||
|
|
@ -145,12 +130,12 @@ struct EGDescription {
|
|||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getHold(const MidiState& state, float velocity) const noexcept
|
||||
float getHold(const MidiState& state, float velocity, int delay = 0) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
float returnedValue { hold + velocity * vel2hold };
|
||||
for (auto& mod: ccHold) {
|
||||
returnedValue += state.getCCValue(mod.cc) * mod.data;
|
||||
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
|
||||
}
|
||||
return returnedValue;
|
||||
}
|
||||
|
|
@ -161,12 +146,12 @@ struct EGDescription {
|
|||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getRelease(const MidiState& state, float velocity) const noexcept
|
||||
float getRelease(const MidiState& state, float velocity, int delay = 0) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
float returnedValue { release + velocity * vel2release };
|
||||
for (auto& mod: ccRelease) {
|
||||
returnedValue += state.getCCValue(mod.cc) * mod.data;
|
||||
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
|
||||
}
|
||||
return returnedValue;
|
||||
}
|
||||
|
|
@ -177,12 +162,12 @@ struct EGDescription {
|
|||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getStart(const MidiState& state, float velocity) const noexcept
|
||||
float getStart(const MidiState& state, float velocity, int delay = 0) const noexcept
|
||||
{
|
||||
UNUSED(velocity);
|
||||
float returnedValue { start };
|
||||
for (auto& mod: ccStart) {
|
||||
returnedValue += state.getCCValue(mod.cc) * mod.data;
|
||||
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
|
||||
}
|
||||
return returnedValue;
|
||||
}
|
||||
|
|
@ -193,12 +178,12 @@ struct EGDescription {
|
|||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getSustain(const MidiState& state, float velocity) const noexcept
|
||||
float getSustain(const MidiState& state, float velocity, int delay = 0) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
float returnedValue { sustain + velocity * vel2sustain };
|
||||
for (auto& mod: ccSustain) {
|
||||
returnedValue += state.getCCValue(mod.cc) * mod.data;
|
||||
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
|
||||
}
|
||||
return returnedValue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
#include "EQPool.h"
|
||||
#include "Region.h"
|
||||
#include "Resources.h"
|
||||
#include "BufferPool.h"
|
||||
#include "SIMDHelpers.h"
|
||||
#include "utility/SwapAndPop.h"
|
||||
#include <absl/algorithm/container.h>
|
||||
|
|
@ -31,9 +34,10 @@ void sfz::EQHolder::setup(const Region& region, unsigned eqId, float velocity)
|
|||
baseBandwidth = description->bandwidth;
|
||||
baseGain = description->gain + velocity * description->vel2gain;
|
||||
|
||||
gainTarget = resources.modMatrix.findTarget(ModKey::createNXYZ(ModId::EqGain, region.id, eqId));
|
||||
bandwidthTarget = resources.modMatrix.findTarget(ModKey::createNXYZ(ModId::EqBandwidth, region.id, eqId));
|
||||
frequencyTarget = resources.modMatrix.findTarget(ModKey::createNXYZ(ModId::EqFrequency, region.id, eqId));
|
||||
const ModMatrix& mm = resources.getModMatrix();
|
||||
gainTarget = mm.findTarget(ModKey::createNXYZ(ModId::EqGain, region.id, eqId));
|
||||
bandwidthTarget = mm.findTarget(ModKey::createNXYZ(ModId::EqBandwidth, region.id, eqId));
|
||||
frequencyTarget = mm.findTarget(ModKey::createNXYZ(ModId::EqFrequency, region.id, eqId));
|
||||
|
||||
// Disables smoothing of the parameters on the first call
|
||||
prepared = false;
|
||||
|
|
@ -47,10 +51,11 @@ void sfz::EQHolder::process(const float** inputs, float** outputs, unsigned numF
|
|||
return;
|
||||
}
|
||||
|
||||
ModMatrix& mm = resources.modMatrix;
|
||||
auto frequencySpan = resources.bufferPool.getBuffer(numFrames);
|
||||
auto bandwidthSpan = resources.bufferPool.getBuffer(numFrames);
|
||||
auto gainSpan = resources.bufferPool.getBuffer(numFrames);
|
||||
ModMatrix& mm = resources.getModMatrix();
|
||||
BufferPool& bufferPool = resources.getBufferPool();
|
||||
auto frequencySpan = bufferPool.getBuffer(numFrames);
|
||||
auto bandwidthSpan = bufferPool.getBuffer(numFrames);
|
||||
auto gainSpan = bufferPool.getBuffer(numFrames);
|
||||
|
||||
if (!frequencySpan || !bandwidthSpan || !gainSpan)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
#pragma once
|
||||
#include "SfzFilter.h"
|
||||
#include "Region.h"
|
||||
#include "Resources.h"
|
||||
#include "Defaults.h"
|
||||
#include "modulations/ModMatrix.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace sfz
|
||||
{
|
||||
namespace sfz {
|
||||
struct Region;
|
||||
class Resources;
|
||||
struct EQDescription;
|
||||
|
||||
class EQHolder
|
||||
{
|
||||
|
|
@ -52,4 +54,4 @@ private:
|
|||
ModMatrix::TargetId bandwidthTarget;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sfz
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <absl/strings/numbers.h>
|
||||
#include <absl/strings/string_view.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ void streamFromFile(sfz::AudioReader& reader, sfz::FileAudioBuffer& output, std:
|
|||
{
|
||||
const auto numFrames = static_cast<size_t>(reader.frames());
|
||||
const auto numChannels = reader.channels();
|
||||
const auto chunkSize = static_cast<size_t>(sfz::config::chunkSize);
|
||||
const auto chunkSize = static_cast<size_t>(sfz::config::fileChunkSize);
|
||||
|
||||
output.reset();
|
||||
output.addChannels(reader.channels());
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue