├── .clang-format
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── labeler.yml
└── workflows
│ ├── auto-format.yml
│ ├── cmake.yml
│ ├── ios.yml
│ ├── labeler.yml
│ └── manual.yml
├── .gitignore
├── .gitmodules
├── CHANGELOG.md
├── CMakeLists.txt
├── LICENSE
├── README.md
├── installers
├── mac
│ ├── ChowMatrix.pkgproj
│ ├── Intro.txt
│ └── build_mac_installer.sh
└── windows
│ ├── ChowMatrix_Install_Script.iss
│ ├── build_win_installer.sh
│ └── chowmatrix.ico
├── manual
├── .gitignore
├── ChowMatrixManual.pdf
├── ChowMatrixManual.tex
├── Makefile
├── manual.cls
└── screenshots
│ ├── BottomBar.png
│ ├── DetailsView.png
│ ├── GraphView.png
│ └── full_gui.png
├── modules
├── CMakeLists.txt
├── cmake
│ ├── SubprojectVersion.cmake
│ ├── WarningFlags.cmake
│ └── pluginval.cmake
└── polylogarithm
│ ├── CMakeLists.txt
│ ├── Li2.cpp
│ └── Li2.hpp
├── res
├── CMakeLists.txt
├── Diceroll.png
├── LeftArrow.svg
├── Panic.png
├── RightArrow.svg
├── cog-solid.svg
├── ellipsis-h-solid.png
├── gui.xml
├── gui_ios.xml
├── home.svg
├── logo.png
├── logo.svg
├── presets
│ ├── Chord.chowpreset
│ ├── Crazy.chowpreset
│ ├── Default.chowpreset
│ ├── Lush.chowpreset
│ ├── Rhythmic.chowpreset
│ ├── Wobble.chowpreset
│ └── Wonky.chowpreset
└── undo-solid.png
├── scripts
├── aax_builds.sh
├── ios_build.sh
├── mac_builds.sh
└── win_builds.sh
└── src
├── CMakeLists.txt
├── ChowMatrix.cpp
├── ChowMatrix.h
├── NodeManager.cpp
├── NodeManager.h
├── dsp
├── BaseNode.cpp
├── BaseNode.h
├── Delay
│ ├── DelayProc.cpp
│ ├── DelayProc.h
│ ├── DelayStore.h
│ ├── Diffusion.cpp
│ ├── Diffusion.h
│ ├── PitchShiftWrapper.cpp
│ ├── PitchShiftWrapper.h
│ ├── Reverser.cpp
│ ├── Reverser.h
│ ├── TempoSyncUtils.h
│ ├── VariableDelay.cpp
│ └── VariableDelay.h
├── DelayNode.cpp
├── DelayNode.h
├── Distortion
│ ├── Distortion.cpp
│ ├── Distortion.h
│ ├── LookupTables.cpp
│ └── LookupTables.h
├── InputNode.cpp
├── InputNode.h
├── Parameters
│ ├── BaseController.cpp
│ ├── BaseController.h
│ ├── DelayTypeControl.cpp
│ ├── DelayTypeControl.h
│ ├── HostParamControl.cpp
│ ├── HostParamControl.h
│ ├── InsanityControl.cpp
│ ├── InsanityControl.h
│ ├── InsanityLockHelper.cpp
│ ├── InsanityLockHelper.h
│ ├── NodeParamControl.cpp
│ ├── NodeParamControl.h
│ ├── ParamHelpers.cpp
│ ├── ParamHelpers.h
│ ├── RandomiseLockHelper.cpp
│ ├── RandomiseLockHelper.h
│ ├── SyncControl.cpp
│ └── SyncControl.h
├── ProcessorBase.h
└── ProcessorChain.h
├── gui
├── AutoUpdating.cpp
├── AutoUpdating.h
├── BottomBar
│ ├── ABComp.cpp
│ ├── ABComp.h
│ ├── BottomBarLNF.cpp
│ ├── BottomBarLNF.h
│ ├── HostControlMenu.cpp
│ ├── HostControlMenu.h
│ ├── HostControlMenuComp.cpp
│ ├── HostControlMenuComp.h
│ ├── TextSlider.h
│ ├── TextSliderItem.h
│ └── WetGainSlider.h
├── DetailsView
│ ├── NodeDetails.cpp
│ ├── NodeDetails.h
│ ├── NodeDetailsComponent.cpp
│ ├── NodeDetailsComponent.h
│ ├── NodeDetailsGUI.cpp
│ ├── NodeDetailsGUI.h
│ ├── NodeDetailsViewport.cpp
│ └── NodeDetailsViewport.h
├── IOSUtils
│ ├── LongPressActionHelper.cpp
│ ├── LongPressActionHelper.h
│ ├── PopupMenuOptionsHelpers.h
│ ├── TipJar.cpp
│ ├── TipJar.h
│ └── TwoFingerDragToScrollListener.h
├── LookAndFeel
│ ├── InsanityLNF.h
│ └── PresetsLNF.h
├── MatrixView
│ ├── DelayNodeComponent.cpp
│ ├── DelayNodeComponent.h
│ ├── GraphView.cpp
│ ├── GraphView.h
│ ├── GraphViewItem.h
│ ├── GraphViewport.cpp
│ ├── GraphViewport.h
│ ├── InputNodeComponent.h
│ ├── MatrixAurora.cpp
│ ├── MatrixAurora.h
│ ├── NodeCompManager.cpp
│ ├── NodeCompManager.h
│ ├── NodeComponent.cpp
│ └── NodeComponent.h
├── NodeInfo.h
├── ParamSlider.cpp
└── ParamSlider.h
├── headless
├── CMakeLists.txt
├── PresetResaver.cpp
├── PresetResaver.h
├── ScreenshotGenerator.cpp
├── ScreenshotGenerator.h
├── UnitTests
│ ├── DelayClearTest.cpp
│ ├── HostControlTest.cpp
│ ├── PerformanceTest.cpp
│ ├── SaveLoadSpeedTest.cpp
│ ├── UnitTests.cpp
│ └── UnitTests.h
└── main.cpp
├── pch.h
└── state
├── PresetManager.cpp
├── PresetManager.h
├── StateManager.cpp
└── StateManager.h
/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | AccessModifierOffset: -4
3 | AlignAfterOpenBracket: Align
4 | AlignConsecutiveAssignments: false
5 | AlignConsecutiveDeclarations: false
6 | AlignEscapedNewlines: Left
7 | AlignOperands: Align
8 | AlignTrailingComments: false
9 | AllowAllParametersOfDeclarationOnNextLine: false
10 | AllowShortBlocksOnASingleLine: Never
11 | AllowShortCaseLabelsOnASingleLine: false
12 | AllowShortFunctionsOnASingleLine: All
13 | AllowShortIfStatementsOnASingleLine: Never
14 | AllowShortLoopsOnASingleLine: false
15 | AlwaysBreakAfterDefinitionReturnType: None
16 | AlwaysBreakAfterReturnType: None
17 | AlwaysBreakBeforeMultilineStrings: false
18 | AlwaysBreakTemplateDeclarations: Yes
19 | BinPackArguments: false
20 | BinPackParameters: false
21 | BreakAfterJavaFieldAnnotations: false
22 | BreakBeforeBinaryOperators: NonAssignment
23 | BreakBeforeBraces: Allman
24 | BreakBeforeTernaryOperators: true
25 | BreakConstructorInitializersBeforeComma: false
26 | BreakStringLiterals: false
27 | ColumnLimit: 0
28 | ConstructorInitializerAllOnOneLineOrOnePerLine: true
29 | ConstructorInitializerIndentWidth: 4
30 | ContinuationIndentWidth: 4
31 | Cpp11BracedListStyle: false
32 | DerivePointerAlignment: false
33 | DisableFormat: false
34 | ExperimentalAutoDetectBinPacking: false
35 | ForEachMacros: ['forEachXmlChildElement']
36 | IndentCaseLabels: true
37 | IndentWidth: 4
38 | IndentWrappedFunctionNames: true
39 | KeepEmptyLinesAtTheStartOfBlocks: false
40 | Language: Cpp
41 | MaxEmptyLinesToKeep: 1
42 | NamespaceIndentation: Inner
43 | PointerAlignment: Left
44 | ReflowComments: false
45 | SortIncludes: true
46 | SpaceAfterCStyleCast: true
47 | SpaceAfterLogicalNot: true
48 | SpaceBeforeAssignmentOperators: true
49 | SpaceBeforeCpp11BracedList: true
50 | SpaceBeforeParens: NonEmptyParentheses
51 | SpaceInEmptyParentheses: false
52 | SpaceBeforeInheritanceColon: true
53 | SpacesInAngles: false
54 | SpacesInCStyleCastParentheses: false
55 | SpacesInContainerLiterals: true
56 | SpacesInParentheses: false
57 | SpacesInSquareBrackets: false
58 | Standard: "c++17"
59 | TabWidth: 4
60 | UseTab: Never
61 | ---
62 | Language: ObjC
63 | BasedOnStyle: Chromium
64 | AlignTrailingComments: true
65 | BreakBeforeBraces: Allman
66 | ColumnLimit: 0
67 | IndentWidth: 4
68 | KeepEmptyLinesAtTheStartOfBlocks: false
69 | ObjCSpaceAfterProperty: true
70 | ObjCSpaceBeforeProtocolList: true
71 | PointerAlignment: Left
72 | SpacesBeforeTrailingComments: 1
73 | TabWidth: 4
74 | UseTab: Never
75 | ...
76 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # repo: Chowdhury-DSP/ChowMatrix
2 | # filename: FUNDING.YML
3 |
4 | patreon: chowdsp
5 | custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=93S67ZSKMBG68&source=url
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a bug report
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: jatinchowdhury18
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. Windows 10, MacOS]
28 | - DAW [e.g. Ableton Live, FL Studio, Audacity]
29 | - Version [e.g. 2.3]
30 | - 32/64 bit (Windows only)
31 |
32 | **Additional context**
33 | Add any other context about the problem here.
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEATURE]"
5 | labels: enhancement
6 | assignees: jatinchowdhury18
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Additional context**
17 | Add any other context or screenshots about the feature request here.
18 |
--------------------------------------------------------------------------------
/.github/labeler.yml:
--------------------------------------------------------------------------------
1 | # labeler.yml:
2 | # Assign labels to pull requests that change various files
3 | # For more information, see https://github.com/actions/labeler
4 |
5 | dsp:
6 | - src/dsp/**/*
7 |
8 | gui:
9 | - src/gui/**/*
10 |
11 | presets:
12 | - src/state/presets/*
13 | - res/presets/*
14 |
15 | documentation:
16 | - manual/**/*
17 | - README.md
18 |
19 | ci-pipeline:
20 | - .github/workflows/*
21 |
22 | installers:
23 | - installers/**/*
24 |
--------------------------------------------------------------------------------
/.github/workflows/auto-format.yml:
--------------------------------------------------------------------------------
1 | name: Auto-formatter
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | - develop
8 | paths:
9 | - '**.cpp'
10 | - '**.h'
11 |
12 | workflow_dispatch:
13 |
14 | jobs:
15 | build_and_test:
16 | if: contains(toJson(github.event.commits), '***NO_CI***') == false && contains(toJson(github.event.commits), '[ci skip]') == false && contains(toJson(github.event.commits), '[skip ci]') == false
17 | name: Apply clang-format to pull request
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - name: Install Linux Deps
22 | run: |
23 | wget https://apt.llvm.org/llvm.sh
24 | chmod +x llvm.sh
25 | sudo ./llvm.sh 11
26 | sudo apt-get install clang-format-11
27 | clang-format-11 --version
28 |
29 | - name: Checkout code
30 | uses: actions/checkout@v2
31 | with:
32 | persist-credentials: false
33 | fetch-depth: 0
34 |
35 | - name: Run clang-format
36 | shell: bash
37 | run: find src/ -iname *.h -o -iname *.cpp | xargs clang-format-11 -style=file -verbose -i
38 |
39 | - name: Commit & Push changes
40 | uses: actions-js/push@master
41 | with:
42 | message: "Apply clang-format"
43 | branch: ${{ github.head_ref }}
44 | github_token: ${{ secrets.GITHUB_TOKEN }}
45 |
--------------------------------------------------------------------------------
/.github/workflows/cmake.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - develop
8 | paths-ignore:
9 | - 'bin/**'
10 | - 'manual/**'
11 | - '*.sh'
12 | - '*.md'
13 | pull_request:
14 | branches:
15 | - main
16 | - develop
17 | paths-ignore:
18 | - 'bin/**'
19 | - 'manual/**'
20 | - '*.sh'
21 | - '*.md'
22 |
23 | workflow_dispatch:
24 |
25 | jobs:
26 | build_and_test:
27 | if: contains(toJson(github.event.commits), '***NO_CI***') == false && contains(toJson(github.event.commits), '[ci skip]') == false && contains(toJson(github.event.commits), '[skip ci]') == false
28 | name: Test ChowMatrix on ${{ matrix.os }}
29 | runs-on: ${{ matrix.os }}
30 | strategy:
31 | fail-fast: false # show all errors for each platform (vs. cancel jobs on error)
32 | matrix:
33 | include:
34 | - os: ubuntu-latest
35 | cmake_args: "-DBUILD_HEADLESS=ON"
36 | build_args: ""
37 | - os: windows-latest
38 | cmake_args: "-DRUN_PLUGINVAL=ON"
39 | build_args: "--target pluginval-all"
40 | - os: macos-latest
41 | cmake_args: "-DRUN_PLUGINVAL=ON"
42 | build_args: "--target pluginval-all"
43 |
44 | steps:
45 | - name: Install Linux Deps
46 | if: runner.os == 'Linux'
47 | run: |
48 | sudo apt-get update
49 | sudo apt install libasound2-dev libcurl4-openssl-dev libx11-dev libxinerama-dev libxext-dev libfreetype6-dev libwebkit2gtk-4.0-dev libglu1-mesa-dev libjack-jackd2-dev lv2-dev
50 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 9
51 | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 9
52 |
53 | - name: Get latest CMake
54 | uses: lukka/get-cmake@latest
55 |
56 | - name: Checkout code
57 | uses: actions/checkout@v2
58 | with:
59 | submodules: true
60 |
61 | - name: Configure
62 | env:
63 | CMAKE_ARGS: ${{ matrix.cmake_args }}
64 | shell: bash
65 | run: cmake -Bbuild $CMAKE_ARGS
66 |
67 | - name: Build
68 | env:
69 | BUILD_ARGS: ${{ matrix.build_args }}
70 | shell: bash
71 | run: cmake --build build --parallel 4 $BUILD_ARGS
72 |
73 | # - name: Setup debug session
74 | # uses: mxschmitt/action-tmate@v3
75 |
76 | - name: Unit Tests
77 | if: runner.os == 'Linux'
78 | run: build/ChowMatrix --unit-tests --all
79 |
80 | # - name: Pluginval
81 | # if: runner.os == 'Windows' || runner.os == 'MacOS'
82 | # run: bash scripts/validate.sh
83 |
--------------------------------------------------------------------------------
/.github/workflows/ios.yml:
--------------------------------------------------------------------------------
1 | name: CI-iOS
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - develop
8 | pull_request:
9 | branches:
10 | - main
11 | - develop
12 | workflow_dispatch:
13 |
14 | jobs:
15 | build_and_test:
16 | if: contains(toJson(github.event.commits), '***NO_CI***') == false && contains(toJson(github.event.commits), '[ci skip]') == false && contains(toJson(github.event.commits), '[skip ci]') == false
17 | name: Build AUv3 plugin for iOS
18 | runs-on: macos-latest
19 |
20 | steps:
21 | - name: Get latest CMake
22 | uses: lukka/get-cmake@latest
23 |
24 | - name: Checkout code
25 | uses: actions/checkout@v2
26 | with:
27 | submodules: recursive
28 |
29 | - uses: Apple-Actions/import-codesign-certs@v1
30 | with:
31 | p12-file-base64: ${{ secrets.IOS_CERTIFICATES_BASE64 }}
32 | p12-password: ${{ secrets.IOS_CERTIFICATES_PASS }}
33 |
34 | - uses: Apple-Actions/download-provisioning-profiles@v1
35 | with:
36 | bundle-id: com.chowdsp.ChowMatrix*
37 | issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
38 | api-key-id: ${{ secrets.APPSTORE_KEY_ID }}
39 | api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }}
40 |
41 | - name: Configure
42 | shell: bash
43 | run: cmake -Bbuild-ios -GXcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=11.4 -DCMAKE_XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY="1,2" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE="Manual" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY="Apple Distribution" -DCMAKE_XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER="ChowMatrix" -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=${{ secrets.APPLE_TEAM_ID }}
44 |
45 | - name: Build
46 | shell: bash
47 | run: cmake --build build-ios --config Release --parallel 4 | xcpretty
48 |
--------------------------------------------------------------------------------
/.github/workflows/labeler.yml:
--------------------------------------------------------------------------------
1 | name: "Pull Request Labeler"
2 |
3 | on:
4 | - pull_request_target
5 |
6 | jobs:
7 | triage:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/labeler@main
11 | with:
12 | repo-token: "${{ secrets.GITHUB_TOKEN }}"
13 |
--------------------------------------------------------------------------------
/.github/workflows/manual.yml:
--------------------------------------------------------------------------------
1 | name: manual
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - 'manual/**'
9 |
10 | workflow_dispatch:
11 |
12 | jobs:
13 | build_and_test:
14 | name: Build and deploy manual
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Install Linux Deps
19 | run: sudo apt install texlive-xetex
20 |
21 | - name: Checkout code
22 | uses: actions/checkout@v2
23 | with:
24 | submodules: false
25 |
26 | - name: Build manual
27 | working-directory: ${{github.workspace}}/manual
28 | run: make
29 |
30 | - name: Deploy manual
31 | uses: appleboy/scp-action@master
32 | with:
33 | host: ccrma-gate.stanford.edu
34 | username: jatin
35 | password: ${{ secrets.CCRMA_PASS }}
36 | source: "manual/ChowMatrixManual.pdf"
37 | target: "~/Library/Web/chowdsp/Products"
38 | strip_components: 1
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # build artifacts
2 | build*/
3 | bin/
4 | *_Packaged
5 | *.exe
6 | *.dmg
7 |
8 | # IDE settings
9 | .vscode/
10 | .idea/
11 |
12 | # Mac extras
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "modules/foleys_gui_magic"]
2 | path = modules/foleys_gui_magic
3 | url = https://github.com/Chowdhury-DSP/foleys_gui_magic.git
4 | [submodule "modules/chowdsp_utils"]
5 | path = modules/chowdsp_utils
6 | url = https://github.com/Chowdhury-DSP/chowdsp_utils.git
7 | [submodule "modules/JUCE"]
8 | path = modules/JUCE
9 | url = https://github.com/lv2-porting-project/JUCE
10 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | ## [1.3.0] 2021-12-21
6 | - Added iOS/AUv3 support.
7 | - Added Pro Tools/AAX support.
8 | - Added forwarding parameters to expose node parameters to DAWs.
9 | - Added "Alien" delay mode.
10 | - Added "Reverse" delay parameter.
11 | - Added optional automatic gain compensation for wet signal.
12 | - Added lock/unlock options for parameter randomisation.
13 | - Fixed "hang" when bringing delay to 0 ms in "Analog" mode.
14 | - Improved performance for pan modulation.
15 | - Improved preset management system.
16 | - Added option to throttle graphics rendering for the Matrix View.
17 | - Improved memory usage.
18 |
19 | ## [1.2.0] 2021-04-23
20 | - Added new delay modes:
21 | - Liquid (Sinc interpolation)
22 | - Super Liquid (more ideal Sinc interpolation)
23 | - Lo-Fi (4096-stage BBD delay emulation)
24 | - Analog (16384-stage BBD delay emulation)
25 | - Added parameter modulation targets for host control.
26 | - Added insanity reset/insanity lock behaviour.
27 | - Support for Macs with Apple Silicon.
28 | - Created installer packages for Windows/Mac/Linux.
29 |
30 | ## [1.1.0] 2021-01-13
31 | - Added individual node modulation, for delay time and panning.
32 | - Added pitch-shifting and diffusion for each delay node.
33 | - Added LV2 compilation for Linux.
34 | - Made Graph View scrollable.
35 | - Added A/B buttons for switching plugin state.
36 | - Added individual node colouring.
37 | - Extended range for tempo-synced delay times.
38 | - Fixed node distortion bug in Logic/Garageband.
39 | - Fixed bug with incorrect delay times between sibling nodes.
40 | - Fixed preset menu not returning to currently selected preset.
41 |
42 | ## [1.0.0] 2020-11-25
43 | - Initial release.
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, jatinchowdhury18
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CHOW Matrix
2 |
3 | [](https://github.com/Chowdhury-DSP/ChowMatrix/releases/latest)
4 | 
5 | [](https://opensource.org/licenses/BSD-3-Clause)
6 | [](https://somsubhra.github.io/github-release-stats/?username=Chowdhury-DSP&repository=ChowMatrix&page=1&per_page=30)
7 |
8 | CHOW Matrix is a delay effect, made up of an infinitely growable
9 | tree of delay lines, each with individual controls for feedback,
10 | panning, distortion, and more. ChowMatrix is available as a VST,
11 | VST3, AU, LV2, AUv3, AAX, and Standalone Application.
12 |
13 |
14 | ### Quick Links
15 | - [Latest Release](https://chowdsp.com/products.html#matrix)
16 | - [ChowMatrix for iOS](https://apps.apple.com/us/app/chowmatrix/id1598726146)
17 | - [Nightly Builds](https://chowdsp.com/nightly.html#matrix)
18 | - [User Manual](https://ccrma.stanford.edu/~jatin/chowdsp/Products/ChowMatrixManual.pdf)
19 |
20 |
21 |
22 |
23 | ## Installing
24 |
25 | To download the latest release, visit the
26 | [official website](https://chowdsp.com/products.html#matrix).
27 | If you would like to try the most recent builds (potentially
28 | unstable) check out the
29 | [nightly builds](https://chowdsp.com/nightly.html#matrix).
30 |
31 |
32 | ## Building
33 |
34 | To build from source, you must have CMake installed.
35 |
36 | ```bash
37 | # Clone the repository
38 | $ git clone https://github.com/Chowdhury-DSP/ChowMatrix.git
39 | $ cd ChowMatrix
40 |
41 | # initialize and set up submodules
42 | $ git submodule update --init --recursive
43 |
44 | # build with CMake
45 | $ cmake -Bbuild
46 | $ cmake --build build --config Release
47 | ```
48 | The built plugins will be located in `ChowMatrix/build/ChowMatrix_artefacts/`
49 |
50 |
51 | ## Credits
52 |
53 | - GUI Design - [Margus Mets](mailto:hello@mmcreative.eu)
54 | - GUI Framework - [Plugin GUI Magic](https://github.com/ffAudio/PluginGUIMagic)
55 | - Dilogarithm function - [Polylogarithm](https://github.com/Expander/polylogarithm)
56 | - Extra Icons - [FontAwesome](https://fontawesome.com/)
57 |
58 |
59 | ## License
60 |
61 | ChowMatrix is open source, and is licensed under the BSD 3-clause license.
62 | Enjoy!
63 |
--------------------------------------------------------------------------------
/installers/mac/Intro.txt:
--------------------------------------------------------------------------------
1 | This application will install the ChowMatrix audio plugin version ##APPVERSION## to your computer.
2 |
--------------------------------------------------------------------------------
/installers/mac/build_mac_installer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | script_file=ChowMatrix.pkgproj
4 |
5 | app_version=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' ../../build/CMakeCache.txt)")
6 | echo "Setting app version: $app_version..."
7 | sed -i '' "s/##APPVERSION##/${app_version}/g" $script_file
8 | sed -i '' "s/##APPVERSION##/${app_version}/g" Intro.txt
9 |
10 | echo "Copying License..."
11 | cp ../../LICENSE LICENSE.txt
12 |
13 | # build installer
14 | echo Building...
15 | /usr/local/bin/packagesbuild $script_file
16 |
17 | # reset version number
18 | sed -i '' "s/${app_version}/##APPVERSION##/g" $script_file
19 | sed -i '' "s/${app_version}/##APPVERSION##/g" Intro.txt
20 |
21 | # clean up license file
22 | rm LICENSE.txt
23 |
24 | # sign the installer package
25 | echo "Signing installer package..."
26 | TEAM_ID=$(more ~/Developer/mac_id)
27 | pkg_dir=ChowMatrix_Installer_Packaged
28 | rm -Rf $pkg_dir
29 | mkdir $pkg_dir
30 | productsign -s "$TEAM_ID" ../../build/ChowMatrix.pkg $pkg_dir/ChowMatrix-signed.pkg
31 |
32 | echo "Notarizing installer package..."
33 | INSTALLER_PASS=$(more ~/Developer/mac_installer_pass)
34 | npx notarize-cli --file $pkg_dir/ChowMatrix-signed.pkg --bundle-id com.chowdsp.ChowMatrix --asc-provider "$TEAM_ID" --username chowdsp@gmail.com --password "$INSTALLER_PASS"
35 |
36 | echo "Building disk image..."
37 | vol_name=Install_ChowMatrix-$app_version
38 | hdiutil create "$vol_name.dmg" -fs HFS+ -srcfolder $pkg_dir -format UDZO -volname "$vol_name"
39 |
--------------------------------------------------------------------------------
/installers/windows/build_win_installer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | script_file=ChowMatrix_Install_Script.iss
4 |
5 | app_version=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' ../../build/CMakeCache.txt)")
6 | echo "Setting app version: $app_version..."
7 | sed -i "s/##APPVERSION##/${app_version}/g" $script_file
8 |
9 | # build installer
10 | echo Building...
11 | $"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" $script_file
12 |
13 | # reset version number
14 | sed -i "s/${app_version}/##APPVERSION##/g" $script_file
15 |
16 | exec="ChowMatrix-Win-$app_version.exe"
17 | direc=$PWD
18 |
19 | # sign
20 | # echo "Signing installer..."
21 | # $"cd" "C:\Program Files (x86)\Windows Kits\10\bin\10.0.16299.0\x64"
22 | # $"./signtool.exe" sign -tr http://timestamp.digicert.com -td sha256 -fd sha256 -a -debug $direc/$exec
23 | # $"./signtool.exe" verify -pa $direc/$exec
24 |
25 | echo SUCCESS
26 |
--------------------------------------------------------------------------------
/installers/windows/chowmatrix.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/installers/windows/chowmatrix.ico
--------------------------------------------------------------------------------
/manual/.gitignore:
--------------------------------------------------------------------------------
1 | *.aux
2 | *.log
3 | *.out
4 | *.sync*
5 |
--------------------------------------------------------------------------------
/manual/ChowMatrixManual.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/manual/ChowMatrixManual.pdf
--------------------------------------------------------------------------------
/manual/Makefile:
--------------------------------------------------------------------------------
1 | SOURCES = ChowMatrixManual.tex manual.cls
2 | TEX_FLAGS =
3 |
4 | PDFS = $(patsubst %.tex, %.pdf, $(SOURCES))
5 |
6 | .PHONY: clean
7 |
8 | # Run xelatex twice to generate references
9 | %.pdf: %.tex
10 | xelatex $(TEX_FLAGS) $<
11 | xelatex $(TEX_FLAGS) $<
12 |
13 | all: $(PDFS)
14 |
15 | clean:
16 | rm -f *.{log,pdf,out,aux,fls,fdb*,sync*}
17 |
18 | deploy: $(PDFS)
19 | scp ChowMatrixManual.pdf jatin@ccrma-gate.stanford.edu:~/Library/Web/chowdsp/Products
20 |
--------------------------------------------------------------------------------
/manual/manual.cls:
--------------------------------------------------------------------------------
1 | \LoadClass{article}
2 |
3 | % for figures
4 | \usepackage{graphicx}
5 |
6 | % Needed to manage colors
7 | \RequirePackage{xcolor}
8 | \colorlet{theme}{black}
9 |
10 | % Manage name
11 | \newcommand*{\pluginname}[1]{\def\@pluginname{#1}}
12 |
13 | % Manage spacing
14 | \def\newpar{\newline\newline}
15 | \usepackage[skip=2.5pt]{caption}
16 | \setlength{\belowcaptionskip}{-12pt}
17 |
18 | % Needed to manage fonts
19 | \RequirePackage[default,opentype]{sourcesanspro}
20 | \usepackage[sfdefault,light]{roboto}
21 | \newcommand*{\bodyfont}{\roboto} % {\sourcesanspro}
22 |
23 | \newcommand\boldtheme[1]{\textcolor{theme}{\textbf{#1}}}
24 |
25 | % format hyperlinks
26 | \usepackage{hyperref}
27 | \hypersetup{
28 | colorlinks=true,
29 | linkcolor=blue,
30 | filecolor=magenta,
31 | urlcolor=blue,
32 | pdfstartview=FitW
33 | }
34 |
35 | % manage title formats
36 | \usepackage{titlesec}
37 |
38 | \titleformat{\section}[block]{\fontsize{18pt}{1em}%
39 | \bodyfont\color{black}\bfseries\filcenter}{}{0em}%
40 | {}[\color{theme}{\titlerule[1pt]}]
41 |
42 | \titleformat{\subsection}[hang]{\fontsize{16pt}{1em}%
43 | \bodyfont\color{black}\bfseries\raggedright}{}{0em}%
44 | {}[\color{theme}{\titlerule[1pt]}]
45 |
46 | \titleformat{\subsubsection}[hang]{\fontsize{11pt}{1em}%
47 | \bodyfont\color{black}\bfseries\raggedright}{}{0em}%
48 | {}[\color{theme}{\titlerule[1pt]}]
49 |
50 | % fancy footer
51 | \usepackage{fancyhdr}
52 | \usepackage[bottom,flushmargin]{footmisc}
53 | \pagestyle{fancy}
54 | \fancyhf{}
55 | \fancyfoot[RO]{\color{gray}\textbf{\@pluginname} Plugin User Manual \textasciitilde \ {\the\numexpr2*\value{page}-1\relax}/{\the\numexpr2*\value{page}\relax}}
56 | \renewcommand{\headrulewidth}{0pt}
57 | \renewcommand{\footrulewidth}{1pt}
58 | \def\footrule{{\if@fancyplain\let\footrulewidth\plainfootrulewidth\fi
59 | \leavevmode\rlap{\color{theme}\rule{\textwidth}{\footrulewidth}}
60 | \vskip\footruleskip}}
61 |
--------------------------------------------------------------------------------
/manual/screenshots/BottomBar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/manual/screenshots/BottomBar.png
--------------------------------------------------------------------------------
/manual/screenshots/DetailsView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/manual/screenshots/DetailsView.png
--------------------------------------------------------------------------------
/manual/screenshots/GraphView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/manual/screenshots/GraphView.png
--------------------------------------------------------------------------------
/manual/screenshots/full_gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/manual/screenshots/full_gui.png
--------------------------------------------------------------------------------
/modules/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(JUCE)
2 | include(cmake/SubprojectVersion.cmake)
3 | subproject_version(JUCE juce_version)
4 | message(STATUS "VERSION for JUCE: ${juce_version}")
5 |
6 | juce_add_modules(foleys_gui_magic)
7 | juce_add_modules(chowdsp_utils/modules/chowdsp_dsp)
8 | juce_add_modules(chowdsp_utils/modules/chowdsp_gui)
9 | juce_add_modules(chowdsp_utils/modules/chowdsp_plugin_utils)
10 | add_subdirectory(polylogarithm)
11 |
12 | include(cmake/WarningFlags.cmake)
13 | add_library(juce_plugin_modules STATIC)
14 | target_link_libraries(juce_plugin_modules
15 | PRIVATE
16 | BinaryData
17 | juce::juce_audio_utils
18 | juce::juce_audio_plugin_client
19 | juce::juce_dsp
20 | foleys_gui_magic
21 | chowdsp_dsp
22 | chowdsp_gui
23 | chowdsp_plugin_utils
24 | polylogarithm
25 | PUBLIC
26 | juce::juce_recommended_config_flags
27 | juce::juce_recommended_lto_flags
28 | warning_flags
29 | )
30 |
31 | target_compile_definitions(juce_plugin_modules
32 | PUBLIC
33 | JUCE_DISPLAY_SPLASH_SCREEN=0
34 | JUCE_REPORT_APP_USAGE=0
35 | JUCE_WEB_BROWSER=0
36 | JUCE_USE_CURL=0
37 | JUCE_VST3_CAN_REPLACE_VST2=0
38 | JUCE_JACK=1
39 | JUCE_ALSA=1
40 | JUCE_MODAL_LOOPS_PERMITTED=1
41 | FOLEYS_SHOW_GUI_EDITOR_PALLETTE=0
42 | FOLEYS_ENABLE_BINARY_DATA=1
43 | JucePlugin_Manufacturer="chowdsp"
44 | JucePlugin_VersionString="${CMAKE_PROJECT_VERSION}"
45 | JucePlugin_Name="${CMAKE_PROJECT_NAME}"
46 | INTERFACE
47 | $
48 | )
49 |
50 | target_include_directories(juce_plugin_modules
51 | INTERFACE
52 | $
53 | )
54 |
55 | set_target_properties(juce_plugin_modules PROPERTIES
56 | POSITION_INDEPENDENT_CODE TRUE
57 | VISIBILITY_INLINES_HIDDEN TRUE
58 | C_VISBILITY_PRESET hidden
59 | CXX_VISIBILITY_PRESET hidden
60 | )
61 |
62 | if(IOS)
63 | target_link_libraries(juce_plugin_modules PRIVATE juce::juce_product_unlocking)
64 | target_compile_definitions(juce_plugin_modules PUBLIC JUCE_IN_APP_PURCHASES=1)
65 | endif()
66 |
67 | if(NOT APPLE)
68 | message(STATUS "Linking with OpenGL")
69 | target_link_libraries(juce_plugin_modules PRIVATE juce::juce_opengl)
70 | target_compile_definitions(juce_plugin_modules PUBLIC FOLEYS_ENABLE_OPEN_GL_CONTEXT=1)
71 | endif()
72 |
73 | include(cmake/pluginval.cmake)
74 |
--------------------------------------------------------------------------------
/modules/cmake/SubprojectVersion.cmake:
--------------------------------------------------------------------------------
1 | # subproject_version( )
2 | #
3 | # Extract version of a sub-project, which was previously included with add_subdirectory().
4 | function(subproject_version subproject_name VERSION_VAR)
5 | # Read CMakeLists.txt for subproject and extract project() call(s) from it.
6 | file(STRINGS "${${subproject_name}_SOURCE_DIR}/CMakeLists.txt" project_calls REGEX "[ \t]*project\\(")
7 | # For every project() call try to extract its VERSION option
8 | foreach(project_call ${project_calls})
9 | string(REGEX MATCH "VERSION[ ]+([^ )]+)" version_param "${project_call}")
10 | if(version_param)
11 | set(version_value "${CMAKE_MATCH_1}")
12 | endif()
13 | endforeach()
14 | if(version_value)
15 | set(${VERSION_VAR} "${version_value}" PARENT_SCOPE)
16 | else()
17 | message("WARNING: Cannot extract version for subproject '${subproject_name}'")
18 | endif()
19 |
20 | endfunction(subproject_version)
21 |
--------------------------------------------------------------------------------
/modules/cmake/WarningFlags.cmake:
--------------------------------------------------------------------------------
1 | add_library(warning_flags INTERFACE)
2 |
3 | if((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR (CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))
4 | target_compile_options(warning_flags INTERFACE
5 | /W4 # base warning level
6 | /wd4458 # declaration hides class member (from Foley's GUI Magic)
7 | /wd4505 # since VS2019 doesn't handle [[ maybe_unused ]] for static functions (RTNeural::debug_print)
8 | )
9 | elseif((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang"))
10 | target_compile_options(warning_flags INTERFACE
11 | -Wall -Wshadow-all -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized
12 | -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion
13 | -Wconditional-uninitialized -Woverloaded-virtual -Wreorder
14 | -Wconstant-conversion -Wsign-conversion -Wunused-private-field
15 | -Wbool-conversion -Wno-extra-semi -Wunreachable-code
16 | -Wzero-as-null-pointer-constant -Wcast-align
17 | -Wno-inconsistent-missing-destructor-override -Wshift-sign-overflow
18 | -Wnullable-to-nonnull-conversion -Wno-missing-field-initializers
19 | -Wno-ignored-qualifiers -Wpedantic -Wno-pessimizing-move
20 | # These lines suppress some custom warnings.
21 | # Comment them out to be more strict.
22 | -Wno-shadow-field-in-constructor
23 | # Needed for ARM processor, OSX versions below 10.14
24 | -fno-aligned-allocation
25 | )
26 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
27 | target_compile_options(warning_flags INTERFACE
28 | -Wall -Wextra -Wstrict-aliasing -Wuninitialized -Wunused-parameter
29 | -Wsign-compare -Woverloaded-virtual -Wreorder -Wunreachable-code
30 | -Wzero-as-null-pointer-constant -Wcast-align -Wno-implicit-fallthrough
31 | -Wno-maybe-uninitialized -Wno-missing-field-initializers -Wno-pedantic
32 | -Wno-ignored-qualifiers -Wno-unused-function -Wno-pessimizing-move
33 | # From LV2 Wrapper
34 | -Wno-parentheses -Wno-deprecated-declarations -Wno-redundant-decls
35 | # These lines suppress some custom warnings.
36 | # Comment them out to be more strict.
37 | -Wno-redundant-move
38 | )
39 |
40 | if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7.0.0")
41 | target_compile_options(warning_flags INTERFACE "-Wno-strict-overflow")
42 | endif()
43 | endif()
44 |
--------------------------------------------------------------------------------
/modules/cmake/pluginval.cmake:
--------------------------------------------------------------------------------
1 | set_property(GLOBAL PROPERTY pluginval_setup)
2 | function(create_pluginval_target target plugin)
3 | message(STATUS "Creating pluginval target for ${target}")
4 |
5 | if (APPLE)
6 | set(pluginval_url "https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_macOS.zip")
7 | set(pluginval_exe pluginval/pluginval.app/Contents/MacOS/pluginval)
8 | elseif (WIN32)
9 | set(pluginval_url "https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_Windows.zip")
10 | set(pluginval_exe pluginval/pluginval.exe)
11 | else ()
12 | set(pluginval_url "https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_Linux.zip")
13 | set(pluginval_exe pluginval/pluginval)
14 | endif ()
15 |
16 | get_property(is_pluginval_setup GLOBAL PROPERTY pluginval_setup)
17 | if ("${is_pluginval_setup}" STREQUAL "yes")
18 | message(STATUS "Skipping pluginval setup...")
19 | else ()
20 | add_custom_target(download-pluginval)
21 | add_custom_command(TARGET download-pluginval
22 | POST_BUILD
23 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
24 | COMMAND cmake -E make_directory pluginval
25 | COMMAND curl -L ${pluginval_url} -o pluginval/pluginval.zip
26 | COMMAND cd pluginval && unzip -o pluginval.zip
27 | )
28 | add_custom_target(pluginval-all)
29 | set_property(GLOBAL PROPERTY pluginval_setup "yes")
30 | endif()
31 |
32 | set(name "${target}-pluginval")
33 | add_custom_target(${name})
34 | add_dependencies(${name} ${target})
35 | add_dependencies(${name} download-pluginval)
36 | get_target_property(plugin_location ${target} LIBRARY_OUTPUT_DIRECTORY)
37 | add_custom_command(TARGET ${name}
38 | POST_BUILD
39 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
40 | COMMAND ${pluginval_exe} --validate-in-process --strictness-level 5 --timeout-ms 600000 --output-dir "." --validate "${plugin_location}/${plugin}" || exit 1
41 | )
42 | add_dependencies(pluginval-all ${name})
43 | endfunction()
44 |
--------------------------------------------------------------------------------
/modules/polylogarithm/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(polylogarithm STATIC
2 | Li2.cpp
3 | )
4 |
5 | # Need to build with -fPIC flag on Linux
6 | set_target_properties(polylogarithm PROPERTIES
7 | POSITION_INDEPENDENT_CODE TRUE)
8 |
--------------------------------------------------------------------------------
/modules/polylogarithm/Li2.hpp:
--------------------------------------------------------------------------------
1 | // ====================================================================
2 | // This file is part of Polylogarithm.
3 | //
4 | // Polylogarithm is licenced under the GNU Lesser General Public
5 | // License (GNU LGPL) version 3.
6 | // ====================================================================
7 |
8 | #pragma once
9 | #include
10 |
11 | namespace polylogarithm {
12 |
13 | /// real polylogarithm with n=2 (dilogarithm)
14 | double Li2(double) noexcept;
15 |
16 | /// real polylogarithm with n=2 (dilogarithm) with long double precision
17 | long double Li2(long double) noexcept;
18 |
19 | } // namespace polylogarithm
20 |
--------------------------------------------------------------------------------
/res/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | juce_add_binary_data(BinaryData SOURCES
2 | gui.xml
3 | gui_ios.xml
4 |
5 | Diceroll.png
6 | Panic.png
7 | home.svg
8 | undo-solid.png
9 | cog-solid.svg
10 | LeftArrow.svg
11 | RightArrow.svg
12 | ellipsis-h-solid.png
13 |
14 | presets/Crazy.chowpreset
15 | presets/Default.chowpreset
16 | presets/Lush.chowpreset
17 | presets/Rhythmic.chowpreset
18 | presets/Chord.chowpreset
19 | presets/Wobble.chowpreset
20 | presets/Wonky.chowpreset
21 | )
22 |
23 | # Need to build BinaryData with -fPIC flag on Linux
24 | set_target_properties(BinaryData PROPERTIES
25 | POSITION_INDEPENDENT_CODE TRUE)
26 |
--------------------------------------------------------------------------------
/res/Diceroll.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/res/Diceroll.png
--------------------------------------------------------------------------------
/res/LeftArrow.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/res/Panic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/res/Panic.png
--------------------------------------------------------------------------------
/res/RightArrow.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/res/cog-solid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/res/ellipsis-h-solid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/res/ellipsis-h-solid.png
--------------------------------------------------------------------------------
/res/home.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/res/logo.png
--------------------------------------------------------------------------------
/res/presets/Default.chowpreset:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/res/undo-solid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chowdhury-DSP/ChowMatrix/40d8e0ef1f752a6843099ff3dfc3d99132b332eb/res/undo-solid.png
--------------------------------------------------------------------------------
/scripts/ios_build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 | TEAM_ID=$(more ~/Developer/mac_id)
5 |
6 | if [ "$1" == "help" ]; then
7 | echo "Run bash ios_build.sh build clean"
8 | echo "Run bash ios_build.sh version"
9 | echo "Go to Xcode Archive Organizer and upload!"
10 | exit
11 | fi
12 |
13 | if [ "$1" == "build" ] || [ "$1" == "configure" ]; then
14 | echo "Running CMake configuration..."
15 |
16 | # clean up old builds
17 | if [ "$2" == "clean" ]; then rm -Rf build-ios; fi
18 |
19 | # generate new builds
20 | if [ "$1" == "configure" ]; then
21 | cmake -Bbuild-ios -GXcode -DCMAKE_SYSTEM_NAME=iOS \
22 | -DCMAKE_OSX_DEPLOYMENT_TARGET=11.4 \
23 | -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM="$TEAM_ID" \
24 | -DCMAKE_XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY="1,2" \
25 | -DCMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE="NO" \
26 | -DCMAKE_XCODE_ATTRIBUTE_ENABLE_IN_APP_PURCHASE="YES"
27 | fi
28 |
29 | if [ "$1" == "build" ]; then
30 | xcodebuild -project build-ios/ChowMatrix.xcodeproj \
31 | -scheme ChowMatrix_Standalone archive -configuration Release \
32 | -sdk iphoneos -jobs 12 -archivePath ChowMatrix.xcarchive | xcpretty
33 | fi
34 | fi
35 |
36 | if [ "$1" == "version" ]; then
37 | # set version number to include commit hash
38 | COMMIT=$(git log --pretty=format:'%h' -n 1)
39 | VERSION=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' build-ios/CMakeCache.txt)")
40 | BUILD_NUMBER="$VERSION-$COMMIT"
41 | echo "Setting version for archive: $BUILD_NUMBER"
42 |
43 | PLIST=ChowMatrix.xcarchive/Info.plist
44 | /usr/libexec/Plistbuddy -c "Set ApplicationProperties:CFBundleVersion $BUILD_NUMBER" "$PLIST"
45 |
46 | # move to archives folder so Xcode can find it
47 | archive_dir="$HOME/Library/Developer/Xcode/Archives/$(date '+%Y-%m-%d')"
48 | echo "Moving to directory: $archive_dir"
49 | mkdir -p "$archive_dir"
50 | mv ChowMatrix.xcarchive "$archive_dir/ChowMatrix-$COMMIT.xcarchive"
51 | fi
52 |
53 |
--------------------------------------------------------------------------------
/scripts/mac_builds.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # exit on failure
4 | set -e
5 |
6 | # clean up old builds
7 | rm -Rf build/
8 | rm -Rf bin/*Mac*
9 |
10 | # set up build VST
11 | VST_PATH=~/Developer/Plugin_SDKs/VST2_SDK/
12 | sed -i '' "9s~.*~juce_set_vst2_sdk_path(${VST_PATH})~" CMakeLists.txt
13 |
14 | # cmake new builds
15 | TEAM_ID=$(more ~/Developer/mac_id)
16 | cmake -Bbuild -GXcode -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY="Developer ID Application" \
17 | -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM="$TEAM_ID" \
18 | -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE="Manual" \
19 | -D"CMAKE_OSX_ARCHITECTURES=arm64;x86_64" \
20 | -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO \
21 | -DCMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS="--timestamp" \
22 | -DMACOS_RELEASE=ON
23 | cmake --build build --config Release -j8 | xcpretty
24 |
25 | # copy builds to bin
26 | mkdir -p bin/Mac
27 | declare -a plugins=("ChowMatrix")
28 | for plugin in "${plugins[@]}"; do
29 | cp -R build/${plugin}_artefacts/Release/Standalone/${plugin}.app bin/Mac/${plugin}.app
30 | cp -R build/${plugin}_artefacts/Release/VST/${plugin}.vst bin/Mac/${plugin}.vst
31 | cp -R build/${plugin}_artefacts/Release/VST3/${plugin}.vst3 bin/Mac/${plugin}.vst3
32 | cp -R build/${plugin}_artefacts/Release/AU/${plugin}.component bin/Mac/${plugin}.component
33 | done
34 |
35 | # reset CMakeLists.txt
36 | git restore CMakeLists.txt
37 |
38 | # run auval
39 | echo "Running AU validation..."
40 | rm -Rf ~/Library/Audio/Plug-Ins/Components/${plugin}.component
41 | cp -R build/${plugin}_artefacts/Release/AU/${plugin}.component ~/Library/Audio/Plug-Ins/Components
42 | manu=$(cut -f 6 -d ' ' <<< "$(grep 'PLUGIN_MANUFACTURER_CODE' CMakeLists.txt)")
43 | code=$(cut -f 6 -d ' ' <<< "$(grep 'PLUGIN_CODE' CMakeLists.txt)")
44 |
45 | set +e
46 | auval_result=$(auval -v aufx "$code" "$manu")
47 | auval_code="$?"
48 | echo "AUVAL code: $auval_code"
49 |
50 | if [ "$auval_code" != 0 ]; then
51 | echo "$auval_result"
52 | echo "auval FAIL!!!"
53 | exit 1
54 | else
55 | echo "auval PASSED"
56 | fi
57 |
58 | # zip builds
59 | echo "Zipping builds..."
60 | VERSION=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' build/CMakeCache.txt)")
61 | (
62 | cd bin
63 | rm -f "ChowMatrix-Mac-${VERSION}.zip"
64 | zip -r "ChowMatrix-Mac-${VERSION}.zip" Mac
65 | )
66 |
67 | # create installer
68 | echo "Creating installer..."
69 | (
70 | cd installers/mac
71 | bash build_mac_installer.sh
72 | )
73 |
--------------------------------------------------------------------------------
/scripts/win_builds.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | build64(){
4 | cmake -Bbuild -G"Visual Studio 16 2019" -A x64 -DASIOSDK_DIR="C:\\SDKs\\ASIO_SDK"
5 | cmake --build build --config Release -j4
6 | }
7 |
8 | build32(){
9 | cmake -Bbuild32 -G"Visual Studio 16 2019" -A Win32 -DASIOSDK_DIR="C:\\SDKs\\ASIO_SDK"
10 | cmake --build build32 --config Release -j4
11 | }
12 |
13 | # exit on failure
14 | set -e
15 |
16 | # clean up old builds
17 | rm -Rf build/
18 | rm -Rf build32/
19 | rm -Rf bin/*Win64*
20 | rm -Rf bin/*Win32*
21 |
22 | # set up VST SDK paths
23 | sed -i -e "9s/#//" CMakeLists.txt
24 |
25 | # cmake new builds
26 | build64 &
27 | build32 &
28 | wait
29 |
30 | # copy builds to bin
31 | mkdir -p bin/Win64
32 | mkdir -p bin/Win32
33 | declare -a plugins=("ChowMatrix")
34 | for plugin in "${plugins[@]}"; do
35 | cp -R build/${plugin}_artefacts/Release/Standalone/${plugin}.exe bin/Win64/${plugin}.exe
36 | cp -R build/${plugin}_artefacts/Release/VST/${plugin}.dll bin/Win64/${plugin}.dll
37 | cp -R build/${plugin}_artefacts/Release/VST3/${plugin}.vst3 bin/Win64/${plugin}.vst3
38 |
39 | cp -R build32/${plugin}_artefacts/Release/Standalone/${plugin}.exe bin/Win32/${plugin}.exe
40 | cp -R build32/${plugin}_artefacts/Release/VST/${plugin}.dll bin/Win32/${plugin}.dll
41 | cp -R build32/${plugin}_artefacts/Release/VST3/${plugin}.vst3 bin/Win32/${plugin}.vst3
42 | done
43 |
44 | # reset CMakeLists.txt
45 | git restore CMakeLists.txt
46 |
47 | # zip builds
48 | VERSION=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' build/CMakeCache.txt)")
49 | (
50 | cd bin
51 | rm -f "ChowMatrix-Win64-${VERSION}.zip"
52 | rm -f "ChowMatrix-Win32-${VERSION}.zip"
53 | zip -r "ChowMatrix-Win64-${VERSION}.zip" Win64
54 | zip -r "ChowMatrix-Win32-${VERSION}.zip" Win32
55 | )
56 |
57 | # create installer
58 | echo "Creating installer..."
59 | (
60 | cd installers/windows
61 | bash build_win_installer.sh
62 | )
63 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | option(BUILD_HEADLESS "Build ChowMatrix headless utilities" OFF)
2 | if(NOT (IOS OR MACOS_RELEASE) AND BUILD_HEADLESS)
3 | message(STATUS "Building ChowMatrix Headless!")
4 | add_subdirectory(headless)
5 | endif()
6 |
7 | target_sources(ChowMatrix PRIVATE
8 | ChowMatrix.cpp
9 | NodeManager.cpp
10 |
11 | dsp/BaseNode.cpp
12 | dsp/DelayNode.cpp
13 | dsp/InputNode.cpp
14 | dsp/Delay/DelayProc.cpp
15 | dsp/Delay/Diffusion.cpp
16 | dsp/Delay/Reverser.cpp
17 | dsp/Delay/PitchShiftWrapper.cpp
18 | dsp/Delay/VariableDelay.cpp
19 | dsp/Distortion/Distortion.cpp
20 | dsp/Distortion/LookupTables.cpp
21 | dsp/Parameters/BaseController.cpp
22 | dsp/Parameters/DelayTypeControl.cpp
23 | dsp/Parameters/HostParamControl.cpp
24 | dsp/Parameters/InsanityControl.cpp
25 | dsp/Parameters/InsanityLockHelper.cpp
26 | dsp/Parameters/NodeParamControl.cpp
27 | dsp/Parameters/RandomiseLockHelper.cpp
28 | dsp/Parameters/ParamHelpers.cpp
29 | dsp/Parameters/SyncControl.cpp
30 |
31 | gui/AutoUpdating.cpp
32 | gui/ParamSlider.cpp
33 | gui/BottomBar/ABComp.cpp
34 | gui/BottomBar/BottomBarLNF.cpp
35 | gui/BottomBar/HostControlMenu.cpp
36 | gui/BottomBar/HostControlMenuComp.cpp
37 | gui/DetailsView/NodeDetails.cpp
38 | gui/DetailsView/NodeDetailsComponent.cpp
39 | gui/DetailsView/NodeDetailsGUI.cpp
40 | gui/DetailsView/NodeDetailsViewport.cpp
41 | gui/IOSUtils/LongPressActionHelper.cpp
42 | gui/IOSUtils/TipJar.cpp
43 | gui/MatrixView/DelayNodeComponent.cpp
44 | gui/MatrixView/GraphView.cpp
45 | gui/MatrixView/GraphViewport.cpp
46 | gui/MatrixView/MatrixAurora.cpp
47 | gui/MatrixView/NodeCompManager.cpp
48 | gui/MatrixView/NodeComponent.cpp
49 |
50 | state/PresetManager.cpp
51 | state/StateManager.cpp
52 | )
53 |
54 | target_precompile_headers(ChowMatrix PRIVATE pch.h)
55 |
--------------------------------------------------------------------------------
/src/ChowMatrix.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "NodeManager.h"
4 | #include "dsp/InputNode.h"
5 | #include "dsp/Parameters/DelayTypeControl.h"
6 | #include "dsp/Parameters/HostParamControl.h"
7 | #include "dsp/Parameters/InsanityControl.h"
8 | #include "dsp/Parameters/NodeParamControl.h"
9 | #include "dsp/Parameters/SyncControl.h"
10 | #include "gui/MatrixView/GraphViewport.h"
11 | #include "state/StateManager.h"
12 | #include
13 |
14 | #if CHOWDSP_AUTO_UPDATE
15 | #include "gui/AutoUpdating.h"
16 | #endif // CHOWDSP_AUTO_UPDATE
17 |
18 | /**
19 | * Main class for the Matrix plugin
20 | */
21 | class ChowMatrix : public chowdsp::PluginBase
22 | {
23 | public:
24 | ChowMatrix();
25 |
26 | static void addParameters (Parameters& params);
27 | void prepareToPlay (double sampleRate, int samplesPerBlock) override;
28 | void releaseResources() override;
29 | void processAudioBlock (AudioBuffer& buffer) override;
30 |
31 | AudioProcessorEditor* createEditor() override;
32 |
33 | void getStateInformation (MemoryBlock& destData) override;
34 | void setStateInformation (const void* data, int sizeInBytes) override;
35 |
36 | /** Handle to access node manager */
37 | NodeManager& getManager() { return manager; }
38 |
39 | /** Handle to read insanity parameter (used by MatrixAurora) */
40 | std::atomic* getInsanityParam() const noexcept { return insanityControl.getParameter(); }
41 |
42 | /** Access to array of input nodes */
43 | std::array* getNodes() { return &inputNodes; }
44 | StateManager& getStateManager() { return stateManager; }
45 | HostParamControl& getHostControl() { return hostParamControl; }
46 | AudioProcessorValueTreeState& getVTS() { return vts; }
47 |
48 | private:
49 | std::array inputNodes;
50 | NodeManager manager;
51 |
52 | std::atomic* dryParamDB = nullptr;
53 | std::atomic* wetParamDB = nullptr;
54 | std::atomic* wetGainCompParam = nullptr;
55 |
56 | AudioBuffer chBuffers[2];
57 |
58 | AudioBuffer dryBuffer;
59 | dsp::Gain dryGain;
60 | dsp::Gain wetGain;
61 |
62 | InsanityControl insanityControl;
63 | HostParamControl hostParamControl;
64 | DelayTypeControl delayTypeControl;
65 | SyncControl syncControl;
66 | NodeParamControl nodeParamControl;
67 |
68 | // create shared resources here so that
69 | // loading new nodes is fast
70 | SharedResourcePointer luts;
71 | SharedResourcePointer delayStore;
72 |
73 | StateManager stateManager;
74 |
75 | #if CHOWDSP_AUTO_UPDATE
76 | AutoUpdater updater;
77 | #endif // CHOWDSP_AUTO_UPDATE
78 |
79 | std::unique_ptr graphViewPtr;
80 |
81 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChowMatrix)
82 | };
83 |
--------------------------------------------------------------------------------
/src/NodeManager.cpp:
--------------------------------------------------------------------------------
1 | #include "NodeManager.h"
2 |
3 | void NodeManager::doForNodes (DBaseNode* root, std::function nodeFunc)
4 | {
5 | for (int i = 0; i < root->getNumChildren(); ++i)
6 | {
7 | auto child = root->getChild (i);
8 | nodeFunc (child);
9 | doForNodes (child, nodeFunc);
10 | }
11 | }
12 |
13 | void NodeManager::doForNodes (std::array* nodes, std::function nodeFunc)
14 | {
15 | for (auto& node : *nodes)
16 | NodeManager::doForNodes (&node, nodeFunc);
17 | }
18 |
19 | void NodeManager::initialise (std::array* _nodes)
20 | {
21 | nodes = _nodes;
22 | nodeCount = 0;
23 |
24 | for (auto& node : *nodes)
25 | {
26 | node.addNodeListener (this);
27 | doForNodes (&node, [=] (DelayNode* n) { nodeAdded (n); });
28 | }
29 | }
30 |
31 | void NodeManager::nodeAdded (DelayNode* newNode)
32 | {
33 | newNode->addNodeListener (this);
34 | newNode->setIndex (nodeCount++);
35 | newNode->setSoloed (newNodeSoloState);
36 | }
37 |
38 | void NodeManager::nodeRemoved (DelayNode* nodeToRemove)
39 | {
40 | nodeToRemove->removeNodeListener (this);
41 | if (nodeToRemove->getSelected())
42 | selectedNodePtr = nullptr;
43 |
44 | nodeCount = 0;
45 | doForNodes (nodes, [=] (DelayNode* n) { n->setIndex (nodeCount++); });
46 | }
47 |
48 | void NodeManager::setParameterDiff (DelayNode* sourceNode, const String& paramID, float diff01)
49 | {
50 | doForNodes (nodes, [=] (DelayNode* n) {
51 | if (n == sourceNode)
52 | return;
53 |
54 | n->setNodeParameterDiff (paramID, diff01);
55 | });
56 | }
57 |
58 | void NodeManager::setParameterDefault (DelayNode* sourceNode, const String& paramID)
59 | {
60 | doForNodes (nodes, [=] (DelayNode* n) {
61 | if (n == sourceNode)
62 | return;
63 |
64 | n->setNodeParameterToDefault (paramID);
65 | });
66 | }
67 |
68 | void NodeManager::setSelected (DelayNode* selectedNode, ActionSource source)
69 | {
70 | selectedNodePtr = selectedNode;
71 |
72 | doForNodes (nodes, [=] (DelayNode* n) {
73 | if (n->getSelected() && selectedNode == n) // already selected!
74 | return;
75 | else if (n->getSelected()) // is currently selected, but no longer
76 | n->setSelected (false);
77 | else if (selectedNode == n) // should now be seleced
78 | n->setSelected (true);
79 | });
80 |
81 | listeners.call (&Listener::nodeSelected, selectedNode, source);
82 | }
83 |
84 | DelayNode* NodeManager::getSelected() const noexcept
85 | {
86 | return selectedNodePtr;
87 | }
88 |
89 | void NodeManager::setSoloed (DelayNode* soloedNode, ActionSource source)
90 | {
91 | newNodeSoloState = soloedNode == nullptr ? DelayNode::SoloState::None : DelayNode::SoloState::Mute;
92 |
93 | doForNodes (nodes, [=] (DelayNode* n) {
94 | if (soloedNode == nullptr) // "un-solo" and currently soloed nodes
95 | {
96 | n->setSoloed (DelayNode::SoloState::None);
97 | return;
98 | }
99 |
100 | if (n == soloedNode) // Node that has been selected for soloing
101 | {
102 | if (n->getSoloed() == DelayNode::SoloState::Solo) // already soloed
103 | return;
104 |
105 | n->setSoloed (DelayNode::SoloState::Solo); // should now be soloed
106 | return;
107 | }
108 |
109 | // any other nodes should be muted
110 | n->setSoloed (DelayNode::SoloState::Mute);
111 | });
112 |
113 | listeners.call (&Listener::nodeSoloed, soloedNode, source);
114 | }
115 |
--------------------------------------------------------------------------------
/src/NodeManager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "dsp/DelayNode.h"
4 | #include "dsp/InputNode.h"
5 |
6 | /**
7 | * Utility class to manage node graph
8 | */
9 | class NodeManager : private DBaseNode::Listener
10 | {
11 | public:
12 | NodeManager() = default;
13 |
14 | /** Iterate through node tree and perform nodeFunc for all nodes */
15 | static void doForNodes (DBaseNode* root, std::function nodeFunc);
16 | static void doForNodes (std::array* nodes, std::function nodeFunc);
17 |
18 | /** Initialise node manager with an array of root nodes */
19 | void initialise (std::array* _nodes);
20 |
21 | // Node listener overrides
22 | void nodeAdded (DelayNode* newNode) override;
23 | void nodeRemoved (DelayNode* nodeToRemove) override;
24 | void setParameterDiff (DelayNode* sourceNode, const String& paramID, float diff01) override;
25 | void setParameterDefault (DelayNode* sourceNode, const String& paramID) override;
26 | void nodeInsanityLockChanged (DelayNode* node) override { listeners.call (&Listener::nodeInsanityLockChanged, node); }
27 |
28 | /** Sources that can trigger node selection */
29 | enum class ActionSource
30 | {
31 | GraphView,
32 | DetailsView
33 | };
34 |
35 | // Manage selected node
36 | void setSelected (DelayNode* node, ActionSource source);
37 | DelayNode* getSelected() const noexcept;
38 |
39 | // Manage soloed node
40 | void setSoloed (DelayNode* node, ActionSource source);
41 |
42 | class Listener
43 | {
44 | public:
45 | virtual ~Listener() {}
46 | virtual void nodeSelected (DelayNode* /*selectedNode*/, ActionSource /*source*/) {}
47 | virtual void nodeSoloed (DelayNode* /*soloedNode*/, ActionSource /*source*/) {}
48 | virtual void nodeInsanityLockChanged (DelayNode* /*node*/) {}
49 | };
50 |
51 | void addListener (Listener* l) { listeners.add (l); }
52 | void removeListener (Listener* l) { listeners.remove (l); }
53 |
54 | private:
55 | std::array* nodes = nullptr;
56 | int nodeCount = 0;
57 | DelayNode* selectedNodePtr = nullptr;
58 | DelayNode::SoloState newNodeSoloState = DelayNode::SoloState::None;
59 |
60 | ListenerList listeners;
61 |
62 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NodeManager)
63 | };
64 |
--------------------------------------------------------------------------------
/src/dsp/BaseNode.cpp:
--------------------------------------------------------------------------------
1 | #include "BaseNode.h"
2 | #include "../gui/MatrixView/DelayNodeComponent.h"
3 | #include "DelayNode.h"
4 | #include "InputNode.h"
5 |
6 | template
7 | BaseNode::BaseNode()
8 | {
9 | }
10 |
11 | template
12 | void BaseNode::prepare (double newSampleRate, int newSamplesPerBlock)
13 | {
14 | sampleRate = newSampleRate;
15 | samplesPerBlock = newSamplesPerBlock;
16 | childBuffer.setSize (1, newSamplesPerBlock);
17 |
18 | for (auto* child : children)
19 | child->prepare (sampleRate, samplesPerBlock);
20 | }
21 |
22 | template
23 | void BaseNode::process (AudioBuffer& inBuffer, AudioBuffer& outBuffer)
24 | {
25 | for (auto* child : children)
26 | {
27 | childBuffer.makeCopyOf (inBuffer, true);
28 | child->process (childBuffer, outBuffer);
29 | }
30 | }
31 |
32 | template
33 | float BaseNode::getNodeLevel (float inputLevel) const
34 | {
35 | float outputLevel = 0.0f;
36 | for (auto* child : children)
37 | outputLevel += child->getNodeLevel (inputLevel);
38 |
39 | return outputLevel;
40 | }
41 |
42 | template
43 | NodeComponent* BaseNode::getEditor()
44 | {
45 | return editor;
46 | }
47 |
48 | template
49 | Child* BaseNode::addChild()
50 | {
51 | // create child
52 | auto newChild = std::make_unique();
53 |
54 | // make this node parent of new child
55 | newChild->setParent (this);
56 |
57 | // add child to array of children
58 | // this must be done last so child is prepared before called to process audio
59 | auto* createdChild = children.add (std::move (newChild));
60 |
61 | // tell listeners about new child
62 | nodeListeners.call (&BaseNode::Listener::nodeAdded, createdChild);
63 |
64 | return createdChild;
65 | }
66 |
67 | template
68 | void BaseNode::removeChild (Child* childToRemove)
69 | {
70 | // remove child from children array
71 | int indexToRemove = children.indexOf (childToRemove);
72 |
73 | // put child in `toBeDeleted` placeholder
74 | nodeBeingDeleted.reset (children.removeAndReturn (indexToRemove));
75 | }
76 |
77 | template
78 | void BaseNode::clearChildren()
79 | {
80 | while (! children.isEmpty())
81 | children.getLast()->deleteNode();
82 |
83 | nodeBeingDeleted.reset();
84 | }
85 |
86 | template
87 | void BaseNode::setParent (BaseNode* newParent)
88 | {
89 | parent = newParent;
90 | prepare (parent->sampleRate, parent->samplesPerBlock);
91 | }
92 |
93 | template
94 | XmlElement* BaseNode::saveXml()
95 | {
96 | if (children.isEmpty())
97 | return new XmlElement ("no_children");
98 |
99 | std::unique_ptr xml = std::make_unique ("children");
100 | for (auto* child : children)
101 | xml->addChildElement (child->saveXml());
102 |
103 | return xml.release();
104 | }
105 |
106 | template
107 | void BaseNode::loadXml (XmlElement* xml)
108 | {
109 | if (xml == nullptr)
110 | return;
111 |
112 | if (xml->hasTagName ("children"))
113 | {
114 | for (auto* childXml : xml->getChildIterator())
115 | addChild()->loadXml (childXml);
116 | }
117 | }
118 |
119 | template class BaseNode;
120 |
--------------------------------------------------------------------------------
/src/dsp/BaseNode.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | class NodeComponent;
6 | class GraphView;
7 |
8 | /**
9 | * Base class to provide shared functionality for Delay Nodes
10 | * and Input Nodes
11 | */
12 | template
13 | class BaseNode
14 | {
15 | public:
16 | BaseNode();
17 | virtual ~BaseNode() = default;
18 |
19 | /** Create a GUI editor for this node */
20 | virtual std::unique_ptr createNodeEditor (GraphView*) = 0;
21 |
22 | /** Access the editor for this node, or nullptr if editor does not exist */
23 | NodeComponent* getEditor();
24 |
25 | virtual void prepare (double sampleRate, int samplesPerBlock);
26 | virtual void process (AudioBuffer& inBuffer, AudioBuffer& outBuffer);
27 |
28 | virtual float getNodeLevel (float inputLevel) const;
29 |
30 | // Adding/removing children
31 | Child* addChild();
32 | void removeChild (Child* childToRemove);
33 | void clearChildren();
34 |
35 | // Managing children
36 | int getNumChildren() const noexcept { return children.size(); }
37 | Child* getChild (int idx) { return children[idx]; }
38 | BaseNode* getParent() { return parent; }
39 | virtual void setParent (BaseNode* parent);
40 |
41 | struct Listener
42 | {
43 | virtual ~Listener() = default;
44 | virtual void nodeAdded (Child* /*newNode*/) {}
45 | virtual void nodeRemoved (Child* /*nodeToRemove*/) {}
46 | virtual void nodeIndexChanged (Child* /*node*/, int /*oldIndex*/, int /*newIndex*/) {}
47 |
48 | virtual void setParameterDiff (Child* /*node*/, const String& /*paramID*/, float /*diff01*/) {}
49 | virtual void setParameterDefault (Child* /*node*/, const String& /*paramID*/) {}
50 | virtual void nodeInsanityLockChanged (Child* /*node*/) {}
51 |
52 | virtual void beginParameterChange (const StringArray& /*paramIDs*/, Child* /*node*/) {}
53 | virtual void endParameterChange (const StringArray& /*paramIDs*/, Child* /*node*/) {}
54 | virtual void applyParameterChange (const String& /*paramID*/, Child* /*node*/, float /*value01*/) {}
55 |
56 | virtual void addParameterMenus (PopupMenu& /*parentMenu*/, const String& /*paramID*/, Child* /*node*/) {}
57 |
58 | virtual void saveExtraNodeState (XmlElement* /*nodeState*/, Child* /*node*/) {}
59 | virtual void loadExtraNodeState (XmlElement* /*nodeState*/, Child* /*node*/) {}
60 | };
61 |
62 | void addNodeListener (Listener* l) { nodeListeners.add (l); }
63 | void removeNodeListener (Listener* l) { nodeListeners.remove (l); }
64 |
65 | virtual XmlElement* saveXml();
66 | virtual void loadXml (XmlElement*);
67 |
68 | protected:
69 | NodeComponent* editor = nullptr;
70 | BaseNode* parent = nullptr;
71 |
72 | OwnedArray children;
73 | ListenerList nodeListeners;
74 |
75 | private:
76 | std::unique_ptr nodeBeingDeleted;
77 |
78 | AudioBuffer childBuffer;
79 |
80 | double sampleRate = 44100.0;
81 | int samplesPerBlock = 256;
82 |
83 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BaseNode)
84 | };
85 |
--------------------------------------------------------------------------------
/src/dsp/Delay/DelayProc.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../Distortion/Distortion.h"
4 | #include "Diffusion.h"
5 | #include "PitchShiftWrapper.h"
6 | #include "Reverser.h"
7 | #include "TempoSyncUtils.h"
8 | #include "VariableDelay.h"
9 | #include
10 |
11 | /**
12 | * Audio processor that implements delay line with feedback,
13 | * including filtering and distortion in the feedback path
14 | */
15 | class DelayProc
16 | {
17 | public:
18 | DelayProc();
19 |
20 | // processing functions
21 | void prepare (const dsp::ProcessSpec& spec);
22 | void reset();
23 |
24 | template
25 | void process (const ProcessContext& context);
26 |
27 | // flush delay line state
28 | void flushDelay();
29 |
30 | struct Parameters
31 | {
32 | float delayMs;
33 | float feedback;
34 | float lpfFreq;
35 | float hpfFreq;
36 | float distortion;
37 | float pitchSt;
38 | float diffAmt;
39 | float revTimeMs;
40 | const AudioProcessorValueTreeState::Parameter* modFreq;
41 | float modDepth;
42 | float tempoBPM;
43 | bool lfoSynced;
44 | AudioPlayHead* playhead;
45 | };
46 |
47 | void setParameters (const Parameters& params, bool force = false);
48 | void setDelayType (VariableDelay::DelayType type) { delay->setDelayType (type); }
49 | float getModDepth() const noexcept { return 1000.0f * delayModValue / fs; }
50 |
51 | private:
52 | template
53 | inline SampleType processSample (SampleType x, size_t ch);
54 |
55 | template
56 | inline SampleType processSampleSmooth (SampleType x, size_t ch);
57 |
58 | SharedResourcePointer delayStore;
59 | std::unique_ptr delay;
60 | // VariableDelay delay { 1 << 19 };
61 |
62 | float fs = 44100.0f;
63 |
64 | SmoothedValue delaySmooth;
65 | SmoothedValue feedback;
66 | SmoothedValue inGain;
67 | std::vector state;
68 |
69 | enum
70 | {
71 | lpfIdx,
72 | hpfIdx,
73 | diffusionIdx,
74 | distortionIdx,
75 | reverserIdx,
76 | pitchIdx,
77 | };
78 |
79 | MyProcessorChain<
80 | chowdsp::IIR::Filter,
81 | chowdsp::IIR::Filter,
82 | Diffusion,
83 | Distortion,
84 | Reverser,
85 | PitchShiftWrapper>
86 | procs;
87 |
88 | TempoSyncUtils::SyncedLFO modSine;
89 | float delayModValue = 0.0f;
90 | float modDepth = 0.0f;
91 | float modDepthFactor = 1.0f;
92 |
93 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DelayProc)
94 | };
95 |
--------------------------------------------------------------------------------
/src/dsp/Delay/DelayStore.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "VariableDelay.h"
4 |
5 | /** A store to create delay objects more quickly. */
6 | class DelayStore
7 | {
8 | public:
9 | DelayStore()
10 | {
11 | // start with a bunch in the queue
12 | for (int i = 0; i < storeSize; ++i)
13 | loadNewDelay();
14 | }
15 |
16 | VariableDelay* getNextDelay()
17 | {
18 | SpinLock::ScopedLockType nextDelayLock (delayStoreLock);
19 |
20 | auto* delay = delayFutureStore.front().get().release();
21 | delayFutureStore.pop_front();
22 | loadNewDelay();
23 |
24 | return delay;
25 | }
26 |
27 | private:
28 | void loadNewDelay()
29 | {
30 | delayFutureStore.push_back (std::async (std::launch::async, [] {
31 | auto newDelay = std::make_unique (1 << 19);
32 | newDelay->prepare ({ 48000.0, 512, 1 });
33 | newDelay->reset();
34 | return std::move (newDelay);
35 | }));
36 | }
37 |
38 | static constexpr int storeSize = 8;
39 |
40 | std::deque>> delayFutureStore;
41 | SpinLock delayStoreLock;
42 |
43 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DelayStore)
44 | };
45 |
--------------------------------------------------------------------------------
/src/dsp/Delay/Diffusion.cpp:
--------------------------------------------------------------------------------
1 | #include "Diffusion.h"
2 |
3 | namespace
4 | {
5 | constexpr double smoothTime = 0.01;
6 | constexpr float allpassFreq = 200.0f;
7 | } // namespace
8 |
9 | Diffusion::Diffusion()
10 | {
11 | depthSmooth.setCurrentAndTargetValue (0.0f);
12 | reset();
13 | }
14 |
15 | void Diffusion::setDepth (float depth, bool force)
16 | {
17 | if (force)
18 | depthSmooth.setCurrentAndTargetValue (depth);
19 | else
20 | depthSmooth.setTargetValue (depth);
21 | }
22 |
23 | void Diffusion::prepare (const dsp::ProcessSpec& spec)
24 | {
25 | fs = (float) spec.sampleRate;
26 | depthSmooth.reset (spec.sampleRate, smoothTime);
27 |
28 | calcCoefs (allpassFreq);
29 | reset();
30 | }
31 |
32 | void Diffusion::reset()
33 | {
34 | std::fill (z, &z[maxNumStages + 1], 0.0f);
35 | }
36 |
37 | void Diffusion::calcCoefs (float freq)
38 | {
39 | const float RC = 1.0f / (MathConstants::twoPi * freq);
40 | const float b0s = RC;
41 | const float b1s = -1.0f;
42 | const float a0s = b0s;
43 | const float a1s = 1.0f;
44 | const auto K = 2.0f * fs;
45 |
46 | const auto a0 = a0s * K + a1s;
47 | b[0] = (b0s * K + b1s) / a0;
48 | b[1] = (-b0s * K + b1s) / a0;
49 | a[0] = 1.0f;
50 | a[1] = (-a0s * K + a1s) / a0;
51 | }
52 |
--------------------------------------------------------------------------------
/src/dsp/Delay/Diffusion.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | /**
4 | * Class for delay line diffusion processing
5 | *
6 | * The DSP is made up of many stages of first-order allpass
7 | * filters, with linear fading between stages.
8 | */
9 | class Diffusion
10 | {
11 | public:
12 | Diffusion();
13 |
14 | void setDepth (float depth, bool force);
15 | void prepare (const dsp::ProcessSpec& spec);
16 | void reset();
17 |
18 | inline float processSample (float x)
19 | {
20 | auto numStages = depthSmooth.getNextValue() * maxNumStages;
21 | const auto numStagesInt = static_cast (numStages);
22 |
23 | // process integer stages
24 | for (size_t stage = 0; stage < numStagesInt; ++stage)
25 | x = processStage (x, stage);
26 |
27 | // process fractional stage
28 | float stageFrac = numStages - numStagesInt;
29 | x = stageFrac * processStage (x, numStagesInt) + (1.0f - stageFrac) * x;
30 |
31 | return x;
32 | }
33 |
34 | inline float processStage (float x, size_t stage)
35 | {
36 | float y = z[stage] + x * b[0];
37 | z[stage] = x * b[1] - y * a[1];
38 | return y;
39 | }
40 |
41 | private:
42 | void calcCoefs (float fc);
43 |
44 | static constexpr size_t maxNumStages = 100;
45 |
46 | float a[2] = { 1.0f, 0.0f };
47 | float b[2] = { 1.0f, 0.0f };
48 | float z[maxNumStages + 1];
49 |
50 | float fs = 44100.0f;
51 | SmoothedValue depthSmooth;
52 |
53 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Diffusion)
54 | };
55 |
--------------------------------------------------------------------------------
/src/dsp/Delay/PitchShiftWrapper.cpp:
--------------------------------------------------------------------------------
1 | #include "PitchShiftWrapper.h"
2 |
3 | PitchShiftWrapper::PitchShiftWrapper()
4 | {
5 | processFunc = &PitchShiftWrapper::processSampleInternal;
6 | }
7 |
8 | void PitchShiftWrapper::prepare (const dsp::ProcessSpec& spec)
9 | {
10 | jassert (spec.numChannels == 1);
11 | shifter.prepare (spec);
12 | pitchStSmooth.reset (spec.sampleRate, 0.05);
13 | crossfade.reset (spec.sampleRate, 0.05);
14 | }
15 |
16 | void PitchShiftWrapper::reset()
17 | {
18 | shifter.reset();
19 | }
20 |
21 | void PitchShiftWrapper::setPitchSemitones (float pitch, bool force)
22 | {
23 | if (force)
24 | {
25 | shifter.setShiftSemitones (pitch);
26 | pitchStSmooth.setCurrentAndTargetValue (pitch);
27 | }
28 | else
29 | {
30 | pitchStSmooth.setTargetValue (pitch);
31 | }
32 |
33 | const auto current = pitchStSmooth.getCurrentValue();
34 | const auto target = pitchStSmooth.getTargetValue();
35 | if (target == 0.0f && current == 0.0f) // bypass
36 | {
37 | shifter.reset();
38 | crossfade.setCurrentAndTargetValue (0.0f);
39 | processFunc = &PitchShiftWrapper::processSampleInternalBypass;
40 | }
41 | else if (target == 0.0f && current != 0.0f) // fade to bypass
42 | {
43 | crossfade.setTargetValue (0.0f);
44 | processFunc = &PitchShiftWrapper::processSampleInternalFade;
45 | }
46 | else if (target != 0.0f && current == 0.0f) // fade from bypass
47 | {
48 | crossfade.setTargetValue (1.0f);
49 | processFunc = &PitchShiftWrapper::processSampleInternalFade;
50 | }
51 | else if (pitchStSmooth.isSmoothing()) // smooth pitch change
52 | {
53 | crossfade.setCurrentAndTargetValue (1.0f);
54 | processFunc = &PitchShiftWrapper::processSampleInternalSmooth;
55 | }
56 | else // normal processing!
57 | {
58 | crossfade.setCurrentAndTargetValue (1.0f);
59 | processFunc = &PitchShiftWrapper::processSampleInternal;
60 | }
61 | }
62 |
63 | inline float PitchShiftWrapper::processSampleInternal (float x) noexcept
64 | {
65 | return shifter.processSample (0, x);
66 | }
67 |
68 | inline float PitchShiftWrapper::processSampleInternalSmooth (float x) noexcept
69 | {
70 | shifter.setShiftSemitones (pitchStSmooth.getNextValue());
71 | return shifter.processSample (0, x);
72 | }
73 |
74 | inline float PitchShiftWrapper::processSampleInternalFade (float x) noexcept
75 | {
76 | shifter.setShiftSemitones (pitchStSmooth.getNextValue());
77 | auto wetGain = crossfade.getNextValue();
78 | return wetGain * shifter.processSample (0, x) + (1.0f - wetGain) * x;
79 | }
80 |
81 | inline float PitchShiftWrapper::processSampleInternalBypass (float x) noexcept
82 | {
83 | return x;
84 | }
85 |
--------------------------------------------------------------------------------
/src/dsp/Delay/PitchShiftWrapper.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | /** Wrapper for chowdsp::PitchShift */
6 | class PitchShiftWrapper
7 | {
8 | public:
9 | PitchShiftWrapper();
10 |
11 | void prepare (const dsp::ProcessSpec& spec);
12 | void reset();
13 | void setPitchSemitones (float pitch, bool force = false);
14 |
15 | inline float processSample (float x) noexcept
16 | {
17 | return (this->*processFunc) (x);
18 | }
19 |
20 | private:
21 | inline float processSampleInternal (float x) noexcept;
22 | inline float processSampleInternalSmooth (float x) noexcept;
23 | inline float processSampleInternalFade (float x) noexcept;
24 | inline float processSampleInternalBypass (float x) noexcept;
25 |
26 | using Processor = float (PitchShiftWrapper::*) (float);
27 | Processor processFunc;
28 |
29 | SmoothedValue pitchStSmooth;
30 | SmoothedValue crossfade;
31 | chowdsp::PitchShifter shifter { 4096, 256 };
32 |
33 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PitchShiftWrapper)
34 | };
35 |
--------------------------------------------------------------------------------
/src/dsp/Delay/Reverser.cpp:
--------------------------------------------------------------------------------
1 | #include "Reverser.h"
2 |
3 | namespace
4 | {
5 | int nearestEvenInt (int to)
6 | {
7 | return (to % 2 == 0) ? to : (to + 1);
8 | }
9 | } // namespace
10 |
11 | void Reverser::prepare (const dsp::ProcessSpec& spec)
12 | {
13 | fs = (float) spec.sampleRate;
14 | maxWindowSize = (int) fs; // 1 second
15 |
16 | windowBuffer.setSize (1, maxWindowSize);
17 | window = windowBuffer.getWritePointer (0);
18 |
19 | reverseBuffer.setSize (1, maxWindowSize * 4);
20 | revBuffPtr = reverseBuffer.getWritePointer (0);
21 |
22 | reset();
23 | }
24 |
25 | void Reverser::reset()
26 | {
27 | reverseBuffer.clear();
28 | windowSize = -1; // this will get reset next time setReverseTime is called
29 | }
30 |
31 | void Reverser::setReverseTime (float revTimeMs)
32 | {
33 | auto newWindowSize = nearestEvenInt (int (revTimeMs * fs / 1000.0f));
34 | newWindowSize = newWindowSize > 0 ? jmax (newWindowSize, 8) : 0;
35 | if (windowSize == newWindowSize)
36 | return;
37 |
38 | if (newWindowSize == 0)
39 | {
40 | windowSize = 0;
41 | reverseBuffer.clear();
42 | bypass = true;
43 | return;
44 | }
45 |
46 | bypass = false;
47 |
48 | windowSize = newWindowSize;
49 | doubleWindowSize = 2 * windowSize;
50 | halfWindowSize = windowSize / 2;
51 |
52 | bufferWrite = 0;
53 | bufferRead1 = doubleWindowSize;
54 | bufferRead2 = bufferRead1 + halfWindowSize;
55 |
56 | dsp::WindowingFunction::fillWindowingTables (window,
57 | (size_t) windowSize,
58 | dsp::WindowingFunction::hann,
59 | false);
60 | }
61 |
--------------------------------------------------------------------------------
/src/dsp/Delay/Reverser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | /**
6 | * Time reversal effect, based on this paper: https://ccrma.stanford.edu/~hskim08/files/pdf/DAFx14_sttr.pdf
7 | */
8 | class Reverser
9 | {
10 | public:
11 | Reverser() = default;
12 |
13 | void prepare (const dsp::ProcessSpec& spec);
14 | void reset();
15 | void setReverseTime (float revTimeMs);
16 |
17 | inline float processSample (float x)
18 | {
19 | if (bypass)
20 | return x;
21 |
22 | // read output sample from reverse buffer
23 | float y = 0.0f;
24 | y += revBuffPtr[bufferRead1] * window[bufferRead1 % windowSize];
25 | y += revBuffPtr[bufferRead2] * window[bufferRead2 % windowSize];
26 |
27 | // update read pointers
28 | bufferRead1--;
29 | bufferRead1 = bufferRead1 > 0 ? bufferRead1 : doubleWindowSize;
30 |
31 | bufferRead2--;
32 | bufferRead2 = bufferRead2 > halfWindowSize ? bufferRead2 : doubleWindowSize + halfWindowSize;
33 |
34 | // push new sample into reverse buffer
35 | revBuffPtr[bufferWrite] = x;
36 | revBuffPtr[bufferWrite + doubleWindowSize] = x;
37 |
38 | // update write pointers
39 | bufferWrite++;
40 | bufferWrite = bufferWrite >= doubleWindowSize ? 0 : bufferWrite;
41 |
42 | return y;
43 | }
44 |
45 | private:
46 | bool bypass = false;
47 |
48 | AudioBuffer reverseBuffer;
49 | float* revBuffPtr = nullptr;
50 |
51 | float fs = 48000.0f;
52 | int maxWindowSize = 8192;
53 |
54 | AudioBuffer windowBuffer;
55 | float* window = nullptr;
56 |
57 | int bufferWrite, bufferRead1, bufferRead2;
58 | int windowSize, halfWindowSize, doubleWindowSize;
59 |
60 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverser)
61 | };
62 |
--------------------------------------------------------------------------------
/src/dsp/Delay/TempoSyncUtils.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../Parameters/ParamHelpers.h"
4 |
5 | /**
6 | * Utils for converting delay parameters
7 | * to tempo-synced delay times.
8 | */
9 | namespace TempoSyncUtils
10 | {
11 | /** A simple struct containing a rhythmic delay length */
12 | struct DelayRhythm
13 | {
14 | constexpr DelayRhythm (const std::string_view& name, const std::string_view& label, double tempoFactor) : name (name),
15 | label (label),
16 | tempoFactor (tempoFactor) {}
17 |
18 | inline String getLabel() const { return String (static_cast (label)); }
19 |
20 | std::string_view name;
21 | std::string_view label;
22 | double tempoFactor;
23 | };
24 |
25 | static constexpr std::array rhythms {
26 | DelayRhythm ("Thirty-Second", "1/32", 0.125),
27 | DelayRhythm ("Thirty-Second Dot", "1/32 D", 0.125 * 1.5),
28 | DelayRhythm ("Sixteenth Triplet", "1/16 T", 0.5 / 3.0),
29 | DelayRhythm ("Sixteenth", "1/16", 0.25),
30 | DelayRhythm ("Sixteenth Dot", "1/16 D", 0.25 * 1.5),
31 | DelayRhythm ("Eigth Triplet", "1/8 T", 1.0 / 3.0),
32 | DelayRhythm ("Eigth", "1/8", 0.5),
33 | DelayRhythm ("Eigth Dot", "1/8 D", 0.5 * 1.5),
34 | DelayRhythm ("Quarter Triplet", "1/4 T", 2.0 / 3.0),
35 | DelayRhythm ("Quarter", "1/4", 1.0),
36 | DelayRhythm ("Quarter Dot", "1/4 D", 1.0 * 1.5),
37 | DelayRhythm ("Half Triplet", "1/2 T", 4.0 / 3.0),
38 | DelayRhythm ("Half", "1/2", 2.0),
39 | DelayRhythm ("Half Dot", "1/2 D", 2.0 * 1.5),
40 | DelayRhythm ("Whole Triplet", "1/1 T", 8.0 / 3.0),
41 | DelayRhythm ("Whole", "1/1", 4.0),
42 | DelayRhythm ("Whole Dot", "1/1 D", 4.0 * 1.5),
43 | DelayRhythm ("Two Whole Triplet", "2/1 T", 16.0 / 3.0),
44 | DelayRhythm ("Two Whole", "2/1", 8.0),
45 | };
46 |
47 | /** Return time in seconds for rhythm and tempo */
48 | static inline double getTimeForRythm (double tempoBPM, const DelayRhythm& rhythm)
49 | {
50 | const auto beatLength = 1.0 / (tempoBPM / 60.0);
51 | return beatLength * rhythm.tempoFactor;
52 | }
53 |
54 | /** Returns the corresponding rhythm for a 0-1 param value */
55 | static inline const DelayRhythm& getRhythmForParam (float param01)
56 | {
57 | auto idx = static_cast ((rhythms.size() - 1) * std::pow (param01, 1.5f));
58 | return rhythms[idx];
59 | }
60 |
61 | /** Sine-wave oscillator synced to the song tempo */
62 | class SyncedLFO : public chowdsp::SineWave
63 | {
64 | public:
65 | SyncedLFO() {}
66 | virtual ~SyncedLFO() {}
67 |
68 | // Sets the correct oscillator frequency for a given tempo
69 | void setFreqSynced (const Parameter* freqParam, float tempoBPM)
70 | {
71 | const auto& rhythm = getRhythmForParam (freqParam->convertTo0to1 (*freqParam));
72 | float freqValue = 1.0f / (float) getTimeForRythm ((double) tempoBPM, rhythm);
73 | setFrequency (freqValue);
74 | }
75 |
76 | // resets the LFO when the DAW starts playing
77 | void setPlayHead (AudioPlayHead* playhead)
78 | {
79 | if (playhead == nullptr)
80 | return;
81 |
82 | AudioPlayHead::CurrentPositionInfo info;
83 | playhead->getCurrentPosition (info);
84 |
85 | if (info.isPlaying && ! wasPlaying)
86 | reset();
87 |
88 | wasPlaying = info.isPlaying;
89 | }
90 |
91 | private:
92 | bool wasPlaying = false;
93 |
94 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SyncedLFO)
95 | };
96 |
97 | } // namespace TempoSyncUtils
98 |
--------------------------------------------------------------------------------
/src/dsp/Delay/VariableDelay.cpp:
--------------------------------------------------------------------------------
1 | #include "VariableDelay.h"
2 |
3 | VariableDelay::VariableDelay (size_t size) : l0Delay (static_cast (size)),
4 | l1Delay (static_cast (size)),
5 | l3Delay (static_cast (size)),
6 | l5Delay (static_cast (size)),
7 | sinc16Delay (static_cast (size)),
8 | sinc32Delay (static_cast (size))
9 | {
10 | }
11 |
12 | void VariableDelay::setDelayForce (float newDelayInSamples) noexcept
13 | {
14 | delaySmooth.setCurrentAndTargetValue (newDelayInSamples);
15 |
16 | for (auto* delay : delays)
17 | delay->setDelay (newDelayInSamples);
18 | }
19 |
20 | void VariableDelay::setDelayType (DelayType newType)
21 | {
22 | auto oldType = type;
23 |
24 | // copy state and parameters first...
25 | delays[newType]->setDelay (delays[oldType]->getDelay());
26 | delays[newType]->copyState (*delays[oldType]);
27 |
28 | // then set new type
29 | type = newType;
30 |
31 | switch (type)
32 | {
33 | case BBDShort:
34 | makeupGain = 0.85f;
35 | delays[type]->reset();
36 | break;
37 | case BBDLong:
38 | makeupGain = 0.65f;
39 | delays[type]->reset();
40 | break;
41 | default:
42 | makeupGain = 1.0f;
43 | };
44 | }
45 |
46 | void VariableDelay::delayBlockStart() noexcept
47 | {
48 | delays[type]->setDelay (delaySmooth.getCurrentValue());
49 |
50 | if (type == BBDShort)
51 | bbdShortDelay.setFilterFreq (2000.0f);
52 |
53 | if (type == BBDLong)
54 | bbdLongDelay.setFilterFreq (9000.0f);
55 | }
56 |
57 | void VariableDelay::prepare (const juce::dsp::ProcessSpec& spec)
58 | {
59 | delaySmooth.reset (spec.sampleRate, 0.1);
60 |
61 | for (auto* delay : delays)
62 | delay->prepare (spec);
63 | }
64 |
65 | void VariableDelay::reset()
66 | {
67 | for (auto* delay : delays)
68 | delay->reset();
69 | }
70 |
--------------------------------------------------------------------------------
/src/dsp/Delay/VariableDelay.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | /**
6 | * Delay class with variable interpolation type
7 | */
8 | class VariableDelay
9 | {
10 | public:
11 | VariableDelay (size_t size);
12 |
13 | enum DelayType
14 | {
15 | NoInterp,
16 | LinearInterp,
17 | ThirdInterp,
18 | FifthInterp,
19 | Sinc16,
20 | Sinc32,
21 | BBDShort,
22 | BBDLong,
23 | BBDAlien,
24 | };
25 |
26 | // manage parameters
27 | inline void setDelay (float newDelayInSamples) noexcept
28 | {
29 | delaySmooth.setTargetValue (newDelayInSamples);
30 | }
31 |
32 | // call this at the start of processinf a block of data
33 | void delayBlockStart() noexcept;
34 |
35 | void setDelayForce (float newDelayInSamples) noexcept;
36 | void setDelayType (DelayType newType);
37 | bool isDelaySmoothing() const { return delaySmooth.isSmoothing(); }
38 |
39 | // delegate everything else to dsp::DelayLine
40 | void prepare (const juce::dsp::ProcessSpec& spec);
41 | void reset();
42 |
43 | inline void pushSample (int channel, float sample)
44 | {
45 | delays[type]->pushSample (channel, sample);
46 | }
47 |
48 | inline void pushSampleSmooth (int channel, float sample)
49 | {
50 | delays[type]->setDelay (delaySmooth.getNextValue());
51 | delays[type]->pushSample (channel, sample);
52 | }
53 |
54 | inline float popSample (int channel)
55 | {
56 | return makeupGain * delays[type]->popSample (channel);
57 | }
58 |
59 | private:
60 | chowdsp::DelayLine l0Delay;
61 | chowdsp::DelayLine l1Delay;
62 | chowdsp::DelayLine l3Delay;
63 | chowdsp::DelayLine l5Delay;
64 | chowdsp::DelayLine> sinc16Delay;
65 | chowdsp::DelayLine> sinc32Delay;
66 | chowdsp::BBD::BBDDelayWrapper<4096> bbdShortDelay;
67 | chowdsp::BBD::BBDDelayWrapper<16384> bbdLongDelay;
68 | chowdsp::BBD::BBDDelayWrapper<8192, true> bbdAlienDelay;
69 |
70 | std::array*, 9> delays { &l0Delay, &l1Delay, &l3Delay, &l5Delay, &sinc16Delay, &sinc32Delay, &bbdShortDelay, &bbdLongDelay, &bbdAlienDelay };
71 | DelayType type = ThirdInterp;
72 |
73 | SmoothedValue delaySmooth;
74 | float makeupGain = 1.0f;
75 |
76 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VariableDelay)
77 | };
78 |
--------------------------------------------------------------------------------
/src/dsp/Distortion/Distortion.cpp:
--------------------------------------------------------------------------------
1 | #include "Distortion.h"
2 |
3 | Distortion::Distortion()
4 | {
5 | gain.setCurrentAndTargetValue (1.0f);
6 | }
7 |
8 | void Distortion::prepare (const dsp::ProcessSpec& spec)
9 | {
10 | adaa.prepare();
11 | gain.reset (spec.sampleRate, 0.05);
12 | }
13 |
14 | void Distortion::reset()
15 | {
16 | adaa.prepare();
17 | gain.setCurrentAndTargetValue (gain.getTargetValue());
18 | }
19 |
20 | void ADAA2::prepare()
21 | {
22 | tables->prepare();
23 |
24 | x1 = 0.0;
25 | x2 = 0.0;
26 | ad2_x1 = 0.0;
27 | d2 = 0.0;
28 | }
29 |
--------------------------------------------------------------------------------
/src/dsp/Distortion/Distortion.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | namespace ADAAConsts
6 | {
7 | constexpr double TOL = 1.0e-4;
8 | }
9 |
10 | /**
11 | * Class to implement 2nd-order anti-derivative anti-aliasing
12 | */
13 | class ADAA2
14 | {
15 | public:
16 | ADAA2() = default;
17 |
18 | void prepare();
19 |
20 | inline double process (double x) noexcept
21 | {
22 | bool illCondition = std::abs (x - x2) < ADAAConsts::TOL;
23 | double d1 = calcD1 (x);
24 |
25 | double y = illCondition ? fallback (x) : (2.0 / (x - x2)) * (d1 - d2);
26 |
27 | // update state
28 | d2 = d1;
29 | x2 = x1;
30 | x1 = x;
31 |
32 | return y;
33 | }
34 |
35 | private:
36 | inline double calcD1 (double x0) noexcept
37 | {
38 | bool illCondition = std::abs (x0 - x1) < ADAAConsts::TOL;
39 | double ad2_x0 = tables->lut_AD2.processSample (x0);
40 |
41 | double y = illCondition ? tables->lut_AD1.processSample (0.5 * (x0 + x1)) : (ad2_x0 - ad2_x1) / (x0 - x1);
42 |
43 | ad2_x1 = ad2_x0;
44 | return y;
45 | }
46 |
47 | inline double fallback (double x) const noexcept
48 | {
49 | double xBar = 0.5 * (x + x2);
50 | double delta = xBar - x;
51 |
52 | bool illCondition = std::abs (delta) < ADAAConsts::TOL;
53 | return illCondition ? tables->lut.processSample (0.5 * (xBar + x)) : (2.0 / delta) * (tables->lut_AD1.processSample (xBar) + (tables->lut_AD2.processSample (x) - tables->lut_AD2.processSample (xBar)) / delta);
54 | }
55 |
56 | double x1 = 0.0;
57 | double x2 = 0.0;
58 | double ad2_x1 = 0.0;
59 | double d2 = 0.0;
60 |
61 | SharedResourcePointer tables;
62 |
63 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ADAA2)
64 | };
65 |
66 | /**
67 | * Class to implement tanh distortion
68 | */
69 | class Distortion
70 | {
71 | public:
72 | Distortion();
73 |
74 | void setGain (float newGain) { gain.setTargetValue (newGain); }
75 | void prepare (const dsp::ProcessSpec& spec);
76 | void reset();
77 |
78 | inline float processSample (float x)
79 | {
80 | float curGain = gain.getNextValue();
81 |
82 | if (curGain < 0.6f)
83 | return x;
84 |
85 | return (float) adaa.process ((double) (curGain * x)) / curGain;
86 | }
87 |
88 | private:
89 | ADAA2 adaa;
90 | SmoothedValue gain;
91 |
92 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Distortion)
93 | };
94 |
--------------------------------------------------------------------------------
/src/dsp/Distortion/LookupTables.cpp:
--------------------------------------------------------------------------------
1 | #include "LookupTables.h"
2 | #include
3 |
4 | namespace
5 | {
6 | constexpr int N = 1 << 17;
7 | constexpr double minVal = -10.0;
8 | constexpr double maxVal = 10.0;
9 | } // namespace
10 |
11 | inline double func (double x) noexcept
12 | {
13 | return std::tanh (x);
14 | }
15 |
16 | /** First antiderivative of hard clipper */
17 | inline double func_AD1 (double x) noexcept
18 | {
19 | return std::log (std::cosh (x));
20 | }
21 |
22 | /** Second antiderivative of hard clipper */
23 | inline double func_AD2 (double x) noexcept
24 | {
25 | const auto expVal = std::exp (-2 * x);
26 | return 0.5 * ((double) polylogarithm::Li2 (-expVal) - x * (x + 2.0 * std::log (expVal + 1.) - 2.0 * std::log (std::cosh (x))))
27 | + (std::pow (MathConstants::pi, 2) / 24.0);
28 | }
29 |
30 | void LookupTables::prepare()
31 | {
32 | for (auto& future : futures)
33 | {
34 | future.wait();
35 | }
36 |
37 | futures.clear();
38 | }
39 |
40 | LookupTables::LookupTables()
41 | {
42 | // loading the lookup tables takes a while, so let's do it asynchronously
43 | auto makeLUTAsync = [=] (auto lutInit) {
44 | futures.push_back (std::async (std::launch::async, lutInit));
45 | };
46 |
47 | makeLUTAsync ([=] { lut.initialise ([=] (double x) { return func (x); }, minVal, maxVal, N); });
48 | makeLUTAsync ([=] { lut_AD1.initialise ([=] (double x) { return func_AD1 (x); }, 2 * minVal, 2 * maxVal, 4 * N); });
49 | makeLUTAsync ([=] { lut_AD2.initialise ([=] (double x) { return func_AD2 (x); }, 4 * minVal, 4 * maxVal, 16 * N); });
50 | }
51 |
--------------------------------------------------------------------------------
/src/dsp/Distortion/LookupTables.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | /**
6 | * Shared resource class to store lookup tables
7 | * for nonlinear functions
8 | */
9 | class LookupTables
10 | {
11 | public:
12 | LookupTables();
13 |
14 | void prepare();
15 |
16 | dsp::LookupTableTransform lut; // tanh
17 | dsp::LookupTableTransform lut_AD1; // anti-derivative of tanh
18 | dsp::LookupTableTransform lut_AD2; // 2nd anti-derivative of tanh
19 |
20 | private:
21 | std::vector> futures;
22 |
23 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookupTables)
24 | };
25 |
--------------------------------------------------------------------------------
/src/dsp/InputNode.cpp:
--------------------------------------------------------------------------------
1 | #include "InputNode.h"
2 | #include "../gui/MatrixView/InputNodeComponent.h"
3 |
4 | XmlElement* InputNode::saveXml()
5 | {
6 | std::unique_ptr xml = std::make_unique ("input_node");
7 | xml->addChildElement (DBaseNode::saveXml());
8 |
9 | return xml.release();
10 | }
11 |
12 | void InputNode::loadXml (XmlElement* xmlState)
13 | {
14 | if (xmlState == nullptr)
15 | return;
16 |
17 | if (xmlState->hasTagName ("input_node"))
18 | {
19 | if (auto* childrenXml = xmlState->getChildByName ("children"))
20 | DBaseNode::loadXml (childrenXml);
21 | }
22 | }
23 |
24 | std::unique_ptr InputNode::createNodeEditor (GraphView* view)
25 | {
26 | auto editorPtr = std::make_unique (*this, view);
27 | editor = editorPtr.get();
28 | return std::move (editorPtr);
29 | }
30 |
--------------------------------------------------------------------------------
/src/dsp/InputNode.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "DelayNode.h"
4 |
5 | /**
6 | * Class for audio Input Node
7 | */
8 | class InputNode : public DBaseNode
9 | {
10 | public:
11 | InputNode() = default;
12 |
13 | XmlElement* saveXml() override;
14 | void loadXml (XmlElement*) override;
15 |
16 | std::unique_ptr createNodeEditor (GraphView*) override;
17 |
18 | private:
19 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InputNode)
20 | };
21 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/BaseController.cpp:
--------------------------------------------------------------------------------
1 | #include "BaseController.h"
2 | #include "../../NodeManager.h"
3 |
4 | BaseController::BaseController (AudioProcessorValueTreeState& vts,
5 | std::array* nodes,
6 | StringArray paramsToListenFor) : vts (vts),
7 | nodes (nodes),
8 | paramsToListenFor (paramsToListenFor)
9 | {
10 | for (auto& node : *nodes)
11 | {
12 | node.addNodeListener (this);
13 | NodeManager::doForNodes (&node, [=] (DelayNode* n) { n->addNodeListener (this); });
14 | }
15 |
16 | for (const auto& param : paramsToListenFor)
17 | vts.addParameterListener (param, this);
18 | }
19 |
20 | BaseController::~BaseController()
21 | {
22 | NodeManager::doForNodes (nodes, [=] (DelayNode* n) { n->removeNodeListener (this); });
23 |
24 | for (const auto& param : paramsToListenFor)
25 | vts.removeParameterListener (param, this);
26 | }
27 |
28 | void BaseController::nodeAdded (DelayNode* newNode)
29 | {
30 | newNode->addNodeListener (this);
31 | newNodeAdded (newNode);
32 | }
33 |
34 | void BaseController::nodeRemoved (DelayNode* nodeToRemove)
35 | {
36 | newNodeRemoved (nodeToRemove);
37 | nodeToRemove->removeNodeListener (this);
38 | }
39 |
40 | void BaseController::nodeIndexChanged (DelayNode* node, int oldIndex, int newIndex)
41 | {
42 | nodeIndexHasChanged (node, oldIndex, newIndex);
43 | }
44 |
45 | void BaseController::doForNodes (std::function nodeFunc)
46 | {
47 | NodeManager::doForNodes (nodes, nodeFunc);
48 | }
49 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/BaseController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../InputNode.h"
4 |
5 | /**
6 | * Base class for some parameter controller that needs
7 | * access to all active nodes
8 | */
9 | class BaseController : private AudioProcessorValueTreeState::Listener,
10 | private DBaseNode::Listener
11 | {
12 | public:
13 | using Parameters = std::vector>;
14 |
15 | BaseController (AudioProcessorValueTreeState& vts,
16 | std::array* nodes,
17 | StringArray paramsToListenFor);
18 | ~BaseController() override;
19 |
20 | protected:
21 | virtual void newNodeAdded (DelayNode*) {}
22 | virtual void newNodeRemoved (DelayNode*) {}
23 | virtual void nodeIndexHasChanged (DelayNode* /*node*/, int /*oldIndex*/, int /*newIndex*/) {}
24 | void doForNodes (std::function nodeFunc);
25 |
26 | private:
27 | void nodeAdded (DelayNode* newNode) override;
28 | void nodeRemoved (DelayNode* nodeToRemove) override;
29 | void nodeIndexChanged (DelayNode* node, int oldIndex, int newIndex) override;
30 |
31 | AudioProcessorValueTreeState& vts;
32 | std::array* nodes;
33 | StringArray paramsToListenFor;
34 |
35 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BaseController)
36 | };
37 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/DelayTypeControl.cpp:
--------------------------------------------------------------------------------
1 | #include "DelayTypeControl.h"
2 | #include "../../NodeManager.h"
3 |
4 | namespace
5 | {
6 | const String delayTypeTag = "delay_type";
7 | }
8 |
9 | static inline VariableDelay::DelayType getDelayType (float param)
10 | {
11 | return static_cast (int (param));
12 | }
13 |
14 | DelayTypeControl::DelayTypeControl (AudioProcessorValueTreeState& vts, std::array* nodes, StateManager& stateMgr) : BaseController (vts, nodes, { delayTypeTag }),
15 | stateManager (stateMgr)
16 | {
17 | delayTypeParam = vts.getRawParameterValue (delayTypeTag);
18 | parameterChanged (delayTypeTag, delayTypeParam->load());
19 | }
20 |
21 | void DelayTypeControl::addParameters (Parameters& params)
22 | {
23 | chowdsp::ParamUtils::emplace_param (params,
24 | delayTypeTag,
25 | "Delay Type",
26 | StringArray ({ "Glitch", "Rough", "Smooth", "Ultra Smooth", "Liquid", "Super Liquid", "Lo-Fi", "Analog", "Alien" }),
27 | 2);
28 | }
29 |
30 | void DelayTypeControl::parameterChanged (const String& paramID, float newValue)
31 | {
32 | if (stateManager.getIsLoading())
33 | {
34 | // StateManager is currently loading a new state.
35 | // Let's wait until it's done and call again...
36 | Thread::sleep (2);
37 | MessageManager::callAsync ([=] { parameterChanged (paramID, newValue); });
38 | }
39 | else
40 | {
41 | const SpinLock::ScopedTryLockType stateLoadTryLock (stateManager.getStateLoadLock());
42 |
43 | if (stateLoadTryLock.isLocked())
44 | {
45 | // We're sure it's safe to set delay types now!
46 | auto type = getDelayType (newValue);
47 | doForNodes ([=] (DelayNode* n) { n->setDelayType (type); });
48 | }
49 | else
50 | {
51 | // Can't reset delay types while processing audio!
52 | // Let's wait and try again...
53 | Thread::sleep (2);
54 | MessageManager::callAsync ([=] { parameterChanged (paramID, newValue); });
55 | }
56 | }
57 | }
58 |
59 | void DelayTypeControl::newNodeAdded (DelayNode* newNode)
60 | {
61 | newNode->setDelayType (getDelayType (delayTypeParam->load()));
62 | }
63 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/DelayTypeControl.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../Delay/VariableDelay.h"
4 | #include "BaseController.h"
5 | #include "state/StateManager.h"
6 |
7 | /**
8 | * Utility class to manage delay interpolation types
9 | */
10 | class DelayTypeControl : private BaseController
11 | {
12 | public:
13 | DelayTypeControl (AudioProcessorValueTreeState& vts, std::array* nodes, StateManager& stateMgr);
14 |
15 | static void addParameters (Parameters& params);
16 | void newNodeAdded (DelayNode* newNode) override;
17 |
18 | void parameterChanged (const String&, float newValue) override;
19 | std::atomic* getParameter() const noexcept { return delayTypeParam; }
20 |
21 | private:
22 | std::atomic* delayTypeParam = nullptr;
23 | StateManager& stateManager;
24 |
25 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DelayTypeControl)
26 | };
27 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/HostParamControl.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BaseController.h"
4 |
5 | class HostParamControl : public BaseController
6 | {
7 | public:
8 | HostParamControl (AudioProcessorValueTreeState& vts, std::array* nodes);
9 |
10 | static void addParameters (Parameters& params);
11 | void newNodeAdded (DelayNode* newNode) override;
12 | void newNodeRemoved (DelayNode* newNode) override;
13 |
14 | void parameterChanged (const String&, float newValue) override;
15 |
16 | void addParameterMenus (PopupMenu& parentMenu, const String& paramID, DelayNode* node) override;
17 |
18 | void beginParameterChange (const StringArray& paramIDs, DelayNode* node) override;
19 | void endParameterChange (const StringArray& paramIDs, DelayNode* node) override;
20 | void applyParameterChange (const String& paramID, DelayNode* node, float value01) override;
21 |
22 | void saveExtraNodeState (XmlElement* nodeState, DelayNode* node) override;
23 | void loadExtraNodeState (XmlElement* nodeState, DelayNode* node) override;
24 |
25 | void saveGlobalMap (XmlElement* mapXml);
26 | void loadGlobalMap (XmlElement* mapXml);
27 |
28 | void loadParamList (StringArray& paramList, std::vector>& xCallbacks, size_t mapIdx);
29 | constexpr size_t getNumAssignableParams() const noexcept { return numParams; }
30 |
31 | private:
32 | struct MapInfo
33 | {
34 | DelayNode* nodePtr = nullptr;
35 | String mappedParamID;
36 | };
37 |
38 | using MapIter = std::vector::const_iterator;
39 | using GlobalIter = std::vector::const_iterator;
40 |
41 | bool doForParamMap (DelayNode* node, const String& paramID, size_t mapIdx, std::function found, std::function notFound);
42 | MapIter findMap (DelayNode* node, const String& paramID, size_t mapIdx) const;
43 | void toggleParamMap (DelayNode* node, const String& paramID, size_t mapIdx);
44 |
45 | bool doForGroupMap (const String& paramID, size_t mapIdx, std::function found, std::function notFound);
46 | GlobalIter findMap (const String& paramID, size_t mapIdx) const;
47 | void toggleGroupParamMap (DelayNode* node, const String& paramID, size_t mapIdx);
48 |
49 | bool isParamMapped (size_t mapIdx) const noexcept;
50 | bool doForBothMaps (DelayNode* node, const String& paramID, size_t mapIdx, std::function found, std::function notFound);
51 |
52 | static constexpr size_t numParams = 8;
53 | static inline StringArray paramIDs;
54 |
55 | std::array, numParams> paramGroupMaps;
56 | std::array, numParams> paramControlMaps;
57 | std::array parameterHandles;
58 |
59 | friend class HostControlTest;
60 |
61 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HostParamControl)
62 | };
63 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/InsanityControl.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BaseController.h"
4 |
5 | /**
6 | * Utility class to manage insanity controls
7 | */
8 | class InsanityControl : private BaseController,
9 | private Timer
10 | {
11 | public:
12 | InsanityControl (AudioProcessorValueTreeState& vts, std::array* nodes);
13 |
14 | static void addParameters (Parameters& params);
15 | void newNodeAdded (DelayNode* newNode) override;
16 |
17 | void timerCallback() override;
18 | void parameterChanged (const String& paramID, float newValue) override;
19 | std::atomic* getParameter() const noexcept { return insanityParam; }
20 |
21 | /**
22 | * Resets the delay parameters to what they were
23 | * before insanity was turned on.
24 | */
25 | void resetInsanityState();
26 |
27 | private:
28 | void insanityStarting();
29 | void insanityEnding();
30 |
31 | std::atomic* insanityParam = nullptr;
32 | int timerFreq = 10;
33 |
34 | std::default_random_engine generator;
35 | std::uniform_real_distribution delay_dist { -0.05f, 0.05f };
36 | std::uniform_real_distribution pan_dist { -0.1f, 0.1f };
37 |
38 | float lastInsanity = 0.0f;
39 | std::unordered_map> insanityResetMap;
40 | std::unordered_map> insanityEndingMap;
41 |
42 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InsanityControl)
43 | };
44 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/InsanityLockHelper.cpp:
--------------------------------------------------------------------------------
1 | #include "InsanityLockHelper.h"
2 |
3 | InsanityLockHelper::InsanityLockHelper (std::function onParamLockChange) : onParamLockChange (onParamLockChange)
4 | {
5 | }
6 |
7 | void InsanityLockHelper::setInsanityLock (const String& paramID, bool shouldBeLocked, bool shouldBeReset)
8 | {
9 | jassert (! (shouldBeLocked && shouldBeReset)); // can't have both!
10 |
11 | const auto isLocked = lockedParams.contains (paramID);
12 | const auto isReset = resetParams.contains (paramID);
13 |
14 | if (shouldBeLocked == isLocked && shouldBeReset == isReset) // no change!
15 | return;
16 |
17 | if (! shouldBeLocked && ! shouldBeReset) // unlock
18 | {
19 | lockedParams.removeString (paramID);
20 | resetParams.removeString (paramID);
21 | }
22 | else if (shouldBeLocked)
23 | {
24 | lockedParams.addIfNotAlreadyThere (paramID);
25 | resetParams.removeString (paramID);
26 | }
27 | else if (shouldBeReset)
28 | {
29 | resetParams.addIfNotAlreadyThere (paramID);
30 | lockedParams.removeString (paramID);
31 | }
32 |
33 | onParamLockChange();
34 | }
35 |
36 | void InsanityLockHelper::toggleInsanityLock (const String& paramID)
37 | {
38 | const auto isLocked = lockedParams.contains (paramID);
39 | const auto isReset = resetParams.contains (paramID);
40 |
41 | if (isLocked)
42 | {
43 | lockedParams.removeString (paramID);
44 | resetParams.addIfNotAlreadyThere (paramID);
45 | }
46 | else if (isReset)
47 | {
48 | resetParams.removeString (paramID);
49 | }
50 | else
51 | {
52 | lockedParams.addIfNotAlreadyThere (paramID);
53 | }
54 |
55 | onParamLockChange();
56 | }
57 |
58 | void InsanityLockHelper::createPopupMenu (PopupMenu& parent, const String& paramID)
59 | {
60 | bool isLocked = isParamLocked (paramID);
61 | bool isReset = shouldParamReset (paramID);
62 |
63 | const std::map> settings {
64 | { 1, { "Unlock", ! (isLocked || isReset) } },
65 | { 2, { "Lock", isLocked } },
66 | { 3, { "Reset", isReset } }
67 | };
68 |
69 | PopupMenu insanityLockMenu;
70 | for (const auto& [idx, setting] : settings)
71 | {
72 | const auto& [name, isOn] = setting;
73 | PopupMenu::Item item (name);
74 | item.itemID = idx;
75 | item.setColour (Colour (isOn ? 0xFF21CCA5 : 0xFFFFFFFF));
76 |
77 | bool setLock = name == "Lock";
78 | bool setReset = name == "Reset";
79 | item.action = [=] { setInsanityLock (paramID, setLock, setReset); };
80 |
81 | insanityLockMenu.addItem (item);
82 | }
83 |
84 | parent.addSubMenu ("Insanity Lock:", insanityLockMenu);
85 | }
86 |
87 | void InsanityLockHelper::saveState (XmlElement* xml)
88 | {
89 | xml->setAttribute ("locked", lockedParams.joinIntoString (",") + ",");
90 | xml->setAttribute ("reset", resetParams.joinIntoString (",") + ",");
91 | }
92 |
93 | void InsanityLockHelper::loadState (XmlElement* xml)
94 | {
95 | ParamHelpers::loadStringArray (lockedParams, xml->getStringAttribute ("locked", String()));
96 | ParamHelpers::loadStringArray (resetParams, xml->getStringAttribute ("reset", String()));
97 | }
98 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/InsanityLockHelper.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "ParamHelpers.h"
4 |
5 | /**
6 | * Helper class to manage parameter insanity
7 | * lock and reset functionality
8 | */
9 | class InsanityLockHelper
10 | {
11 | public:
12 | InsanityLockHelper (std::function onParamLockChange);
13 |
14 | void toggleInsanityLock (const String& paramID);
15 | void setInsanityLock (const String& paramID, bool shouldBeLocked, bool shouldBeReset);
16 |
17 | bool isParamLocked (const String& paramID) const noexcept { return lockedParams.contains (paramID); }
18 | bool shouldParamReset (const String& paramID) const noexcept { return resetParams.contains (paramID); }
19 |
20 | void createPopupMenu (PopupMenu& parent, const String& paramID);
21 |
22 | void saveState (XmlElement* xml);
23 | void loadState (XmlElement* xml);
24 |
25 | private:
26 | StringArray lockedParams;
27 | StringArray resetParams;
28 |
29 | std::function onParamLockChange;
30 |
31 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InsanityLockHelper)
32 | };
33 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/NodeParamControl.cpp:
--------------------------------------------------------------------------------
1 | #include "NodeParamControl.h"
2 |
3 | namespace
4 | {
5 | constexpr int maxNumNodes = 50;
6 |
7 | String getForwardParamID (int nodeNum, int paramNum)
8 | {
9 | return "node_" + String (nodeNum) + "_" + ParamHelpers::getParamID (paramNum);
10 | }
11 |
12 | template
13 | void doForParams (FuncType&& func)
14 | {
15 | for (int nodeNum = 0; nodeNum < maxNumNodes; ++nodeNum)
16 | for (int paramNum = 0; paramNum < ParamHelpers::numParams; ++paramNum)
17 | func (nodeNum, paramNum);
18 | }
19 |
20 | template
21 | void doForNodeParams (DelayNode* node, FuncType&& func)
22 | {
23 | const auto nodeIndex = node->getIndex();
24 | if (nodeIndex >= maxNumNodes)
25 | return;
26 |
27 | int paramIndex = 0;
28 | for (auto* param : node->getParameters())
29 | {
30 | auto* paramCast = dynamic_cast (param);
31 | jassert (paramCast != nullptr);
32 |
33 | int forwardIndex = nodeIndex * ParamHelpers::numParams + (paramIndex++);
34 | func (paramCast, nodeIndex, forwardIndex);
35 | }
36 | }
37 |
38 | template
39 | void doForNodeParamIndexes (int nodeIndex, FuncType&& func)
40 | {
41 | if (nodeIndex >= maxNumNodes)
42 | return;
43 |
44 | for (int paramIndex = 0; paramIndex < ParamHelpers::numParams; ++paramIndex)
45 | {
46 | int forwardIndex = nodeIndex * ParamHelpers::numParams + paramIndex;
47 | func (forwardIndex);
48 | }
49 | }
50 | } // namespace
51 |
52 | NodeParamControl::NodeParamControl (AudioProcessorValueTreeState& vts, std::array* nodes, std::unique_ptr& presetMgr)
53 | : BaseController (vts, nodes, StringArray()), presetManager (presetMgr)
54 | {
55 | doForParams ([&] (int nodeNum, int paramNum) {
56 | auto id = getForwardParamID (nodeNum, paramNum);
57 | auto forwardedParam = std::make_unique (id, nullptr, "Blank");
58 |
59 | forwardedParam->setProcessor (&vts.processor);
60 | forwardedParams.add (forwardedParam.get());
61 | forwardedParam->addListener (this);
62 | vts.processor.addParameter (forwardedParam.release());
63 | });
64 | }
65 |
66 | NodeParamControl::~NodeParamControl()
67 | {
68 | for (auto* param : forwardedParams)
69 | param->setParam (nullptr);
70 | }
71 |
72 | void NodeParamControl::newNodeAdded (DelayNode* newNode)
73 | {
74 | doForNodeParams (newNode, [=] (RangedAudioParameter* param, int nodeIndex, int forwardIndex) { forwardedParams[forwardIndex]->setParam (param, "Node " + String (nodeIndex + 1) + ": " + param->getName (1024)); });
75 |
76 | setCurrentPresetDirty();
77 | }
78 |
79 | void NodeParamControl::newNodeRemoved (DelayNode* node)
80 | {
81 | doForNodeParamIndexes (node->getIndex(), [=] (int forwardIndex) { forwardedParams[forwardIndex]->setParam (nullptr); });
82 |
83 | setCurrentPresetDirty();
84 | }
85 |
86 | void NodeParamControl::nodeIndexHasChanged (DelayNode* node, int oldIndex, int /*newIndex*/)
87 | {
88 | doForNodeParamIndexes (oldIndex, [=] (int forwardIndex) { forwardedParams[forwardIndex]->setParam (nullptr); });
89 |
90 | newNodeAdded (node);
91 | }
92 |
93 | void NodeParamControl::parameterValueChanged (int /*paramIndex*/, float /*newValue*/)
94 | {
95 | setCurrentPresetDirty();
96 | }
97 |
98 | void NodeParamControl::setCurrentPresetDirty()
99 | {
100 | if (presetManager == nullptr)
101 | return;
102 |
103 | // Getting a crash here in some DAWs, so let's comment this out for now!
104 | // presetManager->setIsDirty (true);
105 | }
106 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/NodeParamControl.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BaseController.h"
4 | #include "ParamHelpers.h"
5 |
6 | class NodeParamControl : public BaseController,
7 | private AudioProcessorParameter::Listener
8 | {
9 | public:
10 | NodeParamControl (AudioProcessorValueTreeState& vts, std::array* nodes, std::unique_ptr& presetMgr);
11 | ~NodeParamControl() override;
12 |
13 | void newNodeAdded (DelayNode* newNode) override;
14 | void newNodeRemoved (DelayNode* newNode) override;
15 | void nodeIndexHasChanged (DelayNode* node, int oldIndex, int newIndex) override;
16 |
17 | void parameterChanged (const String& /*parameterID*/, float /*newValue*/) override {}
18 |
19 | void parameterValueChanged (int parameterIndex, float newValue) override;
20 | void parameterGestureChanged (int, bool) override {}
21 |
22 | private:
23 | void setCurrentPresetDirty();
24 |
25 | Array forwardedParams;
26 |
27 | std::unique_ptr& presetManager;
28 |
29 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NodeParamControl)
30 | };
31 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/ParamHelpers.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | namespace ParamTags
6 | {
7 | const String delayTag = "DLY";
8 | const String panTag = "PAN";
9 | const String fbTag = "FDBK";
10 | const String gainTag = "GAIN";
11 | const String lpfTag = "LPF";
12 | const String hpfTag = "HPF";
13 | const String distTag = "DIST";
14 | const String pitchTag = "PITCH";
15 | const String diffTag = "DIFF";
16 | const String revTag = "REV";
17 | const String modFreqTag = "MOD_FREQ";
18 | const String delayModTag = "MOD_DELAY";
19 | const String panModTag = "MOD_PAN";
20 | } // namespace ParamTags
21 |
22 | using Parameter = AudioProcessorValueTreeState::Parameter;
23 |
24 | namespace ParamHelpers
25 | {
26 | // parameter constants
27 | constexpr float maxDelay = 1500.0f;
28 | constexpr float centreDelay = 200.0f;
29 | constexpr float maxFeedback = 0.99f;
30 | constexpr float maxGain = 12.0f;
31 | constexpr float minLPF = 200.0f;
32 | constexpr float maxLPF = 20000.0f;
33 | constexpr float minHPF = 20.0f;
34 | constexpr float maxHPF = 2000.0f;
35 | constexpr float maxPitch = 12.0f;
36 | constexpr float minModFreq = 0.0f;
37 | constexpr float maxModFreq = 5.0f;
38 | constexpr int numParams = 13;
39 |
40 | /** Sets a parameter value */
41 | void setParameterValue (Parameter* param, float newVal);
42 |
43 | /** Creates a parameter layout for a DelayNode */
44 | AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
45 |
46 | String delayValToString (float delayVal);
47 | float stringToDelayVal (const String& s);
48 |
49 | String panValToString (float panVal);
50 | float stringToPanVal (const String& s);
51 |
52 | String fbValToString (float fbVal);
53 | float stringToFbVal (const String& s);
54 |
55 | String gainValToString (float gainVal);
56 | float stringToGainVal (const String& s);
57 |
58 | String pitchValToString (float pitchVal);
59 | float stringToPitchVal (const String& s);
60 |
61 | String freqValToString (float freqVal);
62 | float stringToFreqVal (const String& s);
63 |
64 | String percentValToString (float percentVal);
65 | float stringToPercentVal (const String& s);
66 |
67 | using StringToValFunc = float (*) (const String&);
68 | StringToValFunc getStringFuncForParam (const String& paramID);
69 |
70 | /** Creates a tooltip for a given paramID */
71 | String getTooltip (const String& paramID);
72 |
73 | /** Gets a parameter name for a given parameter ID */
74 | String getName (const String& paramID);
75 |
76 | /** Gets the parameter ID for a given index */
77 | String getParamID (int index);
78 |
79 | /** Loads parameters from a comma-separated string into a StringArray */
80 | void loadStringArray (StringArray& array, String string);
81 |
82 | } // namespace ParamHelpers
83 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/RandomiseLockHelper.cpp:
--------------------------------------------------------------------------------
1 | #include "RandomiseLockHelper.h"
2 |
3 | RandomiseLockHelper::RandomiseLockHelper()
4 | {
5 | lockedParams.add (ParamTags::pitchTag);
6 | lockedParams.add (ParamTags::modFreqTag);
7 | }
8 |
9 | void RandomiseLockHelper::getLockedParamsFromParent (const RandomiseLockHelper& parentHelper)
10 | {
11 | // copy locked params from parent
12 | lockedParams = parentHelper.lockedParams;
13 | }
14 |
15 | void RandomiseLockHelper::toggleRandomiseLock (const String& paramID)
16 | {
17 | if (lockedParams.contains (paramID))
18 | lockedParams.removeString (paramID);
19 | else
20 | lockedParams.addIfNotAlreadyThere (paramID);
21 | }
22 |
23 | void RandomiseLockHelper::createPopupMenu (PopupMenu& parent, const String& paramID)
24 | {
25 | bool isLocked = isParamLocked (paramID);
26 |
27 | PopupMenu randLockMenu;
28 | for (int i = 0; i < 2; ++i)
29 | {
30 | PopupMenu::Item item (i == 0 ? "Unlock" : "Lock");
31 | item.itemID = i + 1;
32 | item.setColour (Colour (i == (int) isLocked ? 0xFF21CCA5 : 0xFFFFFFFF));
33 | item.action = [=] { toggleRandomiseLock (paramID); };
34 |
35 | randLockMenu.addItem (item);
36 | }
37 |
38 | parent.addSubMenu ("Randomise Lock:", randLockMenu);
39 | }
40 |
41 | void RandomiseLockHelper::saveState (XmlElement* xml)
42 | {
43 | xml->setAttribute ("rand_lock", lockedParams.joinIntoString (",") + ",");
44 | }
45 |
46 | void RandomiseLockHelper::loadState (XmlElement* xml)
47 | {
48 | ParamHelpers::loadStringArray (lockedParams, xml->getStringAttribute ("rand_lock", String()));
49 | }
50 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/RandomiseLockHelper.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "ParamHelpers.h"
4 |
5 | /**
6 | * Helper class to manage parameter randomise
7 | * lock functionality
8 | */
9 | class RandomiseLockHelper
10 | {
11 | public:
12 | RandomiseLockHelper();
13 |
14 | void getLockedParamsFromParent (const RandomiseLockHelper& parentHelper);
15 |
16 | void toggleRandomiseLock (const String& paramID);
17 | bool isParamLocked (const String& paramID) const noexcept { return lockedParams.contains (paramID); }
18 |
19 | void createPopupMenu (PopupMenu& parent, const String& paramID);
20 |
21 | void saveState (XmlElement* xml);
22 | void loadState (XmlElement* xml);
23 |
24 | private:
25 | StringArray lockedParams;
26 |
27 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RandomiseLockHelper)
28 | };
29 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/SyncControl.cpp:
--------------------------------------------------------------------------------
1 | #include "SyncControl.h"
2 | #include "../../NodeManager.h"
3 |
4 | namespace
5 | {
6 | const String syncTag = "snyc";
7 | }
8 |
9 | SyncControl::SyncControl (AudioProcessorValueTreeState& vts, std::array* nodes) : BaseController (vts, nodes, { syncTag })
10 | {
11 | syncParam = vts.getRawParameterValue (syncTag);
12 | parameterChanged (syncTag, syncParam->load());
13 | }
14 |
15 | void SyncControl::addParameters (Parameters& params)
16 | {
17 | chowdsp::ParamUtils::emplace_param (params, syncTag, "Sync", false);
18 | }
19 |
20 | void SyncControl::setTempo (AudioPlayHead* playhead)
21 | {
22 | double newTempo = 120.0;
23 |
24 | // get tempo from host
25 | if (playhead)
26 | {
27 | AudioPlayHead::CurrentPositionInfo posInfo;
28 | playhead->getCurrentPosition (posInfo);
29 | newTempo = posInfo.bpm > 10.0 ? posInfo.bpm : 120.0;
30 | }
31 |
32 | // load new tempo if needed
33 | if (newTempo != tempo.load())
34 | {
35 | tempo.store (newTempo);
36 | doForNodes ([=] (DelayNode* n) { n->setTempo (newTempo); });
37 | }
38 |
39 | doForNodes ([=] (DelayNode* n) { n->setPlayHead (playhead); });
40 | }
41 |
42 | void SyncControl::newNodeAdded (DelayNode* newNode)
43 | {
44 | newNode->setTempo (tempo.load());
45 | newNode->setDelaySync (static_cast (syncParam->load()));
46 | }
47 |
48 | void SyncControl::parameterChanged (const String&, float newValue)
49 | {
50 | if (MessageManager::getInstance()->isThisTheMessageThread())
51 | {
52 | doForNodes ([=] (DelayNode* n) { n->setDelaySync (static_cast (newValue)); });
53 | }
54 | else
55 | {
56 | MessageManager::callAsync ([=] { doForNodes ([=] (DelayNode* n) { n->setDelaySync (static_cast (newValue)); }); });
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/dsp/Parameters/SyncControl.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BaseController.h"
4 |
5 | /**
6 | * Utility class to manage sync/free delay modes
7 | */
8 | class SyncControl : private BaseController
9 | {
10 | public:
11 | SyncControl (AudioProcessorValueTreeState& vts, std::array* nodes);
12 |
13 | static void addParameters (Parameters& params);
14 | void newNodeAdded (DelayNode* newNode) override;
15 |
16 | void parameterChanged (const String&, float newValue) override;
17 | void setTempo (AudioPlayHead* playhead);
18 |
19 | private:
20 | std::atomic* syncParam = nullptr;
21 | std::atomic tempo = 120.0;
22 |
23 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SyncControl)
24 | };
25 |
--------------------------------------------------------------------------------
/src/dsp/ProcessorBase.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | /** Base class to override (mostly) unused juce::AudioProcessor functions */
6 | class ProcessorBase : public AudioProcessor
7 | {
8 | public:
9 | ProcessorBase (String name = String()) : name (name) {}
10 |
11 | const String getName() const override { return name; }
12 |
13 | double getTailLengthSeconds() const override { return 0.0; }
14 |
15 | void prepareToPlay (double /*sampleRate*/, int /*samplesPerBlock*/) override {}
16 | void processBlock (AudioBuffer&, MidiBuffer&) override {}
17 | void releaseResources() override {}
18 |
19 | bool acceptsMidi() const override { return false; }
20 | bool producesMidi() const override { return false; }
21 |
22 | AudioProcessorEditor* createEditor() override { return nullptr; }
23 | bool hasEditor() const override { return false; }
24 |
25 | int getNumPrograms() override { return 0; }
26 | void setCurrentProgram (int /*index*/) override {}
27 | int getCurrentProgram() override { return 0; }
28 |
29 | const String getProgramName (int /*index*/) override { return {}; }
30 | void changeProgramName (int /*index*/, const String& /*newName*/) override {}
31 |
32 | void getStateInformation (MemoryBlock& /*destData*/) override {}
33 | void setStateInformation (const void* /*data*/, int /*sizeInBytes*/) override {}
34 |
35 | private:
36 | String name;
37 |
38 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProcessorBase)
39 | };
40 |
--------------------------------------------------------------------------------
/src/gui/AutoUpdating.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #if CHOWDSP_AUTO_UPDATE
4 |
5 | #include
6 |
7 | class AutoUpdater : public Component
8 | {
9 | public:
10 | AutoUpdater();
11 | ~AutoUpdater();
12 |
13 | void paint (Graphics& g) override;
14 | void resized() override;
15 |
16 | void showUpdaterScreen (Component* parent);
17 |
18 | bool runAutoUpdateCheck();
19 | void noButtonPressed();
20 | void yesButtonPressed();
21 |
22 | private:
23 | File getUpdateCheckFile();
24 | String getLatestVersion();
25 | String getUpdateFileVersion (const File& updateFile);
26 | bool getUpdateFileYesNo (const File& updateFile);
27 | void editUpdateCheckFile (String version, bool wantsUpdate);
28 | void parentSizeChanged() override;
29 |
30 | chowdsp::VersionUtils::Version newVersion { JucePlugin_VersionString };
31 |
32 | TextButton yesButton { "Yes" };
33 | TextButton noButton { "No" };
34 |
35 | std::future needsUpdate;
36 |
37 | chowdsp::SharedLNFAllocator lnfAllocator;
38 |
39 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoUpdater)
40 | };
41 |
42 | #endif // CHOWDSP_AUTO_UPDATE
43 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/ABComp.cpp:
--------------------------------------------------------------------------------
1 | #include "ABComp.h"
2 |
3 | struct ABLNF : public BottomBarLNF
4 | {
5 | Font getTextButtonFont (TextButton&, int) override { return Font (15.0f).boldened(); }
6 |
7 | void drawButtonText (Graphics& g, TextButton& button, bool, bool) override
8 | {
9 | Font font (getTextButtonFont (button, button.getHeight()));
10 | g.setFont (font);
11 | g.setColour (button.findColour (button.getToggleState() ? TextButton::textColourOnId
12 | : TextButton::textColourOffId)
13 | .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f));
14 |
15 | const int yIndent = jmin (4, button.proportionOfHeight (0.3f));
16 | const int leftIndent = 2;
17 | const int rightIndent = 2;
18 | const int textWidth = button.getWidth() - leftIndent - rightIndent;
19 |
20 | if (textWidth > 0)
21 | g.drawFittedText (button.getButtonText(),
22 | leftIndent,
23 | yIndent,
24 | textWidth,
25 | button.getHeight() - yIndent * 2,
26 | Justification::centred,
27 | 2);
28 | }
29 | };
30 |
31 | ABComp::ABComp (StateManager& manager) : manager (manager)
32 | {
33 | auto setupButton = [=, &manager] (TextButton& button, String name, String text, String tooltip, int index) {
34 | button.setColour (TextButton::buttonColourId, Colours::transparentBlack);
35 | button.setColour (TextButton::buttonOnColourId, Colour (0xFFC954D4).brighter (0.2f));
36 |
37 | button.setName (name);
38 | button.setButtonText (text);
39 | button.setTooltip (tooltip);
40 | button.setClickingTogglesState (false);
41 |
42 | button.setLookAndFeel (lnfAllocator->getLookAndFeel());
43 | addAndMakeVisible (button);
44 |
45 | button.onClick = [=, &manager] {
46 | manager.setCurrentABState (index);
47 | refreshStates();
48 | };
49 | };
50 |
51 | setupButton (aButton, "A/B", "A", "Load state \"A\"", 0);
52 | setupButton (bButton, "A/B", "B", "Load state \"B\"", 1);
53 | setupButton (arrowButton, "A/B", String(), String(), -1);
54 | arrowButton.onClick = std::bind (&StateManager::copyABState, &manager);
55 |
56 | refreshStates();
57 | }
58 |
59 | ABComp::~ABComp()
60 | {
61 | aButton.setLookAndFeel (nullptr);
62 | bButton.setLookAndFeel (nullptr);
63 | arrowButton.setLookAndFeel (nullptr);
64 | }
65 |
66 | void ABComp::refreshStates()
67 | {
68 | auto selectedState = manager.getCurrentABState();
69 |
70 | if (selectedState == 0)
71 | {
72 | aButton.setToggleState (true, dontSendNotification);
73 | bButton.setToggleState (false, dontSendNotification);
74 | arrowButton.setButtonText (">");
75 | arrowButton.setTooltip ("Copy the current state into state B");
76 | }
77 | else if (selectedState == 1)
78 | {
79 | aButton.setToggleState (false, dontSendNotification);
80 | bButton.setToggleState (true, dontSendNotification);
81 | arrowButton.setButtonText ("<");
82 | arrowButton.setTooltip ("Copy the current state into state A");
83 | }
84 |
85 | repaint();
86 | }
87 |
88 | void ABComp::paint (Graphics&)
89 | {
90 | }
91 |
92 | void ABComp::resized()
93 | {
94 | const auto thirdWidth = getWidth() / 3;
95 | aButton.setBounds (0 * thirdWidth, 0, thirdWidth, getHeight());
96 | arrowButton.setBounds (1 * thirdWidth, 0, thirdWidth, getHeight());
97 | bButton.setBounds (2 * thirdWidth, 0, thirdWidth, getHeight());
98 | }
99 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/ABComp.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../../ChowMatrix.h"
4 | #include "../../state/StateManager.h"
5 | #include "BottomBarLNF.h"
6 |
7 | /** GUI component to manage A/B states */
8 | class ABComp : public Component
9 | {
10 | public:
11 | ABComp (StateManager& manager);
12 | ~ABComp();
13 |
14 | void paint (Graphics& g) override;
15 | void resized() override;
16 |
17 | void refreshStates();
18 |
19 | private:
20 | StateManager& manager;
21 |
22 | TextButton aButton;
23 | TextButton bButton;
24 | TextButton arrowButton;
25 |
26 | chowdsp::SharedLNFAllocator lnfAllocator;
27 |
28 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ABComp)
29 | };
30 |
31 | class ABCompItem : public foleys::GuiItem
32 | {
33 | public:
34 | FOLEYS_DECLARE_GUI_FACTORY (ABCompItem)
35 |
36 | ABCompItem (foleys::MagicGUIBuilder& builder, const ValueTree& node) : foleys::GuiItem (builder, node)
37 | {
38 | if (auto* proc = dynamic_cast (builder.getMagicState().getProcessor()))
39 | {
40 | comp = std::make_unique (proc->getStateManager());
41 | addAndMakeVisible (comp.get());
42 | }
43 | }
44 |
45 | void update() override
46 | {
47 | }
48 |
49 | Component* getWrappedComponent() override
50 | {
51 | return comp.get();
52 | }
53 |
54 | private:
55 | std::unique_ptr comp;
56 |
57 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ABCompItem)
58 | };
59 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/BottomBarLNF.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | /** Look and feel for the bottom bar section */
6 | class BottomBarLNF : public chowdsp::ChowLNF
7 | {
8 | public:
9 | BottomBarLNF();
10 | virtual ~BottomBarLNF() = default;
11 |
12 | protected:
13 | void drawRotarySlider (Graphics& g, int, int, int, int, float, const float, const float, Slider& slider) override;
14 |
15 | Slider::SliderLayout getSliderLayout (Slider& slider) override;
16 | Label* createSliderTextBox (Slider& slider) override;
17 |
18 | void drawComboBox (Graphics& g, int width, int height, bool, int, int, int, int, ComboBox& box) override;
19 | void positionComboBoxText (ComboBox& box, Label& label) override;
20 |
21 | void drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override;
22 |
23 | Font getTextButtonFont (TextButton&, int buttonHeight) override
24 | {
25 | float fontHeight = jmin (17.0f, (float) buttonHeight * 0.85f);
26 | return Font { fontHeight, Font::bold };
27 | }
28 |
29 | void drawPopupMenuItem (Graphics& g, const Rectangle& area, const bool isSeparator, const bool isActive, const bool isHighlighted, const bool /*isTicked*/, const bool hasSubMenu, const String& text, const String& shortcutKeyText, const Drawable* icon, const Colour* const textColourToUse) override
30 | {
31 | LookAndFeel_V4::drawPopupMenuItem (g, area, isSeparator, isActive, isHighlighted, false /*isTicked*/, hasSubMenu, text, shortcutKeyText, icon, textColourToUse);
32 | }
33 |
34 | void drawPopupMenuBackground (Graphics& g, int width, int height) override
35 | {
36 | g.fillAll (findColour (PopupMenu::backgroundColourId));
37 | ignoreUnused (width, height);
38 | }
39 | };
40 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/HostControlMenu.cpp:
--------------------------------------------------------------------------------
1 | #include "HostControlMenu.h"
2 | #include "../IOSUtils/PopupMenuOptionsHelpers.h"
3 | #include "HostControlMenuComp.h"
4 |
5 | HostControlMenu::HostControlMenu (HostParamControl& controller) : controller (controller)
6 | {
7 | cog = Drawable::createFromImageData (BinaryData::cogsolid_svg, BinaryData::cogsolid_svgSize);
8 | cog->replaceColour (Colours::black, Colours::white);
9 |
10 | setTooltip ("Lists which parameters are currently assigned to targets");
11 | }
12 |
13 | void HostControlMenu::paint (Graphics& g)
14 | {
15 | auto bounds = getLocalBounds().toFloat();
16 | auto dim = jmin (bounds.getWidth(), bounds.getHeight());
17 | bounds.setSize (dim, dim);
18 | bounds.reduce (6.0f, 6.0f);
19 |
20 | auto placement = RectanglePlacement (RectanglePlacement::stretchToFit);
21 | cog->drawWithin (g, bounds, placement, 1.0f);
22 | }
23 |
24 | void HostControlMenu::mouseDown (const MouseEvent& e)
25 | {
26 | PopupMenu menu;
27 | for (size_t i = 0; i < controller.getNumAssignableParams(); ++i)
28 | menu.addCustomItem ((int) i + 1, std::make_unique (controller, i));
29 |
30 | auto popupOptions = PopupMenuOptionsHelpers::createPopupMenuOptions (this)
31 | .withPreferredPopupDirection (PopupMenu::Options::PopupDirection::upwards);
32 |
33 | menu.setLookAndFeel (&getLookAndFeel());
34 | menu.showMenuAsync (popupOptions,
35 | [=] (int id) {
36 | if (id > 0)
37 | mouseDown (e);
38 | });
39 | }
40 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/HostControlMenu.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "ChowMatrix.h"
4 | #include "dsp/Parameters/HostParamControl.h"
5 |
6 | class HostControlMenu : public Component,
7 | public SettableTooltipClient
8 | {
9 | public:
10 | HostControlMenu (HostParamControl& controller);
11 |
12 | void paint (Graphics& g) override;
13 | void mouseDown (const MouseEvent& e) override;
14 |
15 | private:
16 | HostParamControl& controller;
17 | std::unique_ptr cog;
18 |
19 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HostControlMenu)
20 | };
21 |
22 | class HostControlMenuItem : public foleys::GuiItem
23 | {
24 | public:
25 | FOLEYS_DECLARE_GUI_FACTORY (HostControlMenuItem)
26 |
27 | HostControlMenuItem (foleys::MagicGUIBuilder& builder, const ValueTree& node) : foleys::GuiItem (builder, node)
28 | {
29 | if (auto* proc = dynamic_cast (builder.getMagicState().getProcessor()))
30 | {
31 | comp = std::make_unique (proc->getHostControl());
32 | addAndMakeVisible (comp.get());
33 | }
34 | }
35 |
36 | void update() override
37 | {
38 | comp->repaint();
39 | }
40 |
41 | Component* getWrappedComponent() override
42 | {
43 | return comp.get();
44 | }
45 |
46 | private:
47 | std::unique_ptr comp;
48 |
49 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HostControlMenuItem)
50 | };
51 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/HostControlMenuComp.cpp:
--------------------------------------------------------------------------------
1 | #include "HostControlMenuComp.h"
2 |
3 | HostControlMenuComp::HostControlMenuComp (HostParamControl& controller, size_t idx) : PopupMenu::CustomComponent (false),
4 | name ("Assign " + String (idx + 1))
5 | {
6 | controller.loadParamList (paramList, xCallbacks, idx);
7 |
8 | for (auto& xCallback : xCallbacks)
9 | {
10 | auto newButton = xButtons.add (std::make_unique ("Delete", DrawableButton::ImageFitted));
11 | createXButton (*newButton);
12 | addAndMakeVisible (newButton);
13 | newButton->onClick = [=] {
14 | xCallback();
15 | triggerMenuItem();
16 | };
17 | }
18 | }
19 |
20 | void HostControlMenuComp::createXButton (DrawableButton& xButton)
21 | {
22 | Rectangle buttonZone (-10.0f, -10.0f, 20.0f, 20.0f);
23 |
24 | DrawableRectangle redSquare;
25 | redSquare.setRectangle (buttonZone);
26 | redSquare.setFill (Colours::red);
27 |
28 | Path p;
29 | constexpr auto thickness = 1.5f;
30 | constexpr auto indent = 2.0f;
31 | p.addRectangle (-8.0f, -thickness, 20.0f - indent * 2.0f, thickness * 2.0f);
32 | p.addRectangle (-thickness, -8.0f, thickness * 2.0f, 10.0f - indent - thickness);
33 | p.addRectangle (-thickness, thickness, thickness * 2.0f, 10.0f - indent - thickness);
34 | p.setUsingNonZeroWinding (false);
35 | p.applyTransform (AffineTransform::rotation (MathConstants::halfPi / 2.0f));
36 |
37 | DrawablePath dp;
38 | dp.setPath (p);
39 | dp.setFill (Colours::white);
40 |
41 | DrawableComposite buttonImage;
42 | buttonImage.addAndMakeVisible (redSquare.createCopy().release());
43 | buttonImage.addAndMakeVisible (dp.createCopy().release());
44 |
45 | xButton.setImages (&buttonImage);
46 | }
47 |
48 | void HostControlMenuComp::getIdealSize (int& idealWidth, int& idealHeight)
49 | {
50 | idealWidth = 250;
51 | idealHeight = labelHeight * jmax (paramList.size(), 1);
52 | }
53 |
54 | void HostControlMenuComp::paint (Graphics& g)
55 | {
56 | auto bounds = getLocalBounds();
57 | bounds.removeFromRight (20); // save space for xButtons
58 |
59 | g.setColour (Colours::white);
60 | g.setFont (Font (font).boldened());
61 |
62 | auto labelBox = bounds.removeFromLeft (60);
63 | g.drawFittedText (name, labelBox.removeFromTop (labelHeight), Justification::centred, 1);
64 |
65 | if (paramList.isEmpty())
66 | {
67 | g.setColour (Colours::lightgrey.withAlpha (0.85f));
68 | g.setFont (Font (font));
69 | g.drawFittedText ("None", bounds, Justification::centred, 1);
70 | }
71 | else
72 | {
73 | g.setColour (Colours::white);
74 | g.setFont (Font (font));
75 | for (auto& pString : paramList)
76 | g.drawFittedText (pString, bounds.removeFromTop (labelHeight), Justification::centred, 1);
77 | }
78 |
79 | g.setColour (Colours::lightgrey.withAlpha (0.25f));
80 | auto lineY = (float) getHeight();
81 | g.drawLine (0, lineY, (float) getWidth(), lineY);
82 | }
83 |
84 | void HostControlMenuComp::resized()
85 | {
86 | auto buttonsBox = getLocalBounds().removeFromRight (20);
87 |
88 | for (auto* xButton : xButtons)
89 | xButton->setBounds (buttonsBox.removeFromTop (labelHeight));
90 | }
91 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/HostControlMenuComp.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "dsp/Parameters/HostParamControl.h"
4 |
5 | class HostControlMenuComp : public PopupMenu::CustomComponent
6 | {
7 | public:
8 | HostControlMenuComp (HostParamControl& controller, size_t idx);
9 | static void createXButton (DrawableButton& xButton);
10 |
11 | void getIdealSize (int& idealWidth, int& idealHeight) override;
12 | void paint (Graphics& g) override;
13 | void resized() override;
14 |
15 | private:
16 | String name;
17 | StringArray paramList;
18 |
19 | OwnedArray xButtons;
20 | std::vector> xCallbacks;
21 |
22 | #if JUCE_IOS
23 | static constexpr float font = 18.0f;
24 | static constexpr int labelHeight = 30;
25 | #else
26 | static constexpr float font = 16.0f;
27 | static constexpr int labelHeight = 25;
28 | #endif
29 |
30 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HostControlMenuComp)
31 | };
32 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/TextSlider.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BottomBarLNF.h"
4 |
5 | /* Slider that shows only a text bubble */
6 | class TextSlider : public Slider
7 | {
8 | public:
9 | TextSlider()
10 | {
11 | setLookAndFeel (lnfAllocator->getLookAndFeel());
12 | }
13 |
14 | ~TextSlider() override
15 | {
16 | setLookAndFeel (nullptr);
17 | }
18 |
19 | void mouseUp (const MouseEvent& e) override
20 | {
21 | Slider::mouseUp (e);
22 |
23 | multiClicking = e.getNumberOfClicks() > 1;
24 | bool dontShowLabel = e.mouseWasDraggedSinceMouseDown() || e.mods.isAnyModifierKeyDown() || e.mods.isPopupMenu() || multiClicking;
25 | if (! dontShowLabel)
26 | {
27 | Timer::callAfterDelay (270,
28 | [=] {
29 | if (multiClicking)
30 | {
31 | multiClicking = false;
32 | return;
33 | }
34 |
35 | showTextBox();
36 | });
37 | }
38 | }
39 |
40 | private:
41 | bool multiClicking = false;
42 |
43 | chowdsp::SharedLNFAllocator lnfAllocator;
44 |
45 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextSlider)
46 | };
47 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/TextSliderItem.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "TextSlider.h"
4 |
5 | class TextSliderItem : public foleys::GuiItem
6 | {
7 | public:
8 | FOLEYS_DECLARE_GUI_FACTORY (TextSliderItem)
9 |
10 | TextSliderItem (foleys::MagicGUIBuilder& builder, const ValueTree& node) : foleys::GuiItem (builder, node)
11 | {
12 | setColourTranslation (
13 | { { "slider-background", juce::Slider::backgroundColourId },
14 | { "slider-thumb", juce::Slider::thumbColourId },
15 | { "slider-track", juce::Slider::trackColourId },
16 | { "rotary-fill", juce::Slider::rotarySliderFillColourId },
17 | { "rotary-outline", juce::Slider::rotarySliderOutlineColourId },
18 | { "slider-text", juce::Slider::textBoxTextColourId },
19 | { "slider-text-background", juce::Slider::textBoxBackgroundColourId },
20 | { "slider-text-highlight", juce::Slider::textBoxHighlightColourId },
21 | { "slider-text-outline", juce::Slider::textBoxOutlineColourId } });
22 |
23 | slider = std::make_unique();
24 | addAndMakeVisible (slider.get());
25 | }
26 |
27 | void update() override
28 | {
29 | attachment.reset();
30 |
31 | slider->setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
32 |
33 | auto paramID = configNode.getProperty (foleys::IDs::parameter, juce::String()).toString();
34 | if (paramID.isNotEmpty())
35 | attachment = getMagicState().createAttachment (paramID, *slider.get());
36 | }
37 |
38 | std::vector getSettableProperties() const override
39 | {
40 | std::vector itemProperties;
41 |
42 | itemProperties.push_back ({ configNode, foleys::IDs::parameter, foleys::SettableProperty::Choice, {}, magicBuilder.createParameterMenuLambda() });
43 |
44 | return itemProperties;
45 | }
46 |
47 | juce::Component* getWrappedComponent() override
48 | {
49 | return slider.get();
50 | }
51 |
52 | protected:
53 | std::unique_ptr slider;
54 |
55 | private:
56 | std::unique_ptr attachment;
57 |
58 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextSliderItem)
59 | };
60 |
--------------------------------------------------------------------------------
/src/gui/BottomBar/WetGainSlider.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../IOSUtils/LongPressActionHelper.h"
4 | #include "../IOSUtils/PopupMenuOptionsHelpers.h"
5 | #include "BottomBarLNF.h"
6 | #include "ChowMatrix.h"
7 | #include "TextSliderItem.h"
8 |
9 | class WetGainSlider : public TextSlider
10 | {
11 | public:
12 | explicit WetGainSlider (AudioProcessorValueTreeState& vts) : attachment (
13 | *vts.getParameter ("wet_gain_comp_param"),
14 | [=] (float v) { updateMenu (v == 1.0f); },
15 | vts.undoManager)
16 | {
17 | attachment.sendInitialUpdate();
18 | gainCompMenu.setLookAndFeel (lnfAllocator->getLookAndFeel());
19 |
20 | #if JUCE_IOS
21 | longPressAction.longPressCallback = [=] (Point) {
22 | gainCompMenu.showMenuAsync (PopupMenuOptionsHelpers::createPopupMenuOptions (this));
23 | };
24 | #endif
25 | }
26 |
27 | void mouseDown (const MouseEvent& e) override
28 | {
29 | if (e.mods.isPopupMenu())
30 | {
31 | gainCompMenu.showMenuAsync (PopupMenuOptionsHelpers::createPopupMenuOptions (this));
32 | return;
33 | }
34 |
35 | TextSlider::mouseDown (e);
36 |
37 | #if JUCE_IOS
38 | longPressAction.startPress (e.getMouseDownPosition());
39 | #endif
40 | }
41 |
42 | void mouseDrag (const MouseEvent& e) override
43 | {
44 | TextSlider::mouseDrag (e);
45 |
46 | #if JUCE_IOS
47 | longPressAction.setDragDistance ((float) e.getDistanceFromDragStart());
48 | #endif
49 | }
50 |
51 | void mouseUp (const MouseEvent& e) override
52 | {
53 | TextSlider::mouseUp (e);
54 |
55 | #if JUCE_IOS
56 | longPressAction.abortPress();
57 | #endif
58 | }
59 |
60 | void updateMenu (bool gainCompOn)
61 | {
62 | PopupMenu::Item gainCompItem;
63 | gainCompItem.itemID = 1;
64 | gainCompItem.text = "Automatic Gain Compensation";
65 | gainCompItem.action = [=] { attachment.setValueAsCompleteGesture ((float) ! gainCompOn); };
66 | gainCompItem.colour = gainCompOn ? Colour (0xFF21CCA5) : Colours::white;
67 |
68 | gainCompMenu.clear();
69 | gainCompMenu.addItem (gainCompItem);
70 | }
71 |
72 | private:
73 | ParameterAttachment attachment;
74 |
75 | PopupMenu gainCompMenu;
76 | chowdsp::SharedLNFAllocator lnfAllocator;
77 |
78 | #if JUCE_IOS
79 | LongPressActionHelper longPressAction;
80 | #endif
81 |
82 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WetGainSlider)
83 | };
84 |
85 | class WetGainSliderItem : public TextSliderItem
86 | {
87 | public:
88 | FOLEYS_DECLARE_GUI_FACTORY (WetGainSliderItem)
89 |
90 | WetGainSliderItem (foleys::MagicGUIBuilder& builder, const ValueTree& node) : TextSliderItem (builder, node)
91 | {
92 | auto* plugin = dynamic_cast (builder.getMagicState().getProcessor());
93 | slider = std::make_unique (plugin->getVTS());
94 | addAndMakeVisible (slider.get());
95 | }
96 |
97 | private:
98 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WetGainSliderItem)
99 | };
100 |
--------------------------------------------------------------------------------
/src/gui/DetailsView/NodeDetails.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../../NodeManager.h"
4 | #include "../NodeInfo.h"
5 | #include "gui/IOSUtils/LongPressActionHelper.h"
6 |
7 | namespace DetailsConsts
8 | {
9 | constexpr int buttonHeight = 45;
10 | constexpr int circleRadius = 16;
11 | } // namespace DetailsConsts
12 |
13 | class NodeDetails : public Component
14 | {
15 | public:
16 | NodeDetails (DelayNode& node, NodeManager& manager);
17 | ~NodeDetails() override;
18 |
19 | void resized() override;
20 | DelayNode* getNode() { return nodeInfo.getNode(); }
21 |
22 | void setSelected() { manager.setSelected (getNode(), NodeManager::ActionSource::DetailsView); }
23 | void setSoloed() { manager.setSoloed (getNode(), NodeManager::ActionSource::DetailsView); }
24 |
25 | struct Button : Component, SettableTooltipClient
26 | {
27 | Button (NodeDetails& nodeDetails);
28 |
29 | enum ColourIDs
30 | {
31 | nodeColour,
32 | selectedColour
33 | };
34 |
35 | void mouseDown (const MouseEvent& e) override;
36 | void mouseDrag (const MouseEvent& e) override;
37 | void mouseUp (const MouseEvent& e) override;
38 |
39 | bool keyPressed (const KeyPress& key) override;
40 | void paint (Graphics& g) override;
41 |
42 | private:
43 | NodeDetails& nodeDetails;
44 |
45 | #if JUCE_IOS
46 | LongPressActionHelper longPressAction;
47 | chowdsp::SharedLNFAllocator lnfAllocator;
48 | #endif
49 | };
50 |
51 | private:
52 | NodeManager& manager;
53 | NodeInfo nodeInfo;
54 | Button button;
55 |
56 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NodeDetails)
57 | };
58 |
--------------------------------------------------------------------------------
/src/gui/DetailsView/NodeDetailsComponent.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../../ChowMatrix.h"
4 | #include "NodeDetails.h"
5 |
6 | class NodeDetailsComponent : public Component,
7 | private DBaseNode::Listener
8 | {
9 | public:
10 | NodeDetailsComponent (ChowMatrix& plugin);
11 | ~NodeDetailsComponent() override;
12 |
13 | void paint (Graphics& g) override;
14 | void resized() override;
15 | void mouseDown (const MouseEvent& e) override;
16 |
17 | void nodeAdded (DelayNode* newNode) override;
18 | void nodeRemoved (DelayNode* nodeToRemove) override;
19 |
20 | void setMinWidth (int newMinWidth);
21 | int calcWidth() const;
22 |
23 | NodeDetails* getNodeDetails (DelayNode* node);
24 | Point getNodeDetailsPosition (DelayNode* node);
25 |
26 | private:
27 | void addNode (DelayNode* node);
28 |
29 | ChowMatrix& plugin;
30 | OwnedArray nodes;
31 | int minWidth = 0;
32 |
33 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NodeDetailsComponent)
34 | };
35 |
--------------------------------------------------------------------------------
/src/gui/DetailsView/NodeDetailsGUI.cpp:
--------------------------------------------------------------------------------
1 | #include "NodeDetailsGUI.h"
2 | #include "../../dsp/Parameters/ParamHelpers.h"
3 |
4 | NodeDetailsGUI::NodeDetailsGUI (ChowMatrix& chowMatrix) : nodeDetailsViewport (chowMatrix)
5 | {
6 | setColour (nodeColour, Colours::green);
7 | setColour (nodeSelectedColour, Colours::pink);
8 | setColour (scrollThumbColour, Colours::pink);
9 | setColour (scrollTrackColour, Colours::grey);
10 |
11 | auto node = std::make_unique();
12 | auto& params = node->getParameters();
13 | for (int i = 0; i < 1 + params.size(); ++i)
14 | {
15 | String name = "Node";
16 | String text = "Node";
17 | String param = "parameters";
18 | if (i > 0)
19 | {
20 | name = params[i - 1]->getName (1024);
21 | text = name;
22 | param = name;
23 | }
24 |
25 | auto* l = labels.add (std::make_unique