Browse Source

merge conflict

pull/166/head
dreamer 3 years ago
parent
commit
97ccb0f7cf
100 changed files with 4993 additions and 972 deletions
  1. +322
    -58
      .github/workflows/build.yml
  2. +2
    -0
      .gitignore
  3. +55
    -4
      .gitmodules
  4. +16
    -19
      Makefile
  5. +66
    -31
      README.md
  6. +1
    -1
      carla
  7. +6
    -3
      deps/Makefile
  8. +1
    -1
      deps/PawPaw
  9. +3
    -4
      docs/DIFFERENCES.md
  10. +8
    -15
      docs/FAQ.md
  11. +54
    -3
      docs/LICENSES.md
  12. +1
    -1
      dpf
  13. +14
    -1
      include/common.hpp
  14. +10
    -14
      include/componentlibrary.hpp
  15. +164
    -0
      include/dsp/fir.hpp
  16. +1
    -1
      include/engine/Port.hpp
  17. +3
    -1
      include/mingw-compat/Shlobj.h
  18. +3
    -1
      include/mingw-compat/Shlwapi.h
  19. +25
    -0
      include/mingw-compat/future
  20. +374
    -0
      include/simd/Vector.hpp
  21. +246
    -15
      jucewrapper/CMakeLists.txt
  22. +519
    -48
      jucewrapper/CardinalWrapper.cpp
  23. +657
    -0
      patches/DRMR_-_Interverb.vcv
  24. +1
    -0
      plugins/AaronStatic
  25. +1
    -0
      plugins/Algoritmarte
  26. +1
    -1
      plugins/AmalgamatedHarmonics
  27. +1
    -0
      plugins/ArableInstruments
  28. +1
    -1
      plugins/BaconPlugs
  29. +1
    -1
      plugins/Befaco
  30. +1
    -1
      plugins/Bidoo
  31. +34
    -0
      plugins/BidooDark/plugin.cpp
  32. +1
    -1
      plugins/BogaudioModules
  33. +4
    -4
      plugins/Cardinal/src/AudioFile.cpp
  34. +16
    -7
      plugins/Cardinal/src/Carla.cpp
  35. +235
    -108
      plugins/Cardinal/src/HostAudio.cpp
  36. +23
    -8
      plugins/Cardinal/src/HostCV.cpp
  37. +93
    -67
      plugins/Cardinal/src/HostMIDI-CC.cpp
  38. +82
    -53
      plugins/Cardinal/src/HostMIDI-Gate.cpp
  39. +6
    -7
      plugins/Cardinal/src/HostMIDI-Map.cpp
  40. +146
    -59
      plugins/Cardinal/src/HostMIDI.cpp
  41. +7
    -4
      plugins/Cardinal/src/HostParameters.cpp
  42. +29
    -11
      plugins/Cardinal/src/HostTime.cpp
  43. +17
    -7
      plugins/Cardinal/src/Ildaeil.cpp
  44. +2
    -0
      plugins/Cardinal/src/Widgets.hpp
  45. +2
    -2
      plugins/Cardinal/src/plugincontext.hpp
  46. +1
    -0
      plugins/CatroModulo
  47. +1
    -1
      plugins/ChowDSP
  48. +1
    -1
      plugins/ExpertSleepers-Encoders
  49. +1
    -0
      plugins/Fundamental
  50. +1
    -1
      plugins/GlueTheGiant
  51. +1
    -0
      plugins/GoodSheperd
  52. +1
    -1
      plugins/GrandeModular
  53. +1
    -0
      plugins/HamptonHarmonics
  54. +1
    -1
      plugins/ImpromptuModular
  55. +1
    -1
      plugins/JW-Modules
  56. +1
    -0
      plugins/LilacLoop
  57. +1
    -1
      plugins/LyraeModules
  58. +1
    -0
      plugins/ML_modules
  59. +1
    -0
      plugins/MSM
  60. +323
    -49
      plugins/Makefile
  61. +1
    -1
      plugins/MindMeldModular
  62. +1
    -1
      plugins/Mog
  63. +1
    -0
      plugins/Orbits
  64. +1
    -0
      plugins/ParableInstruments/parasites
  65. +25
    -0
      plugins/ParableInstruments/plugin.json
  66. +1
    -0
      plugins/ParableInstruments/res/CKSS_rot_0.svg
  67. +1
    -0
      plugins/ParableInstruments/res/CKSS_rot_1.svg
  68. +1
    -0
      plugins/ParableInstruments/res/Neil.png
  69. +1
    -0
      plugins/ParableInstruments/res/Neil.svg
  70. +1
    -0
      plugins/ParableInstruments/src/ArableInstruments.hpp
  71. +1
    -0
      plugins/ParableInstruments/src/Clouds.cpp
  72. +1
    -0
      plugins/PathSet
  73. +1
    -0
      plugins/PinkTrombone
  74. +1
    -1
      plugins/Prism
  75. +1
    -1
      plugins/ValleyAudio
  76. +1
    -1
      plugins/ihtsyn
  77. +1
    -0
      plugins/kocmoc
  78. +1
    -0
      plugins/nonlinearcircuits
  79. +597
    -109
      plugins/plugins.cpp
  80. +1
    -1
      plugins/repelzen
  81. +1
    -0
      plugins/stocaudio
  82. +0
    -1
      plugins/substation-opensource
  83. +1
    -0
      plugins/unless_modules
  84. +1
    -0
      plugins/voxglitch
  85. +40
    -13
      src/CardinalCommon.cpp
  86. +10
    -0
      src/CardinalCommon.hpp
  87. +50
    -21
      src/CardinalModuleWidget.cpp
  88. +232
    -22
      src/CardinalPlugin.cpp
  89. +51
    -6
      src/CardinalUI.cpp
  90. +30
    -7
      src/Makefile
  91. +38
    -9
      src/Makefile.cardinal.mk
  92. +4
    -2
      src/PluginContext.hpp
  93. +1
    -1
      src/Rack
  94. +6
    -0
      src/WindowParameters.hpp
  95. +18
    -137
      src/custom/RemoteWindow.cpp
  96. +225
    -11
      src/custom/dep.cpp
  97. +3
    -3
      src/override/MenuBar.cpp
  98. +16
    -3
      src/override/Model.cpp
  99. +10
    -2
      src/override/Scene.cpp
  100. +24
    -0
      src/override/Window.cpp

+ 322
- 58
.github/workflows/build.yml View File

@@ -4,7 +4,7 @@ on:
push:

env:
CACHE_VERSION: 6
CACHE_VERSION: 19
DEBIAN_FRONTEND: noninteractive
HOMEBREW_NO_AUTO_UPDATE: 1
LIBGL_ALWAYS_SOFTWARE: 'true'
@@ -17,10 +17,31 @@ jobs:
with:
submodules: recursive
- name: Set up cache
id: cache
uses: actions/cache@v2
with:
path: |
~/PawPawBuilds
*/*.a
bin/*.*/*.so
bin/*.vst3/Contents/*/*.so
bin/Cardinal
build/Cardinal
build/CardinalFX
build/CardinalSynth
build/plugins
build/rack
carla/build
dpf/build
src/Rack/dep/bin
src/Rack/dep/include
src/Rack/dep/lib
src/Rack/dep/share
src/Rack/dep/jansson-2.12
src/Rack/dep/libarchive-3.4.3
src/Rack/dep/libsamplerate-0.1.9
src/Rack/dep/speexdsp-SpeexDSP-1.2rc3
src/Rack/dep/zstd-1.4.5
key: linux-arm64-v${{ env.CACHE_VERSION }}
- name: Fix GitHub's mess
run: |
@@ -40,12 +61,12 @@ jobs:
env:
PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
run: |
./deps/PawPaw/bootstrap-cardinal.sh linux-aarch64
./deps/PawPaw/bootstrap-cardinal.sh linux-aarch64 && ./deps/PawPaw/.cleanup.sh linux-aarch64
- name: Build linux arm64 cross-compiled
run: |
pushd deps/PawPaw; source local.env linux-aarch64; popd
make features
make NOOPT=true WITH_LTO=true -j $(nproc)
make CIBUILD=true NOOPT=true WITH_LTO=true -j $(nproc)
make unzipfx
- name: Set sha8 (non-release)
if: startsWith(github.ref, 'refs/tags/') != true
@@ -83,10 +104,31 @@ jobs:
with:
submodules: recursive
- name: Set up cache
id: cache
uses: actions/cache@v2
with:
path: |
~/PawPawBuilds
*/*.a
bin/*.*/*.so
bin/*.vst3/Contents/*/*.so
bin/Cardinal
build/Cardinal
build/CardinalFX
build/CardinalSynth
build/plugins
build/rack
carla/build
dpf/build
src/Rack/dep/bin
src/Rack/dep/include
src/Rack/dep/lib
src/Rack/dep/share
src/Rack/dep/jansson-2.12
src/Rack/dep/libarchive-3.4.3
src/Rack/dep/libsamplerate-0.1.9
src/Rack/dep/speexdsp-SpeexDSP-1.2rc3
src/Rack/dep/zstd-1.4.5
key: linux-armhf-v${{ env.CACHE_VERSION }}
- name: Fix GitHub's mess
run: |
@@ -106,12 +148,12 @@ jobs:
env:
PKG_CONFIG_PATH: /usr/lib/arm-linux-gnueabihf/pkgconfig
run: |
./deps/PawPaw/bootstrap-cardinal.sh linux-armhf
./deps/PawPaw/bootstrap-cardinal.sh linux-armhf && ./deps/PawPaw/.cleanup.sh linux-armhf
- name: Build linux armhf cross-compiled
run: |
pushd deps/PawPaw; source local.env linux-armhf; popd
make features
make NOOPT=true WITH_LTO=true -j $(nproc)
make CIBUILD=true NOOPT=true WITH_LTO=true -j $(nproc)
make unzipfx
- name: Set sha8 (non-release)
if: startsWith(github.ref, 'refs/tags/') != true
@@ -149,10 +191,31 @@ jobs:
with:
submodules: recursive
- name: Set up cache
id: cache
uses: actions/cache@v2
with:
path: |
~/PawPawBuilds
*/*.a
bin/*.*/*.so
bin/*.vst3/Contents/*/*.so
bin/Cardinal
build/Cardinal
build/CardinalFX
build/CardinalSynth
build/plugins
build/rack
carla/build
dpf/build
src/Rack/dep/bin
src/Rack/dep/include
src/Rack/dep/lib
src/Rack/dep/share
src/Rack/dep/jansson-2.12
src/Rack/dep/libarchive-3.4.3
src/Rack/dep/libsamplerate-0.1.9
src/Rack/dep/speexdsp-SpeexDSP-1.2rc3
src/Rack/dep/zstd-1.4.5
key: linux-i686-v${{ env.CACHE_VERSION }}
- name: Fix GitHub's mess
run: |
@@ -168,12 +231,12 @@ jobs:
env:
PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig
run: |
./deps/PawPaw/bootstrap-cardinal.sh linux-i686
./deps/PawPaw/bootstrap-cardinal.sh linux-i686 && ./deps/PawPaw/.cleanup.sh linux-i686
- name: Build linux i686
run: |
pushd deps/PawPaw; source local.env linux-i686; popd
make features
make NOOPT=true WITH_LTO=true -j $(nproc)
make CIBUILD=true NOOPT=true WITH_LTO=true -j $(nproc)
make unzipfx
- name: Set sha8 (non-release)
if: startsWith(github.ref, 'refs/tags/') != true
@@ -211,10 +274,31 @@ jobs:
with:
submodules: recursive
- name: Set up cache
id: cache
uses: actions/cache@v2
with:
path: |
~/PawPawBuilds
*/*.a
bin/*.*/*.so
bin/*.vst3/Contents/*/*.so
bin/Cardinal
build/Cardinal
build/CardinalFX
build/CardinalSynth
build/plugins
build/rack
carla/build
dpf/build
src/Rack/dep/bin
src/Rack/dep/include
src/Rack/dep/lib
src/Rack/dep/share
src/Rack/dep/jansson-2.12
src/Rack/dep/libarchive-3.4.3
src/Rack/dep/libsamplerate-0.1.9
src/Rack/dep/speexdsp-SpeexDSP-1.2rc3
src/Rack/dep/zstd-1.4.5
key: linux-x86_64-v${{ env.CACHE_VERSION }}
- name: Set up dependencies
run: |
@@ -222,12 +306,12 @@ jobs:
sudo apt-get install -yqq libdbus-1-dev libgl1-mesa-dev libglib2.0-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev
- name: Build extra dependencies
run: |
./deps/PawPaw/bootstrap-cardinal.sh linux
./deps/PawPaw/bootstrap-cardinal.sh linux && ./deps/PawPaw/.cleanup.sh linux
- name: Build linux x86_64
run: |
pushd deps/PawPaw; source local.env linux; popd
make features
make NOOPT=true WITH_LTO=true -j $(nproc)
make CIBUILD=true NOOPT=true WITH_LTO=true -j $(nproc)
make unzipfx
- name: Set sha8 (non-release)
if: startsWith(github.ref, 'refs/tags/') != true
@@ -317,6 +401,89 @@ jobs:
make features
make SYSDEPS=true -j $(nproc)

macos-intel:
runs-on: macos-10.15
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up cache
id: cache
uses: actions/cache@v2
with:
path: |
~/PawPawBuilds
*/*.a
bin/*.*/*.dylib
bin/*.*/Contents/MacOS/*
bin/Cardinal
build/Cardinal
build/CardinalFX
build/CardinalSynth
build/plugins
build/rack
carla/build
dpf/build
jucewrapper/build
src/Rack/dep/bin
src/Rack/dep/include
src/Rack/dep/lib
src/Rack/dep/share
src/Rack/dep/jansson-2.12
src/Rack/dep/libarchive-3.4.3
src/Rack/dep/libsamplerate-0.1.9
src/Rack/dep/speexdsp-SpeexDSP-1.2rc3
src/Rack/dep/zstd-1.4.5
key: macos-intel-v${{ env.CACHE_VERSION }}
- name: Build extra dependencies
run: |
./deps/PawPaw/bootstrap-cardinal.sh macos && ./deps/PawPaw/.cleanup.sh macos
- name: Build macOS intel (base)
run: |
pushd deps/PawPaw; source local.env macos; popd
make features
make CIBUILD=true NOOPT=true WITH_LTO=true -j $(sysctl -n hw.logicalcpu)
- name: Build macOS intel (AU using juce)
run: |
pushd deps/PawPaw; source local.env macos; popd
git clone --depth=1 -b master https://github.com/juce-framework/JUCE.git jucewrapper/JUCE
sed -i -e 's/kAudioUnitProperty_SupportsMPE/kAudioUnitProperty_ignore_SupportsMPE/' jucewrapper/JUCE/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.h
mkdir -p jucewrapper/build
pushd jucewrapper/build; cmake -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.8 -DCMAKE_BUILD_TYPE=Release .. && make -j $(sysctl -n hw.logicalcpu); popd
mv jucewrapper/build/*_artefacts/Release/AU/*.component bin/
- name: Build macOS intel (packaging)
run: |
pushd deps/PawPaw; source local.env macos; popd
./utils/create-macos-installer.sh
- name: Set sha8 (non-release)
if: startsWith(github.ref, 'refs/tags/') != true
id: slug1
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)"
- name: Set sha8 (release)
if: startsWith(github.ref, 'refs/tags/')
id: slug2
run: echo "::set-output name=sha8::$(echo ${{ github.ref_name }})"
- name: Set sha8
id: slug
run: echo "::set-output name=sha8::$(echo ${{ steps.slug1.outputs.sha8 || steps.slug2.outputs.sha8 }})"
- name: Rename macOS bundle
run: |
mv ${{ github.event.repository.name }}-macOS.pkg ${{ github.event.repository.name }}-macOS-intel-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}.pkg
- uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}-macOS-intel-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
path: |
${{ github.event.repository.name }}-*.pkg
- uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
draft: false
prerelease: false
files: |
${{ github.event.repository.name }}-*.pkg

macos-universal:
runs-on: macos-10.15
steps:
@@ -324,10 +491,32 @@ jobs:
with:
submodules: recursive
- name: Set up cache
id: cache
uses: actions/cache@v2
with:
path: |
~/PawPawBuilds
*/*.a
bin/*.*/*.dylib
bin/*.*/Contents/MacOS/*
bin/Cardinal
build/Cardinal
build/CardinalFX
build/CardinalSynth
build/plugins
build/rack
carla/build
dpf/build
jucewrapper/build
src/Rack/dep/bin
src/Rack/dep/include
src/Rack/dep/lib
src/Rack/dep/share
src/Rack/dep/jansson-2.12
src/Rack/dep/libarchive-3.4.3
src/Rack/dep/libsamplerate-0.1.9
src/Rack/dep/speexdsp-SpeexDSP-1.2rc3
src/Rack/dep/zstd-1.4.5
key: macos-universal-v${{ env.CACHE_VERSION }}
- name: Fix up Xcode
run: |
@@ -335,13 +524,23 @@ jobs:
sudo xcode-select -s "/Applications/Xcode_12.3.app"
- name: Build extra dependencies
run: |
./deps/PawPaw/bootstrap-cardinal.sh macos-universal
- name: Build macOS universal
./deps/PawPaw/bootstrap-cardinal.sh macos-universal && ./deps/PawPaw/.cleanup.sh macos-universal
- name: Build macOS universal (base)
run: |
pushd deps/PawPaw; source local.env macos-universal; popd
make features
make NOOPT=true WITH_LTO=true -j $(sysctl -n hw.logicalcpu)
./dpf/utils/package-osx-bundles.sh
make CIBUILD=true NOOPT=true WITH_LTO=true -j $(sysctl -n hw.logicalcpu)
- name: Build macOS universal (AU using juce)
run: |
pushd deps/PawPaw; source local.env macos-universal; popd
git clone --depth=1 -b master https://github.com/juce-framework/JUCE.git jucewrapper/JUCE
mkdir -p jucewrapper/build
pushd jucewrapper/build; cmake -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DCMAKE_BUILD_TYPE=Release .. && make -j $(sysctl -n hw.logicalcpu); popd
mv jucewrapper/build/*_artefacts/Release/AU/*.component bin/
- name: Build macOS universal (packaging)
run: |
pushd deps/PawPaw; source local.env macos-universal; popd
./utils/create-macos-installer.sh
- name: Set sha8 (non-release)
if: startsWith(github.ref, 'refs/tags/') != true
id: slug1
@@ -355,7 +554,7 @@ jobs:
run: echo "::set-output name=sha8::$(echo ${{ steps.slug1.outputs.sha8 || steps.slug2.outputs.sha8 }})"
- name: Rename macOS bundle
run: |
mv ${{ github.event.repository.name }}-macOS.pkg ${{ github.event.repository.name }}-macOS-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}.pkg
mv ${{ github.event.repository.name }}-macOS.pkg ${{ github.event.repository.name }}-macOS-universal-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}.pkg
- uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}-macOS-universal-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
@@ -495,10 +694,31 @@ jobs:
with:
submodules: recursive
- name: Set up cache
id: cache
uses: actions/cache@v2
with:
path: |
~/PawPawBuilds
*/*.a
bin/*.*/*.dll
bin/*.vst3/Contents/*/*.vst3
bin/Cardinal.exe
build/Cardinal
build/CardinalFX
build/CardinalSynth
build/plugins
build/rack
carla/build
dpf/build
src/Rack/dep/bin
src/Rack/dep/include
src/Rack/dep/lib
src/Rack/dep/share
src/Rack/dep/jansson-2.12
src/Rack/dep/libarchive-3.4.3
src/Rack/dep/libsamplerate-0.1.9
src/Rack/dep/speexdsp-SpeexDSP-1.2rc3
src/Rack/dep/zstd-1.4.5
key: win32-v${{ env.CACHE_VERSION }}
- name: Fix GitHub's mess
run: |
@@ -509,15 +729,25 @@ jobs:
run: |
sudo dpkg --add-architecture i386
sudo apt-get update -qq
sudo apt-get install -yqq binutils-mingw-w64-i686 g++-mingw-w64-i686 mingw-w64 wine-stable:i386
sudo apt-get install -yqq binutils-mingw-w64-i686 g++-mingw-w64-i686 mingw-w64 wine-stable:i386 qttools5-dev qttools5-dev-tools xvfb
- name: Build extra dependencies
run: |
./deps/PawPaw/bootstrap-cardinal.sh win32
- name: Build win32 cross-compiled
./deps/PawPaw/bootstrap-cardinal.sh win32 && ./deps/PawPaw/.cleanup.sh win32
- name: Build win32 cross-compiled (base)
run: |
pushd deps/PawPaw; source local.env win32; popd
make features
make NOOPT=true WITH_LTO=true -j $(nproc)
make CIBUILD=true NOOPT=true WITH_LTO=true -j $(nproc)
- name: Build win64 cross-compiled (carla)
run: |
pushd deps/PawPaw; source local.env win32; popd
make -C carla CARLA_BACKEND_NAMESPACE=Cardinal EXTERNAL_PLUGINS=true HAVE_FLUIDSYNTH=false HAVE_ZYN_DEPS=false HAVE_ZYN_UI_DEPS=false HAVE_PYQT=true HAVE_QT5=true HAVE_QT5PKG=true STATIC_PLUGIN_TARGET=true USING_JUCE=false USING_JUCE_GUI_EXTRA=false -j $(nproc)
make -C carla EMBED_TARGET=true TESTING=true dist
make -C carla EMBED_TARGET=true TESTING=true dist
- name: Build win64 cross-compiled (packaging)
run: |
pushd deps/PawPaw; source local.env win32; popd
xvfb-run ./utils/create-windows-installer.sh 32
- name: Set sha8 (non-release)
if: startsWith(github.ref, 'refs/tags/') != true
id: slug1
@@ -536,6 +766,7 @@ jobs:
with:
name: ${{ github.event.repository.name }}-win32-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
path: |
*.exe
*.zip
- uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
@@ -545,6 +776,7 @@ jobs:
draft: false
prerelease: false
files: |
*.exe
*.zip

win64:
@@ -554,10 +786,31 @@ jobs:
with:
submodules: recursive
- name: Set up cache
id: cache
uses: actions/cache@v2
with:
path: |
~/PawPawBuilds
*/*.a
bin/*.*/*.dll
bin/*.vst3/Contents/*/*.vst3
bin/Cardinal.exe
build/Cardinal
build/CardinalFX
build/CardinalSynth
build/plugins
build/rack
carla/build
dpf/build
src/Rack/dep/bin
src/Rack/dep/include
src/Rack/dep/lib
src/Rack/dep/share
src/Rack/dep/jansson-2.12
src/Rack/dep/libarchive-3.4.3
src/Rack/dep/libsamplerate-0.1.9
src/Rack/dep/speexdsp-SpeexDSP-1.2rc3
src/Rack/dep/zstd-1.4.5
key: win64-v${{ env.CACHE_VERSION }}
- name: Fix GitHub's mess
run: |
@@ -566,16 +819,27 @@ jobs:
sudo apt-get install -yqq --allow-downgrades libpcre2-8-0/focal libpcre2-16-0/focal libpcre2-32-0/focal libpcre2-posix2/focal
- name: Set up dependencies
run: |
sudo dpkg --add-architecture i386
sudo apt-get update -qq
sudo apt-get install -yqq binutils-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64 wine-stable
sudo apt-get install -yqq binutils-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64 wine-stable qttools5-dev qttools5-dev-tools xvfb
- name: Build extra dependencies
run: |
./deps/PawPaw/bootstrap-cardinal.sh win64
- name: Build win64 cross-compiled
./deps/PawPaw/bootstrap-cardinal.sh win64 && ./deps/PawPaw/.cleanup.sh win64
- name: Build win64 cross-compiled (base)
run: |
pushd deps/PawPaw; source local.env win64; popd
make features
make NOOPT=true WITH_LTO=true -j $(nproc)
make CIBUILD=true NOOPT=true WITH_LTO=true -j $(nproc)
- name: Build win64 cross-compiled (carla)
run: |
pushd deps/PawPaw; source local.env win64; popd
make -C carla CARLA_BACKEND_NAMESPACE=Cardinal EXTERNAL_PLUGINS=true HAVE_FLUIDSYNTH=false HAVE_ZYN_DEPS=false HAVE_ZYN_UI_DEPS=false HAVE_PYQT=true HAVE_QT5=true HAVE_QT5PKG=true STATIC_PLUGIN_TARGET=true USING_JUCE=false USING_JUCE_GUI_EXTRA=false all win32r -j $(nproc)
make -C carla EMBED_TARGET=true TESTING=true dist
make -C carla EMBED_TARGET=true TESTING=true dist
- name: Build win64 cross-compiled (packaging)
run: |
pushd deps/PawPaw; source local.env win64; popd
xvfb-run ./utils/create-windows-installer.sh 64
- name: Set sha8 (non-release)
if: startsWith(github.ref, 'refs/tags/') != true
id: slug1
@@ -594,6 +858,7 @@ jobs:
with:
name: ${{ github.event.repository.name }}-win64-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
path: |
*.exe
*.zip
- uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
@@ -603,6 +868,7 @@ jobs:
draft: false
prerelease: false
files: |
*.exe
*.zip

