| @@ -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 | |||
| @@ -15,3 +15,5 @@ | |||
| bin/ | |||
| build/ | |||
| documentation.pdf | |||
| utils/inno/resources.iss | |||
| utils/inno/version.iss | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
|  | |||
| ## 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 @@ | |||
| Subproject commit 4b6010bd0adbbed5a0cb89a1253e52e72e648b18 | |||
| Subproject commit 04558b63101de55556733edfa4a369b51f36e9b3 | |||
| @@ -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 @@ | |||
| Subproject commit b41a693b64cdba1abd8d278c9985fb690b522854 | |||
| Subproject commit 01d07086586818e427b2898d2d446d30b68f3139 | |||
| @@ -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 | |||
| @@ -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? | |||
| @@ -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 @@ | |||
| Subproject commit af042cb682d693e9ce5b87a145e6c704be31adf8 | |||
| Subproject commit 68de732eecbd1d8febf94e15558c5adaa45dfa9b | |||
| @@ -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, | |||
| @@ -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 | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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. */ | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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) | |||
| @@ -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" | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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" | |||
| } | |||
| ] | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 4ace0a1789c577ee4eb12dc03da5271f80598d62 | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 9d41fe882ab5029100b55c98ba7f10172d452795 | |||
| @@ -1 +1 @@ | |||
| Subproject commit 35b89c93152ac2194eecffbd4aa39e71caa90cc0 | |||
| Subproject commit 21a031870db2068a98d9690eaffc24bbbfc99c2e | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 890448f087e3ab47eac391f9bcfe03f7bbd2123e | |||
| @@ -1 +1 @@ | |||
| Subproject commit 0a6c390eedf98884393f82cb066038a78a316ea5 | |||
| Subproject commit 9d35b745af8569d6a9d6bc5c3f2c3e64c852d8e0 | |||
| @@ -1 +1 @@ | |||
| Subproject commit ec406ce181f340bce8e475cb508c4db0db02fdc6 | |||
| Subproject commit 7e0b020000225e3d2aecba3f09054595339fe540 | |||
| @@ -1 +1 @@ | |||
| Subproject commit e55fcd2e1d7c0fef69d4919baac6f791172c89ca | |||
| Subproject commit f771bf270393b8587516329614ba76e2894355b7 | |||
| @@ -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 @@ | |||
| Subproject commit 8e982f462c4117f84794cbf6a13740992ff17d92 | |||
| Subproject commit a86e7d7b18e0c7cb6e857df36263b0e85bf85566 | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| @@ -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"); | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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 | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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); | |||
| @@ -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); | |||
| @@ -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; | |||
| @@ -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 | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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"); | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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"); | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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)) | |||
| { | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit bf6f969c5f7fff6a419a54197fb4318671281ad5 | |||
| @@ -1 +1 @@ | |||
| Subproject commit 80f61cd0171bb7d988c9ec3a144e0566d62c767c | |||
| Subproject commit 52f89b94a25828f9debf8bca4d58854fb1e70228 | |||
| @@ -1 +1 @@ | |||
| Subproject commit df096a635fe8d3ea86a1e6451a55e45ccee5b83d | |||
| Subproject commit 95496e8a955407889bbab94cf404cf356802bb76 | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 48bf3c84ebafc9effe7e565d8cdbf8a46b9d503c | |||
| @@ -1 +1 @@ | |||
| Subproject commit 54fed7f78bbaac1f1d6275aa737acc39aebc6e72 | |||
| Subproject commit 2c535bc38d61fd4d776aad7307c1dfbbed062b66 | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 636351059f2eec629f3b8a537451dd3d0eb01c30 | |||
| @@ -1 +1 @@ | |||
| Subproject commit 40a197698496030a30ead69658b1484eca2ae299 | |||
| Subproject commit b0d8a6fcdb28d2e56d6b024326a7378a2c8ee45d | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit e5cf81f1c356fdc98fd08584146cda8af7e16b1f | |||
| @@ -1 +1 @@ | |||
| Subproject commit 368cbd6ee17398c7329b263dde0409bf7a57ce3b | |||
| Subproject commit 2bd5691c8f12e21e1e6db33e42fe2fe3db7726a3 | |||
| @@ -1 +1 @@ | |||
| Subproject commit 68f04a5a8a1a656e7a3aa7e217524f0feb91167e | |||
| Subproject commit f7399c473735c0a5bec95bb40953e781f9a47ca4 | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 69d8d66521175cd561d53e65728d460b398950c9 | |||
| @@ -1 +1 @@ | |||
| Subproject commit 1c32b02bd11a549d28da0620719541ac6f966652 | |||
| Subproject commit b21cbe8ee25ddf2a927e0b4ec9f2c97c115857af | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 788ceb73cfabddaff7245b0df072b0e22a19b360 | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit abe3c24d40b11d31f9f38b2125eff9280c77ad1b | |||
| @@ -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 @@ | |||
| Subproject commit 621a29f3f1b521582fd11ca7f17635d19faa340c | |||
| Subproject commit a721e381fa1d72d738c9c2daae08c740107e3d5e | |||
| @@ -1 +1 @@ | |||
| Subproject commit 01c4fac9f2e91f60125d36767224f457b2057fb7 | |||
| Subproject commit 00a7e3b01f56da5cfc86720ae6951ecdf8953ee5 | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit ff0c007feb9ed1de57ea246f86e8b2b68572f5e6 | |||
| @@ -0,0 +1 @@ | |||
| ../ArableInstruments/parasites | |||
| @@ -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" | |||
| ] | |||
| } | |||
| ] | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| ../../ArableInstruments/res/CKSS_rot_0.svg | |||
| @@ -0,0 +1 @@ | |||
| ../../ArableInstruments/res/CKSS_rot_1.svg | |||
| @@ -0,0 +1 @@ | |||
| ../../ArableInstruments/res/Neil.png | |||
| @@ -0,0 +1 @@ | |||
| ../../ArableInstruments/res/Neil.svg | |||
| @@ -0,0 +1 @@ | |||
| ../../ArableInstruments/src/ArableInstruments.hpp | |||
| @@ -0,0 +1 @@ | |||
| ../../ArableInstruments/src/Clouds.cpp | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit d03e8b486deba4740bc7f15ae95a08d823a7d00a | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit ea6ab0c6887102ebbf6e3534e0e891b867b130cc | |||
| @@ -1 +1 @@ | |||
| Subproject commit 453da225742f3829ba037770245333a28751fbb8 | |||
| Subproject commit 8a9cc034d905079f156ed6c64efaeb4baf81490f | |||
| @@ -1 +1 @@ | |||
| Subproject commit c05209a6ad74e8b99703a033842797f06515f865 | |||
| Subproject commit 98698dc28e6ed7aec56e4ab8280171a160d673ef | |||
| @@ -1 +1 @@ | |||
| Subproject commit 31c4229eb328f6aaa4024f76c595b55213cdf1cf | |||
| Subproject commit 1b77e3c3ba12734bbd29a4aa59dd408e679b5cf7 | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit f2a8c19f8aa81769e13d085d69a44de5afaacfaa | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 57eb090f233c21b2edee541ea17d800f22045d91 | |||
| @@ -1 +1 @@ | |||
| Subproject commit 185e07ea94086a04b3daacb4bf94c0fbd3544725 | |||
| Subproject commit f812cc56b7fe9e41bb13da99ef23f014015c86c1 | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit ed5c85b0d9391c37f4ec4d9de4ef8aa30d94bcd6 | |||
| @@ -1 +0,0 @@ | |||
| Subproject commit cbc09d4db02d038493689c192d63b746d453d18f | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 3f895c7663e3e54c4e30c406c56d420ea407133e | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 03f4fc5cebb5d8eb152ac59d3a51ce0ec87a2740 | |||
| @@ -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); | |||
| } | |||
| @@ -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 { | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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 $<) $@ | |||
| @@ -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 @@ | |||
| Subproject commit 0d003b96476af45102117c2bb958aeb59eb523cf | |||
| Subproject commit 30665d62801c2ced7260a37a2d0214edfe6528a9 | |||
| @@ -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; | |||
| }; | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| @@ -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", "", [=]() { | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| @@ -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; | |||
| } | |||