├── resources ├── fonts │ └── README.txt ├── Inter │ ├── static │ │ ├── Inter-Black.ttf │ │ ├── Inter-Bold.ttf │ │ ├── Inter-Light.ttf │ │ ├── Inter-Thin.ttf │ │ ├── Inter-Medium.ttf │ │ ├── Inter-Regular.ttf │ │ ├── Inter-SemiBold.ttf │ │ ├── Inter-ExtraBold.ttf │ │ └── Inter-ExtraLight.ttf │ ├── README.txt │ └── OFL.txt ├── Anonymous_Pro │ ├── AnonymousPro-Bold.ttf │ ├── AnonymousPro-Italic.ttf │ ├── AnonymousPro-Regular.ttf │ ├── AnonymousPro-BoldItalic.ttf │ └── OFL.txt ├── NightlyBlurb.md ├── CMakeLists.txt └── README.rtf ├── scripts ├── fix_code.sh ├── fix_header_guards.pl ├── fix_file_comments.pl └── mac_installer.sh ├── src ├── polysynth │ ├── CMakeLists.txt │ ├── effects-impl.h │ └── voice.h ├── chord-memory │ ├── CMakeLists.txt │ ├── chord-memory-editor.cpp │ ├── chord-memory.h │ └── chord-memory.cpp ├── midi2-sawsynth │ ├── CMakeLists.txt │ ├── midi2-sawsynth.h │ └── midi2-sawsynth.cpp ├── multiout-synth │ ├── CMakeLists.txt │ ├── multiout-synth-editor.cpp │ ├── multiout-synth.h │ └── multiout-synth.cpp ├── polymetric-delay │ ├── CMakeLists.txt │ └── polymetric-delay.h ├── ring-modulator │ ├── CMakeLists.txt │ ├── ring-modulator.h │ ├── ring-modulator-editor.cpp │ └── ring-modulator.cpp ├── clap-event-monitor │ ├── CMakeLists.txt │ ├── clap-event-monitor.h │ └── clap-event-monitor.cpp ├── mts-to-noteexpression │ ├── CMakeLists.txt │ ├── mts-to-noteexpression.h │ ├── mts-to-noteexpression-editor.cpp │ └── mts-to-noteexpression.cpp ├── conduit-shared │ ├── sse-include.h │ ├── shared-symbols.cpp │ └── debug-helpers.h ├── CMakeLists.txt └── conduit-clap-entry.cpp ├── .github └── workflows │ ├── code-checks.yml │ ├── build-pr.yml │ └── build-release.yml ├── cmake ├── git-info.cmake ├── version.cpp.in ├── version.h └── versiontools.cmake ├── LICENSE.md ├── .clang-format ├── .gitignore ├── .gitmodules ├── README.md └── CMakeLists.txt /resources/fonts/README.txt: -------------------------------------------------------------------------------- 1 | Anonymous_Pro: https://fonts.google.com/specimen/Anonymous+Pro?category=Monospace#about 2 | -------------------------------------------------------------------------------- /scripts/fix_code.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | perl scripts/fix_header_guards.pl 4 | perl scripts/fix_file_comments.pl 5 | -------------------------------------------------------------------------------- /resources/Inter/static/Inter-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Inter/static/Inter-Black.ttf -------------------------------------------------------------------------------- /resources/Inter/static/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Inter/static/Inter-Bold.ttf -------------------------------------------------------------------------------- /resources/Inter/static/Inter-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Inter/static/Inter-Light.ttf -------------------------------------------------------------------------------- /resources/Inter/static/Inter-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Inter/static/Inter-Thin.ttf -------------------------------------------------------------------------------- /resources/Inter/static/Inter-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Inter/static/Inter-Medium.ttf -------------------------------------------------------------------------------- /resources/Inter/static/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Inter/static/Inter-Regular.ttf -------------------------------------------------------------------------------- /resources/Inter/static/Inter-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Inter/static/Inter-SemiBold.ttf -------------------------------------------------------------------------------- /resources/Inter/static/Inter-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Inter/static/Inter-ExtraBold.ttf -------------------------------------------------------------------------------- /resources/Inter/static/Inter-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Inter/static/Inter-ExtraLight.ttf -------------------------------------------------------------------------------- /resources/Anonymous_Pro/AnonymousPro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Anonymous_Pro/AnonymousPro-Bold.ttf -------------------------------------------------------------------------------- /resources/Anonymous_Pro/AnonymousPro-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Anonymous_Pro/AnonymousPro-Italic.ttf -------------------------------------------------------------------------------- /resources/Anonymous_Pro/AnonymousPro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Anonymous_Pro/AnonymousPro-Regular.ttf -------------------------------------------------------------------------------- /resources/Anonymous_Pro/AnonymousPro-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surge-synthesizer/conduit/HEAD/resources/Anonymous_Pro/AnonymousPro-BoldItalic.ttf -------------------------------------------------------------------------------- /src/polysynth/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(polysynth) 2 | 3 | add_to_conduit(SOURCE 4 | ${PROJECT_NAME}.cpp 5 | ${PROJECT_NAME}-editor.cpp 6 | voice.cpp 7 | INCLUDE .) 8 | -------------------------------------------------------------------------------- /src/chord-memory/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(chord-memory) 2 | 3 | add_to_conduit(SOURCE 4 | ${PROJECT_NAME}.cpp 5 | ${PROJECT_NAME}-editor.cpp 6 | INCLUDE . 7 | ) 8 | 9 | -------------------------------------------------------------------------------- /src/midi2-sawsynth/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(midi2-sawsynth) 2 | 3 | add_to_conduit(SOURCE 4 | ${PROJECT_NAME}.cpp 5 | ${PROJECT_NAME}-editor.cpp 6 | INCLUDE . 7 | ) 8 | -------------------------------------------------------------------------------- /src/multiout-synth/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(multiout-synth) 2 | 3 | add_to_conduit(SOURCE 4 | ${PROJECT_NAME}.cpp 5 | ${PROJECT_NAME}-editor.cpp 6 | INCLUDE . 7 | ) 8 | -------------------------------------------------------------------------------- /src/polymetric-delay/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(polymetric-delay) 2 | 3 | add_to_conduit(SOURCE 4 | ${PROJECT_NAME}.cpp 5 | ${PROJECT_NAME}-editor.cpp 6 | INCLUDE . 7 | ) 8 | -------------------------------------------------------------------------------- /src/ring-modulator/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(ring-modulator) 2 | 3 | add_to_conduit(SOURCE 4 | ${PROJECT_NAME}.cpp 5 | ${PROJECT_NAME}-editor.cpp 6 | INCLUDE . 7 | ) 8 | -------------------------------------------------------------------------------- /src/clap-event-monitor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(clap-event-monitor) 2 | 3 | add_to_conduit(SOURCE 4 | ${PROJECT_NAME}.cpp 5 | ${PROJECT_NAME}-editor.cpp 6 | INCLUDE . 7 | ) 8 | -------------------------------------------------------------------------------- /src/mts-to-noteexpression/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(mts-to-noteexpression) 2 | 3 | add_to_conduit(SOURCE 4 | ${PROJECT_NAME}.cpp 5 | ${PROJECT_NAME}-editor.cpp 6 | INCLUDE . 7 | ) 8 | -------------------------------------------------------------------------------- /resources/NightlyBlurb.md: -------------------------------------------------------------------------------- 1 | ## Conduit Nightly Build 2 | 3 | This is an incomplete pre-alpha of a project to test concepts in clap-first and clap-wrapper development. Please join our discord to find out more. 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(conduit-resources) 2 | 3 | cmrc_add_resource_library(${PROJECT_NAME} 4 | NAMESPACE conduit_resources 5 | "Inter/static/Inter-Medium.ttf" 6 | "Anonymous_Pro/AnonymousPro-Regular.ttf" 7 | ) 8 | -------------------------------------------------------------------------------- /.github/workflows/code-checks.yml: -------------------------------------------------------------------------------- 1 | name: Format Check 2 | on: [pull_request] 3 | jobs: 4 | formatting-check: 5 | name: Clang Format Check 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | path: [ 'src'] 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v3 13 | 14 | - name: Run clang-format style check 15 | uses: surge-synthesizer/sst-githubactions/clang-format-check@main 16 | with: 17 | path: ${{ matrix.path }} -------------------------------------------------------------------------------- /.github/workflows/build-pr.yml: -------------------------------------------------------------------------------- 1 | name: Build Pull Request 2 | on: 3 | pull_request: 4 | 5 | defaults: 6 | run: 7 | shell: bash 8 | 9 | jobs: 10 | build_plugin: 11 | name: PR - ${{ matrix.os }} 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | include: 16 | - os: windows-latest 17 | - os: macos-latest 18 | - os: ubuntu-latest 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v4 22 | with: 23 | submodules: recursive 24 | 25 | - name: Prepare for JUCE 26 | uses: surge-synthesizer/sst-githubactions/prepare-for-juce@main 27 | with: 28 | os: ${{ runner.os }} 29 | 30 | - name: Build pull request version 31 | run: | 32 | cmake -S . -B ./build -DCMAKE_BUILD_TYPE=Debug 33 | cmake --build ./build --config Debug --target conduit_all --parallel 3 34 | -------------------------------------------------------------------------------- /resources/README.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf2758 2 | \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\expandedcolortbl;;} 5 | \margl1440\margr1440\vieww11520\viewh8400\viewkind0 6 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 7 | 8 | \f0\b\fs48 \cf0 Welcome to Conduit\ 9 | \ 10 | 11 | \f1\b0\fs24 Conduit is a product of the surge synth team (https://surge-synth-team.org/) with three goals\ 12 | \ 13 | 1. Demonstrate true \'91clap-first\'92 development\ 14 | 2. Exercise our re-factored libraries for sharing code among our properties as we approach XT2 and SCXT\ 15 | 3. Let us write those little plugins we always wanted\ 16 | \ 17 | Still pretty alpha. The AUv2 is missing some stuff. The plugins are unfinished. But you know. 18 | \f0\b\fs48 \ 19 | 20 | \f1\b0\fs28 \ 21 | \ 22 | } -------------------------------------------------------------------------------- /cmake/git-info.cmake: -------------------------------------------------------------------------------- 1 | add_custom_target(conduit-generate-version-info 2 | BYPRODUCTS ${CMAKE_BINARY_DIR}/geninclude/version.cpp 3 | DEPENDS ${CMAKE_SOURCE_DIR}/cmake/version.h 4 | ${CMAKE_SOURCE_DIR}/cmake/version.cpp.in 5 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 6 | COMMAND ${CMAKE_COMMAND} 7 | -D PROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} 8 | -D PROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR} 9 | -D CONDUITSRC=${CMAKE_SOURCE_DIR} 10 | -D CONDUITBLD=${CMAKE_BINARY_DIR} 11 | -D AZURE_PIPELINE=${AZURE_PIPELINE} 12 | -D WIN32=${WIN32} 13 | -D CMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID} 14 | -D CMAKE_CXX_COMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION} 15 | -P ${CMAKE_SOURCE_DIR}/cmake/versiontools.cmake 16 | ) 17 | 18 | # Platform Specific Compile Settings 19 | add_library(conduit-version-info STATIC) 20 | target_sources(conduit-version-info PRIVATE ${CMAKE_BINARY_DIR}/geninclude/version.cpp) 21 | target_include_directories(conduit-version-info PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 22 | target_link_libraries(conduit-version-info) 23 | add_dependencies(conduit-version-info conduit-generate-version-info) 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The source code and cmake files in this repository are released under the MIT License, below. 2 | Please see the README for important information on licensing though. The binary which is a combined 3 | work of compiling this software has GPL3 dependencies. 4 | 5 | --------- 6 | 7 | Copyright 2023, Paul Walker 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/conduit-shared/sse-include.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_CONDUIT_SHARED_SSE_INCLUDE_H 23 | #define CONDUIT_SRC_CONDUIT_SHARED_SSE_INCLUDE_H 24 | 25 | #if defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || \ 26 | (defined(_M_IX86_FP) && _M_IX86_FP >= 2) 27 | #include 28 | #include 29 | #else 30 | #if defined(__arm__) || defined(__aarch64__) || defined(__riscv) 31 | #define SIMDE_ENABLE_NATIVE_ALIASES 32 | #include "simde/x86/sse2.h" 33 | #else 34 | #error Conduit requires either X86/SSE2 or ARM architectures. 35 | #endif 36 | #endif 37 | 38 | #endif // CONDUIT_SSE_INCLUDE_H 39 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | IndentWidth: 4 4 | --- 5 | Language: Cpp 6 | BasedOnStyle: LLVM 7 | IndentWidth: 4 8 | AlignAfterOpenBracket: Align 9 | BreakBeforeBraces: Custom 10 | BraceWrapping: 11 | AfterCaseLabel: true 12 | AfterClass: true 13 | AfterControlStatement: Always 14 | AfterEnum: true 15 | AfterFunction: true 16 | AfterNamespace: true 17 | AfterObjCDeclaration: true 18 | AfterStruct: true 19 | AfterUnion: true 20 | AfterExternBlock: true 21 | BeforeCatch: true 22 | BeforeElse: true 23 | BeforeLambdaBody: false 24 | BeforeWhile: false 25 | IndentBraces: false 26 | SplitEmptyFunction: true 27 | SplitEmptyRecord: true 28 | SplitEmptyNamespace: true 29 | ColumnLimit: 100 30 | SortIncludes: false 31 | --- 32 | Language: ObjC 33 | BasedOnStyle: LLVM 34 | IndentWidth: 4 35 | AlignAfterOpenBracket: Align 36 | BreakBeforeBraces: Custom 37 | BraceWrapping: 38 | AfterCaseLabel: true 39 | AfterClass: true 40 | AfterControlStatement: Always 41 | AfterEnum: true 42 | AfterFunction: true 43 | AfterNamespace: true 44 | AfterObjCDeclaration: true 45 | AfterStruct: true 46 | AfterUnion: true 47 | AfterExternBlock: true 48 | BeforeCatch: true 49 | BeforeElse: true 50 | BeforeLambdaBody: false 51 | BeforeWhile: false 52 | IndentBraces: false 53 | SplitEmptyFunction: true 54 | SplitEmptyRecord: true 55 | SplitEmptyNamespace: true 56 | ColumnLimit: 100 57 | SortIncludes: false 58 | --- 59 | 60 | -------------------------------------------------------------------------------- /src/conduit-shared/shared-symbols.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "clap-base-class.h" 23 | 24 | // Eject the core symbols for the plugin 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | CMRC_DECLARE(conduit_resources); 33 | 34 | namespace csh = sst::conduit::shared; 35 | namespace chlp = clap::helpers; 36 | template class chlp::Plugin; 37 | template class chlp::HostProxy; 38 | static_assert(std::is_same_v>); -------------------------------------------------------------------------------- /cmake/version.cpp.in: -------------------------------------------------------------------------------- 1 | /* 2 | ** This file is rebuilt and substitited every time you run a build. 3 | ** Things which need to be per-build should be defined here, declared 4 | ** in the version.h header, and then used wherever you want 5 | */ 6 | #include 7 | 8 | // clang-format off 9 | namespace sst::conduit 10 | { 11 | const char *build::MajorVersionStr = "@CONDUIT_MAJOR_VERSION@"; 12 | const int build::MajorVersionInt = @CONDUIT_MAJOR_VERSION@; 13 | 14 | const char *build::SubVersionStr = "@CONDUIT_SUB_VERSION@"; 15 | const int build::SubVersionInt = @CONDUIT_SUB_VERSION@; 16 | 17 | const char *build::ReleaseNumberStr = "@CONDUIT_RELEASE_NUMBER@"; 18 | const char *build::ReleaseStr = "@CONDUIT_RELEASE_VERSION@"; 19 | 20 | const char *build::BuildNumberStr = 21 | "@CONDUIT_BUILD_HASH@"; // Build number to be sure that each result could identified. 22 | 23 | const char *build::FullVersionStr = "@CONDUIT_FULL_VERSION@"; 24 | const char *build::BuildHost = "@CONDUIT_BUILD_FQDN@"; 25 | const char *build::BuildArch = "@CONDUIT_BUILD_ARCH@"; 26 | const char *build::BuildCompiler = "@CMAKE_CXX_COMPILER_ID@-@CMAKE_CXX_COMPILER_VERSION@"; 27 | 28 | const char *build::BuildLocation = "@CONDUIT_BUILD_LOCATION@"; 29 | 30 | const char *build::BuildDate = "@CONDUIT_BUILD_DATE@"; 31 | const char *build::BuildTime = "@CONDUIT_BUILD_TIME@"; 32 | const char *build::BuildYear = "@CONDUIT_BUILD_YEAR@"; 33 | 34 | const char *build::GitHash = "@GIT_COMMIT_HASH@"; 35 | const char *build::GitBranch = "@GIT_BRANCH@"; 36 | 37 | const char *build::CMAKE_INSTALL_PREFIX = "@CMAKE_INSTALL_PREFIX@"; 38 | } // namespace scxt 39 | //clang-format on 40 | -------------------------------------------------------------------------------- /scripts/fix_header_guards.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | 4 | use File::Find; 5 | use File::Basename; 6 | 7 | find( 8 | { 9 | wanted => \&findfiles, 10 | }, 11 | 'src' 12 | ); 13 | 14 | sub findfiles 15 | { 16 | $f = $File::Find::name; 17 | if ($f =~ m/\.h$/) 18 | { 19 | #To search the files inside the directories 20 | 21 | $hg = $f; 22 | $hg =~ s:/:_:g; 23 | $hg =~ s:\.:_:g; 24 | $hg =~ s:-:_:g; 25 | $hg =~ s:src:conduit_src:; 26 | $hg = uc($hg); 27 | print "$f -> $hg\n"; 28 | 29 | print "$f -> ${f}.bak\n"; 30 | 31 | $q = basename($f); 32 | print "$q\n"; 33 | open(IN, "<$q") || die "Cant open IN $!"; 34 | open(OUT, "> ${q}.bak") || die "Cant open BAK $!"; 35 | 36 | $tg = "notyet"; 37 | $pragmaOnce = 0; 38 | while() 39 | { 40 | if (m/\#ifndef\s+(\S*)/) 41 | { 42 | $tg = $1; 43 | print OUT "#ifndef $hg\n"; 44 | } 45 | elsif (m/\#define\s+${tg}/) 46 | { 47 | print OUT "#define $hg\n"; 48 | } 49 | elsif (m/#pragma\s*once/) 50 | { 51 | print OUT "#ifndef $hg\n#define $hg\n"; 52 | $pragmaOnce = ff; 53 | } 54 | else 55 | { 56 | print OUT; 57 | } 58 | } 59 | if ($pragmaOnce) 60 | { 61 | print OUT "\n#endif // $hg\n"; 62 | } 63 | close(IN); 64 | close(OUT); 65 | system("mv ${q}.bak ${q}"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Visual Studio 35 | obj/ 36 | *.sln 37 | *.vcxproj 38 | *.vcxproj.filters 39 | *.vcxproj.user 40 | .vs/ 41 | packages/ 42 | target/ 43 | *.pdb 44 | packages.config 45 | CMakeSettings.json 46 | .vscode 47 | .cache 48 | 49 | # XCode 50 | Surge.xcworkspace/ 51 | surge-au.xcodeproj/ 52 | surge-vst2.xcodeproj/ 53 | surge-vst3.xcodeproj/ 54 | surge-headless.xcodeproj/ 55 | products/ 56 | installer_mac/installer 57 | installer_mac/*.dmg 58 | installer_osx/installer 59 | installer_osx/Install_Surge_*.dmg 60 | build_logs/ 61 | fxbuild/ 62 | .DS_Store 63 | 64 | # IntelliJ IDEA 65 | .idea 66 | 67 | # Linux 68 | Makefile 69 | surge-*.make 70 | premake-stamp 71 | cmake-stamp 72 | /Debug 73 | *.deb 74 | 75 | # Qt Creator 76 | *.txt.user 77 | *.txt.user.* 78 | 79 | # CMake 80 | build/ 81 | build32/ 82 | buildlin/ 83 | buildlin-*/ 84 | buildmac/ 85 | buildwin/ 86 | buildxt/ 87 | build-arm/ 88 | build_lv2 89 | buildiwyu/ 90 | buildpy/ 91 | cmake-build-*/ 92 | bwd/ 93 | CMakeUserPresets.json 94 | 95 | # Reaper 96 | *.RPP-bak 97 | 98 | VERSION_GIT_INFO 99 | .clang-tidy 100 | 101 | # Juce until we add a submodule 102 | libs/juce-* 103 | 104 | # A place for BP to store stuff 105 | ignore/* 106 | __pycache__ 107 | minst.sh 108 | 109 | Testing/ 110 | installer-tmp 111 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(conduit-src) 2 | 3 | add_library(conduit-impl STATIC 4 | conduit-shared/shared-symbols.cpp) 5 | target_include_directories(conduit-impl PUBLIC .) 6 | target_compile_definitions(conduit-impl PUBLIC -DCONDUIT_SOURCE_DIR=\"${CONDUIT_SOURCE_DIR}\") 7 | target_link_libraries(conduit-impl PUBLIC 8 | clap 9 | clap-helpers 10 | sst-basic-blocks 11 | sst-cpputils 12 | sst-filters 13 | sst-effects 14 | sst-voicemanager 15 | sst-plugininfra 16 | sst-waveshapers 17 | mts-esp-client 18 | clap-wrapper-extensions 19 | simde 20 | conduit-resources 21 | conduit-version-info 22 | fmt 23 | tinyxml 24 | sst::clap_juce_shim_headers 25 | ni-midi2 26 | ) 27 | target_link_libraries(conduit-impl PRIVATE 28 | sst-jucegui 29 | juce::juce_gui_basics 30 | sst::clap_juce_shim 31 | ) 32 | 33 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") 34 | target_compile_options(conduit-impl PUBLIC -Wall -Werror -Wno-sign-compare -Wno-ignored-attributes -Wno-ignored-qualifiers) 35 | endif() 36 | 37 | target_compile_definitions(conduit-impl PUBLIC $<$:CONDUIT_DEBUG_BUILD>) 38 | 39 | function(add_to_conduit) 40 | set(multiValArgs SOURCE INCLUDE) 41 | 42 | cmake_parse_arguments(A2C "" "" "${multiValArgs}" ${ARGN}) 43 | 44 | target_sources(conduit-impl PRIVATE ${A2C_SOURCE}) 45 | target_include_directories(conduit-impl PRIVATE ${A2C_INCLUDE}) 46 | endfunction(add_to_conduit) 47 | 48 | add_subdirectory(polysynth) 49 | add_subdirectory(polymetric-delay) 50 | add_subdirectory(chord-memory) 51 | add_subdirectory(ring-modulator) 52 | add_subdirectory(clap-event-monitor) 53 | add_subdirectory(mts-to-noteexpression) 54 | add_subdirectory(midi2-sawsynth) 55 | add_subdirectory(multiout-synth) 56 | -------------------------------------------------------------------------------- /src/conduit-shared/debug-helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_CONDUIT_SHARED_DEBUG_HELPERS_H 23 | #define CONDUIT_SRC_CONDUIT_SHARED_DEBUG_HELPERS_H 24 | 25 | // These are just some macros I put in to trace certain lifecycle and value moments to stdout 26 | #include 27 | #include 28 | 29 | namespace sst::conduit::shared::details 30 | { 31 | inline std::string fixFile(std::string s) 32 | { 33 | // TODO this sucks also 34 | auto sp = s.find(CONDUIT_SOURCE_DIR); 35 | 36 | if (sp == 0) 37 | { 38 | s = s.substr(sp + strlen(CONDUIT_SOURCE_DIR) + 1); 39 | } 40 | return s; 41 | } 42 | } // namespace sst::conduit::shared::details 43 | 44 | #define CNDOUT \ 45 | std::cout << "[conduit] " << sst::conduit::shared::details::fixFile(__FILE__) << ":" \ 46 | << __LINE__ << " (" << __func__ << ") : " 47 | #define CNDVAR(x) " (" << #x << "=" << x << ") " 48 | 49 | #endif // CLAP_SAW_DEMO_DEBUG_HELPERS_H 50 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/clap"] 2 | path = libs/clap 3 | url = https://github.com/free-audio/clap.git 4 | [submodule "libs/clap-helpers"] 5 | path = libs/clap-helpers 6 | url = https://github.com/free-audio/clap-helpers.git 7 | [submodule "libs/JUCE"] 8 | path = libs/JUCE 9 | url = https://github.com/juce-framework/JUCE 10 | [submodule "libs/sst/sst-jucegui"] 11 | path = libs/sst/sst-jucegui 12 | url = https://github.com/surge-synthesizer/sst-jucegui 13 | [submodule "libs/sst/sst-basic-blocks"] 14 | path = libs/sst/sst-basic-blocks 15 | url = https://github.com/surge-synthesizer/sst-basic-blocks 16 | [submodule "libs/fmt"] 17 | path = libs/fmt 18 | url = https://github.com/fmtlib/fmt.git 19 | [submodule "libs/clap-wrapper"] 20 | path = libs/clap-wrapper 21 | url = https://github.com/free-audio/clap-wrapper 22 | [submodule "libs/sst/sst-plugininfra"] 23 | path = libs/sst/sst-plugininfra 24 | url = https://github.com/surge-synthesizer/sst-plugininfra 25 | [submodule "libs/simde"] 26 | path = libs/simde 27 | url = https://github.com/simd-everywhere/simde 28 | [submodule "libs/sst/sst-cpputils"] 29 | path = libs/sst/sst-cpputils 30 | url = https://github.com/surge-synthesizer/sst-cpputils 31 | [submodule "libs/sst/sst-filters"] 32 | path = libs/sst/sst-filters 33 | url = https://github.com/surge-synthesizer/sst-filters 34 | [submodule "libs/sst/sst-clap-helpers"] 35 | path = libs/sst/sst-clap-helpers 36 | url = https://github.com/surge-synthesizer/sst-clap-helpers 37 | [submodule "libs/sst/sst-voicemanager"] 38 | path = libs/sst/sst-voicemanager 39 | url = https://github.com/surge-synthesizer/sst-voicemanager 40 | [submodule "libs/MTS-ESP"] 41 | path = libs/MTS-ESP 42 | url = https://github.com/ODDSound/MTS-ESP 43 | [submodule "libs/sst/sst-effects"] 44 | path = libs/sst/sst-effects 45 | url = https://github.com/surge-synthesizer/sst-effects 46 | [submodule "libs/sst/sst-waveshapers"] 47 | path = libs/sst/sst-waveshapers 48 | url = https://github.com/surge-synthesizer/sst-waveshapers 49 | [submodule "libs/ni-midi2"] 50 | path = libs/ni-midi2 51 | url = https://github.com/midi2-dev/ni-midi2.git 52 | -------------------------------------------------------------------------------- /cmake/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Shortcircuit XT - a Surge Synth Team product 3 | * 4 | * A fully featured creative sampler, available as a standalone 5 | * and plugin for multiple platforms. 6 | * 7 | * Copyright 2019 - 2023, Various authors, as described in the github 8 | * transaction log. 9 | * 10 | * ShortcircuitXT is released under the Gnu General Public Licence 11 | * V3 or later (GPL-3.0-or-later). The license is found in the file 12 | * "LICENSE" in the root of this repository or at 13 | * https://www.gnu.org/licenses/gpl-3.0.en.html 14 | * 15 | * Individual sections of code which comprises ShortcircuitXT in this 16 | * repository may also be used under an MIT license. Please see the 17 | * section "Licensing" in "README.md" for details. 18 | * 19 | * ShortcircuitXT is inspired by, and shares code with, the 20 | * commercial product Shortcircuit 1 and 2, released by VemberTech 21 | * in the mid 2000s. The code for Shortcircuit 2 was opensourced in 22 | * 2020 at the outset of this project. 23 | * 24 | * All source for ShortcircuitXT is available at 25 | * https://github.com/surge-synthesizer/shortcircuit-xt 26 | */ 27 | #ifndef SCXT_SRC_VERSION_H 28 | #define SCXT_SRC_VERSION_H 29 | 30 | namespace sst::conduit 31 | { 32 | struct build 33 | { 34 | static const char *MajorVersionStr; 35 | static const int MajorVersionInt; 36 | 37 | static const char *SubVersionStr; 38 | static const int SubVersionInt; 39 | 40 | static const char *ReleaseNumberStr; 41 | static const char *ReleaseStr; 42 | 43 | static const char *GitHash; 44 | static const char *GitBranch; 45 | 46 | static const char *BuildNumberStr; 47 | 48 | static const char *FullVersionStr; 49 | static const char *BuildHost; 50 | static const char *BuildArch; 51 | 52 | static const char *BuildLocation; // Local or Pipeline 53 | 54 | static const char *BuildDate; 55 | static const char *BuildTime; 56 | static const char *BuildYear; 57 | 58 | // Some features from cmake 59 | static const char *CMAKE_INSTALL_PREFIX; 60 | static const char *BuildCompiler; 61 | }; 62 | } // namespace scxt 63 | 64 | #endif //__version__ 65 | -------------------------------------------------------------------------------- /resources/Inter/README.txt: -------------------------------------------------------------------------------- 1 | Inter Variable Font 2 | =================== 3 | 4 | This download contains Inter as both a variable font and static fonts. 5 | 6 | Inter is a variable font with these axes: 7 | slnt 8 | wght 9 | 10 | This means all the styles are contained in a single file: 11 | Inter-VariableFont_slnt,wght.ttf 12 | 13 | If your app fully supports variable fonts, you can now pick intermediate styles 14 | that aren’t available as static fonts. Not all apps support variable fonts, and 15 | in those cases you can use the static font files for Inter: 16 | static/Inter-Thin.ttf 17 | static/Inter-ExtraLight.ttf 18 | static/Inter-Light.ttf 19 | static/Inter-Regular.ttf 20 | static/Inter-Medium.ttf 21 | static/Inter-SemiBold.ttf 22 | static/Inter-Bold.ttf 23 | static/Inter-ExtraBold.ttf 24 | static/Inter-Black.ttf 25 | 26 | Get started 27 | ----------- 28 | 29 | 1. Install the font files you want to use 30 | 31 | 2. Use your app's font picker to view the font family and all the 32 | available styles 33 | 34 | Learn more about variable fonts 35 | ------------------------------- 36 | 37 | https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts 38 | https://variablefonts.typenetwork.com 39 | https://medium.com/variable-fonts 40 | 41 | In desktop apps 42 | 43 | https://theblog.adobe.com/can-variable-fonts-illustrator-cc 44 | https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts 45 | 46 | Online 47 | 48 | https://developers.google.com/fonts/docs/getting_started 49 | https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide 50 | https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts 51 | 52 | Installing fonts 53 | 54 | MacOS: https://support.apple.com/en-us/HT201749 55 | Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux 56 | Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows 57 | 58 | Android Apps 59 | 60 | https://developers.google.com/fonts/docs/android 61 | https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts 62 | 63 | License 64 | ------- 65 | Please read the full license text (OFL.txt) to understand the permissions, 66 | restrictions and requirements for usage, redistribution, and modification. 67 | 68 | You can use them in your products & projects – print or digital, 69 | commercial or otherwise. 70 | 71 | This isn't legal advice, please consider consulting a lawyer and see the full 72 | license for all details. 73 | -------------------------------------------------------------------------------- /scripts/fix_file_comments.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | 4 | use File::Find; 5 | use File::Basename; 6 | 7 | find( 8 | { 9 | wanted => \&findfiles, 10 | }, 11 | 'src' 12 | ); 13 | 14 | sub findfiles 15 | { 16 | 17 | $header = < ${q}.bak") || die "Cant open BAK $!"; 50 | 51 | $nonBlank = 0; 52 | $inComment = 0; 53 | while() 54 | { 55 | if ($nonBlank) 56 | { 57 | print OUT 58 | } 59 | else 60 | { 61 | if (m:^\s*/\*:) { 62 | $inComment = 1; 63 | } 64 | elsif (m:\s*\*/:) 65 | { 66 | print OUT $header; 67 | $nonBlank = true; 68 | $inComment = false; 69 | } 70 | elsif ($inComment) 71 | { 72 | 73 | } 74 | elsif (m:^//:) 75 | { 76 | 77 | } 78 | else 79 | { 80 | print OUT $header; 81 | $nonBlank = true; 82 | print OUT; 83 | 84 | } 85 | } 86 | } 87 | close(IN); 88 | close(OUT); 89 | system("mv ${q}.bak ${q}"); 90 | system("clang-format -i ${q}"); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Conduit Plugins 2 | 3 | Conduit is a project with four goals 4 | 5 | 1. Be a clear example of "clap first" development. It is a clap and everything - formats, standalones, and more - follows. As such it acts as a fulsome test bed for the clap wrapper project. 6 | 2. Be an example and test bed for our library factoring ahead of XT2 and SCXT. The plugins refer well factored reusable libraries for their work 7 | 3. Be a set of awesome small plugins where the activation cost for a new plugin is "low" 8 | 4. work out the mechanics of using juce as a gui but not as a plug-in or dsp layer 9 | 10 | ## Status 11 | 12 | Conduit is still experimental. Things are getting better but lots 13 | still doesn't work, and a few things only work on Mac. 14 | 15 | You can download the nightly from here, but your best bet is: 16 | 17 | ```bash 18 | git clone https://github.com/surge-synthesizer/conduit 19 | cd conduit 20 | git submodule update --init --recursive 21 | cmake -Bbuild -DCMAKE_BUILD_TYPE=Release 22 | cmake --build build --target conduit_all 23 | ``` 24 | 25 | results in a `Conduit.clap` and `Conduit.vst3` in `build/conduit_products`. 26 | 27 | The best way to interact with this project is to reac us via: 28 | 29 | 1. The `#conduit-dev` channel on surge discord 30 | 2. The `#wrappers` channel on clap discord or 31 | 3. Github Issues here 32 | 33 | 34 | ## An Important Note about Licensing 35 | 36 | The source code in the 'src' directory and associated CMake files in 37 | the root and source directories are licensed under the MIT License, as 38 | described in LICENSE.md. 39 | 40 | However, this project serves multiple purposes, including being an 41 | example of clap-first development and a test-bed and highlight for 42 | the surge synth team libraries. The majority of those libraries, 43 | as well as several of the SDK dependencies (VST3 and JUCE) are 44 | GPL3 code. As a result *the combined work of building this project, 45 | if distributed, would trigger the GPL3 license terms*. 46 | 47 | This is a bit odd, right? If you have GPL3 dependencies and are MIT 48 | using the code in an MIT context is difficult. You could copy the code 49 | but you would have to strip the dependencies. But that's exactly the 50 | goal here. Since one of the purposes of this project is to highlight 51 | CLAP-first development, we want the way we structure our projects, our builds, 52 | and our plugin to be something that developers can copy and use no matter 53 | what their chosen software license. 54 | 55 | So copy this project, strip out our dependencies, inject your own DSP 56 | and UI, but keep the cmake and layout shenanigans, borrow our tricks, 57 | and that's totally fine. You get no GPL3 contagion. But *dont* strip out 58 | the GPL3 dependencies and you will trigger the GPL3 clauses. 59 | 60 | If you don't understand this, basically it means "feel free to read the code 61 | in src/ and cmake/ and copy the ideas, but if you find you have to use 62 | the libraries in libs/ be careful". 63 | -------------------------------------------------------------------------------- /.github/workflows/build-release.yml: -------------------------------------------------------------------------------- 1 | name: Build Installer 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - 'v**' 9 | 10 | defaults: 11 | run: 12 | shell: bash 13 | 14 | jobs: 15 | build_plugin: 16 | name: Release Build - ${{ matrix.os }} 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | matrix: 20 | include: 21 | - os: windows-latest 22 | - os: macos-latest 23 | - os: ubuntu-latest 24 | steps: 25 | - name: Checkout code 26 | uses: actions/checkout@v4 27 | with: 28 | submodules: recursive 29 | 30 | - uses: apple-actions/import-codesign-certs@v3 31 | if: runner.os == 'macOS' 32 | with: 33 | p12-file-base64: ${{ secrets.MAC_CERTS_P12 }} 34 | p12-password: ${{ secrets.CERT_PWD }} 35 | 36 | - name: Prepare for JUCE 37 | uses: surge-synthesizer/sst-githubactions/prepare-for-juce@main 38 | with: 39 | os: ${{ runner.os }} 40 | 41 | 42 | - name: Build release version 43 | run: | 44 | 45 | export MAC_SIGNING_CERT="${{ secrets.MAC_SIGNING_CERT_NAME }}" 46 | export MAC_INSTALLING_CERT="${{ secrets.MAC_INSTALLING_CERT_NAME }}" 47 | 48 | export MAC_SIGNING_ID="${{ secrets.MAC_SIGNING_ID }}" 49 | export MAC_SIGNING_1UPW="${{ secrets.MAC_SIGNING_1UPW }}" 50 | export MAC_SIGNING_TEAM="${{ secrets.MAC_SIGNING_TEAM }}" 51 | 52 | cmake -S . -B ./build -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" 53 | cmake --build ./build --config Release --target conduit_installer --parallel 3 54 | 55 | - name: Show Installer Directory 56 | run: | 57 | ls -l ./build/installer 58 | 59 | - name: Upload artifact 60 | uses: actions/upload-artifact@v4 61 | with: 62 | path: build/installer 63 | name: build-${{ matrix.os }} 64 | 65 | publish-monique-nightly: 66 | name: Publish Conduits Nightly 67 | if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'surge-synthesizer' }} 68 | runs-on: ubuntu-latest 69 | needs: [build_plugin] 70 | steps: 71 | - name: Upload to Nightly 72 | uses: surge-synthesizer/sst-githubactions/upload-to-release@main 73 | with: 74 | tag: Nightly 75 | reuse_tag: true 76 | create_tag: false 77 | token: ${{ secrets.GITHUB_TOKEN }} 78 | 79 | - name: Post to Discord 80 | uses: surge-synthesizer/sst-githubactions/discord-release-notify@main 81 | with: 82 | webhook: ${{ secrets.DISCORD_OTHER_WEBHOOK }} 83 | tag: Nightly 84 | title: "A Conduit Nightly is Available" 85 | 86 | 87 | publish-monique-release: 88 | name: Publish Conduit Release 89 | if: startsWith(github.ref, 'refs/tags/v') && github.repository_owner == 'surge-synthesizer' 90 | runs-on: ubuntu-latest 91 | needs: [build_plugin] 92 | steps: 93 | - name: Upload to Release 94 | uses: surge-synthesizer/sst-githubactions/upload-to-release@main 95 | with: 96 | tag: ${{ github.ref_name }} 97 | reuse_tag: false 98 | create_tag: true 99 | token: ${{ secrets.GITHUB_TOKEN }} 100 | 101 | - name: Post to Discord 102 | uses: surge-synthesizer/sst-githubactions/discord-release-notify@main 103 | with: 104 | webhook: ${{ secrets.DISCORD_OTHER_WEBHOOK }} 105 | tag: ${{ github.ref_name }} 106 | title: "A Conduit Release is Available" 107 | subtitle: "Release ${{ github.ref_name }}" 108 | 109 | -------------------------------------------------------------------------------- /cmake/versiontools.cmake: -------------------------------------------------------------------------------- 1 | 2 | find_package(Git) 3 | 4 | if( EXISTS ${CONDUITSRC}/VERSION_GIT_INFO ) 5 | message( STATUS "VERSION_GIT_INFO file is present; using that rather than git query" ) 6 | # Line 2 is the branch, line 3 is the hash 7 | execute_process( 8 | COMMAND sed -n "2p" ${CONDUITSRC}/VERSION_GIT_INFO 9 | WORKING_DIRECTORY ${CONDUITSRC} 10 | OUTPUT_VARIABLE GIT_BRANCH 11 | OUTPUT_STRIP_TRAILING_WHITESPACE 12 | ) 13 | 14 | execute_process( 15 | COMMAND sed -n "3p" ${CONDUITSRC}/VERSION_GIT_INFO 16 | WORKING_DIRECTORY ${CONDUITSRC} 17 | OUTPUT_VARIABLE GIT_COMMIT_HASH 18 | OUTPUT_STRIP_TRAILING_WHITESPACE 19 | ) 20 | 21 | elseif( Git_FOUND ) 22 | execute_process( 23 | COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD 24 | WORKING_DIRECTORY ${CONDUITSRC} 25 | OUTPUT_VARIABLE GIT_BRANCH 26 | OUTPUT_STRIP_TRAILING_WHITESPACE 27 | ) 28 | 29 | execute_process( 30 | COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD 31 | WORKING_DIRECTORY ${CONDUITSRC} 32 | OUTPUT_VARIABLE GIT_COMMIT_HASH 33 | OUTPUT_STRIP_TRAILING_WHITESPACE 34 | ) 35 | endif() 36 | 37 | if("${GIT_BRANCH}" STREQUAL "") 38 | message(WARNING "Could not determine Git branch, using placeholder.") 39 | set(GIT_BRANCH "git-no-branch") 40 | endif() 41 | if ("${GIT_COMMIT_HASH}" STREQUAL "") 42 | message(WARNING "Could not determine Git commit hash, using placeholder.") 43 | set(GIT_COMMIT_HASH "git-no-commit") 44 | endif() 45 | 46 | if( WIN32 ) 47 | set( CONDUIT_BUILD_ARCH "Intel" ) 48 | else() 49 | execute_process( 50 | COMMAND uname -m 51 | OUTPUT_VARIABLE CONDUIT_BUILD_ARCH 52 | OUTPUT_STRIP_TRAILING_WHITESPACE 53 | ) 54 | endif() 55 | 56 | cmake_host_system_information(RESULT CONDUIT_BUILD_FQDN QUERY FQDN ) 57 | 58 | message( STATUS "Setting up Conduit version:" ) 59 | message( STATUS " Git hash is ${GIT_COMMIT_HASH} and branch is ${GIT_BRANCH}" ) 60 | message( STATUS " Build host is ${CONDUIT_BUILD_FQDN}" ) 61 | message( STATUS " Build architecture is ${CONDUIT_BUILD_ARCH}" ) 62 | 63 | if( ${AZURE_PIPELINE} ) 64 | message( STATUS "Azure pipeline build" ) 65 | set( lpipeline "pipeline" ) 66 | else() 67 | message( STATUS "Developer local build" ) 68 | set( lpipeline "local" ) 69 | endif() 70 | 71 | if(${GIT_BRANCH} STREQUAL "main" ) 72 | if( ${AZURE_PIPELINE} ) 73 | set( lverpatch "nightly" ) 74 | else() 75 | set( lverpatch "main" ) 76 | endif() 77 | set( lverrel "999" ) 78 | set( fverpatch ${lverpatch} ) 79 | else() 80 | string( FIND ${GIT_BRANCH} "release/" RLOC ) 81 | if( ${RLOC} EQUAL 0 ) 82 | message( STATUS "Configuring a Release build from '${GIT_BRANCH}'" ) 83 | string( SUBSTRING ${GIT_BRANCH} 11 100 RV ) # that's release slash 1.7. 84 | string( FIND ${RV} "." DLOC ) 85 | if( NOT ( DLOC EQUAL -1 ) ) 86 | math( EXPR DLP1 "${DLOC} + 1" ) 87 | string( SUBSTRING ${RV} ${DLP1} 100 LRV ) # skip that first dots 88 | set( lverrel ${LRV} ) 89 | else() 90 | set( lverrel "99" ) 91 | endif() 92 | set( lverpatch "stable-${lverrel}" ) 93 | set( fverpatch "${lverrel}" ) 94 | else() 95 | set( lverpatch ${GIT_BRANCH} ) 96 | set( fverpatch ${lverpatch} ) 97 | set( lverrel "1000" ) 98 | endif() 99 | endif() 100 | 101 | set( CONDUIT_FULL_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${fverpatch}.${GIT_COMMIT_HASH}" ) 102 | set( CONDUIT_MAJOR_VERSION "${PROJECT_VERSION_MAJOR}" ) 103 | set( CONDUIT_SUB_VERSION "${PROJECT_VERSION_MINOR}" ) 104 | set( CONDUIT_RELEASE_VERSION "${lverpatch}" ) 105 | set( CONDUIT_RELEASE_NUMBER "${lverrel}" ) 106 | set( CONDUIT_BUILD_HASH "${GIT_COMMIT_HASH}" ) 107 | set( CONDUIT_BUILD_LOCATION "${lpipeline}" ) 108 | 109 | string( TIMESTAMP CONDUIT_BUILD_DATE "%Y-%m-%d" ) 110 | string( TIMESTAMP CONDUIT_BUILD_YEAR "%Y" ) 111 | string( TIMESTAMP CONDUIT_BUILD_TIME "%H:%M:%S" ) 112 | 113 | message( STATUS "Using CONDUIT_VERSION=${CONDUIT_FULL_VERSION}" ) 114 | 115 | message( STATUS "Configuring ${CONDUITBLD}/geninclude/version.cpp" ) 116 | configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in 117 | ${CONDUITBLD}/geninclude/version.cpp ) 118 | file(WRITE ${CONDUITBLD}/geninclude/githash.txt ${GIT_COMMIT_HASH}) 119 | -------------------------------------------------------------------------------- /src/chord-memory/chord-memory-editor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "chord-memory.h" 23 | #include 24 | 25 | #include "sst/jucegui/accessibility/Ignored.h" 26 | #include "sst/jucegui/components/NamedPanel.h" 27 | #include "sst/jucegui/components/WindowPanel.h" 28 | #include "sst/jucegui/components/Knob.h" 29 | #include "sst/jucegui/components/MultiSwitch.h" 30 | #include "conduit-shared/editor-base.h" 31 | 32 | namespace sst::conduit::chord_memory::editor 33 | { 34 | namespace jcmp = sst::jucegui::components; 35 | namespace jdat = sst::jucegui::data; 36 | 37 | using cps_t = sst::conduit::chord_memory::ConduitChordMemory; 38 | using uicomm_t = cps_t::UICommunicationBundle; 39 | 40 | struct ConduitChordMemoryEditor; 41 | 42 | struct ControlsPanel : juce::Component 43 | { 44 | uicomm_t &uic; 45 | 46 | ControlsPanel(uicomm_t &p, ConduitChordMemoryEditor &e); 47 | ~ControlsPanel() { time->setSource(nullptr); } 48 | 49 | void resized() override 50 | { 51 | auto b = getLocalBounds().reduced(5); 52 | auto ks = std::min(b.getWidth(), b.getHeight()); 53 | 54 | auto bx = b.withHeight(ks).withWidth(ks - 18).reduced(4); 55 | time->setBounds(bx); 56 | } 57 | 58 | std::unique_ptr time; 59 | }; 60 | 61 | struct ConduitChordMemoryEditor : public sst::jucegui::accessibility::IgnoredComponent, 62 | shared::ToolTipMixIn 63 | { 64 | uicomm_t &uic; 65 | using comms_t = sst::conduit::shared::EditorCommunicationsHandler; 67 | std::unique_ptr comms; 68 | 69 | ConduitChordMemoryEditor(uicomm_t &p) : uic(p) 70 | { 71 | comms = std::make_unique(p, *this); 72 | 73 | ctrlPanel = std::make_unique("Controls"); 74 | addAndMakeVisible(*ctrlPanel); 75 | 76 | auto oct = std::make_unique(uic, *this); 77 | ctrlPanel->setContentAreaComponent(std::move(oct)); 78 | 79 | setSize(600, 700); 80 | 81 | comms->startProcessing(); 82 | } 83 | 84 | ~ConduitChordMemoryEditor() { comms->stopProcessing(); } 85 | 86 | std::unique_ptr unisonSpread; 87 | 88 | void resized() override { ctrlPanel->setBounds(getLocalBounds()); } 89 | 90 | std::unique_ptr ctrlPanel; 91 | }; 92 | 93 | ControlsPanel::ControlsPanel(sst::conduit::chord_memory::editor::uicomm_t &p, 94 | sst::conduit::chord_memory::editor::ConduitChordMemoryEditor &e) 95 | : uic(p) 96 | { 97 | // TODO: Stepped knob 98 | time = std::make_unique(); 99 | addAndMakeVisible(*time); 100 | e.comms->attachDiscreteToParam(time.get(), cps_t::paramIds::pmKeyShift); 101 | } 102 | } // namespace sst::conduit::chord_memory::editor 103 | 104 | namespace sst::conduit::chord_memory 105 | { 106 | std::unique_ptr ConduitChordMemory::createEditor() 107 | { 108 | uiComms.refreshUIValues = true; 109 | auto innards = 110 | std::make_unique(uiComms); 111 | auto editor = std::make_unique>(uiComms); 112 | editor->setContentComponent(std::move(innards)); 113 | 114 | return editor; 115 | } 116 | } // namespace sst::conduit::chord_memory 117 | -------------------------------------------------------------------------------- /src/multiout-synth/multiout-synth-editor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "multiout-synth.h" 23 | #include 24 | #include 25 | 26 | #include "sst/jucegui/accessibility/Ignored.h" 27 | #include "sst/jucegui/components/NamedPanel.h" 28 | #include "sst/jucegui/components/WindowPanel.h" 29 | #include "sst/jucegui/components/Knob.h" 30 | #include "sst/jucegui/components/MultiSwitch.h" 31 | #include "sst/jucegui/components/ToggleButton.h" 32 | #include "sst/jucegui/components/TextPushButton.h" 33 | #include "sst/jucegui/component-adapters/DiscreteToReference.h" 34 | 35 | #include "sst/jucegui/data/Continuous.h" 36 | #include "conduit-shared/editor-base.h" 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | namespace sst::conduit::multiout_synth::editor 43 | { 44 | namespace jcmp = sst::jucegui::components; 45 | namespace jcad = sst::jucegui::component_adapters; 46 | namespace jdat = sst::jucegui::data; 47 | 48 | using cps_t = sst::conduit::multiout_synth::ConduitMultiOutSynth; 49 | using uicomm_t = cps_t::UICommunicationBundle; 50 | 51 | struct ConduitMultiOutSynthEditor; 52 | 53 | struct ConduitMultiOutSynthEditor : public sst::jucegui::accessibility::IgnoredComponent, 54 | shared::ToolTipMixIn 55 | { 56 | uicomm_t &uic; 57 | using comms_t = sst::conduit::shared::EditorCommunicationsHandler; 59 | std::unique_ptr comms; 60 | 61 | struct Controls : juce::Component 62 | { 63 | ConduitMultiOutSynthEditor &editor; 64 | Controls(ConduitMultiOutSynthEditor &e) : editor(e) {} 65 | void resized() override {} 66 | void paint(juce::Graphics &g) override 67 | { 68 | g.setFont(20); 69 | g.setColour(juce::Colours::white); 70 | g.drawText("Coming Soon", getLocalBounds(), juce::Justification::centred); 71 | } 72 | }; 73 | 74 | ConduitMultiOutSynthEditor(uicomm_t &p) : uic(p) 75 | { 76 | comms = std::make_unique(p, *this); 77 | comms->startProcessing(); 78 | 79 | ctrlPanel = std::make_unique("Controls"); 80 | ctrlPanel->setContentAreaComponent(std::make_unique(*this)); 81 | addAndMakeVisible(*ctrlPanel); 82 | 83 | setSize(400, 300); 84 | } 85 | 86 | ~ConduitMultiOutSynthEditor() { comms->stopProcessing(); } 87 | 88 | void resized() override { ctrlPanel->setBounds(getLocalBounds()); } 89 | std::unique_ptr evtPanel, ctrlPanel; 90 | juce::Typeface::Ptr fixedFace{nullptr}; 91 | bool noteOnly{true}; 92 | }; 93 | } // namespace sst::conduit::multiout_synth::editor 94 | 95 | namespace sst::conduit::multiout_synth 96 | { 97 | std::unique_ptr ConduitMultiOutSynth::createEditor() 98 | { 99 | uiComms.refreshUIValues = true; 100 | auto innards = 101 | std::make_unique(uiComms); 102 | auto editor = std::make_unique>(uiComms); 103 | innards->fixedFace = editor->loadFont("Anonymous_Pro/AnonymousPro-Regular.ttf"); 104 | editor->setContentComponent(std::move(innards)); 105 | 106 | return editor; 107 | } 108 | 109 | } // namespace sst::conduit::multiout_synth 110 | -------------------------------------------------------------------------------- /src/chord-memory/chord-memory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_CHORD_MEMORY_CHORD_MEMORY_H 23 | #define CONDUIT_SRC_CHORD_MEMORY_CHORD_MEMORY_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include "sst/basic-blocks/params/ParamMetadata.h" 34 | #include "conduit-shared/clap-base-class.h" 35 | 36 | namespace sst::conduit::chord_memory 37 | { 38 | 39 | static constexpr int nParams = 1; 40 | 41 | struct ConduitChordMemoryConfig 42 | { 43 | static constexpr int nParams{sst::conduit::chord_memory::nParams}; 44 | static constexpr bool baseClassProvidesMonoModSupport{true}; 45 | static constexpr bool usesSpecializedMessages{false}; 46 | struct PatchExtension 47 | { 48 | static constexpr bool hasExtension{true}; 49 | 50 | std::array, 128> companionNotes; // the +24 and -24 notes generated by a key 51 | 52 | bool toXml(TiXmlElement &); 53 | bool fromXml(TiXmlElement *); 54 | }; 55 | 56 | struct DataCopyForUI 57 | { 58 | std::atomic updateCount{0}; 59 | std::atomic isProcessing{false}; 60 | }; 61 | 62 | static const clap_plugin_descriptor *getDescription(); 63 | }; 64 | 65 | struct ConduitChordMemory 66 | : sst::conduit::shared::ClapBaseClass 67 | { 68 | ConduitChordMemory(const clap_host *host); 69 | ~ConduitChordMemory(); 70 | 71 | bool activate(double sampleRate, uint32_t minFrameCount, 72 | uint32_t maxFrameCount) noexcept override 73 | { 74 | setSampleRate(sampleRate); 75 | return true; 76 | } 77 | 78 | enum paramIds : uint32_t 79 | { 80 | pmKeyShift = 7241 81 | }; 82 | 83 | bool implementsNotePorts() const noexcept override { return true; } 84 | uint32_t notePortsCount(bool isInput) const noexcept override { return 1; } 85 | bool notePortsInfo(uint32_t index, bool isInput, 86 | clap_note_port_info *info) const noexcept override; 87 | 88 | clap_process_status process(const clap_process *process) noexcept override; 89 | 90 | bool startProcessing() noexcept override 91 | { 92 | uiComms.dataCopyForUI.isProcessing = true; 93 | uiComms.dataCopyForUI.updateCount++; 94 | return true; 95 | } 96 | void stopProcessing() noexcept override 97 | { 98 | uiComms.dataCopyForUI.isProcessing = false; 99 | uiComms.dataCopyForUI.updateCount++; 100 | } 101 | 102 | protected: 103 | std::unique_ptr createEditor() override; 104 | std::atomic refreshUIValues{false}; 105 | 106 | std::array, 16> activeNotes{}; 107 | std::array, 128>, 16> notesStartedFromParent{}; 108 | 109 | // Generate 0 or more output note events from a single input note event 110 | void handleMIDI1NoteChange(const clap_output_events *ov, const clap_event_midi *under, 111 | int16_t channel, int16_t key, double vel, bool on); 112 | void handleClapNoteChange(const clap_output_events *ov, const clap_event_note *under, 113 | int16_t channel, int16_t key, double vel, bool on); 114 | 115 | // If these return TRUE then you need to send a note on or off out. 116 | bool updateNoteOnOffData(int16_t channel, int16_t key, bool isOn); 117 | 118 | public: 119 | float *keyShift; 120 | }; 121 | } // namespace sst::conduit::chord_memory 122 | 123 | #endif // CONDUIT_POLYMETRIC_DELAY_H 124 | -------------------------------------------------------------------------------- /resources/Inter/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /resources/Anonymous_Pro/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Mark Simonson (http://www.ms-studio.com, mark@marksimonson.com), 2 | with Reserved Font Name Anonymous Pro. 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /src/multiout-synth/multiout-synth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_MULTIOUT_SYNTH_MULTIOUT_SYNTH_H 23 | #define CONDUIT_SRC_MULTIOUT_SYNTH_MULTIOUT_SYNTH_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "sst/basic-blocks/dsp/QuadratureOscillators.h" 31 | #include "sst/basic-blocks/modulators/DAHDEnvelope.h" 32 | #include "sst/cpputils/ring_buffer.h" 33 | 34 | #include "conduit-shared/clap-base-class.h" 35 | 36 | namespace sst::conduit::multiout_synth 37 | { 38 | static constexpr int nOuts = 4; 39 | static constexpr int nParams = 3 * nOuts; 40 | 41 | struct ConduitMultiOutSynthConfig 42 | { 43 | static constexpr int nParams{sst::conduit::multiout_synth::nParams}; 44 | static constexpr bool baseClassProvidesMonoModSupport{true}; 45 | static constexpr bool usesSpecializedMessages{false}; 46 | using PatchExtension = sst::conduit::shared::EmptyPatchExtension; 47 | struct DataCopyForUI 48 | { 49 | std::atomic updateCount{0}; 50 | std::atomic isProcessing{false}; 51 | }; 52 | 53 | static const clap_plugin_descriptor *getDescription(); 54 | }; 55 | 56 | struct ConduitMultiOutSynth 57 | : sst::conduit::shared::ClapBaseClass 58 | { 59 | static constexpr int blockSize{8}; 60 | ConduitMultiOutSynth(const clap_host *host); 61 | ~ConduitMultiOutSynth(); 62 | 63 | bool activate(double sampleRate, uint32_t minFrameCount, 64 | uint32_t maxFrameCount) noexcept override 65 | { 66 | setSampleRate(sampleRate); 67 | return true; 68 | } 69 | 70 | enum paramIds : uint32_t 71 | { 72 | pmFreq0 = 8675309, 73 | pmFreq1, 74 | pmFreq2, 75 | pmFreq3, 76 | 77 | pmTime0 = 7421, 78 | pmTime1, 79 | pmTime2, 80 | pmTime3, 81 | 82 | pmMute0 = 8824, 83 | pmMute1, 84 | pmMute2, 85 | pmMute3, 86 | 87 | }; 88 | 89 | bool implementsAudioPorts() const noexcept override { return true; } 90 | uint32_t audioPortsCount(bool isInput) const noexcept override { return isInput ? 0 : nOuts; } 91 | bool audioPortsInfo(uint32_t index, bool isInput, 92 | clap_audio_port_info *info) const noexcept override; 93 | 94 | bool implementsNotePorts() const noexcept override { return true; } 95 | uint32_t notePortsCount(bool isInput) const noexcept override { return isInput ? 1 : 0; } 96 | bool notePortsInfo(uint32_t index, bool isInput, 97 | clap_note_port_info *info) const noexcept override; 98 | 99 | clap_process_status process(const clap_process *process) noexcept override; 100 | 101 | bool startProcessing() noexcept override 102 | { 103 | uiComms.dataCopyForUI.isProcessing = true; 104 | uiComms.dataCopyForUI.updateCount++; 105 | return true; 106 | } 107 | void stopProcessing() noexcept override 108 | { 109 | uiComms.dataCopyForUI.isProcessing = false; 110 | uiComms.dataCopyForUI.updateCount++; 111 | } 112 | 113 | typedef std::unordered_map PatchPluginExtension; 114 | 115 | protected: 116 | std::unique_ptr createEditor() override; 117 | std::atomic refreshUIValues{false}; 118 | 119 | struct Chan 120 | { 121 | Chan(ConduitMultiOutSynth *s) : env(s) {} 122 | int chan{0}; 123 | float timeSinceTrigger{0}; 124 | sst::basic_blocks::dsp::QuadratureOscillator osc; 125 | sst::basic_blocks::modulators::DAHDEnvelope env; 126 | float *freq; 127 | float *time; 128 | float *mute; 129 | }; 130 | std::array chans; 131 | 132 | public: 133 | inline float envelope_rate_linear_nowrap(float f) 134 | { 135 | return blockSize * sampleRateInv * pow(2.f, -f); 136 | } 137 | 138 | public: 139 | uint64_t samplePos{0}; 140 | }; 141 | } // namespace sst::conduit::multiout_synth 142 | 143 | #endif // CONDUIT_POLYMETRIC_DELAY_H 144 | -------------------------------------------------------------------------------- /src/mts-to-noteexpression/mts-to-noteexpression.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_MTS_TO_NOTEEXPRESSION_MTS_TO_NOTEEXPRESSION_H 23 | #define CONDUIT_SRC_MTS_TO_NOTEEXPRESSION_MTS_TO_NOTEEXPRESSION_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include "sst/basic-blocks/params/ParamMetadata.h" 33 | #include "sst/cpputils/ring_buffer.h" 34 | 35 | #include "conduit-shared/clap-base-class.h" 36 | 37 | struct MTSClient; 38 | 39 | namespace sst::conduit::mts_to_noteexpression 40 | { 41 | static constexpr int nParams = 2; 42 | 43 | struct ConduitMTSToNoteExpressionConfig 44 | { 45 | static constexpr int nParams{sst::conduit::mts_to_noteexpression::nParams}; 46 | static constexpr bool baseClassProvidesMonoModSupport{true}; 47 | static constexpr bool usesSpecializedMessages{false}; 48 | using PatchExtension = sst::conduit::shared::EmptyPatchExtension; 49 | struct DataCopyForUI 50 | { 51 | std::atomic isProcessing{false}; 52 | std::atomic updateCount{0}; 53 | MTSClient *mtsClient{nullptr}; 54 | 55 | std::array, 16> 56 | noteRemaining; // -1 means still held, otherwise its the time 57 | std::atomic noteRemainingUpdate{0}; 58 | }; 59 | 60 | static const clap_plugin_descriptor *getDescription(); 61 | }; 62 | 63 | struct ConduitMTSToNoteExpression 64 | : sst::conduit::shared::ClapBaseClass 66 | { 67 | ConduitMTSToNoteExpression(const clap_host *host); 68 | ~ConduitMTSToNoteExpression(); 69 | 70 | bool activate(double sampleRate, uint32_t minFrameCount, 71 | uint32_t maxFrameCount) noexcept override 72 | { 73 | setSampleRate(sampleRate); 74 | secondsPerSample = 1.0 / sampleRate; 75 | return true; 76 | } 77 | 78 | enum paramIds : uint32_t 79 | { 80 | pmRetuneHeld = 1024, 81 | pmReleaseTuning = 5027 82 | }; 83 | 84 | bool implementsAudioPorts() const noexcept override { return false; } 85 | 86 | bool implementsNotePorts() const noexcept override { return true; } 87 | uint32_t notePortsCount(bool isInput) const noexcept override { return 1; } 88 | bool notePortsInfo(uint32_t index, bool isInput, 89 | clap_note_port_info *info) const noexcept override; 90 | 91 | /* 92 | * I have an unacceptably crude state dump and restore. If you want to 93 | * improve it, PRs welcome! But it's just like any other read-and-write-goop 94 | * from-a-stream api really. 95 | */ 96 | 97 | clap_process_status process(const clap_process *process) noexcept override; 98 | 99 | bool startProcessing() noexcept override 100 | { 101 | uiComms.dataCopyForUI.isProcessing = true; 102 | uiComms.dataCopyForUI.updateCount++; 103 | return true; 104 | } 105 | void stopProcessing() noexcept override 106 | { 107 | uiComms.dataCopyForUI.isProcessing = false; 108 | uiComms.dataCopyForUI.updateCount++; 109 | } 110 | 111 | typedef std::unordered_map PatchPluginExtension; 112 | 113 | MTSClient *mtsClient{nullptr}; 114 | char priorScaleName[CLAP_NAME_SIZE]; 115 | std::array, 16> 116 | noteRemaining{}; // -1 means still held, otherwise its the time 117 | std::array, 16> sclTuning; 118 | 119 | int lastUIUpdate{0}; 120 | 121 | float retuningFor(int key, int channel) const; 122 | bool tuningActive(); 123 | bool retuneHeldNotes(); 124 | 125 | float *postNoteRelease{nullptr}; 126 | float *retunHeld{nullptr}; 127 | double secondsPerSample{0}; 128 | 129 | protected: 130 | std::unique_ptr createEditor() override; 131 | std::atomic refreshUIValues{false}; 132 | 133 | uint64_t samplePos{0}; 134 | }; 135 | } // namespace sst::conduit::mts_to_noteexpression 136 | 137 | #endif // CONDUIT_POLYMETRIC_DELAY_H 138 | -------------------------------------------------------------------------------- /src/ring-modulator/ring-modulator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_RING_MODULATOR_RING_MODULATOR_H 23 | #define CONDUIT_SRC_RING_MODULATOR_RING_MODULATOR_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include "sst/basic-blocks/params/ParamMetadata.h" 33 | #include "sst/basic-blocks/dsp/QuadratureOscillators.h" 34 | #include "sst/filters/HalfRateFilter.h" 35 | 36 | #include "conduit-shared/clap-base-class.h" 37 | 38 | namespace sst::conduit::ring_modulator 39 | { 40 | 41 | static constexpr int nParams = 4; 42 | 43 | struct ConduitRingModulatorConfig 44 | { 45 | static constexpr int nParams{sst::conduit::ring_modulator::nParams}; 46 | static constexpr bool baseClassProvidesMonoModSupport{true}; 47 | static constexpr bool usesSpecializedMessages{false}; 48 | using PatchExtension = sst::conduit::shared::EmptyPatchExtension; 49 | struct DataCopyForUI 50 | { 51 | std::atomic updateCount{0}; 52 | std::atomic isProcessing{false}; 53 | }; 54 | 55 | static const clap_plugin_descriptor *getDescription(); 56 | }; 57 | 58 | struct ConduitRingModulator 59 | : sst::conduit::shared::ClapBaseClass 60 | { 61 | ConduitRingModulator(const clap_host *host); 62 | ~ConduitRingModulator(); 63 | 64 | bool activate(double sampleRate, uint32_t minFrameCount, 65 | uint32_t maxFrameCount) noexcept override 66 | { 67 | setSampleRate(sampleRate); 68 | return true; 69 | } 70 | 71 | enum paramIds : uint32_t 72 | { 73 | pmMixLevel = 842, 74 | 75 | pmSource = 712, 76 | pmInternalSourceFrequency = 1524, 77 | 78 | pmAlgo = 17 79 | }; 80 | 81 | enum Algos : uint32_t 82 | { 83 | algoDigital = 0, 84 | algoAnalog = 1 85 | }; 86 | 87 | enum Source : uint32_t 88 | { 89 | srcInternal = 0, 90 | srcSidechain = 1 91 | }; 92 | 93 | float delayInSamples{1000}; 94 | 95 | bool implementsAudioPorts() const noexcept override { return true; } 96 | uint32_t audioPortsCount(bool isInput) const noexcept override { return isInput ? 2 : 1; } 97 | bool audioPortsInfo(uint32_t index, bool isInput, 98 | clap_audio_port_info *info) const noexcept override; 99 | 100 | /* 101 | * I have an unacceptably crude state dump and restore. If you want to 102 | * improve it, PRs welcome! But it's just like any other read-and-write-goop 103 | * from-a-stream api really. 104 | */ 105 | 106 | clap_process_status process(const clap_process *process) noexcept override; 107 | void handleInboundEvent(const clap_event_header_t *evt); 108 | 109 | bool startProcessing() noexcept override 110 | { 111 | uiComms.dataCopyForUI.isProcessing = true; 112 | uiComms.dataCopyForUI.updateCount++; 113 | return true; 114 | } 115 | void stopProcessing() noexcept override 116 | { 117 | uiComms.dataCopyForUI.isProcessing = false; 118 | uiComms.dataCopyForUI.updateCount++; 119 | } 120 | 121 | protected: 122 | bool implementsLatency() const noexcept override { return true; } 123 | uint32_t latencyGet() const noexcept override { return blockSize; } 124 | 125 | public: 126 | typedef std::unordered_map PatchPluginExtension; 127 | 128 | protected: 129 | std::unique_ptr createEditor() override; 130 | std::atomic refreshUIValues{false}; 131 | 132 | sst::filters::HalfRate::HalfRateFilter hr_up, hr_scup, hr_down; 133 | sst::basic_blocks::dsp::QuadratureOscillator internalSource; 134 | 135 | static constexpr int blockSize{4}, blockSizeOS{blockSize << 1}; 136 | float inputBuf alignas(16)[2][blockSize]; 137 | float inputOS alignas(16)[2][blockSizeOS]; 138 | float sidechainBuf alignas(16)[2][blockSize]; 139 | float sourceOS alignas(16)[2][blockSizeOS]; 140 | 141 | float outBuf[2][blockSize]{}; 142 | float inMixBuf[2][blockSize]{}; 143 | 144 | uint32_t pos{0}; 145 | 146 | lag_t mix, freq; 147 | 148 | float *algo, *src; 149 | }; 150 | } // namespace sst::conduit::ring_modulator 151 | 152 | #endif // CONDUIT_POLYMETRIC_DELAY_H 153 | -------------------------------------------------------------------------------- /src/clap-event-monitor/clap-event-monitor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_CLAP_EVENT_MONITOR_CLAP_EVENT_MONITOR_H 23 | #define CONDUIT_SRC_CLAP_EVENT_MONITOR_CLAP_EVENT_MONITOR_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include "sst/basic-blocks/params/ParamMetadata.h" 33 | #include "sst/cpputils/ring_buffer.h" 34 | 35 | #include "conduit-shared/clap-base-class.h" 36 | 37 | namespace sst::conduit::clap_event_monitor 38 | { 39 | static constexpr int nParams = 3; 40 | 41 | struct ConduitClapEventMonitorConfig 42 | { 43 | static constexpr int nParams{sst::conduit::clap_event_monitor::nParams}; 44 | static constexpr bool baseClassProvidesMonoModSupport{true}; 45 | static constexpr bool usesSpecializedMessages{false}; 46 | using PatchExtension = sst::conduit::shared::EmptyPatchExtension; 47 | struct DataCopyForUI 48 | { 49 | std::atomic updateCount{0}; 50 | std::atomic isProcessing{false}; 51 | 52 | std::atomic processedSamples{0}; 53 | static constexpr uint32_t maxEventSize{4096}, maxEvents{4096}; 54 | 55 | struct evtCopy 56 | { 57 | unsigned char data[maxEventSize]; 58 | void assign(const clap_event_header_t *e) 59 | { 60 | assert(e->size < maxEventSize); 61 | memcpy(data, e, std::min(e->size, maxEventSize)); 62 | } 63 | 64 | const clap_event_header_t *view() const 65 | { 66 | return reinterpret_cast(data); 67 | } 68 | }; 69 | sst::cpputils::SimpleRingBuffer eventBuf; 70 | 71 | unsigned char eventData[maxEventSize * maxEvents]; 72 | void writeEventTo(const clap_event_header_t *e) 73 | { 74 | evtCopy ec; 75 | ec.assign(e); 76 | eventBuf.push(ec); 77 | } 78 | 79 | // Read happens ui thread. Might be corrupted. Big queue. Copy soon. 80 | const clap_event_header_t *readEventFrom(int idx) 81 | { 82 | return (const clap_event_header_t *)(eventData + maxEventSize * idx); 83 | } 84 | }; 85 | 86 | static const clap_plugin_descriptor *getDescription(); 87 | }; 88 | 89 | struct ConduitClapEventMonitor 90 | : sst::conduit::shared::ClapBaseClass 91 | { 92 | ConduitClapEventMonitor(const clap_host *host); 93 | ~ConduitClapEventMonitor(); 94 | 95 | bool activate(double sampleRate, uint32_t minFrameCount, 96 | uint32_t maxFrameCount) noexcept override 97 | { 98 | setSampleRate(sampleRate); 99 | return true; 100 | } 101 | 102 | enum paramIds : uint32_t 103 | { 104 | pmStepped = 24842, 105 | 106 | pmAuto = 912, 107 | pmMod = 2112 108 | }; 109 | 110 | bool implementsAudioPorts() const noexcept override { return true; } 111 | uint32_t audioPortsCount(bool isInput) const noexcept override { return isInput ? 0 : 1; } 112 | bool audioPortsInfo(uint32_t index, bool isInput, 113 | clap_audio_port_info *info) const noexcept override; 114 | 115 | bool implementsNotePorts() const noexcept override { return true; } 116 | uint32_t notePortsCount(bool isInput) const noexcept override { return 1; } 117 | bool notePortsInfo(uint32_t index, bool isInput, 118 | clap_note_port_info *info) const noexcept override; 119 | 120 | /* 121 | * I have an unacceptably crude state dump and restore. If you want to 122 | * improve it, PRs welcome! But it's just like any other read-and-write-goop 123 | * from-a-stream api really. 124 | */ 125 | 126 | clap_process_status process(const clap_process *process) noexcept override; 127 | 128 | bool startProcessing() noexcept override 129 | { 130 | uiComms.dataCopyForUI.isProcessing = true; 131 | uiComms.dataCopyForUI.updateCount++; 132 | return true; 133 | } 134 | void stopProcessing() noexcept override 135 | { 136 | uiComms.dataCopyForUI.isProcessing = false; 137 | uiComms.dataCopyForUI.updateCount++; 138 | } 139 | 140 | typedef std::unordered_map PatchPluginExtension; 141 | 142 | protected: 143 | std::unique_ptr createEditor() override; 144 | std::atomic refreshUIValues{false}; 145 | 146 | uint64_t samplePos{0}; 147 | }; 148 | } // namespace sst::conduit::clap_event_monitor 149 | 150 | #endif // CONDUIT_POLYMETRIC_DELAY_H 151 | -------------------------------------------------------------------------------- /src/clap-event-monitor/clap-event-monitor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "clap-event-monitor.h" 23 | #include "juce_gui_basics/juce_gui_basics.h" 24 | #include "version.h" 25 | 26 | namespace sst::conduit::clap_event_monitor 27 | { 28 | const clap_plugin_descriptor *ConduitClapEventMonitorConfig::getDescription() 29 | { 30 | static const char *features[] = {CLAP_PLUGIN_FEATURE_INSTRUMENT, CLAP_PLUGIN_FEATURE_DELAY, 31 | nullptr}; 32 | static clap_plugin_descriptor desc = {CLAP_VERSION, 33 | "org.surge-synth-team.conduit.clap-event-monitor", 34 | "Conduit Clap Event Monitor", 35 | "Surge Synth Team", 36 | "https://surge-synth-team.org", 37 | "", 38 | "", 39 | sst::conduit::build::FullVersionStr, 40 | "The Conduit ClapEventMonitor is a work in progress", 41 | &features[0]}; 42 | return &desc; 43 | } 44 | 45 | ConduitClapEventMonitor::ConduitClapEventMonitor(const clap_host *host) 46 | : sst::conduit::shared::ClapBaseClass( 47 | host) 48 | { 49 | auto autoFlag = CLAP_PARAM_IS_AUTOMATABLE; 50 | auto steppedFlag = autoFlag | CLAP_PARAM_IS_STEPPED; 51 | auto modFlag = autoFlag | CLAP_PARAM_IS_MODULATABLE | CLAP_PARAM_IS_MODULATABLE_PER_CHANNEL | 52 | CLAP_PARAM_IS_MODULATABLE_PER_KEY | CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID; 53 | 54 | paramDescriptions.push_back(ParamDesc() 55 | .asInt() 56 | .withID(pmStepped) 57 | .withName("Stepped Param") 58 | .withGroupName("Monitor") 59 | .withRange(-4, 17) 60 | .withDefault(2) 61 | .withLinearScaleFormatting("things") 62 | .withFlags(steppedFlag)); 63 | 64 | paramDescriptions.push_back(ParamDesc() 65 | .asPercentBipolar() 66 | .withID(pmAuto) 67 | .withName("Automatable Param") 68 | .withGroupName("Monitor") 69 | .withFlags(autoFlag)); 70 | 71 | paramDescriptions.push_back(ParamDesc() 72 | .asPercentBipolar() 73 | .withID(pmMod) 74 | .withName("Modulatable Param") 75 | .withGroupName("Monitor") 76 | .withFlags(modFlag)); 77 | 78 | configureParams(); 79 | 80 | clapJuceShim = std::make_unique(this); 81 | clapJuceShim->setResizable(true); 82 | } 83 | 84 | ConduitClapEventMonitor::~ConduitClapEventMonitor() {} 85 | 86 | bool ConduitClapEventMonitor::audioPortsInfo(uint32_t index, bool isInput, 87 | clap_audio_port_info *info) const noexcept 88 | { 89 | static constexpr uint32_t outId{72}; 90 | if (isInput) 91 | { 92 | return false; 93 | } 94 | else 95 | { 96 | info->id = outId; 97 | info->in_place_pair = CLAP_INVALID_ID; 98 | strncpy(info->name, "main output", sizeof(info->name)); 99 | info->flags = CLAP_AUDIO_PORT_IS_MAIN; 100 | info->channel_count = 2; 101 | info->port_type = CLAP_PORT_STEREO; 102 | 103 | return true; 104 | } 105 | return false; 106 | } 107 | 108 | bool ConduitClapEventMonitor::notePortsInfo(uint32_t index, bool isInput, 109 | clap_note_port_info *info) const noexcept 110 | { 111 | info->id = isInput ? 178 : 23; 112 | info->supported_dialects = CLAP_NOTE_DIALECT_MIDI | CLAP_NOTE_DIALECT_CLAP; 113 | info->preferred_dialect = CLAP_NOTE_DIALECT_CLAP; 114 | strncpy(info->name, (std::string("Note ") + (isInput ? "In" : "Out")).c_str(), 115 | CLAP_NAME_SIZE - 1); 116 | return true; 117 | } 118 | clap_process_status ConduitClapEventMonitor::process(const clap_process *process) noexcept 119 | { 120 | auto ev = process->in_events; 121 | auto ov = process->out_events; 122 | auto sz = ev->size(ev); 123 | 124 | if (samplePos == 0) 125 | { 126 | uiComms.dataCopyForUI.writeEventTo((const clap_event_header_t *)process->transport); 127 | } 128 | samplePos += process->frames_count; 129 | if (samplePos > sampleRate / 30) // 30 hz transport udpate is probably fine 130 | { 131 | samplePos = 0; 132 | } 133 | for (auto i = 0U; i < sz; ++i) 134 | { 135 | auto et = ev->get(ev, i); 136 | uiComms.dataCopyForUI.writeEventTo(et); 137 | 138 | ov->try_push(ov, et); 139 | } 140 | 141 | return CLAP_PROCESS_CONTINUE; 142 | } 143 | 144 | } // namespace sst::conduit::clap_event_monitor 145 | -------------------------------------------------------------------------------- /src/polysynth/effects-impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_POLYSYNTH_EFFECTS_IMPL_H 23 | #define CONDUIT_SRC_POLYSYNTH_EFFECTS_IMPL_H 24 | 25 | #include "polysynth.h" 26 | 27 | namespace sst::conduit::polysynth 28 | { 29 | namespace details 30 | { 31 | struct FXUtilityBase 32 | { 33 | ConduitPolysynth *synth{nullptr}; 34 | FXUtilityBase(ConduitPolysynth *p, ConduitPolysynth *, ConduitPolysynth *) : synth(p) {} 35 | }; 36 | } // namespace details 37 | 38 | struct SharedConfig 39 | { 40 | using BaseClass = details::FXUtilityBase; 41 | using GlobalStorage = ConduitPolysynth; 42 | using EffectStorage = ConduitPolysynth; 43 | using BiquadAdapter = SharedConfig; 44 | using ValueStorage = ConduitPolysynth; 45 | static constexpr int blockSize{PolysynthVoice::blockSize}; 46 | 47 | static float envelopeRateLinear(GlobalStorage *g, float f) 48 | { 49 | return blockSize * sampleRateInv(g) * pow(2.f, -f); 50 | } 51 | static bool isDeactivated(EffectStorage *, int) { return false; } 52 | static bool isExtended(EffectStorage *, int) { return false; } 53 | static float rand01(GlobalStorage *g) { return g->urd(g->gen); } 54 | static double sampleRate(GlobalStorage *g) { return g->sampleRate; } 55 | static double sampleRateInv(GlobalStorage *g) { return g->sampleRateInv; } 56 | static float noteToPitch(GlobalStorage *g, float note) 57 | { 58 | return g->note_to_pitch_ignoring_tuning(note); 59 | } 60 | static float noteToPitchInv(GlobalStorage *g, float note) { return 1.f / noteToPitch(g, note); } 61 | static float noteToPitchIgnoringTuning(GlobalStorage *g, float note) 62 | { 63 | return g->note_to_pitch_ignoring_tuning(note); 64 | } 65 | static float dbToLinear(GlobalStorage *g, float val) { return g->dbToLinear(val); } 66 | }; 67 | 68 | struct PhaserConfig : SharedConfig 69 | { 70 | static constexpr std::array, 3> presets{ 71 | {{-0.35, -0.75, 0.75, 1.32193, 1, 0.493306, 0.5, 0, 6, 0.35, 3, 0}, 72 | {0, -0.2, 1, 0, 1, 0.5, 0.5, 0, 4, 0.4, 1, 0}, 73 | {-0.75, 0.797292, -0.3, -1, 0.994367, 1, 0.4, 0, 2, 0.7, 0, 0}}}; 74 | 75 | static int presetIndex(const BaseClass *bc) 76 | { 77 | auto s = bc->synth; 78 | auto pn = s->paramToValue[ConduitPolysynth::pmModFXPreset]; 79 | return (int)std::round(*pn); 80 | } 81 | 82 | static float temposyncRatio(GlobalStorage *g, EffectStorage *, int) { return 1.; } 83 | 84 | static float floatValueAt(const BaseClass *bc, const ValueStorage *, int idx) 85 | { 86 | if (idx == PhaserFX::ph_mix) 87 | { 88 | return *(bc->synth->paramToValue[ConduitPolysynth::pmModFXMix]); 89 | } 90 | 91 | if (idx == PhaserFX::ph_mod_rate) 92 | { 93 | return *(bc->synth->paramToValue[ConduitPolysynth::pmModFXRate]); 94 | } 95 | return presets[presetIndex(bc)][idx]; 96 | } 97 | static int intValueAt(const BaseClass *bc, const ValueStorage *, int idx) 98 | { 99 | return (int)presets[presetIndex(bc)][idx]; 100 | } 101 | }; 102 | 103 | struct FlangerConfig : SharedConfig 104 | { 105 | static constexpr std::array, 3> presets{ 106 | {{0, 3, 0.892556, 0.725957, 2, 65, 4.86, 0.12, 0, 0, 0.5}, 107 | {0, 1, 0, 0.415452, 3, 70, 10.6554, 0.18, 0.12, 0, 0.7}, 108 | {2, 0, -.241504, 0.737768, 4, 55, 8, 0.72, 0.2, 0, -0.9}}}; 109 | 110 | static float temposyncRatio(GlobalStorage *g, EffectStorage *, int) { return 1.; } 111 | 112 | static int presetIndex(const BaseClass *bc) 113 | { 114 | auto s = bc->synth; 115 | auto pn = s->paramToValue[ConduitPolysynth::pmModFXPreset]; 116 | return (int)std::round(*pn); 117 | } 118 | static float floatValueAt(const BaseClass *bc, const ValueStorage *, int idx) 119 | { 120 | if (idx == FlangerFX::fl_mix) 121 | { 122 | return *(bc->synth->paramToValue[ConduitPolysynth::pmModFXMix]); 123 | } 124 | if (idx == FlangerFX::fl_rate) 125 | { 126 | return *(bc->synth->paramToValue[ConduitPolysynth::pmModFXRate]); 127 | } 128 | return presets[presetIndex(bc)][idx]; 129 | } 130 | static int intValueAt(const BaseClass *bc, const ValueStorage *, int idx) 131 | { 132 | return (int)presets[presetIndex(bc)][idx]; 133 | } 134 | }; 135 | 136 | struct Reverb1Config : SharedConfig 137 | { 138 | static constexpr std::array, 3> presets{ 139 | {{-6.59756, 2, 0.346016, 0.921308, 0.247126, -21.462, -12.3269, -7.86461, 70, 0.221484, 0}, 140 | {-5.74933, 1, 0.953453, 1.38905, 0.0735602, -25.0596, -6.88824, 1.06879, 70, 0.213031, 141 | 3.2536}, 142 | {-8, 3, 0.445348, 1.61731, 0.444403, -21.3982, 14.0079, 14.4087, 70, 0.193794, 0}}}; 143 | 144 | static float temposyncRatio(GlobalStorage *g, EffectStorage *, int) { return 1.; } 145 | 146 | static int presetIndex(const BaseClass *bc) 147 | { 148 | auto s = bc->synth; 149 | auto pn = s->paramToValue[ConduitPolysynth::pmRevFXPreset]; 150 | return (int)std::round(*pn); 151 | } 152 | 153 | static float floatValueAt(const BaseClass *bc, const ValueStorage *, int idx) 154 | { 155 | if (idx == ReverbFX::rev1_mix) 156 | { 157 | return *(bc->synth->paramToValue[ConduitPolysynth::pmRevFXMix]); 158 | } 159 | if (idx == ReverbFX::rev1_decaytime) 160 | { 161 | return *(bc->synth->paramToValue[ConduitPolysynth::pmRevFXTime]); 162 | } 163 | return presets[presetIndex(bc)][idx]; 164 | } 165 | static int intValueAt(const BaseClass *bc, const ValueStorage *, int idx) 166 | { 167 | return (int)presets[presetIndex(bc)][idx]; 168 | } 169 | }; 170 | } // namespace sst::conduit::polysynth 171 | 172 | #endif // CONDUIT_EFFECTS_IMPL_H 173 | -------------------------------------------------------------------------------- /src/ring-modulator/ring-modulator-editor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "ring-modulator.h" 23 | #include 24 | 25 | #include "sst/jucegui/accessibility/Ignored.h" 26 | #include "sst/jucegui/components/NamedPanel.h" 27 | #include "sst/jucegui/components/WindowPanel.h" 28 | #include "sst/jucegui/components/Knob.h" 29 | #include "sst/jucegui/components/Label.h" 30 | #include "sst/jucegui/components/MultiSwitch.h" 31 | #include "sst/jucegui/data/Continuous.h" 32 | #include "conduit-shared/editor-base.h" 33 | 34 | namespace sst::conduit::ring_modulator::editor 35 | { 36 | namespace jcmp = sst::jucegui::components; 37 | namespace jdat = sst::jucegui::data; 38 | 39 | using cps_t = sst::conduit::ring_modulator::ConduitRingModulator; 40 | using uicomm_t = cps_t::UICommunicationBundle; 41 | 42 | struct ConduitRingModulatorEditor; 43 | 44 | struct RMPanel : sst::jucegui::accessibility::IgnoredComponent 45 | { 46 | uicomm_t &uic; 47 | 48 | RMPanel(uicomm_t &p, ConduitRingModulatorEditor &e); 49 | 50 | void resized() override 51 | { 52 | auto sz = 110; 53 | auto bx = getLocalBounds().reduced(13, 0).withWidth(sz).withHeight(sz); 54 | mix->setBounds(bx); 55 | auto mlBx = bx.translated(0, sz).withHeight(20); 56 | mixLabel->setBounds(mlBx); 57 | 58 | mlBx = mlBx.translated(0, 20).withHeight(30); 59 | 60 | model->setBounds(mlBx); 61 | } 62 | 63 | std::unique_ptr mix; 64 | std::unique_ptr model; 65 | std::unique_ptr mixLabel; 66 | }; 67 | 68 | struct SourcePanel : sst::jucegui::accessibility::IgnoredComponent 69 | { 70 | uicomm_t &uic; 71 | 72 | SourcePanel(uicomm_t &p, ConduitRingModulatorEditor &e); 73 | ~SourcePanel() {} 74 | 75 | void resized() override 76 | { 77 | auto sz = 110; 78 | auto bx = getLocalBounds().reduced(13, 0).withWidth(sz).withHeight(sz); 79 | freq->setBounds(bx); 80 | auto mlBx = bx.translated(0, sz).withHeight(20); 81 | freqLabel->setBounds(mlBx); 82 | 83 | mlBx = mlBx.translated(0, 20).withHeight(30); 84 | src->setBounds(mlBx); 85 | } 86 | 87 | std::unique_ptr freq; 88 | std::unique_ptr src; 89 | std::unique_ptr mixLabel, freqLabel; 90 | }; 91 | 92 | struct ConduitRingModulatorEditor : public sst::jucegui::accessibility::IgnoredComponent, 93 | shared::ToolTipMixIn 94 | { 95 | uicomm_t &uic; 96 | using comms_t = sst::conduit::shared::EditorCommunicationsHandler; 98 | std::unique_ptr comms; 99 | 100 | ConduitRingModulatorEditor(uicomm_t &p) : uic(p) 101 | { 102 | comms = std::make_unique(p, *this); 103 | 104 | ctrlPanel = std::make_unique("Ring Modulation"); 105 | addAndMakeVisible(*ctrlPanel); 106 | 107 | auto oct = std::make_unique(uic, *this); 108 | ctrlPanel->setContentAreaComponent(std::move(oct)); 109 | 110 | sourcePanel = std::make_unique("Modulator Source"); 111 | auto soct = std::make_unique(uic, *this); 112 | sourcePanel->setContentAreaComponent(std::move(soct)); 113 | addAndMakeVisible(*sourcePanel); 114 | 115 | setSize(300, 200); 116 | 117 | comms->startProcessing(); 118 | } 119 | 120 | ~ConduitRingModulatorEditor() { comms->stopProcessing(); } 121 | 122 | std::unique_ptr unisonSpread; 123 | 124 | void resized() override 125 | { 126 | auto cpW = 150; 127 | ctrlPanel->setBounds(getLocalBounds().withWidth(cpW)); 128 | sourcePanel->setBounds(getLocalBounds().withTrimmedLeft(cpW)); 129 | } 130 | 131 | std::unique_ptr ctrlPanel, sourcePanel; 132 | }; 133 | 134 | RMPanel::RMPanel(sst::conduit::ring_modulator::editor::uicomm_t &p, 135 | sst::conduit::ring_modulator::editor::ConduitRingModulatorEditor &e) 136 | : uic(p) 137 | { 138 | mix = std::make_unique(); 139 | addAndMakeVisible(*mix); 140 | e.comms->attachContinuousToParam(mix.get(), cps_t::paramIds::pmMixLevel); 141 | 142 | mixLabel = std::make_unique(); 143 | mixLabel->setText("Mix"); 144 | addAndMakeVisible(*mixLabel); 145 | 146 | model = std::make_unique(jcmp::MultiSwitch::HORIZONTAL); 147 | 148 | addAndMakeVisible(*model); 149 | e.comms->attachDiscreteToParam(model.get(), cps_t::paramIds::pmAlgo); 150 | } 151 | 152 | SourcePanel::SourcePanel(sst::conduit::ring_modulator::editor::uicomm_t &p, 153 | sst::conduit::ring_modulator::editor::ConduitRingModulatorEditor &e) 154 | : uic(p) 155 | { 156 | freq = std::make_unique(); 157 | freq->pathDrawMode = jucegui::components::Knob::ALWAYS_FROM_MIN; 158 | addAndMakeVisible(*freq); 159 | e.comms->attachContinuousToParam(freq.get(), cps_t::paramIds::pmInternalSourceFrequency); 160 | 161 | freqLabel = std::make_unique(); 162 | freqLabel->setText("Frequency"); 163 | addAndMakeVisible(*freqLabel); 164 | 165 | src = std::make_unique(jcmp::MultiSwitch::HORIZONTAL); 166 | addAndMakeVisible(*src); 167 | e.comms->attachDiscreteToParam(src.get(), cps_t::paramIds::pmSource); 168 | } 169 | } // namespace sst::conduit::ring_modulator::editor 170 | 171 | namespace sst::conduit::ring_modulator 172 | { 173 | std::unique_ptr ConduitRingModulator::createEditor() 174 | { 175 | uiComms.refreshUIValues = true; 176 | auto innards = 177 | std::make_unique(uiComms); 178 | auto editor = std::make_unique>(uiComms); 179 | editor->setContentComponent(std::move(innards)); 180 | 181 | return editor; 182 | } 183 | 184 | } // namespace sst::conduit::ring_modulator -------------------------------------------------------------------------------- /src/midi2-sawsynth/midi2-sawsynth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_MIDI2_SAWSYNTH_MIDI2_SAWSYNTH_H 23 | #define CONDUIT_SRC_MIDI2_SAWSYNTH_MIDI2_SAWSYNTH_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include "sst/basic-blocks/params/ParamMetadata.h" 33 | #include "sst/cpputils/ring_buffer.h" 34 | 35 | #include "conduit-shared/clap-base-class.h" 36 | #include "sst/voicemanager/voicemanager.h" 37 | 38 | namespace sst::conduit::midi2_sawsynth 39 | { 40 | static constexpr int nParams = 3; 41 | 42 | struct ConduitMIDI2SawSynthConfig 43 | { 44 | static constexpr int nParams{sst::conduit::midi2_sawsynth::nParams}; 45 | static constexpr bool baseClassProvidesMonoModSupport{true}; 46 | static constexpr bool usesSpecializedMessages{false}; 47 | using PatchExtension = sst::conduit::shared::EmptyPatchExtension; 48 | struct DataCopyForUI 49 | { 50 | std::atomic updateCount{0}; 51 | std::atomic isProcessing{false}; 52 | 53 | std::atomic processedSamples{0}; 54 | static constexpr uint32_t maxEventSize{4096}, maxEvents{4096}; 55 | 56 | struct evtCopy 57 | { 58 | unsigned char data[maxEventSize]; 59 | void assign(const clap_event_header_t *e) 60 | { 61 | assert(e->size < maxEventSize); 62 | memcpy(data, e, std::max(e->size, maxEventSize)); 63 | } 64 | 65 | const clap_event_header_t *view() const 66 | { 67 | return reinterpret_cast(data); 68 | } 69 | }; 70 | sst::cpputils::SimpleRingBuffer eventBuf; 71 | 72 | unsigned char eventData[maxEventSize * maxEvents]; 73 | void writeEventTo(const clap_event_header_t *e) 74 | { 75 | evtCopy ec; 76 | ec.assign(e); 77 | eventBuf.push(ec); 78 | } 79 | 80 | // Read happens ui thread. Might be corrupted. Big queue. Copy soon. 81 | const clap_event_header_t *readEventFrom(int idx) 82 | { 83 | return (const clap_event_header_t *)(eventData + maxEventSize * idx); 84 | } 85 | }; 86 | 87 | static const clap_plugin_descriptor *getDescription(); 88 | }; 89 | 90 | struct ConduitMIDI2SawSynth 91 | : sst::conduit::shared::ClapBaseClass 92 | { 93 | ConduitMIDI2SawSynth(const clap_host *host); 94 | ~ConduitMIDI2SawSynth(); 95 | 96 | bool activate(double sampleRate, uint32_t minFrameCount, 97 | uint32_t maxFrameCount) noexcept override 98 | { 99 | setSampleRate(sampleRate); 100 | return true; 101 | } 102 | 103 | enum paramIds : uint32_t 104 | { 105 | pmStepped = 24842, 106 | 107 | pmAuto = 912, 108 | pmMod = 2112 109 | }; 110 | 111 | bool implementsAudioPorts() const noexcept override { return true; } 112 | uint32_t audioPortsCount(bool isInput) const noexcept override { return isInput ? 0 : 1; } 113 | bool audioPortsInfo(uint32_t index, bool isInput, 114 | clap_audio_port_info *info) const noexcept override; 115 | 116 | bool implementsNotePorts() const noexcept override { return true; } 117 | uint32_t notePortsCount(bool isInput) const noexcept override { return isInput ? 1 : 0; } 118 | bool notePortsInfo(uint32_t index, bool isInput, 119 | clap_note_port_info *info) const noexcept override; 120 | 121 | /* 122 | * I have an unacceptably crude state dump and restore. If you want to 123 | * improve it, PRs welcome! But it's just like any other read-and-write-goop 124 | * from-a-stream api really. 125 | */ 126 | 127 | clap_process_status process(const clap_process *process) noexcept override; 128 | 129 | bool startProcessing() noexcept override 130 | { 131 | uiComms.dataCopyForUI.isProcessing = true; 132 | uiComms.dataCopyForUI.updateCount++; 133 | return true; 134 | } 135 | void stopProcessing() noexcept override 136 | { 137 | uiComms.dataCopyForUI.isProcessing = false; 138 | uiComms.dataCopyForUI.updateCount++; 139 | } 140 | 141 | typedef std::unordered_map PatchPluginExtension; 142 | 143 | struct M2Voice 144 | { 145 | }; 146 | struct VMConfig 147 | { 148 | static constexpr size_t maxVoiceCount{128}; 149 | using voice_t = M2Voice; 150 | }; 151 | 152 | protected: 153 | std::unique_ptr createEditor() override; 154 | std::atomic refreshUIValues{false}; 155 | 156 | public: 157 | using voiceManager_t = sst::voicemanager::VoiceManager; 158 | voiceManager_t voiceManager; 159 | 160 | void setVoiceEndCallback(std::function v) {} 161 | 162 | constexpr int32_t beginVoiceCreationTransaction(uint16_t port, uint16_t channel, uint16_t key, 163 | int32_t noteId, float velocity) 164 | { 165 | return 1; 166 | } 167 | constexpr void endVoiceCreationTransaction(uint16_t port, uint16_t channel, uint16_t key, 168 | int32_t noteId, float velocity) 169 | { 170 | } 171 | int32_t 172 | initializeMultipleVoices(std::array &voiceInitWorkingBuffer, 173 | uint16_t port, uint16_t channel, uint16_t key, int32_t noteId, 174 | float velocity, float retune) 175 | { 176 | voiceInitWorkingBuffer[0] = initializeVoice(port, channel, key, noteId, velocity, retune); 177 | return 1; 178 | } 179 | M2Voice *initializeVoice(uint16_t port, uint16_t channel, uint16_t key, int32_t noteId, 180 | float velocity, float retune) 181 | { 182 | return nullptr; 183 | } 184 | void releaseVoice(M2Voice *v, float velocity) {} 185 | void retriggerVoiceWithNewNoteID(M2Voice *v, int32_t noteid, float velocity) 186 | { 187 | CNDOUT << "retriggerVoice" << std::endl; 188 | } 189 | void setVoiceMIDIPitchBend(M2Voice *v, uint16_t pb14bit) {} 190 | void setMIDI1CC(M2Voice *v, int ccid, int val) {} 191 | 192 | uint64_t samplePos{0}; 193 | }; 194 | } // namespace sst::conduit::midi2_sawsynth 195 | 196 | #endif // CONDUIT_POLYMETRIC_DELAY_H 197 | -------------------------------------------------------------------------------- /src/polymetric-delay/polymetric-delay.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_POLYMETRIC_DELAY_POLYMETRIC_DELAY_H 23 | #define CONDUIT_SRC_POLYMETRIC_DELAY_POLYMETRIC_DELAY_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include "conduit-shared/sse-include.h" 34 | 35 | #include "sst/basic-blocks/params/ParamMetadata.h" 36 | #include "sst/basic-blocks/dsp/VUPeak.h" 37 | #include "sst/basic-blocks/dsp/SSESincDelayLine.h" 38 | #include "sst/basic-blocks/dsp/QuadratureOscillators.h" 39 | #include "sst/basic-blocks/tables/SincTableProvider.h" 40 | 41 | #include "sst/filters/BiquadFilter.h" 42 | 43 | #include "conduit-shared/clap-base-class.h" 44 | 45 | namespace sst::conduit::polymetric_delay 46 | { 47 | 48 | static constexpr int nParams = 45; 49 | 50 | struct ConduitPolymetricDelayConfig 51 | { 52 | static constexpr int nParams{sst::conduit::polymetric_delay::nParams}; 53 | static constexpr bool baseClassProvidesMonoModSupport{true}; 54 | static constexpr bool usesSpecializedMessages{false}; 55 | using PatchExtension = sst::conduit::shared::EmptyPatchExtension; 56 | struct DataCopyForUI 57 | { 58 | std::atomic updateCount{0}; 59 | std::atomic isProcessing{false}; 60 | 61 | std::atomic isPlayingOrRecording; 62 | std::atomic tempo; 63 | std::atomic bar_start; 64 | std::atomic bar_number; 65 | std::atomic song_pos_beats; 66 | 67 | std::atomic tsig_num, tsig_denom; 68 | 69 | std::atomic inVu[2], outVu[2], tapVu[4][2]; 70 | }; 71 | 72 | static const clap_plugin_descriptor *getDescription(); 73 | }; 74 | 75 | struct ConduitPolymetricDelay 76 | : sst::conduit::shared::ClapBaseClass 77 | { 78 | ConduitPolymetricDelay(const clap_host *host); 79 | ~ConduitPolymetricDelay(); 80 | 81 | bool activate(double sr, uint32_t minFrameCount, uint32_t maxFrameCount) noexcept override 82 | { 83 | setSampleRate(sr); 84 | recalcTaps(); 85 | recalcModulators(); 86 | 87 | inVU.setSampleRate(sr); 88 | outVU.setSampleRate(sr); 89 | for (auto &t : tapOutVU) 90 | t.setSampleRate(sr); 91 | return true; 92 | } 93 | 94 | static constexpr int nTaps{4}; 95 | enum paramIds : uint32_t 96 | { 97 | // These set of parameters are one-per 98 | pmDryLevel = 81, // in dsp, in gui 99 | 100 | // These set of parameters are per tap, so level on tap N = pmTapLevel + N. So you need 101 | // to space them by more than nTaps. I space them by 1000 here 102 | pmTapActive = 103241, // in dsp, in gui 103 | 104 | pmDelayTimeNTaps = 100241, // in dsp, in gui, need new widget 105 | pmDelayTimeEveryM = 101241, // in dsp, in gui, need new widget 106 | pmDelayModRate = 107241, 107 | pmDelayModDepth = 108241, 108 | 109 | pmTapLowCut = 104241, 110 | pmTapHighCut = 105241, 111 | 112 | pmTapLevel = 109241, // in dsp, in gui 113 | pmTapFeedback = 110241, // in dsp, in gui 114 | pmTapCrossFeedback = 110341, // in dsp in gui 115 | pmTapOutputPan = 110441 116 | 117 | }; 118 | 119 | inline bool isTapParam(clap_id pid, paramIds base) { return pid >= base && pid < base + nTaps; } 120 | 121 | bool implementsAudioPorts() const noexcept override { return true; } 122 | uint32_t audioPortsCount(bool isInput) const noexcept override { return 1; } 123 | bool audioPortsInfo(uint32_t index, bool isInput, 124 | clap_audio_port_info *info) const noexcept override; 125 | 126 | /* 127 | * I have an unacceptably crude state dump and restore. If you want to 128 | * improve it, PRs welcome! But it's just like any other read-and-write-goop 129 | * from-a-stream api really. 130 | */ 131 | 132 | clap_process_status process(const clap_process *process) noexcept override; 133 | void handleInboundEvent(const clap_event_header_t *evt); 134 | 135 | bool startProcessing() noexcept override 136 | { 137 | uiComms.dataCopyForUI.isProcessing = true; 138 | uiComms.dataCopyForUI.updateCount++; 139 | 140 | for (int i = 0; i < nTaps; ++i) 141 | { 142 | hp[i].suspend(); 143 | lp[i].suspend(); 144 | 145 | setTapFilterFrequencies(i); 146 | 147 | hp[i].coeff_instantize(); 148 | lp[i].coeff_instantize(); 149 | } 150 | 151 | return true; 152 | } 153 | 154 | void stopProcessing() noexcept override 155 | { 156 | uiComms.dataCopyForUI.isProcessing = false; 157 | uiComms.dataCopyForUI.updateCount++; 158 | } 159 | 160 | void setTapFilterFrequencies(int i) 161 | { 162 | hp[i].coeff_HP(hp[i].calc_omega(*(tapData[i].locut) / 12.0), 0.707); 163 | lp[i].coeff_LP2B(lp[i].calc_omega(*(tapData[i].hicut) / 12.0), 0.707); 164 | } 165 | 166 | typedef std::unordered_map PatchPluginExtension; 167 | 168 | sst::basic_blocks::dsp::VUPeak inVU, outVU, tapOutVU[nTaps]; 169 | uint32_t slowProcess{blockSize}; 170 | 171 | // For now our strategy is to just have honkin big delay lines 172 | // but we want to make these adapt with max time going forward 173 | // This is enough for about 20 seconds of delay at 48khz 174 | sst::basic_blocks::tables::SurgeSincTableProvider st{}; 175 | static constexpr uint32_t dlSize{1 << 20}; 176 | sst::basic_blocks::dsp::SSESincDelayLine delayLine[2]{st, st}; 177 | 178 | protected: 179 | std::unique_ptr createEditor() override; 180 | std::atomic refreshUIValues{false}; 181 | 182 | float tapPanMatrix[nTaps][4]; 183 | 184 | void specificParamChange(clap_id id, float val); 185 | 186 | public: 187 | float *dryLev; 188 | 189 | struct TapData 190 | { 191 | float *ntaps, *mbeats, *active; 192 | 193 | float *locut, *hicut, *pan; 194 | lag_t level, fblev, crossfblev, moddepth, modrate; 195 | 196 | sst::basic_blocks::dsp::QuadratureOscillator modulator; 197 | } tapData[nTaps]; 198 | 199 | float baseTapSamples[nTaps]{}; 200 | float tempo; 201 | 202 | void recalcTaps(); 203 | void recalcModulators(); 204 | 205 | static constexpr float modDepthScale{0.05f}; 206 | 207 | void onStateRestored() override 208 | { 209 | recalcTaps(); 210 | recalcModulators(); 211 | } 212 | 213 | std::array, nTaps> hp, lp; 214 | }; 215 | } // namespace sst::conduit::polymetric_delay 216 | 217 | #endif // CONDUIT_POLYMETRIC_DELAY_H 218 | -------------------------------------------------------------------------------- /src/multiout-synth/multiout-synth.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "multiout-synth.h" 23 | #include "juce_gui_basics/juce_gui_basics.h" 24 | #include "version.h" 25 | 26 | #include "sst/cpputils/constructors.h" 27 | #include "sst/cpputils/iterators.h" 28 | 29 | namespace sst::conduit::multiout_synth 30 | { 31 | const clap_plugin_descriptor *ConduitMultiOutSynthConfig::getDescription() 32 | { 33 | static const char *features[] = {CLAP_PLUGIN_FEATURE_INSTRUMENT, 34 | CLAP_PLUGIN_FEATURE_SYNTHESIZER, nullptr}; 35 | static clap_plugin_descriptor desc = {CLAP_VERSION, 36 | "org.surge-synth-team.conduit.multiout_synth", 37 | "Conduit MultiOut Clock Synth", 38 | "Surge Synth Team", 39 | "https://surge-synth-team.org", 40 | "", 41 | "", 42 | sst::conduit::build::FullVersionStr, 43 | "The Conduit MultiOutSynth is a work in progress", 44 | &features[0]}; 45 | return &desc; 46 | } 47 | 48 | ConduitMultiOutSynth::ConduitMultiOutSynth(const clap_host *host) 49 | : sst::conduit::shared::ClapBaseClass(host), 50 | chans{sst::cpputils::make_array(this)} 51 | { 52 | auto autoFlag = CLAP_PARAM_IS_AUTOMATABLE; 53 | auto steppedFlag = autoFlag | CLAP_PARAM_IS_STEPPED; 54 | 55 | auto nts = std::vector{60, 64, 67, 70}; 56 | for (int i = 0; i < nOuts; ++i) 57 | { 58 | chans[i].chan = i; 59 | paramDescriptions.push_back(ParamDesc() 60 | .asFloat() 61 | .withID(pmFreq0 + i) 62 | .withName("Frequency " + std::to_string(i + 1)) 63 | .withGroupName("Output " + std::to_string(i + 1)) 64 | .withRange(48, 96) 65 | .withDefault(nts[i] + 12) 66 | .withSemitoneZeroAtMIDIZeroFormatting() 67 | .withFlags(autoFlag)); 68 | 69 | paramDescriptions.push_back(ParamDesc() 70 | .asFloat() 71 | .withID(pmTime0 + i) 72 | .withName("Time Between Pulses " + std::to_string(i + 1)) 73 | .withGroupName("Output " + std::to_string(i + 1)) 74 | .withRange(0.1f, 2.f) 75 | .withDefault(0.5 + i * 0.278234) 76 | .withLinearScaleFormatting("seconds") 77 | .withFlags(autoFlag)); 78 | 79 | paramDescriptions.push_back(ParamDesc() 80 | .asBool() 81 | .withID(pmMute0 + i) 82 | .withName("Mute " + std::to_string(i + 1)) 83 | .withGroupName("Output " + std::to_string(i + 1)) 84 | .withFlags(steppedFlag)); 85 | } 86 | 87 | configureParams(); 88 | 89 | for (int i = 0; i < nOuts; ++i) 90 | { 91 | attachParam(pmFreq0 + i, chans[i].freq); 92 | attachParam(pmTime0 + i, chans[i].time); 93 | attachParam(pmMute0 + i, chans[i].mute); 94 | } 95 | 96 | clapJuceShim = std::make_unique(this); 97 | clapJuceShim->setResizable(true); 98 | } 99 | 100 | ConduitMultiOutSynth::~ConduitMultiOutSynth() {} 101 | 102 | bool ConduitMultiOutSynth::audioPortsInfo(uint32_t index, bool isInput, 103 | clap_audio_port_info *info) const noexcept 104 | { 105 | if (!isInput) 106 | { 107 | info->id = 652 + index * 13; 108 | info->in_place_pair = CLAP_INVALID_ID; 109 | if (index == 0) 110 | { 111 | strncpy(info->name, "main output", sizeof(info->name)); 112 | info->flags = CLAP_AUDIO_PORT_IS_MAIN; 113 | } 114 | else 115 | { 116 | snprintf(info->name, sizeof(info->name) - 1, "aux output %d", index); 117 | } 118 | info->channel_count = 2; 119 | info->port_type = CLAP_PORT_STEREO; 120 | 121 | return true; 122 | } 123 | return false; 124 | } 125 | 126 | bool ConduitMultiOutSynth::notePortsInfo(uint32_t index, bool isInput, 127 | clap_note_port_info *info) const noexcept 128 | { 129 | info->id = 8427; 130 | info->supported_dialects = CLAP_NOTE_DIALECT_MIDI | CLAP_NOTE_DIALECT_CLAP; 131 | info->preferred_dialect = CLAP_NOTE_DIALECT_CLAP; 132 | strncpy(info->name, "MIDI In", CLAP_NAME_SIZE - 1); 133 | return true; 134 | } 135 | 136 | clap_process_status ConduitMultiOutSynth::process(const clap_process *process) noexcept 137 | { 138 | auto ev = process->in_events; 139 | auto sz = ev->size(ev); 140 | 141 | // This pointer is the sentinel to our next event which we advance once an event is processed 142 | const clap_event_header_t *nextEvent{nullptr}; 143 | uint32_t nextEventIndex{0}; 144 | if (sz != 0) 145 | { 146 | nextEvent = ev->get(ev, nextEventIndex); 147 | } 148 | 149 | for (auto s = 0U; s < process->frames_count; ++s) 150 | { 151 | while (nextEvent && nextEvent->time == s) 152 | { 153 | handleParamBaseEvents(nextEvent); 154 | nextEventIndex++; 155 | if (nextEventIndex >= sz) 156 | nextEvent = nullptr; 157 | else 158 | nextEvent = ev->get(ev, nextEventIndex); 159 | } 160 | 161 | for (auto &c : chans) 162 | { 163 | c.env.process(0.0, 0.1, 0.1, 0.1, 0, 0, 0, true); 164 | c.timeSinceTrigger += sampleRateInv; 165 | if (c.timeSinceTrigger > *(c.time)) 166 | { 167 | c.timeSinceTrigger -= *(c.time); 168 | c.env.attackFrom(0, 0.1, 0, true); 169 | 170 | c.osc.setRate(2.0 * M_PI * 440.0 * pow(2.f, (*(c.freq) - 69) / 12) * 171 | dsamplerate_inv); 172 | } 173 | auto v = c.env.output * c.osc.u; 174 | c.osc.step(); 175 | process->audio_outputs[c.chan].data32[0][s] = v; 176 | process->audio_outputs[c.chan].data32[1][s] = v; 177 | } 178 | } 179 | 180 | return CLAP_PROCESS_CONTINUE; 181 | } 182 | 183 | } // namespace sst::conduit::multiout_synth 184 | -------------------------------------------------------------------------------- /src/mts-to-noteexpression/mts-to-noteexpression-editor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "mts-to-noteexpression.h" 23 | #include 24 | #include 25 | 26 | #include "sst/jucegui/accessibility/Ignored.h" 27 | #include "sst/jucegui/components/NamedPanel.h" 28 | #include "sst/jucegui/components/WindowPanel.h" 29 | #include "sst/jucegui/components/Knob.h" 30 | #include "sst/jucegui/components/MultiSwitch.h" 31 | #include "sst/jucegui/data/Continuous.h" 32 | #include "conduit-shared/editor-base.h" 33 | 34 | #include "libMTSClient.h" 35 | 36 | namespace sst::conduit::mts_to_noteexpression::editor 37 | { 38 | namespace jcmp = sst::jucegui::components; 39 | namespace jdat = sst::jucegui::data; 40 | 41 | using cps_t = sst::conduit::mts_to_noteexpression::ConduitMTSToNoteExpression; 42 | using uicomm_t = cps_t::UICommunicationBundle; 43 | 44 | struct ConduitMTSToNoteExpressionEditor; 45 | 46 | struct ConduitMTSToNoteExpressionEditor : public sst::jucegui::accessibility::IgnoredComponent, 47 | shared::ToolTipMixIn 48 | { 49 | uicomm_t &uic; 50 | using comms_t = 51 | sst::conduit::shared::EditorCommunicationsHandler; 53 | std::unique_ptr comms; 54 | 55 | ConduitMTSToNoteExpressionEditor(uicomm_t &p) : uic(p) 56 | { 57 | comms = std::make_unique(p, *this); 58 | 59 | comms->startProcessing(); 60 | 61 | comms->addIdleHandler("poll_events", [w = juce::Component::SafePointer(this)]() { 62 | if (w) 63 | { 64 | w->onIdle(); 65 | } 66 | }); 67 | 68 | statusPanel = std::make_unique("Status"); 69 | statusPanel->setContentAreaComponent(std::make_unique(this)); 70 | addAndMakeVisible(*statusPanel); 71 | 72 | controlsPanel = std::make_unique("Controls"); 73 | addAndMakeVisible(*controlsPanel); 74 | 75 | notesPanel = std::make_unique("Notes"); 76 | notesPanel->setContentAreaComponent(std::make_unique(this)); 77 | addAndMakeVisible(*notesPanel); 78 | 79 | setSize(600, 400); 80 | } 81 | 82 | ~ConduitMTSToNoteExpressionEditor() 83 | { 84 | comms->removeIdleHandler("poll_events"); 85 | comms->stopProcessing(); 86 | } 87 | 88 | int lastNoteUpd{-1}; 89 | void onIdle() 90 | { 91 | auto cl = uic.dataCopyForUI.mtsClient; 92 | auto sn = scaleName; 93 | if (cl && MTS_HasMaster(cl)) 94 | { 95 | isConnected = true; 96 | scaleName = MTS_GetScaleName(cl); 97 | } 98 | else 99 | { 100 | isConnected = false; 101 | scaleName = ""; 102 | } 103 | 104 | if (lastNoteUpd != uic.dataCopyForUI.noteRemainingUpdate) 105 | { 106 | lastNoteUpd = uic.dataCopyForUI.noteRemainingUpdate; 107 | repaint(); 108 | } 109 | if (sn != scaleName) 110 | { 111 | repaint(); 112 | } 113 | } 114 | 115 | struct InfoComp : juce::Component 116 | { 117 | ConduitMTSToNoteExpressionEditor *editor{nullptr}; 118 | InfoComp(ConduitMTSToNoteExpressionEditor *that) : editor(that) {} 119 | 120 | void paint(juce::Graphics &g) 121 | { 122 | auto b = getLocalBounds().withHeight(20).withTrimmedLeft(3); 123 | auto ln = [&b, &g, this](auto s) { 124 | g.setColour(juce::Colours::white); 125 | g.setFont(juce::Font(juce::FontOptions(editor->fixedFace)).withHeight(12)); 126 | g.drawText(s, b, juce::Justification::centredLeft); 127 | b = b.translated(0, 20); 128 | }; 129 | ln(editor->isConnected ? "MTS Connected" : "No MTS Connection"); 130 | ln("Scale: " + editor->scaleName); 131 | 132 | int ct{0}; 133 | for (auto &c : editor->uic.dataCopyForUI.noteRemaining) 134 | { 135 | for (auto &m : c) 136 | { 137 | if (m != 0.f) 138 | ct++; 139 | } 140 | } 141 | ln("Active Voices : " + std::to_string(ct)); 142 | } 143 | }; 144 | 145 | struct NotesComp : juce::Component 146 | { 147 | ConduitMTSToNoteExpressionEditor *editor{nullptr}; 148 | NotesComp(ConduitMTSToNoteExpressionEditor *that) : editor(that) {} 149 | 150 | void paint(juce::Graphics &g) 151 | { 152 | auto b = getLocalBounds().withHeight(20).withTrimmedLeft(3); 153 | auto ln = [&b, &g, this](auto s) { 154 | g.setColour(juce::Colours::white); 155 | g.setFont(juce::FontOptions(editor->fixedFace).withHeight(12)); 156 | g.drawText(s, b, juce::Justification::centredLeft); 157 | b = b.translated(0, 20); 158 | if (!getLocalBounds().contains(b)) 159 | { 160 | b = getLocalBounds().withHeight(20).withTrimmedLeft(b.getX() + 200); 161 | } 162 | }; 163 | int ch{0}; 164 | auto cl = editor->uic.dataCopyForUI.mtsClient; 165 | for (auto &c : editor->uic.dataCopyForUI.noteRemaining) 166 | { 167 | int nt{0}; 168 | for (auto &m : c) 169 | { 170 | if (m != 0.f) 171 | { 172 | auto fr = MTS_NoteToFrequency(cl, nt, ch); 173 | auto m = fmt::format("ch={} k={} freq={:.2f}Hz", ch, nt, fr); 174 | ln(m); 175 | } 176 | nt++; 177 | } 178 | ch++; 179 | } 180 | } 181 | }; 182 | 183 | void resized() override 184 | { 185 | auto th = 90; 186 | auto sw = 250; 187 | if (statusPanel) 188 | statusPanel->setBounds(getLocalBounds().withHeight(th).withWidth(sw)); 189 | 190 | if (controlsPanel) 191 | controlsPanel->setBounds(getLocalBounds().withHeight(th).withTrimmedLeft(sw)); 192 | if (notesPanel) 193 | notesPanel->setBounds(getLocalBounds().withTrimmedTop(th)); 194 | } 195 | std::unique_ptr statusPanel, controlsPanel, notesPanel; 196 | juce::Typeface::Ptr fixedFace{nullptr}; 197 | 198 | std::string scaleName{""}; 199 | bool isConnected{false}; 200 | }; 201 | } // namespace sst::conduit::mts_to_noteexpression::editor 202 | 203 | namespace sst::conduit::mts_to_noteexpression 204 | { 205 | std::unique_ptr ConduitMTSToNoteExpression::createEditor() 206 | { 207 | uiComms.refreshUIValues = true; 208 | auto innards = std::make_unique< 209 | sst::conduit::mts_to_noteexpression::editor::ConduitMTSToNoteExpressionEditor>(uiComms); 210 | auto editor = 211 | std::make_unique>(uiComms); 212 | innards->fixedFace = editor->loadFont("Anonymous_Pro/AnonymousPro-Regular.ttf"); 213 | editor->setContentComponent(std::move(innards)); 214 | 215 | return editor; 216 | } 217 | 218 | } // namespace sst::conduit::mts_to_noteexpression 219 | -------------------------------------------------------------------------------- /src/chord-memory/chord-memory.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "chord-memory.h" 23 | #include "juce_gui_basics/juce_gui_basics.h" 24 | #include "version.h" 25 | 26 | namespace sst::conduit::chord_memory 27 | { 28 | const clap_plugin_descriptor *ConduitChordMemoryConfig::getDescription() 29 | { 30 | static const char *features[] = {CLAP_PLUGIN_FEATURE_NOTE_EFFECT, CLAP_PLUGIN_FEATURE_DELAY, 31 | nullptr}; 32 | static clap_plugin_descriptor desc = {CLAP_VERSION, 33 | "org.surge-synth-team.conduit.chord-memory", 34 | "Conduit Chord Memory", 35 | "Surge Synth Team", 36 | "https://surge-synth-team.org", 37 | "", 38 | "", 39 | sst::conduit::build::FullVersionStr, 40 | "The Conduit Chord Memory is a work in progress", 41 | &features[0]}; 42 | return &desc; 43 | } 44 | 45 | ConduitChordMemory::ConduitChordMemory(const clap_host *host) 46 | : sst::conduit::shared::ClapBaseClass(host) 47 | { 48 | auto autoFlag = CLAP_PARAM_IS_AUTOMATABLE; 49 | auto steppedFlag = autoFlag | CLAP_PARAM_IS_STEPPED; 50 | 51 | paramDescriptions.push_back(ParamDesc() 52 | .asInt() 53 | .withID(pmKeyShift) 54 | .withName("Key Shift") 55 | .withGroupName("Retune") 56 | .withRange(-24, 24) 57 | .withDefault(7) 58 | .withLinearScaleFormatting("keys") 59 | .withFlags(steppedFlag)); 60 | 61 | configureParams(); 62 | 63 | attachParam(pmKeyShift, keyShift); 64 | 65 | clapJuceShim = std::make_unique(this); 66 | clapJuceShim->setResizable(true); 67 | } 68 | 69 | ConduitChordMemory::~ConduitChordMemory() {} 70 | 71 | bool ConduitChordMemory::notePortsInfo(uint32_t index, bool isInput, 72 | clap_note_port_info *info) const noexcept 73 | { 74 | if (isInput) 75 | { 76 | info->id = 172; 77 | info->supported_dialects = 78 | CLAP_NOTE_DIALECT_MIDI | CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI_MPE; 79 | info->preferred_dialect = CLAP_NOTE_DIALECT_CLAP; 80 | strncpy(info->name, "NoteInput", CLAP_NAME_SIZE - 1); 81 | return true; 82 | } 83 | else 84 | { 85 | info->id = 2321; 86 | info->supported_dialects = 87 | CLAP_NOTE_DIALECT_MIDI | CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI_MPE; 88 | info->preferred_dialect = CLAP_NOTE_DIALECT_CLAP; 89 | strncpy(info->name, "NoteOutput", CLAP_NAME_SIZE - 1); 90 | return true; 91 | } 92 | return false; 93 | } 94 | 95 | clap_process_status ConduitChordMemory::process(const clap_process *process) noexcept 96 | { 97 | handleEventsFromUIQueue(process->out_events); 98 | 99 | auto ev = process->in_events; 100 | auto ov = process->out_events; 101 | auto sz = ev->size(ev); 102 | 103 | if (sz == 0) 104 | return CLAP_PROCESS_CONTINUE; 105 | 106 | // We know the input list is sorted 107 | for (auto i = 0U; i < sz; ++i) 108 | { 109 | auto evt = ev->get(ev, i); 110 | 111 | if (handleParamBaseEvents(evt)) 112 | { 113 | } 114 | else if (evt->space_id == CLAP_CORE_EVENT_SPACE_ID) 115 | { 116 | switch (evt->type) 117 | { 118 | case CLAP_EVENT_MIDI: 119 | { 120 | auto mevt = reinterpret_cast(evt); 121 | 122 | auto msg = mevt->data[0] & 0xF0; 123 | auto chan = mevt->data[0] & 0x0F; 124 | 125 | if (msg == 0x90 || msg == 0x80) 126 | { 127 | handleMIDI1NoteChange(ov, mevt, chan, mevt->data[1], mevt->data[2] / 127.0, 128 | msg == 0x90); 129 | /* 130 | clap_event_midi mextra; 131 | memcpy(&mextra, mevt, sizeof(clap_event_midi)); 132 | mextra.data[1] += iks; 133 | ov->try_push(ov, (const clap_event_header *)(&mextra)); 134 | */ 135 | } 136 | else 137 | { 138 | ov->try_push(ov, evt); 139 | } 140 | } 141 | break; 142 | 143 | case CLAP_EVENT_NOTE_ON: 144 | case CLAP_EVENT_NOTE_OFF: 145 | { 146 | auto nevt = reinterpret_cast(evt); 147 | handleClapNoteChange(ov, nevt, nevt->channel, nevt->key, nevt->velocity, 148 | evt->type == CLAP_EVENT_NOTE_ON); 149 | } 150 | break; 151 | 152 | default: 153 | ov->try_push(ov, evt); 154 | break; 155 | } 156 | } 157 | else 158 | { 159 | ov->try_push(ov, evt); 160 | } 161 | } 162 | 163 | return CLAP_PROCESS_CONTINUE; 164 | } 165 | 166 | void ConduitChordMemory::handleMIDI1NoteChange(const clap_output_events *ov, 167 | const clap_event_midi *mevt, int16_t channel, 168 | int16_t key, double vel, bool on) 169 | { 170 | auto ks = (int)std::round(*keyShift); 171 | auto nk = std::clamp(key + ks, 0, 127); 172 | if (updateNoteOnOffData(channel, key, on)) 173 | { 174 | ov->try_push(ov, (const clap_event_header *)mevt); 175 | } 176 | if (updateNoteOnOffData(channel, nk, on)) 177 | { 178 | clap_event_midi mextra; 179 | memcpy(&mextra, mevt, sizeof(clap_event_midi)); 180 | mextra.data[1] = nk; 181 | ov->try_push(ov, (const clap_event_header *)(&mextra)); 182 | } 183 | } 184 | 185 | void ConduitChordMemory::handleClapNoteChange(const clap_output_events *ov, 186 | const clap_event_note *nevt, int16_t channel, 187 | int16_t key, double vel, bool on) 188 | { 189 | auto ks = (int)std::round(*keyShift); 190 | auto nk = std::clamp(key + ks, 0, 127); 191 | if (updateNoteOnOffData(nevt->channel, nevt->key, on)) 192 | { 193 | ov->try_push(ov, (const clap_event_header *)nevt); 194 | } 195 | if (updateNoteOnOffData(nevt->channel, nk, on)) 196 | { 197 | clap_event_note mextra; 198 | memcpy(&mextra, nevt, sizeof(clap_event_note)); 199 | mextra.key = nk; 200 | ov->try_push(ov, (const clap_event_header *)(&mextra)); 201 | } 202 | } 203 | 204 | bool ConduitChordMemory::updateNoteOnOffData(int16_t channel, int16_t key, bool isOn) 205 | { 206 | activeNotes[channel][key] += isOn ? 1 : -1; 207 | auto an = activeNotes[channel][key]; 208 | CNDOUT << "After note " << (isOn ? "on" : "off") << " at " << channel << " " << key 209 | << " resulting an=" << an << std::endl; 210 | return isOn ? an == 1 : an == 0; 211 | } 212 | 213 | bool ConduitChordMemoryConfig::PatchExtension::toXml(TiXmlElement &el) 214 | { 215 | TiXmlElement cn("companionNotes"); 216 | for (auto i = 0U; i < companionNotes.size(); ++i) 217 | { 218 | TiXmlElement nt("note"); 219 | nt.SetAttribute("n", i); 220 | nt.SetAttribute("b", companionNotes[i].to_string()); 221 | cn.InsertEndChild(nt); 222 | } 223 | el.InsertEndChild(cn); 224 | return true; 225 | } 226 | 227 | bool ConduitChordMemoryConfig::PatchExtension::fromXml(TiXmlElement *) { return true; } 228 | 229 | } // namespace sst::conduit::chord_memory 230 | -------------------------------------------------------------------------------- /scripts/mac_installer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Documentation for pkgbuild and productbuild: https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html 4 | 5 | # preflight check 6 | PRODUCT="Conduit" 7 | INDIR=$1 8 | TARGET_DIR=$2 9 | VERSION=$3 10 | 11 | TMPDIR="./installer-tmp" 12 | mkdir -p $TMPDIR 13 | 14 | echo "MAKE from $INDIR $RESOURCESDIR into $TARGET_DIR with $VERSION" 15 | 16 | VST3="${PRODUCT}.vst3" 17 | AU="${PRODUCT}.component" 18 | CLAP="${PRODUCT}.clap" 19 | 20 | PRODUCTFILE=`echo $PRODUCT | tr ' ' '-' | tr '[:upper:]' '[:lower:]'` 21 | echo "PRODUCTFILE is ${PRODUCTFILE}" 22 | 23 | #mkdir $TMPDIR/AppResourcesPackageScript 24 | #cat > $TMPDIR/AppResourcesPackageScript/postinstall << PIEND 25 | ##!/bin/bash 26 | #rsync -r --delete "/tmp/sst-installer/$APP" /Applications 27 | #chown -R $USER:staff "/Applications/$APP"; 28 | ## rm -rf /tmp/sst-installer 29 | #date > /tmp/sst-installer/handled 30 | #PIEND 31 | 32 | #chmod 755 $TMPDIR/AppResourcesPackageScript/postinstall 33 | 34 | 35 | if [ "$VERSION" == "" ]; then 36 | echo "You must specify the version you are packaging!" 37 | echo "eg: ./make_installer.sh 1.0.6b4" 38 | exit 1 39 | fi 40 | 41 | 42 | OUTPUT_BASE_FILENAME="${PRODUCTFILE}-macOS-$VERSION" 43 | 44 | build_flavor() 45 | { 46 | flavor=$1 47 | flavorprod=$2 48 | ident=$3 49 | loc=$4 50 | scripts=$5 51 | 52 | echo --- BUILDING ${PRODUCTFILE}_${flavor}.pkg from "$flavorprod" --- 53 | 54 | workdir=$TMPDIR/$flavor 55 | mkdir -p $workdir 56 | 57 | # In the past we would pkgbuild --analyze first to make a plist file, but we are perfectly fine with the 58 | # defaults, so we skip that step here. http://thegreyblog.blogspot.com/2014/06/os-x-creating-packages-from-command_2.html 59 | # was pretty handy in figuring that out and man pkgbuild convinced us to not do it, as did testing. 60 | # 61 | # The defaults only work if a component is a sole entry in a staging directory though, so synthesize that 62 | # by moving the product to a tmp dir 63 | 64 | cp -r "$INDIR/$flavorprod" "$workdir" 65 | ls -l $workdir 66 | 67 | sca="" 68 | if [[ ! -z $scripts ]]; then 69 | sca="--scripts $scripts" 70 | fi 71 | 72 | if [[ ! -z $MAC_SIGNING_CERT ]]; then 73 | [[ -z $MAC_INSTALLING_CERT ]] && echo "You need an installing cert too " && exit 2 74 | codesign --force -s "$MAC_SIGNING_CERT" -o runtime --deep "$workdir/$flavorprod" 75 | codesign -vvv "$workdir/$flavorprod" 76 | 77 | pkgbuild --sign "$MAC_INSTALLING_CERT" --root $workdir --identifier $ident --version $VERSION --install-location "$loc" "$TMPDIR/${PRODUCTFILE}_${flavor}.pkg" $sca || exit 1 78 | echo pkgbuild --sign "$MAC_INSTALLING_CERT" --root $workdir --identifier $ident --version $VERSION --install-location "$loc" "$TMPDIR/${PRODUCTFILE}_${flavor}.pkg" $sca || exit 1 79 | else 80 | pkgbuild --root $workdir --identifier $ident --version $VERSION --install-location "$loc" "$TMPDIR/${PRODUCTFILE}_${flavor}.pkg" $sca || exit 1 81 | fi 82 | 83 | #rm -rf $workdir 84 | } 85 | 86 | 87 | if [[ -d $INDIR/$VST3 ]]; then 88 | build_flavor "VST3" "$VST3" "org.surge-synth-team.${PRODUCTFILE}.vst3.pkg" "/Library/Audio/Plug-Ins/VST3" 89 | fi 90 | 91 | if [[ -d $INDIR/$AU ]]; then 92 | build_flavor "AU" "$AU" "org.surge-synth-team.${PRODUCTFILE}.component.pkg" "/Library/Audio/Plug-Ins/Components" 93 | fi 94 | 95 | #if [[ -d $INDIR/$APP ]]; then 96 | # build_flavor "APP" "$APP" "org.surge-synth-team.${PRODUCTFILE}.app.pkg" "/tmp/sst-installer" $TMPDIR/AppResourcesPackageScript 97 | #fi 98 | 99 | if [[ -d $INDIR/$CLAP ]]; then 100 | build_flavor "CLAP" "$CLAP" "org.surge-synth-team.${PRODUCTFILE}.clap.pkg" "/Library/Audio/Plug-Ins/CLAP" 101 | fi 102 | 103 | cp ${INDIR}/License.txt "${TMPDIR}" 104 | cp ${INDIR}/README.rtf "${TMPDIR}/Readme.rtf" 105 | echo --- Sub Packages Created --- 106 | ls -l "${TMPDIR}" 107 | 108 | # create distribution.xml 109 | 110 | if [[ -d $INDIR/$VST3 ]]; then 111 | VST3_PKG_REF="" 112 | VST3_CHOICE="" 113 | VST3_CHOICE_DEF="${PRODUCTFILE}_VST3.pkg" 114 | fi 115 | if [[ -d $INDIR/$AU ]]; then 116 | AU_PKG_REF="" 117 | AU_CHOICE="" 118 | AU_CHOICE_DEF="${PRODUCTFILE}_AU.pkg" 119 | fi 120 | if [[ -d $INDIR/$CLAP ]]; then 121 | CLAP_PKG_REF="" 122 | CLAP_CHOICE="" 123 | CLAP_CHOICE_DEF="${PRODUCTFILE}_CLAP.pkg" 124 | fi 125 | #if [[ -d $INDIR/$APP ]]; then 126 | # APP_PKG_REF="" 127 | # APP_CHOICE="" 128 | # APP_CHOICE_DEF="${PRODUCTFILE}_APP.pkg" 129 | #fi 130 | 131 | 132 | cat > $TMPDIR/distribution.xml << XMLEND 133 | 134 | 135 | ${PRODUCT} ${VERSION} 136 | 137 | 138 | ${VST3_PKG_REF} 139 | ${AU_PKG_REF} 140 | ${CLAP_PKG_REF} 141 | ${APP_PKG_REF} 142 | 143 | 144 | ${VST3_CHOICE} 145 | ${AU_CHOICE} 146 | ${CLAP_CHOICE} 147 | ${APP_CHOICE} 148 | 149 | ${VST3_CHOICE_DEF} 150 | ${AU_CHOICE_DEF} 151 | ${CLAP_CHOICE_DEF} 152 | ${APP_CHOICE_DEF} 153 | 154 | XMLEND 155 | 156 | # build installation bundle 157 | 158 | pushd ${TMPDIR} 159 | if [[ ! -z $MAC_INSTALLING_CERT ]]; then 160 | echo "Building SIGNED PKG" 161 | echo productbuild --sign "$MAC_INSTALLING_CERT" --distribution "distribution.xml" --package-path "." --resources ${RESOURCESDIR} "$OUTPUT_BASE_FILENAME.pkg" 162 | productbuild --sign "$MAC_INSTALLING_CERT" --distribution "distribution.xml" --package-path "." --resources "." "$OUTPUT_BASE_FILENAME.pkg" 163 | else 164 | echo "Building UNSIGNED PKG" 165 | echo productbuild --distribution "distribution.xml" --package-path "." "$OUTPUT_BASE_FILENAME.pkg" 166 | productbuild --distribution "distribution.xml" --package-path "." --resources "." "$OUTPUT_BASE_FILENAME.pkg" 167 | ls -al 168 | echo "DONE" 169 | fi 170 | 171 | popd 172 | 173 | #Rez -append ${RESOURCESDIR}/icns.rsrc -o "${TMPDIR}/${OUTPUT_BASE_FILENAME}.pkg" 174 | #SetFile -a C "${TMPDIR}/${OUTPUT_BASE_FILENAME}.pkg" 175 | 176 | mkdir -p "${TMPDIR}/${PRODUCTFILE}" 177 | mv "${TMPDIR}/${OUTPUT_BASE_FILENAME}.pkg" "${TMPDIR}/${PRODUCTFILE}" 178 | 179 | # create a DMG if required 180 | 181 | if [[ -f "${TARGET_DIR}/$OUTPUT_BASE_FILENAME.dmg" ]]; then 182 | rm "${TARGET_DIR}/$OUTPUT_BASE_FILENAME.dmg" 183 | fi 184 | hdiutil create /tmp/tmp.dmg -ov -volname "$OUTPUT_BASE_FILENAME" -fs HFS+ -srcfolder "${TMPDIR}/${PRODUCTFILE}/" 185 | hdiutil convert /tmp/tmp.dmg -format UDZO -o "${TARGET_DIR}/$OUTPUT_BASE_FILENAME.dmg" 186 | 187 | if [[ ! -z $MAC_SIGNING_CERT ]]; then 188 | codesign --force -s "$MAC_SIGNING_CERT" --timestamp "${TARGET_DIR}/$OUTPUT_BASE_FILENAME.dmg" 189 | codesign -vvv "${TARGET_DIR}/$OUTPUT_BASE_FILENAME.dmg" 190 | xcrun notarytool submit "${TARGET_DIR}/$OUTPUT_BASE_FILENAME.dmg" --apple-id ${MAC_SIGNING_ID} --team-id ${MAC_SIGNING_TEAM} --password ${MAC_SIGNING_1UPW} --wait 191 | 192 | xcrun stapler staple "${TARGET_DIR}/${OUTPUT_BASE_FILENAME}.dmg" 193 | fi 194 | 195 | # clean up 196 | 197 | #rm distribution.xml 198 | #rm Surge_*.pkg 199 | -------------------------------------------------------------------------------- /src/conduit-clap-entry.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | /* 23 | * This file provides the `clap_plugin_entry` entry point required in the DLL for all 24 | * clap plugins. It also provides the basic functions for the resulting factory class 25 | * which generates the plugin. In a single plugin case, this really is just plumbing 26 | * through to expose polysynth::ConduitPolysynthConfig::getDescription() and create a polysynth 27 | * plugin instance using the helper classes. 28 | * 29 | * For more information on this mechanism, see include/clap/entry.h 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | #include "clapwrapper/vst3.h" 40 | #include "clapwrapper/auv2.h" 41 | 42 | #include "conduit-shared/debug-helpers.h" 43 | 44 | #include "polysynth/polysynth.h" 45 | #include "polymetric-delay/polymetric-delay.h" 46 | #include "chord-memory/chord-memory.h" 47 | #include "ring-modulator/ring-modulator.h" 48 | #include "clap-event-monitor/clap-event-monitor.h" 49 | #include "mts-to-noteexpression/mts-to-noteexpression.h" 50 | #include "midi2-sawsynth/midi2-sawsynth.h" 51 | #include "multiout-synth/multiout-synth.h" 52 | 53 | #include "version.h" 54 | 55 | namespace sst::conduit::pluginentry 56 | { 57 | 58 | uint32_t clap_get_plugin_count(const clap_plugin_factory *f) { return 8; } 59 | const clap_plugin_descriptor *clap_get_plugin_descriptor(const clap_plugin_factory *f, uint32_t w) 60 | { 61 | if (w == 0) 62 | return polysynth::ConduitPolysynthConfig::getDescription(); 63 | if (w == 1) 64 | return polymetric_delay::ConduitPolymetricDelayConfig::getDescription(); 65 | if (w == 2) 66 | return chord_memory::ConduitChordMemoryConfig::getDescription(); 67 | if (w == 3) 68 | return ring_modulator::ConduitRingModulatorConfig::getDescription(); 69 | if (w == 4) 70 | return clap_event_monitor::ConduitClapEventMonitorConfig::getDescription(); 71 | if (w == 5) 72 | return mts_to_noteexpression::ConduitMTSToNoteExpressionConfig::getDescription(); 73 | if (w == 6) 74 | return midi2_sawsynth::ConduitMIDI2SawSynthConfig::getDescription(); 75 | if (w == 7) 76 | return multiout_synth::ConduitMultiOutSynthConfig::getDescription(); 77 | 78 | CNDOUT << "Clap Plugin not found at " << w << std::endl; 79 | return nullptr; 80 | } 81 | 82 | static const clap_plugin *clap_create_plugin(const clap_plugin_factory *f, const clap_host *host, 83 | const char *plugin_id) 84 | { 85 | if (strcmp(plugin_id, polysynth::ConduitPolysynthConfig::getDescription()->id) == 0) 86 | { 87 | auto p = new polysynth::ConduitPolysynth(host); 88 | return p->clapPlugin(); 89 | } 90 | if (strcmp(plugin_id, polymetric_delay::ConduitPolymetricDelayConfig::getDescription()->id) == 91 | 0) 92 | { 93 | auto p = new polymetric_delay::ConduitPolymetricDelay(host); 94 | return p->clapPlugin(); 95 | } 96 | if (strcmp(plugin_id, chord_memory::ConduitChordMemoryConfig::getDescription()->id) == 0) 97 | { 98 | auto p = new chord_memory::ConduitChordMemory(host); 99 | return p->clapPlugin(); 100 | } 101 | if (strcmp(plugin_id, ring_modulator::ConduitRingModulatorConfig::getDescription()->id) == 0) 102 | { 103 | auto p = new ring_modulator::ConduitRingModulator(host); 104 | return p->clapPlugin(); 105 | } 106 | if (strcmp(plugin_id, 107 | clap_event_monitor::ConduitClapEventMonitorConfig::getDescription()->id) == 0) 108 | { 109 | auto p = new clap_event_monitor::ConduitClapEventMonitor(host); 110 | return p->clapPlugin(); 111 | } 112 | 113 | if (strcmp(plugin_id, 114 | mts_to_noteexpression::ConduitMTSToNoteExpressionConfig::getDescription()->id) == 0) 115 | { 116 | auto p = new mts_to_noteexpression::ConduitMTSToNoteExpression(host); 117 | return p->clapPlugin(); 118 | } 119 | 120 | if (strcmp(plugin_id, midi2_sawsynth::ConduitMIDI2SawSynthConfig::getDescription()->id) == 0) 121 | { 122 | auto p = new midi2_sawsynth::ConduitMIDI2SawSynth(host); 123 | return p->clapPlugin(); 124 | } 125 | 126 | if (strcmp(plugin_id, multiout_synth::ConduitMultiOutSynthConfig::getDescription()->id) == 0) 127 | { 128 | auto p = new multiout_synth::ConduitMultiOutSynth(host); 129 | return p->clapPlugin(); 130 | } 131 | CNDOUT << "No plugin found; returning nullptr" << std::endl; 132 | return nullptr; 133 | } 134 | 135 | static bool clap_get_auv2_info(const clap_plugin_factory_as_auv2 *factory, uint32_t index, 136 | clap_plugin_info_as_auv2_t *info) 137 | { 138 | auto desc = clap_get_plugin_descriptor(nullptr, index); // we don't use the factory above 139 | 140 | auto plugin_id = desc->id; 141 | 142 | info->au_type[0] = 0; // use the features to determine the type 143 | 144 | if (strcmp(plugin_id, polysynth::ConduitPolysynthConfig::getDescription()->id) == 0) 145 | { 146 | strncpy(info->au_subt, "PlyS", 5); 147 | return true; 148 | } 149 | if (strcmp(plugin_id, polymetric_delay::ConduitPolymetricDelayConfig::getDescription()->id) == 150 | 0) 151 | { 152 | strncpy(info->au_subt, "dLay", 5); 153 | return true; 154 | } 155 | if (strcmp(plugin_id, chord_memory::ConduitChordMemoryConfig::getDescription()->id) == 0) 156 | { 157 | strncpy(info->au_subt, "crMm", 5); 158 | return true; 159 | } 160 | if (strcmp(plugin_id, ring_modulator::ConduitRingModulatorConfig::getDescription()->id) == 0) 161 | { 162 | strncpy(info->au_subt, "rngM", 5); 163 | return true; 164 | } 165 | if (strcmp(plugin_id, 166 | clap_event_monitor::ConduitClapEventMonitorConfig::getDescription()->id) == 0) 167 | { 168 | strncpy(info->au_subt, "clEv", 5); 169 | return true; 170 | } 171 | if (strcmp(plugin_id, 172 | mts_to_noteexpression::ConduitMTSToNoteExpressionConfig::getDescription()->id) == 0) 173 | { 174 | return false; // don't export this synth 175 | } 176 | 177 | if (strcmp(plugin_id, midi2_sawsynth::ConduitMIDI2SawSynthConfig::getDescription()->id) == 0) 178 | { 179 | strncpy(info->au_subt, "m2sn", 5); 180 | return true; 181 | } 182 | 183 | if (strcmp(plugin_id, multiout_synth::ConduitMultiOutSynthConfig::getDescription()->id) == 0) 184 | { 185 | strncpy(info->au_subt, "mlOs", 5); 186 | return true; 187 | } 188 | return false; 189 | } 190 | 191 | const struct clap_plugin_factory conduit_clap_factory = { 192 | sst::conduit::pluginentry::clap_get_plugin_count, 193 | sst::conduit::pluginentry::clap_get_plugin_descriptor, 194 | sst::conduit::pluginentry::clap_create_plugin, 195 | }; 196 | 197 | const struct clap_plugin_factory_as_auv2 conduit_auv2_factory = { 198 | "SSTx", // manu 199 | "Surge Synth Team", // manu name 200 | sst::conduit::pluginentry::clap_get_auv2_info}; 201 | 202 | static const void *get_factory(const char *factory_id) 203 | { 204 | if (!strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID)) 205 | return &conduit_clap_factory; 206 | 207 | if (!strcmp(factory_id, CLAP_PLUGIN_FACTORY_INFO_AUV2)) 208 | return &conduit_auv2_factory; 209 | 210 | return nullptr; 211 | } 212 | 213 | // clap_init and clap_deinit are required to be fast, but we have nothing we need to do here 214 | bool clap_init(const char *p) 215 | { 216 | CNDOUT << "Conduit - clap_init\n"; 217 | CNDOUT << " Version: " << sst::conduit::build::FullVersionStr << "\n"; 218 | CNDOUT << " Build : " << sst::conduit::build::BuildDate << " @ " 219 | << sst::conduit::build::BuildTime << std::endl; 220 | return true; 221 | } 222 | void clap_deinit() {} 223 | 224 | } // namespace sst::conduit::pluginentry 225 | 226 | extern "C" 227 | { 228 | 229 | #ifdef __GNUC__ 230 | #pragma GCC diagnostic push 231 | #pragma GCC diagnostic ignored "-Wattributes" // other peoples errors are outside my scope 232 | #endif 233 | 234 | // clang-format off 235 | const CLAP_EXPORT struct clap_plugin_entry clap_entry = { 236 | CLAP_VERSION, 237 | sst::conduit::pluginentry::clap_init, 238 | sst::conduit::pluginentry::clap_deinit, 239 | sst::conduit::pluginentry::get_factory 240 | }; 241 | // clang-format on 242 | #ifdef __GNUC__ 243 | #pragma GCC diagnostic pop 244 | #endif 245 | } 246 | -------------------------------------------------------------------------------- /src/mts-to-noteexpression/mts-to-noteexpression.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "mts-to-noteexpression.h" 23 | #include "juce_gui_basics/juce_gui_basics.h" 24 | #include "version.h" 25 | 26 | #include "libMTSClient.h" 27 | 28 | namespace sst::conduit::mts_to_noteexpression 29 | { 30 | const clap_plugin_descriptor *ConduitMTSToNoteExpressionConfig::getDescription() 31 | { 32 | static const char *features[] = {CLAP_PLUGIN_FEATURE_NOTE_EFFECT, CLAP_PLUGIN_FEATURE_DELAY, 33 | nullptr}; 34 | static clap_plugin_descriptor desc = {CLAP_VERSION, 35 | "org.surge-synth-team.conduit.mts-to-noteexpression", 36 | "Conduit MTS to Note Expression Adapter", 37 | "Surge Synth Team", 38 | "https://surge-synth-team.org", 39 | "", 40 | "", 41 | sst::conduit::build::FullVersionStr, 42 | "The Conduit ClapEventMonitor is a work in progress", 43 | &features[0]}; 44 | return &desc; 45 | } 46 | 47 | ConduitMTSToNoteExpression::ConduitMTSToNoteExpression(const clap_host *host) 48 | : sst::conduit::shared::ClapBaseClass(host) 50 | { 51 | mtsClient = MTS_RegisterClient(); 52 | 53 | auto autoFlag = CLAP_PARAM_IS_AUTOMATABLE; 54 | auto steppedFlag = autoFlag | CLAP_PARAM_IS_STEPPED; 55 | 56 | paramDescriptions.push_back(ParamDesc() 57 | .asBool() 58 | .withID(pmRetuneHeld) 59 | .withName("Retune Held Notes") 60 | .withGroupName("MTS NE") 61 | .withDefault(1) 62 | .withFlags(steppedFlag)); 63 | 64 | paramDescriptions.push_back(ParamDesc() 65 | .asFloat() 66 | .withID(pmReleaseTuning) 67 | .withName("Tune Release Time") 68 | .withGroupName("MTS NE") 69 | .withRange(0, 10) 70 | .withDefault(2) 71 | .withLinearScaleFormatting("s") 72 | .withFlags(autoFlag)); 73 | 74 | configureParams(); 75 | attachParam(pmReleaseTuning, postNoteRelease); 76 | attachParam(pmRetuneHeld, retunHeld); 77 | 78 | clapJuceShim = std::make_unique(this); 79 | clapJuceShim->setResizable(true); 80 | 81 | uiComms.dataCopyForUI.mtsClient = mtsClient; 82 | } 83 | 84 | ConduitMTSToNoteExpression::~ConduitMTSToNoteExpression() {} 85 | 86 | bool ConduitMTSToNoteExpression::notePortsInfo(uint32_t index, bool isInput, 87 | clap_note_port_info *info) const noexcept 88 | { 89 | info->id = isInput ? 178 : 23; 90 | info->supported_dialects = CLAP_NOTE_DIALECT_MIDI | CLAP_NOTE_DIALECT_CLAP; 91 | info->preferred_dialect = CLAP_NOTE_DIALECT_CLAP; 92 | strncpy(info->name, (std::string("Note ") + (isInput ? "In" : "Out")).c_str(), 93 | CLAP_NAME_SIZE - 1); 94 | return true; 95 | } 96 | 97 | float ConduitMTSToNoteExpression::retuningFor(int key, int channel) const 98 | { 99 | return MTS_RetuningInSemitones(mtsClient, key, channel); 100 | } 101 | 102 | bool ConduitMTSToNoteExpression::tuningActive() { return true; } 103 | bool ConduitMTSToNoteExpression::retuneHeldNotes() { return (*retunHeld > 0.5); } 104 | 105 | clap_process_status ConduitMTSToNoteExpression::process(const clap_process *process) noexcept 106 | { 107 | auto ev = process->in_events; 108 | auto ov = process->out_events; 109 | auto sz = ev->size(ev); 110 | 111 | // Generate top-of-block tuning messages for all our notes that are on 112 | for (int c = 0; c < 16; ++c) 113 | { 114 | for (int i = 0; i < 128; ++i) 115 | { 116 | if (tuningActive() && noteRemaining[c][i] != 0.f && retuneHeldNotes()) 117 | { 118 | auto prior = sclTuning[c][i]; 119 | sclTuning[c][i] = retuningFor(i, c); 120 | if (sclTuning[c][i] != prior) 121 | { 122 | auto q = clap_event_note_expression(); 123 | q.header.size = sizeof(clap_event_note_expression); 124 | q.header.type = (uint16_t)CLAP_EVENT_NOTE_EXPRESSION; 125 | q.header.time = 0; 126 | q.header.space_id = CLAP_CORE_EVENT_SPACE_ID; 127 | q.header.flags = 0; 128 | q.key = i; 129 | q.channel = c; 130 | q.port_index = 0; 131 | q.expression_id = CLAP_NOTE_EXPRESSION_TUNING; 132 | 133 | q.value = sclTuning[c][i]; 134 | 135 | ov->try_push(ov, reinterpret_cast(&q)); 136 | } 137 | } 138 | } 139 | } 140 | 141 | if (lastUIUpdate == 0) 142 | { 143 | // if a paint happens through the copy it doesnt matter 144 | uiComms.dataCopyForUI.noteRemaining = noteRemaining; 145 | // since this will force a paint anyway on next idle 146 | uiComms.dataCopyForUI.noteRemainingUpdate++; 147 | } 148 | lastUIUpdate = (lastUIUpdate + 1) & 15; 149 | 150 | for (uint32_t i = 0; i < sz; ++i) 151 | { 152 | auto evt = ev->get(ev, i); 153 | switch (evt->type) 154 | { 155 | case CLAP_EVENT_PARAM_VALUE: 156 | { 157 | auto v = reinterpret_cast(evt); 158 | updateParamInPatch(v); 159 | return true; 160 | } 161 | break; 162 | case CLAP_EVENT_MIDI: 163 | case CLAP_EVENT_MIDI2: 164 | case CLAP_EVENT_MIDI_SYSEX: 165 | case CLAP_EVENT_NOTE_CHOKE: 166 | ov->try_push(ov, evt); 167 | break; 168 | case CLAP_EVENT_NOTE_ON: 169 | { 170 | auto nevt = reinterpret_cast(evt); 171 | assert(nevt->channel >= 0); 172 | assert(nevt->channel < 16); 173 | assert(nevt->key >= 0); 174 | assert(nevt->key < 128); 175 | noteRemaining[nevt->channel][nevt->key] = -1; 176 | 177 | auto q = clap_event_note_expression(); 178 | q.header.size = sizeof(clap_event_note_expression); 179 | q.header.type = (uint16_t)CLAP_EVENT_NOTE_EXPRESSION; 180 | q.header.time = nevt->header.time; 181 | q.header.space_id = CLAP_CORE_EVENT_SPACE_ID; 182 | q.header.flags = 0; 183 | q.key = nevt->key; 184 | q.channel = nevt->channel; 185 | q.port_index = nevt->port_index; 186 | q.note_id = nevt->note_id; 187 | q.expression_id = CLAP_NOTE_EXPRESSION_TUNING; 188 | 189 | if (tuningActive()) 190 | { 191 | sclTuning[nevt->channel][nevt->key] = retuningFor(nevt->key, nevt->channel); 192 | } 193 | q.value = sclTuning[nevt->channel][nevt->key]; 194 | 195 | ov->try_push(ov, evt); 196 | ov->try_push(ov, &(q.header)); 197 | } 198 | break; 199 | case CLAP_EVENT_NOTE_OFF: 200 | { 201 | auto nevt = reinterpret_cast(evt); 202 | assert(nevt->channel >= 0); 203 | assert(nevt->channel < 16); 204 | assert(nevt->key >= 0); 205 | assert(nevt->key < 128); 206 | noteRemaining[nevt->channel][nevt->key] = *postNoteRelease; 207 | ov->try_push(ov, evt); 208 | } 209 | break; 210 | case CLAP_EVENT_NOTE_EXPRESSION: 211 | { 212 | auto nevt = reinterpret_cast(evt); 213 | 214 | auto oevt = clap_event_note_expression(); 215 | memcpy(&oevt, evt, nevt->header.size); 216 | 217 | if (nevt->expression_id == CLAP_NOTE_EXPRESSION_TUNING) 218 | { 219 | if (tuningActive()) 220 | { 221 | sclTuning[nevt->channel][nevt->key] = retuningFor(nevt->key, nevt->channel); 222 | } 223 | oevt.value += sclTuning[nevt->channel][nevt->key]; 224 | } 225 | 226 | ov->try_push(ov, &oevt.header); 227 | } 228 | break; 229 | } 230 | } 231 | 232 | // subtract block size seconds from everyone with remaining time and zero out some 233 | for (auto &c : noteRemaining) 234 | for (auto &n : c) 235 | if (n > 0.f) 236 | { 237 | n -= secondsPerSample * process->frames_count; 238 | if (n < 0) 239 | n = 0.f; 240 | } 241 | 242 | return CLAP_PROCESS_CONTINUE; 243 | } 244 | 245 | } // namespace sst::conduit::mts_to_noteexpression 246 | -------------------------------------------------------------------------------- /src/midi2-sawsynth/midi2-sawsynth.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "midi2-sawsynth.h" 23 | #include "juce_gui_basics/juce_gui_basics.h" 24 | #include "version.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | namespace sst::conduit::midi2_sawsynth 31 | { 32 | const clap_plugin_descriptor *ConduitMIDI2SawSynthConfig::getDescription() 33 | { 34 | static const char *features[] = {CLAP_PLUGIN_FEATURE_INSTRUMENT, 35 | CLAP_PLUGIN_FEATURE_SYNTHESIZER, nullptr}; 36 | static clap_plugin_descriptor desc = {CLAP_VERSION, 37 | "org.surge-synth-team.conduit.midi2_sawsynth", 38 | "Conduit MIDI2 SawSynth", 39 | "Surge Synth Team", 40 | "https://surge-synth-team.org", 41 | "", 42 | "", 43 | sst::conduit::build::FullVersionStr, 44 | "The Conduit MIDI2SawSynth is a work in progress", 45 | &features[0]}; 46 | return &desc; 47 | } 48 | 49 | ConduitMIDI2SawSynth::ConduitMIDI2SawSynth(const clap_host *host) 50 | : sst::conduit::shared::ClapBaseClass(host), 51 | voiceManager(*this) 52 | { 53 | auto autoFlag = CLAP_PARAM_IS_AUTOMATABLE; 54 | auto steppedFlag = autoFlag | CLAP_PARAM_IS_STEPPED; 55 | auto modFlag = autoFlag | CLAP_PARAM_IS_MODULATABLE | CLAP_PARAM_IS_MODULATABLE_PER_CHANNEL | 56 | CLAP_PARAM_IS_MODULATABLE_PER_KEY | CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID; 57 | 58 | paramDescriptions.push_back(ParamDesc() 59 | .asInt() 60 | .withID(pmStepped) 61 | .withName("Stepped Param") 62 | .withGroupName("Monitor") 63 | .withRange(-4, 17) 64 | .withDefault(2) 65 | .withLinearScaleFormatting("things") 66 | .withFlags(steppedFlag)); 67 | 68 | paramDescriptions.push_back(ParamDesc() 69 | .asPercentBipolar() 70 | .withID(pmAuto) 71 | .withName("Automatable Param") 72 | .withGroupName("Monitor") 73 | .withFlags(autoFlag)); 74 | 75 | paramDescriptions.push_back(ParamDesc() 76 | .asPercentBipolar() 77 | .withID(pmMod) 78 | .withName("Modulatable Param") 79 | .withGroupName("Monitor") 80 | .withFlags(modFlag)); 81 | 82 | configureParams(); 83 | 84 | clapJuceShim = std::make_unique(this); 85 | clapJuceShim->setResizable(true); 86 | } 87 | 88 | ConduitMIDI2SawSynth::~ConduitMIDI2SawSynth() {} 89 | 90 | bool ConduitMIDI2SawSynth::audioPortsInfo(uint32_t index, bool isInput, 91 | clap_audio_port_info *info) const noexcept 92 | { 93 | if (!isInput) 94 | { 95 | info->id = 652; 96 | info->in_place_pair = CLAP_INVALID_ID; 97 | strncpy(info->name, "main output", sizeof(info->name)); 98 | info->flags = CLAP_AUDIO_PORT_IS_MAIN; 99 | info->channel_count = 2; 100 | info->port_type = CLAP_PORT_STEREO; 101 | 102 | return true; 103 | } 104 | return false; 105 | } 106 | 107 | bool ConduitMIDI2SawSynth::notePortsInfo(uint32_t index, bool isInput, 108 | clap_note_port_info *info) const noexcept 109 | { 110 | info->id = 8427; 111 | info->supported_dialects = CLAP_NOTE_DIALECT_MIDI2; 112 | info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI2; 113 | strncpy(info->name, "MIDI2 In", CLAP_NAME_SIZE - 1); 114 | return true; 115 | } 116 | 117 | clap_process_status ConduitMIDI2SawSynth::process(const clap_process *process) noexcept 118 | { 119 | auto ev = process->in_events; 120 | auto sz = ev->size(ev); 121 | 122 | for (auto i = 0U; i < sz; ++i) 123 | { 124 | auto et = ev->get(ev, i); 125 | if (et->type != CLAP_EVENT_TRANSPORT) 126 | { 127 | uiComms.dataCopyForUI.writeEventTo(et); 128 | } 129 | 130 | if (et->type == CLAP_EVENT_MIDI2) 131 | { 132 | auto m2e = reinterpret_cast(et); 133 | auto pk = 134 | midi::universal_packet(m2e->data[0], m2e->data[1], m2e->data[2], m2e->data[3]); 135 | 136 | if (pk.type() == midi::packet_type::midi2_channel_voice) 137 | { 138 | if (midi::is_note_on_message(pk)) 139 | { 140 | // port channel key noteid velocity retune 141 | voiceManager.processNoteOnEvent( 142 | m2e->port_index, pk.channel(), midi::get_note_pitch(pk).value, 143 | midi::get_note_nr(pk), midi::get_note_velocity(pk).as_float(), 0); 144 | } 145 | #if 0 146 | else if (midi::is_note_off_message(pk)) 147 | { 148 | oss << "note off " 149 | << " nr=" << (int)midi::get_note_nr(pk) 150 | << " pt=" << midi::get_note_pitch(pk).as_float() 151 | << " vel=" << midi::get_note_velocity(pk).as_float(); 152 | 153 | for (int i = 0; i < 4; ++i) 154 | oss << std::hex << " " << (uint32_t)m2e->data[i] << std::dec; 155 | } 156 | else if (midi::is_channel_pressure_message(pk)) 157 | { 158 | oss << "channel pressure " 159 | << midi::get_channel_pressure_value(pk).as_float(); 160 | } 161 | else if (midi::is_poly_pressure_message(pk)) 162 | { 163 | oss << "poly pressure " << midi::get_poly_pressure_value(pk).as_float() 164 | << std::dec << " n=" << (int)midi::get_note_nr(pk) << " " 165 | << " " << std::hex << m2e->data[0] << " " << m2e->data[1] << " " 166 | << m2e->data[2] << " " << m2e->data[3] << " "; 167 | } 168 | else if (midi::is_channel_pitch_bend_message(pk)) 169 | { 170 | oss << "channel bend " << midi::get_channel_pitch_bend_value(pk).as_float() 171 | << std::endl; 172 | } 173 | else if (midi::is_control_change_message(pk)) 174 | { 175 | oss << "control change " << (int)midi::get_controller_nr(pk) << " " 176 | << midi::get_controller_value(pk).as_float() << std::endl; 177 | } 178 | else if (midi::is_midi2_registered_per_note_controller_message(pk)) 179 | { 180 | oss << "registered per note controller n=" << std::dec 181 | << (int)midi::get_note_nr(pk) << " ct=" << std::hex 182 | << (int)midi::get_midi2_per_note_controller_index(pk) 183 | << " val=" << midi::get_controller_value(pk).as_float() << std::endl; 184 | } 185 | else if (midi::is_midi2_per_note_pitch_bend_message(pk)) 186 | { 187 | oss << "note pitch bend n=" << std::dec << (int)midi::get_note_nr(pk) 188 | << " val=" << midi::get_controller_value(pk).as_float() << std::endl; 189 | } 190 | else if (midi::is_midi2_registered_controller_message(pk)) 191 | { 192 | oss << "registered controller" 193 | << " ct=" << std::hex 194 | << (int)midi::get_midi2_per_note_controller_index(pk); 195 | if (midi::get_midi2_per_note_controller_index(pk) == 0x7) 196 | { 197 | oss << " pitch bend config="; 198 | auto cf = midi::pitch_7_25(m2e->data[1]); 199 | oss << cf.as_float() << " cents"; 200 | } 201 | } 202 | else 203 | { 204 | oss << "Unknown " << std::hex << "type=" << (int)pk.type() 205 | << " group=" << (int)pk.group() << " stat=" << (int)pk.status() << " " 206 | << m2e->data[0] << " " << m2e->data[1] << " " << m2e->data[2] << " " 207 | << m2e->data[3] << " "; 208 | } 209 | } 210 | else 211 | { 212 | oss << "unknown pktype=" << (int)pk.type() << std::endl; 213 | } 214 | #endif 215 | } 216 | } 217 | } 218 | 219 | return CLAP_PROCESS_CONTINUE; 220 | } 221 | 222 | } // namespace sst::conduit::midi2_sawsynth 223 | -------------------------------------------------------------------------------- /src/polysynth/voice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #ifndef CONDUIT_SRC_POLYSYNTH_VOICE_H 23 | #define CONDUIT_SRC_POLYSYNTH_VOICE_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #include "conduit-shared/debug-helpers.h" 33 | #include "conduit-shared/sse-include.h" 34 | 35 | #include "sst/basic-blocks/dsp/DPWSawPulseOscillator.h" 36 | #include "sst/basic-blocks/dsp/QuadratureOscillators.h" 37 | #include "sst/basic-blocks/modulators/ADSREnvelope.h" 38 | #include "sst/basic-blocks/modulators/SimpleLFO.h" 39 | #include "sst/basic-blocks/dsp/BlockInterpolators.h" 40 | 41 | #include "sst/filters.h" 42 | #include "sst/waveshapers.h" 43 | 44 | struct MTSClient; 45 | 46 | namespace sst::conduit::polysynth 47 | { 48 | 49 | struct ConduitPolysynth; 50 | 51 | struct PolysynthVoice 52 | { 53 | static constexpr int max_uni{7}; 54 | static constexpr int blockSize{8}; 55 | static constexpr int blockSizeOS{blockSize << 1}; 56 | 57 | const ConduitPolysynth &synth; 58 | PolysynthVoice(const ConduitPolysynth &sy) 59 | : synth(sy), gen((uint64_t)(this)), urd(-1.0, 1.0), aeg(this), feg(this), lfos{this, this} 60 | { 61 | for (int i = 0; i < 128; ++i) 62 | { 63 | baseFrequencyByMidiKey[i] = 440.0 * pow(2.0, (i - 69.0) / 12.0); 64 | } 65 | } 66 | 67 | void setSampleRate(double sr) 68 | { 69 | samplerate = sr; 70 | aeg.onSampleRateChanged(); 71 | feg.onSampleRateChanged(); 72 | } 73 | 74 | int portid; // clap note port index 75 | int channel; // midi channel 76 | int key; // The midi key which triggered me 77 | int note_id; // and the note_id delivered by the host (used for note expressions) 78 | 79 | /* Midi Controller Values */ 80 | float velocity{0.f}; 81 | float releaseVelocity{0.f}; 82 | float polyphonicAT{0.f}; // scaled 0...1 83 | float channelPressure{0.f}; // scaled 0..1 84 | float midi1CC[128]{}; // scaled 0...1 85 | 86 | MTSClient *mtsClient{nullptr}; 87 | void attachTo(ConduitPolysynth &p); 88 | 89 | struct ModulatedValue 90 | { 91 | float *base{nullptr}; 92 | float *externalMod{nullptr}; 93 | float *internalMod{nullptr}; 94 | 95 | inline float value() 96 | { 97 | assert(base); 98 | assert(internalMod); 99 | assert(externalMod); 100 | return *base + *externalMod + *internalMod; 101 | } 102 | }; 103 | 104 | // If you change this also change the param in polysynth.cpp 105 | enum FilterRouting 106 | { 107 | LowWSMulti, 108 | MultiWSLow, 109 | WSLowMulti, 110 | LowMultiWS, 111 | WSPar, 112 | ParWS 113 | } filterRouting; 114 | 115 | enum Waveshapers 116 | { 117 | Soft, 118 | OJD, 119 | Digital, 120 | FullWaveRect, 121 | WestcoastFold, 122 | Fuzz 123 | }; 124 | 125 | enum LPFTypes 126 | { 127 | OBXD, 128 | Vintage, 129 | K35, 130 | CutWarp, 131 | ResWarp, 132 | Comb 133 | }; 134 | 135 | std::unordered_map externalMods, internalMods; 136 | 137 | void applyExternalMod(clap_id param, float value); 138 | 139 | // Saw Oscillator 140 | int sawUnison{3}; 141 | bool sawActive{true}; 142 | ModulatedValue sawUnisonDetune, sawCoarse, sawFine, sawLevel; 143 | sst::basic_blocks::dsp::lipol sawLevel_lipol; 144 | std::array sawUniPanL, sawUniPanR, sawUniVoiceDetune, sawUniLevelNorm; 145 | std::array>, 147 | max_uni> 148 | sawOsc; 149 | 150 | // Pulse Oscillator 151 | bool pulseActive{true}; 152 | ModulatedValue pulseWidth, pulseOctave, pulseCoarse, pulseFine, pulseLevel; 153 | sst::basic_blocks::dsp::lipol pulseLevel_lipol; 154 | sst::basic_blocks::dsp::DPWPulseOscillator< 155 | sst::basic_blocks::dsp::BlockInterpSmoothingStrategy> 156 | pulseOsc; 157 | 158 | // Sin Oscillator 159 | bool sinActive{true}; 160 | ModulatedValue sinOctave, sinCoarse, sinLevel; 161 | sst::basic_blocks::dsp::lipol sinLevel_lipol; 162 | sst::basic_blocks::dsp::QuadratureOscillator sinOsc; 163 | 164 | // Noise Source 165 | bool noiseActive{true}; 166 | ModulatedValue noiseColor, noiseLevel; 167 | sst::basic_blocks::dsp::lipol noiseLevel_lipol; 168 | float w0, w1; 169 | std::default_random_engine gen; 170 | std::uniform_real_distribution urd; 171 | 172 | sst::basic_blocks::dsp::lipol_sse aegPFG_lipol; 173 | ModulatedValue aegPFG; 174 | 175 | bool svfActive; 176 | int svfMode{StereoSimperSVF::Mode::LP}; 177 | ModulatedValue svfCutoff, svfResonance; 178 | 179 | bool lpfActive; 180 | int lpfMode{0}; 181 | ModulatedValue lpfCutoff, lpfResonance; 182 | 183 | struct EnvValue 184 | { 185 | ModulatedValue attack, decay, sustain, release; 186 | } aegValues, fegValues; 187 | ModulatedValue fegToSvfCutoff; 188 | ModulatedValue fegToLPFCutoff; 189 | ModulatedValue velocitySens; 190 | 191 | ModulatedValue svfKeytrack; 192 | ModulatedValue lpfKeytrack; 193 | 194 | bool wsActive; 195 | sst::basic_blocks::dsp::lipol wsDrive_lipol, wsBias_lipol; 196 | ModulatedValue wsDrive, wsBias; 197 | 198 | sst::basic_blocks::dsp::lipol filterFeedback_lipol; 199 | ModulatedValue filterFeedback; 200 | __m128 filterFeedbackSignal; 201 | 202 | bool anyFilterStepActive; 203 | 204 | ModulatedValue outputPan, outputLevel; 205 | sst::basic_blocks::dsp::lipol_sse outputLevel_lipol; 206 | 207 | // Two values can modify pitch, the note expression and the bend wheel. 208 | // After adjusting these, call 'recalcPitch' 209 | float pitchNoteExpressionValue{0.f}, pitchBendWheel{0.f}; 210 | 211 | // In MPE MIDI1 mode we also get mpePitchBend from midi. 212 | float mpePitchBend{0.f}; 213 | 214 | float mpeTimbre{0.f}, mpePressure{0.f}; 215 | 216 | // Finally, please set my sample rate at voice on. Thanks! 217 | float samplerate{0}; 218 | 219 | using env_t = sst::basic_blocks::modulators::ADSREnvelope; 220 | env_t aeg, feg; 221 | bool gated{false}; 222 | bool active{false}; 223 | 224 | using lfo_t = sst::basic_blocks::modulators::SimpleLFO; 225 | std::array lfos; 226 | struct LfoData 227 | { 228 | lfo_t::Shape shape; 229 | ModulatedValue rate, deform, amplitude; 230 | } lfoData[2]; 231 | 232 | void processBlock(); 233 | 234 | float outputOS alignas(16)[2][blockSizeOS]; 235 | 236 | void start(int16_t port, int16_t channel, int16_t key, int32_t noteid, double velocity); 237 | void release(); 238 | 239 | float baseFrequencyByMidiKey[128]; 240 | void recalcPitch(); 241 | void recalcFilter(); 242 | 243 | void receiveNoteExpression(int expression, double value); 244 | void applyPolyphonicAftertouch(int8_t val) { polyphonicAT = 1.f * val / 127.f; } 245 | void applyChannelPressure(int8_t val) 246 | { 247 | channelPressure = 1.f * val / 127.f; 248 | mpePressure = channelPressure; 249 | } 250 | void applyMIDI1CC(uint8_t cc, uint8_t val) 251 | { 252 | assert(cc >= 0); 253 | midi1CC[cc] = 1.f * val / 127.f; 254 | if (cc == 74) 255 | mpeTimbre = midi1CC[cc]; 256 | } 257 | 258 | // Sigh - fix this to a table of course 259 | inline float envelope_rate_linear_nowrap(float f) { return blockSizeOS * srInv * pow(2.f, -f); } 260 | 261 | inline bool isPlaying() const { return aeg.stage < env_t::s_eoc; } 262 | 263 | struct StereoSimperSVF // thanks to urs @ u-he and andy simper @ cytomic 264 | { 265 | __m128 ic1eq{_mm_setzero_ps()}, ic2eq{_mm_setzero_ps()}; 266 | __m128 g, k, gk, a1, a2, a3, ak; 267 | 268 | __m128 oneSSE{_mm_set1_ps(1.0)}; 269 | __m128 twoSSE{_mm_set1_ps(2.0)}; 270 | enum Mode 271 | { 272 | LP, 273 | HP, 274 | BP, 275 | NOTCH, 276 | PEAK, 277 | ALL 278 | }; 279 | 280 | void setCoeff(float key, float res, float srInv); 281 | 282 | template static void step(StereoSimperSVF &that, float &L, float &R); 283 | template static __m128 stepSSE(StereoSimperSVF &that, __m128); 284 | 285 | void init(); 286 | } svfImpl; 287 | std::function<__m128(StereoSimperSVF &, __m128)> svfFilterOp; 288 | 289 | sst::waveshapers::QuadWaveshaperPtr wsPtr{nullptr}; 290 | sst::waveshapers::QuadWaveshaperState wsState; 291 | 292 | sst::filters::FilterUnitQFPtr qfPtr{nullptr}; 293 | sst::filters::QuadFilterUnitState qfState; 294 | sst::filters::FilterType qfType; 295 | sst::filters::FilterSubType qfSubType; 296 | 297 | float delayBufferData[4][sst::filters::utilities::MAX_FB_COMB + 298 | sst::filters::utilities::SincTable::FIRipol_N]{}; 299 | 300 | struct ModRoutingData 301 | { 302 | float *source{nullptr}; 303 | float *via{nullptr}; 304 | float *target{nullptr}; 305 | float *depth{nullptr}; 306 | float range; 307 | }; 308 | 309 | std::array routings; 310 | 311 | private: 312 | double baseFreq{440.0}; 313 | double srInv{1.0 / 44100.0}; 314 | }; 315 | } // namespace sst::conduit::polysynth 316 | #endif 317 | -------------------------------------------------------------------------------- /src/ring-modulator/ring-modulator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Conduit - a project highlighting CLAP-first development 3 | * and exercising the surge synth team libraries. 4 | * 5 | * Copyright 2023-2024 Paul Walker and authors in github 6 | * 7 | * This file you are viewing now is released under the 8 | * MIT license as described in LICENSE.md 9 | * 10 | * The assembled program which results from compiling this 11 | * project has GPL3 dependencies, so if you distribute 12 | * a binary, the combined work would be a GPL3 product. 13 | * 14 | * Roughly, that means you are welcome to copy the code and 15 | * ideas in the src/ directory, but perhaps not code from elsewhere 16 | * if you are closed source or non-GPL3. And if you do copy this code 17 | * you will need to replace some of the dependencies. Please see 18 | * the discussion in README.md for further information on what this may 19 | * mean for you. 20 | */ 21 | 22 | #include "ring-modulator.h" 23 | #include "juce_gui_basics/juce_gui_basics.h" 24 | #include "sst/basic-blocks/mechanics/block-ops.h" 25 | #include "version.h" 26 | 27 | namespace sst::conduit::ring_modulator 28 | { 29 | 30 | namespace mech = sst::basic_blocks::mechanics; 31 | 32 | const clap_plugin_descriptor *ConduitRingModulatorConfig::getDescription() 33 | { 34 | 35 | static const char *features[] = {CLAP_PLUGIN_FEATURE_AUDIO_EFFECT, CLAP_PLUGIN_FEATURE_DELAY, 36 | nullptr}; 37 | static clap_plugin_descriptor desc = {CLAP_VERSION, 38 | "org.surge-synth-team.conduit.ring-modulator", 39 | "Conduit Ring Modulator", 40 | "Surge Synth Team", 41 | "https://surge-synth-team.org", 42 | "", 43 | "", 44 | sst::conduit::build::FullVersionStr, 45 | "The Conduit RingModulator is a work in progress", 46 | &features[0]}; 47 | return &desc; 48 | } 49 | 50 | ConduitRingModulator::ConduitRingModulator(const clap_host *host) 51 | : sst::conduit::shared::ClapBaseClass(host), 52 | hr_up(6, true), hr_scup(6, true), hr_down(6, true) 53 | { 54 | auto autoFlag = CLAP_PARAM_IS_AUTOMATABLE; 55 | auto steppedFlag = autoFlag | CLAP_PARAM_IS_STEPPED; 56 | auto modFlag = autoFlag | CLAP_PARAM_IS_MODULATABLE; 57 | 58 | paramDescriptions.push_back(ParamDesc() 59 | .asPercent() 60 | .withID(pmMixLevel) 61 | .withName("Mix Level") 62 | .withGroupName("Ring Modulator") 63 | .withDefault(0.8) 64 | .withFlags(modFlag)); 65 | 66 | paramDescriptions.push_back( 67 | ParamDesc() 68 | .asInt() 69 | .withID(pmAlgo) 70 | .withName("Algorithm") 71 | .withGroupName("Ring Modulator") 72 | .withDefault(0) 73 | .withRange(0, 1) 74 | .withFlags(steppedFlag) 75 | .withUnorderedMapFormatting({{algoDigital, "Digital"}, {algoAnalog, "Analog"}})); 76 | 77 | paramDescriptions.push_back( 78 | ParamDesc() 79 | .asInt() 80 | .withID(pmSource) 81 | .withName("Modulator Source") 82 | .withGroupName("Ring Modulator") 83 | .withDefault(0) 84 | .withRange(0, 1) 85 | .withFlags(steppedFlag) 86 | .withUnorderedMapFormatting({{srcInternal, "Internal"}, {srcSidechain, "Sidechain"}})); 87 | 88 | paramDescriptions.push_back(ParamDesc() 89 | .asFloat() 90 | .withID(pmInternalSourceFrequency) 91 | .asAudibleFrequency() 92 | .withFlags(modFlag) 93 | .withName("Source Frequency") 94 | .withGroupName("Ring Modulator")); 95 | configureParams(); 96 | 97 | attachParam(pmMixLevel, mix); 98 | attachParam(pmInternalSourceFrequency, freq); 99 | 100 | attachParam(pmAlgo, algo); 101 | attachParam(pmSource, src); 102 | 103 | clapJuceShim = std::make_unique(this); 104 | clapJuceShim->setResizable(true); 105 | } 106 | 107 | ConduitRingModulator::~ConduitRingModulator() {} 108 | 109 | bool ConduitRingModulator::audioPortsInfo(uint32_t index, bool isInput, 110 | clap_audio_port_info *info) const noexcept 111 | { 112 | static constexpr uint32_t inId{16}, outId{72}; 113 | if (isInput) 114 | { 115 | if (index == 0) 116 | { 117 | info->id = inId; 118 | info->in_place_pair = CLAP_INVALID_ID; 119 | strncpy(info->name, "main input", sizeof(info->name)); 120 | info->flags = CLAP_AUDIO_PORT_IS_MAIN; 121 | info->channel_count = 2; 122 | info->port_type = CLAP_PORT_STEREO; 123 | } 124 | else 125 | { 126 | info->id = inId; 127 | info->in_place_pair = CLAP_INVALID_ID; 128 | strncpy(info->name, "ring sidechain", sizeof(info->name)); 129 | info->flags = 0; 130 | info->channel_count = 2; 131 | info->port_type = CLAP_PORT_STEREO; 132 | } 133 | return true; 134 | } 135 | else 136 | { 137 | info->id = outId; 138 | info->in_place_pair = CLAP_INVALID_ID; 139 | strncpy(info->name, "main output", sizeof(info->name)); 140 | info->flags = CLAP_AUDIO_PORT_IS_MAIN; 141 | info->channel_count = 2; 142 | info->port_type = CLAP_PORT_STEREO; 143 | 144 | return true; 145 | } 146 | return false; 147 | } 148 | 149 | float diode_sim(float v) 150 | { 151 | auto vb = 0.2; 152 | auto vl = 0.5; 153 | auto h = 1.f; 154 | vl = std::max(vl, vb + 0.02f); 155 | if (v < vb) 156 | { 157 | return 0; 158 | } 159 | if (v < vl) 160 | { 161 | auto vvb = v - vb; 162 | return h * vvb * vvb / (2.f * vl - 2.f * vb); 163 | } 164 | auto vlvb = vl - vb; 165 | return h * v - h * vl + h * vlvb * vlvb / (2.f * vl - 2.f * vb); 166 | } 167 | 168 | clap_process_status ConduitRingModulator::process(const clap_process *process) noexcept 169 | { 170 | handleEventsFromUIQueue(process->out_events); 171 | 172 | if (process->audio_outputs_count <= 0) 173 | return CLAP_PROCESS_SLEEP; 174 | if (process->audio_inputs_count <= 0) 175 | return CLAP_PROCESS_SLEEP; 176 | 177 | float **out = process->audio_outputs[0].data32; 178 | auto ochans = process->audio_outputs->channel_count; 179 | 180 | float **const in = process->audio_inputs[0].data32; 181 | auto ichans = process->audio_inputs->channel_count; 182 | 183 | float **const sidechain = process->audio_inputs[1].data32; 184 | auto scchans = process->audio_inputs->channel_count; 185 | 186 | assert(ochans == 2 || ichans == 2 || scchans == 2); 187 | 188 | auto chans = std::min({ochans, ichans, scchans}); 189 | if (chans < 2) 190 | return CLAP_PROCESS_SLEEP; 191 | 192 | auto ev = process->in_events; 193 | auto sz = ev->size(ev); 194 | 195 | // This pointer is the sentinel to our next event which we advance once an event is processed 196 | const clap_event_header_t *nextEvent{nullptr}; 197 | uint32_t nextEventIndex{0}; 198 | if (sz != 0) 199 | { 200 | nextEvent = ev->get(ev, nextEventIndex); 201 | } 202 | 203 | auto isDigital = *algo < 0.5; 204 | 205 | for (auto i = 0U; i < process->frames_count; ++i) 206 | { 207 | while (nextEvent && nextEvent->time == i) 208 | { 209 | handleInboundEvent(nextEvent); 210 | nextEventIndex++; 211 | if (nextEventIndex >= sz) 212 | nextEvent = nullptr; 213 | else 214 | nextEvent = ev->get(ev, nextEventIndex); 215 | } 216 | 217 | inputBuf[0][pos] = in[0][i]; 218 | inputBuf[1][pos] = in[1][i]; 219 | sidechainBuf[0][pos] = sidechain[0][i]; 220 | sidechainBuf[1][pos] = sidechain[1][i]; 221 | 222 | out[0][i] = outBuf[0][pos] * mix.v + inMixBuf[0][pos] * (1 - mix.v); 223 | out[1][i] = outBuf[1][pos] * mix.v + inMixBuf[1][pos] * (1 - mix.v); 224 | 225 | pos++; 226 | 227 | if (pos == blockSize) 228 | { 229 | memcpy(inMixBuf, inputBuf, sizeof(inMixBuf)); 230 | hr_up.process_block_U2(inputBuf[0], inputBuf[1], inputOS[0], inputOS[1], blockSizeOS); 231 | 232 | if ((Source)(*src) == srcInternal) 233 | { 234 | static constexpr double mf0{8.17579891564}; 235 | internalSource.setRate(2.0 * M_PI * note_to_pitch_ignoring_tuning(freq.v + 69) * 236 | mf0 * dsamplerate_inv * 0.5); // 0.5 for oversample 237 | 238 | for (int i = 0; i < blockSizeOS; ++i) 239 | { 240 | internalSource.step(); 241 | sourceOS[0][i] = 2 * internalSource.u; 242 | sourceOS[1][i] = 2 * internalSource.u; 243 | } 244 | } 245 | else 246 | { 247 | hr_scup.process_block_U2(sidechainBuf[0], sidechainBuf[1], sourceOS[0], sourceOS[1], 248 | blockSizeOS); 249 | mech::scale_by(4, sourceOS[0], sourceOS[1]); 250 | } 251 | 252 | if (isDigital) 253 | { 254 | mech::mul_block(inputOS[0], sourceOS[0]); 255 | mech::mul_block(inputOS[1], sourceOS[1]); 256 | } 257 | else 258 | { 259 | for (int c = 0; c < 2; ++c) 260 | { 261 | for (int s = 0; s < blockSizeOS; ++s) 262 | { 263 | auto vin = inputOS[c][s]; 264 | auto vc = sourceOS[c][s]; 265 | auto A = 0.5 * vin + vc; 266 | auto B = vc - 0.5 * vin; 267 | 268 | auto dPA = diode_sim(A); 269 | auto dMA = diode_sim(-A); 270 | auto dPB = diode_sim(B); 271 | auto dMB = diode_sim(-B); 272 | 273 | auto res = dPA + dMA - dPB - dMB; 274 | 275 | inputOS[c][s] = res; 276 | } 277 | } 278 | } 279 | 280 | hr_down.process_block_D2(inputOS[0], inputOS[1], blockSizeOS, outBuf[0], outBuf[1]); 281 | pos = 0; 282 | } 283 | 284 | processLags(); 285 | } 286 | return CLAP_PROCESS_CONTINUE; 287 | } 288 | 289 | void ConduitRingModulator::handleInboundEvent(const clap_event_header_t *evt) 290 | { 291 | if (handleParamBaseEvents(evt)) 292 | { 293 | return; 294 | } 295 | 296 | // Other events just get dropped right now 297 | } 298 | } // namespace sst::conduit::ring_modulator -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 4 | # I don't want to deal with filesystem from two places right now 5 | set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE STRING "Build for 10.1") 6 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 7 | 8 | string(TIMESTAMP DAY_OF_YEAR "%j") 9 | string(TIMESTAMP YEAR "%Y") 10 | math(EXPR PART0 "${YEAR}-2023 + 2") 11 | math(EXPR PART1 "${DAY_OF_YEAR}/2") 12 | message(STATUS "Using date-driven version while in alpha. 0.${PART0}.${PART1}.0") 13 | 14 | project(conduit VERSION 0.${PART0}.${PART1}.0 LANGUAGES C CXX) 15 | 16 | if (APPLE) 17 | enable_language(OBJC) 18 | enable_language(OBJCXX) 19 | set(CMAKE_OBJC_VISIBILITY_PRESET hidden) 20 | set(CMAKE_OBJCXX_VISIBILITY_PRESET hidden) 21 | endif() 22 | 23 | set(BUILD_SHARED_LIBS OFF CACHE BOOL "Never want shared if not specified") 24 | set(CMAKE_CXX_EXTENSIONS OFF) 25 | set(CMAKE_CXX_STANDARD 20) 26 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 27 | set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) 28 | 29 | # use asan as an option (currently mac only) 30 | option(USE_SANITIZER "Build and link with ASAN" FALSE) 31 | 32 | # Compiler specific choices 33 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") 34 | add_compile_options( 35 | $<$:-fsanitize=address> 36 | $<$:-fsanitize=undefined> 37 | 38 | $<$,$,$>:-fno-char8_t> 39 | ) 40 | 41 | add_link_options( 42 | $<$:-fsanitize=address> 43 | $<$:-fsanitize=undefined> 44 | ) 45 | endif() 46 | 47 | if (MSVC) 48 | add_compile_options( 49 | # Set source and executable charsets to UTF-8 50 | $<$:/utf-8> 51 | # Do *not* use the new, breaking char8_t UTF-8 bits in C++20. 52 | $<$:/Zc:char8_t-> 53 | # make msvc define __cplulsplus properly 54 | $<$:/Zc:__cplusplus> 55 | ) 56 | endif() 57 | 58 | # Copy on mac (could expand to other platforms) 59 | option(COPY_AFTER_BUILD "Copy the clap to ~/Library on MACOS, ~/.clap on linux" FALSE) 60 | 61 | add_subdirectory(libs/clap EXCLUDE_FROM_ALL) 62 | add_subdirectory(libs/clap-helpers EXCLUDE_FROM_ALL) 63 | add_subdirectory(libs/fmt EXCLUDE_FROM_ALL) 64 | 65 | add_subdirectory(libs/sst/sst-clap-helpers) 66 | if (NOT DEFINED JUCE_PATH) 67 | set(JUCE_PATH "${CMAKE_SOURCE_DIR}/libs/JUCE") 68 | endif() 69 | add_clap_juce_shim(JUCE_PATH ${JUCE_PATH}) 70 | 71 | add_library(simde INTERFACE) 72 | target_include_directories(simde INTERFACE libs/simde) 73 | 74 | add_subdirectory(libs/sst/sst-jucegui) 75 | add_subdirectory(libs/sst/sst-basic-blocks) 76 | set(SST_PLUGININFRA_FILESYSTEM_FORCE_PLATFORM TRUE CACHE BOOL "Use System FS") 77 | add_subdirectory(libs/sst/sst-plugininfra) 78 | add_subdirectory(libs/sst/sst-cpputils) 79 | add_subdirectory(libs/sst/sst-filters) 80 | add_subdirectory(libs/sst/sst-waveshapers) 81 | add_subdirectory(libs/sst/sst-effects) 82 | add_subdirectory(libs/sst/sst-voicemanager) 83 | 84 | add_subdirectory(libs/ni-midi2 EXCLUDE_FROM_ALL) 85 | 86 | add_library(mts-esp-client STATIC libs/MTS-ESP/Client/libMTSClient.cpp) 87 | target_include_directories(mts-esp-client PUBLIC libs/MTS-ESP/Client) 88 | 89 | set(CLAP_WRAPPER_DOWNLOAD_DEPENDENCIES TRUE CACHE BOOL "Get em") 90 | set(CLAP_WRAPPER_DONT_ADD_TARGETS TRUE CACHE BOOL "I'll targetize") 91 | set(CLAP_WRAPPER_BUILD_AUV2 TRUE CACHE BOOL "It's only logical") 92 | if (APPLE) 93 | # BaconPaul has jack kinda installed 94 | set(RTAUDIO_API_JACK FALSE CACHE BOOL "Not on apple") 95 | endif() 96 | 97 | add_subdirectory(libs/clap-wrapper) 98 | 99 | set(CONDUIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE STRING "Top level source") 100 | 101 | include(cmake/CmakeRC.cmake) 102 | add_subdirectory(resources) 103 | 104 | include(cmake/git-info.cmake) 105 | 106 | add_subdirectory(src) 107 | 108 | set(CLAP_TARGET ${PROJECT_NAME}_clap) 109 | add_library(${CLAP_TARGET} MODULE 110 | src/conduit-clap-entry.cpp 111 | ) 112 | target_link_libraries(${CLAP_TARGET} conduit-impl) 113 | target_library_make_clap(TARGET ${CLAP_TARGET} 114 | CLAP_NAME "Conduit" 115 | CLAP_MACOS_BUNDLE_IDENTIFIER org.surge-synth-team.conduit.clap 116 | CLAP_MACOS_BUNDLE_VERSION ${PROJECT_VERSION} 117 | COPY_AFTER_BUILD ${COPY_AFTER_BUILD} 118 | ) 119 | set_target_properties(${CLAP_TARGET} PROPERTIES BUNDLE TRUE MACOSX_BUNDLE TRUE) 120 | 121 | set(VST3_TARGET ${PROJECT_NAME}_vst3) 122 | add_library(${VST3_TARGET} MODULE) 123 | target_sources(${VST3_TARGET} PRIVATE src/conduit-clap-entry.cpp) 124 | target_link_libraries(${VST3_TARGET} PRIVATE conduit-impl) 125 | target_add_vst3_wrapper(TARGET ${VST3_TARGET} 126 | OUTPUT_NAME "Conduit" 127 | SUPPORTS_ALL_NOTE_EXPRESSIONS TRUE 128 | ) 129 | if (WIN32) 130 | # Question - do we want this default in the helpers 131 | set_target_properties(${VST3_TARGET} 132 | PROPERTIES 133 | LIBRARY_OUTPUT_DIRECTORY VST3 134 | ) 135 | endif() 136 | if (${COPY_AFTER_BUILD}) 137 | target_vst3_copy_after_build(TARGET ${VST3_TARGET}) 138 | endif() 139 | 140 | if (APPLE) 141 | set(AUV2_TARGET ${PROJECT_NAME}_auv2) 142 | add_library(${AUV2_TARGET} MODULE) 143 | target_sources(${AUV2_TARGET} PRIVATE src/conduit-clap-entry.cpp) 144 | target_link_libraries(${AUV2_TARGET} PRIVATE conduit-impl) 145 | message(STATUS "Adding AUv2 with ${PROJECT_VERSION}") 146 | target_add_auv2_wrapper( 147 | TARGET ${AUV2_TARGET} 148 | OUTPUT_NAME "Conduit" 149 | BUNDLE_IDENTIFIER "org.surge-synth-team.conduit.auv2" 150 | BUNDLE_VERSION ${PROJECT_VERSION} 151 | 152 | CLAP_TARGET_FOR_CONFIG ${PROJECT_NAME}_clap 153 | ) 154 | 155 | if (${COPY_AFTER_BUILD}) 156 | message(STATUS "conduit: will install auv2") 157 | target_auv2_copy_after_build(TARGET ${AUV2_TARGET}) 158 | endif() 159 | endif() 160 | 161 | 162 | ## Finally set up an ALL target which builds and collects 163 | function(add_to_all) 164 | cmake_parse_arguments(AA "" "" "TARGETS" ${ARGN} ) 165 | 166 | set(pn ${PROJECT_NAME}_all) 167 | if (NOT TARGET ${pn}) 168 | add_custom_target(${pn}) 169 | endif() 170 | 171 | add_dependencies(${pn} ${AA_TARGETS}) 172 | 173 | set(coldir "${CMAKE_BINARY_DIR}/conduit_products") 174 | foreach(TGT ${AA_TARGETS}) 175 | if (APPLE) 176 | add_custom_command(TARGET ${pn} 177 | POST_BUILD 178 | COMMAND ${CMAKE_COMMAND} -E make_directory ${coldir} 179 | COMMAND ${CMAKE_COMMAND} -E copy_directory $/../../../$.$/ 180 | ${coldir}/$.$/ 181 | ) 182 | else() 183 | set(isb FALSE) 184 | get_property(isb TARGET ${TGT} PROPERTY CONDUIT_HAS_BUNDLE_STRUCTURE) 185 | 186 | if (${isb}) 187 | message(STATUS "conduit: Treating ${TGT} as a bundle on linux") 188 | add_custom_command(TARGET ${pn} 189 | POST_BUILD 190 | COMMAND ${CMAKE_COMMAND} -E make_directory ${coldir} 191 | COMMAND ${CMAKE_COMMAND} -E copy_directory $/../../../$.$/ 192 | ${coldir}/$.$/ 193 | ) 194 | else() 195 | add_custom_command(TARGET ${pn} 196 | POST_BUILD 197 | COMMAND ${CMAKE_COMMAND} -E make_directory ${coldir} 198 | COMMAND ${CMAKE_COMMAND} -E copy $ ${coldir} 199 | ) 200 | endif() 201 | endif() 202 | endforeach () 203 | endfunction(add_to_all) 204 | 205 | function(make_conduit_standalone) 206 | set(oaf ID NAME) 207 | cmake_parse_arguments(AA "" "${oaf}" "" ${ARGN}) 208 | 209 | set(sat ${PROJECT_NAME}_${AA_ID}_standalone) 210 | add_executable(${sat}) 211 | target_sources(${sat} PRIVATE src/conduit-clap-entry.cpp) 212 | target_link_libraries(${sat} PRIVATE conduit-impl) 213 | target_add_standalone_wrapper(TARGET ${sat} 214 | OUTPUT_NAME "Conduit ${AA_NAME}" 215 | STATICALLY_LINKED_CLAP_ENTRY True 216 | PLUGIN_ID "org.surge-synth-team.conduit.${AA_ID}") 217 | 218 | add_to_all(TARGETS ${sat}) 219 | endfunction() 220 | 221 | 222 | if (${CONDUIT_BUILD_STANDALONES}) 223 | make_conduit_standalone(NAME "Polysynth" ID "polysynth") 224 | make_conduit_standalone(NAME "Polymetric Delay" ID "polymetric-delay") 225 | make_conduit_standalone(NAME "Ring Modulator" ID "ring-modulator") 226 | make_conduit_standalone(NAME "Chord Memory" ID "chord-memory") 227 | endif() 228 | 229 | if (UNIX) 230 | set_target_properties(${PROJECT_NAME}_vst3 PROPERTIES CONDUIT_HAS_BUNDLE_STRUCTURE TRUE CONDUIT_BUNDLE_SUFFIX "vst3") 231 | endif() 232 | add_to_all(TARGETS ${PROJECT_NAME}_clap ${PROJECT_NAME}_vst3) 233 | if (APPLE) 234 | add_to_all(TARGETS ${PROJECT_NAME}_auv2) 235 | endif() 236 | 237 | add_custom_command(TARGET ${PROJECT_NAME}_all 238 | POST_BUILD 239 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 240 | COMMAND ${CMAKE_COMMAND} -E copy resources/README.rtf "${CMAKE_BINARY_DIR}/conduit_products" 241 | COMMAND ${CMAKE_COMMAND} -E copy resources/License_GPL3.txt "${CMAKE_BINARY_DIR}/conduit_products/License.txt") 242 | 243 | 244 | add_custom_target(${PROJECT_NAME}_installer) 245 | add_custom_command(TARGET ${PROJECT_NAME}_installer POST_BUILD 246 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 247 | COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/installer 248 | ) 249 | 250 | string( TIMESTAMP NIGHTLY_TAG "%Y-%m-%d-%H-%M") 251 | add_dependencies(${PROJECT_NAME}_installer ${PROJECT_NAME}_all) 252 | if (APPLE) 253 | add_custom_command(TARGET ${PROJECT_NAME}_installer POST_BUILD 254 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 255 | COMMAND ${CMAKE_SOURCE_DIR}/scripts/mac_installer.sh ${CMAKE_BINARY_DIR}/conduit_products ${CMAKE_BINARY_DIR}/installer nightly-${NIGHTLY_TAG} 256 | ) 257 | elseif (WIN32) 258 | message(STATUS "Configuring for Windows installer") 259 | add_custom_command( 260 | TARGET ${PROJECT_NAME}_installer 261 | POST_BUILD 262 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 263 | COMMAND ${CMAKE_COMMAND} -E make_directory installer 264 | COMMAND 7z a -r installer/conduit-win32-x64-nightly-${NIGHTLY_TAG}.zip ${CMAKE_BINARY_DIR}/conduit_products 265 | COMMAND ${CMAKE_COMMAND} -E echo "ZIP Installer in: installer/conduit-win32-x64-nightly-${NIGHTLY_TAG}.zip") 266 | else () 267 | message(STATUS "Basic installer: Target is installer/${SCXT_ZIP}") 268 | add_custom_command( 269 | TARGET ${PROJECT_NAME}_installer 270 | POST_BUILD 271 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 272 | COMMAND ${CMAKE_COMMAND} -E make_directory installer 273 | COMMAND ${CMAKE_COMMAND} -E tar cvf installer/conduit-lin-x64-nightly-${NIGHTLY_TAG}.zip --format=zip ${CMAKE_BINARY_DIR}/conduit_products 274 | COMMAND ${CMAKE_COMMAND} -E echo "Installer in: installer/conduit-lin-x64-nightly-${NIGHTLY_TAG}.zip") 275 | endif () 276 | --------------------------------------------------------------------------------