source-tarball:
@@ -662,17 +928,17 @@ jobs:
sudo dpkg -i kxstudio-repos_10.0.3_all.deb
sudo apt-get update -qq
# build-deps
sudo apt-get install -yqq libgl1-mesa-dev liblo-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev
sudo apt-get install -yqq liblo-dev
# runtime testing
sudo apt-get install -yqq carla-git lilv-utils lv2-dev lv2lint valgrind
- name: Build plugins
- name: Build Cardinal
env:
CFLAGS: -g
CXXFLAGS: -g -DDPF_ABORT_ON_ERROR
LDFLAGS: -static-libgcc -static-libstdc++
run: |
make features
make NOOPT=true SKIP_STRIPPING=true -j $(nproc)
make HEADLESS=true features
make HEADLESS=true NOOPT=true NOPLUGINS=true STATIC_BUILD=true SKIP_STRIPPING=true -j $(nproc)
- name: Validate LV2 ttl syntax
run: |
lv2_validate \
@@ -681,14 +947,14 @@ jobs:
/usr/lib/lv2/kx-control-input-port-change-request.lv2/*.ttl \
/usr/lib/lv2/kx-programs.lv2/*.ttl \
./bin/*.lv2/*.ttl
#- name: Validate LV2 metadata and binaries
#run: |
#export LV2_PATH=/tmp/lv2-path
#mkdir ${LV2_PATH}
#cp -r bin/*.lv2 \
#/usr/lib/lv2/{atom,buf-size,core,data-access,kx-control-input-port-change-request,kx-programs,instance-access,midi,parameters,port-groups,port-props,options,patch,presets,resize-port,state,time,ui,units,urid,worker}.lv2 \
#${LV2_PATH}
#lv2lint -s lv2_generate_ttl -l ld-linux-x86-64.so.2 -M nopack $(lv2ls)
- name: Validate LV2 metadata and binaries
run: |
export LV2_PATH=/tmp/lv2-path
mkdir ${LV2_PATH}
cp -r bin/CardinalFX.lv2 bin/CardinalSynth.lv2 \
/usr/lib/lv2/{atom,buf-size,core,data-access,kx-control-input-port-change-request,kx-programs,instance-access,midi,mod,parameters,port-groups,port-props,options,patch,presets,resize-port,state,time,ui,units,urid,worker}.lv2 \
${LV2_PATH}
lv2lint -s lv2_generate_ttl -l ld-linux-x86-64.so.2 -M nopack $(lv2ls)
- name: Test LV2 plugin
run: |
export LV2_PATH=/tmp/lv2-path
@@ -701,27 +967,25 @@ jobs:
--suppressions=./dpf/utils/valgrind-dpf.supp \
/usr/lib/carla/carla-bridge-native lv2 "" ${p} 1>/dev/null; \
done
# - name: Test VST2 plugin
# env:
# CARLA_DO_NOT_USE_JUCE_FOR_VST2: 1
# run: |
# for p in $(ls bin/*.vst/*.so); do \
# env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \
# valgrind \
# --error-exitcode=255 \
# --leak-check=no \
# --track-origins=yes \
# --suppressions=./dpf/utils/valgrind-dpf.supp \
# /usr/lib/carla/carla-bridge-native vst2 ./${p} "" 1>/dev/null; \
# done
# - name: Test VST3 plugin
# run: |
# for p in $(ls bin/ | grep vst3); do \
# env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \
# valgrind \
# --error-exitcode=255 \
# --leak-check=no \
# --track-origins=yes \
# --suppressions=./dpf/utils/valgrind-dpf.supp \
# /usr/lib/carla/carla-bridge-native vst3 ./bin/${p} "" 1>/dev/null; \
# done
- name: Test VST2 plugin
run: |
for p in $(ls bin/*.vst/*.so); do \
env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \
valgrind \
--error-exitcode=255 \
--leak-check=no \
--track-origins=yes \
--suppressions=./dpf/utils/valgrind-dpf.supp \
/usr/lib/carla/carla-bridge-native vst2 ./${p} "" 1>/dev/null; \
done
- name: Test VST3 plugin
run: |
for p in $(ls bin/ | grep vst3); do \
env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \
valgrind \
--error-exitcode=255 \
--leak-check=no \
--track-origins=yes \
--suppressions=./dpf/utils/valgrind-dpf.supp \
/usr/lib/carla/carla-bridge-native vst3 ./bin/${p} "" 1>/dev/null; \
done

+ 2
- 0
.gitignore View File

@@ -15,3 +15,5 @@
bin/
build/
documentation.pdf
utils/inno/resources.iss
utils/inno/version.iss

+ 55
- 4
.gitmodules View File

@@ -84,7 +84,7 @@
url = https://gitlab.com/sonusdept/sonusmodular.git
[submodule "plugins/Mog"]
path = plugins/Mog
url = https://github.com/CardinalModules/Mog-VCV.git
url = https://github.com/JustMog/Mog-VCV.git
[submodule "plugins/ChowDSP"]
path = plugins/ChowDSP
url = https://github.com/jatinchowdhury18/ChowDSP-VCV.git
@@ -130,9 +130,6 @@
[submodule "plugins/Autinn"]
path = plugins/Autinn
url = https://github.com/NikolaiVChr/Autinn.git
[submodule "plugins/substation-opensource"]
path = plugins/substation-opensource
url = https://gitlab.com/falktx/substation-opensource.git
[submodule "plugins/MockbaModular"]
path = plugins/MockbaModular
url = https://github.com/MockbaTheBorg/MockbaModular.git
@@ -142,6 +139,60 @@
[submodule "plugins/Axioma"]
path = plugins/Axioma
url = https://github.com/kauewerner/Axioma.git
[submodule "plugins/GoodSheperd"]
path = plugins/GoodSheperd
url = https://github.com/jensschulze/GoodSheperd.git
[submodule "plugins/HamptonHarmonics"]
path = plugins/HamptonHarmonics
url = https://gitlab.com/hampton-harmonics/hampton-harmonics-modules.git
[submodule "plugins/ML_modules"]
path = plugins/ML_modules
url = https://github.com/martin-lueders/ML_modules.git
[submodule "plugins/Orbits"]
path = plugins/Orbits
url = https://github.com/RareBreeds/Orbits.git
[submodule "plugins/stocaudio"]
path = plugins/stocaudio
url = https://github.com/aptrn/stocaudio-modules.git
[submodule "plugins/CatroModulo"]
path = plugins/CatroModulo
url = https://github.com/catronomix/catro-modulo.git
[submodule "plugins/LilacLoop"]
path = plugins/LilacLoop
url = https://github.com/grough/lilac-loop-vcv.git
[submodule "plugins/kocmoc"]
path = plugins/kocmoc
url = https://github.com/janne808/kocmoc-rack-modules.git
[submodule "plugins/PathSet"]
path = plugins/PathSet
url = https://github.com/patheros/PathSetModules.git
[submodule "plugins/Algoritmarte"]
path = plugins/Algoritmarte
url = https://github.com/algoritmarte/AlgoritmarteVCVPlugin.git
[submodule "plugins/AaronStatic"]
path = plugins/AaronStatic
url = https://github.com/aaronstatic/AaronStatic_modules.git
[submodule "plugins/MSM"]
path = plugins/MSM
url = https://github.com/netboy3/MSM-vcvrack-plugin.git
[submodule "plugins/nonlinearcircuits"]
path = plugins/nonlinearcircuits
url = https://github.com/mhetrick/nonlinearcircuits.git
[submodule "plugins/voxglitch"]
path = plugins/voxglitch
url = https://github.com/clone45/voxglitch.git
[submodule "plugins/ArableInstruments"]
path = plugins/ArableInstruments
url = https://github.com/CardinalModules/ArableInstruments.git
[submodule "plugins/Fundamental"]
path = plugins/Fundamental
url = https://github.com/CardinalModules/Fundamental.git
[submodule "plugins/unless_modules"]
path = plugins/unless_modules
url = https://gitlab.com/unlessgames/unless_modules.git
[submodule "plugins/PinkTrombone"]
path = plugins/PinkTrombone
url = https://github.com/VegaDeftwing/PinkTromboneVCV.git
[submodule "plugins/dBiz"]
path = plugins/dBiz
url = https://github.com/dBiz/dBiz.git

+ 16
- 19
Makefile View File

@@ -7,7 +7,7 @@
# also set in:
# src/CardinalCommon.cpp `CARDINAL_VERSION`
# src/CardinalPlugin.cpp `getVersion`
VERSION = 22.02
VERSION = 22.04

# --------------------------------------------------------------
# Import base definitions
@@ -57,6 +57,15 @@ endif
CARLA_EXTRA_ARGS += USING_JUCE=false
CARLA_EXTRA_ARGS += USING_JUCE_GUI_EXTRA=false

# --------------------------------------------------------------
# DGL config

DGL_EXTRA_ARGS = \
NVG_DISABLE_SKIPPING_WHITESPACE=true \
NVG_FONT_TEXTURE_FLAGS=NVG_IMAGE_NEAREST \
USE_NANOVG_FBO=true \
WINDOWS_ICON_ID=401

# --------------------------------------------------------------
# Check for system-wide dependencies

@@ -196,7 +205,7 @@ endif

dgl:
ifneq ($(HEADLESS),true)
$(MAKE) -C dpf/dgl opengl NVG_DISABLE_SKIPPING_WHITESPACE=true NVG_FONT_TEXTURE_FLAGS=NVG_IMAGE_NEAREST USE_NANOVG_FBO=true
$(MAKE) -C dpf/dgl opengl $(DGL_EXTRA_ARGS)
endif

plugins: deps
@@ -255,8 +264,7 @@ install:
install -d $(DESTDIR)$(PREFIX)/lib/lv2/Cardinal.lv2
install -d $(DESTDIR)$(PREFIX)/lib/lv2/CardinalFX.lv2
install -d $(DESTDIR)$(PREFIX)/lib/lv2/CardinalSynth.lv2
install -d $(DESTDIR)$(PREFIX)/lib/vst/CardinalFX.vst
install -d $(DESTDIR)$(PREFIX)/lib/vst/CardinalSynth.vst
install -d $(DESTDIR)$(PREFIX)/lib/vst/Cardinal.vst
ifeq ($(VST3_SUPPORTED),true)
install -d $(DESTDIR)$(PREFIX)/lib/vst3/Cardinal.vst3/Contents
install -d $(DESTDIR)$(PREFIX)/lib/vst3/CardinalFX.vst3/Contents
@@ -269,8 +277,7 @@ endif
install -m 644 bin/CardinalFX.lv2/*.* $(DESTDIR)$(PREFIX)/lib/lv2/CardinalFX.lv2/
install -m 644 bin/CardinalSynth.lv2/*.* $(DESTDIR)$(PREFIX)/lib/lv2/CardinalSynth.lv2/

install -m 644 bin/CardinalFX.vst/*.* $(DESTDIR)$(PREFIX)/lib/vst/CardinalFX.vst/
install -m 644 bin/CardinalSynth.vst/*.* $(DESTDIR)$(PREFIX)/lib/vst/CardinalSynth.vst/
install -m 644 bin/Cardinal.vst/*.* $(DESTDIR)$(PREFIX)/lib/vst/Cardinal.vst/

ifeq ($(VST3_SUPPORTED),true)
cp -rL bin/Cardinal.vst3/Contents/*-* $(DESTDIR)$(PREFIX)/lib/vst3/Cardinal.vst3/Contents/
@@ -284,19 +291,6 @@ endif
install -m 644 README.md $(DESTDIR)$(PREFIX)/share/doc/cardinal/
install -m 644 docs/*.md docs/*.png $(DESTDIR)$(PREFIX)/share/doc/cardinal/docs/

ln -sf $(PREFIX)/share/cardinal $(DESTDIR)$(PREFIX)/lib/lv2/Cardinal.lv2/resources
ln -sf $(PREFIX)/share/cardinal $(DESTDIR)$(PREFIX)/lib/lv2/CardinalFX.lv2/resources
ln -sf $(PREFIX)/share/cardinal $(DESTDIR)$(PREFIX)/lib/lv2/CardinalSynth.lv2/resources

ln -sf $(PREFIX)/share/cardinal $(DESTDIR)$(PREFIX)/lib/vst/CardinalFX.vst/resources
ln -sf $(PREFIX)/share/cardinal $(DESTDIR)$(PREFIX)/lib/vst/CardinalSynth.vst/resources

ifeq ($(VST3_SUPPORTED),true)
ln -sf $(PREFIX)/share/cardinal $(DESTDIR)$(PREFIX)/lib/vst3/Cardinal.vst3/Contents/Resources
ln -sf $(PREFIX)/share/cardinal $(DESTDIR)$(PREFIX)/lib/vst3/CardinalFX.vst3/Contents/Resources
ln -sf $(PREFIX)/share/cardinal $(DESTDIR)$(PREFIX)/lib/vst3/CardinalSynth.vst3/Contents/Resources
endif

# --------------------------------------------------------------
# Tarball step, for releases

@@ -404,6 +398,9 @@ tarball+deps: download
rm -f ../cardinal+deps-$(VERSION).tar.xz
tar -c --lzma $(TAR_ARGS) -f ../cardinal+deps-$(VERSION).tar.xz .

version:
@echo $(VERSION)

# --------------------------------------------------------------

.PHONY: carla deps plugins

+ 66
- 31
README.md View File

@@ -3,7 +3,7 @@
*Cardinal, the Rack!*

Cardinal is a free and open-source virtual modular synthesizer plugin,
available as JACK standalone and LV2, VST2 and VST3 audio plugin for FreeBSD, Linux, macOS and Windows.
available as JACK standalone and AU, LV2, VST2 and VST3 audio plugin for FreeBSD, Linux, macOS and Windows.
It is based on the popular [VCV Rack](https://vcvrack.com/) but with a focus on being a fully self-contained plugin version.

More specifically, this is a [DPF-based](https://github.com/DISTRHO/DPF/)
@@ -19,10 +19,42 @@ All "Core" modules from Rack have been replaced by Cardinal equivalents, simplif
Cardinal does not load external modules and does not connect to the official Rack library/store.
All VCV branding has been removed (to the best of our knowledge) in order to avoid any trademark issues.

Because it is using DPF, Cardinal already supports LV2 and VST2 with an extra JACK standalone mode for some systems.
The VST3 version is in progress, already part of the build but still experimental.


## Current status

With the exception of a few bugs, Cardinal can be considered stable.
Though currently the following should be noted:

- Keyboard input does not always work in some hosts [#24](https://github.com/DISTRHO/Cardinal/issues/24)
- VST3 support incomplete/experimental [#41](https://github.com/DISTRHO/Cardinal/issues/41)
- Windows 32bit builds do not work well [#80](https://github.com/DISTRHO/Cardinal/issues/80)

### Stable release

Cardinal releases have official builds for Linux, macOS and Windows.
You can find these under https://github.com/DISTRHO/Cardinal/releases.

There are Linux builds for various architectures (armhf, arm64, i686 and x86_64), macOS "universal" (arm64 + intel) and Windows 32 and 64bit builds.
Both macOS and Windows builds have an installer.
Install instructions are available [here](https://github.com/DISTRHO/Cardinal/wiki/Install).

Note: Neither the macOS or Windows builds are signed, so expect warnings saying they are from an "untrusted developer".

### Nightly builds

You can find builds for pretty much any recent Cardinal commit [here](https://github.com/DISTRHO/Cardinal/actions/workflows/build.yml).
Just click on any successful build, and scroll to the bottom to find the builds.
(note the canvas-like area in the middle prevents mouse wheel scrolling)

A GitHub account is required in order to download these builds.

### Building

Basic building instructions are available in [BUILDING.md](docs/BUILDING.md).


## Plugin variants

Cardinal provides 3 plugin variants - "main", Synth and FX.
@@ -36,7 +68,7 @@ All variants have MIDI input and output support.

This variant provides 8 audio inputs and outputs and 10 CV inputs and outputs.

NOTE: Due to VST2 format not supporting CV ports, this variant is not available for VST2.
NOTE: Due to AU and VST2 formats not supporting CV ports, this variant is not available for those formats.

### Synth

@@ -70,33 +102,6 @@ But a couple of modules background's have their colors flipped, because damn we

![screenshot](docs/Screenshot_Carla+Ildaeil.png "Screenshot")

## Current status

With the exception of a few bugs, Cardinal can be considered stable.
Though currently the following should be noted:

- Keyboard input does not always work in some hosts [#24](https://github.com/DISTRHO/Cardinal/issues/24)
- VST3 support incomplete/experimental [#41](https://github.com/DISTRHO/Cardinal/issues/41)
- Windows 32bit builds do not work well [#80](https://github.com/DISTRHO/Cardinal/issues/80)

### Current builds

If you want to try this out early, checkout the [GitHub actions tab](https://github.com/DISTRHO/Cardinal/actions/workflows/build.yml).
There is absolutely no warranty, use at your own risk and all that...

Basic building instructions are available in [BUILDING.md](docs/BUILDING.md)

### Community chat

Currently we are all on #cardinal IRC room in irc.libera.chat server.
Come join us in your favorite IRC client or through a Matrix bridge.


## License

Cardinal is licensed under GPLv3+, see [LICENSE](LICENSE) for more details.
An overview of the included code and linked submodules can be seen [here](docs/LICENSES.md#code-license--binary).


## Included modules

@@ -104,8 +109,11 @@ At the moment the following 3rd-party modules are provided:

- 21kHz
- 8Mode
- Aaron Static
- AlgoritmArte
- Amalgamated Harmonics
- Animated Circuits
- Arable Instruments
- Aria Salvatrice
- Audible Instruments
- Autinn
@@ -114,6 +122,7 @@ At the moment the following 3rd-party modules are provided:
- Befaco
- Bidoo
- Bogaudio
- Catro/Modulo
- cf
- ChowDSP
- dBiz
@@ -122,26 +131,40 @@ At the moment the following 3rd-party modules are provided:
- ExpertSleepers Encoders
- Extratone
- Fehler Fabrik
- Fundamental
- Glue the Giant
- GoodSheperd
- Grande
- Hampton Harmonics
- HetrickCV
- ihtsyn
- Impromptu
- JW-Modules
- kocmoc
- LifeFormModular
- Lilac Loop
- Little Utils
- Lomas Modules
- Lyrae Modules
- MindMeld
- ML Modules
- Mockba Modular
- Mog
- mscHack
- MSM
- Nonlinear Circuits
- Orbits
- Parable Instruments
- Path Set
- PinkTrombone
- Prism
- rackwindows
- repelzen
- Sonus Modular
- Substation Opensource
- stocaudio
- unless_modules
- Valley
- Voxglitch
- ZetaCarinae
- ZZC

@@ -215,3 +238,15 @@ Cardinal and Rack should be able to co-exist friendly and peacefully, as they cl
It is likely most people will prefer to use Rack Pro for its official support and its big module collection (including commercial ones).

A feature comparison between Cardinal and Rack Pro can be seen [here](docs/DIFFERENCES.md).


## License

Cardinal is licensed under GPLv3+, see [LICENSE](LICENSE) for more details.
An overview of the included code and linked submodules can be seen [here](docs/LICENSES.md#code-license--binary).


## Community chat

Currently we are all on #cardinal IRC room in irc.libera.chat server.
Come join us in your favorite IRC client or through a Matrix bridge.

+ 1
- 1
carla

@@ -1 +1 @@
Subproject commit 4b6010bd0adbbed5a0cb89a1253e52e72e648b18
Subproject commit 04558b63101de55556733edfa4a369b51f36e9b3

+ 6
- 3
deps/Makefile View File

@@ -54,8 +54,8 @@ BASE_FLAGS += -I../include/mingw-compat
BASE_FLAGS += -I../include/mingw-std-threads
endif

BUILD_C_FLAGS += -fno-finite-math-only
BUILD_CXX_FLAGS += -fno-finite-math-only
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing

# Rack code is not tested for this flag, unset it
BUILD_CXX_FLAGS += -U_GLIBCXX_ASSERTIONS -Wp,-U_GLIBCXX_ASSERTIONS
@@ -190,7 +190,10 @@ $(DEP_PATH)/libsamplerate-0.1.9/.stamp-patched:
$(DEP_PATH)/lib/libspeexdsp.a: $(DEP_PATH)/speexdsp-SpeexDSP-1.2rc3/.stamp-patched

$(DEP_PATH)/speexdsp-SpeexDSP-1.2rc3/.stamp-patched:
$(DEP_MAKE) -C $(DEP_PATH) speexdsp-SpeexDSP-1.2rc3
$(DEP_MAKE) -C $(DEP_PATH) speexdsp-SpeexDSP-1.2rc3 \
WGET="wget -c http://downloads.xiph.org/releases/speex/speexdsp-1.2rc3.tar.gz && mv speexdsp-1.2rc3.tar.gz speexdsp-SpeexDSP-1.2rc3.tgz #" \
SHA256SUM="true" \
UNTAR="mkdir -p speexdsp-SpeexDSP-1.2rc3 && tar -x --strip-components=1 --directory=$(DEP_PATH)/speexdsp-SpeexDSP-1.2rc3 -f"
sed -i -e "s/#pragma GCC visibility push/#error we dont want this/" $(DEP_PATH)/speexdsp-SpeexDSP-1.2rc3/configure
touch $@



+ 1
- 1
deps/PawPaw

@@ -1 +1 @@
Subproject commit b41a693b64cdba1abd8d278c9985fb690b522854
Subproject commit 01d07086586818e427b2898d2d446d30b68f3139

+ 3
- 4
docs/DIFFERENCES.md View File

@@ -21,6 +21,7 @@ Bellow follows a list of features comparing the official plugin to Cardinal.
| Loads external modules | Yes | No | |
| Supports closed-source modules | Yes | No | |
| Supports physical devices | Yes | No | Audio + MIDI only through the DAW/Host or via JACK in standalone |
| Plugin in AU format | No | Yes | |
| Plugin in LV2 format | No | Yes | |
| Plugin in VST2 format | Yes | Yes | |
| Plugin in VST3 format | No | WIP | |
@@ -30,24 +31,22 @@ Bellow follows a list of features comparing the official plugin to Cardinal.
| Supports BSD systems | No | Yes | Available as FreeBSD port |
| Synth plugin variant | 16 ins, 16 outs | 2 ins, 2 outs | |
| FX plugin variant | 16 ins, 16 outs | 2 ins, 2 outs | |
| Raw-CV plugin variant | Unsupported | 8 audio IO + 10 CV IO | Available in JACK, LV2 and VST3 formats, not possible in VST2 |
| Raw-CV plugin variant | Unsupported | 8 audio IO + 10 CV IO | Available in JACK, LV2 and VST3 formats, not possible in AU and VST2 |
| Arbitrary parameter automation | Yes | No | Unsupported in Cardinal, tricky to do for many plugin formats at once |
| Integrated plugin host | No, Host payed separately | Yes, using Carla or Ildaeil | |
| Host sync/timing | Using MIDI signals | Using dedicated module | |
| Linux/X11 event handling | Runs on 2nd thread | Runs on main/GUI thread | |
| v1 module compatibility | No | No, but with less restrictions | Module widgets can load resources at any point |
| Online phone-home | Yes | No | Online access is strictly forbidden in Cardinal |
| Proper dark theme | No, only room brightness | Yes | CC-ND respected by leaving files intact, dark mode applied at runtime |
| Proper dark theme | No, only room brightness | Yes | All dark panel variants have explicit permission when required |
| Proper Linux headless mode | No, always requires X11 | Yes | |

Additionally, Cardinal contains the following built-in modules not present in the official plugin or standalone:

* Amalgamated Harmonics
* Aria Salvatrice modules (except Arcane related modules, due to their online requirement)
* Mog (never updated to v2)
* mscHack (never updated to v2)
* rackwindows
* repelzen
* Audio File
* Carla Plugin Host
* Ildaeil Host


+ 8
- 15
docs/FAQ.md View File

@@ -9,19 +9,6 @@ But basically we want an open-source plugin version of "Rack Pro",
where we are free to change things as we see fit, work on new features and fix bugs.
This is simply not possible with proprietary software, which is the case of "Rack Pro".

## Where is Fundamental?

There are some artwork license issues that prevent us from using Fundamental exactly as we want.
We could in theory use it as-is, VCV logo and everything, but it looks out of place with Cardinal's general dark mode theme.
The artwork license does not allow modifications, and that VCV logo being present on the panels makes Cardinal's authors unease.
Cardinal is not a VCV product in any way, or endorsed by it. Would be quite bad to give that impression.

Current plan is to redo Fundamental panel graphics in a more liberal license, so it then can be included in Cardinal.
In the mean time, check [this wiki page](https://github.com/DISTRHO/Cardinal/wiki/Fundamental-replacements)
for a list of module replacements for Fundamental stuff.

PS: Don't forget to contribute back as well! ;)

## Can I install extra modules?

No, Cardinal is intentionally a fully self-contained plugin.
@@ -64,9 +51,15 @@ As a plugin, the state will be saved together with the host/DAW project.
## On BSD/Linux/X11 the menu item "Save As/Export..." does nothing

The save-file dialogs in Cardinal requires a working [xdg-desktop-portal](https://github.com/flatpak/xdg-desktop-portal) DBus implementation from your desktop environment.
Typically your desktop already provides this, if not consider looking for a package to install with "desktop-portal" in the name.
Typically your desktop already provides this, if not consider looking for a package to install with "desktop-portal" in the name.
If you are running a window manager without a "real" desktop environment (like custom X11 or i3 setups),
you will need to manually activate the systemd unit that provides these DBus services, like so:

```
systemctl enable xdg-desktop-portal --user --now
```

The open-file dialogs in Cardinal do not have this restriction, with a fallback in case desktop portal is not available.
Note: The open-file dialogs in Cardinal do not have this restriction, with a fallback in case the desktop portal is not available.

## Why IRC and not Discord?



+ 54
- 3
docs/LICENSES.md View File

@@ -4,7 +4,7 @@

While Cardinal itself is licensed under GPLv3+, some modules/plugins used by it are not.
And since Cardinal builds the entire Rack and modules as a static library,
the more restrictive of the **code licenses** will apply to the final binary.
the more restrictive of the **code licenses** will apply to the final binary.

Bellow follows a list of all code licenses used in Cardinal and linked submodules.

@@ -15,8 +15,11 @@ Bellow follows a list of all code licenses used in Cardinal and linked submodule
| Rack | GPL-3.0-or-later | The actual Rack code, internal dependencies are compatible with GPLv3+ |
| 21kHz | MIT | |
| 8Mode | BSD-3-Clause | |
| Aaron Static | MIT | |
| AlgoritmArte | GPL-3.0-or-later | |
| Amalgamated Harmonics | BSD-3-Clause | |
| Animated Circuits | GPL-3.0-or-later | |
| Arable Instruments | GPL-3.0-or-later | |
| Aria Salvatrice | GPL-3.0-or-later | |
| Audible Instruments | GPL-3.0-or-later | |
| Autinn | GPL-3.0-or-later | |
@@ -25,6 +28,7 @@ Bellow follows a list of all code licenses used in Cardinal and linked submodule
| Befaco | GPL-3.0-or-later | |
| Bidoo | GPL-3.0-or-later | |
| Bogaudio | GPL-3.0-or-later | |
| Catro/Modulo | BSD-3-Clause | |
| cf | BSD-3-Clause | |
| ChowDSP | GPL-3.0-or-later | |
| dBiz | GPL-3.0-or-later | |
@@ -33,26 +37,40 @@ Bellow follows a list of all code licenses used in Cardinal and linked submodule
| ExpertSleepers Encoders | MIT | |
| Extratone | GPL-3.0-or-later | |
| Fehler Fabrik | GPL-3.0-or-later | |
| Fundamental | GPL-3.0-or-later | |
| Glue the Giant | GPL-3.0-or-later | |
| GoodSheperd | GPL-3.0-or-later | |
| Grande | GPL-3.0-or-later | |
| Hampton Harmonics | MIT | |
| HetrickCV | CC0-1.0 | |
| ihtsyn | GPL-3.0-or-later | |
| Impromptu | GPL-3.0-or-later | |
| JW-Modules | BSD-3-Clause | |
| kocmoc | GPL-3.0-or-later | |
| LifeFormModular | MIT | |
| Lilac Loop | GPL-3.0-or-later | |
| Little Utils | EUPL-1.2 | |
| Lomas Modules | GPL-3.0-or-later | |
| Lyrae Modules | GPL-3.0-or-later | |
| MindMeld | GPL-3.0-or-later | |
| ML Modules | BSD-3-Clause | |
| Mockba Modular | MIT | |
| Mog | CC0-1.0 | |
| mscHack | BSD-3-Clause | |
| MSM | MIT | Repo's [LICENSE-dist.md](https://github.com/netboy3/MSM-vcvrack-plugin/issues/10) includes wrong information |
| Nonlinear Circuits | CC0-1.0 | |
| Orbits | GPL-3.0-or-later | |
| Parable Instruments | GPL-3.0-or-later | |
| Path Set | GPL-3.0-or-later | |
| PinkTrombone | GPL-3.0-or-later | |
| Prism | BSD-3-Clause | |
| Rackwindows | MIT | |
| repelzen | GPL-3.0-or-later | |
| Substation Opensource | BSD-3-Clause-Attribution | Need to check full compatibility with GPLv3+ |
| Sonus Modular | GPL-3.0-or-later | |
| stocaudio | GPL-3.0-or-later | |
| unless_modules | GPL-3.0-or-later | |
| Valley | GPL-3.0-or-later | |
| Voxglitch | GPL-3.0-or-later | |
| ZetaCarinae | GPL-3.0-or-later | |
| ZZC | GPL-3.0-or-later | |

@@ -74,10 +92,15 @@ Below is a list of artwork licenses from plugins
|-----------------------------------------|------------------|------------------|
| 21kHz | MIT | No artwork specific license provided |
| 8Mode | BSD-3-Clause | No artwork specific license provided |
| AaronStatic/* | MIT | No artwork specific license provided |
| AaronStatic/fonts/PixelOperator.ttf | CC0-1.0 | |
| Algoritmarte/* | GPL-3.0-or-later | No artwork specific license provided |
| Algoritmarte/LEDSliderGreenHandle.svg | CC-BY-NC-4.0 | |
| AmalgamatedHarmonics/* | BSD-3-Clause | No artwork specific license provided |
| AmalgamatedHarmonics/DSEG*.ttf | OFL-1.1-RFN | |
| AmalgamatedHarmonics/Roboto*.ttf | Apache-2.0 | |
| AnimatedCircuits/* | CC-BY-NC-SA-4.0 | |
| ArableInstruments/* | Custom | Copyright © Alex Brandt, [used and distributed with permission](https://github.com/adbrant/ArableInstruments/issues/21) |
| AriaModules/* | CC-BY-SA-4.0 | |
| AriaModules/Arcane/* | CC-BY-NC-SA-3.0 | Unused in Cardinal |
| AriaModules/components/* | WTFPL | |
@@ -95,12 +118,15 @@ Below is a list of artwork licenses from plugins
| BaconPlugs/Keypunch029.json | OFL-1.1 | |
| Bidoo/* | CC-BY-NC-ND-4.0 | [Special permission granted for runtime dark mode](https://github.com/sebastien-bouffier/Bidoo/issues/191) |
| Befaco/components/* | CC-BY-NC-4.0 | |
| Befaco/fonts/Segment7Standard.otf | OFL-1.1-RFN | |
| Befaco/panels/* | Custom | Copyright © [Befaco](https://www.befaco.org/), [used and distributed with permission](LICENSE-PERMISSIONS.md#befaco-manu-retamero--befaco) |
| BogaudioModules/* | CC-BY-SA-4.0 | |
| BogaudioModules/fonts/audiowide.ttf | OFL-1.1-RFN | |
| BogaudioModules/fonts/inconsolata*.ttf | OFL-1.1-no-RFN | |
| Cardinal/* | CC0-1.0 | |
| Cardinal/Miku/Miku.png | CC-BY-NC-3.0 | https://piapro.net/intl/en_for_creators.html |
| CatroModulo/* | BSD-3-Clause | No artwork specific license provided |
| CatroModulo/Segment7Standard.ttf | OFL-1.1-RFN | |
| cf/* | BSD-3-Clause | No artwork specific license provided |
| cf/DejaVuSansMono.ttf | Bitstream-Vera | |
| cf/Segment7Standard.ttf | OFL-1.1-RFN | |
@@ -119,16 +145,22 @@ Below is a list of artwork licenses from plugins
| ExpertSleepers-Encoders/* | MIT | [Same license as source code](https://github.com/expertsleepersltd/vcvrack-encoders/issues/3) |
| Extratone/* | GPL-3.0-or-later | [Same license as source code](https://github.com/EaterOfSheep/Extratone/issues/7) |
| FehlerFabrik/* | GPL-3.0-or-later | No artwork specific license provided, see [FehlerFabrik#17](https://github.com/RCameron93/FehlerFabrik/issues/17) |
| Fundamental/* | GPL-3.0-or-later | Same license as source code |
| GlueTheGiant/* | GPL-3.0-or-later | Same license as source code |
| GlueTheGiant/fonts/DSEG7-* | OFL-1.1-RFN | |
| GoodSheperd/* | GPL-3.0-or-later | No artwork specific license provided |
| GrandeModular/* | CC-BY-NC-ND-4.0 | |
| HamptonHarmonics/* | MIT | No artwork specific license provided |
| HamptonHarmonics/PixelOperator.ttf | CC0-1.0 | |
| HetrickCV/* | CC0-1.0 | |
| ihtsyn/* | GPL-3.0-or-later | [Same license as source code](https://github.com/nysthi/nysthi/issues/379#issuecomment-1027873902) |
| ImpromptuModular/* | CC-BY-NC-ND-4.0 | |
| ImpromptuModular/res/comp/complib/* | CC-BY-NC-4.0 | |
| JW-Modules/* | BSD-3-Clause | No artwork specific license provided |
| JW-Modules/DejaVuSansMono.ttf | Bitstream-Vera | Unused in Cardinal |
| kocmoc/* | GPL-3.0-or-later | No artwork specific license provided |
| LifeFormModular/* | MIT | No artwork specific license provided |
| LilacLoop/* | GPL-3.0-or-later | No artwork specific license provided |
| LittleUtils/* | EUPL-1.2 | Same license as source code |
| LittleUtils/fonts/CooperHewitt-*.ttf | OFL-1.1-RFN | |
| LittleUtils/fonts/Overpass-*.ttf | OFL-1.1-RFN | |
@@ -138,21 +170,40 @@ Below is a list of artwork licenses from plugins
| LyraeModules/* | CC-BY-NC-SA-4.0 | |
| MindMeld/* | CC-BY-NC-ND-4.0 | |
| MindMeld/fonts/RobotoCondensed-*.ttf | Apache-2.0 | |
| ML_modules/* | BSD-3-Clause | No artwork specific license provided |
| ML_modules/DejaVuSansMono.ttf | Bitstream-Vera | |
| ML_modules/Segment7Standard.ttf | OFL-1.1-RFN | |
| MockbaModular/* | MIT | No artwork specific license provided |
| Mog/* | CC0-1.0 | |
| Mog/components/* | CC-BY-NC-4.0 | |
| Mog/Exo2-BoldItalic.ttf | OFL-1.1-RFN | |
| mscHack/* | BSD-3-Clause | No artwork specific license provided, see [mschack#108](https://github.com/mschack/VCV-Rack-Plugins/issues/108) |
| MSM/* | MIT | No artwork specific license provided |
| MSM/Fonts/DejaVuSansMono.ttf | Bitstream-Vera | |
| MSM/Fonts/Segment7Standard.ttf | OFL-1.1-RFN | |
| MSM/Fonts/Sudo.ttf | OFL-1.1-no-RFN | |
| nonlinearcircuits/* | CC0-1.0 | No artwork specific license provided |
| nonlinearcircuits/Audiowide-Regular.ttf | OFL-1.1-RFN | |
| Orbits/* | CC-BY-NC-ND-4.0 | |
| Orbits/fonts/ShareTechMono-Regular.ttf | OFL-1.1-RFN | |
| ParableInstruments/* | Custom | Copyright © Alex Brandt, [used and distributed with permission](https://github.com/adbrant/ArableInstruments/issues/21) |
| PathSet/* | GPL-3.0-or-later | No artwork specific license provided |
| PinkTrombone/* | GPL-3.0-or-later | No artwork specific license provided |
| Prism/* | CC-BY-SA-4.0 | |
| Prism/RobotoCondensed-Regular.ttf | Apache-2.0 | |
| Rackwindows/* | MIT | [Same license as source code](https://github.com/n0jo/rackwindows/issues/15) |
| repelzen/* | CC-BY-SA-4.0 | |
| substation-opensource/* | BSD-3-Clause-Attribution | No artwork specific license provided |
| sonusmodular/* | GPL-3.0-or-later | [Same license as source code](https://gitlab.com/sonusdept/sonusmodular/-/issues/14) |
| stocaudio/* | GPL-3.0-or-later | No artwork specific license provided |
| unless_modules/* | CC-BY-NC-ND-4.0 | |
| unless_modules/font/CuteFont-Regular.ttf| OFL-1.1 | |
| unless_modules/font/Terminus.ttf | GPL-2.0-or-later | [Starting from v4.32, font license is OFL-1.1](https://files.ax86.net/terminus-ttf/#license) |
| ValleyAudio/* | GPL-3.0-or-later | [Same license as source code](https://github.com/ValleyAudio/ValleyRackFree/issues/73) |
| ValleyAudio/din1451alt.ttf | CC-BY-3.0-DE | |
| ValleyAudio/DSEG14Classic-*.ttf | OFL-1.1-RFN | |
| ValleyAudio/ShareTechMono-*.ttf | OFL-1.1-RFN | |
| voxglitch/* | GPL-3.0-or-later | No artwork specific license provided |
| voxglitch/ShareTechMono-Regular.ttf | OFL-1.1-RFN | |
| ZetaCarinaeModules/* | GPL-3.0-or-later | [Same license as source code](https://github.com/mhampton/ZetaCarinaeModules/issues/8) |
| ZZC/* | CC-BY-NC-SA-4.0 | |
| ZZC/panels/* | CC-BY-NC-SA-4.0 | NOTE: The ZZC Logo is Copyright (c) 2019 Sergey Ukolov and cannot be used in derivative works; Cardinal's use does not officially constitute derivative work. |


+ 1
- 1
dpf

@@ -1 +1 @@
Subproject commit af042cb682d693e9ce5b87a145e6c704be31adf8
Subproject commit 68de732eecbd1d8febf94e15558c5adaa45dfa9b

+ 14
- 1
include/common.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -53,6 +53,19 @@
#include <functional>
#define USING_CARDINAL_NOT_RACK

// OS separator macros
#ifdef ARCH_WIN
# define CARDINAL_OS_SEP '\\'
# define CARDINAL_OS_SEP_STR "\\"
# define CARDINAL_OS_SPLIT ';'
# define CARDINAL_OS_SPLIT_STR ";"
#else
# define CARDINAL_OS_SEP '/'
# define CARDINAL_OS_SEP_STR "/"
# define CARDINAL_OS_SPLIT ':'
# define CARDINAL_OS_SPLIT_STR ":"
#endif

// opens a file browser, startDir and title can be null
// action is always triggered on close (path can be null), must be freed if not null
void async_dialog_filebrowser(bool saving, const char* startDir, const char* title,


plugins/substation-settings/Settings.cpp → include/componentlibrary.hpp View File

@@ -15,21 +15,17 @@
* For a full copy of the GNU General Public License see the LICENSE file.
*/

#include "../substation-opensource/src/Settings.hpp"
#pragma once

namespace slime {
namespace plugin {
namespace substation {
#define SCHEME_YELLOW SCHEME_YELLOW_OldVCV
#include_next "componentlibrary.hpp"
#undef SCHEME_YELLOW

PluginSettings::PluginSettings(void) {}
PluginSettings::~PluginSettings(void) {}
void PluginSettings::save() {}
void PluginSettings::load() {}
void PluginSettings::appendContextMenu(rack::ui::Menu* menu) {}
void PluginSettings::updateCableColors(const bool& value) {}
namespace rack {
namespace componentlibrary {

PluginSettings settings;
// Yellow? What's that?
static const NVGcolor SCHEME_YELLOW = nvgRGBf(0.76f, 0.11f, 0.22f);

} // namespace substation
} // namespace plugin
} // namespace slime
}
}

+ 164
- 0
include/dsp/fir.hpp View File

@@ -0,0 +1,164 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the LICENSE file.
*/

/**
* This file is an edited version of VCVRack's dsp/fir.hpp
* Copyright (C) 2016-2021 VCV.
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*/

#pragma once

#include <pffft.h>

#include <dsp/common.hpp>


namespace rack {
namespace dsp {


/** Performs a direct sum convolution */
inline float convolveNaive(const float* in, const float* kernel, int len) {
float y = 0.f;
for (int i = 0; i < len; i++) {
y += in[len - 1 - i] * kernel[i];
}
return y;
}

/** Computes the impulse response of a boxcar lowpass filter */
inline void boxcarLowpassIR(float* out, int len, float cutoff = 0.5f) {
for (int i = 0; i < len; i++) {
float t = i - (len - 1) / 2.f;
out[i] = 2 * cutoff * sinc(2 * cutoff * t);
}
}


struct RealTimeConvolver {
// `kernelBlocks` number of contiguous FFT blocks of size `blockSize`
// indexed by [i * blockSize*2 + j]
float* kernelFfts = NULL;
float* inputFfts = NULL;
float* outputTail = NULL;
float* tmpBlock = NULL;
size_t blockSize;
size_t kernelBlocks = 0;
size_t inputPos = 0;
PFFFT_Setup* pffft;

/** `blockSize` is the size of each FFT block. It should be >=32 and a power of 2. */
RealTimeConvolver(size_t blockSize) {
this->blockSize = blockSize;
pffft = pffft_new_setup(blockSize * 2, PFFFT_REAL);
outputTail = (float*) pffft_aligned_malloc(sizeof(float) * blockSize);
std::memset(outputTail, 0, blockSize * sizeof(float));
tmpBlock = (float*) pffft_aligned_malloc(sizeof(float) * blockSize * 2);
std::memset(tmpBlock, 0, blockSize * 2 * sizeof(float));
}

~RealTimeConvolver() {
setKernel(NULL, 0);
pffft_aligned_free(outputTail);
pffft_aligned_free(tmpBlock);
pffft_destroy_setup(pffft);
}

void setKernel(const float* kernel, size_t length) {
// Clear existing kernel
if (kernelFfts) {
pffft_aligned_free(kernelFfts);
kernelFfts = NULL;
}
if (inputFfts) {
pffft_aligned_free(inputFfts);
inputFfts = NULL;
}
kernelBlocks = 0;
inputPos = 0;

if (kernel && length > 0) {
// Round up to the nearest factor of `blockSize`
kernelBlocks = (length - 1) / blockSize + 1;

// Allocate blocks
kernelFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize * 2 * kernelBlocks);
inputFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize * 2 * kernelBlocks);
std::memset(inputFfts, 0, sizeof(float) * blockSize * 2 * kernelBlocks);

for (size_t i = 0; i < kernelBlocks; i++) {
// Pad each block with zeros
std::memset(tmpBlock, 0, sizeof(float) * blockSize * 2);
size_t len = std::min((int) blockSize, (int)(length - i * blockSize));
std::memcpy(tmpBlock, &kernel[i * blockSize], sizeof(float)*len);
// Compute fft
pffft_transform(pffft, tmpBlock, &kernelFfts[blockSize * 2 * i], NULL, PFFFT_FORWARD);
}
}
}

/** Applies reverb to input
input and output must be of size `blockSize`
*/
void processBlock(const float* input, float* output) {
if (kernelBlocks == 0) {
std::memset(output, 0, sizeof(float) * blockSize);
return;
}

// Step input position
inputPos = (inputPos + 1) % kernelBlocks;
// Pad block with zeros
std::memset(tmpBlock, 0, sizeof(float) * blockSize * 2);
std::memcpy(tmpBlock, input, sizeof(float) * blockSize);
// Compute input fft
pffft_transform(pffft, tmpBlock, &inputFfts[blockSize * 2 * inputPos], NULL, PFFFT_FORWARD);
// Create output fft
std::memset(tmpBlock, 0, sizeof(float) * blockSize * 2);
// convolve input fft by kernel fft
// Note: This is the CPU bottleneck loop
for (size_t i = 0; i < kernelBlocks; i++) {
size_t pos = (inputPos - i + kernelBlocks) % kernelBlocks;
pffft_zconvolve_accumulate(pffft, &kernelFfts[blockSize * 2 * i], &inputFfts[blockSize * 2 * pos], tmpBlock, 1.f);
}
// Compute output
pffft_transform(pffft, tmpBlock, tmpBlock, NULL, PFFFT_BACKWARD);
// Add block tail from last output block
for (size_t i = 0; i < blockSize; i++) {
tmpBlock[i] += outputTail[i];
}
// Copy output block to output
float scale = 1.f / (blockSize * 2);
for (size_t i = 0; i < blockSize; i++) {
// Scale based on FFT
output[i] = tmpBlock[i] * scale;
}
// Set tail
for (size_t i = 0; i < blockSize; i++) {
outputTail[i] = tmpBlock[i + blockSize];
}
}
};


} // namespace dsp
} // namespace rack

+ 1
- 1
include/engine/Port.hpp View File

