├── .clang-format ├── .clang-tidy ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── auto-format.yml │ └── cmake.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── modules ├── CMakeLists.txt └── cmake │ ├── CMakeRC.cmake │ ├── SourceFileGroup.cmake │ └── WarningFlags.cmake ├── res └── CMakeLists.txt ├── scripts ├── mac_builds.sh ├── validate.sh └── win_builds.sh ├── setup.sh └── src ├── CMakeLists.txt ├── TempPlugin.cpp ├── TempPlugin.h └── pch.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: -4 3 | AlignAfterOpenBracket: Align 4 | AlignConsecutiveAssignments: false 5 | AlignConsecutiveDeclarations: false 6 | AlignEscapedNewlines: Left 7 | AlignOperands: Align 8 | AlignTrailingComments: false 9 | AllowAllParametersOfDeclarationOnNextLine: false 10 | AllowShortBlocksOnASingleLine: Never 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: All 13 | AllowShortIfStatementsOnASingleLine: Never 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterDefinitionReturnType: None 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: false 18 | AlwaysBreakTemplateDeclarations: Yes 19 | BinPackArguments: false 20 | BinPackParameters: false 21 | BreakAfterJavaFieldAnnotations: false 22 | BreakBeforeBinaryOperators: NonAssignment 23 | BreakBeforeBraces: Allman 24 | BreakBeforeTernaryOperators: true 25 | BreakConstructorInitializersBeforeComma: false 26 | BreakStringLiterals: false 27 | ColumnLimit: 0 28 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 29 | ConstructorInitializerIndentWidth: 4 30 | ContinuationIndentWidth: 4 31 | Cpp11BracedListStyle: false 32 | DerivePointerAlignment: false 33 | DisableFormat: false 34 | ExperimentalAutoDetectBinPacking: false 35 | ForEachMacros: ['forEachXmlChildElement'] 36 | IndentCaseLabels: true 37 | IndentWidth: 4 38 | IndentWrappedFunctionNames: true 39 | KeepEmptyLinesAtTheStartOfBlocks: false 40 | Language: Cpp 41 | MaxEmptyLinesToKeep: 1 42 | NamespaceIndentation: Inner 43 | PointerAlignment: Left 44 | ReflowComments: false 45 | SortIncludes: true 46 | SpaceAfterCStyleCast: true 47 | SpaceAfterLogicalNot: true 48 | SpaceBeforeAssignmentOperators: true 49 | SpaceBeforeCpp11BracedList: true 50 | SpaceBeforeParens: NonEmptyParentheses 51 | SpaceInEmptyParentheses: false 52 | SpaceBeforeInheritanceColon: true 53 | SpacesInAngles: false 54 | SpacesInCStyleCastParentheses: false 55 | SpacesInContainerLiterals: true 56 | SpacesInParentheses: false 57 | SpacesInSquareBrackets: false 58 | Standard: "c++17" 59 | TabWidth: 4 60 | UseTab: Never 61 | --- 62 | Language: ObjC 63 | BasedOnStyle: Chromium 64 | AlignTrailingComments: true 65 | BreakBeforeBraces: Allman 66 | ColumnLimit: 0 67 | IndentWidth: 4 68 | KeepEmptyLinesAtTheStartOfBlocks: false 69 | ObjCSpaceAfterProperty: true 70 | ObjCSpaceBeforeProtocolList: true 71 | PointerAlignment: Left 72 | SpacesBeforeTrailingComments: 1 73 | TabWidth: 4 74 | UseTab: Never 75 | ... 76 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: '-*, 2 | bugprone-argument-comment, 3 | bugprone-assert-side-effect, 4 | bugprone-bad-signal-to-kill-thread, 5 | bugprone-branch-clone, 6 | bugprone-copy-constructor-init, 7 | bugprone-dangling-handle, 8 | bugprone-dynamic-static-initializers, 9 | bugprone-fold-init-type, 10 | bugprone-forward-declaration-namespace, 11 | bugprone-forwarding-reference-overload, 12 | bugprone-inaccurate-erase, 13 | bugprone-incorrect-roundings, 14 | bugprone-integer-division, 15 | bugprone-lambda-function-name, 16 | bugprone-macro-parentheses, 17 | bugprone-macro-repeated-side-effects, 18 | bugprone-misplaced-operator-in-strlen-in-alloc, 19 | bugprone-misplaced-pointer-arithmetic-in-alloc, 20 | bugprone-misplaced-widening-cast, 21 | bugprone-move-forwarding-reference, 22 | bugprone-multiple-statement-macro, 23 | bugprone-no-escape,bugprone-not-null-terminated-result, 24 | bugprone-posix-return, 25 | bugprone-reserved-identifier, 26 | bugprone-sizeof-container, 27 | bugprone-sizeof-expression, 28 | bugprone-spuriously-wake-up-functions, 29 | bugprone-string-constructor, 30 | bugprone-string-integer-assignment, 31 | bugprone-string-literal-with-embedded-nul, 32 | bugprone-suspicious-enum-usage, 33 | bugprone-suspicious-include, 34 | bugprone-suspicious-memory-comparison, 35 | bugprone-suspicious-memset-usage, 36 | bugprone-suspicious-missing-comma, 37 | bugprone-suspicious-semicolon, 38 | bugprone-suspicious-string-compare, 39 | bugprone-swapped-arguments, 40 | bugprone-terminating-continue, 41 | bugprone-throw-keyword-missing, 42 | bugprone-too-small-loop-variable, 43 | bugprone-undefined-memory-manipulation, 44 | bugprone-undelegated-constructor, 45 | bugprone-unhandled-self-assignment, 46 | bugprone-unused-raii, 47 | bugprone-unused-return-value, 48 | bugprone-use-after-move, 49 | bugprone-virtual-near-miss, 50 | cert-dcl21-cpp, 51 | cert-dcl58-cpp, 52 | cert-err34-c, 53 | cert-err52-cpp, 54 | cert-err60-cpp, 55 | cert-msc50-cpp, 56 | cert-msc51-cpp, 57 | cert-str34-c, 58 | cppcoreguidelines-interfaces-global-init, 59 | cppcoreguidelines-narrowing-conversions, 60 | cppcoreguidelines-pro-type-member-init, 61 | cppcoreguidelines-pro-type-static-cast-downcast, 62 | google-explicit-constructor, 63 | google-runtime-operator, 64 | hicpp-exception-baseclass, 65 | hicpp-multiway-paths-covered, 66 | misc-misplaced-const, 67 | misc-new-delete-overloads, 68 | misc-non-copyable-objects, 69 | misc-throw-by-value-catch-by-reference, 70 | misc-unconventional-assign-operator, 71 | misc-uniqueptr-reset-release, 72 | modernize-avoid-bind, 73 | modernize-concat-nested-namespaces, 74 | modernize-deprecated-headers, 75 | modernize-deprecated-ios-base-aliases, 76 | modernize-loop-convert, 77 | modernize-make-shared, 78 | modernize-make-unique, 79 | modernize-raw-string-literal, 80 | modernize-redundant-void-arg, 81 | modernize-replace-auto-ptr, 82 | modernize-replace-disallow-copy-and-assign-macro, 83 | modernize-replace-random-shuffle, 84 | modernize-return-braced-init-list, 85 | modernize-shrink-to-fit, 86 | modernize-unary-static-assert, 87 | modernize-use-auto, 88 | modernize-use-bool-literals, 89 | modernize-use-emplace, 90 | modernize-use-equals-default, 91 | modernize-use-equals-delete, 92 | modernize-use-noexcept, 93 | modernize-use-nullptr, 94 | modernize-use-override, 95 | modernize-use-transparent-functors, 96 | modernize-use-uncaught-exceptions, 97 | mpi-buffer-deref, 98 | mpi-type-mismatch, 99 | openmp-use-default-none, 100 | performance-faster-string-find, 101 | performance-for-range-copy, 102 | performance-implicit-conversion-in-loop, 103 | performance-inefficient-algorithm, 104 | performance-inefficient-string-concatenation, 105 | performance-inefficient-vector-operation, 106 | performance-move-constructor-init, 107 | performance-no-automatic-move, 108 | performance-noexcept-move-constructor, 109 | performance-trivially-destructible, 110 | performance-type-promotion-in-math-fn, 111 | performance-unnecessary-copy-initialization, 112 | performance-unnecessary-value-param, 113 | portability-simd-intrinsics, 114 | readability-const-return-type, 115 | readability-container-size-empty, 116 | readability-convert-member-functions-to-static, 117 | readability-delete-null-pointer, 118 | readability-deleted-default, 119 | readability-make-member-function-const, 120 | readability-misleading-indentation, 121 | readability-misplaced-array-index, 122 | readability-non-const-parameter, 123 | readability-redundant-control-flow, 124 | readability-redundant-declaration, 125 | readability-redundant-function-ptr-dereference, 126 | readability-redundant-smartptr-get, 127 | readability-redundant-string-cstr, 128 | readability-redundant-string-init, 129 | readability-simplify-subscript-expr, 130 | readability-static-definition-in-anonymous-namespace, 131 | readability-string-compare, 132 | readability-uniqueptr-delete-release, 133 | readability-use-anyofallof' 134 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: jatinchowdhury18 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Windows 10, MacOS] 28 | - DAW [e.g. Ableton Live, FL Studio, Audacity] 29 | - Version [e.g. 2.3] 30 | - 32/64 bit (Windows only) 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: jatinchowdhury18 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/workflows/auto-format.yml: -------------------------------------------------------------------------------- 1 | name: Auto-formatter 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - develop 8 | paths: 9 | - '**.cpp' 10 | - '**.h' 11 | 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build_and_test: 16 | if: contains(toJson(github.event.commits), '***NO_CI***') == false && contains(toJson(github.event.commits), '[ci skip]') == false && contains(toJson(github.event.commits), '[skip ci]') == false 17 | name: Apply clang-format to pull request 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Install Linux Deps 22 | run: | 23 | sudo apt update 24 | sudo apt -y install clang-format-14 25 | clang-format-14 --version 26 | 27 | - name: Checkout code 28 | uses: actions/checkout@v2 29 | with: 30 | persist-credentials: false 31 | fetch-depth: 0 32 | 33 | - name: Run clang-format 34 | shell: bash 35 | run: find src/ -iname *.h -o -iname *.cpp | xargs clang-format-14 -style=file -verbose -i 36 | 37 | - name: Commit & Push changes 38 | uses: actions-js/push@master 39 | with: 40 | message: "Apply clang-format" 41 | branch: ${{ github.head_ref }} 42 | github_token: ${{ secrets.GITHUB_TOKEN }} 43 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - develop 8 | pull_request: 9 | branches: 10 | - main 11 | - develop 12 | 13 | workflow_dispatch: 14 | 15 | jobs: 16 | build_and_test: 17 | name: Test plugin on ${{ matrix.os }} 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | fail-fast: false # show all errors for each platform (vs. cancel jobs on error) 21 | matrix: 22 | include: 23 | - os: ubuntu-22.04 24 | cmake_args: "-DCMAKE_CXX_COMPILER=g++-11" 25 | - os: windows-2022 26 | cmake_gen: -G"Ninja Multi-Config" 27 | cmake_args: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl 28 | - os: macos-13 29 | cmake_args: "-DCMAKE_BUILD_TYPE=Release" 30 | 31 | steps: 32 | - name: Install Linux Deps 33 | if: runner.os == 'Linux' 34 | run: | 35 | sudo apt-get update 36 | sudo apt install libasound2-dev libcurl4-openssl-dev libx11-dev libxinerama-dev libxext-dev libfreetype6-dev libwebkit2gtk-4.0-dev libglu1-mesa-dev libjack-jackd2-dev 37 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 9 38 | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 9 39 | 40 | - name: Set Xcode version 41 | if: runner.os == 'MacOS' 42 | uses: maxim-lobanov/setup-xcode@v1 43 | with: 44 | xcode-version: '15.2' 45 | 46 | - name: Install Ninja 47 | if: runner.os == 'Windows' 48 | uses: seanmiddleditch/gha-setup-ninja@master 49 | 50 | - name: Add msbuild to PATH 51 | if: runner.os == 'Windows' 52 | uses: microsoft/setup-msbuild@v2 53 | 54 | - name: Setup MSVC devcmd 55 | if: runner.os == 'Windows' 56 | uses: ilammy/msvc-dev-cmd@v1 57 | 58 | - name: Get latest CMake 59 | uses: lukka/get-cmake@latest 60 | 61 | - name: Checkout code 62 | uses: actions/checkout@v2 63 | 64 | - name: Fetch submodules # only on Linux, since setup script will do this otherwsie 65 | if: runner.os == 'Linux' 66 | run: git submodule update --init --recursive 67 | 68 | - name: Run setup script 69 | if: runner.os != 'Linux' 70 | run: | 71 | git config --global user.email "jatinchowdhury18@gmail.com" 72 | git config --global user.name "Jatin Chowdhury" 73 | bash setup.sh MyPlugin Tst2 74 | 75 | - name: Configure 76 | shell: bash 77 | run: cmake -Bbuild ${{ matrix.cmake_args }} ${{ matrix.cmake_gen }} 78 | 79 | - name: Build 80 | shell: bash 81 | run: cmake --build build --config Release --parallel 4 82 | 83 | - name: Validate 84 | if: runner.os == 'Windows' 85 | run: bash scripts/validate.sh 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build artifacts 2 | build*/ 3 | bin/ 4 | 5 | # IDE settings 6 | .vscode/ 7 | .idea/ 8 | 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "modules/JUCE"] 2 | path = modules/JUCE 3 | url = https://github.com/juce-framework/JUCE.git 4 | [submodule "modules/chowdsp_utils"] 5 | path = modules/chowdsp_utils 6 | url = https://github.com/Chowdhury-DSP/chowdsp_utils.git 7 | [submodule "modules/clap-juce-extensions"] 8 | path = modules/clap-juce-extensions 9 | url = https://github.com/free-audio/clap-juce-extensions 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment target") 3 | if(WIN32) 4 | set(CMAKE_SYSTEM_VERSION 7.1 CACHE STRING INTERNAL FORCE) # Windows SDK for Windows 7 and up 5 | endif() 6 | project(TempPlugin VERSION 1.0.0) 7 | 8 | set(CMAKE_CXX_STANDARD 20) 9 | set(COMPANY_NAME "chowdsp") 10 | set(PRODUCT_NAME "TempPlugin") 11 | 12 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/modules/cmake") 13 | add_subdirectory(modules) 14 | 15 | # set default plugin formats to build 16 | if(IOS) 17 | set(JUCE_FORMATS Standalone AUv3) 18 | else() 19 | set(JUCE_FORMATS AU VST3 Standalone) 20 | endif() 21 | 22 | juce_add_plugin(TempPlugin 23 | COMPANY_NAME "${COMPANY_NAME}" 24 | PLUGIN_MANUFACTURER_CODE Chow 25 | PLUGIN_CODE XXXX 26 | FORMATS ${JUCE_FORMATS} 27 | PRODUCT_NAME "${PRODUCT_NAME}" 28 | 29 | LV2URI https://github.com/Chowdhury-DSP/TempPlugin 30 | 31 | MICROPHONE_PERMISSION_ENABLED TRUE 32 | ) 33 | 34 | if(NOT IOS) 35 | clap_juce_extensions_plugin( 36 | TARGET TempPlugin 37 | CLAP_ID "org.chowdsp.TempPlugin" 38 | CLAP_FEATURES audio-effect 39 | CLAP_PROCESS_EVENTS_RESOLUTION_SAMPLES 64 40 | CLAP_USE_JUCE_PARAMETER_RANGES DISCRETE 41 | ) 42 | endif() 43 | 44 | add_subdirectory(src) 45 | include_directories(src) 46 | add_subdirectory(res) 47 | 48 | target_compile_definitions(TempPlugin PUBLIC 49 | JUCE_VST3_CAN_REPLACE_VST2=0 50 | ) 51 | 52 | target_link_libraries(TempPlugin PRIVATE juce_plugin_modules) 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, jatinchowdhury18 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JUCE Plugin Template 2 | 3 | ![CI](https://github.com/Chowdhury-DSP/JUCEPluginTemplate/workflows/CI/badge.svg) 4 | [![License](https://img.shields.io/badge/License-BSD-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) 5 | 6 | This repository contains template code for building a Chowdhury DSP 7 | audio plugin. 8 | 9 | ## Building 10 | 11 | To build from scratch, you must have CMake installed. 12 | 13 | ```bash 14 | # Clone the repository 15 | $ git clone https://github.com/Chowdhury-DSP/JUCEPluginTemplate.git 16 | $ cd JUCEPluginTemplate 17 | 18 | # set up plugin 19 | $ ./setup.sh MyPluginName MyPluginID 20 | 21 | # build with CMake 22 | $ cmake -Bbuild 23 | $ cmake --build build --config Release 24 | ``` 25 | 26 | ## License 27 | 28 | JUCEPluginTemplate is open source, and is licensed under the BSD 3-clause license. 29 | Enjoy! 30 | -------------------------------------------------------------------------------- /modules/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(JUCE) 2 | add_subdirectory(chowdsp_utils) 3 | 4 | subproject_version(JUCE juce_version) 5 | message(STATUS "VERSION for JUCE: ${juce_version}") 6 | subproject_version(chowdsp_utils chowdsp_version) 7 | message(STATUS "VERSION for chowdsp: ${chowdsp_version}") 8 | 9 | include(WarningFlags) 10 | add_library(juce_plugin_modules STATIC) 11 | target_link_libraries(juce_plugin_modules 12 | PRIVATE 13 | juce::juce_audio_utils 14 | juce::juce_dsp 15 | chowdsp::chowdsp_plugin_base 16 | chowdsp::chowdsp_plugin_state 17 | chowdsp::chowdsp_gui 18 | PUBLIC 19 | juce::juce_recommended_config_flags 20 | juce::juce_recommended_lto_flags 21 | warning_flags 22 | ) 23 | 24 | if(NOT IOS) 25 | add_subdirectory(clap-juce-extensions EXCLUDE_FROM_ALL) 26 | target_link_libraries(juce_plugin_modules 27 | PRIVATE 28 | chowdsp::chowdsp_clap_extensions 29 | clap_juce_extensions 30 | ) 31 | endif() 32 | 33 | add_diagnostic_info(juce_plugin_modules) 34 | 35 | target_compile_definitions(juce_plugin_modules 36 | PUBLIC 37 | JUCE_DISPLAY_SPLASH_SCREEN=0 38 | JUCE_REPORT_APP_USAGE=0 39 | JUCE_WEB_BROWSER=0 40 | JUCE_USE_CURL=0 41 | JUCE_JACK=1 42 | JUCE_ALSA=1 43 | JucePlugin_Manufacturer="${COMPANY_NAME}" 44 | JucePlugin_VersionString="${CMAKE_PROJECT_VERSION}" 45 | JucePlugin_Name="${PRODUCT_NAME}" 46 | JUCE_MODAL_LOOPS_PERMITTED=0 47 | INTERFACE 48 | $ 49 | ) 50 | 51 | target_include_directories(juce_plugin_modules 52 | INTERFACE 53 | $ 54 | ) 55 | 56 | set_target_properties(juce_plugin_modules PROPERTIES 57 | POSITION_INDEPENDENT_CODE TRUE 58 | VISIBILITY_INLINES_HIDDEN TRUE 59 | C_VISBILITY_PRESET hidden 60 | CXX_VISIBILITY_PRESET hidden 61 | ) 62 | -------------------------------------------------------------------------------- /modules/cmake/CMakeRC.cmake: -------------------------------------------------------------------------------- 1 | # This block is executed when generating an intermediate resource file, not when 2 | # running in CMake configure mode 3 | if(_CMRC_GENERATE_MODE) 4 | # Read in the digits 5 | file(READ "${INPUT_FILE}" bytes HEX) 6 | # Format each pair into a character literal. Heuristics seem to favor doing 7 | # the conversion in groups of five for fastest conversion 8 | string(REGEX REPLACE "(..)(..)(..)(..)(..)" "'\\\\x\\1','\\\\x\\2','\\\\x\\3','\\\\x\\4','\\\\x\\5'," chars "${bytes}") 9 | # Since we did this in groups, we have some leftovers to clean up 10 | string(LENGTH "${bytes}" n_bytes2) 11 | math(EXPR n_bytes "${n_bytes2} / 2") 12 | math(EXPR remainder "${n_bytes} % 5") # <-- '5' is the grouping count from above 13 | set(cleanup_re "$") 14 | set(cleanup_sub ) 15 | while(remainder) 16 | set(cleanup_re "(..)${cleanup_re}") 17 | set(cleanup_sub "'\\\\x\\${remainder}',${cleanup_sub}") 18 | math(EXPR remainder "${remainder} - 1") 19 | endwhile() 20 | if(NOT cleanup_re STREQUAL "$") 21 | string(REGEX REPLACE "${cleanup_re}" "${cleanup_sub}" chars "${chars}") 22 | endif() 23 | string(CONFIGURE [[ 24 | namespace { const char file_array[] = { @chars@ 0 }; } 25 | namespace cmrc { namespace @NAMESPACE@ { namespace res_chars { 26 | extern const char* const @SYMBOL@_begin = file_array; 27 | extern const char* const @SYMBOL@_end = file_array + @n_bytes@; 28 | }}} 29 | ]] code) 30 | file(WRITE "${OUTPUT_FILE}" "${code}") 31 | # Exit from the script. Nothing else needs to be processed 32 | return() 33 | endif() 34 | 35 | set(_version 2.0.0) 36 | 37 | cmake_minimum_required(VERSION 3.5) 38 | include(CMakeParseArguments) 39 | 40 | if(COMMAND cmrc_add_resource_library) 41 | if(NOT DEFINED _CMRC_VERSION OR NOT (_version STREQUAL _CMRC_VERSION)) 42 | message(WARNING "More than one CMakeRC version has been included in this project.") 43 | endif() 44 | # CMakeRC has already been included! Don't do anything 45 | return() 46 | endif() 47 | 48 | set(_CMRC_VERSION "${_version}" CACHE INTERNAL "CMakeRC version. Used for checking for conflicts") 49 | 50 | set(_CMRC_SCRIPT "${CMAKE_CURRENT_LIST_FILE}" CACHE INTERNAL "Path to CMakeRC script") 51 | 52 | function(_cmrc_normalize_path var) 53 | set(path "${${var}}") 54 | file(TO_CMAKE_PATH "${path}" path) 55 | while(path MATCHES "//") 56 | string(REPLACE "//" "/" path "${path}") 57 | endwhile() 58 | string(REGEX REPLACE "/+$" "" path "${path}") 59 | set("${var}" "${path}" PARENT_SCOPE) 60 | endfunction() 61 | 62 | get_filename_component(_inc_dir "${CMAKE_BINARY_DIR}/_cmrc/include" ABSOLUTE) 63 | set(CMRC_INCLUDE_DIR "${_inc_dir}" CACHE INTERNAL "Directory for CMakeRC include files") 64 | # Let's generate the primary include file 65 | file(MAKE_DIRECTORY "${CMRC_INCLUDE_DIR}/cmrc") 66 | set(hpp_content [==[ 67 | #ifndef CMRC_CMRC_HPP_INCLUDED 68 | #define CMRC_CMRC_HPP_INCLUDED 69 | 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | 80 | #if !(defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) || defined(CMRC_NO_EXCEPTIONS)) 81 | #define CMRC_NO_EXCEPTIONS 1 82 | #endif 83 | 84 | namespace cmrc { namespace detail { struct dummy; } } 85 | 86 | #define CMRC_DECLARE(libid) \ 87 | namespace cmrc { namespace detail { \ 88 | struct dummy; \ 89 | static_assert(std::is_same::value, "CMRC_DECLARE() must only appear at the global namespace"); \ 90 | } } \ 91 | namespace cmrc { namespace libid { \ 92 | cmrc::embedded_filesystem get_filesystem(); \ 93 | } } static_assert(true, "") 94 | 95 | namespace cmrc { 96 | 97 | class file { 98 | const char* _begin = nullptr; 99 | const char* _end = nullptr; 100 | 101 | public: 102 | using iterator = const char*; 103 | using const_iterator = iterator; 104 | iterator begin() const noexcept { return _begin; } 105 | iterator cbegin() const noexcept { return _begin; } 106 | iterator end() const noexcept { return _end; } 107 | iterator cend() const noexcept { return _end; } 108 | std::size_t size() const { return static_cast(std::distance(begin(), end())); } 109 | 110 | file() = default; 111 | file(iterator beg, iterator end) noexcept : _begin(beg), _end(end) {} 112 | }; 113 | 114 | class directory_entry; 115 | 116 | namespace detail { 117 | 118 | class directory; 119 | class file_data; 120 | 121 | class file_or_directory { 122 | union _data_t { 123 | class file_data* file_data; 124 | class directory* directory; 125 | } _data; 126 | bool _is_file = true; 127 | 128 | public: 129 | explicit file_or_directory(file_data& f) { 130 | _data.file_data = &f; 131 | } 132 | explicit file_or_directory(directory& d) { 133 | _data.directory = &d; 134 | _is_file = false; 135 | } 136 | bool is_file() const noexcept { 137 | return _is_file; 138 | } 139 | bool is_directory() const noexcept { 140 | return !is_file(); 141 | } 142 | const directory& as_directory() const noexcept { 143 | assert(!is_file()); 144 | return *_data.directory; 145 | } 146 | const file_data& as_file() const noexcept { 147 | assert(is_file()); 148 | return *_data.file_data; 149 | } 150 | }; 151 | 152 | class file_data { 153 | public: 154 | const char* begin_ptr; 155 | const char* end_ptr; 156 | file_data(const file_data&) = delete; 157 | file_data(const char* b, const char* e) : begin_ptr(b), end_ptr(e) {} 158 | }; 159 | 160 | inline std::pair split_path(const std::string& path) { 161 | auto first_sep = path.find("/"); 162 | if (first_sep == path.npos) { 163 | return std::make_pair(path, ""); 164 | } else { 165 | return std::make_pair(path.substr(0, first_sep), path.substr(first_sep + 1)); 166 | } 167 | } 168 | 169 | struct created_subdirectory { 170 | class directory& directory; 171 | class file_or_directory& index_entry; 172 | }; 173 | 174 | class directory { 175 | std::list _files; 176 | std::list _dirs; 177 | std::map _index; 178 | 179 | using base_iterator = std::map::const_iterator; 180 | 181 | public: 182 | 183 | directory() = default; 184 | directory(const directory&) = delete; 185 | 186 | created_subdirectory add_subdir(std::string name) & { 187 | _dirs.emplace_back(); 188 | auto& back = _dirs.back(); 189 | auto& fod = _index.emplace(name, file_or_directory{back}).first->second; 190 | return created_subdirectory{back, fod}; 191 | } 192 | 193 | file_or_directory* add_file(std::string name, const char* begin, const char* end) & { 194 | assert(_index.find(name) == _index.end()); 195 | _files.emplace_back(begin, end); 196 | return &_index.emplace(name, file_or_directory{_files.back()}).first->second; 197 | } 198 | 199 | const file_or_directory* get(const std::string& path) const { 200 | auto pair = split_path(path); 201 | auto child = _index.find(pair.first); 202 | if (child == _index.end()) { 203 | return nullptr; 204 | } 205 | auto& entry = child->second; 206 | if (pair.second.empty()) { 207 | // We're at the end of the path 208 | return &entry; 209 | } 210 | 211 | if (entry.is_file()) { 212 | // We can't traverse into a file. Stop. 213 | return nullptr; 214 | } 215 | // Keep going down 216 | return entry.as_directory().get(pair.second); 217 | } 218 | 219 | class iterator { 220 | base_iterator _base_iter; 221 | base_iterator _end_iter; 222 | public: 223 | using value_type = directory_entry; 224 | using difference_type = std::ptrdiff_t; 225 | using pointer = const value_type*; 226 | using reference = const value_type&; 227 | using iterator_category = std::input_iterator_tag; 228 | 229 | iterator() = default; 230 | explicit iterator(base_iterator iter, base_iterator end) : _base_iter(iter), _end_iter(end) {} 231 | 232 | iterator begin() const noexcept { 233 | return *this; 234 | } 235 | 236 | iterator end() const noexcept { 237 | return iterator(_end_iter, _end_iter); 238 | } 239 | 240 | inline value_type operator*() const noexcept; 241 | 242 | bool operator==(const iterator& rhs) const noexcept { 243 | return _base_iter == rhs._base_iter; 244 | } 245 | 246 | bool operator!=(const iterator& rhs) const noexcept { 247 | return !(*this == rhs); 248 | } 249 | 250 | iterator& operator++() noexcept { 251 | ++_base_iter; 252 | return *this; 253 | } 254 | 255 | iterator operator++(int) noexcept { 256 | auto cp = *this; 257 | ++_base_iter; 258 | return cp; 259 | } 260 | }; 261 | 262 | using const_iterator = iterator; 263 | 264 | iterator begin() const noexcept { 265 | return iterator(_index.begin(), _index.end()); 266 | } 267 | 268 | iterator end() const noexcept { 269 | return iterator(); 270 | } 271 | }; 272 | 273 | inline std::string normalize_path(std::string path) { 274 | while (path.find("/") == 0) { 275 | path.erase(path.begin()); 276 | } 277 | while (!path.empty() && (path.rfind("/") == path.size() - 1)) { 278 | path.pop_back(); 279 | } 280 | auto off = path.npos; 281 | while ((off = path.find("//")) != path.npos) { 282 | path.erase(path.begin() + static_cast(off)); 283 | } 284 | return path; 285 | } 286 | 287 | using index_type = std::map; 288 | 289 | } // detail 290 | 291 | class directory_entry { 292 | std::string _fname; 293 | const detail::file_or_directory* _item; 294 | 295 | public: 296 | directory_entry() = delete; 297 | explicit directory_entry(std::string filename, const detail::file_or_directory& item) 298 | : _fname(filename) 299 | , _item(&item) 300 | {} 301 | 302 | const std::string& filename() const & { 303 | return _fname; 304 | } 305 | std::string filename() const && { 306 | return std::move(_fname); 307 | } 308 | 309 | bool is_file() const { 310 | return _item->is_file(); 311 | } 312 | 313 | bool is_directory() const { 314 | return _item->is_directory(); 315 | } 316 | }; 317 | 318 | directory_entry detail::directory::iterator::operator*() const noexcept { 319 | assert(begin() != end()); 320 | return directory_entry(_base_iter->first, _base_iter->second); 321 | } 322 | 323 | using directory_iterator = detail::directory::iterator; 324 | 325 | class embedded_filesystem { 326 | // Never-null: 327 | const cmrc::detail::index_type* _index; 328 | const detail::file_or_directory* _get(std::string path) const { 329 | path = detail::normalize_path(path); 330 | auto found = _index->find(path); 331 | if (found == _index->end()) { 332 | return nullptr; 333 | } else { 334 | return found->second; 335 | } 336 | } 337 | 338 | public: 339 | explicit embedded_filesystem(const detail::index_type& index) 340 | : _index(&index) 341 | {} 342 | 343 | file open(const std::string& path) const { 344 | auto entry_ptr = _get(path); 345 | if (!entry_ptr || !entry_ptr->is_file()) { 346 | #ifdef CMRC_NO_EXCEPTIONS 347 | fprintf(stderr, "Error no such file or directory: %s\n", path.c_str()); 348 | abort(); 349 | #else 350 | throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path); 351 | #endif 352 | } 353 | auto& dat = entry_ptr->as_file(); 354 | return file{dat.begin_ptr, dat.end_ptr}; 355 | } 356 | 357 | bool is_file(const std::string& path) const noexcept { 358 | auto entry_ptr = _get(path); 359 | return entry_ptr && entry_ptr->is_file(); 360 | } 361 | 362 | bool is_directory(const std::string& path) const noexcept { 363 | auto entry_ptr = _get(path); 364 | return entry_ptr && entry_ptr->is_directory(); 365 | } 366 | 367 | bool exists(const std::string& path) const noexcept { 368 | return !!_get(path); 369 | } 370 | 371 | directory_iterator iterate_directory(const std::string& path) const { 372 | auto entry_ptr = _get(path); 373 | if (!entry_ptr) { 374 | #ifdef CMRC_NO_EXCEPTIONS 375 | fprintf(stderr, "Error no such file or directory: %s\n", path.c_str()); 376 | abort(); 377 | #else 378 | throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path); 379 | #endif 380 | } 381 | if (!entry_ptr->is_directory()) { 382 | #ifdef CMRC_NO_EXCEPTIONS 383 | fprintf(stderr, "Error not a directory: %s\n", path.c_str()); 384 | abort(); 385 | #else 386 | throw std::system_error(make_error_code(std::errc::not_a_directory), path); 387 | #endif 388 | } 389 | return entry_ptr->as_directory().begin(); 390 | } 391 | }; 392 | 393 | } 394 | 395 | #endif // CMRC_CMRC_HPP_INCLUDED 396 | ]==]) 397 | 398 | set(cmrc_hpp "${CMRC_INCLUDE_DIR}/cmrc/cmrc.hpp" CACHE INTERNAL "") 399 | set(_generate 1) 400 | if(EXISTS "${cmrc_hpp}") 401 | file(READ "${cmrc_hpp}" _current) 402 | if(_current STREQUAL hpp_content) 403 | set(_generate 0) 404 | endif() 405 | endif() 406 | file(GENERATE OUTPUT "${cmrc_hpp}" CONTENT "${hpp_content}" CONDITION ${_generate}) 407 | 408 | add_library(cmrc-base INTERFACE) 409 | target_include_directories(cmrc-base INTERFACE $) 410 | # Signal a basic C++11 feature to require C++11. 411 | target_compile_features(cmrc-base INTERFACE cxx_nullptr) 412 | set_property(TARGET cmrc-base PROPERTY INTERFACE_CXX_EXTENSIONS OFF) 413 | add_library(cmrc::base ALIAS cmrc-base) 414 | 415 | function(cmrc_add_resource_library name) 416 | set(args ALIAS NAMESPACE TYPE) 417 | cmake_parse_arguments(ARG "" "${args}" "" "${ARGN}") 418 | # Generate the identifier for the resource library's namespace 419 | set(ns_re "[a-zA-Z_][a-zA-Z0-9_]*") 420 | if(NOT DEFINED ARG_NAMESPACE) 421 | # Check that the library name is also a valid namespace 422 | if(NOT name MATCHES "${ns_re}") 423 | message(SEND_ERROR "Library name is not a valid namespace. Specify the NAMESPACE argument") 424 | endif() 425 | set(ARG_NAMESPACE "${name}") 426 | else() 427 | if(NOT ARG_NAMESPACE MATCHES "${ns_re}") 428 | message(SEND_ERROR "NAMESPACE for ${name} is not a valid C++ namespace identifier (${ARG_NAMESPACE})") 429 | endif() 430 | endif() 431 | set(libname "${name}") 432 | # Check that type is either "STATIC" or "OBJECT", or default to "STATIC" if 433 | # not set 434 | if(NOT DEFINED ARG_TYPE) 435 | set(ARG_TYPE STATIC) 436 | elseif(NOT "${ARG_TYPE}" MATCHES "^(STATIC|OBJECT)$") 437 | message(SEND_ERROR "${ARG_TYPE} is not a valid TYPE (STATIC and OBJECT are acceptable)") 438 | set(ARG_TYPE STATIC) 439 | endif() 440 | # Generate a library with the compiled in character arrays. 441 | string(CONFIGURE [=[ 442 | #include 443 | #include 444 | #include 445 | 446 | namespace cmrc { 447 | namespace @ARG_NAMESPACE@ { 448 | 449 | namespace res_chars { 450 | // These are the files which are available in this resource library 451 | $, 452 | > 453 | } 454 | 455 | namespace { 456 | 457 | const cmrc::detail::index_type& 458 | get_root_index() { 459 | static cmrc::detail::directory root_directory_; 460 | static cmrc::detail::file_or_directory root_directory_fod{root_directory_}; 461 | static cmrc::detail::index_type root_index; 462 | root_index.emplace("", &root_directory_fod); 463 | struct dir_inl { 464 | class cmrc::detail::directory& directory; 465 | }; 466 | dir_inl root_directory_dir{root_directory_}; 467 | (void)root_directory_dir; 468 | $, 469 | > 470 | $, 471 | > 472 | return root_index; 473 | } 474 | 475 | } 476 | 477 | cmrc::embedded_filesystem get_filesystem() { 478 | static auto& index = get_root_index(); 479 | return cmrc::embedded_filesystem{index}; 480 | } 481 | 482 | } // @ARG_NAMESPACE@ 483 | } // cmrc 484 | ]=] cpp_content @ONLY) 485 | get_filename_component(libdir "${CMAKE_CURRENT_BINARY_DIR}/__cmrc_${name}" ABSOLUTE) 486 | get_filename_component(lib_tmp_cpp "${libdir}/lib_.cpp" ABSOLUTE) 487 | string(REPLACE "\n " "\n" cpp_content "${cpp_content}") 488 | file(GENERATE OUTPUT "${lib_tmp_cpp}" CONTENT "${cpp_content}") 489 | get_filename_component(libcpp "${libdir}/lib.cpp" ABSOLUTE) 490 | add_custom_command(OUTPUT "${libcpp}" 491 | DEPENDS "${lib_tmp_cpp}" "${cmrc_hpp}" 492 | COMMAND ${CMAKE_COMMAND} -E copy_if_different "${lib_tmp_cpp}" "${libcpp}" 493 | COMMENT "Generating ${name} resource loader" 494 | ) 495 | # Generate the actual static library. Each source file is just a single file 496 | # with a character array compiled in containing the contents of the 497 | # corresponding resource file. 498 | add_library(${name} ${ARG_TYPE} ${libcpp}) 499 | set_property(TARGET ${name} PROPERTY CMRC_LIBDIR "${libdir}") 500 | set_property(TARGET ${name} PROPERTY CMRC_NAMESPACE "${ARG_NAMESPACE}") 501 | target_link_libraries(${name} PUBLIC cmrc::base) 502 | set_property(TARGET ${name} PROPERTY CMRC_IS_RESOURCE_LIBRARY TRUE) 503 | if(ARG_ALIAS) 504 | add_library("${ARG_ALIAS}" ALIAS ${name}) 505 | endif() 506 | cmrc_add_resources(${name} ${ARG_UNPARSED_ARGUMENTS}) 507 | endfunction() 508 | 509 | function(_cmrc_register_dirs name dirpath) 510 | if(dirpath STREQUAL "") 511 | return() 512 | endif() 513 | # Skip this dir if we have already registered it 514 | get_target_property(registered "${name}" _CMRC_REGISTERED_DIRS) 515 | if(dirpath IN_LIST registered) 516 | return() 517 | endif() 518 | # Register the parent directory first 519 | get_filename_component(parent "${dirpath}" DIRECTORY) 520 | if(NOT parent STREQUAL "") 521 | _cmrc_register_dirs("${name}" "${parent}") 522 | endif() 523 | # Now generate the registration 524 | set_property(TARGET "${name}" APPEND PROPERTY _CMRC_REGISTERED_DIRS "${dirpath}") 525 | _cm_encode_fpath(sym "${dirpath}") 526 | if(parent STREQUAL "") 527 | set(parent_sym root_directory) 528 | else() 529 | _cm_encode_fpath(parent_sym "${parent}") 530 | endif() 531 | get_filename_component(leaf "${dirpath}" NAME) 532 | set_property( 533 | TARGET "${name}" 534 | APPEND PROPERTY CMRC_MAKE_DIRS 535 | "static auto ${sym}_dir = ${parent_sym}_dir.directory.add_subdir(\"${leaf}\")\;" 536 | "root_index.emplace(\"${dirpath}\", &${sym}_dir.index_entry)\;" 537 | ) 538 | endfunction() 539 | 540 | function(cmrc_add_resources name) 541 | get_target_property(is_reslib ${name} CMRC_IS_RESOURCE_LIBRARY) 542 | if(NOT TARGET ${name} OR NOT is_reslib) 543 | message(SEND_ERROR "cmrc_add_resources called on target '${name}' which is not an existing resource library") 544 | return() 545 | endif() 546 | 547 | set(options) 548 | set(args WHENCE PREFIX) 549 | set(list_args) 550 | cmake_parse_arguments(ARG "${options}" "${args}" "${list_args}" "${ARGN}") 551 | 552 | if(NOT ARG_WHENCE) 553 | set(ARG_WHENCE ${CMAKE_CURRENT_SOURCE_DIR}) 554 | endif() 555 | _cmrc_normalize_path(ARG_WHENCE) 556 | get_filename_component(ARG_WHENCE "${ARG_WHENCE}" ABSOLUTE) 557 | 558 | # Generate the identifier for the resource library's namespace 559 | get_target_property(lib_ns "${name}" CMRC_NAMESPACE) 560 | 561 | get_target_property(libdir ${name} CMRC_LIBDIR) 562 | get_target_property(target_dir ${name} SOURCE_DIR) 563 | file(RELATIVE_PATH reldir "${target_dir}" "${CMAKE_CURRENT_SOURCE_DIR}") 564 | if(reldir MATCHES "^\\.\\.") 565 | message(SEND_ERROR "Cannot call cmrc_add_resources in a parent directory from the resource library target") 566 | return() 567 | endif() 568 | 569 | foreach(input IN LISTS ARG_UNPARSED_ARGUMENTS) 570 | _cmrc_normalize_path(input) 571 | get_filename_component(abs_in "${input}" ABSOLUTE) 572 | # Generate a filename based on the input filename that we can put in 573 | # the intermediate directory. 574 | file(RELATIVE_PATH relpath "${ARG_WHENCE}" "${abs_in}") 575 | if(relpath MATCHES "^\\.\\.") 576 | # For now we just error on files that exist outside of the soure dir. 577 | message(SEND_ERROR "Cannot add file '${input}': File must be in a subdirectory of ${ARG_WHENCE}") 578 | continue() 579 | endif() 580 | if(DEFINED ARG_PREFIX) 581 | _cmrc_normalize_path(ARG_PREFIX) 582 | endif() 583 | if(ARG_PREFIX AND NOT ARG_PREFIX MATCHES "/$") 584 | set(ARG_PREFIX "${ARG_PREFIX}/") 585 | endif() 586 | get_filename_component(dirpath "${ARG_PREFIX}${relpath}" DIRECTORY) 587 | _cmrc_register_dirs("${name}" "${dirpath}") 588 | get_filename_component(abs_out "${libdir}/intermediate/${ARG_PREFIX}${relpath}.cpp" ABSOLUTE) 589 | # Generate a symbol name relpath the file's character array 590 | _cm_encode_fpath(sym "${relpath}") 591 | # Get the symbol name for the parent directory 592 | if(dirpath STREQUAL "") 593 | set(parent_sym root_directory) 594 | else() 595 | _cm_encode_fpath(parent_sym "${dirpath}") 596 | endif() 597 | # Generate the rule for the intermediate source file 598 | _cmrc_generate_intermediate_cpp(${lib_ns} ${sym} "${abs_out}" "${abs_in}") 599 | target_sources(${name} PRIVATE "${abs_out}") 600 | set_property(TARGET ${name} APPEND PROPERTY CMRC_EXTERN_DECLS 601 | "// Pointers to ${input}" 602 | "extern const char* const ${sym}_begin\;" 603 | "extern const char* const ${sym}_end\;" 604 | ) 605 | get_filename_component(leaf "${relpath}" NAME) 606 | set_property( 607 | TARGET ${name} 608 | APPEND PROPERTY CMRC_MAKE_FILES 609 | "root_index.emplace(" 610 | " \"${ARG_PREFIX}${relpath}\"," 611 | " ${parent_sym}_dir.directory.add_file(" 612 | " \"${leaf}\"," 613 | " res_chars::${sym}_begin," 614 | " res_chars::${sym}_end" 615 | " )" 616 | ")\;" 617 | ) 618 | endforeach() 619 | endfunction() 620 | 621 | function(_cmrc_generate_intermediate_cpp lib_ns symbol outfile infile) 622 | add_custom_command( 623 | # This is the file we will generate 624 | OUTPUT "${outfile}" 625 | # These are the primary files that affect the output 626 | DEPENDS "${infile}" "${_CMRC_SCRIPT}" 627 | COMMAND 628 | "${CMAKE_COMMAND}" 629 | -D_CMRC_GENERATE_MODE=TRUE 630 | -DNAMESPACE=${lib_ns} 631 | -DSYMBOL=${symbol} 632 | "-DINPUT_FILE=${infile}" 633 | "-DOUTPUT_FILE=${outfile}" 634 | -P "${_CMRC_SCRIPT}" 635 | COMMENT "Generating intermediate file for ${infile}" 636 | ) 637 | endfunction() 638 | 639 | function(_cm_encode_fpath var fpath) 640 | string(MAKE_C_IDENTIFIER "${fpath}" ident) 641 | string(MD5 hash "${fpath}") 642 | string(SUBSTRING "${hash}" 0 4 hash) 643 | set(${var} f_${hash}_${ident} PARENT_SCOPE) 644 | endfunction() 645 | -------------------------------------------------------------------------------- /modules/cmake/SourceFileGroup.cmake: -------------------------------------------------------------------------------- 1 | function(setup_source_group target src_group_name) 2 | set(multiValueArgs SOURCES) 3 | cmake_parse_arguments(ARG "" "" "${multiValueArgs}" ${ARGN}) 4 | 5 | message(STATUS "Setting up source group ${src_group_name}, from SOURCES: ${ARG_SOURCES}") 6 | target_sources(${target} PRIVATE ${ARG_SOURCES}) 7 | set_source_files_properties( 8 | ${ARG_SOURCES} 9 | TARGET_DIRECTORY ${target} 10 | PROPERTIES UNITY_GROUP "${src_group_name}" 11 | ) 12 | endfunction(setup_source_group) 13 | -------------------------------------------------------------------------------- /modules/cmake/WarningFlags.cmake: -------------------------------------------------------------------------------- 1 | add_library(warning_flags INTERFACE) 2 | 3 | if(WIN32) 4 | if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 5 | message(STATUS "Setting ClangCL compiler flags") 6 | target_compile_options(warning_flags INTERFACE 7 | -Wall 8 | -Wno-pessimizing-move 9 | -Wno-missing-field-initializers 10 | -Wno-c++98-compat 11 | -Wno-c++98-compat-pedantic 12 | -Wno-exit-time-destructors 13 | -Wno-old-style-cast 14 | -Wno-undef 15 | -Wno-unused-macros 16 | -Wno-nonportable-system-include-path 17 | -Wno-reserved-macro-identifier 18 | -Wno-documentation 19 | -Wno-documentation-unknown-command 20 | -Wno-double-promotion 21 | -Wno-global-constructors 22 | -Wno-suggest-override 23 | -Wno-suggest-destructor-override 24 | -Wno-non-virtual-dtor 25 | -Wno-extra-semi-stmt 26 | -Wno-cast-qual 27 | -Wno-float-equal 28 | -Wno-covered-switch-default 29 | -Wno-unused-template 30 | -Wno-undefined-func-template 31 | -Wno-missing-variable-declarations 32 | -Wno-ctad-maybe-unsupported 33 | -Wno-missing-noreturn 34 | -Wno-reserved-identifier 35 | -Wno-undefined-reinterpret-cast 36 | -Wno-disabled-macro-expansion 37 | -Wno-deprecated 38 | -Wno-newline-eof 39 | -Wno-date-time 40 | -Wno-cast-function-type 41 | -Wno-range-loop-bind-reference 42 | -Wno-sign-conversion 43 | -Wno-implicit-int-float-conversion 44 | -Wno-implicit-const-int-float-conversion 45 | -Wno-unsafe-buffer-usage 46 | -Wno-unqualified-std-cast-call 47 | -Wno-unknown-warning-option 48 | -Wno-header-hygiene 49 | ) 50 | elseif((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR (CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")) 51 | message(STATUS "Setting MSVC compiler flags") 52 | target_compile_options(warning_flags INTERFACE 53 | /W4 # base warning level 54 | /wd4458 # declaration hides class member (from Foley's GUI Magic) 55 | /wd4505 # since VS2019 doesn't handle [[ maybe_unused ]] for static functions 56 | /wd4244 # for XSIMD 57 | /wd5051 # for [[maybe-unused]] in RTNeural 58 | /wd5054 # for Eigen 59 | /wd4127 # RTNeural is C++14, but C++17+ prefers if constexpr 60 | /wd4324 # structure padded due to alignment specifier 61 | ) 62 | endif() 63 | elseif((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) 64 | target_compile_options(warning_flags INTERFACE 65 | -Wall -Wshadow-all -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized 66 | -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion 67 | -Wconditional-uninitialized -Woverloaded-virtual -Wreorder 68 | -Wconstant-conversion -Wsign-conversion -Wunused-private-field 69 | -Wbool-conversion -Wno-extra-semi -Wunreachable-code 70 | -Wzero-as-null-pointer-constant -Wcast-align 71 | -Wno-inconsistent-missing-destructor-override -Wshift-sign-overflow 72 | -Wnullable-to-nonnull-conversion -Wno-missing-field-initializers 73 | -Wno-ignored-qualifiers -Wpedantic -Wno-pessimizing-move 74 | -Wno-unqualified-std-cast-call -Wno-nullable-to-nonnull-conversion 75 | # These lines suppress some custom warnings. 76 | # Comment them out to be more strict. 77 | -Wno-shadow-field-in-constructor 78 | # Needed for ARM processor, OSX versions below 10.14 79 | -fno-aligned-allocation 80 | ) 81 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 82 | target_compile_options(warning_flags INTERFACE 83 | -Wall -Wextra -Wstrict-aliasing -Wuninitialized -Wunused-parameter 84 | -Wsign-compare -Woverloaded-virtual -Wreorder -Wunreachable-code 85 | -Wzero-as-null-pointer-constant -Wcast-align -Wno-implicit-fallthrough 86 | -Wno-maybe-uninitialized -Wno-missing-field-initializers -Wno-pedantic 87 | -Wno-ignored-qualifiers -Wno-unused-function -Wno-pessimizing-move 88 | # From LV2 Wrapper 89 | -Wno-parentheses -Wno-deprecated-declarations -Wno-redundant-decls 90 | # These lines suppress some custom warnings. 91 | # Comment them out to be more strict. 92 | -Wno-redundant-move 93 | ) 94 | 95 | if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7.0.0") 96 | target_compile_options(warning_flags INTERFACE "-Wno-strict-overflow") 97 | endif() 98 | endif() 99 | -------------------------------------------------------------------------------- /res/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CMakeRC) 2 | -------------------------------------------------------------------------------- /scripts/mac_builds.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # exit on failure 4 | set -e 5 | 6 | # clean up old builds 7 | rm -Rf build/ 8 | rm -Rf bin/*Mac* 9 | 10 | # set up build VST 11 | VST_PATH=~/Developer/Plugin_SDKs/VST2_SDK/ 12 | sed -i '' "s~# juce_set_vst2_sdk_path.*~juce_set_vst2_sdk_path(${VST_PATH})~" CMakeLists.txt 13 | 14 | # cmake new builds 15 | TEAM_ID=$(more ~/Developer/mac_id) 16 | cmake -Bbuild -GXcode -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY="Developer ID Application" \ 17 | -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM="$TEAM_ID" \ 18 | -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE="Manual" \ 19 | -D"CMAKE_OSX_ARCHITECTURES=arm64;x86_64" \ 20 | -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO \ 21 | -DCMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS="--timestamp" \ 22 | -DMACOS_RELEASE=ON 23 | cmake --build build --config Release -j10 | xcpretty 24 | 25 | # copy builds to bin 26 | mkdir -p bin/Mac 27 | declare -a plugins=("TempPlugin") 28 | for plugin in "${plugins[@]}"; do 29 | cp -R build/${plugin}_artefacts/Release/Standalone/${plugin}.app bin/Mac/${plugin}.app 30 | cp -R build/${plugin}_artefacts/Release/VST/${plugin}.vst bin/Mac/${plugin}.vst 31 | cp -R build/${plugin}_artefacts/Release/VST3/${plugin}.vst3 bin/Mac/${plugin}.vst3 32 | cp -R build/${plugin}_artefacts/Release/AU/${plugin}.component bin/Mac/${plugin}.component 33 | done 34 | 35 | # reset CMakeLists.txt 36 | git restore CMakeLists.txt 37 | 38 | # run auval 39 | echo "Running AU validation..." 40 | rm -Rf ~/Library/Audio/Plug-Ins/Components/${plugin}.component 41 | cp -R build/${plugin}_artefacts/Release/AU/${plugin}.component ~/Library/Audio/Plug-Ins/Components 42 | manu=$(cut -f 6 -d ' ' <<< "$(grep 'PLUGIN_MANUFACTURER_CODE' CMakeLists.txt)") 43 | code=$(cut -f 6 -d ' ' <<< "$(grep 'PLUGIN_CODE' CMakeLists.txt)") 44 | 45 | set +e 46 | auval_result=$(auval -v aufx "$code" "$manu") 47 | auval_code="$?" 48 | echo "AUVAL code: $auval_code" 49 | 50 | if [ "$auval_code" != 0 ]; then 51 | echo "$auval_result" 52 | echo "auval FAIL!!!" 53 | exit 1 54 | else 55 | echo "auval PASSED" 56 | fi 57 | 58 | # zip builds 59 | echo "Zipping builds..." 60 | VERSION=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' build/CMakeCache.txt)") 61 | ( 62 | cd bin 63 | rm -f "TempPlugin-Mac-${VERSION}.zip" 64 | zip -r "TempPlugin-Mac-${VERSION}.zip" Mac 65 | ) 66 | 67 | # create installer 68 | echo "Creating installer..." 69 | ( 70 | cd installers/mac 71 | bash build_mac_installer.sh 72 | ) 73 | -------------------------------------------------------------------------------- /scripts/validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | version="v0.3.0" 6 | 7 | # install functions 8 | install_pluginval_linux() 9 | { 10 | curl -L "https://github.com/Tracktion/pluginval/releases/download/${version}/pluginval_Linux.zip" -o pluginval.zip 11 | unzip pluginval > /dev/null 12 | echo "./pluginval" 13 | } 14 | 15 | install_pluginval_mac() 16 | { 17 | curl -L "https://github.com/Tracktion/pluginval/releases/download/${version}/pluginval_macOS.zip" -o pluginval.zip 18 | unzip pluginval > /dev/null 19 | echo "pluginval.app/Contents/MacOS/pluginval" 20 | } 21 | 22 | install_pluginval_win() 23 | { 24 | powershell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest https://github.com/Tracktion/pluginval/releases/download/${version}/pluginval_Windows.zip -OutFile pluginval.zip" 25 | powershell -Command "Expand-Archive pluginval.zip -DestinationPath ." 26 | echo "./pluginval.exe" 27 | } 28 | 29 | # install 30 | if [[ "$OSTYPE" == "linux-gnu"* ]]; then 31 | exit 0 32 | # pluginval=$(install_pluginval_linux) 33 | # declare -a plugins=() 34 | elif [[ "$OSTYPE" == "darwin"* ]]; then 35 | pluginval=$(install_pluginval_mac) 36 | declare -a plugins=("build/TempPlugin_artefacts/VST3/TempPlugin.vst3") 37 | else 38 | pluginval=$(install_pluginval_win) 39 | declare -a plugins=("build/TempPlugin_artefacts/Release/VST3/TempPlugin.vst3") 40 | fi 41 | 42 | echo "Pluginval installed at ${pluginval}" 43 | 44 | # run 45 | for plugin in "${plugins[@]}"; do 46 | echo "Validating ${plugin}" 47 | $pluginval --strictness-level 8 --validate-in-process --validate $plugin 48 | done 49 | 50 | # clean up 51 | rm -Rf pluginval* 52 | -------------------------------------------------------------------------------- /scripts/win_builds.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | build64(){ 4 | cmake -Bbuild -G"Visual Studio 16 2019" -A x64 5 | cmake --build build --config Release -j4 6 | } 7 | 8 | build32(){ 9 | cmake -Bbuild32 -G"Visual Studio 16 2019" -A Win32 10 | cmake --build build32 --config Release -j4 11 | } 12 | 13 | # exit on failure 14 | set -e 15 | 16 | # clean up old builds 17 | rm -Rf build/ 18 | rm -Rf build32/ 19 | rm -Rf bin/*Win64* 20 | rm -Rf bin/*Win32* 21 | 22 | # set up VST SDK path 23 | VST_SDK="C:/SDKs/VST_SDK/VST2_SDK/" 24 | sed -i -e "s~# juce_set_vst2_sdk_path.*~juce_set_vst2_sdk_path(${VST_SDK})~" CMakeLists.txt 25 | 26 | # cmake new builds 27 | build64 & 28 | # build32 & 29 | wait 30 | 31 | # copy builds to bin 32 | mkdir -p bin/Win64 33 | mkdir -p bin/Win32 34 | declare -a plugins=("TempPlugin") 35 | for plugin in "${plugins[@]}"; do 36 | cp -R build/${plugin}_artefacts/Release/Standalone/${plugin}.exe bin/Win64/${plugin}.exe 37 | cp -R build/${plugin}_artefacts/Release/VST/${plugin}.dll bin/Win64/${plugin}.dll 38 | cp -R build/${plugin}_artefacts/Release/VST3/${plugin}.vst3 bin/Win64/${plugin}.vst3 39 | 40 | # cp -R build32/${plugin}_artefacts/Release/Standalone/${plugin}.exe bin/Win32/${plugin}.exe 41 | # cp -R build32/${plugin}_artefacts/Release/VST/${plugin}.dll bin/Win32/${plugin}.dll 42 | # cp -R build32/${plugin}_artefacts/Release/VST3/${plugin}.vst3 bin/Win32/${plugin}.vst3 43 | done 44 | 45 | # reset CMakeLists.txt 46 | git restore CMakeLists.txt 47 | 48 | # zip builds 49 | VERSION=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' build/CMakeCache.txt)") 50 | ( 51 | cd bin 52 | rm -f "TempPlugin-Win64-${VERSION}.zip" 53 | zip -r "TempPlugin-Win64-${VERSION}.zip" Win64 54 | # rm -f "TempPlugin-Win32-${VERSION}.zip" 55 | # zip -r "TempPlugin-Win32-${VERSION}.zip" Win32 56 | ) 57 | 58 | # create installer 59 | echo "Creating installer..." 60 | ( 61 | cd installers/windows 62 | bash build_win_installer.sh 63 | ) 64 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ]; then 4 | echo "No plugin name provided!" 5 | exit 1 6 | fi 7 | 8 | if [ -z "$2" ]; then 9 | echo "No plugin ID provided!" 10 | exit 1 11 | fi 12 | 13 | plugin_name=$1 14 | plugin_id=$2 15 | echo "Creating plugin ${plugin_name}, with ID ${plugin_id} ..." 16 | 17 | echo "Copying source files..." 18 | mv src/TempPlugin.h src/${plugin_name}.h 19 | mv src/TempPlugin.cpp src/${plugin_name}.cpp 20 | 21 | echo "Copying installer files..." 22 | mv installers/mac/TempPlugin.pkgproj installers/mac/${plugin_name}.pkgproj 23 | mv installers/windows/TempPlugin_Install_Script.iss installers/windows/${plugin_name}_Install_Script.iss 24 | 25 | echo "Setting plugin ID..." 26 | sed -i.bak -e "s/XXXX/${plugin_id}/g" CMakeLists.txt 27 | 28 | echo "Setting up source files..." 29 | declare -a source_files=("scripts/validate.sh" 30 | "scripts/win_builds.sh" 31 | "scripts/mac_builds.sh" 32 | "CMakeLists.txt" 33 | "src/CMakeLists.txt" 34 | "src/${plugin_name}.h" 35 | "src/${plugin_name}.cpp" 36 | ) 37 | for file in "${source_files[@]}"; do 38 | sed -i.bak -e "s/TempPlugin/${plugin_name}/g" $file 39 | done 40 | 41 | sed -i.bak -e "s/JUCEPluginTemplate/${plugin_name}/g" README.md 42 | sed -i.bak -e "s/JUCE Plugin Template/${plugin_name}/g" README.md 43 | 44 | # Remove `run setup.sh` lines from CI and README 45 | sed -i.bak -e '42,48d' .github/workflows/cmake.yml 46 | sed -i.bak -e '/setup.sh/{N;d;}' README.md 47 | sed -i.bak -e '/set up plugin/d' README.md 48 | 49 | # Clean up files we no longer need 50 | rm *.bak 51 | rm */*.bak 52 | rm .github/*.bak 53 | rm .github/*/*.bak 54 | rm installers/*/*.bak 55 | rm setup.sh 56 | 57 | echo "Fetching submodules..." 58 | git submodule update --init --recursive 59 | 60 | # update chowdsp_utils 61 | ( 62 | echo "Updating submodule: chowdsp_utils..." 63 | cd modules/chowdsp_utils 64 | git fetch origin 65 | git checkout master 66 | git pull 67 | git log -n 1 68 | ) 69 | 70 | # update clap-juce-extensions 71 | ( 72 | echo "Updating submodule: clap-juce-extensions..." 73 | cd modules/clap-juce-extensions 74 | git fetch origin 75 | git checkout main 76 | git submodule update --init --recursive 77 | git pull 78 | git log -n 1 79 | ) 80 | 81 | # Stop tracking from template repo 82 | git remote remove origin 83 | 84 | # Remove old git commit history 85 | clean_git_history(){ 86 | git add . 87 | git commit -m "Set up ${plugin_name}" 88 | git checkout --orphan new-branch 89 | git add -A 90 | git commit -m "Initial commit" 91 | git branch -D main 92 | git branch -m main 93 | } 94 | 95 | clean_git_history > /dev/null 96 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(SourceFileGroup) 2 | 3 | setup_source_group(TempPlugin PLUGIN_SRCS SOURCES 4 | TempPlugin.cpp 5 | ) 6 | 7 | file(GLOB_RECURSE juce_module_sources CONFIGURE_DEPENDS 8 | ${CMAKE_CURRENT_SOURCE_DIR}/../modules/JUCE/modules/juce_*/*.cpp 9 | ${CMAKE_CURRENT_SOURCE_DIR}/../modules/JUCE/modules/juce_*/*.mm 10 | ${CMAKE_CURRENT_SOURCE_DIR}/../modules/chowdsp_utils/modules/*/chowdsp_*/*.cpp 11 | ) 12 | set_source_files_properties(${juce_module_sources} 13 | TARGET_DIRECTORY TempPlugin 14 | PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE SKIP_UNITY_BUILD_INCLUSION TRUE 15 | ) 16 | set_target_properties(TempPlugin PROPERTIES 17 | UNITY_BUILD ON 18 | UNITY_BUILD_MODE GROUP 19 | UNITY_BUILD_BATCH_SIZE 8 20 | ) 21 | target_precompile_headers(TempPlugin PRIVATE pch.h) 22 | -------------------------------------------------------------------------------- /src/TempPlugin.cpp: -------------------------------------------------------------------------------- 1 | #include "TempPlugin.h" 2 | 3 | TempPlugin::TempPlugin() = default; 4 | 5 | void TempPlugin::prepareToPlay ([[maybe_unused]] double sample_rate, [[maybe_unused]] int samples_per_block) 6 | { 7 | } 8 | 9 | void TempPlugin::processAudioBlock ([[maybe_unused]] juce::AudioBuffer& buffer) 10 | { 11 | } 12 | 13 | juce::AudioProcessorEditor* TempPlugin::createEditor() 14 | { 15 | return new chowdsp::ParametersViewEditor { *this, state, state.params }; 16 | } 17 | 18 | // This creates new instances of the plugin 19 | juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() 20 | { 21 | return new TempPlugin(); 22 | } 23 | -------------------------------------------------------------------------------- /src/TempPlugin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Params : chowdsp::ParamHolder 6 | { 7 | }; 8 | 9 | using State = chowdsp::PluginStateImpl; 10 | 11 | class TempPlugin : public chowdsp::PluginBase 12 | { 13 | public: 14 | TempPlugin(); 15 | 16 | void prepareToPlay (double sample_rate, int samples_per_block) override; 17 | void releaseResources() override {} 18 | void processAudioBlock (juce::AudioBuffer& buffer) override; 19 | 20 | juce::AudioProcessorEditor* createEditor() override; 21 | 22 | private: 23 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TempPlugin) 24 | }; 25 | -------------------------------------------------------------------------------- /src/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Pre-compiled headers for JUCE plugins 5 | */ 6 | 7 | // C++/STL headers here... 8 | 9 | // JUCE modules 10 | #include 11 | #include 12 | 13 | // Any other widely used headers that don't change... 14 | --------------------------------------------------------------------------------