@@ -48,7 +48,7 @@ struct Port {
/** Voltage of the port. */
/** NOTE alignas is required in order to allow SSE usage.
Consecutive data (like in a vector) would otherwise pack Ports in a way that breaks SSE. */
union alignas(PORT_MAX_CHANNELS) {
union alignas(32) {
/** Unstable API. Use getVoltage() and setVoltage() instead. */
float voltages[PORT_MAX_CHANNELS] = {};
/** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */


+ 3
- 1
include/mingw-compat/Shlobj.h View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -15,4 +15,6 @@
* For a full copy of the GNU General Public License see the LICENSE file.
*/

#pragma once

#include <shlobj.h>

+ 3
- 1
include/mingw-compat/Shlwapi.h View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -15,4 +15,6 @@
* For a full copy of the GNU General Public License see the LICENSE file.
*/

#pragma once

#include <shlwapi.h>

+ 25
- 0
include/mingw-compat/future View File

@@ -0,0 +1,25 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the LICENSE file.
*/

#pragma once
#include_next <future>
#include "mingw.future.h"

#undef IN
#undef OUT
#undef far
#undef near

+ 374
- 0
include/simd/Vector.hpp View File

@@ -0,0 +1,374 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the LICENSE file.
*/

/**
* This file is an edited version of VCVRack's simd/Vector.hpp
* Copyright (C) 2016-2021 VCV.
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*/

#pragma once

#include <cstring>
#include <pmmintrin.h>


namespace rack {


/** Abstraction of aligned types for SIMD computation
*/
namespace simd {


/** Generic class for vector types.

This class is designed to be used just like you use scalars, with extra features for handling bitwise logic, conditions, loading, and storing.

Example:

float a[4], b[4];
float_4 a = float_4::load(in);
float_4 b = 2.f * a / (1 - a);
b *= sin(2 * M_PI * a);
b.store(out);
*/
template <typename TYPE, int SIZE>
struct Vector;


/** Wrapper for `__m128` representing an aligned vector of 4 single-precision float values.
*/
template <>
struct Vector<float, 4> {
using type = float;
constexpr static int size = 4;

/** NOTE alignas is required in order to allow SSE usage. */
union alignas(32) {
__m128 v;
/** Accessing this array of scalars is slow and defeats the purpose of vectorizing.
*/
float s[4];
};

/** Constructs an uninitialized vector. */
Vector() = default;

/** Constructs a vector from a native `__m128` type. */
Vector(__m128 v) : v(v) {}

/** Constructs a vector with all elements set to `x`. */
Vector(float x) {
v = _mm_set1_ps(x);
}

/** Constructs a vector from four scalars. */
Vector(float x1, float x2, float x3, float x4) {
v = _mm_setr_ps(x1, x2, x3, x4);
}

/** Returns a vector with all 0 bits. */
static Vector zero() {
return Vector(_mm_setzero_ps());
}

/** Returns a vector with all 1 bits. */
static Vector mask() {
return Vector(_mm_castsi128_ps(_mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())));
}

/** Reads an array of 4 values.
On little-endian machines (e.g. x86_64), the order is reversed, so `x[0]` corresponds to `vector.s[3]`.
*/
static Vector load(const float* x) {
/*
My benchmarks show that _mm_loadu_ps() performs equally as fast as _mm_load_ps() when data is actually aligned.
This post seems to agree. https://stackoverflow.com/a/20265193/272642
I therefore use _mm_loadu_ps() for generality, so you can load unaligned arrays using the same function (although load aligned arrays if you can for best performance).
*/
return Vector(_mm_loadu_ps(x));
}

/** Writes an array of 4 values.
On little-endian machines (e.g. x86_64), the order is reversed, so `x[0]` corresponds to `vector.s[3]`.
*/
void store(float* x) {
_mm_storeu_ps(x, v);
}

/** Accessing vector elements individually is slow and defeats the purpose of vectorizing.
However, this operator is convenient when writing simple serial code in a non-bottlenecked section.
*/
float& operator[](int i) {
return s[i];
}
const float& operator[](int i) const {
return s[i];
}

// Conversions
Vector(Vector<int32_t, 4> a);
// Casts
static Vector cast(Vector<int32_t, 4> a);
};


template <>
struct Vector<int32_t, 4> {
using type = int32_t;
constexpr static int size = 4;

/** NOTE alignas is required in order to allow SSE usage. */
union alignas(32) {
__m128i v;
int32_t s[4];
};

Vector() = default;
Vector(__m128i v) : v(v) {}
Vector(int32_t x) {
v = _mm_set1_epi32(x);
}
Vector(int32_t x1, int32_t x2, int32_t x3, int32_t x4) {
v = _mm_setr_epi32(x1, x2, x3, x4);
}
static Vector zero() {
return Vector(_mm_setzero_si128());
}
static Vector mask() {
return Vector(_mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
}
static Vector load(const int32_t* x) {
// HACK
// Use _mm_loadu_si128() because GCC doesn't support _mm_loadu_si32()
return Vector(_mm_loadu_si128((const __m128i*) x));
}
void store(int32_t* x) {
// HACK
// Use _mm_storeu_si128() because GCC doesn't support _mm_storeu_si32()
_mm_storeu_si128((__m128i*) x, v);
}
int32_t& operator[](int i) {
return s[i];
}
const int32_t& operator[](int i) const {
return s[i];
}
Vector(Vector<float, 4> a);
static Vector cast(Vector<float, 4> a);
};


// Conversions and casts


inline Vector<float, 4>::Vector(Vector<int32_t, 4> a) {
v = _mm_cvtepi32_ps(a.v);
}

inline Vector<int32_t, 4>::Vector(Vector<float, 4> a) {
v = _mm_cvttps_epi32(a.v);
}

inline Vector<float, 4> Vector<float, 4>::cast(Vector<int32_t, 4> a) {
return Vector(_mm_castsi128_ps(a.v));
}

inline Vector<int32_t, 4> Vector<int32_t, 4>::cast(Vector<float, 4> a) {
return Vector(_mm_castps_si128(a.v));
}


// Operator overloads


/** `a @ b` */
#define DECLARE_VECTOR_OPERATOR_INFIX(t, s, operator, func) \
inline Vector<t, s> operator(const Vector<t, s>& a, const Vector<t, s>& b) { \
return Vector<t, s>(func(a.v, b.v)); \
}

/** `a @= b` */
#define DECLARE_VECTOR_OPERATOR_INCREMENT(t, s, operator, opfunc) \
inline Vector<t, s>& operator(Vector<t, s>& a, const Vector<t, s>& b) { \
return a = opfunc(a, b); \
}

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator+, _mm_add_ps)
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator+, _mm_add_epi32)

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator-, _mm_sub_ps)
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator-, _mm_sub_epi32)

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator*, _mm_mul_ps)
// DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator*, NOT AVAILABLE IN SSE3)

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator/, _mm_div_ps)
// DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator/, NOT AVAILABLE IN SSE3)

/* Use these to apply logic, bit masks, and conditions to elements.
Boolean operators on vectors give 0x00000000 for false and 0xffffffff for true, for each vector element.

Examples:

Subtract 1 from value if greater than or equal to 1.

x -= (x >= 1.f) & 1.f;
*/
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator^, _mm_xor_ps)
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator^, _mm_xor_si128)

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator&, _mm_and_ps)
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator&, _mm_and_si128)

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator|, _mm_or_ps)
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator|, _mm_or_si128)

DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator+=, operator+)
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator+=, operator+)

DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator-=, operator-)
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator-=, operator-)

DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator*=, operator*)
// DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator*=, NOT AVAILABLE IN SSE3)

DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator/=, operator/)
// DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator/=, NOT AVAILABLE IN SSE3)

DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator^=, operator^)
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator^=, operator^)

DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator&=, operator&)
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator&=, operator&)

DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator|=, operator|)
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator|=, operator|)

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator==, _mm_cmpeq_ps)
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator==, _mm_cmpeq_epi32)

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator>=, _mm_cmpge_ps)
inline Vector<int32_t, 4> operator>=(const Vector<int32_t, 4>& a, const Vector<int32_t, 4>& b) {
return Vector<int32_t, 4>(_mm_cmpgt_epi32(a.v, b.v)) ^ Vector<int32_t, 4>::mask();
}

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator>, _mm_cmpgt_ps)
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator>, _mm_cmpgt_epi32)

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator<=, _mm_cmple_ps)
inline Vector<int32_t, 4> operator<=(const Vector<int32_t, 4>& a, const Vector<int32_t, 4>& b) {
return Vector<int32_t, 4>(_mm_cmplt_epi32(a.v, b.v)) ^ Vector<int32_t, 4>::mask();
}

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator<, _mm_cmplt_ps)
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator<, _mm_cmplt_epi32)

DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator!=, _mm_cmpneq_ps)
inline Vector<int32_t, 4> operator!=(const Vector<int32_t, 4>& a, const Vector<int32_t, 4>& b) {
return Vector<int32_t, 4>(_mm_cmpeq_epi32(a.v, b.v)) ^ Vector<int32_t, 4>::mask();
}

/** `+a` */
inline Vector<float, 4> operator+(const Vector<float, 4>& a) {
return a;
}
inline Vector<int32_t, 4> operator+(const Vector<int32_t, 4>& a) {
return a;
}

/** `-a` */
inline Vector<float, 4> operator-(const Vector<float, 4>& a) {
return 0.f - a;
}
inline Vector<int32_t, 4> operator-(const Vector<int32_t, 4>& a) {
return 0 - a;
}

/** `++a` */
inline Vector<float, 4>& operator++(Vector<float, 4>& a) {
return a += 1.f;
}
inline Vector<int32_t, 4>& operator++(Vector<int32_t, 4>& a) {
return a += 1;
}

/** `--a` */
inline Vector<float, 4>& operator--(Vector<float, 4>& a) {
return a -= 1.f;
}
inline Vector<int32_t, 4>& operator--(Vector<int32_t, 4>& a) {
return a -= 1;
}

/** `a++` */
inline Vector<float, 4> operator++(Vector<float, 4>& a, int) {
Vector<float, 4> b = a;
++a;
return b;
}
inline Vector<int32_t, 4> operator++(Vector<int32_t, 4>& a, int) {
Vector<int32_t, 4> b = a;
++a;
return b;
}

/** `a--` */
inline Vector<float, 4> operator--(Vector<float, 4>& a, int) {
Vector<float, 4> b = a;
--a;
return b;
}
inline Vector<int32_t, 4> operator--(Vector<int32_t, 4>& a, int) {
Vector<int32_t, 4> b = a;
--a;
return b;
}

/** `~a` */
inline Vector<float, 4> operator~(const Vector<float, 4>& a) {
return a ^ Vector<float, 4>::mask();
}
inline Vector<int32_t, 4> operator~(const Vector<int32_t, 4>& a) {
return a ^ Vector<int32_t, 4>::mask();
}

/** `a << b` */
inline Vector<int32_t, 4> operator<<(const Vector<int32_t, 4>& a, const int& b) {
return Vector<int32_t, 4>(_mm_slli_epi32(a.v, b));
}

/** `a >> b` */
inline Vector<int32_t, 4> operator>>(const Vector<int32_t, 4>& a, const int& b) {
return Vector<int32_t, 4>(_mm_srli_epi32(a.v, b));
}


// Typedefs


using float_4 = Vector<float, 4>;
using int32_4 = Vector<int32_t, 4>;


} // namespace simd
} // namespace rack

+ 246
- 15
jucewrapper/CMakeLists.txt View File

@@ -1,19 +1,124 @@
cmake_minimum_required(VERSION 3.15)
project(Cardinal VERSION 0.0.0)
project(Cardinal VERSION 22.03)

add_subdirectory(JUCE)

# Config

set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)

# Define static libs

add_library(dgl STATIC IMPORTED)
set_property(TARGET dgl PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../dpf/build/libdgl-opengl.a")

add_library(carla_host_plugin STATIC IMPORTED)
set_property(TARGET carla_host_plugin PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/plugin/Release/carla-host-plugin.cpp.o")

add_library(carla_engine_plugin STATIC IMPORTED)
set_property(TARGET carla_engine_plugin PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/carla_engine_plugin.a")

add_library(carla_plugin STATIC IMPORTED)
set_property(TARGET carla_plugin PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/carla_plugin.a")

add_library(native_plugins STATIC IMPORTED)
set_property(TARGET native_plugins PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/native-plugins.a")

add_library(audio_decoder STATIC IMPORTED)
set_property(TARGET audio_decoder PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/audio_decoder.a")

add_library(jackbridge STATIC IMPORTED)
set_property(TARGET jackbridge PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/jackbridge.min.a")

add_library(lilv STATIC IMPORTED)
set_property(TARGET lilv PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/lilv.a")

add_library(rtmempool STATIC IMPORTED)
set_property(TARGET rtmempool PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/rtmempool.a")

add_library(sfzero STATIC IMPORTED)
set_property(TARGET sfzero PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/sfzero.a")

add_library(water STATIC IMPORTED)
set_property(TARGET water PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/water.a")

add_library(zita_resampler STATIC IMPORTED)
set_property(TARGET zita_resampler PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../carla/build/modules/Release/zita-resampler.a")

add_library(sCardinalFX STATIC IMPORTED)
set_property(TARGET sCardinalFX PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../bin/CardinalFX.a")

add_library(sCardinalSynth STATIC IMPORTED)
set_property(TARGET sCardinalSynth PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../bin/CardinalSynth.a")

add_library(sPlugins STATIC IMPORTED)
set_property(TARGET sPlugins PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../plugins/plugins.a")

add_library(sRack STATIC IMPORTED)
set_property(TARGET sRack PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../src/rack.a")

add_library(libarchive STATIC IMPORTED)
if (WIN32)
set_property(TARGET libarchive PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../src/Rack/dep/lib/libarchive_static.a")
else (WIN32)
set_property(TARGET libarchive PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../src/Rack/dep/lib/libarchive.a")
endif (WIN32)

add_library(libjansson STATIC IMPORTED)
set_property(TARGET libjansson PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../src/Rack/dep/lib/libjansson.a")

add_library(libquickjs STATIC IMPORTED)
set_property(TARGET libquickjs PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../src/Rack/dep/lib/libquickjs.a")

add_library(libsamplerate STATIC IMPORTED)
set_property(TARGET libsamplerate PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../src/Rack/dep/lib/libsamplerate.a")

add_library(libspeexdsp STATIC IMPORTED)
set_property(TARGET libspeexdsp PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../src/Rack/dep/lib/libspeexdsp.a")

add_library(libzstd STATIC IMPORTED)
set_property(TARGET libzstd PROPERTY IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../src/Rack/dep/lib/libzstd.a")

# dependencies

find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBLO REQUIRED liblo)
pkg_check_modules(SNDFILE REQUIRED sndfile)

if (APPLE)
set(EXTRA_LIBS "-lz")
set(GL_LIBRARIES "-framework OpenGL")
set(PLUGIN_FORMATS AU)
else (APPLE)
pkg_check_modules(DBUS REQUIRED dbus-1)
pkg_check_modules(GL REQUIRED gl)
pkg_check_modules(X11 REQUIRED x11)
pkg_check_modules(XCURSOR REQUIRED xcursor)
pkg_check_modules(XEXT REQUIRED xext)
pkg_check_modules(XRANDR REQUIRED xrandr)
set(EXTRA_LIBS "-lrt")
set(STATIC_LIBS_START "-Wl,--whole-archive")
set(STATIC_LIBS_END "-Wl,--no-whole-archive")
set(PLUGIN_FORMATS Standalone VST3)
endif (APPLE)

# FX variant

juce_add_plugin(CardinalFX
IS_SYNTH FALSE
NEEDS_MIDI_INPUT FALSE
NEEDS_MIDI_OUTPUT FALSE
IS_MIDI_EFFECT FALSE
AU_MAIN_TYPE kAudioUnitType_MusicEffect
COMPANY_COPYRIGHT "GPL-3.0-or-later"
COMPANY_NAME "DISTRHO"
COMPANY_WEBSITE "https://github.com/DISTRHO/Cardinal"
DESCRIPTION "Virtual modular synthesizer plugin"
EDITOR_WANTS_KEYBOARD_FOCUS TRUE
FORMATS ${PLUGIN_FORMATS}
IS_MIDI_EFFECT FALSE
IS_SYNTH FALSE
NEEDS_MIDI_INPUT TRUE
NEEDS_MIDI_OUTPUT TRUE
PLUGIN_CODE DcnF
PLUGIN_MANUFACTURER_CODE Dstr
PLUGIN_CODE dCnF
FORMATS VST3 AU
PRODUCT_NAME "CardinalFX")

target_sources(CardinalFX
@@ -22,26 +127,152 @@ target_sources(CardinalFX

target_include_directories(CardinalFX
PRIVATE
.
../dpf/distrho)
../dpf/distrho
../src/CardinalFX)

target_compile_definitions(CardinalFX
PUBLIC
JucePlugin_PreferredChannelConfigurations=2,2
JUCE_CHECK_MEMORY_LEAKS=0
JUCE_DISABLE_NATIVE_FILECHOOSERS=1
JUCE_DISPLAY_SPLASH_SCREEN=0
JUCE_MODAL_LOOPS_PERMITTED=0
JUCE_USE_CURL=0
JUCE_USE_FLAC=0
JUCE_USE_OGGVORBIS=0
JUCE_USE_XINERAMA=0
JUCE_VST3_CAN_REPLACE_VST2=0
JUCE_ALSA=1
JUCE_DIRECTSOUND=0
JUCE_JACK=1
JUCE_WASAPI=0
JUCE_WEB_BROWSER=0)

target_link_options(CardinalFX
PRIVATE
"-l/Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_Cardinal/bin/CardinalFX.so"
"-Wl,-rpath,."
)

target_link_libraries(CardinalFX
PRIVATE
juce::juce_audio_utils
${STATIC_LIBS_START}
sCardinalFX
sPlugins
sRack
carla_host_plugin
carla_engine_plugin
carla_plugin
native_plugins
audio_decoder
jackbridge
lilv
rtmempool
sfzero
water
zita_resampler
dgl
libarchive
libjansson
libquickjs
libsamplerate
libspeexdsp
libzstd
${STATIC_LIBS_END}
${GL_LIBRARIES}
${DBUS_LIBRARIES}
-L${LIBLO_LIBRARY_DIRS}
${LIBLO_LIBRARIES}
${SNDFILE_LIBRARIES}
${X11_LIBRARIES}
${XCURSOR_LIBRARIES}
${XEXT_LIBRARIES}
${XRANDR_LIBRARIES}
${EXTRA_LIBS}
-lmagic
PUBLIC
juce::juce_recommended_config_flags
juce::juce_recommended_lto_flags
juce::juce_recommended_warning_flags)

# Synth variant

juce_add_plugin(CardinalSynth
AU_MAIN_TYPE kAudioUnitType_MusicDevice
COMPANY_COPYRIGHT "GPL-3.0-or-later"
COMPANY_NAME "DISTRHO"
COMPANY_WEBSITE "https://github.com/DISTRHO/Cardinal"
DESCRIPTION "Virtual modular synthesizer plugin"
EDITOR_WANTS_KEYBOARD_FOCUS TRUE
FORMATS ${PLUGIN_FORMATS}
IS_MIDI_EFFECT FALSE
IS_SYNTH TRUE
NEEDS_MIDI_INPUT TRUE
NEEDS_MIDI_OUTPUT TRUE
PLUGIN_CODE DcnS
PLUGIN_MANUFACTURER_CODE Dstr
PRODUCT_NAME "CardinalSynth")

target_sources(CardinalSynth
PRIVATE
CardinalWrapper.cpp)

target_include_directories(CardinalSynth
PRIVATE
../dpf/distrho
../src/CardinalSynth)

target_compile_definitions(CardinalSynth
PUBLIC
JucePlugin_PreferredChannelConfigurations=0,2
JUCE_CHECK_MEMORY_LEAKS=0
JUCE_DISABLE_NATIVE_FILECHOOSERS=1
JUCE_DISPLAY_SPLASH_SCREEN=0
JUCE_MODAL_LOOPS_PERMITTED=0
JUCE_USE_CURL=0
JUCE_USE_FLAC=0
JUCE_USE_OGGVORBIS=0
JUCE_USE_XINERAMA=0
JUCE_VST3_CAN_REPLACE_VST2=0
JUCE_ALSA=1
JUCE_DIRECTSOUND=0
JUCE_JACK=1
JUCE_WASAPI=0
JUCE_WEB_BROWSER=0)

target_link_libraries(CardinalSynth
PRIVATE
juce::juce_audio_utils
${STATIC_LIBS_START}
sCardinalSynth
sPlugins
sRack
carla_host_plugin
carla_engine_plugin
carla_plugin
native_plugins
audio_decoder
jackbridge
lilv
rtmempool
sfzero
water
zita_resampler
dgl
libarchive
libjansson
libquickjs
libsamplerate
libspeexdsp
libzstd
${STATIC_LIBS_END}
${GL_LIBRARIES}
${DBUS_LIBRARIES}
-L${LIBLO_LIBRARY_DIRS}
${LIBLO_LIBRARIES}
${SNDFILE_LIBRARIES}
${X11_LIBRARIES}
${XCURSOR_LIBRARIES}
${XEXT_LIBRARIES}
${XRANDR_LIBRARIES}
${EXTRA_LIBS}
-lmagic
PUBLIC
juce::juce_recommended_config_flags
juce::juce_recommended_lto_flags
juce::juce_recommended_warning_flags)

+ 519
- 48
jucewrapper/CardinalWrapper.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -17,46 +17,62 @@

#include <juce_audio_processors/juce_audio_processors.h>

#include "DistrhoPlugin.hpp"
#include "DistrhoUI.hpp"

DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin();
#define createPlugin ::createSharedPlugin
#define createPlugin createStaticPlugin
#include "src/DistrhoPluginInternal.hpp"
#include "src/DistrhoUIInternal.hpp"

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

class ParameterForDPF : public juce::AudioProcessorParameter
class ParameterFromDPF : public juce::AudioProcessorParameter
{
PluginExporter& plugin;
const ParameterEnumerationValues& enumValues;
const ParameterRanges& ranges;
const uint32_t hints;
const uint index;
bool* const updatedPtr;
mutable juce::StringArray dpfValueStrings;

public:
ParameterForDPF(PluginExporter& plugin_, const uint index_)
ParameterFromDPF(PluginExporter& plugin_, const uint index_, bool* const updatedPtr_)
: plugin(plugin_),
index(index_) {}
enumValues(plugin_.getParameterEnumValues(index_)),
ranges(plugin_.getParameterRanges(index_)),
hints(plugin_.getParameterHints(index_)),
index(index_),
updatedPtr(updatedPtr_) {}

void setValueNotifyingHostFromDPF(const float newValue)
{
setValueNotifyingHost(ranges.getNormalizedValue(newValue));
*updatedPtr = false;
}

protected:
float getValue() const override
{
return plugin.getParameterRanges(index).getNormalizedValue(plugin.getParameterValue(index));
return ranges.getNormalizedValue(plugin.getParameterValue(index));
}

void setValue(const float newValue) override
{
plugin.setParameterValue(index, plugin.getParameterRanges(index).getUnnormalizedValue(newValue));
*updatedPtr = true;
plugin.setParameterValue(index, ranges.getUnnormalizedValue(newValue));
}

float getDefaultValue() const override
{
return plugin.getParameterDefault(index);
return ranges.getNormalizedValue(plugin.getParameterDefault(index));
}

juce::String getName(int) const override
juce::String getName(const int maximumStringLength) const override
{
return plugin.getParameterName(index).buffer();
if (maximumStringLength <= 0)
return juce::String(plugin.getParameterName(index).buffer());

return juce::String(plugin.getParameterName(index).buffer(), static_cast<size_t>(maximumStringLength));
}

juce::String getLabel() const override
@@ -64,40 +80,219 @@ protected:
return plugin.getParameterUnit(index).buffer();
}

float getValueForText(const juce::String& text) const override
int getNumSteps() const override
{
return 0.0f;
}
};
if (hints & kParameterIsBoolean)
return 2;

// -----------------------------------------------------------------------------------------------------------
if (enumValues.restrictedMode)
return enumValues.count;

class CardinalWrapperProcessor : public juce::AudioProcessor
{
PluginExporter plugin;
if (hints & kParameterIsInteger)
return ranges.max - ranges.min;

static bool writeMidiCb(void* ptr, const MidiEvent& midiEvent)
return juce::AudioProcessorParameter::getNumSteps();
}

bool isDiscrete() const override
{
if (hints & (kParameterIsBoolean|kParameterIsInteger))
return true;

if (enumValues.restrictedMode)
return true;

return false;
}

static bool requestParameterValueChangeCb(void* ptr, uint32_t index, float value)
bool isBoolean() const override
{
return false;
return (hints & kParameterIsBoolean) != 0x0;
}

juce::String getText(const float normalizedValue, const int maximumStringLength) const override
{
float value = ranges.getUnnormalizedValue(normalizedValue);

if (hints & kParameterIsBoolean)
{
const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f;
value = value > midRange ? ranges.max : ranges.min;
}
else if (hints & kParameterIsInteger)
{
value = std::round(value);
}

if (enumValues.restrictedMode)
{
for (uint32_t i=0; i < enumValues.count; ++i)
{
if (d_isEqual(enumValues.values[i].value, value))
{
if (maximumStringLength <= 0)
return juce::String(enumValues.values[i].label);

return juce::String(enumValues.values[i].label, static_cast<size_t>(maximumStringLength));
}
}
}

juce::String text;
if (hints & kParameterIsInteger)
text = juce::String(static_cast<int>(value));
else
text = juce::String(value);

if (maximumStringLength <= 0)
return text;

return juce::String(text.toRawUTF8(), static_cast<size_t>(maximumStringLength));
}

float getValueForText(const juce::String& text) const override
{
if (enumValues.restrictedMode)
{
for (uint32_t i=0; i < enumValues.count; ++i)
{
if (text == enumValues.values[i].label.buffer())
return ranges.getNormalizedValue(enumValues.values[i].value);
}
}

float value;
if (hints & kParameterIsInteger)
value = std::atoi(text.toRawUTF8());
else
value = std::atof(text.toRawUTF8());

return ranges.getFixedAndNormalizedValue(value);
}

bool isAutomatable() const override
{
return (hints & kParameterIsAutomatable) != 0x0;
}

juce::String getCurrentValueAsText() const override
{
const float value = plugin.getParameterValue(index);

if (enumValues.restrictedMode)
{
for (uint32_t i=0; i < enumValues.count; ++i)
{
if (d_isEqual(enumValues.values[i].value, value))
return juce::String(enumValues.values[i].label);
}
}

if (hints & kParameterIsInteger)
return juce::String(static_cast<int>(value));

return juce::String(value);
}

juce::StringArray getAllValueStrings() const override
{
if (dpfValueStrings.size() != 0)
return dpfValueStrings;

if (enumValues.restrictedMode)
{
for (uint32_t i=0; i < enumValues.count; ++i)
dpfValueStrings.add(enumValues.values[i].label.buffer());

return dpfValueStrings;
}

if (hints & kParameterIsBoolean)
{
if (hints & kParameterIsInteger)
{
dpfValueStrings.add(juce::String(static_cast<int>(ranges.min)));
dpfValueStrings.add(juce::String(static_cast<int>(ranges.max)));
}
else
{
dpfValueStrings.add(juce::String(ranges.min));
dpfValueStrings.add(juce::String(ranges.max));
}
}
else if (hints & kParameterIsInteger)
{
const int imin = static_cast<int>(ranges.min);
const int imax = static_cast<int>(ranges.max);

for (int i=imin; i<=imax; ++i)
dpfValueStrings.add(juce::String(i));
}

return dpfValueStrings;
}
};

// --------------------------------------------------------------------------------------------------------------------

// unused in cardinal
static constexpr const requestParameterValueChangeFunc nullRequestParameterValueChangeFunc = nullptr;

// only needed for headless builds, which this wrapper never builds for
static constexpr const updateStateValueFunc nullUpdateStateValueFunc = nullptr;

// DSP/processor implementation
class CardinalWrapperProcessor : public juce::AudioProcessor
{
friend class CardinalWrapperEditor;

PluginExporter plugin;
MidiEvent midiEvents[kMaxMidiEvents];
TimePosition timePosition;
const uint32_t parameterCount;

juce::AudioProcessorParameter* bypassParameter;
juce::MidiBuffer* currentMidiMessages;
bool* updatedParameters;

public:
CardinalWrapperProcessor()
: plugin(this, writeMidiCb, requestParameterValueChangeCb)
: plugin(this, writeMidiFunc, nullRequestParameterValueChangeFunc, nullUpdateStateValueFunc),
parameterCount(plugin.getParameterCount()),
bypassParameter(nullptr),
currentMidiMessages(nullptr),
updatedParameters(nullptr)
{
for (uint i=0; i<plugin.getParameterCount(); ++i)
addParameter(new ParameterForDPF(plugin, i));
if (const double sampleRate = getSampleRate())
if (sampleRate > 0.0)
plugin.setSampleRate(sampleRate);

if (const int samplesPerBlock = getBlockSize())
if (samplesPerBlock > 0)
plugin.setBufferSize(static_cast<uint32_t>(samplesPerBlock));

if (parameterCount != 0)
{
updatedParameters = new bool[parameterCount];
std::memset(updatedParameters, 0, sizeof(bool)*parameterCount);

for (uint i=0; i<parameterCount; ++i)
{
ParameterFromDPF* const param = new ParameterFromDPF(plugin, i, updatedParameters + i);
addParameter(param);

if (plugin.getParameterDesignation(i) == kParameterDesignationBypass)
bypassParameter = param;
}
}
}

~CardinalWrapperProcessor() override
{
delete[] updatedParameters;
}

protected:
const juce::String getName() const override
{
return plugin.getName();
@@ -108,11 +303,13 @@ public:
return juce::StringArray(plugin.getLabel());
}

void prepareToPlay(double sampleRate, int samplesPerBlock) override
void prepareToPlay(const double sampleRate, const int samplesPerBlock) override
{
DISTRHO_SAFE_ASSERT_RETURN(samplesPerBlock > 0,);

plugin.deactivateIfNeeded();
plugin.setSampleRate(sampleRate);
plugin.setBufferSize(samplesPerBlock);
plugin.setBufferSize(static_cast<uint32_t>(samplesPerBlock));
plugin.activate();
}

@@ -123,13 +320,100 @@ public:

void processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) override
{
const int numSamples = buffer.getNumSamples();
DISTRHO_SAFE_ASSERT_INT_RETURN(numSamples > 0, numSamples, midiMessages.clear());

uint32_t midiEventCount = 0;

for (const juce::MidiMessageMetadata midiMessage : midiMessages)
{
DISTRHO_SAFE_ASSERT_CONTINUE(midiMessage.numBytes > 0);
DISTRHO_SAFE_ASSERT_CONTINUE(midiMessage.samplePosition >= 0);

if (midiMessage.numBytes > static_cast<int>(MidiEvent::kDataSize))
continue;

MidiEvent& midiEvent(midiEvents[midiEventCount++]);

midiEvent.frame = static_cast<uint32_t>(midiMessage.samplePosition);
midiEvent.size = (static_cast<uint8_t>(midiMessage.numBytes));
std::memcpy(midiEvent.data, midiMessage.data, midiEvent.size);

if (midiEventCount == kMaxMidiEvents)
break;
}

midiMessages.clear();
// AudioPlayHead* getPlayHead()

const juce::ScopedValueSetter<juce::MidiBuffer*> cvs(currentMidiMessages, &midiMessages, nullptr);

juce::AudioPlayHead* const playhead = getPlayHead();
juce::AudioPlayHead::CurrentPositionInfo posInfo;

if (playhead != nullptr && playhead->getCurrentPosition(posInfo))
{
timePosition.playing = posInfo.isPlaying;
timePosition.bbt.valid = true;

// ticksPerBeat is not possible with JUCE
timePosition.bbt.ticksPerBeat = 1920.0;

if (posInfo.timeInSamples >= 0)
timePosition.frame = static_cast<uint64_t>(posInfo.timeInSamples);
else
timePosition.frame = 0;

timePosition.bbt.beatsPerMinute = posInfo.bpm;

const double ppqPos = std::abs(posInfo.ppqPosition);
const int ppqPerBar = posInfo.timeSigNumerator * 4 / posInfo.timeSigDenominator;
const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * posInfo.timeSigNumerator;
const double rest = std::fmod(barBeats, 1.0);

timePosition.bbt.bar = static_cast<int32_t>(ppqPos) / ppqPerBar + 1;
timePosition.bbt.beat = static_cast<int32_t>(barBeats - rest + 0.5) + 1;
timePosition.bbt.tick = rest * timePosition.bbt.ticksPerBeat;
timePosition.bbt.beatsPerBar = posInfo.timeSigNumerator;
timePosition.bbt.beatType = posInfo.timeSigDenominator;

if (posInfo.ppqPosition < 0.0)
{
--timePosition.bbt.bar;
timePosition.bbt.beat = posInfo.timeSigNumerator - timePosition.bbt.beat + 1;
timePosition.bbt.tick = timePosition.bbt.ticksPerBeat - timePosition.bbt.tick - 1;
}

timePosition.bbt.barStartTick = timePosition.bbt.ticksPerBeat*
timePosition.bbt.beatsPerBar*
(timePosition.bbt.bar-1);
}
else
{
timePosition.frame = 0;
timePosition.playing = false;
timePosition.bbt.valid = false;
}

plugin.setTimePosition(timePosition);

DISTRHO_SAFE_ASSERT_RETURN(buffer.getNumChannels() == 2,);

const float* audioBufferIn[2];
float* audioBufferOut[2];
audioBufferIn[0] = buffer.getReadPointer(0);
audioBufferIn[1] = buffer.getReadPointer(1);
audioBufferOut[0] = buffer.getWritePointer(0);
audioBufferOut[1] = buffer.getWritePointer(1);

plugin.run(audioBufferIn, audioBufferOut, static_cast<uint32_t>(numSamples), midiEvents, midiEventCount);
}

// fix compiler warning
void processBlock(juce::AudioBuffer<double>&, juce::MidiBuffer&) override {}

double getTailLengthSeconds() const override
{
return true;
return 0.0;
}

bool acceptsMidi() const override
@@ -142,6 +426,11 @@ public:
return true;
}

juce::AudioProcessorParameter* getBypassParameter() const override
{
return bypassParameter;
}

juce::AudioProcessorEditor* createEditor() override;

bool hasEditor() const override
@@ -151,7 +440,7 @@ public:

int getNumPrograms() override
{
return 0;
return 1;
}

int getCurrentProgram() override
@@ -165,7 +454,7 @@ public:

const juce::String getProgramName(int) override
{
return {};
return "Default";
}

void changeProgramName(int, const juce::String&) override
@@ -174,37 +463,219 @@ public:

void getStateInformation(juce::MemoryBlock& destData) override
{
juce::XmlElement xmlState("CardinalState");

for (uint32_t i=0; i<parameterCount; ++i)
xmlState.setAttribute(plugin.getParameterSymbol(i).buffer(), plugin.getParameterValue(i));

for (uint32_t i=0, stateCount=plugin.getStateCount(); i<stateCount; ++i)
{
const String& key(plugin.getStateKey(i));
xmlState.setAttribute(key.buffer(), plugin.getStateValue(key).buffer());
}

copyXmlToBinary(xmlState, destData);
}

void setStateInformation(const void* const data, const int sizeInBytes) override
{
std::unique_ptr<juce::XmlElement> xmlState(getXmlFromBinary(data, sizeInBytes));
DISTRHO_SAFE_ASSERT_RETURN(xmlState.get() != nullptr,);

const juce::Array<juce::AudioProcessorParameter*>& parameters(getParameters());

for (uint32_t i=0; i<parameterCount; ++i)
{
const double value = xmlState->getDoubleAttribute(plugin.getParameterSymbol(i).buffer(),
plugin.getParameterDefault(i));
const float normalizedValue = plugin.getParameterRanges(i).getFixedAndNormalizedValue(value);
parameters.getUnchecked(static_cast<int>(i))->setValueNotifyingHost(normalizedValue);
}

for (uint32_t i=0, stateCount=plugin.getStateCount(); i<stateCount; ++i)
{
const String& key(plugin.getStateKey(i));
const juce::String value = xmlState->getStringAttribute(key.buffer(),
plugin.getStateDefaultValue(i).buffer());
plugin.setState(key, value.toRawUTF8());
}
}

void setStateInformation(const void* data, int sizeInBytes) override
private:
static bool writeMidiFunc(void* const ptr, const MidiEvent& midiEvent)
{
CardinalWrapperProcessor* const processor = static_cast<CardinalWrapperProcessor*>(ptr);
DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, false);

juce::MidiBuffer* const currentMidiMessages = processor->currentMidiMessages;
DISTRHO_SAFE_ASSERT_RETURN(currentMidiMessages != nullptr, false);

const uint8_t* const data = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data;
return currentMidiMessages->addEvent(data,
static_cast<int>(midiEvent.size),
static_cast<int>(midiEvent.frame));
}
};

class CardinalWrapperEditor : public juce::AudioProcessorEditor
// --------------------------------------------------------------------------------------------------------------------

// unused in cardinal
static constexpr const sendNoteFunc nullSendNoteFunc = nullptr;

// unwanted, juce file dialogs are ugly
static constexpr const fileRequestFunc nullFileRequestFunc = nullptr;

// UI/editor implementation
class CardinalWrapperEditor : public juce::AudioProcessorEditor,
private juce::Timer
{
CardinalWrapperProcessor& cardinalProcessor;

UIExporter* ui;
void* const dspPtr;

public:
CardinalWrapperEditor(CardinalWrapperProcessor& processor)
: juce::AudioProcessorEditor(processor)
{}
CardinalWrapperEditor(CardinalWrapperProcessor& cardinalProc)
: juce::AudioProcessorEditor(cardinalProc),
cardinalProcessor(cardinalProc),
ui(nullptr),
dspPtr(cardinalProc.plugin.getInstancePointer())
{
setOpaque(true);
setResizable(true, false);
// setResizeLimits(648, 538, -1, -1);
setSize(1228, 666);

startTimer(1000.0 / 60.0);
}

~CardinalWrapperEditor() override
{}
{
stopTimer();
delete ui;
}

protected:
void timerCallback() override
{
if (ui == nullptr)
return;

for (uint32_t i=0; i<cardinalProcessor.parameterCount; ++i)
{
if (cardinalProcessor.updatedParameters[i])
{
cardinalProcessor.updatedParameters[i] = false;
ui->parameterChanged(i, cardinalProcessor.plugin.getParameterValue(i));
}
}

repaint();
}

void paint(juce::Graphics&) override
{
if (ui == nullptr)
{
juce::ComponentPeer* const peer = getPeer();
DISTRHO_SAFE_ASSERT_RETURN(peer != nullptr,);

void* const nativeHandle = peer->getNativeHandle();
DISTRHO_SAFE_ASSERT_RETURN(nativeHandle != nullptr,);

ui = new UIExporter(this,
(uintptr_t)nativeHandle,
cardinalProcessor.getSampleRate(),
editParamFunc,
setParamFunc,
setStateFunc,
nullSendNoteFunc,
setSizeFunc,
nullFileRequestFunc,
nullptr, // bundlePath
dspPtr,
0.0 // scaleFactor
);

if (cardinalProcessor.wrapperType == juce::AudioProcessor::wrapperType_Standalone)
{
const double scaleFactor = ui->getScaleFactor();
ui->setWindowOffset(4 * scaleFactor, 30 * scaleFactor);
}
}

ui->plugin_idle();
}

private:
static void editParamFunc(void* const ptr, const uint32_t index, const bool started)
{
CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);

CardinalWrapperProcessor& cardinalProcessor(editor->cardinalProcessor);

if (started)
cardinalProcessor.getParameters().getUnchecked(static_cast<int>(index))->beginChangeGesture();
else
cardinalProcessor.getParameters().getUnchecked(static_cast<int>(index))->endChangeGesture();
}

static void setParamFunc(void* const ptr, const uint32_t index, const float value)
{
CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);

CardinalWrapperProcessor& cardinalProcessor(editor->cardinalProcessor);
const juce::Array<juce::AudioProcessorParameter*>& parameters(cardinalProcessor.getParameters());
juce::AudioProcessorParameter* const parameter = parameters.getUnchecked(static_cast<int>(index));
static_cast<ParameterFromDPF*>(parameter)->setValueNotifyingHostFromDPF(value);
}

static void setStateFunc(void* const ptr, const char* const key, const char* const value)
{
CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);

CardinalWrapperProcessor& cardinalProcessor(editor->cardinalProcessor);
cardinalProcessor.plugin.setState(key, value);
}

static void setSizeFunc(void* const ptr, uint width, uint height)
{
CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);

#ifdef DISTRHO_OS_MAC
UIExporter* const ui = editor->ui;
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

const double scaleFactor = ui->getScaleFactor();
width /= scaleFactor;
height /= scaleFactor;
#endif

editor->setSize(static_cast<int>(width), static_cast<int>(height));
}
};

// -----------------------------------------------------------------------------------------------------------
juce::AudioProcessorEditor* CardinalWrapperProcessor::createEditor()
{
return new CardinalWrapperEditor(*this);
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

juce::AudioProcessor* createPluginFilter()
{
// set valid but dummy values
d_nextBufferSize = 512;
d_nextSampleRate = 48000.0;
return new DISTRHO_NAMESPACE::CardinalWrapperProcessor;
}

// -----------------------------------------------------------------------------------------------------------

#define DISTRHO_IS_STANDALONE 0
#include "src/DistrhoPlugin.cpp"
#include "src/DistrhoUtils.cpp"
// --------------------------------------------------------------------------------------------------------------------

+ 657
- 0
patches/DRMR_-_Interverb.vcv View File

@@ -0,0 +1,657 @@
{
"version": "2.0",
"zoom": 1.0,
"gridOffset": [
-3.4424479007720947,
0.86171877384185791
],
"modules": [
{
"id": 1184757612963547,
"plugin": "Valley",
"model": "Interzone",
"version": "2.0",
"params": [
{
"value": 0.0,
"id": 0
},
{
"value": 1.0,
"id": 1
},
{
"value": 0.0,
"id": 2
},
{
"value": 0.0,
"id": 3
},
{
"value": 1.0,
"id": 4
},
{
"value": 0.0,
"id": 5
},
{
"value": 0.26100000739097595,
"id": 6
},
{
"value": 0.26799961924552917,
"id": 7
},
{
"value": 0.0,
"id": 8
},
{
"value": 0.0,
"id": 9
},
{
"value": 0.0,
"id": 10
},
{
"value": 0.23399992287158966,
"id": 11
},
{
"value": 1.8360022306442261,
"id": 12
},
{
"value": 0.0,
"id": 13
},
{
"value": 1.0,
"id": 14
},
{
"value": 0.24399974942207336,
"id": 15
},
{
"value": 0.92999976873397827,
"id": 16
},
{
"value": 0.48200002312660217,
"id": 17
},
{
"value": 0.0,
"id": 18
},
{
"value": 0.0,
"id": 19
},
{
"value": 8.0599746704101562,
"id": 20
},
{
"value": 3.6200008392333984,
"id": 21
},
{
"value": 0.0,
"id": 22
},
{
"value": 1.0,
"id": 23
},
{
"value": 0.24999970197677612,
"id": 24
},
{
"value": 0.058000005781650543,
"id": 25
},
{
"value": 0.45800071954727173,
"id": 26
},
{
"value": 0.0,
"id": 27
},
{
"value": 0.0,
"id": 28
},
{
"value": 0.46746969223022461,
"id": 29
},
{
"value": 0.0,
"id": 30
},
{
"value": 0.0,
"id": 31
},
{
"value": 0.20722892880439758,
"id": 32
},
{
"value": 1.0,
"id": 33
},
{
"value": 0.60399961471557617,
"id": 34
},
{
"value": 0.24399995803833008,
"id": 35
},
{
"value": 0.85800004005432129,
"id": 36
},
{
"value": 0.25000002980232239,
"id": 37
},
{
"value": 0.0,
"id": 38
},
{
"value": 0.0,
"id": 39
},
{
"value": 0.0,
"id": 40
},
{
"value": 1.0,
"id": 41
},
{
"value": 0.0,
"id": 42
}
],
"data": {
"panelStyle": 0
},
"pos": [
12,
1
]
},
{
"id": 7479062205976098,
"plugin": "repelzen",
"model": "rexmix",
"version": "2.0",
"params": [
{
"value": 0.0,
"id": 0
},
{
"value": 0.0,
"id": 1
},
{
"value": 0.0,
"id": 2
},
{
"value": 0.0,
"id": 3
},
{
"value": -11.855396270751953,
"id": 4
},
{
"value": -10.481927871704102,
"id": 5
},
{
"value": 0.0,
"id": 6
},
{
"value": -60.0,
"id": 7
},
{
"value": -60.0,
"id": 8
},
{
"value": -60.0,
"id": 9
},
{
"value": 0.0,
"id": 10
},
{
"value": 0.0,
"id": 11
},
{
"value": 0.0,
"id": 12
},
{
"value": 0.0,
"id": 13
},
{
"value": 0.0,
"id": 14
},
{
"value": 0.0,
"id": 15
},
{
"value": 0.0,
"id": 16
},
{
"value": 0.0,
"id": 17
},
{
"value": 0.0,
"id": 18
},
{
"value": 0.0,
"id": 19
},
{
"value": 0.0,
"id": 20
},
{
"value": 0.0,
"id": 21
},
{
"value": 0.0,
"id": 22
},
{
"value": 0.0,
"id": 23
},
{
"value": 0.0,
"id": 24
},
{
"value": 0.0,
"id": 25
},
{
"value": 0.0,
"id": 26
},
{
"value": 0.0,
"id": 27
},
{
"value": 0.0,
"id": 28
},
{
"value": 0.0,
"id": 29
},
{
"value": 0.0,
"id": 30
},
{
"value": 0.0,
"id": 31
},
{
"value": 0.0,
"id": 32
},
{
"value": 0.0,
"id": 33
},
{
"value": -0.21927711367607117,
"id": 34
},
{
"value": 0.26265060901641846,
"id": 35
},
{
"value": 0.0,
"id": 36
},
{
"value": 0.0,
"id": 37
},
{
"value": 0.0,
"id": 38
},
{
"value": 0.0,
"id": 39
},
{
"value": 0.66747087240219116,
"id": 40
},
{
"value": 0.31686747074127197,
"id": 41
},
{
"value": 0.0,
"id": 42
},
{
"value": 0.0,
"id": 43
},
{
"value": 0.0,
"id": 44
},
{
"value": 0.0,
"id": 45
},
{
"value": 0.0,
"id": 46
},
{
"value": 0.0,
"id": 47
},
{
"value": 0.0,
"id": 48
},
{
"value": 0.0,
"id": 49
},
{
"value": 0.0,
"id": 50
},
{
"value": 0.0,
"id": 51
}
],
"rightModuleId": 1,
"pos": [
47,
1
]
},
{
"id": 5265678395554143,
"plugin": "ihtsyn",
"model": "MVerb",
"version": "2.0",
"params": [
{
"value": 1.0,
"id": 0
},
{
"value": 0.31204831600189209,
"id": 1
},
{
"value": 0.69638609886169434,
"id": 2
},
{
"value": 0.50963789224624634,
"id": 3
},
{
"value": 0.5,
"id": 4
},
{
"value": 0.68000000715255737,
"id": 5
},
{
"value": 0.60000002384185791,
"id": 6
},
{
"value": 0.80000001192092896,
"id": 7
},
{
"value": 0.5,
"id": 8
},
{
"value": 0.0,
"id": 9
}
],
"pos": [
50,
0
]
},
{
"id": 8996849652715896,
"plugin": "Cardinal",
"model": "Carla",
"version": "2.0",
"params": [
{
"value": 1.0,
"id": 0
},
{
"value": 1.0,
"id": 1
}
],
"data": "<?xml version='1.0' encoding='UTF-8'?>\n<!DOCTYPE CARLA-PROJECT>\n<CARLA-PROJECT VERSION='2.4'>\n <EngineSettings>\n <ForceStereo>false</ForceStereo>\n <PreferPluginBridges>false</PreferPluginBridges>\n <PreferUiBridges>false</PreferUiBridges>\n <UIsAlwaysOnTop>true</UIsAlwaysOnTop>\n <MaxParameters>200</MaxParameters>\n <UIBridgesTimeout>4000</UIBridgesTimeout>\n <LADSPA_PATH></LADSPA_PATH>\n <DSSI_PATH></DSSI_PATH>\n <LV2_PATH></LV2_PATH>\n <VST2_PATH></VST2_PATH>\n <VST3_PATH></VST3_PATH>\n <SF2_PATH></SF2_PATH>\n <SFZ_PATH></SFZ_PATH>\n <JSFX_PATH></JSFX_PATH>\n </EngineSettings>\n\n <Patchbay>\n </Patchbay>\n</CARLA-PROJECT>\n",
"pos": [
123,
1
]
},
{
"id": 644714212872810,
"plugin": "Cardinal",
"model": "TextEditor",
"version": "2.0",
"params": [],
"data": {
"filepath": "",
"lang": "None",
"etext": " \n ^ ^ ,_, \n (O,O) (.,.) \n ( ) ( ) \n--------\"-\"---dwb--\"-\"---dwb- \n\nversatile 4 voice polyphonic synth with a bit\nof reverb\n\nParam1: pulse width modulation\nParam2: filter frequency\nParam3: filter resonance\n\n\n /^--^\\ /^--^\\ /^--^\\ \n \\____/ \\____/ \\____/ \n / \\ / \\ / \\ \n | | | | | | \n \\__ __/ \\__ __/ \\__ __/ \n|^|^|^|^\\ \\^|^|^|^/ /^|^|^|^|^\\ \\^|^|^|^|^|^|^|^|^|\n| | | | |\\ \\| | |/ /| | | | | |\\ \\| | | | | | | | |\n#########/ /#####\\ \\###########/ /#################\n| | | | |\\/ | | | \\/| | | | | |\\/ | | | | | | | | |\n|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|\n\n\n\n\n\n\n",
"width": 27
},
"pos": [
2,
2
]
},
{
"id": 1,
"plugin": "Cardinal",
"model": "HostAudio2",
"version": "2.0",
"params": [
{
"value": 1.0,
"id": 0
}
],
"leftModuleId": 7479062205976098,
"data": {
"dcFilter": true
},
"pos": [
75,
1
]
},
{
"id": 2,
"plugin": "Cardinal",
"model": "HostMIDI",
"version": "2.0",
"params": [],
"data": {
"smooth": true,
"channels": 4,
"polyMode": 0,
"lastPitch": 8192,
"lastMod": 0,
"inputChannel": 0,
"outputChannel": 0
},
"pos": [
0,
1
]
},
{
"id": 4,
"plugin": "Cardinal",
"model": "HostParameters",
"version": "2.0",
"params": [],
"pos": [
32,
2
]
}
],
"cables": [
{
"id": 6433670563492534,
"outputModuleId": 7479062205976098,
"outputId": 1,
"inputModuleId": 1,
"inputId": 1,
"color": "#67ff52"
},
{
"id": 7811632878270413,
"outputModuleId": 7479062205976098,
"outputId": 0,
"inputModuleId": 1,
"inputId": 0,
"color": "#52ff7d"
},
{
"id": 5639365994348230,
"outputModuleId": 1184757612963547,
"outputId": 5,
"inputModuleId": 7479062205976098,
"inputId": 4,
"color": "#6752ff"
},
{
"id": 4697080526212779,
"outputModuleId": 7479062205976098,
"outputId": 3,
"inputModuleId": 5265678395554143,
"inputId": 1,
"color": "#527dff"
},
{
"id": 749068877206824,
"outputModuleId": 7479062205976098,
"outputId": 2,
"inputModuleId": 5265678395554143,
"inputId": 0,
"color": "#52beff"
},
{
"id": 385489337409134,
"outputModuleId": 5265678395554143,
"outputId": 1,
"inputModuleId": 7479062205976098,
"inputId": 1,
"color": "#52ffff"
},
{
"id": 6967686280457428,
"outputModuleId": 5265678395554143,
"outputId": 0,
"inputModuleId": 7479062205976098,
"inputId": 0,
"color": "#52ffbe"
},
{
"id": 8997840924253604,
"outputModuleId": 4,
"outputId": 0,
"inputModuleId": 1184757612963547,
"inputId": 2,
"color": "#a852ff"
},
{
"id": 2740863142747665,
"outputModuleId": 4,
"outputId": 1,
"inputModuleId": 1184757612963547,
"inputId": 7,
"color": "#e952ff"
},
{
"id": 5297103153509182,
"outputModuleId": 2,
"outputId": 1,
"inputModuleId": 1184757612963547,
"inputId": 3,
"color": "#ff9352"
},
{
"id": 7340028675801259,
"outputModuleId": 4,
"outputId": 2,
"inputModuleId": 1184757612963547,
"inputId": 8,
"color": "#ff5252"
},
{
"id": 5629473447667825,
"outputModuleId": 2,
"outputId": 0,
"inputModuleId": 1184757612963547,
"inputId": 0,
"color": "#ff5252"
}
]
}

+ 1
- 0
plugins/AaronStatic

@@ -0,0 +1 @@
Subproject commit 4ace0a1789c577ee4eb12dc03da5271f80598d62

+ 1
- 0
plugins/Algoritmarte

@@ -0,0 +1 @@
Subproject commit 9d41fe882ab5029100b55c98ba7f10172d452795

+ 1
- 1
plugins/AmalgamatedHarmonics

@@ -1 +1 @@
Subproject commit 35b89c93152ac2194eecffbd4aa39e71caa90cc0
Subproject commit 21a031870db2068a98d9690eaffc24bbbfc99c2e

+ 1
- 0
plugins/ArableInstruments

@@ -0,0 +1 @@
Subproject commit 890448f087e3ab47eac391f9bcfe03f7bbd2123e

+ 1
- 1
plugins/BaconPlugs

@@ -1 +1 @@
Subproject commit 0a6c390eedf98884393f82cb066038a78a316ea5
Subproject commit 9d35b745af8569d6a9d6bc5c3f2c3e64c852d8e0

+ 1
- 1
plugins/Befaco

@@ -1 +1 @@
Subproject commit ec406ce181f340bce8e475cb508c4db0db02fdc6
Subproject commit 7e0b020000225e3d2aecba3f09054595339fe540

+ 1
- 1
plugins/Bidoo

@@ -1 +1 @@
Subproject commit e55fcd2e1d7c0fef69d4919baac6f791172c89ca
Subproject commit f771bf270393b8587516329614ba76e2894355b7

+ 34
- 0
plugins/BidooDark/plugin.cpp View File

@@ -0,0 +1,34 @@
#include "../Bidoo/src/plugin.hpp"
#undef ModuleWidget

void InstantiateExpanderItem::onAction(const event::Action &e) {
engine::Module* module = model->createModule();
APP->engine->addModule(module);
ModuleWidget* mw = model->createModuleWidget(module);
if (mw) {
APP->scene->rack->setModulePosNearest(mw, posit);
APP->scene->rack->addModule(mw);
history::ModuleAdd *h = new history::ModuleAdd;
h->name = "create expander module";
h->setModule(mw);
APP->history->push(h);
}
}

json_t* BidooModule::dataToJson() {
return nullptr;
}

void BidooModule::dataFromJson(json_t*) {
}

void BidooWidget::appendContextMenu(Menu*) {
}

void BidooWidget::prepareThemes(const std::string& filename) {
setPanel(APP->window->loadSvg(filename));
}

void BidooWidget::step() {
CardinalModuleWidget::step();
}

+ 1
- 1
plugins/BogaudioModules

@@ -1 +1 @@
Subproject commit 8e982f462c4117f84794cbf6a13740992ff17d92
Subproject commit a86e7d7b18e0c7cb6e857df36263b0e85bf85566

+ 4
- 4
plugins/Cardinal/src/AudioFile.cpp View File

@@ -99,7 +99,7 @@ struct CarlaInternalPluginModule : Module, Thread {
float dataOut[NUM_OUTPUTS][BUFFER_SIZE];
float* dataOutPtr[NUM_OUTPUTS];
unsigned audioDataFill = 0;
int64_t lastBlockFrame = -1;
uint32_t lastProcessCounter = 0;
bool fileChanged = false;
std::string currentFile;

@@ -300,12 +300,12 @@ struct CarlaInternalPluginModule : Module, Thread {

if (audioDataFill == BUFFER_SIZE)
{
const int64_t blockFrame = pcontext->engine->getBlockFrame();
const uint32_t processCounter = pcontext->processCounter;

// Update time position if running a new audio block
if (lastBlockFrame != blockFrame)
if (lastProcessCounter != processCounter)
{
lastBlockFrame = blockFrame;
lastProcessCounter = processCounter;
fCarlaTimeInfo.playing = pcontext->playing;
fCarlaTimeInfo.frame = pcontext->frame;
}


+ 16
- 7
plugins/Cardinal/src/Carla.cpp View File

@@ -95,10 +95,15 @@ struct CarlaModule : Module {
float* dataInPtr[NUM_INPUTS];
float* dataOutPtr[NUM_OUTPUTS];
unsigned audioDataFill = 0;
int64_t lastBlockFrame = -1;
uint32_t lastProcessCounter = 0;
CardinalExpanderFromCarlaMIDIToCV* midiOutExpander = nullptr;
std::string patchStorage;

#ifdef CARLA_OS_WIN
// must keep string pointer valid
std::string winResourceDir;
#endif

CarlaModule()
: pcontext(static_cast<CardinalPluginContext*>(APP))
{
@@ -138,10 +143,14 @@ struct CarlaModule : Module {
binaryDir = "/Applications/Carla.app/Contents/MacOS";
resourceDir = "/Applications/Carla.app/Contents/MacOS/resources";
}
#elif defined(CARLA_OS_WINDOWS)
// Carla does not support system-wide install on Windows right now
if (false)
#elif defined(CARLA_OS_WIN)
const std::string winBinaryDir = system::join(asset::systemDir, "Carla");

if (system::exists(winBinaryDir))
{
winResourceDir = system::join(winBinaryDir, "resources");
binaryDir = winBinaryDir.c_str();
resourceDir = winResourceDir.c_str();
}
#else
if (system::exists("/usr/local/lib/carla"))
@@ -318,12 +327,12 @@ struct CarlaModule : Module {

if (audioDataFill == BUFFER_SIZE)
{
const int64_t blockFrame = pcontext->engine->getBlockFrame();
const uint32_t processCounter = pcontext->processCounter;

// Update time position if running a new audio block
if (lastBlockFrame != blockFrame)
if (lastProcessCounter != processCounter)
{
lastBlockFrame = blockFrame;
lastProcessCounter = processCounter;
fCarlaTimeInfo.playing = pcontext->playing;
fCarlaTimeInfo.frame = pcontext->frame;
fCarlaTimeInfo.bbt.valid = pcontext->bbtValid;


+ 235
- 108
plugins/Cardinal/src/HostAudio.cpp View File

@@ -29,18 +29,16 @@ struct HostAudio : TerminalModule {
const int numParams;
const int numInputs;
const int numOutputs;
int dataFrame = 0;
int64_t lastBlockFrame = -1;
bool bypassed = false;
bool in1connected = false;
bool in2connected = false;
uint32_t dataFrame = 0;
uint32_t lastProcessCounter = 0;

// for rack core audio module compatibility
dsp::RCFilter dcFilters[numIO];
bool dcFilterEnabled = (numIO == 2);

// for stereo meter
volatile bool resetMeters = true;
float gainMeterL = 0.0f;
float gainMeterR = 0.0f;

HostAudio()
: pcontext(static_cast<CardinalPluginContext*>(APP)),
numParams(numIO == 2 ? 1 : 0),
@@ -63,35 +61,39 @@ struct HostAudio : TerminalModule {
void onReset() override
{
dcFilterEnabled = (numIO == 2);
resetMeters = true;
}

void onSampleRateChange(const SampleRateChangeEvent& e) override
{
resetMeters = true;

for (int i=0; i<numIO; ++i)
dcFilters[i].setCutoffFreq(10.f * e.sampleTime);
}

void processTerminalInput(const ProcessArgs&) override
{
const int blockFrames = pcontext->engine->getBlockFrames();
const int64_t blockFrame = pcontext->engine->getBlockFrame();
const uint32_t bufferSize = pcontext->bufferSize;
const uint32_t processCounter = pcontext->processCounter;

// only checked on input
if (lastBlockFrame != blockFrame)
if (lastProcessCounter != processCounter)
{
bypassed = isBypassed();
dataFrame = 0;
lastBlockFrame = blockFrame;
lastProcessCounter = processCounter;

if (numIO == 2)
{
in1connected = inputs[0].isConnected();
in2connected = inputs[1].isConnected();
}
}

// only incremented on output
const int k = dataFrame;
DISTRHO_SAFE_ASSERT_INT2_RETURN(k < blockFrames, k, blockFrames,);
const uint32_t k = dataFrame;
DISTRHO_SAFE_ASSERT_INT2_RETURN(k < bufferSize, k, bufferSize,);

// from host into cardinal, shows as output plug
if (isBypassed())
if (bypassed)
{
for (int i=0; i<numOutputs; ++i)
outputs[i].setVoltage(0.0f);
@@ -103,97 +105,210 @@ struct HostAudio : TerminalModule {
}
}

json_t* dataToJson() override
{
json_t* const rootJ = json_object();
DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr);

json_object_set_new(rootJ, "dcFilter", json_boolean(dcFilterEnabled));
return rootJ;
}

void dataFromJson(json_t* const rootJ) override
{
json_t* const dcFilterJ = json_object_get(rootJ, "dcFilter");
DISTRHO_SAFE_ASSERT_RETURN(dcFilterJ != nullptr,);

dcFilterEnabled = json_boolean_value(dcFilterJ);
}
};

struct HostAudio2 : HostAudio<2> {
#ifndef HEADLESS
// for stereo meter
uint32_t internalDataFrame = 0;
float internalDataBuffer[2][128];
volatile bool resetMeters = true;
float gainMeterL = 0.0f;
float gainMeterR = 0.0f;
#endif

HostAudio2()
: HostAudio<2>()
{
#ifndef HEADLESS
std::memset(internalDataBuffer, 0, sizeof(internalDataBuffer));
#endif
}

#ifndef HEADLESS
void onReset() override
{
HostAudio<2>::onReset();
resetMeters = true;
}

void onSampleRateChange(const SampleRateChangeEvent& e) override
{
HostAudio<2>::onSampleRateChange(e);
resetMeters = true;
}
#endif

void processTerminalOutput(const ProcessArgs&) override
{
const int blockFrames = pcontext->engine->getBlockFrames();
if (!in1connected && !in2connected)
return;

const uint32_t bufferSize = pcontext->bufferSize;

// only incremented on output
const int k = dataFrame++;
DISTRHO_SAFE_ASSERT_INT2_RETURN(k < blockFrames, k, blockFrames,);
const uint32_t k = dataFrame++;
DISTRHO_SAFE_ASSERT_INT2_RETURN(k < bufferSize, k, bufferSize,);

if (isBypassed())
if (bypassed)
return;

float** const dataOuts = pcontext->dataOuts;

// stereo version gain
const float gain = numParams != 0 ? std::pow(params[0].getValue(), 2.f) : 1.0f;
// gain (stereo variant only)
const float gain = std::pow(params[0].getValue(), 2.f);

// read first value, special case for mono mode
float valueL = inputs[0].getVoltageSum() * 0.1f;
// read stereo values
float valueL, valueR;

// Apply DC filter
if (dcFilterEnabled)
if (in1connected)
{
dcFilters[0].process(valueL);
valueL = dcFilters[0].highpass();
}
valueL = inputs[0].getVoltageSum() * 0.1f;

valueL = clamp(valueL * gain, -1.0f, 1.0f);
dataOuts[0][k] += valueL;
if (dcFilterEnabled)
{
dcFilters[0].process(valueL);
valueL = dcFilters[0].highpass();
}

// read everything else
for (int i=1; i<numInputs; ++i)
valueL = clamp(valueL * gain, -1.0f, 1.0f);
dataOuts[0][k] += valueL;
}
else
{
float v = inputs[i].getVoltageSum() * 0.1f;
valueL = 0.0f;
}

if (in2connected)
{
valueR = inputs[1].getVoltageSum() * 0.1f;

// Apply DC filter
if (dcFilterEnabled)
{
dcFilters[i].process(v);
v = dcFilters[i].highpass();
dcFilters[1].process(valueR);
valueR = dcFilters[1].highpass();
}

dataOuts[i][k] += clamp(v * gain, -1.0f, 1.0f);
valueR = clamp(valueR * gain, -1.0f, 1.0f);
dataOuts[1][k] += valueR;
}

if (numInputs == 2)
else if (in1connected)
{
const bool connected = inputs[1].isConnected();
valueR = valueL;
dataOuts[1][k] += valueL;
}
#ifndef HEADLESS
else
{
valueR = 0.0f;
}

if (! connected)
dataOuts[1][k] += valueL;
const uint32_t j = internalDataFrame++;
internalDataBuffer[0][j] = valueL;
internalDataBuffer[1][j] = valueR;

if (dataFrame == blockFrames)
{
if (resetMeters)
gainMeterL = gainMeterR = 0.0f;
if (internalDataFrame == 128)
{
internalDataFrame = 0;

gainMeterL = std::max(gainMeterL, d_findMaxNormalizedFloat(dataOuts[0], blockFrames));
if (resetMeters)
gainMeterL = gainMeterR = 0.0f;

if (connected)
gainMeterR = std::max(gainMeterR, d_findMaxNormalizedFloat(dataOuts[1], blockFrames));
else
gainMeterR = gainMeterL;
gainMeterL = std::max(gainMeterL, d_findMaxNormalizedFloat(internalDataBuffer[0], 128));

resetMeters = false;
}
if (in2connected)
gainMeterR = std::max(gainMeterR, d_findMaxNormalizedFloat(internalDataBuffer[1], 128));
else
gainMeterR = gainMeterL;

resetMeters = false;
}
#endif
}
};

json_t* dataToJson() override
struct HostAudio8 : HostAudio<8> {
// no meters in this variant

void processTerminalOutput(const ProcessArgs&) override
{
json_t* const rootJ = json_object();
DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr);
const uint32_t bufferSize = pcontext->bufferSize;

json_object_set_new(rootJ, "dcFilter", json_boolean(dcFilterEnabled));
return rootJ;
// only incremented on output
const uint32_t k = dataFrame++;
DISTRHO_SAFE_ASSERT_INT2_RETURN(k < bufferSize, k, bufferSize,);

if (bypassed)
return;

float** const dataOuts = pcontext->dataOuts;

for (int i=0; i<numInputs; ++i)
{
float v = inputs[i].getVoltageSum() * 0.1f;

if (dcFilterEnabled)
{
dcFilters[i].process(v);
v = dcFilters[i].highpass();
}

dataOuts[i][k] += clamp(v, -1.0f, 1.0f);
}
}

void dataFromJson(json_t* const rootJ) override
};

#ifndef HEADLESS
// --------------------------------------------------------------------------------------------------------------------

template<int numIO>
struct HostAudioWidget : ModuleWidgetWith8HP {
HostAudio<numIO>* const module;

HostAudioWidget(HostAudio<numIO>* const m)
: module(m)
{
json_t* const dcFilterJ = json_object_get(rootJ, "dcFilter");
DISTRHO_SAFE_ASSERT_RETURN(dcFilterJ != nullptr,);
setModule(m);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostAudio.svg")));

dcFilterEnabled = json_boolean_value(dcFilterJ);
createAndAddScrews();

for (uint i=0; i<numIO; ++i)
{
createAndAddInput(i);
createAndAddOutput(i);
}
}

void appendContextMenu(Menu* const menu) override {
menu->addChild(new MenuSeparator);
menu->addChild(createBoolPtrMenuItem("DC blocker", "", &module->dcFilterEnabled));
}
};

template<int numIO>
// --------------------------------------------------------------------------------------------------------------------

struct HostAudioNanoMeter : NanoMeter {
HostAudio<numIO>* const module;
HostAudio2* const module;

HostAudioNanoMeter(HostAudio<numIO>* const m)
HostAudioNanoMeter(HostAudio2* const m)
: module(m)
{
hasGainKnob = true;
@@ -211,69 +326,81 @@ struct HostAudioNanoMeter : NanoMeter {
}
};

template<int numIO>
struct HostAudioWidget : ModuleWidgetWith8HP {
HostAudio<numIO>* const module;
// --------------------------------------------------------------------------------------------------------------------

HostAudioWidget(HostAudio<numIO>* const m)
: module(m)
struct HostAudioWidget2 : HostAudioWidget<2> {
HostAudioWidget2(HostAudio2* const m)
: HostAudioWidget<2>(m)
{
setModule(m);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostAudio.svg")));
// FIXME
const float middleX = box.size.x * 0.5f;
addParam(createParamCentered<NanoKnob>(Vec(middleX, 310.0f), m, 0));

HostAudioNanoMeter* const meter = new HostAudioNanoMeter(m);
meter->box.pos = Vec(middleX - padding + 2.75f, startY + padding * 2);
meter->box.size = Vec(padding * 2.0f - 4.0f, 136.0f);
addChild(meter);
}

createAndAddScrews();
void draw(const DrawArgs& args) override
{
drawBackground(args.vg);
drawOutputJacksArea(args.vg, 2);
setupTextLines(args.vg);

for (uint i=0; i<numIO; ++i)
{
createAndAddInput(i);
createAndAddOutput(i);
}
drawTextLine(args.vg, 0, "Left/M");
drawTextLine(args.vg, 1, "Right");

if (numIO == 2)
{
// FIXME
const float middleX = box.size.x * 0.5f;
addParam(createParamCentered<NanoKnob>(Vec(middleX, 310.0f), m, 0));

HostAudioNanoMeter<numIO>* const meter = new HostAudioNanoMeter<numIO>(m);
meter->box.pos = Vec(middleX - padding + 2.75f, startY + padding * 2);
meter->box.size = Vec(padding * 2.0f - 4.0f, 136.0f);
addChild(meter);
}
ModuleWidgetWith8HP::draw(args);
}
};

struct HostAudioWidget8 : HostAudioWidget<8> {
HostAudioWidget8(HostAudio8* const m)
: HostAudioWidget<8>(m) {}

void draw(const DrawArgs& args) override
{
drawBackground(args.vg);
drawOutputJacksArea(args.vg, numIO);
drawOutputJacksArea(args.vg, 8);
setupTextLines(args.vg);

if (numIO == 2)
for (int i=0; i<8; ++i)
{
drawTextLine(args.vg, 0, "Left/M");
drawTextLine(args.vg, 1, "Right");
}
else
{
for (int i=0; i<numIO; ++i)
{
char text[] = {'A','u','d','i','o',' ',static_cast<char>('0'+i+1),'\0'};
drawTextLine(args.vg, i, text);
}
char text[] = {'A','u','d','i','o',' ',static_cast<char>('0'+i+1),'\0'};
drawTextLine(args.vg, i, text);
}

ModuleWidgetWith8HP::draw(args);
}
};

void appendContextMenu(Menu* const menu) override {
menu->addChild(new MenuSeparator);
menu->addChild(createBoolPtrMenuItem("DC blocker", "", &module->dcFilterEnabled));
#else
// --------------------------------------------------------------------------------------------------------------------

struct HostAudioWidget2 : ModuleWidget {
HostAudioWidget2(HostAudio2* const module) {
setModule(module);
for (uint i=0; i<2; ++i) {
addInput(createInput<PJ301MPort>({}, module, i));
addOutput(createOutput<PJ301MPort>({}, module, i));
}
}
};
struct HostAudioWidget8 : ModuleWidget {
HostAudioWidget8(HostAudio8* const module) {
setModule(module);
for (uint i=0; i<8; ++i) {
addInput(createInput<PJ301MPort>({}, module, i));
addOutput(createOutput<PJ301MPort>({}, module, i));
}
}
};

// --------------------------------------------------------------------------------------------------------------------
#endif

Model* modelHostAudio2 = createModel<HostAudio<2>, HostAudioWidget<2>>("HostAudio2");
Model* modelHostAudio8 = createModel<HostAudio<8>, HostAudioWidget<8>>("HostAudio8");
Model* modelHostAudio2 = createModel<HostAudio2, HostAudioWidget2>("HostAudio2");
Model* modelHostAudio8 = createModel<HostAudio8, HostAudioWidget8>("HostAudio8");

// --------------------------------------------------------------------------------------------------------------------

+ 23
- 8
plugins/Cardinal/src/HostCV.cpp View File

@@ -27,7 +27,7 @@ USE_NAMESPACE_DISTRHO;
struct HostCV : TerminalModule {
CardinalPluginContext* const pcontext;
int dataFrame = 0;
int64_t lastBlockFrame = -1;
uint32_t lastProcessCounter = 0;

enum ParamIds {
BIPOLAR_INPUTS_1_5,
@@ -64,18 +64,19 @@ struct HostCV : TerminalModule {
if (pcontext->variant != kCardinalVariantMain)
return;

const int64_t blockFrame = pcontext->engine->getBlockFrame();
const uint32_t bufferSize = pcontext->bufferSize;
const uint32_t processCounter = pcontext->processCounter;

// only checked on input
if (lastBlockFrame != blockFrame)
if (lastProcessCounter != processCounter)
{
dataFrame = 0;
lastBlockFrame = blockFrame;
lastProcessCounter = processCounter;
}

// only incremented on output
const int k = dataFrame;
DISTRHO_SAFE_ASSERT_RETURN(k < pcontext->engine->getBlockFrames(),);
const uint32_t k = dataFrame;
DISTRHO_SAFE_ASSERT_RETURN(k < bufferSize,);

if (isBypassed())
{
@@ -102,9 +103,11 @@ struct HostCV : TerminalModule {
if (pcontext->variant != kCardinalVariantMain)
return;

const uint32_t bufferSize = pcontext->bufferSize;

// only incremented on output
const int k = dataFrame++;
DISTRHO_SAFE_ASSERT_RETURN(k < pcontext->engine->getBlockFrames(),);
const uint32_t k = dataFrame++;
DISTRHO_SAFE_ASSERT_RETURN(k < bufferSize,);

if (isBypassed())
return;
@@ -124,6 +127,7 @@ struct HostCV : TerminalModule {
}
};

#ifndef HEADLESS
struct HostCVWidget : ModuleWidgetWith8HP {
HostCVWidget(HostCV* const module)
{
@@ -184,6 +188,17 @@ struct HostCVWidget : ModuleWidgetWith8HP {
));
}
};
#else
struct HostCVWidget : ModuleWidget {
HostCVWidget(HostCV* const module) {
setModule(module);
for (uint i=0; i<HostCV::NUM_INPUTS; ++i)
addInput(createInput<PJ301MPort>({}, module, i));
for (uint i=0; i<HostCV::NUM_OUTPUTS; ++i)
addOutput(createOutput<PJ301MPort>({}, module, i));
}
};
#endif

// --------------------------------------------------------------------------------------------------------------------



+ 93
- 67
plugins/Cardinal/src/HostMIDI-CC.cpp View File

@@ -62,13 +62,13 @@ struct HostMIDICC : TerminalModule {
const MidiEvent* midiEvents;
uint32_t midiEventsLeft;
uint32_t midiEventFrame;
int64_t lastBlockFrame;
uint32_t lastProcessCounter;
uint8_t channel;

uint8_t chPressure[16];
uint16_t pitchbend[16];

// stuff from Rack
// adapted from Rack
/** [cc][channel] */
uint8_t ccValues[128][16];
/** When LSB is enabled for CC 0-31, the MSB is stored here until the LSB is received.
@@ -85,10 +85,11 @@ struct HostMIDICC : TerminalModule {
MidiInput(CardinalPluginContext* const pc)
: pcontext(pc)
{
for (int i = 0; i < NUM_OUTPUTS; i++) {
for (int c = 0; c < 16; c++) {
valueFilters[i][c].setTau(1 / 30.f);
}
// adapted from Rack
for (int id = 0; id < NUM_OUTPUTS; ++id)
{
for (int c = 0; c < 16; ++c)
valueFilters[id][c].setTau(1 / 30.f);
}
reset();
}
@@ -98,40 +99,33 @@ struct HostMIDICC : TerminalModule {
midiEvents = nullptr;
midiEventsLeft = 0;
midiEventFrame = 0;
lastBlockFrame = -1;
lastProcessCounter = 0;
channel = 0;

for (int cc = 0; cc < 128; cc++) {
for (int c = 0; c < 16; c++) {
ccValues[cc][c] = 0;
}
}
for (int cc = 0; cc < 32; cc++) {
for (int c = 0; c < 16; c++) {
msbValues[cc][c] = 0;
}
}
for (int c = 0; c < 16; c++) {
chPressure[c] = 0;
// adapted from Rack
std::memset(ccValues, 0, sizeof(ccValues));
std::memset(msbValues, 0, sizeof(msbValues));
std::memset(chPressure, 0, sizeof(chPressure));

for (int c = 0; c < 16; ++c)
pitchbend[c] = 8192;
}

learningId = -1;
smooth = true;
mpeMode = false;
lsbMode = false;
}

bool process(const ProcessArgs& args, std::vector<rack::engine::Output>& outputs, int learnedCcs[16],
bool process(const ProcessArgs& args, std::vector<rack::engine::Output>& outputs, int8_t learnedCcs[16],
const bool isBypassed)
{
// Cardinal specific
const int64_t blockFrame = pcontext->engine->getBlockFrame();
const bool blockFrameChanged = lastBlockFrame != blockFrame;
const uint32_t processCounter = pcontext->processCounter;
const bool processCounterChanged = lastProcessCounter != processCounter;

if (blockFrameChanged)
if (processCounterChanged)
{
lastBlockFrame = blockFrame;

lastProcessCounter = processCounter;
midiEvents = pcontext->midiEvents;
midiEventsLeft = pcontext->midiEventCount;
midiEventFrame = 0;
@@ -166,27 +160,38 @@ struct HostMIDICC : TerminalModule {
const uint8_t status = data[0] & 0xF0;
const uint8_t chan = data[0] & 0x0F;

/**/ if (status == 0xD0)
if (status == 0xD0)
{
chPressure[chan] = data[1];
continue;
}
else if (status == 0xE0)
if (status == 0xE0)
{
pitchbend[chan] = (data[2] << 7) | data[1];
continue;
}
else if (status != 0xB0)
if (status != 0xB0)
{
continue;
}

// adapted from Rack
// adapted from Rack `processCC`
const uint8_t c = mpeMode ? chan : 0;
const uint8_t cc = data[1];
const int8_t cc = data[1];
const uint8_t value = data[2];

// Learn
if (learningId >= 0 && ccValues[cc][c] != value)
{
// NOTE: does the same as `setLearnedCc`
if (cc >= 0)
{
for (int id = 0; id < 16; ++id)
{
if (learnedCcs[id] == cc)
learnedCcs[id] = -1;
}
}
learnedCcs[learningId] = cc;
learningId = -1;
}
@@ -196,7 +201,7 @@ struct HostMIDICC : TerminalModule {
// Don't set MSB yet. Wait for LSB to be received.
msbValues[cc][c] = value;
}
else if (lsbMode && 32 <= cc && cc < 64)
else if (lsbMode && cc >= 32 && cc < 64)
{
// Apply MSB when LSB is received
ccValues[cc - 32][c] = msbValues[cc - 32][c];
@@ -213,15 +218,21 @@ struct HostMIDICC : TerminalModule {
// Rack stuff
const int channels = mpeMode ? 16 : 1;

for (int i = 0; i < 16; i++)
for (int id = 0; id < 16; ++id)
{
if (!outputs[CC_OUTPUT + i].isConnected())
if (!outputs[CC_OUTPUT + id].isConnected())
continue;
outputs[CC_OUTPUT + i].setChannels(channels);
outputs[CC_OUTPUT + id].setChannels(channels);

int cc = learnedCcs[i];
const int8_t cc = learnedCcs[id];

for (int c = 0; c < channels; c++)
if (cc < 0)
{
outputs[CC_OUTPUT + id].clearVoltages();
continue;
}

for (int c = 0; c < channels; ++c)
{
int16_t cellValue = int16_t(ccValues[cc][c]) * 128;
if (lsbMode && cc < 32)
@@ -231,18 +242,18 @@ struct HostMIDICC : TerminalModule {
const float value = static_cast<float>(cellValue) / (128.0f * 127.0f);

// Detect behavior from MIDI buttons.
if (smooth && std::fabs(valueFilters[i][c].out - value) < 1.f)
if (smooth && std::fabs(valueFilters[id][c].out - value) < 1.f)
{
// Smooth value with filter
valueFilters[i][c].process(args.sampleTime, value);
valueFilters[id][c].process(args.sampleTime, value);
}
else
{
// Jump value
valueFilters[i][c].out = value;
valueFilters[id][c].out = value;
}

outputs[CC_OUTPUT + i].setVoltage(valueFilters[i][c].out * 10.f, c);
outputs[CC_OUTPUT + id].setVoltage(valueFilters[id][c].out * 10.f, c);
}
}

@@ -250,7 +261,7 @@ struct HostMIDICC : TerminalModule {
{
outputs[CC_OUTPUT_CH_PRESSURE].setChannels(channels);

for (int c = 0; c < channels; c++)
for (int c = 0; c < channels; ++c)
{
const float value = static_cast<float>(chPressure[c]) / 128.0f;

@@ -274,7 +285,7 @@ struct HostMIDICC : TerminalModule {
{
outputs[CC_OUTPUT_PITCHBEND].setChannels(channels);

for (int c = 0; c < channels; c++)
for (int c = 0; c < channels; ++c)
{
const float value = static_cast<float>(pitchbend[c]) / 16384.0f;

@@ -294,7 +305,7 @@ struct HostMIDICC : TerminalModule {
}
}

return blockFrameChanged;
return processCounterChanged;
}

} midiInput;
@@ -365,7 +376,7 @@ struct HostMIDICC : TerminalModule {

} midiOutput;

int learnedCcs[16];
int8_t learnedCcs[16];

HostMIDICC()
: pcontext(static_cast<CardinalPluginContext*>(APP)),
@@ -377,14 +388,14 @@ struct HostMIDICC : TerminalModule {

config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);

for (int i = 0; i < 16; i++)
configInput(CC_INPUTS + i, string::f("Cell %d", i + 1));
for (int id = 0; id < 16; ++id)
configInput(CC_INPUTS + id, string::f("Cell %d", id + 1));

configInput(CC_INPUT_CH_PRESSURE, "Channel pressure");
configInput(CC_INPUT_PITCHBEND, "Pitchbend");

for (int i = 0; i < 16; i++)
configOutput(CC_OUTPUT + i, string::f("Cell %d", i + 1));
for (int id = 0; id < 16; ++id)
configOutput(CC_OUTPUT + id, string::f("Cell %d", id + 1));

configOutput(CC_OUTPUT_CH_PRESSURE, "Channel pressure");
configOutput(CC_OUTPUT_PITCHBEND, "Pitchbend");
@@ -394,9 +405,8 @@ struct HostMIDICC : TerminalModule {

void onReset() override
{
for (int i = 0; i < 16; i++) {
learnedCcs[i] = i + 1;
}
for (int id = 0; id < 16; ++id)
learnedCcs[id] = id + 1;
midiInput.reset();
midiOutput.reset();
}
@@ -414,11 +424,13 @@ struct HostMIDICC : TerminalModule {
if (isBypassed())
return;

for (int i = 0; i < 16; i++)
for (int id = 0; id < 16; ++id)
{
int value = (int) std::round(inputs[CC_INPUTS + i].getVoltage() / 10.f * 127);
value = clamp(value, 0, 127);
midiOutput.sendCC(learnedCcs[i], value);
if (learnedCcs[id] < 0)
continue;

uint8_t value = (uint8_t) clamp(std::round(inputs[CC_INPUTS + id].getVoltage() / 10.f * 127), 0.f, 127.f);
midiOutput.sendCC(learnedCcs[id], value);
}

{
@@ -434,6 +446,20 @@ struct HostMIDICC : TerminalModule {
}
}

void setLearnedCc(const int id, const int8_t cc)
{
// Unset IDs of similar CCs
if (cc >= 0)
{
for (int idx = 0; idx < 16; ++idx)
{
if (learnedCcs[idx] == cc)
learnedCcs[idx] = -1;
}
}
learnedCcs[id] = cc;
}

json_t* dataToJson() override
{
json_t* const rootJ = json_object();
@@ -442,8 +468,8 @@ struct HostMIDICC : TerminalModule {
// input and output
if (json_t* const ccsJ = json_array())
{
for (int i = 0; i < 16; i++)
json_array_append_new(ccsJ, json_integer(learnedCcs[i]));
for (int id = 0; id < 16; ++id)
json_array_append_new(ccsJ, json_integer(learnedCcs[id]));
json_object_set_new(rootJ, "ccs", ccsJ);
}

@@ -473,12 +499,12 @@ struct HostMIDICC : TerminalModule {
// input and output
if (json_t* const ccsJ = json_object_get(rootJ, "ccs"))
{
for (int i = 0; i < 16; i++)
for (int id = 0; id < 16; ++id)
{
if (json_t* const ccJ = json_array_get(ccsJ, i))
learnedCcs[i] = json_integer_value(ccJ);
if (json_t* const ccJ = json_array_get(ccsJ, id))
setLearnedCc(id, json_integer_value(ccJ));
else
learnedCcs[i] = i + 1;
learnedCcs[id] = -1;
}
}

@@ -524,7 +550,7 @@ struct HostMIDICC : TerminalModule {
struct CardinalCcChoice : CardinalLedDisplayChoice {
HostMIDICC* const module;
const int id;
int focusCc = -1;
int8_t focusCc = -1;

CardinalCcChoice(HostMIDICC* const m, const int i)
: CardinalLedDisplayChoice(),
@@ -540,7 +566,7 @@ struct CardinalCcChoice : CardinalLedDisplayChoice {

void step() override
{
int cc;
int8_t cc;

if (module == nullptr)
{
@@ -583,8 +609,8 @@ struct CardinalCcChoice : CardinalLedDisplayChoice {

if (module->midiInput.learningId == id)
{
if (0 <= focusCc && focusCc < 128)
module->learnedCcs[id] = focusCc;
if (focusCc >= 0)
module->setLearnedCc(id, focusCc);
module->midiInput.learningId = -1;
}
}
@@ -600,7 +626,7 @@ struct CardinalCcChoice : CardinalLedDisplayChoice {
focusCc = focusCc * 10 + (c - '0');
}

if (focusCc >= 128)
if (focusCc < 0)
focusCc = -1;

e.consume(this);


+ 82
- 53
plugins/Cardinal/src/HostMIDI-Gate.cpp View File

@@ -58,7 +58,7 @@ struct HostMIDIGate : TerminalModule {
const MidiEvent* midiEvents;
uint32_t midiEventsLeft;
uint32_t midiEventFrame;
int64_t lastBlockFrame;
uint32_t lastProcessCounter;
uint8_t channel;

// stuff from Rack
@@ -84,7 +84,7 @@ struct HostMIDIGate : TerminalModule {
midiEvents = nullptr;
midiEventsLeft = 0;
midiEventFrame = 0;
lastBlockFrame = -1;
lastProcessCounter = 0;
channel = 0;
learningId = -1;
mpeMode = false;
@@ -104,16 +104,15 @@ struct HostMIDIGate : TerminalModule {
}

bool process(const ProcessArgs& args, std::vector<rack::engine::Output>& outputs,
const bool velocityMode, uint8_t learnedNotes[18], const bool isBypassed)
const bool velocityMode, int8_t learnedNotes[18], const bool isBypassed)
{
// Cardinal specific
const int64_t blockFrame = pcontext->engine->getBlockFrame();
const bool blockFrameChanged = lastBlockFrame != blockFrame;
const uint32_t processCounter = pcontext->processCounter;
const bool processCounterChanged = lastProcessCounter != processCounter;

if (blockFrameChanged)
if (processCounterChanged)
{
lastBlockFrame = blockFrame;

lastProcessCounter = processCounter;
midiEvents = pcontext->midiEvents;
midiEventsLeft = pcontext->midiEventCount;
midiEventFrame = 0;
@@ -122,7 +121,7 @@ struct HostMIDIGate : TerminalModule {
if (isBypassed)
{
++midiEventFrame;
return blockFrameChanged;
return processCounterChanged;
}

while (midiEventsLeft != 0)
@@ -153,17 +152,30 @@ struct HostMIDIGate : TerminalModule {
if (data[2] > 0)
{
const int c = mpeMode ? (data[0] & 0x0F) : 0;
const int8_t note = data[1];
// Learn
if (learningId >= 0) {
learnedNotes[learningId] = data[1];
if (learningId >= 0)
{
// NOTE: does the same as `setLearnedNote`
if (note >= 0)
{
for (int id = 0; id < 18; ++id)
{
if (learnedNotes[id] == note)
learnedNotes[id] = -1;
}
}
learnedNotes[learningId] = note;
learningId = -1;
}
// Find id
for (int i = 0; i < 18; i++) {
if (learnedNotes[i] == data[1]) {
gates[i][c] = true;
gateTimes[i][c] = 1e-3f;
velocities[i][c] = data[2];
for (int id = 0; id < 18; ++id)
{
if (learnedNotes[id] == note)
{
gates[id][c] = true;
gateTimes[id][c] = 1e-3f;
velocities[id][c] = data[2];
}
}
break;
@@ -172,11 +184,12 @@ struct HostMIDIGate : TerminalModule {
// note off
case 0x80:
const int c = mpeMode ? (data[0] & 0x0F) : 0;
const int8_t note = data[1];
// Find id
for (int i = 0; i < 18; i++) {
if (learnedNotes[i] == data[1]) {
gates[i][c] = false;
}
for (int id = 0; id < 18; ++id)
{
if (learnedNotes[id] == note)
gates[id][c] = false;
}
break;
}
@@ -206,7 +219,7 @@ struct HostMIDIGate : TerminalModule {
}
}

return blockFrameChanged;
return processCounterChanged;
}

} midiInput;
@@ -217,7 +230,7 @@ struct HostMIDIGate : TerminalModule {
uint8_t channel = 0;

// base class vars
int vels[128];
uint8_t vels[128];
bool lastGates[128];
int64_t frame = 0;

@@ -230,7 +243,7 @@ struct HostMIDIGate : TerminalModule {
void reset()
{
// base class vars
for (int note = 0; note < 128; ++note)
for (uint8_t note = 0; note < 128; ++note)
{
vels[note] = 100;
lastGates[note] = false;
@@ -245,7 +258,7 @@ struct HostMIDIGate : TerminalModule {
// TODO send all notes off CC

// Send all note off commands
for (int note = 0; note < 128; note++)
for (uint8_t note = 0; note < 128; note++)
{
// Note off
midi::Message m;
@@ -258,12 +271,12 @@ struct HostMIDIGate : TerminalModule {
}
}

void setVelocity(int vel, int note)
void setVelocity(uint8_t note, uint8_t vel)
{
vels[note] = vel;
}

void setGate(bool gate, int note)
void setGate(uint8_t note, bool gate)
{
if (gate && !lastGates[note])
{
@@ -296,7 +309,8 @@ struct HostMIDIGate : TerminalModule {
} midiOutput;

bool velocityMode = false;
uint8_t learnedNotes[18] = {};
int8_t learnedNotes[18] = {};
dsp::SchmittTrigger cellTriggers[18];

HostMIDIGate()
: pcontext(static_cast<CardinalPluginContext*>(APP)),
@@ -308,19 +322,19 @@ struct HostMIDIGate : TerminalModule {

config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);

for (int i = 0; i < 18; i++)
configInput(GATE_INPUTS + i, string::f("Gate %d", i + 1));
for (int id = 0; id < 18; ++id)
configInput(GATE_INPUTS + id, string::f("Gate %d", id + 1));

for (int i = 0; i < 18; i++)
configOutput(GATE_OUTPUTS + i, string::f("Gate %d", i + 1));
for (int id = 0; id < 18; ++id)
configOutput(GATE_OUTPUTS + id, string::f("Gate %d", id + 1));

onReset();
}

void onReset() override
{
for (int i = 0; i < 18; ++i)
learnedNotes[i] = 36 + i;
for (int id = 0; id < 18; ++id)
learnedNotes[id] = 36 + id;

velocityMode = false;

@@ -341,24 +355,39 @@ struct HostMIDIGate : TerminalModule {
if (isBypassed())
return;

for (int i = 0; i < 18; ++i)
for (int id = 0; id < 18; ++id)
{
const int note = learnedNotes[i];
const int8_t note = learnedNotes[id];

if (note < 0)
continue;

if (velocityMode)
{
int vel = (int) std::round(inputs[GATE_INPUTS + i].getVoltage() / 10.f * 127);
vel = clamp(vel, 0, 127);
midiOutput.setVelocity(vel, note);
midiOutput.setGate(vel > 0, note);
uint8_t vel = (uint8_t) clamp(std::round(inputs[GATE_INPUTS + id].getVoltage() / 10.f * 127), 0.f, 127.f);
midiOutput.setVelocity(note, vel);
midiOutput.setGate(note, vel > 0);
}
else
{
const bool gate = inputs[GATE_INPUTS + i].getVoltage() >= 1.f;
midiOutput.setVelocity(100, note);
midiOutput.setGate(gate, note);
const bool gate = inputs[GATE_INPUTS + id].getVoltage() >= 1.f;
midiOutput.setVelocity(note, 100);
midiOutput.setGate(note, gate);
}
}
}

void setLearnedNote(const int id, const int8_t note) {
// Unset IDs of similar note
if (note >= 0)
{
for (int idx = 0; idx < 18; ++idx)
{
if (learnedNotes[idx] == note)
learnedNotes[idx] = -1;
}
}
learnedNotes[id] = note;
}

json_t* dataToJson() override
@@ -369,8 +398,8 @@ struct HostMIDIGate : TerminalModule {
// input and output
if (json_t* const notesJ = json_array())
{
for (int i = 0; i < 18; i++)
json_array_append_new(notesJ, json_integer(learnedNotes[i]));
for (int id = 0; id < 18; ++id)
json_array_append_new(notesJ, json_integer(learnedNotes[id]));
json_object_set_new(rootJ, "notes", notesJ);
}
json_object_set_new(rootJ, "velocity", json_boolean(velocityMode));
@@ -390,12 +419,12 @@ struct HostMIDIGate : TerminalModule {
// input and output
if (json_t* const notesJ = json_object_get(rootJ, "notes"))
{
for (int i = 0; i < 18; i++)
for (int id = 0; id < 18; ++id)
{
if (json_t* const noteJ = json_array_get(notesJ, i))
learnedNotes[i] = json_integer_value(noteJ);
if (json_t* const noteJ = json_array_get(notesJ, id))
setLearnedNote(id, json_integer_value(noteJ));
else
learnedNotes[i] = -1;
learnedNotes[id] = -1;
}
}

@@ -430,7 +459,7 @@ struct HostMIDIGate : TerminalModule {
struct CardinalNoteChoice : CardinalLedDisplayChoice {
HostMIDIGate* const module;
const int id;
int focusNote = -1;
int8_t focusNote = -1;

CardinalNoteChoice(HostMIDIGate* const m, const int i)
: CardinalLedDisplayChoice(),
@@ -439,7 +468,7 @@ struct CardinalNoteChoice : CardinalLedDisplayChoice {

void step() override
{
int note;
int8_t note;

if (module == nullptr)
{
@@ -489,8 +518,8 @@ struct CardinalNoteChoice : CardinalLedDisplayChoice {

if (module->midiInput.learningId == id)
{
if (0 <= focusNote && focusNote < 128)
module->learnedNotes[id] = focusNote;
if (focusNote >= 0)
module->setLearnedNote(id, focusNote);
module->midiInput.learningId = -1;
}
}
@@ -518,7 +547,7 @@ struct CardinalNoteChoice : CardinalLedDisplayChoice {
}
}

if (focusNote >= 128)
if (focusNote < 0)
focusNote = -1;

e.consume(this);


+ 6
- 7
plugins/Cardinal/src/HostMIDI-Map.cpp View File

@@ -55,7 +55,7 @@ struct HostMIDIMap : TerminalModule {
const MidiEvent* midiEvents;
uint32_t midiEventsLeft;
uint32_t midiEventFrame;
int64_t lastBlockFrame;
uint32_t lastProcessCounter;
int nextLearningId;
uint8_t channel;

@@ -117,7 +117,7 @@ struct HostMIDIMap : TerminalModule {
midiEvents = nullptr;
midiEventsLeft = 0;
midiEventFrame = 0;
lastBlockFrame = -1;
lastProcessCounter = 0;
nextLearningId = -1;
channel = 0;

@@ -134,13 +134,12 @@ struct HostMIDIMap : TerminalModule {
void processTerminalInput(const ProcessArgs& args) override
{
// Cardinal specific
const int64_t blockFrame = pcontext->engine->getBlockFrame();
const bool blockFrameChanged = lastBlockFrame != blockFrame;
const uint32_t processCounter = pcontext->processCounter;
const bool processCounterChanged = lastProcessCounter != processCounter;

if (blockFrameChanged)
if (processCounterChanged)
{
lastBlockFrame = blockFrame;

lastProcessCounter = processCounter;
midiEvents = pcontext->midiEvents;
midiEventsLeft = pcontext->midiEventCount;
midiEventFrame = 0;


+ 146
- 59
plugins/Cardinal/src/HostMIDI.cpp View File

@@ -81,11 +81,13 @@ struct HostMIDI : TerminalModule {
const MidiEvent* midiEvents;
uint32_t midiEventsLeft;
uint32_t midiEventFrame;
int64_t lastBlockFrame;
uint32_t lastProcessCounter;
bool wasPlaying;
uint8_t channel;

// stuff from Rack
/** Number of semitones to bend up/down by pitch wheel */
float pwRange;
bool smooth;
int channels;
enum PolyMode {
@@ -120,6 +122,7 @@ struct HostMIDI : TerminalModule {
dsp::PulseGenerator startPulse;
dsp::PulseGenerator stopPulse;
dsp::PulseGenerator continuePulse;
dsp::PulseGenerator retriggerPulses[16];

MidiInput(CardinalPluginContext* const pc)
: pcontext(pc)
@@ -138,12 +141,13 @@ struct HostMIDI : TerminalModule {
midiEvents = nullptr;
midiEventsLeft = 0;
midiEventFrame = 0;
lastBlockFrame = -1;
lastProcessCounter = 0;
wasPlaying = false;
channel = 0;
smooth = true;
smooth = false;
channels = 1;
polyMode = ROTATE_MODE;
pwRange = 0;
panic();
}

@@ -168,12 +172,12 @@ struct HostMIDI : TerminalModule {
bool process(const ProcessArgs& args, std::vector<rack::engine::Output>& outputs, const bool isBypassed)
{
// Cardinal specific
const int64_t blockFrame = pcontext->engine->getBlockFrame();
const bool blockFrameChanged = lastBlockFrame != blockFrame;
const uint32_t processCounter = pcontext->processCounter;
const bool processCounterChanged = lastProcessCounter != processCounter;

if (blockFrameChanged)
if (processCounterChanged)
{
lastBlockFrame = blockFrame;
lastProcessCounter = processCounter;

midiEvents = pcontext->midiEvents;
midiEventsLeft = pcontext->midiEventCount;
@@ -246,30 +250,20 @@ struct HostMIDI : TerminalModule {
++midiEventFrame;

// Rack stuff
outputs[PITCH_OUTPUT].setChannels(channels);
outputs[GATE_OUTPUT].setChannels(channels);
outputs[VELOCITY_OUTPUT].setChannels(channels);
outputs[AFTERTOUCH_OUTPUT].setChannels(channels);

for (int c = 0; c < channels; c++) {
outputs[PITCH_OUTPUT].setVoltage((notes[c] - 60.f) / 12.f, c);
outputs[GATE_OUTPUT].setVoltage(gates[c] ? 10.f : 0.f, c);
outputs[VELOCITY_OUTPUT].setVoltage(rescale(velocities[c], 0, 127, 0.f, 10.f), c);
outputs[AFTERTOUCH_OUTPUT].setVoltage(rescale(aftertouches[c], 0, 127, 0.f, 10.f), c);
}

// Set pitch and mod wheel
const int wheelChannels = (polyMode == MPE_MODE) ? 16 : 1;
float pwValues[16] = {};
outputs[PITCHBEND_OUTPUT].setChannels(wheelChannels);
outputs[MODWHEEL_OUTPUT].setChannels(wheelChannels);
for (int c = 0; c < wheelChannels; c++) {
float pw = ((int) pws[c] - 8192) / 8191.f;
float pw = (int16_t(pws[c]) - 8192) / 8191.f;
pw = clamp(pw, -1.f, 1.f);
if (smooth)
pw = pwFilters[c].process(args.sampleTime, pw);
else
pwFilters[c].out = pw;
outputs[PITCHBEND_OUTPUT].setVoltage(pw * 5.f);
pwValues[c] = pw;
outputs[PITCHBEND_OUTPUT].setVoltage(pw * 5.f, c);

float mod = mods[c] / 127.f;
mod = clamp(mod, 0.f, 1.f);
@@ -277,14 +271,31 @@ struct HostMIDI : TerminalModule {
mod = modFilters[c].process(args.sampleTime, mod);
else
modFilters[c].out = mod;
outputs[MODWHEEL_OUTPUT].setVoltage(mod * 10.f);
outputs[MODWHEEL_OUTPUT].setVoltage(mod * 10.f, c);
}

// Set note outputs
outputs[PITCH_OUTPUT].setChannels(channels);
outputs[GATE_OUTPUT].setChannels(channels);
outputs[VELOCITY_OUTPUT].setChannels(channels);
outputs[AFTERTOUCH_OUTPUT].setChannels(channels);
outputs[RETRIGGER_OUTPUT].setChannels(channels);

for (int c = 0; c < channels; c++) {
float pw = pwValues[(polyMode == MPE_MODE) ? c : 0];
float pitch = (notes[c] - 60.f + pw * pwRange) / 12.f;
outputs[PITCH_OUTPUT].setVoltage(pitch, c);
outputs[GATE_OUTPUT].setVoltage(gates[c] ? 10.f : 0.f, c);
outputs[VELOCITY_OUTPUT].setVoltage(rescale(velocities[c], 0, 127, 0.f, 10.f), c);
outputs[AFTERTOUCH_OUTPUT].setVoltage(rescale(aftertouches[c], 0, 127, 0.f, 10.f), c);
outputs[RETRIGGER_OUTPUT].setVoltage(retriggerPulses[c].process(args.sampleTime) ? 10.f : 0.f, c);
}

outputs[START_OUTPUT].setVoltage(startPulse.process(args.sampleTime) ? 10.f : 0.f);
outputs[STOP_OUTPUT].setVoltage(stopPulse.process(args.sampleTime) ? 10.f : 0.f);
outputs[CONTINUE_OUTPUT].setVoltage(continuePulse.process(args.sampleTime) ? 10.f : 0.f);

return blockFrameChanged;
return processCounterChanged;
}

void processMessage(const midi::Message& msg)
@@ -452,6 +463,7 @@ struct HostMIDI : TerminalModule {
// Set note
notes[*channel] = note;
gates[*channel] = true;
retriggerPulses[*channel].trigger(1e-3);
}

void releaseNote(uint8_t note) {
@@ -533,6 +545,18 @@ struct HostMIDI : TerminalModule {
CardinalPluginContext* const pcontext;
uint8_t channel = 0;

// caching
struct {
bool gate = false;
bool velocity = false;
bool aftertouch = false;
bool pitchbend = false;
bool modwheel = false;
bool start = false;
bool stop = false;
bool cont = false;
} connected;

MidiOutput(CardinalPluginContext* const pc)
: pcontext(pc) {}

@@ -587,9 +611,21 @@ struct HostMIDI : TerminalModule {
void processTerminalInput(const ProcessArgs& args) override
{
if (midiInput.process(args, outputs, isBypassed()))
{
midiOutput.frame = 0;
midiOutput.connected.gate = inputs[GATE_INPUT].isConnected();
midiOutput.connected.velocity = inputs[VELOCITY_INPUT].isConnected();
midiOutput.connected.aftertouch = inputs[AFTERTOUCH_INPUT].isConnected();
midiOutput.connected.pitchbend = inputs[PITCHBEND_INPUT].isConnected();
midiOutput.connected.modwheel = inputs[MODWHEEL_INPUT].isConnected();
midiOutput.connected.start = inputs[START_INPUT].isConnected();
midiOutput.connected.stop = inputs[STOP_INPUT].isConnected();
midiOutput.connected.cont = inputs[CONTINUE_INPUT].isConnected();
}
else
{
++midiOutput.frame;
}
}

void processTerminalOutput(const ProcessArgs&) override
@@ -597,37 +633,67 @@ struct HostMIDI : TerminalModule {
if (isBypassed())
return;

auto connected = midiOutput.connected;

for (int c = 0; c < inputs[PITCH_INPUT].getChannels(); ++c)
{
int vel = (int) std::round(inputs[VELOCITY_INPUT].getNormalPolyVoltage(10.f * 100 / 127, c) / 10.f * 127);
vel = clamp(vel, 0, 127);
midiOutput.setVelocity(vel, c);
if (connected.velocity)
{
const constexpr float n = 10.f * 100.f / 127.f;
const int vel = clamp(
static_cast<int>(inputs[VELOCITY_INPUT].getNormalPolyVoltage(n, c) / 10.f * 127.f + 0.5f), 0, 127);
midiOutput.setVelocity(vel, c);
}
else
{
midiOutput.setVelocity(100, c);
}

int note = (int) std::round(inputs[PITCH_INPUT].getVoltage(c) * 12.f + 60.f);
note = clamp(note, 0, 127);
bool gate = inputs[GATE_INPUT].getPolyVoltage(c) >= 1.f;
const int note = clamp(static_cast<int>(inputs[PITCH_INPUT].getVoltage(c) * 12.f + 60.5f), 0, 127);
const bool gate = connected.gate ? inputs[GATE_INPUT].getPolyVoltage(c) >= 1.f : false;
midiOutput.setNoteGate(note, gate, c);

int aft = (int) std::round(inputs[AFTERTOUCH_INPUT].getPolyVoltage(c) / 10.f * 127);
aft = clamp(aft, 0, 127);
midiOutput.setKeyPressure(aft, c);
if (connected.aftertouch)
{
const int aft = clamp(
static_cast<int>(inputs[AFTERTOUCH_INPUT].getPolyVoltage(c) / 10.f * 127.f + 0.5f), 0, 127);
midiOutput.setKeyPressure(aft, c);
}
else
{
midiOutput.setKeyPressure(0, c);
}
}

int pw = (int) std::round((inputs[PITCHBEND_INPUT].getVoltage() + 5.f) / 10.f * 16383);
pw = clamp(pw, 0, 16383);
midiOutput.setPitchWheel(pw);
if (connected.pitchbend)
{
const int pw = clamp(
static_cast<int>((inputs[PITCHBEND_INPUT].getVoltage() + 5.f) / 10.f * 16383.f + 0.5f), 0, 16383);
midiOutput.setPitchWheel(pw);
}
else
{
midiOutput.setPitchWheel(0);
}

int mw = (int) std::round(inputs[MODWHEEL_INPUT].getVoltage() / 10.f * 127);
mw = clamp(mw, 0, 127);
midiOutput.setModWheel(mw);
if (connected.modwheel)
{
const int mw = clamp(
static_cast<int>(inputs[MODWHEEL_INPUT].getVoltage() / 10.f * 127.f + 0.5f), 0, 127);
midiOutput.setModWheel(mw);
}
else
{
midiOutput.setModWheel(0);
}

bool start = inputs[START_INPUT].getVoltage() >= 1.f;
const bool start = connected.start ? inputs[START_INPUT].getVoltage() >= 1.f : false;
midiOutput.setStart(start);

bool stop = inputs[STOP_INPUT].getVoltage() >= 1.f;
const bool stop = connected.stop ? inputs[STOP_INPUT].getVoltage() >= 1.f : false;
midiOutput.setStop(stop);

bool cont = inputs[CONTINUE_INPUT].getVoltage() >= 1.f;
const bool cont = connected.cont ? inputs[CONTINUE_INPUT].getVoltage() >= 1.f : false;
midiOutput.setContinue(cont);
}

@@ -636,6 +702,7 @@ struct HostMIDI : TerminalModule {
json_t* const rootJ = json_object();
DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr);

json_object_set_new(rootJ, "pwRange", json_real(midiInput.pwRange));
json_object_set_new(rootJ, "smooth", json_boolean(midiInput.smooth));
json_object_set_new(rootJ, "channels", json_integer(midiInput.channels));
json_object_set_new(rootJ, "polyMode", json_integer(midiInput.polyMode));
@@ -655,6 +722,12 @@ struct HostMIDI : TerminalModule {

void dataFromJson(json_t* const rootJ) override
{
if (json_t* const pwRangeJ = json_object_get(rootJ, "pwRange"))
midiInput.pwRange = json_number_value(pwRangeJ);
// For backwards compatibility, set to 0 if undefined in JSON.
else
midiInput.pwRange = 0;

if (json_t* const smoothJ = json_object_get(rootJ, "smooth"))
midiInput.smooth = json_boolean_value(smoothJ);

@@ -680,6 +753,7 @@ struct HostMIDI : TerminalModule {

// --------------------------------------------------------------------------------------------------------------------

#ifndef HEADLESS
struct HostMIDIWidget : ModuleWidgetWith9HP {
HostMIDI* const module;

@@ -710,12 +784,13 @@ struct HostMIDIWidget : ModuleWidgetWith9HP {
createAndAddOutput(6, HostMIDI::START_OUTPUT);
createAndAddOutput(7, HostMIDI::STOP_OUTPUT);
createAndAddOutput(8, HostMIDI::CONTINUE_OUTPUT);
createAndAddOutput(9, HostMIDI::RETRIGGER_OUTPUT);
}

void draw(const DrawArgs& args) override
{
drawBackground(args.vg);
drawOutputJacksArea(args.vg, 9);
drawOutputJacksArea(args.vg, 10);
setupTextLines(args.vg);

drawTextLine(args.vg, 0, "V/Oct");
@@ -727,6 +802,7 @@ struct HostMIDIWidget : ModuleWidgetWith9HP {
drawTextLine(args.vg, 6, "Start");
drawTextLine(args.vg, 7, "Stop");
drawTextLine(args.vg, 8, "Cont");
drawTextLine(args.vg, 9, "Retrigger");

ModuleWidgetWith9HP::draw(args);
}
@@ -738,6 +814,16 @@ struct HostMIDIWidget : ModuleWidgetWith9HP {

menu->addChild(createBoolPtrMenuItem("Smooth pitch/mod wheel", "", &module->midiInput.smooth));

static const std::vector<float> pwRanges = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 24, 36, 48};
menu->addChild(createSubmenuItem("Pitch bend range", string::f("%g", module->midiInput.pwRange), [=](Menu* menu) {
for (size_t i = 0; i < pwRanges.size(); i++) {
menu->addChild(createCheckMenuItem(string::f("%g", pwRanges[i]), "",
[=]() {return module->midiInput.pwRange == pwRanges[i];},
[=]() {module->midiInput.pwRange = pwRanges[i];}
));
}
}));

struct InputChannelItem : MenuItem {
HostMIDI* module;
Menu* createChildMenu() override {
@@ -758,24 +844,14 @@ struct HostMIDIWidget : ModuleWidgetWith9HP {
inputChannelItem->module = module;
menu->addChild(inputChannelItem);

struct PolyphonyChannelItem : MenuItem {
HostMIDI* module;
Menu* createChildMenu() override {
Menu* menu = new Menu;
for (int c = 1; c <= 16; c++) {
menu->addChild(createCheckMenuItem((c == 1) ? "Monophonic" : string::f("%d", c), "",
[=]() {return module->midiInput.channels == c;},
[=]() {module->midiInput.setChannels(c);}
));
}
return menu;
menu->addChild(createSubmenuItem("Polyphony channels", string::f("%d", module->midiInput.channels), [=](Menu* menu) {
for (int c = 1; c <= 16; c++) {
menu->addChild(createCheckMenuItem((c == 1) ? "Monophonic" : string::f("%d", c), "",
[=]() {return module->midiInput.channels == c;},
[=]() {module->midiInput.setChannels(c);}
));
}
};
PolyphonyChannelItem* const polyphonyChannelItem = new PolyphonyChannelItem;
polyphonyChannelItem->text = "Polyphony channels";
polyphonyChannelItem->rightText = string::f("%d", module->midiInput.channels) + " " + RIGHT_ARROW;
polyphonyChannelItem->module = module;
menu->addChild(polyphonyChannelItem);
}));

menu->addChild(createIndexPtrSubmenuItem("Polyphony mode", {
"Rotate",
@@ -814,6 +890,17 @@ struct HostMIDIWidget : ModuleWidgetWith9HP {
));
}
};
#else
struct HostMIDIWidget : ModuleWidget {
HostMIDIWidget(HostMIDI* const module) {
setModule(module);
for (uint i=0; i<HostMIDI::NUM_INPUTS; ++i)
addInput(createInput<PJ301MPort>({}, module, i));
for (uint i=0; i<HostMIDI::NUM_OUTPUTS; ++i)
addOutput(createOutput<PJ301MPort>({}, module, i));
}
};
#endif

// --------------------------------------------------------------------------------------------------------------------



+ 7
- 4
plugins/Cardinal/src/HostParameters.cpp View File

@@ -20,8 +20,6 @@

// -----------------------------------------------------------------------------------------------------------

USE_NAMESPACE_DISTRHO;

struct HostParameters : TerminalModule {
enum ParamIds {
NUM_PARAMS
@@ -91,6 +89,8 @@ struct HostParameters : TerminalModule {
}
};

// --------------------------------------------------------------------------------------------------------------------

#ifndef HEADLESS
struct CardinalParameterPJ301MPort : PJ301MPort {
void onDragStart(const DragStartEvent& e) override {
@@ -159,11 +159,14 @@ struct HostParametersWidget : ModuleWidgetWith9HP {
struct HostParametersWidget : ModuleWidget {
HostParametersWidget(HostParameters* const module) {
setModule(module);

for (int i=0; i<24; ++i)
for (uint i=0; i<HostParameters::NUM_OUTPUTS; ++i)
addOutput(createOutput<PJ301MPort>({}, module, i));
}
};
#endif

// --------------------------------------------------------------------------------------------------------------------

Model* modelHostParameters = createModel<HostParameters, HostParametersWidget>("HostParameters");

// --------------------------------------------------------------------------------------------------------------------

+ 29
- 11
plugins/Cardinal/src/HostTime.cpp View File

@@ -17,6 +17,8 @@

#include "plugincontext.hpp"

// --------------------------------------------------------------------------------------------------------------------

struct HostTime : TerminalModule {
enum ParamIds {
NUM_PARAMS
@@ -39,7 +41,7 @@ struct HostTime : TerminalModule {

rack::dsp::PulseGenerator pulseReset, pulseBar, pulseBeat, pulseClock;
float sampleTime = 0.0f;
int64_t lastBlockFrame = -1;
uint32_t lastProcessCounter = 0;
// cached time values
struct {
bool reset = true;
@@ -61,15 +63,15 @@ struct HostTime : TerminalModule {

void processTerminalInput(const ProcessArgs& args) override
{
const int64_t blockFrame = pcontext->engine->getBlockFrame();
const uint32_t processCounter = pcontext->processCounter;

// local variables for faster access
double tick, tickClock;

// Update time position if running a new audio block
if (lastBlockFrame != blockFrame)
if (lastProcessCounter != processCounter)
{
lastBlockFrame = blockFrame;
lastProcessCounter = processCounter;
timeInfo.reset = pcontext->reset;
timeInfo.bar = pcontext->bar;
timeInfo.beat = pcontext->beat;
@@ -127,6 +129,13 @@ struct HostTime : TerminalModule {
}
}

// store back the local values
timeInfo.tick = tick;
timeInfo.tickClock = tickClock;

if (isBypassed())
return;

const bool hasReset = pulseReset.process(args.sampleTime);
const bool hasBar = pulseBar.process(args.sampleTime);
const bool hasBeat = pulseBeat.process(args.sampleTime);
@@ -138,13 +147,6 @@ struct HostTime : TerminalModule {
? ((float) (timeInfo.beat - 1) + beatPhase) / pcontext->beatsPerBar
: 0.0f;

// store back the local values
timeInfo.tick = tick;
timeInfo.tickClock = tickClock;

if (isBypassed())
return;

lights[kHostTimeRolling].setBrightness(playing ? 1.0f : 0.0f);
lights[kHostTimeReset].setBrightnessSmooth(hasReset ? 1.0f : 0.0f, args.sampleTime * 0.5f);
lights[kHostTimeBar].setBrightnessSmooth(hasBar ? 1.0f : 0.0f, args.sampleTime * 0.5f);
@@ -166,6 +168,9 @@ struct HostTime : TerminalModule {
{}
};

// --------------------------------------------------------------------------------------------------------------------

#ifndef HEADLESS
struct HostTimeWidget : ModuleWidget {
static constexpr const float startX = 10.0f;
static constexpr const float startY_top = 71.0f;
@@ -285,5 +290,18 @@ struct HostTimeWidget : ModuleWidget {
ModuleWidget::drawLayer(args, layer);
}
};
#else
struct HostTimeWidget : ModuleWidget {
HostTimeWidget(HostTime* const module) {
setModule(module);
for (uint i=0; i<HostTime::kHostTimeCount; ++i)
addOutput(createOutput<PJ301MPort>({}, module, i));
}
};
#endif

// --------------------------------------------------------------------------------------------------------------------

Model* modelHostTime = createModel<HostTime, HostTimeWidget>("HostTime");

// --------------------------------------------------------------------------------------------------------------------

+ 17
- 7
plugins/Cardinal/src/Ildaeil.cpp View File

@@ -97,12 +97,14 @@ static void projectLoadedFromDSP(void* ui);

static Mutex sPluginInfoLoadMutex;

/*
#ifndef HEADLESS
struct JuceInitializer {
JuceInitializer() { carla_juce_init(); }
~JuceInitializer() { carla_juce_cleanup(); }
};
#endif
*/

struct IldaeilModule : Module {
enum ParamIds {
@@ -122,9 +124,11 @@ struct IldaeilModule : Module {
NUM_LIGHTS
};

/*
#ifndef HEADLESS
SharedResourcePointer<JuceInitializer> juceInitializer;
#endif
*/

const CardinalPluginContext* const pcontext;

@@ -144,7 +148,7 @@ struct IldaeilModule : Module {
float audioDataOut1[BUFFER_SIZE];
float audioDataOut2[BUFFER_SIZE];
unsigned audioDataFill = 0;
int64_t lastBlockFrame = -1;
uint32_t lastProcessCounter = 0;
CardinalExpanderFromCarlaMIDIToCV* midiOutExpander = nullptr;

volatile bool resetMeterIn = true;
@@ -209,10 +213,14 @@ struct IldaeilModule : Module {
carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_BINARIES, 0, "/Applications/Carla.app/Contents/MacOS");
carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_RESOURCES, 0, "/Applications/Carla.app/Contents/MacOS/resources");
}
#elif defined(CARLA_OS_WINDOWS)
// Carla does not support system-wide install on Windows right now
if (false)
#elif defined(CARLA_OS_WIN)
const std::string winBinaryDir = system::join(asset::systemDir, "Carla");

if (system::exists(winBinaryDir))
{
const std::string winResourceDir = system::join(winBinaryDir, "resources");
carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_BINARIES, 0, winBinaryDir.c_str());
carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_RESOURCES, 0, winResourceDir.c_str());
}
#else
if (system::exists("/usr/local/lib/carla"))
@@ -351,12 +359,12 @@ struct IldaeilModule : Module {

if (audioDataFill == BUFFER_SIZE)
{
const int64_t blockFrame = pcontext->engine->getBlockFrame();
const uint32_t processCounter = pcontext->processCounter;

// Update time position if running a new audio block
if (lastBlockFrame != blockFrame)
if (lastProcessCounter != processCounter)
{
lastBlockFrame = blockFrame;
lastProcessCounter = processCounter;
fCarlaTimeInfo.playing = pcontext->playing;
fCarlaTimeInfo.frame = pcontext->frame;
fCarlaTimeInfo.bbt.valid = pcontext->bbtValid;
@@ -973,7 +981,9 @@ struct IldaeilWidget : ImGuiWidget, IdleCallback, Thread {
const CarlaHostHandle handle = module->fCarlaHostHandle;
DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr,);

/*
carla_juce_idle();
*/

if (fileBrowserHandle != nullptr && fileBrowserIdle(fileBrowserHandle))
{


+ 2
- 0
plugins/Cardinal/src/Widgets.hpp View File

@@ -25,6 +25,7 @@

using namespace rack;

#ifndef HEADLESS
struct CardinalLedDisplayChoice : LedDisplayChoice {
bool alignTextCenter = true;

@@ -403,3 +404,4 @@ struct OpenGlWidgetWithBrowserPreview : OpenGlWidget {

virtual void drawFramebufferForBrowserPreview() = 0;
};
#endif

+ 2
- 2
plugins/Cardinal/src/plugincontext.hpp View File

@@ -51,11 +51,11 @@ struct MidiEvent {
};

struct CardinalPluginContext : rack::Context {
uint32_t bufferSize;
uint32_t bufferSize, processCounter;
double sampleRate;
float parameters[kModuleParameters];
CardinalVariant variant;
bool playing, reset, bbtValid;
bool bypassed, playing, reset, bbtValid;
int32_t bar, beat, beatsPerBar, beatType;
uint64_t frame;
double barStartTick, beatsPerMinute;


+ 1
- 0
plugins/CatroModulo

@@ -0,0 +1 @@
Subproject commit bf6f969c5f7fff6a419a54197fb4318671281ad5

+ 1
- 1
plugins/ChowDSP

@@ -1 +1 @@
Subproject commit 80f61cd0171bb7d988c9ec3a144e0566d62c767c
Subproject commit 52f89b94a25828f9debf8bca4d58854fb1e70228

+ 1
- 1
plugins/ExpertSleepers-Encoders

@@ -1 +1 @@
Subproject commit df096a635fe8d3ea86a1e6451a55e45ccee5b83d
Subproject commit 95496e8a955407889bbab94cf404cf356802bb76

+ 1
- 0
plugins/Fundamental

@@ -0,0 +1 @@
Subproject commit 48bf3c84ebafc9effe7e565d8cdbf8a46b9d503c

+ 1
- 1
plugins/GlueTheGiant

@@ -1 +1 @@
Subproject commit 54fed7f78bbaac1f1d6275aa737acc39aebc6e72
Subproject commit 2c535bc38d61fd4d776aad7307c1dfbbed062b66

+ 1
- 0
plugins/GoodSheperd

@@ -0,0 +1 @@
Subproject commit 636351059f2eec629f3b8a537451dd3d0eb01c30

+ 1
- 1
plugins/GrandeModular

@@ -1 +1 @@
Subproject commit 40a197698496030a30ead69658b1484eca2ae299
Subproject commit b0d8a6fcdb28d2e56d6b024326a7378a2c8ee45d

+ 1
- 0
plugins/HamptonHarmonics

@@ -0,0 +1 @@
Subproject commit e5cf81f1c356fdc98fd08584146cda8af7e16b1f

+ 1
- 1
plugins/ImpromptuModular

@@ -1 +1 @@
Subproject commit 368cbd6ee17398c7329b263dde0409bf7a57ce3b
Subproject commit 2bd5691c8f12e21e1e6db33e42fe2fe3db7726a3

+ 1
- 1
plugins/JW-Modules

@@ -1 +1 @@
Subproject commit 68f04a5a8a1a656e7a3aa7e217524f0feb91167e
Subproject commit f7399c473735c0a5bec95bb40953e781f9a47ca4

+ 1
- 0
plugins/LilacLoop

@@ -0,0 +1 @@
Subproject commit 69d8d66521175cd561d53e65728d460b398950c9

+ 1
- 1
plugins/LyraeModules

@@ -1 +1 @@
Subproject commit 1c32b02bd11a549d28da0620719541ac6f966652
Subproject commit b21cbe8ee25ddf2a927e0b4ec9f2c97c115857af

+ 1
- 0
plugins/ML_modules

@@ -0,0 +1 @@
Subproject commit 788ceb73cfabddaff7245b0df072b0e22a19b360

+ 1
- 0
plugins/MSM

@@ -0,0 +1 @@
Subproject commit abe3c24d40b11d31f9f38b2125eff9280c77ad1b

+ 323
- 49
plugins/Makefile View File

@@ -222,6 +222,15 @@ PLUGIN_FILES += $(wildcard Cardinal/src/DearImGui/*.cpp)
PLUGIN_FILES += $(wildcard Cardinal/src/DearImGuiColorTextEditor/*.cpp)
endif

# --------------------------------------------------------------
# Fundamental (always enabled)

PLUGIN_FILES += $(filter-out Fundamental/src/plugin.cpp,$(wildcard Fundamental/src/*.cpp))
PLUGIN_FILES += Fundamental/src/dr_wav.c

# modules/types which are present in other plugins
FUNDAMENTAL_CUSTOM = $(DRWAV)

ifneq ($(NOPLUGINS),true)
# --------------------------------------------------------------
# 21kHz
@@ -231,7 +240,20 @@ PLUGIN_FILES += $(filter-out 21kHz/src/21kHz.cpp,$(wildcard 21kHz/src/*.cpp))
# --------------------------------------------------------------
# 8Mode

PLUGIN_FILES += $(wildcard 8Mode/src/*.cpp)
PLUGIN_FILES += $(filter-out 8Mode/src/8mode.cpp,$(wildcard 8Mode/src/*.cpp))

# --------------------------------------------------------------
# AlgoritmArte

PLUGIN_FILES += $(filter-out Algoritmarte/src/plugin.cpp,$(wildcard Algoritmarte/src/*.cpp))

# --------------------------------------------------------------
# Aaron Static

PLUGIN_FILES += $(wildcard AaronStatic/src/*.cpp)

# modules/types which are present in other plugins
AARONSTATIC_CUSTOM = RefreshCounter

# --------------------------------------------------------------
# AmalgamatedHarmonics
@@ -248,6 +270,24 @@ AMALGAMATEDHARMONICS_CUSTOM += bogaudio
PLUGIN_FILES += $(wildcard AnimatedCircuits/src/Folding/*.cpp)
PLUGIN_FILES += $(wildcard AnimatedCircuits/src/LFold/*.cpp)

# --------------------------------------------------------------
# ArableInstruments

PLUGIN_FILES += ArableInstruments/src/Clouds.cpp
PLUGIN_FILES += ArableInstruments/eurorack/clouds/dsp/correlator.cc
PLUGIN_FILES += ArableInstruments/eurorack/clouds/dsp/granular_processor.cc
PLUGIN_FILES += ArableInstruments/eurorack/clouds/dsp/mu_law.cc
PLUGIN_FILES += ArableInstruments/eurorack/clouds/dsp/pvoc/frame_transformation.cc
PLUGIN_FILES += ArableInstruments/eurorack/clouds/dsp/pvoc/phase_vocoder.cc
PLUGIN_FILES += ArableInstruments/eurorack/clouds/dsp/pvoc/stft.cc
PLUGIN_FILES += ArableInstruments/eurorack/clouds/resources.cc
PLUGIN_FILES += ArableInstruments/eurorack/stmlib/utils/random.cc
PLUGIN_FILES += ArableInstruments/eurorack/stmlib/dsp/atan.cc
PLUGIN_FILES += ArableInstruments/eurorack/stmlib/dsp/units.cc

# modules/types which are present in other plugins
ARABLE_CUSTOM = Clouds FreezeLight clouds stmlib

# --------------------------------------------------------------
# Aria

@@ -378,7 +418,7 @@ AUTINN_CUSTOM = Chord Vibrato
# --------------------------------------------------------------
# Axioma

PLUGIN_FILES += $(wildcard Axioma/src/*.cpp)
PLUGIN_FILES += $(filter-out Axioma/src/plugin.cpp,$(wildcard Axioma/src/*.cpp))

# --------------------------------------------------------------
# BaconPlugs
@@ -391,6 +431,7 @@ PLUGIN_FILES += $(wildcard BaconPlugs/libs/open303-code/Source/DSPCode/*.cpp)
# Befaco

PLUGIN_FILES += $(filter-out Befaco/src/plugin.cpp,$(wildcard Befaco/src/*.cpp))
PLUGIN_FILES += $(wildcard Befaco/src/noise-plethora/*/*.cpp)
PLUGIN_BINARIES += Befaco/src/SpringReverbIR.pcm

# modules/types which are present in other plugins
@@ -405,9 +446,10 @@ PLUGIN_FILES += $(wildcard Bidoo/src/dep/filters/*.cpp)
PLUGIN_FILES += $(wildcard Bidoo/src/dep/freeverb/*.cpp)
PLUGIN_FILES += $(wildcard Bidoo/src/dep/lodepng/*.cpp)
PLUGIN_FILES += $(filter-out Bidoo/src/dep/resampler/main.cpp,$(wildcard Bidoo/src/dep/resampler/*.cpp))
PLUGIN_FILES += BidooDark/plugin.cpp

# modules/types which are present in other plugins
BIDOO_CUSTOM = ChannelDisplay LadderFilter $(DRWAV)
BIDOO_CUSTOM = ChannelDisplay InstantiateExpanderItem LadderFilter $(DRWAV)
BIDOO_CUSTOM_PER_FILE = channel channel filterType

# --------------------------------------------------------------
@@ -424,11 +466,18 @@ BOGAUDIO_CUSTOM_PER_FILE = ARQuantity AttackMenuItem ReleaseMenuItem
# --------------------------------------------------------------
# ChowDSP

# Credit module crashes on save, see https://github.com/DISTRHO/Cardinal/issues/98
PLUGIN_FILES += $(filter-out ChowDSP/src/Credit.cpp,$(wildcard ChowDSP/src/*/*.cpp))
PLUGIN_FILES += $(wildcard ChowDSP/src/*/*.cpp)
PLUGIN_FILES += $(wildcard ChowDSP/src/*/*/*.cpp)
PLUGIN_FILES += $(wildcard ChowDSP/lib/r8lib/*.cpp)

# --------------------------------------------------------------
# CatroModulo

PLUGIN_FILES += $(filter-out CatroModulo/src/CatroModulo.cpp,$(wildcard CatroModulo/src/*.cpp))

# modules/types which are present in other plugins
CATROMODULO_CUSTOM = LowFrequencyOscillator NumDisplayWidget

# --------------------------------------------------------------
# cf

@@ -449,6 +498,7 @@ PLUGIN_FILES += $(wildcard DrumKit/src/model/*.cpp)
PLUGIN_FILES += $(wildcard DrumKit/deps/*.cpp)
PLUGIN_FILES += $(wildcard DrumKit/deps/SynthDevKit/src/*.cpp)

# modules/types which are present in other plugins
DRUMKIT_CUSTOM = ADSR Envelope LowFrequencyOscillator

# --------------------------------------------------------------
@@ -471,35 +521,36 @@ PLUGIN_FILES += $(filter-out Extratone/src/plugin.cpp,$(wildcard Extratone/src/*

PLUGIN_FILES += $(filter-out FehlerFabrik/src/plugin.cpp,$(wildcard FehlerFabrik/src/*.cpp))

# modules/types which are present in other plugins
FEHLERFABRIK_CUSTOM = Operator Sequencer SlewLimiter

# --------------------------------------------------------------
# Fundamental

ifeq ($(WITH_FUNDAMENTAL),true)
BASE_FLAGS += -DWITH_FUNDAMENTAL

PLUGIN_FILES += $(filter-out Fundamental/src/plugin.cpp,$(wildcard Fundamental/src/*.cpp))
PLUGIN_FILES += Fundamental/src/dr_wav.c
# GlueTheGiant

# modules/types which are present in other plugins
FUNDAMENTAL_CUSTOM = $(DRWAV)
endif
PLUGIN_FILES += $(filter-out GlueTheGiant/src/plugin.cpp,$(wildcard GlueTheGiant/src/*.cpp))

# --------------------------------------------------------------
# GlueTheGiant
# GoodSheperd

PLUGIN_FILES += $(filter-out GlueTheGiant/src/plugin.cpp,$(wildcard GlueTheGiant/src/*.cpp))
PLUGIN_FILES += $(filter-out GoodSheperd/src/plugin.cpp,$(wildcard GoodSheperd/src/*.cpp))

# --------------------------------------------------------------
# GrandeModular

PLUGIN_FILES += $(filter-out GrandeModular/src/plugin.cpp,$(wildcard GrandeModular/src/*.cpp))

# --------------------------------------------------------------
# Hampton Harmonics

PLUGIN_FILES += $(filter-out HamptonHarmonics/src/plugin.cpp,$(wildcard HamptonHarmonics/src/*.cpp))

# modules/types which are present in other plugins
HAMPTONHARMONICS_CUSTOM = Arp Progress

# --------------------------------------------------------------
# HetrickCV

PLUGIN_FILES += $(wildcard HetrickCV/src/*.cpp)
PLUGIN_FILES += $(filter-out HetrickCV/src/HetrickCV.cpp,$(wildcard HetrickCV/src/*.cpp))
PLUGIN_FILES += $(wildcard HetrickCV/src/DSP/*.cpp)
PLUGIN_FILES += $(wildcard HetrickCV/Gamma/src/arr.cpp)
PLUGIN_FILES += $(wildcard HetrickCV/Gamma/src/Domain.cpp)
@@ -551,6 +602,14 @@ endif
# modules/types which are present in other plugins
JW_CUSTOM = PlayHead Quantizer

# --------------------------------------------------------------
# kocmoc

PLUGIN_FILES += $(filter-out kocmoc/src/plugin.cpp,$(wildcard kocmoc/src/*.cpp))

# modules/types which are present in other plugins
KOCMOC_CUSTOM = Phasor __ct_base __ct_comp

# --------------------------------------------------------------
# LifeFormModular

@@ -559,6 +618,14 @@ PLUGIN_FILES += $(filter-out LifeFormModular/src/plugin.cpp,$(wildcard LifeFormM
# modules/types which are present in other plugins
LIFEFORMMODULAR_CUSTOM = IO MS __ct_base __ct_comp

# --------------------------------------------------------------
# Lilac Loop

PLUGIN_FILES += $(filter-out LilacLoop/src/plugin.cpp,$(wildcard LilacLoop/src/*.cpp))

# modules/types which are present in other plugins
LILACLOOP_CUSTOM = AudioFile Mode

# --------------------------------------------------------------
# LittleUtils

@@ -596,6 +663,15 @@ PLUGIN_FILES += $(wildcard MindMeldModular/src/Utilities/*.cpp)
# modules/types which are present in other plugins
MINDMELD_CUSTOM = printNote

# --------------------------------------------------------------
# ML_modules

PLUGIN_FILES += $(filter-out ML_modules/src/ML_modules.cpp,$(wildcard ML_modules/src/*.cpp))
PLUGIN_FILES += ML_modules/freeverb/revmodel.cpp

# modules/types which are present in other plugins
ML_CUSTOM = Mode Quant Quantizer QuantizerWidget SH8 allpass comb revmodel

# --------------------------------------------------------------
# MockbaModular

@@ -618,6 +694,53 @@ PLUGIN_FILES += $(wildcard mscHack/src/*.cpp)
# modules/types which are present in other plugins
MSCHACK_CUSTOM_PER_FILE = MAIN_SYNC_CLOCK FILTER_STRUCT FILTER_PARAM_STRUCT OSC_PARAM_STRUCT PHRASE_CHANGE_STRUCT

# --------------------------------------------------------------
# MSM

PLUGIN_FILES += $(filter-out MSM/src/MSM.cpp,$(wildcard MSM/src/*.cpp))

# modules/types which are present in other plugins
MSM_CUSTOM = ADSR BlankPanel Delay LFO LowFrequencyOscillator Mult Noise OP VCA VCO sawTable triTable

# --------------------------------------------------------------
# Nonlinear Circuits

PLUGIN_FILES += $(filter-out nonlinearcircuits/src/NLC.cpp,$(wildcard nonlinearcircuits/src/*.cpp))

# --------------------------------------------------------------
# Orbits

PLUGIN_FILES += $(filter-out Orbits/src/plugin.cpp,$(wildcard Orbits/src/*.cpp))

# --------------------------------------------------------------
# ParableInstruments

PLUGIN_FILES += ParableInstruments/src/Clouds.cpp
PLUGIN_FILES += ParableInstruments/parasites/clouds/dsp/correlator.cc
PLUGIN_FILES += ParableInstruments/parasites/clouds/dsp/granular_processor.cc
PLUGIN_FILES += ParableInstruments/parasites/clouds/dsp/mu_law.cc
PLUGIN_FILES += ParableInstruments/parasites/clouds/dsp/pvoc/frame_transformation.cc
PLUGIN_FILES += ParableInstruments/parasites/clouds/dsp/pvoc/phase_vocoder.cc
PLUGIN_FILES += ParableInstruments/parasites/clouds/dsp/pvoc/stft.cc
PLUGIN_FILES += ParableInstruments/parasites/clouds/resources.cc
PLUGIN_FILES += ParableInstruments/parasites/stmlib/utils/random.cc
PLUGIN_FILES += ParableInstruments/parasites/stmlib/dsp/atan.cc
PLUGIN_FILES += ParableInstruments/parasites/stmlib/dsp/units.cc

# modules/types which are present in other plugins
PARABLE_CUSTOM = Clouds CustomPanel CloudsWidget FreezeLight clouds stmlib

# --------------------------------------------------------------
# Path Set

PLUGIN_FILES += $(filter-out PathSet/src/plugin.cpp,$(wildcard PathSet/src/*.cpp))

# --------------------------------------------------------------
# PinkTrombone

PLUGIN_FILES += $(filter-out PinkTrombone/src/plugin.cpp,$(wildcard PinkTrombone/src/*.cpp))
PLUGIN_FILES += $(wildcard PinkTrombone/src/PinkTrombone/*.cpp)

# --------------------------------------------------------------
# Prism

@@ -643,15 +766,21 @@ REPELZEN_CUSTOM = Blank Mixer Werner tanh_pade
# --------------------------------------------------------------
# sonusmodular

PLUGIN_FILES += $(filter-out sonusmodular/src/sonusmodular,$(wildcard sonusmodular/src/*.cpp))
PLUGIN_FILES += $(filter-out sonusmodular/src/sonusmodular.cpp,$(wildcard sonusmodular/src/*.cpp))

# --------------------------------------------------------------
# stocaudio

PLUGIN_FILES += $(filter-out stocaudio/src/plugin.cpp,$(wildcard stocaudio/src/*.cpp))

# --------------------------------------------------------------
# Substation (Open source release)

PLUGIN_FILES += $(wildcard substation-opensource/dep/slime4rack/src/slime/*.cpp)
PLUGIN_FILES += $(wildcard substation-opensource/dep/slime4rack/src/slime/**/*.cpp)
PLUGIN_FILES += $(filter-out substation-opensource/src/_plugin.cpp substation-opensource/src/Settings.cpp,$(wildcard substation-opensource/src/*.cpp))
PLUGIN_FILES += substation-settings/Settings.cpp
# unless_modules

PLUGIN_FILES += $(filter-out unless_modules/src/unless.cpp,$(wildcard unless_modules/src/*.cpp))

# modules/types which are present in other plugins
UNLESS_MODULES_CUSTOM = Selection

# --------------------------------------------------------------
# ValleyAudio
@@ -729,6 +858,15 @@ PLUGIN_BINARIES += ValleyAudio/src/XFADE.bin
VALLEYAUDIO_CUSTOM = $(DRWAV) DigitalDisplay
VALLEYAUDIO_CUSTOM_PER_FILE = TempoKnob

# --------------------------------------------------------------
# Voxglitch

PLUGIN_FILES += $(filter-out voxglitch/src/plugin.cpp,$(wildcard voxglitch/src/*.cpp))

# modules/types which are present in other plugins
VOXGLITCH_CUSTOM = $(DRWAV) AudioFile Looper Readout
VOXGLITCH_CUSTOM_PER_FILE = AudioBuffer GateSequencer Grain Sequencer SequencerDisplay VoltageSequencer

# --------------------------------------------------------------
# ZetaCarinaeModules

@@ -762,7 +900,6 @@ BASE_FLAGS += -DARCH_LIN
endif

BASE_FLAGS += -DBUILDING_PLUGIN_MODULES
BASE_FLAGS += -fno-strict-aliasing
BASE_FLAGS += -I../dpf/dgl/src/nanovg
BASE_FLAGS += -I../dpf/distrho

@@ -832,8 +969,8 @@ BASE_FLAGS += -DHAVE_SNDFILE
endif

BUILD_C_FLAGS += -std=gnu11
BUILD_C_FLAGS += -fno-finite-math-only
BUILD_CXX_FLAGS += -fno-finite-math-only
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing -faligned-new

# Rack code is not tested for this flag, unset it
BUILD_CXX_FLAGS += -U_GLIBCXX_ASSERTIONS -Wp,-U_GLIBCXX_ASSERTIONS
@@ -872,18 +1009,26 @@ else
PLUGIN_LIST = $(subst /plugin.json,,$(wildcard */plugin.json))
endif

UNWANTED_FILES = HetrickCV/res/illustrator - deprecated/MyModule.svg
UNWANTED_FILES = HetrickCV/res/illustrator - deprecated/MyModule.svg
UNWANTED_FILES += $(wildcard Mog/res/*)
UNWANTED_FILES += $(wildcard Mog/res/*/*)
UNWANTED_FILES += $(wildcard nonlinearcircuits/res/*)

RESOURCE_FILES = \
$(filter-out $(UNWANTED_FILES), \
$(wildcard */res/*.svg) \
$(wildcard */res/*/*.svg) \
$(wildcard */res/*/*/*.svg) \
$(wildcard */res/*.otf) \
$(wildcard */res/*/*.otf) \
$(wildcard */res/*/*/*.otf) \
$(wildcard */res/*.ttf) \
$(wildcard */res/*/*.ttf) \
$(wildcard */res/*/*/*.ttf))

RESOURCE_FILES += $(wildcard */presets)
RESOURCE_FILES += $(wildcard Orbits/res/*.json)
RESOURCE_FILES += ArableInstruments/res/Joni.png
RESOURCE_FILES += BaconPlugs/res/Keypunch029.json
RESOURCE_FILES += BaconPlugs/res/midi/chopin
RESOURCE_FILES += BaconPlugs/res/midi/debussy
@@ -895,6 +1040,13 @@ RESOURCE_FILES += MindMeldModular/res/ShapeMaster/CommunityPresets
RESOURCE_FILES += MindMeldModular/res/ShapeMaster/CommunityShapes
RESOURCE_FILES += MindMeldModular/res/ShapeMaster/MindMeldPresets
RESOURCE_FILES += MindMeldModular/res/ShapeMaster/MindMeldShapes
RESOURCE_FILES += Mog/res
RESOURCE_FILES += nonlinearcircuits/res
RESOURCE_FILES += ParableInstruments/res/Neil.png
RESOURCE_FILES += $(wildcard unless_modules/art/*.art)
RESOURCE_FILES += $(wildcard unless_modules/art/svg/*/*.svg)
RESOURCE_FILES += $(wildcard unless_modules/font/*.ttf)
# RESOURCE_FILES += $(wildcard unless_modules/manual/*)

# MOD builds only have LV2 FX variant for now
ifeq ($(MOD_BUILD),true)
@@ -914,10 +1066,8 @@ VST2_RESOURCES += $(PLUGIN_LIST:%=../bin/CardinalSynth.vst/Contents/Resources/Pl
VST2_RESOURCES += $(RESOURCE_FILES:%=../bin/CardinalFX.vst/Contents/Resources/%)
VST2_RESOURCES += $(RESOURCE_FILES:%=../bin/CardinalSynth.vst/Contents/Resources/%)
else
VST2_RESOURCES = $(PLUGIN_LIST:%=../bin/CardinalFX.vst/resources/PluginManifests/%.json)
VST2_RESOURCES += $(PLUGIN_LIST:%=../bin/CardinalSynth.vst/resources/PluginManifests/%.json)
VST2_RESOURCES += $(RESOURCE_FILES:%=../bin/CardinalFX.vst/resources/%)
VST2_RESOURCES += $(RESOURCE_FILES:%=../bin/CardinalSynth.vst/resources/%)
VST2_RESOURCES = $(PLUGIN_LIST:%=../bin/Cardinal.vst/resources/PluginManifests/%.json)
VST2_RESOURCES += $(RESOURCE_FILES:%=../bin/Cardinal.vst/resources/%)
endif

VST3_RESOURCES = $(PLUGIN_LIST:%=../bin/Cardinal.vst3/Contents/Resources/PluginManifests/%.json)
@@ -1001,19 +1151,11 @@ ifeq ($(MACOS),true)
-@mkdir -p "$(shell dirname $@)"
$(SILENT)ln -sf $(abspath $<) $@
else
../bin/CardinalFX.vst/resources/%: %
-@mkdir -p "$(shell dirname $@)"
$(SILENT)ln -sf $(abspath $<) $@

../bin/CardinalSynth.vst/resources/%: %
-@mkdir -p "$(shell dirname $@)"
$(SILENT)ln -sf $(abspath $<) $@

../bin/CardinalFX.vst/resources/PluginManifests/%.json: %/plugin.json
../bin/Cardinal.vst/resources/%: %
-@mkdir -p "$(shell dirname $@)"
$(SILENT)ln -sf $(abspath $<) $@

../bin/CardinalSynth.vst/resources/PluginManifests/%.json: %/plugin.json
../bin/Cardinal.vst/resources/PluginManifests/%.json: %/plugin.json
-@mkdir -p "$(shell dirname $@)"
$(SILENT)ln -sf $(abspath $<) $@
endif
@@ -1082,6 +1224,21 @@ $(BUILD_DIR)/8Mode/%.cpp.o: 8Mode/%.cpp
$(foreach m,$(8MODE_CUSTOM),$(call custom_module_names,$(m),8Mode)) \
-DpluginInstance=pluginInstance__8Mode

$(BUILD_DIR)/AaronStatic/%.cpp.o: AaronStatic/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(AARONSTATIC_CUSTOM),$(call custom_module_names,$(m),AaronStatic)) \
-DpluginInstance=pluginInstance__AaronStatic \
-Dinit=init__AaronStatic

$(BUILD_DIR)/Algoritmarte/%.cpp.o: Algoritmarte/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(ALGORITMARTE_CUSTOM),$(call custom_module_names,$(m),Algoritmarte)) \
-DpluginInstance=pluginInstance__Algoritmarte

$(BUILD_DIR)/AmalgamatedHarmonics/%.cpp.o: AmalgamatedHarmonics/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -1096,6 +1253,17 @@ $(BUILD_DIR)/AnimatedCircuits/%.cpp.o: AnimatedCircuits/%.cpp
$(foreach m,$(ANIMATEDCIRCUITS_CUSTOM),$(call custom_module_names,$(m),AnimatedCircuits)) \
-DpluginInstance=pluginInstance__AnimatedCircuits

$(BUILD_DIR)/ArableInstruments/%.o: ArableInstruments/%
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(ARABLE_CUSTOM),$(call custom_module_names,$(m),Arable)) \
-DpluginInstance=pluginInstance__ArableInstruments \
-DTEST \
-IArableInstruments/eurorack \
-Wno-class-memaccess \
-Wno-unused-local-typedefs

$(BUILD_DIR)/AriaModules/%.cpp.o: AriaModules/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -1150,7 +1318,7 @@ $(BUILD_DIR)/Befaco/%.cpp.o: Befaco/%.cpp
$(foreach m,$(BEFACO_CUSTOM),$(call custom_module_names,$(m),Befaco)) \
-DpluginInstance=pluginInstance__Befaco

$(BUILD_DIR)/Bidoo/%.cpp.o: Bidoo/%.cpp
$(BUILD_DIR)/Bidoo%.cpp.o: Bidoo%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
@@ -1187,6 +1355,13 @@ $(BUILD_DIR)/BogaudioModules/%.cpp.o: BogaudioModules/%.cpp
-IBogaudioModules/lib \
-IBogaudioModules/src/dsp

$(BUILD_DIR)/CatroModulo/src/%.cpp.o: CatroModulo/src/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(CATROMODULO_CUSTOM),$(call custom_module_names,$(m),CatroModulo)) \
-DpluginInstance=pluginInstance__CatroModulo

$(BUILD_DIR)/cf/src/%.cpp.o: cf/src/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -1282,6 +1457,13 @@ $(BUILD_DIR)/GlueTheGiant/src/gtgComponents.cpp.o: GlueTheGiant/src/gtgComponent
-DloadGtgPluginDefault=ignoredGlueTheGiant1 \
-DsaveGtgPluginDefault=ignoredGlueTheGiant2

$(BUILD_DIR)/GoodSheperd/%.cpp.o: GoodSheperd/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(GOODSHEPERD_CUSTOM),$(call custom_module_names,$(m),GoodSheperd)) \
-DpluginInstance=pluginInstance__GoodSheperd

$(BUILD_DIR)/GrandeModular/%.cpp.o: GrandeModular/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -1289,8 +1471,16 @@ $(BUILD_DIR)/GrandeModular/%.cpp.o: GrandeModular/%.cpp
$(foreach m,$(GRANDEMODULAR_CUSTOM),$(call custom_module_names,$(m),GrandeModular)) \
-DpluginInstance=pluginInstance__GrandeModular \
-Wno-missing-braces \
-Wno-narrowing \
-Wno-self-assign

$(BUILD_DIR)/HamptonHarmonics/%.cpp.o: HamptonHarmonics/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(HAMPTONHARMONICS_CUSTOM),$(call custom_module_names,$(m),HamptonHarmonics)) \
-DpluginInstance=pluginInstance__HamptonHarmonics

$(BUILD_DIR)/HetrickCV/%.cpp.o: HetrickCV/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -1360,6 +1550,13 @@ $(BUILD_DIR)/JW-Modules/%.cpp.o: JW-Modules/%.cpp
-Wno-unused-but-set-variable \
-Wno-unused-result

$(BUILD_DIR)/kocmoc/%.cpp.o: kocmoc/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(KOCMOC_CUSTOM),$(call custom_module_names,$(m),kocmoc)) \
-DpluginInstance=pluginInstance__kocmoc

$(BUILD_DIR)/LifeFormModular/%.cpp.o: LifeFormModular/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -1367,6 +1564,14 @@ $(BUILD_DIR)/LifeFormModular/%.cpp.o: LifeFormModular/%.cpp
$(foreach m,$(LIFEFORMMODULAR_CUSTOM),$(call custom_module_names,$(m),LifeFormModular)) \
-DpluginInstance=pluginInstance__LifeFormModular

$(BUILD_DIR)/LilacLoop/%.cpp.o: LilacLoop/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(LILACLOOP_CUSTOM),$(call custom_module_names,$(m),LilacLoop)) \
-DpluginInstance=pluginInstance__LilacLoop \
-DSKIP_MINGW_FORMAT

$(BUILD_DIR)/LittleUtils/%.cpp.o: LittleUtils/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -1388,7 +1593,7 @@ $(BUILD_DIR)/LyraeModules/%.cpp.o: LyraeModules/%.cpp
$(foreach m,$(LYRAE_CUSTOM),$(call custom_module_names,$(m),Lyrae)) \
-DpluginInstance=pluginInstance__Lyrae

$(BUILD_DIR)/MindMeldModular/MindMeldModular.cpp.o: MindMeldModular/src/MindMeldModular.cpp
$(BUILD_DIR)/MindMeldModular/src/MindMeldModular.cpp.o: MindMeldModular/src/MindMeldModular.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
@@ -1403,6 +1608,13 @@ $(BUILD_DIR)/MindMeldModular/%.cpp.o: MindMeldModular/%.cpp
$(foreach m,$(MINDMELD_CUSTOM),$(call custom_module_names,$(m),MindMeld)) \
-DpluginInstance=pluginInstance__MindMeld

$(BUILD_DIR)/ML_modules/%.cpp.o: ML_modules/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(ML_CUSTOM),$(call custom_module_names,$(m),ML)) \
-DpluginInstance=pluginInstance__ML

$(BUILD_DIR)/MockbaModular/%.cpp.o: MockbaModular/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -1430,6 +1642,54 @@ $(BUILD_DIR)/mscHack/%.cpp.o: mscHack/%.cpp
-Wno-non-c-typedef-for-linkage \
-Wno-unused-but-set-variable

$(BUILD_DIR)/MSM/%.cpp.o: MSM/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(MSM_CUSTOM),$(call custom_module_names,$(m),MSM)) \
-DpluginInstance=pluginInstance__MSM \
-DDARKTHEME

$(BUILD_DIR)/nonlinearcircuits/%.cpp.o: nonlinearcircuits/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(NONLINEARCIRCUITS_CUSTOM),$(call custom_module_names,$(m),nonlinearcircuits)) \
-DpluginInstance=pluginInstance__nonlinearcircuits

$(BUILD_DIR)/Orbits/%.cpp.o: Orbits/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(ORBITS_CUSTOM),$(call custom_module_names,$(m),Orbits)) \
-DpluginInstance=pluginInstance__Orbits

$(BUILD_DIR)/ParableInstruments/%.o: ParableInstruments/%
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(PARABLE_CUSTOM),$(call custom_module_names,$(m),Parable)) \
-DpluginInstance=pluginInstance__ParableInstruments \
-DPARASITES \
-DTEST \
-IArableInstruments/parasites \
-Wno-class-memaccess \
-Wno-unused-local-typedefs

$(BUILD_DIR)/PathSet/%.cpp.o: PathSet/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(PATHSET_CUSTOM),$(call custom_module_names,$(m),PathSet)) \
-DpluginInstance=pluginInstance__PathSet

$(BUILD_DIR)/PinkTrombone/%.cpp.o: PinkTrombone/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(PINKTROMBONE_CUSTOM),$(call custom_module_names,$(m),PinkTrombone)) \
-DpluginInstance=pluginInstance__PinkTrombone

$(BUILD_DIR)/Prism/%.cpp.o: Prism/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -1461,14 +1721,19 @@ $(BUILD_DIR)/sonusmodular/%.cpp.o: sonusmodular/%.cpp
$(foreach m,$(SONUSMODULAR_CUSTOM),$(call custom_module_names,$(m),sonusmodular)) \
-DpluginInstance=pluginInstance__sonusmodular

$(BUILD_DIR)/substation-%.cpp.o: substation-%.cpp
$(BUILD_DIR)/stocaudio/%.cpp.o: stocaudio/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(STOCAUDIO_CUSTOM),$(call custom_module_names,$(m),stocaudio)) \
-DpluginInstance=pluginInstance__stocaudio

$(BUILD_DIR)/unless_modules/%.cpp.o: unless_modules/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(SUBSTATION_CUSTOM),$(call custom_module_names,$(m),substation)) \
-DpluginInstance=pluginInstance__substation \
-D'PRIVATE=__attribute__((error("Using internal Rack function or symbol")))' \
-Isubstation-opensource/dep/slime4rack/include
$(foreach m,$(UNLESS_MODULES_CUSTOM),$(call custom_module_names,$(m),unless_modules)) \
-DpluginInstance=pluginInstance__unless_modules

$(BUILD_DIR)/ValleyAudio/%.cpp.o: ValleyAudio/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@@ -1481,6 +1746,15 @@ $(BUILD_DIR)/ValleyAudio/%.cpp.o: ValleyAudio/%.cpp
-Wno-sign-compare \
-Wno-unused-but-set-variable

$(BUILD_DIR)/voxglitch/%.cpp.o: voxglitch/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ \
$(foreach m,$(VOXGLITCH_CUSTOM),$(call custom_module_names,$(m),Voxglitch)) \
$(foreach m,$(VOXGLITCH_CUSTOM_PER_FILE),$(call custom_per_file_names,$(m),Voxglitch_$(shell basename $*))) \
-DpluginInstance=pluginInstance__Voxglitch \
-DSKIP_MINGW_FORMAT

$(BUILD_DIR)/ZetaCarinaeModules/%.cpp.o: ZetaCarinaeModules/%.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"


+ 1
- 1
plugins/MindMeldModular

@@ -1 +1 @@
Subproject commit 621a29f3f1b521582fd11ca7f17635d19faa340c
Subproject commit a721e381fa1d72d738c9c2daae08c740107e3d5e

+ 1
- 1
plugins/Mog

@@ -1 +1 @@
Subproject commit 01c4fac9f2e91f60125d36767224f457b2057fb7
Subproject commit 00a7e3b01f56da5cfc86720ae6951ecdf8953ee5

+ 1
- 0
plugins/Orbits

@@ -0,0 +1 @@
Subproject commit ff0c007feb9ed1de57ea246f86e8b2b68572f5e6

+ 1
- 0
plugins/ParableInstruments/parasites View File

@@ -0,0 +1 @@
../ArableInstruments/parasites

+ 25
- 0
plugins/ParableInstruments/plugin.json View File

@@ -0,0 +1,25 @@
{
"slug": "ParableInstruments",
"name": "Parable Instruments",
"version": "2.0.0",
"license": "GPL-3.0-or-later",
"brand": "Parable Instruments",
"author": "adbrant",
"authorEmail": "",
"authorUrl": "https://github.com/adbrant/ArableInstruments/blob/master/README.md",
"pluginUrl": "https://github.com/adbrant/ArableInstruments/blob/master/README.md",
"manualUrl": "https://github.com/adbrant/ArableInstruments/blob/master/README.md",
"sourceUrl": "https://github.com/adbrant/ArableInstruments.git",
"donateUrl": "",
"modules": [
{
"slug": "Neil",
"name": "Neil",
"description": "",
"tags": [
"Granular",
"Reverb"
]
}
]
}

+ 1
- 0
plugins/ParableInstruments/res/CKSS_rot_0.svg View File

@@ -0,0 +1 @@
../../ArableInstruments/res/CKSS_rot_0.svg

+ 1
- 0
plugins/ParableInstruments/res/CKSS_rot_1.svg View File

@@ -0,0 +1 @@
../../ArableInstruments/res/CKSS_rot_1.svg

+ 1
- 0
plugins/ParableInstruments/res/Neil.png View File

@@ -0,0 +1 @@
../../ArableInstruments/res/Neil.png

+ 1
- 0
plugins/ParableInstruments/res/Neil.svg View File

@@ -0,0 +1 @@
../../ArableInstruments/res/Neil.svg

+ 1
- 0
plugins/ParableInstruments/src/ArableInstruments.hpp View File

@@ -0,0 +1 @@
../../ArableInstruments/src/ArableInstruments.hpp

+ 1
- 0
plugins/ParableInstruments/src/Clouds.cpp View File

@@ -0,0 +1 @@
../../ArableInstruments/src/Clouds.cpp

+ 1
- 0
plugins/PathSet

@@ -0,0 +1 @@
Subproject commit d03e8b486deba4740bc7f15ae95a08d823a7d00a

+ 1
- 0
plugins/PinkTrombone

@@ -0,0 +1 @@
Subproject commit ea6ab0c6887102ebbf6e3534e0e891b867b130cc

+ 1
- 1
plugins/Prism

@@ -1 +1 @@
Subproject commit 453da225742f3829ba037770245333a28751fbb8
Subproject commit 8a9cc034d905079f156ed6c64efaeb4baf81490f

+ 1
- 1
plugins/ValleyAudio

@@ -1 +1 @@
Subproject commit c05209a6ad74e8b99703a033842797f06515f865
Subproject commit 98698dc28e6ed7aec56e4ab8280171a160d673ef

+ 1
- 1
plugins/ihtsyn

@@ -1 +1 @@
Subproject commit 31c4229eb328f6aaa4024f76c595b55213cdf1cf
Subproject commit 1b77e3c3ba12734bbd29a4aa59dd408e679b5cf7

+ 1
- 0
plugins/kocmoc

@@ -0,0 +1 @@
Subproject commit f2a8c19f8aa81769e13d085d69a44de5afaacfaa

+ 1
- 0
plugins/nonlinearcircuits

@@ -0,0 +1 @@
Subproject commit 57eb090f233c21b2edee541ea17d800f22045d91

+ 597
- 109
plugins/plugins.cpp
File diff suppressed because it is too large
View File


+ 1
- 1
plugins/repelzen

@@ -1 +1 @@
Subproject commit 185e07ea94086a04b3daacb4bf94c0fbd3544725
Subproject commit f812cc56b7fe9e41bb13da99ef23f014015c86c1

+ 1
- 0
plugins/stocaudio

@@ -0,0 +1 @@
Subproject commit ed5c85b0d9391c37f4ec4d9de4ef8aa30d94bcd6

+ 0
- 1
plugins/substation-opensource

@@ -1 +0,0 @@
Subproject commit cbc09d4db02d038493689c192d63b746d453d18f

+ 1
- 0
plugins/unless_modules

@@ -0,0 +1 @@
Subproject commit 3f895c7663e3e54c4e30c406c56d420ea407133e

+ 1
- 0
plugins/voxglitch

@@ -0,0 +1 @@
Subproject commit 03f4fc5cebb5d8eb152ac59d3a51ce0ec87a2740

+ 40
- 13
src/CardinalCommon.cpp View File

@@ -29,6 +29,7 @@

#include "AsyncDialog.hpp"
#include "PluginContext.hpp"
#include "DistrhoPluginUtils.hpp"

#include <asset.hpp>
#include <context.hpp>
@@ -43,18 +44,50 @@
# undef DEBUG
#endif

// for finding home dir
#ifndef ARCH_WIN
// for finding special paths
#ifdef ARCH_WIN
# include <shlobj.h>
#else
# include <pwd.h>
# include <unistd.h>
#endif

const std::string CARDINAL_VERSION = "22.02";
const std::string CARDINAL_VERSION = "22.04";

namespace rack {
namespace settings {
int rateLimit = 0;
}

bool isStandalone()
{
return std::strstr(getPluginFormatName(), "JACK") != nullptr;
}

#ifdef ARCH_WIN
std::string getSpecialPath(const SpecialPath type)
{
int csidl;
switch (type)
{
case kSpecialPathUserProfile:
csidl = CSIDL_PROFILE;
break;
case kSpecialPathCommonProgramFiles:
csidl = CSIDL_PROGRAM_FILES_COMMON;
break;
default:
return {};
}

WCHAR path[MAX_PATH + 256];

if (SHGetSpecialFolderPathW(nullptr, path, csidl, FALSE))
return string::UTF16toUTF8(path);

return {};
}
#endif
}

namespace patchUtils
@@ -74,19 +107,11 @@ static void promptClear(const char* const message, const std::function<void()> a
static std::string homeDir()
{
# ifdef ARCH_WIN
if (const char* const userprofile = getenv("USERPROFILE"))
{
return userprofile;
}
else if (const char* const homedrive = getenv("HOMEDRIVE"))
{
if (const char* const homepath = getenv("HOMEPATH"))
return system::join(homedrive, homepath);
}
return getSpecialPath(kSpecialPathUserProfile);
# else
if (const char* const home = getenv("HOME"))
return home;
else if (struct passwd* const pwd = getpwuid(getuid()))
if (struct passwd* const pwd = getpwuid(getuid()))
return pwd->pw_dir;
# endif
return {};
@@ -112,6 +137,7 @@ void loadDialog()
FileBrowserOptions opts;
opts.startDir = dir.c_str();
opts.saving = ui->saving = false;
opts.title = "Open patch";
ui->openFileBrowser(opts);
});
#endif
@@ -208,6 +234,7 @@ static void saveAsDialog(const bool uncompressed)
FileBrowserOptions opts;
opts.startDir = dir.c_str();
opts.saving = ui->saving = true;
opts.title = "Save patch";
ui->savingUncompressed = uncompressed;
ui->openFileBrowser(opts);
}


+ 10
- 0
src/CardinalCommon.hpp View File

@@ -41,6 +41,16 @@ namespace window {
void generateScreenshot();
}

bool isStandalone();

#ifdef ARCH_WIN
enum SpecialPath {
kSpecialPathUserProfile,
kSpecialPathCommonProgramFiles,
};
std::string getSpecialPath(SpecialPath type);
#endif

} // namespace rack

namespace patchUtils {


+ 50
- 21
src/CardinalModuleWidget.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -37,6 +37,7 @@
#include <asset.hpp>
#include <context.hpp>
#include <helpers.hpp>
#include <settings.hpp>
#include <system.hpp>

namespace rack {
@@ -54,7 +55,6 @@ struct ModuleWidget::Internal {
math::Vec dragOffset;
math::Vec dragRackPos;
bool dragEnabled;
math::Vec oldPos;
widget::Widget* panel;
};

@@ -300,36 +300,65 @@ static void CardinalModuleWidget__saveSelectionDialog(RackWidget* const w)

void CardinalModuleWidget::onButton(const ButtonEvent& e)
{
bool selected = APP->scene->rack->isSelected(this);
const bool selected = APP->scene->rack->isSelected(this);

if (selected) {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
ui::Menu* menu = createMenu();
patchUtils::appendSelectionContextMenu(menu);
if (e.button == GLFW_MOUSE_BUTTON_RIGHT) {
if (e.action == GLFW_PRESS) {
// Open selection context menu on right-click
ui::Menu* menu = createMenu();
patchUtils::appendSelectionContextMenu(menu);
}
e.consume(this);
}

e.consume(this);
if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
if (e.action == GLFW_PRESS) {
// Toggle selection on Shift-click
if ((e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
APP->scene->rack->select(this, false);
e.consume(NULL);
return;
}

internal->dragOffset = e.pos;
}

e.consume(this);
}

return;
}

OpaqueWidget::onButton(e);
// Dispatch event to children
Widget::onButton(e);
e.stopPropagating();
if (e.isConsumed())
return;

if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
if (e.action == GLFW_PRESS) {
// Toggle selection on Shift-click
if ((e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
APP->scene->rack->select(this, true);
e.consume(NULL);
return;
}

// If module positions are locked, don't consume left-click
if (settings::lockModules) {
return;
}

if (e.getTarget() == this) {
// Set starting drag position
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
internal->dragOffset = e.pos;
}
// Toggle selection on Shift-click
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
APP->scene->rack->select(this, !selected);
}
e.consume(this);
}

if (!e.isConsumed() && !selected) {
// Open context menu on right-click
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
CardinalModuleWidget__createContextMenu(this, model, module);
e.consume(this);
}
// Open context menu on right-click
if (e.button == GLFW_MOUSE_BUTTON_RIGHT && e.action == GLFW_PRESS) {
CardinalModuleWidget__createContextMenu(this, model, module);
e.consume(this);
}
}



+ 232
- 22
src/CardinalPlugin.cpp View File

@@ -49,12 +49,22 @@
#include "extra/Base64.hpp"
#include "extra/SharedResourcePointer.hpp"
static const constexpr uint kCardinalStateBaseCount = 3; // patch, screenshot, comment
#ifndef HEADLESS
# include "WindowParameters.hpp"
static const constexpr uint kCardinalStateCount = 4; // patch, screenshot, comment, windowSize
static const constexpr uint kCardinalStateCount = kCardinalStateBaseCount + 2; // moduleInfos, windowSize
#else
# define kWindowParameterCount 0
static const constexpr uint kCardinalStateCount = 3; // patch, screenshot, comment
static const constexpr uint kCardinalStateCount = kCardinalStateBaseCount;
#endif
#if CARDINAL_VARIANT_FX
# define CARDINAL_TEMPLATE_NAME "template-fx.vcv"
#elif CARDINAL_VARIANT_SYNTH
# define CARDINAL_TEMPLATE_NAME "template-synth.vcv"
#else
# define CARDINAL_TEMPLATE_NAME "template.vcv"
#endif
namespace rack {
@@ -134,11 +144,11 @@ struct Initializer
{
asset::bundlePath = system::join(resourcePath, "PluginManifests");
asset::systemDir = resourcePath;
templatePath = system::join(asset::systemDir, "template.vcv");
templatePath = system::join(asset::systemDir, CARDINAL_TEMPLATE_NAME);
}
}
if (asset::systemDir.empty())
if (asset::systemDir.empty() || ! system::exists(asset::systemDir))
{
#ifdef CARDINAL_PLUGIN_SOURCE_DIR
// Make system dir point to source code location as fallback
@@ -146,16 +156,27 @@ struct Initializer
if (system::exists(system::join(asset::systemDir, "res")))
{
templatePath = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR "template.vcv";
templatePath = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR CARDINAL_TEMPLATE_NAME;
}
// If source code dir does not exist use install target prefix as system dir
else
#endif
if (system::exists(CARDINAL_PLUGIN_PREFIX "/share/cardinal"))
{
asset::bundlePath = CARDINAL_PLUGIN_PREFIX "/share/cardinal/PluginManifests";
#if defined(ARCH_MAC)
asset::systemDir = "/Library/Application Support/Cardinal";
#elif defined(ARCH_WIN)
const std::string commonprogfiles = getSpecialPath(kSpecialPathCommonProgramFiles);
if (! commonprogfiles.empty())
asset::systemDir = system::join(commonprogfiles, "Cardinal");
#else
asset::systemDir = CARDINAL_PLUGIN_PREFIX "/share/cardinal";
templatePath = system::join(asset::systemDir, "template.vcv");
#endif
if (! asset::systemDir.empty())
{
asset::bundlePath = system::join(asset::systemDir, "PluginManifests");
templatePath = system::join(asset::systemDir, CARDINAL_TEMPLATE_NAME);
}
}
}
@@ -333,6 +354,9 @@ struct Initializer
void CardinalPluginContext::writeMidiMessage(const rack::midi::Message& message, const uint8_t channel)
{
if (bypassed)
return;
const size_t size = message.bytes.size();
DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
DISTRHO_SAFE_ASSERT_RETURN(message.frame >= 0,);
@@ -425,9 +449,18 @@ class CardinalPlugin : public CardinalBasePlugin
std::string fAutosavePath;
uint64_t fPreviousFrame;
String fStateComment;
String fStateScreenshot;
String fWindowSize;
struct {
String comment;
String screenshot;
#ifndef HEADLESS
String windowSize;
#endif
} fState;
// bypass handling
bool fWasBypassed;
MidiEvent bypassMidiEvents[16];
#ifndef HEADLESS
// real values, not VCV interpreted ones
@@ -441,7 +474,8 @@ public:
#if DISTRHO_PLUGIN_NUM_INPUTS != 0
fAudioBufferCopy(nullptr),
#endif
fPreviousFrame(0)
fPreviousFrame(0),
fWasBypassed(false)
{
#ifndef HEADLESS
fWindowParameters[kWindowParameterShowTooltips] = 1.0f;
@@ -454,6 +488,9 @@ public:
fWindowParameters[kWindowParameterWheelSensitivity] = 1.0f;
fWindowParameters[kWindowParameterLockModulePositions] = 0.0f;
fWindowParameters[kWindowParameterUpdateRateLimit] = 0.0f;
fWindowParameters[kWindowParameterBrowserSort] = 3.0f;
fWindowParameters[kWindowParameterBrowserZoom] = 50.0f;
fWindowParameters[kWindowParameterInvertZoom] = 0.0f;
#endif
// create unique temporary path for this instance
@@ -475,6 +512,16 @@ public:
}
} DISTRHO_SAFE_EXCEPTION("create unique temporary path");
// initialize midi events used when entering bypassed state
std::memset(bypassMidiEvents, 0, sizeof(bypassMidiEvents));
for (uint8_t i=0; i<16; ++i)
{
bypassMidiEvents[i].size = 3;
bypassMidiEvents[i].data[0] = 0xB0 + i;
bypassMidiEvents[i].data[1] = 0x7B;
}
const float sampleRate = getSampleRate();
rack::settings::sampleRate = sampleRate;
@@ -564,7 +611,7 @@ protected:
uint32_t getVersion() const override
{
return d_version(0, 22, 2);
return d_version(0, 22, 4);
}
int64_t getUniqueId() const override
@@ -721,6 +768,63 @@ protected:
parameter.enumValues.values[2].label = "4x";
parameter.enumValues.values[2].value = 2.0f;
break;
case kWindowParameterBrowserSort:
parameter.name = "Browser sort";
parameter.symbol = "browserSort";
parameter.hints = kParameterIsAutomatable|kParameterIsInteger;
parameter.ranges.def = 3.0f;
parameter.ranges.min = 0.0f;
parameter.ranges.max = 5.0f;
parameter.enumValues.count = 6;
parameter.enumValues.restrictedMode = true;
parameter.enumValues.values = new ParameterEnumerationValue[6];
parameter.enumValues.values[0].label = "Updated";
parameter.enumValues.values[0].value = 0.0f;
parameter.enumValues.values[1].label = "Last used";
parameter.enumValues.values[1].value = 1.0f;
parameter.enumValues.values[2].label = "Most used";
parameter.enumValues.values[2].value = 2.0f;
parameter.enumValues.values[3].label = "Brand";
parameter.enumValues.values[3].value = 3.0f;
parameter.enumValues.values[4].label = "Name";
parameter.enumValues.values[4].value = 4.0f;
parameter.enumValues.values[5].label = "Random";
parameter.enumValues.values[5].value = 5.0f;
break;
case kWindowParameterBrowserZoom:
parameter.name = "Browser zoom";
parameter.symbol = "browserZoom";
parameter.hints = kParameterIsAutomatable;
parameter.unit = "%";
parameter.ranges.def = 50.0f;
parameter.ranges.min = 25.0f;
parameter.ranges.max = 200.0f;
parameter.enumValues.count = 7;
parameter.enumValues.restrictedMode = true;
parameter.enumValues.values = new ParameterEnumerationValue[7];
parameter.enumValues.values[0].label = "25";
parameter.enumValues.values[0].value = 25.0f;
parameter.enumValues.values[1].label = "35";
parameter.enumValues.values[1].value = 35.0f;
parameter.enumValues.values[2].label = "50";
parameter.enumValues.values[2].value = 50.0f;
parameter.enumValues.values[3].label = "71";
parameter.enumValues.values[3].value = 71.0f;
parameter.enumValues.values[4].label = "100";
parameter.enumValues.values[4].value = 100.0f;
parameter.enumValues.values[5].label = "141";
parameter.enumValues.values[5].value = 141.0f;
parameter.enumValues.values[6].label = "200";
parameter.enumValues.values[6].value = 200.0f;
break;
case kWindowParameterInvertZoom:
parameter.name = "Invert zoom";
parameter.symbol = "invertZoom";
parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
parameter.ranges.def = 0.0f;
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
break;
}
#endif
}
@@ -745,6 +849,11 @@ protected:
state.label = "Comment";
break;
case 3:
state.hints = kStateIsOnlyForUI;
state.key = "moduleInfos";
state.label = "moduleInfos";
break;
case 4:
state.hints = kStateIsOnlyForUI;
state.key = "windowSize";
state.label = "Window size";
@@ -763,7 +872,7 @@ protected:
// bypass
if (index == kModuleParameters)
return 0.0f;
return context->bypassed ? 1.0f : 0.0f;
#ifndef HEADLESS
// window related parameters
@@ -787,7 +896,10 @@ protected:
// bypass
if (index == kModuleParameters)
{
context->bypassed = value > 0.5f;
return;
}
#ifndef HEADLESS
// window related parameters
@@ -804,14 +916,57 @@ protected:
String getState(const char* const key) const override
{
#ifndef HEADLESS
if (std::strcmp(key, "moduleInfos") == 0)
{
json_t* const rootJ = json_object();
DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, String());
for (const auto& pluginPair : rack::settings::moduleInfos)
{
json_t* const pluginJ = json_object();
DISTRHO_SAFE_ASSERT_CONTINUE(pluginJ != nullptr);
for (const auto& modulePair : pluginPair.second)
{
json_t* const moduleJ = json_object();
DISTRHO_SAFE_ASSERT_CONTINUE(moduleJ != nullptr);
const rack::settings::ModuleInfo& m(modulePair.second);
// To make setting.json smaller, only set properties if not default values.
if (m.favorite)
json_object_set_new(moduleJ, "favorite", json_boolean(m.favorite));
if (m.added > 0)
json_object_set_new(moduleJ, "added", json_integer(m.added));
if (std::isfinite(m.lastAdded))
json_object_set_new(moduleJ, "lastAdded", json_real(m.lastAdded));
if (json_object_size(moduleJ))
json_object_set_new(pluginJ, modulePair.first.c_str(), moduleJ);
else
json_decref(moduleJ);
}
if (json_object_size(pluginJ))
json_object_set_new(rootJ, pluginPair.first.c_str(), pluginJ);
else
json_decref(pluginJ);
}
const String info(json_dumps(rootJ, JSON_COMPACT), false);
json_decref(rootJ);
return info;
}
if (std::strcmp(key, "windowSize") == 0)
return fWindowSize;
return fState.windowSize;
#endif
if (std::strcmp(key, "comment") == 0)
return fStateComment;
return fState.comment;
if (std::strcmp(key, "screenshot") == 0)
return fStateScreenshot;
return fState.screenshot;
if (std::strcmp(key, "patch") != 0)
return String();
@@ -839,22 +994,56 @@ protected:
void setState(const char* const key, const char* const value) override
{
#ifndef HEADLESS
if (std::strcmp(key, "moduleInfos") == 0)
{
json_error_t error;
json_t* const rootJ = json_loads(value, 0, &error);
DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr,);
const char* pluginSlug;
json_t* pluginJ;
json_object_foreach(rootJ, pluginSlug, pluginJ)
{
const char* moduleSlug;
json_t* moduleJ;
json_object_foreach(pluginJ, moduleSlug, moduleJ)
{
rack::settings::ModuleInfo m;
if (json_t* const favoriteJ = json_object_get(moduleJ, "favorite"))
m.favorite = json_boolean_value(favoriteJ);
if (json_t* const addedJ = json_object_get(moduleJ, "added"))
m.added = json_integer_value(addedJ);
if (json_t* const lastAddedJ = json_object_get(moduleJ, "lastAdded"))
m.lastAdded = json_number_value(lastAddedJ);
rack::settings::moduleInfos[pluginSlug][moduleSlug] = m;
}
}
json_decref(rootJ);
return;
}
if (std::strcmp(key, "windowSize") == 0)
{
fWindowSize = value;
fState.windowSize = value;
return;
}
#endif
if (std::strcmp(key, "comment") == 0)
{
fStateComment = value;
fState.comment = value;
return;
}
if (std::strcmp(key, "screenshot") == 0)
{
fStateScreenshot = value;
fState.screenshot = value;
#if defined(HAVE_LIBLO) && !defined(HEADLESS)
patchUtils::sendScreenshotToRemote(value);
#endif
@@ -914,6 +1103,8 @@ protected:
{
rack::contextSet(context);
const bool bypassed = context->bypassed;
{
const TimePosition& timePos(getTimePosition());
@@ -967,10 +1158,29 @@ protected:
for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
std::memset(outputs[i], 0, sizeof(float)*frames);
context->midiEvents = midiEvents;
context->midiEventCount = midiEventCount;
if (bypassed)
{
if (fWasBypassed != bypassed)
{
context->midiEvents = bypassMidiEvents;
context->midiEventCount = 16;
}
else
{
context->midiEvents = nullptr;
context->midiEventCount = 0;
}
}
else
{
context->midiEvents = midiEvents;
context->midiEventCount = midiEventCount;
}
++context->processCounter;
context->engine->stepBlock(frames);
fWasBypassed = bypassed;
}
void bufferSizeChanged(const uint32_t newBufferSize) override


+ 51
- 6
src/CardinalUI.cpp View File

@@ -304,9 +304,7 @@ public:
// hide "Browse VCV Library" button
rack::widget::Widget* const browser = context->scene->browser->children.back();
rack::widget::Widget* const headerLayout = browser->children.front();
rack::widget::Widget* const favoriteButton = *std::next(headerLayout->children.begin(), 3);
rack::widget::Widget* const libraryButton = headerLayout->children.back();
favoriteButton->hide();
libraryButton->hide();

// Report to user if something is wrong with the installation
@@ -325,7 +323,15 @@ public:
}

if (! errorMessage.empty())
asyncDialog::create(errorMessage.c_str());
{
static bool shown = false;

if (! shown)
{
shown = true;
asyncDialog::create(errorMessage.c_str());
}
}

context->window->step();

@@ -382,13 +388,11 @@ public:
filebrowserhandle = nullptr;
}

#ifndef DISTRHO_OS_MAC
if (windowParameters.rateLimit != 0 && ++rateLimitStep % (windowParameters.rateLimit * 2))
return;

rateLimitStep = 0;
repaint();
#endif
}

void WindowParametersChanged(const WindowParameterList param, float value) override
@@ -447,6 +451,16 @@ public:
windowParameters.rateLimit = static_cast<int>(value + 0.5f);
rateLimitStep = 0;
break;
case kWindowParameterBrowserSort:
windowParameters.browserSort = static_cast<int>(value + 0.5f);
break;
case kWindowParameterBrowserZoom:
windowParameters.browserZoom = value;
value = std::pow(2.f, value) * 100.0f;
break;
case kWindowParameterInvertZoom:
windowParameters.invertZoom = value > 0.5f;
break;
default:
return;
}
@@ -512,6 +526,37 @@ protected:
windowParameters.rateLimit = static_cast<int>(value + 0.5f);
rateLimitStep = 0;
break;
case kWindowParameterBrowserSort:
windowParameters.browserSort = static_cast<int>(value + 0.5f);
break;
case kWindowParameterBrowserZoom:
// round up to nearest valid value
{
float rvalue = value - 1.0f;

if (rvalue <= 25.0f)
rvalue = -2.0f;
else if (rvalue <= 35.0f)
rvalue = -1.5f;
else if (rvalue <= 50.0f)
rvalue = -1.0f;
else if (rvalue <= 71.0f)
rvalue = -0.5f;
else if (rvalue <= 100.0f)
rvalue = 0.0f;
else if (rvalue <= 141.0f)
rvalue = 0.5f;
else if (rvalue <= 200.0f)
rvalue = 1.0f;
else
rvalue = 0.0f;

windowParameters.browserZoom = rvalue;
}
break;
case kWindowParameterInvertZoom:
windowParameters.invertZoom = value > 0.5f;
break;
default:
return;
}
@@ -519,7 +564,7 @@ protected:
WindowParametersSetValues(context->window, windowParameters);
}

void stateChanged(const char* key, const char* value) override
void stateChanged(const char* const key, const char* const value) override
{
if (std::strcmp(key, "windowSize") != 0)
return;


+ 30
- 7
src/Makefile View File

@@ -36,7 +36,6 @@ ifeq ($(HAIKU),true)
BASE_FLAGS += -I../include/haiku-compat
endif

BASE_FLAGS += -fno-strict-aliasing
BASE_FLAGS += -DPRIVATE=
BASE_FLAGS += -I../dpf/dgl/src/nanovg
BASE_FLAGS += -I../dpf/distrho
@@ -95,8 +94,8 @@ BASE_FLAGS += -I../include/mingw-std-threads
endif

BUILD_C_FLAGS += -std=gnu11
BUILD_C_FLAGS += -fno-finite-math-only
BUILD_CXX_FLAGS += -fno-finite-math-only
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing -faligned-new

# use our custom function to invert some colors
BUILD_CXX_FLAGS += -DnsvgParseFromFile=nsvgParseFromFileCardinal
@@ -116,6 +115,7 @@ RACK_FILES += custom/network.cpp
RACK_FILES += custom/osdialog.cpp
RACK_FILES += override/blendish.c
RACK_FILES += override/context.cpp
RACK_FILES += override/minblep.cpp
RACK_FILES += override/plugin.cpp
RACK_FILES += override/Engine.cpp
RACK_FILES += override/MenuBar.cpp
@@ -145,6 +145,7 @@ IGNORED_FILES += Rack/src/app/MenuBar.cpp
IGNORED_FILES += Rack/src/app/MidiDisplay.cpp
IGNORED_FILES += Rack/src/app/Scene.cpp
IGNORED_FILES += Rack/src/app/TipWindow.cpp
IGNORED_FILES += Rack/src/dsp/minblep.cpp
IGNORED_FILES += Rack/src/engine/Engine.cpp
IGNORED_FILES += Rack/src/plugin/Model.cpp
IGNORED_FILES += Rack/src/window/Window.cpp
@@ -172,21 +173,43 @@ endif

TARGET = rack.a

ifneq ($(MACOS),true)
CARDINAL_FX_ARGS = VST2_FILENAME=Cardinal.vst/CardinalFX$(LIB_EXT)
CARDINAL_SYNTH_ARGS = VST2_FILENAME=Cardinal.vst/CardinalSynth$(LIB_EXT)
endif

all: $(TARGET)
ifeq ($(MOD_BUILD),true)
$(MAKE) -C CardinalFX lv2
else
$(MAKE) -C Cardinal
$(MAKE) -C CardinalFX
$(MAKE) -C CardinalSynth
$(MAKE) -C CardinalFX $(CARDINAL_FX_ARGS)
$(MAKE) -C CardinalSynth $(CARDINAL_SYNTH_ARGS)
endif

jack: $(TARGET)
$(MAKE) jack -C Cardinal

lv2: $(TARGET)
$(MAKE) lv2 -C Cardinal
$(MAKE) lv2 -C CardinalFX $(CARDINAL_FX_ARGS)
$(MAKE) lv2 -C CardinalSynth $(CARDINAL_SYNTH_ARGS)

vst2: $(TARGET)
$(MAKE) vst2 -C CardinalFX $(CARDINAL_FX_ARGS)
$(MAKE) vst2 -C CardinalSynth $(CARDINAL_SYNTH_ARGS)

vst3: $(TARGET)
$(MAKE) vst3 -C Cardinal
$(MAKE) vst3 -C CardinalFX $(CARDINAL_FX_ARGS)
$(MAKE) vst3 -C CardinalSynth $(CARDINAL_SYNTH_ARGS)

clean:
rm -f $(TARGET)
rm -rf $(BUILD_DIR)
$(MAKE) clean -C Cardinal
$(MAKE) clean -C CardinalFX
$(MAKE) clean -C CardinalSynth
$(MAKE) clean -C CardinalFX $(CARDINAL_FX_ARGS)
$(MAKE) clean -C CardinalSynth $(CARDINAL_SYNTH_ARGS)

# --------------------------------------------------------------
# Build commands


+ 38
- 9
src/Makefile.cardinal.mk View File

@@ -85,6 +85,10 @@ FILES_UI = CardinalUI.cpp
FILES_UI += Window.cpp
endif

ifeq ($(WINDOWS),true)
FILES_UI += distrho.rc
endif

# --------------------------------------------------------------
# Extra libraries to link against

@@ -129,7 +133,6 @@ else
BASE_FLAGS += -DARCH_LIN
endif

BASE_FLAGS += -fno-strict-aliasing
BASE_FLAGS += -DPRIVATE=
BASE_FLAGS += -I..
BASE_FLAGS += -I../../dpf/dgl/src/nanovg
@@ -170,8 +173,8 @@ BASE_FLAGS += -I../../include/mingw-std-threads
endif

BUILD_C_FLAGS += -std=gnu11
BUILD_C_FLAGS += -fno-finite-math-only
BUILD_CXX_FLAGS += -fno-finite-math-only
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing -faligned-new

# Rack code is not tested for this flag, unset it
BUILD_CXX_FLAGS += -U_GLIBCXX_ASSERTIONS -Wp,-U_GLIBCXX_ASSERTIONS
@@ -207,7 +210,7 @@ ifeq ($(MACOS),true)
LINK_FLAGS += -framework IOKit
else ifeq ($(WINDOWS),true)
# needed by VCVRack
EXTRA_LIBS += -ldbghelp -lshlwapi
EXTRA_LIBS += -ldbghelp -lshlwapi -Wl,--stack,0x100000
# needed by JW-Modules
EXTRA_LIBS += -lws2_32 -lwinmm
endif
@@ -236,6 +239,7 @@ endif
# --------------------------------------------------------------
# fallback path to resource files

ifneq ($(CIBUILD),true)
ifneq ($(SYSDEPS),true)

ifeq ($(EXE_WRAPPER),wine)
@@ -246,6 +250,7 @@ endif

BUILD_CXX_FLAGS += -DCARDINAL_PLUGIN_SOURCE_DIR='"$(SOURCE_DIR)"'

endif
endif

# --------------------------------------------------------------
@@ -257,13 +262,17 @@ BUILD_CXX_FLAGS += -DCARDINAL_PLUGIN_PREFIX='"$(PREFIX)"'
# Enable all possible plugin types and setup resources

ifeq ($(CARDINAL_VARIANT),main)
ifneq ($(STATIC_BUILD),true)
all: jack lv2 vst3
else
all: lv2 vst2 vst3
all: lv2 vst3
endif # STATIC_BUILD
else
all: lv2 vst2 vst3 static
endif

CORE_RESOURCES = $(subst ../Rack/res/,,$(wildcard ../Rack/res/ComponentLibrary/*.svg ../Rack/res/fonts/*.ttf))
CORE_RESOURCES += template.vcv
CORE_RESOURCES += $(subst ../,,$(wildcard ../template*.vcv))

LV2_RESOURCES = $(CORE_RESOURCES:%=$(TARGET_DIR)/$(NAME).lv2/resources/%)
VST3_RESOURCES = $(CORE_RESOURCES:%=$(TARGET_DIR)/$(NAME).vst3/Contents/Resources/%)
@@ -285,7 +294,7 @@ ifneq ($(CARDINAL_VARIANT),main)
ifeq ($(MACOS),true)
VST2_RESOURCES = $(CORE_RESOURCES:%=$(TARGET_DIR)/$(NAME).vst/Contents/Resources/%)
else
VST2_RESOURCES = $(CORE_RESOURCES:%=$(TARGET_DIR)/$(NAME).vst/resources/%)
VST2_RESOURCES = $(CORE_RESOURCES:%=$(TARGET_DIR)/Cardinal.vst/resources/%)
endif
endif

@@ -294,8 +303,28 @@ vst2: $(VST2_RESOURCES)
vst3: $(VST3_RESOURCES)

# --------------------------------------------------------------
# Extra rules for Windows icon

ifeq ($(WINDOWS),true)
JACK_LIBS += -Wl,-subsystem,windows

$(BUILD_DIR)/distrho.rc.o: ../../utils/distrho.rc ../../utils/distrho.ico
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling distrho.rc"
$(SILENT)$(WINDRES) $< -O coff -o $@
endif

# --------------------------------------------------------------

$(TARGET_DIR)/%/template.vcv: ../template.vcv
-@mkdir -p "$(shell dirname $@)"
$(SILENT)ln -sf $(abspath $<) $@

$(TARGET_DIR)/%/template-fx.vcv: ../template-fx.vcv
-@mkdir -p "$(shell dirname $@)"
$(SILENT)ln -sf $(abspath $<) $@

$(TARGET_DIR)/$(NAME).%/template.vcv: ../template.vcv
$(TARGET_DIR)/%/template-synth.vcv: ../template-synth.vcv
-@mkdir -p "$(shell dirname $@)"
$(SILENT)ln -sf $(abspath $<) $@

@@ -316,7 +345,7 @@ $(TARGET_DIR)/$(NAME).lv2/modgui/documentation.pdf: ../../docs/MODDEVICES.md $(T
(cd ../../docs/ && pandoc MODDEVICES.md -f markdown+implicit_figures -o $(abspath $@))
endif

$(TARGET_DIR)/$(NAME).vst/resources/%: ../Rack/res/%
$(TARGET_DIR)/Cardinal.vst/resources/%: ../Rack/res/%
-@mkdir -p "$(shell dirname $@)"
$(SILENT)ln -sf $(abspath $<) $@



+ 4
- 2
src/PluginContext.hpp View File

@@ -48,11 +48,11 @@ enum CardinalVariant {
// -----------------------------------------------------------------------------------------------------------

struct CardinalPluginContext : rack::Context {
uint32_t bufferSize;
uint32_t bufferSize, processCounter;
double sampleRate;
float parameters[kModuleParameters];
CardinalVariant variant;
bool playing, reset, bbtValid;
bool bypassed, playing, reset, bbtValid;
int32_t bar, beat, beatsPerBar, beatType;
uint64_t frame;
double barStartTick, beatsPerMinute;
@@ -69,6 +69,7 @@ struct CardinalPluginContext : rack::Context {

CardinalPluginContext(Plugin* const p)
: bufferSize(p->getBufferSize()),
processCounter(0),
sampleRate(p->getSampleRate()),
#if CARDINAL_VARIANT_MAIN
variant(kCardinalVariantMain),
@@ -79,6 +80,7 @@ struct CardinalPluginContext : rack::Context {
#else
#error cardinal variant not set
#endif
bypassed(false),
playing(false),
reset(false),
bbtValid(false),


+ 1
- 1
src/Rack

@@ -1 +1 @@
Subproject commit 0d003b96476af45102117c2bb958aeb59eb523cf
Subproject commit 30665d62801c2ced7260a37a2d0214edfe6528a9

+ 6
- 0
src/WindowParameters.hpp View File

@@ -44,6 +44,9 @@ enum WindowParameterList {
kWindowParameterWheelSensitivity,
kWindowParameterLockModulePositions,
kWindowParameterUpdateRateLimit,
kWindowParameterBrowserSort,
kWindowParameterBrowserZoom,
kWindowParameterInvertZoom,
kWindowParameterCount,
};

@@ -53,10 +56,13 @@ struct WindowParameters {
float rackBrightness = 1.0f;
float haloBrightness = 0.25f;
float knobScrollSensitivity = 0.001f;
float browserZoom = -1.0f;
int knobMode = 0;
int browserSort = 3;
bool tooltips = true;
bool knobScroll = false;
bool lockModules = false;
bool invertZoom = false;
// cardinal specific
int rateLimit = 0;
};


+ 18
- 137
src/custom/RemoteWindow.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -51,16 +51,10 @@ static const math::Vec minWindowSize = math::Vec(1228, 666);


void Font::loadFile(const std::string& filename, NVGcontext* vg) {
this->vg = vg;
handle = nvgCreateFont(vg, filename.c_str(), filename.c_str());
if (handle < 0)
throw Exception("Failed to load font %s", filename.c_str());
INFO("Loaded font %s", filename.c_str());
}


Font::~Font() {
// There is no NanoVG deleteFont() function yet, so do nothing
}


@@ -70,18 +64,10 @@ std::shared_ptr<Font> Font::load(const std::string& filename) {


void Image::loadFile(const std::string& filename, NVGcontext* vg) {
this->vg = vg;
handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
if (handle <= 0)
throw Exception("Failed to load image %s", filename.c_str());
INFO("Loaded image %s", filename.c_str());
}


Image::~Image() {
// TODO What if handle is invalid?
if (handle >= 0)
nvgDeleteImage(vg, handle);
}


@@ -90,115 +76,39 @@ std::shared_ptr<Image> Image::load(const std::string& filename) {
}


struct WindowParams {
float rackBrightness = 1.0f;
};

struct Window::Internal
{
Context* context = nullptr;
Window* self = nullptr;

math::Vec size = minWindowSize;
std::string lastWindowTitle;

int mods = 0;
int frame = 0;
int frameSwapInterval = 1;
double monitorRefreshRate = 60.0; // FIXME
double frameTime = 0.0;
double lastFrameDuration = 0.0;

std::map<std::string, std::shared_ptr<Font>> fontCache;
std::map<std::string, std::shared_ptr<Image>> imageCache;

bool fbDirtyOnSubpixelChange = true;
int fbCount = 0;
};

Window::Window() {
internal = new Internal;
internal->context = APP;
internal->self = this;
windowRatio = minWindowSize.x / minWindowSize.y;
widget::Widget::ContextCreateEvent e;
APP->scene->onContextCreate(e);
}

Window::~Window() {
// internal->stopThread(5000);

Window::~Window() {
if (APP->scene) {
widget::Widget::ContextDestroyEvent e;
APP->scene->onContextDestroy(e);
}

// Fonts and Images in the cache must be deleted before the NanoVG context is deleted
internal->fontCache.clear();
internal->imageCache.clear();

delete internal;
}


math::Vec Window::getSize() {
return internal->size;
return minWindowSize;
}


void Window::setSize(math::Vec size) {
internal->size = size.max(minWindowSize);
void Window::setSize(math::Vec) {
}


void Window::run() {
internal->frame = 0;
}


void Window::step() {
double frameTime = system::getTime();
double lastFrameTime = internal->frameTime;
internal->frameTime = frameTime;
internal->lastFrameDuration = frameTime - lastFrameTime;
internal->fbCount = 0;
// DEBUG("%.2lf Hz", 1.0 / internal->lastFrameDuration);

// Make event handlers and step() have a clean NanoVG context
nvgReset(vg);

if (uiFont != nullptr)
bndSetFont(uiFont->handle);

// Get framebuffer/window ratio
windowRatio = internal->size.x / internal->size.y;

if (APP->scene) {
// Resize scene
APP->scene->box.size = internal->size;

// Step scene
APP->scene->step();

// Update and render
nvgBeginFrame(vg, internal->size.x, internal->size.y, pixelRatio);
nvgScale(vg, pixelRatio, pixelRatio);

// Draw scene
widget::Widget::DrawArgs args;
args.vg = vg;
args.clipBox = APP->scene->box.zeroPos();
APP->scene->draw(args);

glViewport(0, 0, internal->size.x, internal->size.y);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
nvgEndFrame(vg);
}

internal->frame++;
}


void Window::activateContext() {
// glfwMakeContextCurrent(win);
}


@@ -228,7 +138,7 @@ bool Window::isCursorLocked() {


int Window::getMods() {
return internal->mods;
return 0;
}


@@ -242,73 +152,44 @@ bool Window::isFullScreen() {


double Window::getMonitorRefreshRate() {
return internal->monitorRefreshRate;
return 60;
}


double Window::getFrameTime() {
return internal->frameTime;
return 0;
}


double Window::getLastFrameDuration() {
return internal->lastFrameDuration;
return 0.0;
}


double Window::getFrameDurationRemaining() {
double frameDurationDesired = internal->frameSwapInterval / internal->monitorRefreshRate;
return frameDurationDesired - (system::getTime() - internal->frameTime);
return 0.0;
}


std::shared_ptr<Font> Window::loadFont(const std::string& filename) {
const auto& pair = internal->fontCache.find(filename);
if (pair != internal->fontCache.end())
return pair->second;

// Load font
std::shared_ptr<Font> font;
try {
font = std::make_shared<Font>();
font->loadFile(filename, vg);
}
catch (Exception& e) {
WARN("%s", e.what());
font = NULL;
}
internal->fontCache[filename] = font;
return font;
return std::make_shared<Font>();
}


std::shared_ptr<Image> Window::loadImage(const std::string& filename) {
const auto& pair = internal->imageCache.find(filename);
if (pair != internal->imageCache.end())
return pair->second;

// Load image
std::shared_ptr<Image> image;
try {
image = std::make_shared<Image>();
image->loadFile(filename, vg);
}
catch (Exception& e) {
WARN("%s", e.what());
image = NULL;
}
internal->imageCache[filename] = image;
return image;
return std::make_shared<Image>();
}


bool& Window::fbDirtyOnSubpixelChange() {
return internal->fbDirtyOnSubpixelChange;
static bool _fbDirtyOnSubpixelChange;
return _fbDirtyOnSubpixelChange;
}


int& Window::fbCount() {
return internal->fbCount;
static int _fbCount;
return _fbCount;
}




+ 225
- 11
src/custom/dep.cpp View File

@@ -56,6 +56,21 @@ static const struct {
{ "/21kHz/res/Panels/D_Inf.svg", {}, -1 },
{ "/21kHz/res/Panels/PalmLoop.svg", {}, -1 },
{ "/21kHz/res/Panels/TachyonEntangler.svg", {}, -1 },
// MIT
{"/AaronStatic/res/ChordCV.svg", {}, -1 },
{"/AaronStatic/res/DiatonicCV.svg", {}, -1 },
{"/AaronStatic/res/RandomNoteCV.svg", {}, -1 },
{"/AaronStatic/res/ScaleCV.svg", {}, -1 },
// GPL3.0-or-later
{ "/Algoritmarte/res/Clockkky.svg", {}, -1 },
{ "/Algoritmarte/res/CyclicCA.svg", {}, -1 },
{ "/Algoritmarte/res/HoldMeTight.svg", {}, -1 },
{ "/Algoritmarte/res/MusiFrog.svg", {}, -1 },
{ "/Algoritmarte/res/MusiMath.svg", {}, -1 },
{ "/Algoritmarte/res/Planetz.svg", {}, -1 },
{ "/Algoritmarte/res/Zefiro.svg", {}, -1 },
// Custom, runtime dark mode used with permission
{ "/ArableInstruments/res/Joni.svg", {}, -1 },
// Custom, runtime dark mode used with permission
{ "/AudibleInstruments/res/Blinds.svg", {}, -1 },
{ "/AudibleInstruments/res/Braids.svg", {}, -1 },
@@ -125,6 +140,7 @@ static const struct {
{ "/Bidoo/res/VOID.svg", {}, -1 },
{ "/Bidoo/res/ZINC.svg", {}, -1 },
{ "/Bidoo/res/ZOUMAI.svg", {}, -1 },
{ "/Bidoo/res/ZOUMAIExpander.svg", {}, -1 },
// BSD-3-Clause
{ "/cf/res/ALGEBRA.svg", {}, -1 },
{ "/cf/res/BUFFER.svg", {}, -1 },
@@ -234,6 +250,8 @@ static const struct {
{ "/JW-Modules/res/Trigs.svg", {}, -1 },
{ "/JW-Modules/res/WavHeadPanel.svg", {}, -1 },
{ "/JW-Modules/res/XYPad.svg", {}, -1 },
// GPL3.0-or-later
{ "/LilacLoop/res/Looper.svg", {}, -1 },
// EUPL-1.2
{ "/LittleUtils/res/Bias_Semitone.svg", {}, -1 },
{ "/LittleUtils/res/ButtonModule.svg", {}, -1 },
@@ -241,16 +259,75 @@ static const struct {
{ "/LittleUtils/res/PulseGenerator.svg", {}, -1 },
{ "/LittleUtils/res/TeleportIn.svg", {}, -1 },
{ "/LittleUtils/res/TeleportOut.svg", {}, -1 },
// GPL-3.0-or-later
{ "/kocmoc/res/DDLY.svg", {}, -1 },
{ "/kocmoc/res/LADR.svg", {}, -1 },
{ "/kocmoc/res/MUL.svg", {}, -1 },
{ "/kocmoc/res/OP.svg", {}, -1 },
{ "/kocmoc/res/PHASR.svg", {}, -1 },
{ "/kocmoc/res/SKF.svg", {}, -1 },
{ "/kocmoc/res/SVF.svg", {}, -1 },
{ "/kocmoc/res/TRG.svg", {}, -1 },
// CC0-1.0
{ "/nonlinearcircuits/res/NLC - 4seq.svg", {}, -1 },
{ "/nonlinearcircuits/res/NLC - 8 BIT CIPHER.svg", {}, -1 },
{ "/nonlinearcircuits/res/BOOLs2.svg", {}, -1 },
{ "/nonlinearcircuits/res/NLC - DIVIDE & CONQUER.svg", {}, -1 },
{ "/nonlinearcircuits/res/NLC - DIVINE CMOS.svg", {}, -1 },
{ "/nonlinearcircuits/res/DoubleNeuronRef.svg", {}, -1 },
{ "/nonlinearcircuits/res/NLC - GENiE.svg", {}, -1 },
{ "/nonlinearcircuits/res/LetsSplosh.svg", {}, -1 },
{ "/nonlinearcircuits/res/NLC - NEURON.svg", {}, -1 },
{ "/nonlinearcircuits/res/NLC - NUMBERWANG.svg", {}, -1 },
{ "/nonlinearcircuits/res/NLC - SEGUE.svg", {}, -1 },
{ "/nonlinearcircuits/res/squid-axon-papernoise-panel2.svg", {}, -1 },
{ "/nonlinearcircuits/res/NLC - STATUES.svg", {}, -1 },
// Custom, runtime dark mode used with permission
{ "/ParableInstruments/res/Neil.svg", {}, -1 },
// GPL-3.0-or-later
{ "/PathSet/res/AstroVibe.svg", {}, -1 },
{ "/PathSet/res/IceTray.svg", {}, -1 },
{ "/PathSet/res/ShiftyMod.svg", {}, -1 },
// BSD-3-Clause
{ "/voxglitch/res/autobreak_front_panel.svg", {}, -1 },
{ "/voxglitch/res/bytebeat_front_panel.svg", {}, -1 },
{ "/voxglitch/res/digital_programmer_front_panel.svg", {}, -1 },
{ "/voxglitch/res/digital_sequencer_front_panel.svg", {}, -1 },
{ "/voxglitch/res/digital_sequencer_xp_front_panel.svg", {}, -1 },
{ "/voxglitch/res/ghosts_front_panel.svg", {}, -1 },
{ "/voxglitch/res/glitch_sequencer_front_panel.svg", {}, -1 },
{ "/voxglitch/res/goblins_front_panel.svg", {}, -1 },
{ "/voxglitch/res/grain_engine_mk2_expander_front_panel.svg", {}, -1 },
{ "/voxglitch/res/grain_engine_mk2_front_panel_r3.svg", {}, -1 },
{ "/voxglitch/res/grain_fx_front_panel.svg", {}, -1 },
{ "/voxglitch/res/hazumi_front_panel.svg", {}, -1 },
{ "/voxglitch/res/looper_front_panel.svg", {}, -1 },
{ "/voxglitch/res/repeater_front_panel.svg", {}, -1 },
{ "/voxglitch/res/samplerx8_front_panel.svg", {}, -1 },
{ "/voxglitch/res/satanonaut_front_panel.svg", {}, -1 },
{ "/voxglitch/res/wav_bank_front_panel.svg", {}, -1 },
{ "/voxglitch/res/wav_bank_mc_front_panel_v2.svg", {}, -1 },
{ "/voxglitch/res/xy_front_panel.svg", {}, -1 },
};

static inline bool invertPaint(NSVGpaint& paint, const char* const svgFileToInvert = nullptr)
static inline bool invertPaint(NSVGshape* const shape, NSVGpaint& paint, const char* const svgFileToInvert = nullptr)
{
// Special case for DrumKit background grandient
if (paint.type == NSVG_PAINT_LINEAR_GRADIENT && svgFileToInvert != nullptr && std::strncmp(svgFileToInvert, "/DrumKit/", 9) == 0)
if (paint.type == NSVG_PAINT_LINEAR_GRADIENT && svgFileToInvert != nullptr)
{
paint.type = NSVG_PAINT_COLOR;
paint.color = 0xff191919;
return true;
// Special case for DrumKit background gradient
if (std::strncmp(svgFileToInvert, "/DrumKit/", 9) == 0)
{
paint.type = NSVG_PAINT_COLOR;
paint.color = 0xff191919;
return true;
}
// Special case for PathSet shifty gradient
if (std::strncmp(svgFileToInvert, "/PathSet/", 9) == 0)
{
paint.gradient->stops[0].color = 0xff7c4919; // 50% darker than main blue
paint.gradient->stops[1].color = 0xff5b3a1a; // 33.3% darker than main blue
return false;
}
}

if (paint.type == NSVG_PAINT_NONE)
@@ -259,10 +336,25 @@ static inline bool invertPaint(NSVGpaint& paint, const char* const svgFileToInve
return false;

// Special case for Bidoo red color
if (paint.color == 0xff001fcd && svgFileToInvert != nullptr && std::strncmp(svgFileToInvert, "/Bidoo/", 7) == 0)
if (svgFileToInvert != nullptr && std::strncmp(svgFileToInvert, "/Bidoo/", 7) == 0)
{
paint.color = 0xcf8b94c4;
return true;
if (paint.color == 0xff001fcd)
{
paint.color = 0xcf8b94c4;
return true;
}
if (paint.color == 0xff000000 && shape->stroke.type == NSVG_PAINT_COLOR)
{
switch (shape->stroke.color)
{
case 0xff777777:
case 0xff7c7c7c:
case 0xff828282:
case 0xffb1b1b1:
case 0xffb2b2b2:
return false;
}
}
}

// Special case for JW-Modules colors
@@ -303,6 +395,127 @@ static inline bool invertPaint(NSVGpaint& paint, const char* const svgFileToInve
}
}

// Special case for Lilac
if (svgFileToInvert != nullptr && std::strncmp(svgFileToInvert, "/LilacLoop/", 11) == 0)
{
switch (paint.color)
{
// main bg (custom)
case 0xffd5d5da:
paint.color = 0xff242228;
return true;
// main color (do nothing)
case 0xffbfb7d7:
return false;
// screws (hide)
case 0xffc8c8cf:
case 0xffbcbcbc:
case 0xffb1b1bb:
case 0xffacacac:
case 0xff898991:
case 0xff727272:
paint.color = 0x00000000;
return true;
}
}

// Special case for Nonlinear Circuits
if (svgFileToInvert != nullptr && std::strncmp(svgFileToInvert, "/nonlinearcircuits/", 19) == 0)
{
switch (paint.color)
{
case 0xff9a7900:
case 0xff96782c:
case 0xff6a07ae:
case 0xffcf8044:
case 0xff2ac6ba:
case 0xff5ba85c:
case 0xffa97b00:
case 0xff9f7a00:
case 0xffff7300:
case 0xffa47b00:
case 0xffb09423:
return false;
case 0xffffffff:
paint.color = 0x00000000;
return true;
}
}

// Special case for PathSet colors
if (svgFileToInvert != nullptr && std::strncmp(svgFileToInvert, "/PathSet/", 9) == 0)
{
switch (paint.color)
{
// main blue tone
case 0xffdf7a1a:
if (shape->opacity == 0.5f && std::strcmp(svgFileToInvert, "/PathSet/res/AstroVibe.svg") == 0)
{
shape->opacity = 0.2f;
return true;
}
if (shape->opacity == 0.25f)
shape->opacity = 0.75f;
return false;
// bottom logo stuff, set to full white
case 0xff000000:
if (shape->stroke.type != NSVG_PAINT_NONE)
{
paint.color = 0xffffffff;
return true;
}
break;
// pink step 2 (pink with 50% opacity on bg)
case 0xffef73ea:
paint.color = 0xff812d7d;
return true;
// pink step 3 (pink with 33.3% opacity on bg)
case 0xfff49ff0:
paint.color = 0xff4d234c;
return true;
// pink and orange
case 0xffe941e2:
case 0xff698efb:
return false;
// blue darker 1 (blue with 50% opacity on bg)
case 0xffde944f:
case 0xffe3b080:
case 0xffe4cbb3:
case 0xfff5c99f:
case 0xfff6d1b0:
paint.color = 0xff7c4919;
return true;
// blue darker 2 (blue with 33.3% opacity on bg)
case 0xffe5d9cd:
case 0xfff8dcc2:
case 0xffe1a265:
paint.color = 0xff5b3a1a;
return true;
// blue darker 3 (blue with 25% opacity on bg)
case 0xffe5cbb3:
paint.color = 0xff4b321a;
return true;
}
}

// Special case for voxglitch colors
if (svgFileToInvert != nullptr && std::strncmp(svgFileToInvert, "/voxglitch/", 11) == 0)
{
switch (paint.color)
{
// wavbank blue
case 0xffc5ae8a:
// various black
case 0xff121212:
case 0xff2a2828:
return false;
// satanonaut
case 0xff595959:
paint.color = 0x7f3219ac;
return true;
}
}

switch (paint.color)
{
// scopes or other special things (do nothing)
@@ -329,6 +542,7 @@ static inline bool invertPaint(NSVGpaint& paint, const char* const svgFileToInve
case 0xff0095fe:
case 0xff4d9a4d:
case 0xff4d4d9a:
case 0xff0187fc:
return false;
// pure black (convert to not quite pure white)
case 0xff000000:
@@ -384,8 +598,8 @@ NSVGimage* nsvgParseFromFileCardinal(const char* const filename, const char* con
if (ignore)
continue;

if (invertPaint(shape->fill, svgFileToInvert))
invertPaint(shape->stroke, svgFileToInvert);
if (invertPaint(shape, shape->fill, svgFileToInvert))
invertPaint(shape, shape->stroke, svgFileToInvert);
}

return handle;


+ 3
- 3
src/override/MenuBar.cpp View File

@@ -479,7 +479,8 @@ struct ViewButton : MenuButton {

menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules));

#ifndef DISTRHO_OS_MAC
menu->addChild(createBoolPtrMenuItem("Invert zoom", "", &settings::invertZoom));

menu->addChild(new ui::MenuSeparator);

static const std::vector<std::string> rateLimitLabels = {
@@ -496,7 +497,6 @@ struct ViewButton : MenuButton {
));
}
}));
#endif
}
};

@@ -534,7 +534,7 @@ struct HelpButton : MenuButton {
menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));

menu->addChild(createMenuItem("Rack User manual", "F1", [=]() {
system::openBrowser("https://vcvrack.com/manual/");
system::openBrowser("https://vcvrack.com/manual");
}));

menu->addChild(createMenuItem("Cardinal Project page", "", [=]() {


+ 16
- 3
src/override/Model.cpp View File

@@ -117,7 +117,7 @@ std::string Model::getManualUrl() {
}


void Model::appendContextMenu(ui::Menu* menu, bool) {
void Model::appendContextMenu(ui::Menu* menu, bool inBrowser) {
// plugin
menu->addChild(createMenuItem("Plugin: " + plugin->name, "", [=]() {
system::openBrowser(plugin->pluginUrl);
@@ -182,15 +182,28 @@ void Model::appendContextMenu(ui::Menu* menu, bool) {
system::openBrowser(plugin->changelogUrl);
}));
}

// Favorite
std::string favoriteRightText = inBrowser ? (RACK_MOD_CTRL_NAME "+click") : "";
if (isFavorite())
favoriteRightText += " " CHECKMARK_STRING;
menu->addChild(createMenuItem("Favorite", favoriteRightText,
[=]() {
setFavorite(!isFavorite());
}
));
}


bool Model::isFavorite() {
return false;
const settings::ModuleInfo* mi = settings::getModuleInfo(plugin->slug, slug);
return mi && mi->favorite;
}


void Model::setFavorite(bool) {
void Model::setFavorite(bool favorite) {
settings::ModuleInfo& mi = settings::moduleInfos[plugin->slug][slug];
mi.favorite = favorite;
}




+ 10
- 2
src/override/Scene.cpp View File

@@ -46,6 +46,10 @@
# undef DEBUG
#endif

#ifdef STATIC_BUILD
# undef HAVE_LIBLO
#endif

#ifdef HAVE_LIBLO
# include <lo/lo.h>
#endif
@@ -123,7 +127,7 @@ struct ResizeHandle : widget::OpaqueWidget {


struct Scene::Internal {
ResizeHandle* resizeHandle;
ResizeHandle* resizeHandle = nullptr;

bool heldArrowKeys[4] = {};

@@ -169,6 +173,9 @@ Scene::Scene() {
browser->hide();
addChild(browser);

if (isStandalone())
return;

internal->resizeHandle = new ResizeHandle;
internal->resizeHandle->box.size = math::Vec(16, 16);
addChild(internal->resizeHandle);
@@ -196,7 +203,8 @@ void Scene::step() {
rackScroll->box.pos.y = menuBar->box.size.y;
}

internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size);
if (internal->resizeHandle != nullptr)
internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size);

// Resize owned descendants
menuBar->box.size.x = box.size.x;


+ 24
- 0
src/override/Window.cpp View File

@@ -718,6 +718,13 @@ void WindowParametersSave(rack::window::Window* const window)
window->internal->callback->WindowParametersChanged(kWindowParameterWheelSensitivity,
rack::settings::knobScrollSensitivity);
}
if (d_isNotEqual(window->internal->params.browserZoom, rack::settings::browserZoom))
{
window->internal->params.browserZoom = rack::settings::browserZoom;
if (window->internal->callback != nullptr)
window->internal->callback->WindowParametersChanged(kWindowParameterBrowserZoom,
rack::settings::browserZoom);
}
if (window->internal->params.knobMode != rack::settings::knobMode)
{
window->internal->params.knobMode = rack::settings::knobMode;
@@ -725,6 +732,13 @@ void WindowParametersSave(rack::window::Window* const window)
window->internal->callback->WindowParametersChanged(kWindowParameterKnobMode,
rack::settings::knobMode);
}
if (window->internal->params.browserSort != rack::settings::browserSort)
{
window->internal->params.browserSort = rack::settings::browserSort;
if (window->internal->callback != nullptr)
window->internal->callback->WindowParametersChanged(kWindowParameterBrowserSort,
rack::settings::browserSort);
}
if (window->internal->params.tooltips != rack::settings::tooltips)
{
window->internal->params.tooltips = rack::settings::tooltips;
@@ -746,6 +760,13 @@ void WindowParametersSave(rack::window::Window* const window)
window->internal->callback->WindowParametersChanged(kWindowParameterLockModulePositions,
rack::settings::lockModules);
}
if (window->internal->params.invertZoom != rack::settings::invertZoom)
{
window->internal->params.invertZoom = rack::settings::invertZoom;
if (window->internal->callback != nullptr)
window->internal->callback->WindowParametersChanged(kWindowParameterInvertZoom,
rack::settings::invertZoom);
}
if (window->internal->params.rateLimit != rack::settings::rateLimit)
{
window->internal->params.rateLimit = rack::settings::rateLimit;
@@ -762,10 +783,13 @@ void WindowParametersRestore(rack::window::Window* const window)
rack::settings::rackBrightness = window->internal->params.rackBrightness;
rack::settings::haloBrightness = window->internal->params.haloBrightness;
rack::settings::knobScrollSensitivity = window->internal->params.knobScrollSensitivity;
rack::settings::browserZoom = window->internal->params.browserZoom;
rack::settings::knobMode = static_cast<rack::settings::KnobMode>(window->internal->params.knobMode);
rack::settings::browserSort = static_cast<rack::settings::BrowserSort>(window->internal->params.browserSort);
rack::settings::tooltips = window->internal->params.tooltips;
rack::settings::knobScroll = window->internal->params.knobScroll;
rack::settings::lockModules = window->internal->params.lockModules;
rack::settings::invertZoom = window->internal->params.invertZoom;
rack::settings::rateLimit = window->internal->params.rateLimit;
}



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

Loading…
Cancel
Save