├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── IDEAS.md ├── LICENSE ├── Makefile ├── README.md ├── patches └── dpf │ ├── fix-lv2-version-export.patch │ ├── lv2-port-groups.patch │ └── no-port-name-lv2-prefix.patch ├── plugins.md ├── plugins ├── MIDICCMapX4 │ ├── DistrhoPluginInfo.h │ ├── Makefile │ ├── PluginMIDICCMapX4.cpp │ └── PluginMIDICCMapX4.hpp ├── MIDICCRecorder │ ├── DistrhoPluginInfo.h │ ├── Makefile │ ├── PluginMIDICCRecorder.cpp │ └── PluginMIDICCRecorder.hpp ├── MIDICCToPressure │ ├── DistrhoPluginInfo.h │ ├── Makefile │ ├── PluginMIDICCToPressure.cpp │ └── PluginMIDICCToPressure.hpp ├── MIDIPBToCC │ ├── DistrhoPluginInfo.h │ ├── Makefile │ ├── PluginMIDIPBToCC.cpp │ └── PluginMIDIPBToCC.hpp ├── MIDIPressureToCC │ ├── DistrhoPluginInfo.h │ ├── Makefile │ ├── PluginMIDIPressureToCC.cpp │ └── PluginMIDIPressureToCC.hpp └── MIDISysFilter │ ├── DistrhoPluginInfo.h │ ├── Makefile │ ├── PluginMIDISysFilter.cpp │ └── PluginMIDISysFilter.hpp ├── screenshots ├── MIDICCMapX4.png ├── MIDICCRecorder.png ├── MIDICCToPressure.png ├── MIDIPBToCC.png ├── MIDIPressureToCC.png ├── MIDISysFilter.png ├── allplugins-carla-800.png └── allplugins-carla.png └── scripts ├── build-win32.sh ├── build-win64.sh └── create-git-archive.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.d 3 | *.o 4 | 5 | *.exe 6 | *.dll 7 | *.dylib 8 | *.so 9 | *.zip 10 | 11 | .kdev_include_paths 12 | .kdev4/ 13 | .DS_Store 14 | 15 | bin/midi* 16 | bin/midi*.exe 17 | bin/midi*-ladspa.* 18 | bin/midi*-dssi.* 19 | bin/midi*-vst.* 20 | bin/midi*.lv2/ 21 | 22 | # Geany editor project files 23 | *.geany 24 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dpf"] 2 | path = dpf 3 | url = https://github.com/DISTRHO/DPF.git 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ChangeLog 2 | ========= 3 | 4 | 0.2.1 (2022-03-19) 5 | ------------------ 6 | 7 | Fixes: 8 | 9 | * `MIDICCRecorder`: fixed default value of `send_interval` parameter. 10 | * `MIDIPBToCC`: fixed default value of `pb_max` parameter. 11 | 12 | Changes: 13 | 14 | * Made Makefile less repetitive and fixed `submodules` target. 15 | * Updated `dpf` submodule to commit ab3e4c36 on (old) `master` branch. 16 | 17 | 18 | 0.2.0 (2021-01-15) 19 | ------------------ 20 | 21 | Features: 22 | 23 | * Added `MIDIPresuureToCC` plugin to turn aftertouch into CC (contributed by Jorik Jinker). 24 | * Added `MIDICCToPressure` plugin to turn a CC into aftertouch. 25 | * Added `MIDICCMAPX4` plugin to turn a CC into up to four others. 26 | * `MIDICCRecorder`: added support for activating send trigger by Program Change MIDI message. 27 | * `MIDICCRecorder`: added support for activating send trigger by transport start. 28 | * `MIDIPBToCC`: added 'Modulation +' preset. 29 | 30 | Fixes: 31 | 32 | * `MIDIPBToCC`: fixed sign of default `pb_max` parameter value. 33 | * Several small readme corrections. 34 | 35 | Changes: 36 | 37 | * `MIDICCRecorder`: tweaked some parameter labels and LV2 symbols. 38 | * `MIDICCRecorder` is considered *beta* quality. 39 | * Moved plugin descriptions and screenshots from readme into separate document. 40 | * Updated plugin descriptions, screenshots and authors section in readme. 41 | * Updated dpf git submodule to commit 27d3046. 42 | 43 | 44 | 0.1.1-beta (2020-08-21) 45 | ----------------------- 46 | 47 | Fixes: 48 | 49 | * `MIDIPBToCC`: fixed a parameter label. 50 | * `MIDICCRecorder`: used proper value range for `sendChannel`. 51 | * `MIDIPBToCC`: fixed `filter_channel` param internal state. 52 | 53 | Changes: 54 | 55 | * Switched dpf git submodule to develop branch and updated it. 56 | 57 | 58 | 0.1.0-beta2 (2019-12-15) 59 | ------------------------ 60 | 61 | Fixes: 62 | 63 | * Fixed `install` make target. 64 | 65 | 66 | 0.1.0-beta1 (2019-10-15) 67 | ------------------------ 68 | 69 | First beta status release. 70 | 71 | * `MIDISysFilter` is considered *stable*. 72 | * `MIDIPBToCC` is considered *stable*. 73 | * `MIDICCRecorder` is considered *experimental*. 74 | 75 | 76 | 0.1.0-alpha (2019-08-13) 77 | ------------------------ 78 | 79 | First alpha release 80 | -------------------------------------------------------------------------------- /IDEAS.md: -------------------------------------------------------------------------------- 1 | # Ideas for new plug-ins and features 2 | 3 | 4 | ## New plugins 5 | 6 | ### Note to Program Change / Bank Select 7 | 8 | Sends Bank Select LSB and/or MSB (optional) and Program Change triggered 9 | by a received Note On or Note Off in a specified range. 10 | 11 | * Input Channel (Any, 1..16) 12 | * Keep other events 13 | * Source Note Min 14 | * Source Note Max 15 | * Destination: 16 | * Channel (Source, 1..16) 17 | * Program Change (None, 0..127) 18 | * Bank MSB Select (None, 0..127) 19 | * Bank LSB Select (None, 0..127) 20 | 21 | 22 | ### Program Change Trigger x 8 23 | 24 | Send up to 8 Bank Select MSB/LSB and PC messages triggered by a 25 | received PC message, via trigger input or transport start. 26 | 27 | Params: 28 | 29 | * Input Channel (Any, 1..16) 30 | * Keep other events 31 | * Source PC 32 | * Send Trigger 33 | * PC #1 34 | * Channel (Source, 1..16) 35 | * Program Change (None, 0..127) 36 | * Bank MSB Select (None, 0..127) 37 | * Bank LSB Select (None, 0..127) 38 | * PC #2 39 | * ... 40 | 41 | ### Program Change to CC x 8 42 | 43 | Convert PC messages into up to 8 CCs 44 | 45 | Params: 46 | 47 | * Input Channel (Any, 1..16) 48 | * Keep other events 49 | * Source PC 50 | * Destination CC #1 51 | * CC (None, 0..127) 52 | * Channel (1..16) 53 | * Value (0..127) 54 | * Destination CC #2 55 | * ... 56 | 57 | ### CC To Program Change x 8 58 | 59 | Convert a CC message into up to 8 PCs 60 | 61 | Params: 62 | 63 | * Input Channel (Any, 1..16) 64 | * Keep other events 65 | * Source CC 66 | * Destination PC A 67 | * PC A "on" (Disabled, 0..127) 68 | * Channel (1..16) 69 | * PC A "off" (Disabled, 0..127) 70 | * Channel (1..16) 71 | * On/Off value threshold (0..127) 72 | * Destination CC #2 73 | * ... 74 | 75 | 76 | ### (N)RPN to CC 77 | 78 | Converts (N)RPN data into CC messages 79 | 80 | 81 | ### CC To Pressure 82 | 83 | As the reverse to MIDI Pressure To CC 84 | 85 | Done. 86 | 87 | 88 | ### MIDI Polyphony 89 | 90 | Route notes to different MIDI channels depending on "voice". 91 | 92 | 93 | ### PitchBend Enforce Scale 94 | 95 | Scale PitchBend dynamically according to specified musical scale and last 96 | played note. 97 | 98 | Would only work properly with monophonic material or MPE. 99 | 100 | 101 | ## New Features 102 | 103 | ### MIDI CC Recorder 104 | 105 | * Trigger via PC - done 106 | * Trigger via Transport - done 107 | 108 | 109 | ## Changes 110 | 111 | ### Midi CC Map X4 112 | 113 | * Rename CC 1..4 to CC A/B/C/D - no - would lead to short labels like CCA,CCB,CCC,CCD 114 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIDI-o-matic plugin collection based on DISTRHO Plugin Framework (DPF) 2 | 3 | SPDX-License-Identifier: MIT 4 | 5 | Copyright (C) 2019 - 2022 Christopher Arndt 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to 9 | deal in the Software without restriction, including without limitation the 10 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Makefile for DISTRHO Plugins # 3 | # ---------------------------- # 4 | # Created by falkTX, Christopher Arndt, and Patrick Desaulniers 5 | # 6 | 7 | SHELL=/bin/bash 8 | 9 | -include dpf/Makefile.base.mk 10 | 11 | all: libs plugins gen 12 | 13 | # -------------------------------------------------------------- 14 | 15 | PLUGINS = \ 16 | MIDICCMapX4 \ 17 | MIDICCRecorder \ 18 | MIDICCToPressure \ 19 | MIDIPBToCC \ 20 | MIDIPressureToCC \ 21 | MIDISysFilter 22 | 23 | DPF_PATCHES = \ 24 | dpf/lv2-port-groups.patch \ 25 | dpf/fix-lv2-version-export.patch \ 26 | dpf/no-port-name-lv2-prefix.patch 27 | 28 | PLUGIN_BASE_URI = https://chrisarndt.de/plugins/ 29 | 30 | submodules: 31 | -test -d .git && git submodule update --init --recursive 32 | 33 | libs: submodules patch 34 | 35 | patch: submodules 36 | @-for p in $(DPF_PATCHES); do \ 37 | echo "Applying patch '$${p}'..."; \ 38 | patch -d dpf -r - -p1 -N -i ../patches/$${p}; \ 39 | done 40 | 41 | plugins: $(PLUGINS) 42 | 43 | $(PLUGINS): 44 | $(MAKE) all -C plugins/$@ 45 | 46 | ifneq ($(CROSS_COMPILING),true) 47 | gen: plugins dpf/utils/lv2_ttl_generator 48 | @$(CURDIR)/dpf/utils/generate-ttl.sh 49 | ifeq ($(MACOS),true) 50 | @$(CURDIR)/dpf/utils/generate-vst-bundles.sh 51 | endif 52 | dpf/utils/lv2_ttl_generator: 53 | $(MAKE) -C dpf/utils/lv2-ttl-generator 54 | else 55 | gen: plugins dpf/utils/lv2_ttl_generator.exe 56 | @$(CURDIR)/dpf/utils/generate-ttl.sh 57 | 58 | dpf/utils/lv2_ttl_generator.exe: 59 | $(MAKE) -C dpf/utils/lv2-ttl-generator WINDOWS=true 60 | endif 61 | 62 | # -------------------------------------------------------------- 63 | 64 | check: plugins 65 | @for plug in $(PLUGINS); do \ 66 | lv2lint -Mpack -q -s lv2_generate_ttl -t "Plugin Author Email" \ 67 | -I bin/$${plug,,}.lv2/ "$(PLUGIN_BASE_URI)$${plug,,}"; \ 68 | done 69 | 70 | # -------------------------------------------------------------- 71 | 72 | clean: 73 | $(MAKE) clean -C dpf/utils/lv2-ttl-generator 74 | @for plug in $(PLUGINS); do \ 75 | $(MAKE) clean -C plugins/$${plug}; \ 76 | done 77 | rm -rf bin build 78 | 79 | install: all 80 | @for plug in $(PLUGINS); do \ 81 | $(MAKE) install -C plugins/$${plug}; \ 82 | done 83 | 84 | install-user: all 85 | @for plug in $(PLUGINS); do \ 86 | $(MAKE) install-user -C plugins/$${plug}; \ 87 | done 88 | 89 | # -------------------------------------------------------------- 90 | 91 | .PHONY: all clean check gen install install-user libs patch plugins submodules 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MIDI-o-matic 2 | 3 | A collection of MIDI filter, generator and processor plugins 4 | 5 | [![Latest version](https://shields.io/github/v/release/SpotlightKid/midiomatic)](https://github.com/SpotlightKid/midiomatic/releases) 6 | ![Date of latest release](https://shields.io/github/release-date/SpotlightKid/midiomatic) 7 | [![MIT license](https://shields.io/aur/license/midiomatic)](./LICENSE) 8 | 9 | 10 | ## Plugins 11 | 12 | [![All midiomatic plugins loade in Carla](./screenshots/allplugins-carla.png)](./plugins.md) 13 | 14 | [MIDI CC Map X4](./plugins.md#midi-cc-map-x4) - Map a single input CC to up to 15 | four output CCs. 16 | 17 | [MIDI CC Recorder](./plugins.md#midi-cc-recorder) - Store received Control 18 | Change messages and replay them when triggered. 19 | 20 | [MIDI CC to Pressure](./plugins.md#midi-cc-to-pressure) - Convert Control 21 | Change messages into (monophonic) Channel Pressure (Aftertouch). 22 | 23 | [MIDI PB to CC](./plugins.md#midi-pb-to-cc) - Convert Pitch Bend into Control 24 | Change messages. 25 | 26 | [MIDI Pressure to CC](./plugins.md#midi-pressure-to-cc) - Convert (monophonic) 27 | Channel Pressure (Aftertouch) into Control Change messages. 28 | 29 | [MIDI Sys Filter](./plugins.md#midi-sys-filter) - Filter out MIDI System 30 | Messages. 31 | 32 | 33 | ## Plugin Formats 34 | 35 | The plugins are available in the following formats: 36 | 37 | * [LV2] 38 | * [VST2] 39 | 40 | 41 | ## Compiling 42 | 43 | Make sure, you have installed the required build tools and libraries (see 44 | section "Prerequisites" below) and then clone this repository (including 45 | sub-modules) and simply run `make` in the project's root directory: 46 | 47 | $ git clone --recursive https://github.com/SpotlightKid/midiomatic.git 48 | $ cd midiomatic 49 | $ make 50 | 51 | 52 | ## Installation 53 | 54 | To install the plugins system-wide, run (root priviledges may be required): 55 | 56 | make install 57 | 58 | The usual `PREFIX` and `DESTDIR` makefile variables are honoured to change 59 | the prefix directory (default: `/usr/local`) and the installation destination 60 | (for distribution packaging). 61 | 62 | You can also set the installation directory for each plugin format with a 63 | dedicated makefile variable. 64 | 65 | * LV2: `LV2_DIR` (`/lib/lv2`) 66 | * VST2: `VST_DIR` (`/lib/vst`) 67 | 68 | Example: 69 | 70 | make DESTDIR=/tmp/build-root PREFIX=/usr VST_DIR=/usr/lib/lxvst install 71 | 72 | Use make's `-n` option to see where the plugins would be installed without 73 | actually installing them. 74 | 75 | To install the plugins under your user's home directory, run: 76 | 77 | make install-user 78 | 79 | No special makefile variables are used in this case. 80 | 81 | 82 | ## Prerequisites 83 | 84 | * Git 85 | 86 | * The GCC C++ compiler and the usual associated software build tools 87 | (`make`, etc.). 88 | 89 | Users of Debian / Ubuntu Linux should install the `build-essential` 90 | meta package to get these, Arch Linux users the `base-devel` package 91 | group. 92 | 93 | * [pkgconf] 94 | 95 | Optional (only needed for checking plugins after building them): 96 | 97 | * [lv2lint] 98 | * [KXStudio LV2 Extensions] 99 | 100 | 101 | ## License 102 | 103 | This software is distributed under the MIT License. 104 | 105 | See the file [LICENSE](./LICENSE) for more information. 106 | 107 | 108 | ## Authors 109 | 110 | This software is written by *Christopher Arndt*. 111 | 112 | Contributions by: 113 | 114 | * *Jorik Jonker* (MIDI Pressure To CC plugin) 115 | 116 | 117 | ## Acknowledgements 118 | 119 | Build using the DISTRHO Plugin Framework ([DPF]) and set up with the 120 | [cookiecutter-dpf-effect] project template. 121 | 122 | 123 | [cookiecutter-dpf-effect]: https://github.com/SpotlightKid/cookiecutter-dpf-effect 124 | [dpf]: https://github.com/DISTRHO/DPF 125 | [kxstudio lv2 extensions]: https://github.com/KXStudio/LV2-Extensions 126 | [lv2]: http://lv2plug.in/ 127 | [lv2lint]: https://open-music-kontrollers.ch/lv2/lv2lint/ 128 | [pkgconf]: https://github.com/pkgconf/pkgconf 129 | [vst2]: https://en.wikipedia.org/wiki/Virtual_Studio_Technology 130 | -------------------------------------------------------------------------------- /patches/dpf/fix-lv2-version-export.patch: -------------------------------------------------------------------------------- 1 | diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp 2 | index c074109..1bce6b0 100644 3 | --- a/distrho/src/DistrhoPluginLV2export.cpp 4 | +++ b/distrho/src/DistrhoPluginLV2export.cpp 5 | @@ -746,16 +746,11 @@ void lv2_generate_ttl(const char* const basename) 6 | { 7 | const uint32_t version(plugin.getVersion()); 8 | 9 | - const uint32_t majorVersion = (version & 0xFF0000) >> 16; 10 | - const uint32_t microVersion = (version & 0x00FF00) >> 8; 11 | - /* */ uint32_t minorVersion = (version & 0x0000FF) >> 0; 12 | + const uint32_t minorVersion = (version & 0x00FF00) >> 8; 13 | + const uint32_t microVersion = (version & 0x0000FF) >> 0; 14 | 15 | - // NOTE: LV2 ignores 'major' version and says 0 for minor is pre-release/unstable. 16 | - if (majorVersion > 0) 17 | - minorVersion += 2; 18 | - 19 | - pluginString += " lv2:microVersion " + String(microVersion) + " ;\n"; 20 | - pluginString += " lv2:minorVersion " + String(minorVersion) + " .\n"; 21 | + pluginString += " lv2:minorVersion " + String(minorVersion) + " ;\n"; 22 | + pluginString += " lv2:microVersion " + String(microVersion) + " .\n"; 23 | } 24 | 25 | pluginFile << pluginString << std::endl; 26 | -------------------------------------------------------------------------------- /patches/dpf/lv2-port-groups.patch: -------------------------------------------------------------------------------- 1 | diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp 2 | index 9e55c87..345e829 100644 3 | --- a/distrho/DistrhoPlugin.hpp 4 | +++ b/distrho/DistrhoPlugin.hpp 5 | @@ -100,6 +100,10 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean; 6 | 7 | /** @} */ 8 | 9 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 10 | +static const int32_t kPortGroupDefault = -1; 11 | +#endif 12 | + 13 | /* ------------------------------------------------------------------------------------------------------------ 14 | * Base Plugin structs */ 15 | 16 | @@ -136,13 +140,21 @@ struct AudioPort { 17 | */ 18 | String symbol; 19 | 20 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 21 | + int32_t group; 22 | +#endif 23 | + 24 | /** 25 | Default constructor for a regular audio port. 26 | */ 27 | AudioPort() noexcept 28 | : hints(0x0), 29 | name(), 30 | - symbol() {} 31 | + symbol() 32 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 33 | + , group(kPortGroupDefault) 34 | +#endif 35 | + {} 36 | }; 37 | 38 | /** 39 | @@ -404,6 +416,10 @@ struct Parameter { 40 | */ 41 | String symbol; 42 | 43 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 44 | + uint32_t group; 45 | +#endif 46 | + 47 | /** 48 | The unit of this parameter.@n 49 | This means something like "dB", "kHz" and "ms".@n 50 | @@ -450,6 +466,9 @@ struct Parameter { 51 | name(), 52 | shortName(), 53 | symbol(), 54 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 55 | + group(kPortGroupDefault), 56 | +#endif 57 | unit(), 58 | ranges(), 59 | enumValues(), 60 | @@ -464,6 +483,9 @@ struct Parameter { 61 | name(n), 62 | shortName(), 63 | symbol(s), 64 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 65 | + group(kPortGroupDefault), 66 | +#endif 67 | unit(u), 68 | ranges(def, min, max), 69 | enumValues(), 70 | @@ -486,6 +508,9 @@ struct Parameter { 71 | name = "Bypass"; 72 | shortName = "Bypass"; 73 | symbol = "dpf_bypass"; 74 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 75 | + group = kPortGroupDefault; 76 | +#endif 77 | unit = ""; 78 | midiCC = 0; 79 | ranges.def = 0.0f; 80 | @@ -496,6 +521,16 @@ struct Parameter { 81 | } 82 | }; 83 | 84 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 85 | +/** 86 | + Port group. 87 | + */ 88 | +struct PortGroup { 89 | + String name; 90 | + String symbol; 91 | +}; 92 | +#endif 93 | + 94 | /** 95 | MIDI event. 96 | */ 97 | @@ -812,6 +847,10 @@ protected: 98 | */ 99 | virtual void initParameter(uint32_t index, Parameter& parameter) = 0; 100 | 101 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 102 | + virtual void initPortGroup(uint32_t index, PortGroup& pgroup) = 0; 103 | +#endif 104 | + 105 | #if DISTRHO_PLUGIN_WANT_PROGRAMS 106 | /** 107 | Set the name of the program @a index.@n 108 | diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp 109 | index 3459876..0170d93 100644 110 | --- a/distrho/src/DistrhoPlugin.cpp 111 | +++ b/distrho/src/DistrhoPlugin.cpp 112 | @@ -32,6 +32,10 @@ const AudioPort PluginExporter::sFallbackAudioPort; 113 | const ParameterRanges PluginExporter::sFallbackRanges; 114 | const ParameterEnumerationValues PluginExporter::sFallbackEnumValues; 115 | 116 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 117 | +const PortGroup PluginExporter::sFallbackPortGroup; 118 | +#endif 119 | + 120 | /* ------------------------------------------------------------------------------------------------------------ 121 | * Plugin */ 122 | 123 | diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h 124 | index 69033de..ce2ec72 100644 125 | --- a/distrho/src/DistrhoPluginChecks.h 126 | +++ b/distrho/src/DistrhoPluginChecks.h 127 | @@ -69,6 +69,10 @@ 128 | # define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 129 | #endif 130 | 131 | +#ifndef DISTRHO_PLUGIN_WANT_PORT_GROUPS 132 | +# define DISTRHO_PLUGIN_WANT_PORT_GROUPS 0 133 | +#endif 134 | + 135 | #ifndef DISTRHO_PLUGIN_WANT_PROGRAMS 136 | # define DISTRHO_PLUGIN_WANT_PROGRAMS 0 137 | #endif 138 | diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp 139 | index c2f2aa3..10f45cf 100644 140 | --- a/distrho/src/DistrhoPluginInternal.hpp 141 | +++ b/distrho/src/DistrhoPluginInternal.hpp 142 | @@ -19,6 +19,8 @@ 143 | 144 | #include "../DistrhoPlugin.hpp" 145 | 146 | +#include 147 | + 148 | START_NAMESPACE_DISTRHO 149 | 150 | // ----------------------------------------------------------------------- 151 | @@ -51,6 +53,11 @@ struct Plugin::PrivateData { 152 | uint32_t parameterOffset; 153 | Parameter* parameters; 154 | 155 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 156 | + PortGroup* portGroups; 157 | + uint32_t portGroupCount; 158 | +#endif 159 | + 160 | #if DISTRHO_PLUGIN_WANT_PROGRAMS 161 | uint32_t programCount; 162 | String* programNames; 163 | @@ -85,6 +92,10 @@ struct Plugin::PrivateData { 164 | parameterCount(0), 165 | parameterOffset(0), 166 | parameters(nullptr), 167 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 168 | + portGroups(nullptr), 169 | + portGroupCount(0), 170 | +#endif 171 | #if DISTRHO_PLUGIN_WANT_PROGRAMS 172 | programCount(0), 173 | programNames(nullptr), 174 | @@ -138,6 +149,14 @@ struct Plugin::PrivateData { 175 | parameters = nullptr; 176 | } 177 | 178 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 179 | + if (portGroups != nullptr) 180 | + { 181 | + delete[] portGroups; 182 | + portGroups = nullptr; 183 | + } 184 | +#endif 185 | + 186 | #if DISTRHO_PLUGIN_WANT_PROGRAMS 187 | if (programNames != nullptr) 188 | { 189 | @@ -203,6 +222,38 @@ public: 190 | for (uint32_t i=0, count=fData->parameterCount; i < count; ++i) 191 | fPlugin->initParameter(i, fData->parameters[i]); 192 | 193 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 194 | + std::set portGroupIndices; 195 | + 196 | +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0 197 | + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) 198 | + portGroupIndices.insert(fData->audioPorts[i].group); 199 | +#endif 200 | + 201 | + for (uint32_t i=0, count=fData->parameterCount; i < count; ++i) 202 | + portGroupIndices.insert(fData->parameters[i].group); 203 | + 204 | + int32_t upperPortGroupIndex = kPortGroupDefault; 205 | + if (!portGroupIndices.empty()) 206 | + upperPortGroupIndex = *portGroupIndices.rbegin(); 207 | + 208 | + if (upperPortGroupIndex != kPortGroupDefault) 209 | + { 210 | + uint32_t portGroupCount = upperPortGroupIndex + 1; 211 | + 212 | + fData->portGroups = new PortGroup[portGroupCount]; 213 | + fData->portGroupCount = portGroupCount; 214 | + 215 | + for (std::set::iterator pos = portGroupIndices.begin(); 216 | + pos != portGroupIndices.end(); ++pos) 217 | + { 218 | + int32_t index = *pos; 219 | + if (index != kPortGroupDefault) 220 | + fPlugin->initPortGroup(index, fData->portGroups[index]); 221 | + } 222 | + } 223 | +#endif 224 | + 225 | #if DISTRHO_PLUGIN_WANT_PROGRAMS 226 | for (uint32_t i=0, count=fData->programCount; i < count; ++i) 227 | fPlugin->initProgramName(i, fData->programNames[i]); 228 | @@ -440,6 +491,30 @@ public: 229 | fPlugin->setParameterValue(index, value); 230 | } 231 | 232 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 233 | + int32_t getParameterGroupIndex(uint32_t index) const noexcept 234 | + { 235 | + DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, kPortGroupDefault); 236 | + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, kPortGroupDefault); 237 | + 238 | + return fData->parameters[index].group; 239 | + } 240 | + 241 | + const PortGroup& getPortGroup(uint32_t groupIndex) const noexcept 242 | + { 243 | + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && groupIndex < fData->portGroupCount, sFallbackPortGroup); 244 | + 245 | + return fData->portGroups[groupIndex]; 246 | + } 247 | + 248 | + uint32_t getPortGroupCount() const noexcept 249 | + { 250 | + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); 251 | + 252 | + return fData->portGroupCount; 253 | + } 254 | +#endif 255 | + 256 | #if DISTRHO_PLUGIN_WANT_PROGRAMS 257 | uint32_t getProgramCount() const noexcept 258 | { 259 | @@ -677,6 +752,10 @@ private: 260 | static const ParameterRanges sFallbackRanges; 261 | static const ParameterEnumerationValues sFallbackEnumValues; 262 | 263 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 264 | + static const PortGroup sFallbackPortGroup; 265 | +#endif 266 | + 267 | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter) 268 | DISTRHO_PREVENT_HEAP_ALLOCATION 269 | }; 270 | diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp 271 | index c074109..5423c6c 100644 272 | --- a/distrho/src/DistrhoPluginLV2export.cpp 273 | +++ b/distrho/src/DistrhoPluginLV2export.cpp 274 | @@ -23,6 +23,7 @@ 275 | #include "lv2/midi.h" 276 | #include "lv2/options.h" 277 | #include "lv2/patch.h" 278 | +#include "lv2/port-groups.h" 279 | #include "lv2/port-props.h" 280 | #include "lv2/presets.h" 281 | #include "lv2/resize-port.h" 282 | @@ -337,6 +338,9 @@ void lv2_generate_ttl(const char* const basename) 283 | #endif 284 | pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; 285 | pluginString += "@prefix patch: <" LV2_PATCH_PREFIX "> .\n"; 286 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 287 | + pluginString += "@prefix pg: <" LV2_PORT_GROUPS_PREFIX "> .\n"; 288 | +#endif 289 | pluginString += "@prefix rdf: .\n"; 290 | pluginString += "@prefix rdfs: .\n"; 291 | #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT 292 | @@ -428,6 +432,14 @@ void lv2_generate_ttl(const char* const basename) 293 | if (port.hints & kAudioPortIsSidechain) 294 | pluginString += " lv2:portProperty lv2:isSideChain;\n"; 295 | 296 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 297 | + if (port.group != kPortGroupDefault) 298 | + { 299 | + const PortGroup &pgroup = plugin.getPortGroup(port.group); 300 | + pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" + pgroup.symbol + "> ;\n"; 301 | + } 302 | +#endif 303 | + 304 | if (i+1 == DISTRHO_PLUGIN_NUM_INPUTS) 305 | pluginString += " ] ;\n"; 306 | else 307 | @@ -458,6 +470,14 @@ void lv2_generate_ttl(const char* const basename) 308 | if (port.hints & kAudioPortIsSidechain) 309 | pluginString += " lv2:portProperty lv2:isSideChain;\n"; 310 | 311 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 312 | + if (port.group != kPortGroupDefault) 313 | + { 314 | + const PortGroup &pgroup = plugin.getPortGroup(port.group); 315 | + pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" + pgroup.symbol + "> ;\n"; 316 | + } 317 | +#endif 318 | + 319 | if (i+1 == DISTRHO_PLUGIN_NUM_OUTPUTS) 320 | pluginString += " ] ;\n"; 321 | else 322 | @@ -694,6 +714,16 @@ void lv2_generate_ttl(const char* const basename) 323 | pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n"; 324 | pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomable "> ;\n"; 325 | } 326 | + 327 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 328 | + // group 329 | + int32_t pgroupIndex = plugin.getParameterGroupIndex(i); 330 | + if (pgroupIndex != kPortGroupDefault) 331 | + { 332 | + const PortGroup &pgroup = plugin.getPortGroup(pgroupIndex); 333 | + pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" + pgroup.symbol + "> ;\n"; 334 | + } 335 | +#endif 336 | } // ! designated 337 | 338 | if (i+1 == count) 339 | @@ -758,6 +788,60 @@ void lv2_generate_ttl(const char* const basename) 340 | pluginString += " lv2:minorVersion " + String(minorVersion) + " .\n"; 341 | } 342 | 343 | +#if DISTRHO_PLUGIN_WANT_PORT_GROUPS 344 | + // port groups 345 | + { 346 | + uint32_t count = plugin.getPortGroupCount(); 347 | + 348 | + for (int32_t pgroupIndex = 0; (uint32_t)pgroupIndex < count; ++pgroupIndex) 349 | + { 350 | + const PortGroup& pgroup = plugin.getPortGroup(pgroupIndex); 351 | + 352 | + DISTRHO_SAFE_ASSERT_CONTINUE(!pgroup.symbol.isEmpty()); 353 | + 354 | + pluginString += "\n<" DISTRHO_PLUGIN_URI "#portGroup_" + pgroup.symbol + ">\n"; 355 | + 356 | + bool isInput = false; 357 | + bool isOutput = false; 358 | + 359 | +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 360 | + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS && !isInput; ++i) 361 | + isInput = plugin.getAudioPort(true, i).group == pgroupIndex; 362 | +#endif 363 | + 364 | +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 365 | + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS && !isOutput; ++i) 366 | + isOutput = plugin.getAudioPort(false, i).group == pgroupIndex; 367 | +#endif 368 | + 369 | + for (uint32_t i=0, count=plugin.getParameterCount(); i < count && (!isInput || !isOutput); ++i) 370 | + { 371 | + if (plugin.getParameterGroupIndex(i) == pgroupIndex) 372 | + { 373 | + isInput = isInput || plugin.isParameterInput(i); 374 | + isOutput = isOutput || plugin.isParameterOutput(i); 375 | + } 376 | + } 377 | + 378 | + pluginString += " a "; 379 | + if (isInput && !isOutput) 380 | + pluginString += "pg:InputGroup"; 381 | + else if (isOutput && !isInput) 382 | + pluginString += "pg:OutputGroup"; 383 | + else 384 | + pluginString += "pg:Group"; 385 | + pluginString += " ;\n"; 386 | + 387 | +#if 0 388 | + pluginString += " rdfs:label \"" + pgroup.name + "\" ;\n"; 389 | +#else 390 | + pluginString += " lv2:name \"" + pgroup.name + "\" ;\n"; 391 | +#endif 392 | + pluginString += " lv2:symbol \"" + pgroup.symbol + "\" .\n"; 393 | + } 394 | + } 395 | +#endif 396 | + 397 | pluginFile << pluginString << std::endl; 398 | pluginFile.close(); 399 | std::cout << " done!" << std::endl; 400 | diff --git a/dpf.doxygen b/dpf.doxygen 401 | index 2516e93..3804409 100644 402 | --- a/dpf.doxygen 403 | +++ b/dpf.doxygen 404 | @@ -258,6 +258,7 @@ PREDEFINED = DOXYGEN \ 405 | DISTRHO_PLUGIN_WANT_MIDI_INPUT=1 \ 406 | DISTRHO_PLUGIN_WANT_MIDI_OUTPUT=1 \ 407 | DISTRHO_PLUGIN_WANT_PROGRAMS=1 \ 408 | + DISTRHO_PLUGIN_WANT_PORT_GROUPS=1 \ 409 | DISTRHO_PLUGIN_WANT_STATE=1 \ 410 | DISTRHO_PLUGIN_WANT_TIMEPOS=1 \ 411 | DISTRHO_PLUGIN_WANT_FULL_STATE=1 \ 412 | diff --git a/examples/Parameters/DistrhoPluginInfo.h b/examples/Parameters/DistrhoPluginInfo.h 413 | index 72bd345..a470ea5 100644 414 | --- a/examples/Parameters/DistrhoPluginInfo.h 415 | +++ b/examples/Parameters/DistrhoPluginInfo.h 416 | @@ -21,11 +21,12 @@ 417 | #define DISTRHO_PLUGIN_NAME "Parameters" 418 | #define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/Parameters" 419 | 420 | -#define DISTRHO_PLUGIN_HAS_UI 1 421 | -#define DISTRHO_PLUGIN_IS_RT_SAFE 1 422 | -#define DISTRHO_PLUGIN_NUM_INPUTS 2 423 | -#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 424 | -#define DISTRHO_PLUGIN_WANT_PROGRAMS 1 425 | -#define DISTRHO_UI_USER_RESIZABLE 1 426 | +#define DISTRHO_PLUGIN_HAS_UI 1 427 | +#define DISTRHO_PLUGIN_IS_RT_SAFE 1 428 | +#define DISTRHO_PLUGIN_NUM_INPUTS 2 429 | +#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 430 | +#define DISTRHO_PLUGIN_WANT_PORT_GROUPS 1 431 | +#define DISTRHO_PLUGIN_WANT_PROGRAMS 1 432 | +#define DISTRHO_UI_USER_RESIZABLE 1 433 | 434 | #endif // DISTRHO_PLUGIN_INFO_H_INCLUDED 435 | diff --git a/examples/Parameters/ExamplePluginParameters.cpp b/examples/Parameters/ExamplePluginParameters.cpp 436 | index ea6ec6d..b900de9 100644 437 | --- a/examples/Parameters/ExamplePluginParameters.cpp 438 | +++ b/examples/Parameters/ExamplePluginParameters.cpp 439 | @@ -104,6 +104,13 @@ The plugin will be treated as an effect, but it will not change the host audio." 440 | /* -------------------------------------------------------------------------------------------------------- 441 | * Init */ 442 | 443 | + enum 444 | + { 445 | + kPortGroupTop, 446 | + kPortGroupMiddle, 447 | + kPortGroupBottom, 448 | + }; 449 | + 450 | /** 451 | Initialize the parameter @a index. 452 | This function will be called once, shortly after the plugin is created. 453 | @@ -137,30 +144,39 @@ The plugin will be treated as an effect, but it will not change the host audio." 454 | { 455 | case 0: 456 | parameter.name = "top-left"; 457 | + parameter.group = kPortGroupTop; 458 | break; 459 | case 1: 460 | parameter.name = "top-center"; 461 | + parameter.group = kPortGroupTop; 462 | break; 463 | case 2: 464 | parameter.name = "top-right"; 465 | + parameter.group = kPortGroupTop; 466 | break; 467 | case 3: 468 | parameter.name = "middle-left"; 469 | + parameter.group = kPortGroupMiddle; 470 | break; 471 | case 4: 472 | parameter.name = "middle-center"; 473 | + parameter.group = kPortGroupMiddle; 474 | break; 475 | case 5: 476 | parameter.name = "middle-right"; 477 | + parameter.group = kPortGroupMiddle; 478 | break; 479 | case 6: 480 | parameter.name = "bottom-left"; 481 | + parameter.group = kPortGroupBottom; 482 | break; 483 | case 7: 484 | parameter.name = "bottom-center"; 485 | + parameter.group = kPortGroupBottom; 486 | break; 487 | case 8: 488 | parameter.name = "bottom-right"; 489 | + parameter.group = kPortGroupBottom; 490 | break; 491 | } 492 | 493 | @@ -171,6 +187,24 @@ The plugin will be treated as an effect, but it will not change the host audio." 494 | parameter.symbol.replace('-', '_'); 495 | } 496 | 497 | + void initPortGroup(uint32_t index, PortGroup& pgroup) override 498 | + { 499 | + switch (index) { 500 | + case kPortGroupTop: 501 | + pgroup.name = "Top"; 502 | + pgroup.symbol = "top"; 503 | + break; 504 | + case kPortGroupMiddle: 505 | + pgroup.name = "Middle"; 506 | + pgroup.symbol = "middle"; 507 | + break; 508 | + case kPortGroupBottom: 509 | + pgroup.name = "Bottom"; 510 | + pgroup.symbol = "bottom"; 511 | + break; 512 | + } 513 | + } 514 | + 515 | /** 516 | Set the name of the program @a index. 517 | This function will be called once, shortly after the plugin is created. 518 | -------------------------------------------------------------------------------- /patches/dpf/no-port-name-lv2-prefix.patch: -------------------------------------------------------------------------------- 1 | diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp 2 | index c074109..b6b2875 100644 3 | --- a/distrho/src/DistrhoPluginLV2export.cpp 4 | +++ b/distrho/src/DistrhoPluginLV2export.cpp 5 | @@ -422,7 +422,7 @@ void lv2_generate_ttl(const char* const basename) 6 | pluginString += " a lv2:InputPort, lv2:AudioPort ;\n"; 7 | 8 | pluginString += " lv2:index " + String(portIndex) + " ;\n"; 9 | - pluginString += " lv2:symbol \"lv2_" + port.symbol + "\" ;\n"; 10 | + pluginString += " lv2:symbol \"" + port.symbol + "\" ;\n"; 11 | pluginString += " lv2:name \"" + port.name + "\" ;\n"; 12 | 13 | if (port.hints & kAudioPortIsSidechain) 14 | @@ -452,7 +452,7 @@ void lv2_generate_ttl(const char* const basename) 15 | pluginString += " a lv2:OutputPort, lv2:AudioPort ;\n"; 16 | 17 | pluginString += " lv2:index " + String(portIndex) + " ;\n"; 18 | - pluginString += " lv2:symbol \"lv2_" + port.symbol + "\" ;\n"; 19 | + pluginString += " lv2:symbol \"" + port.symbol + "\" ;\n"; 20 | pluginString += " lv2:name \"" + port.name + "\" ;\n"; 21 | 22 | if (port.hints & kAudioPortIsSidechain) 23 | @@ -471,7 +471,7 @@ void lv2_generate_ttl(const char* const basename) 24 | pluginString += " a lv2:InputPort, atom:AtomPort ;\n"; 25 | pluginString += " lv2:index " + String(portIndex) + " ;\n"; 26 | pluginString += " lv2:name \"Events Input\" ;\n"; 27 | - pluginString += " lv2:symbol \"lv2_events_in\" ;\n"; 28 | + pluginString += " lv2:symbol \"events_in\" ;\n"; 29 | pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; 30 | pluginString += " atom:bufferType atom:Sequence ;\n"; 31 | # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) 32 | @@ -492,7 +492,7 @@ void lv2_generate_ttl(const char* const basename) 33 | pluginString += " a lv2:OutputPort, atom:AtomPort ;\n"; 34 | pluginString += " lv2:index " + String(portIndex) + " ;\n"; 35 | pluginString += " lv2:name \"Events Output\" ;\n"; 36 | - pluginString += " lv2:symbol \"lv2_events_out\" ;\n"; 37 | + pluginString += " lv2:symbol \"events_out\" ;\n"; 38 | pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; 39 | pluginString += " atom:bufferType atom:Sequence ;\n"; 40 | # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) 41 | @@ -510,7 +510,7 @@ void lv2_generate_ttl(const char* const basename) 42 | pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; 43 | pluginString += " lv2:index " + String(portIndex) + " ;\n"; 44 | pluginString += " lv2:name \"Latency\" ;\n"; 45 | - pluginString += " lv2:symbol \"lv2_latency\" ;\n"; 46 | + pluginString += " lv2:symbol \"latency\" ;\n"; 47 | pluginString += " lv2:designation lv2:latency ;\n"; 48 | pluginString += " lv2:portProperty lv2:reportsLatency, lv2:integer, <" LV2_PORT_PROPS__notOnGUI "> ;\n"; 49 | pluginString += " ] ;\n\n"; 50 | @@ -561,7 +561,7 @@ void lv2_generate_ttl(const char* const basename) 51 | String symbol(plugin.getParameterSymbol(i)); 52 | 53 | if (symbol.isEmpty()) 54 | - symbol = "lv2_port_" + String(portIndex-1); 55 | + symbol = "port_" + String(portIndex-1); 56 | 57 | pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; 58 | 59 | -------------------------------------------------------------------------------- /plugins.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | 4 | ## MIDI CC Map X4 5 | 6 | Map a single input CC to up to four output CCs. 7 | 8 | ![MIDI CC Map X4 screenshot](screenshots/MIDICCMapX4.png) 9 | 10 | * Can act on a selected CC received on all MIDI channels or a specific one. 11 | * Converts the matching source Control Change message into up-to-four 12 | destination CCs. 13 | * Each of the four destination CCs can be enabled separately and the following 14 | settings can be set for each (where X = 1..4): 15 | * `CC X Mode` - Enable destination CC X and set the control value mapping 16 | mode. 17 | * `CC X Destination` - the controller number 18 | * `CC X Channel` - the MIDI channel 19 | * `CC X Filter repeated values` - when enabled, if the control value 20 | (after conversion) of the destination Control Change message is the same 21 | as the one sent before (for this channel and controller number), it is 22 | supressed. 23 | * `CC X Start` and `CC X End` - the value range of the source 24 | Control Change message, which gets converted into this destination CC. 25 | 26 | If `Start > End`, only messages, whose controller values lie *outside* 27 | of the range `End` .. `Start`, are converted. 28 | * `CC X Minimum value` and `CC X maximum value` - the value range of 29 | the destination Control Change message to which the range of the source 30 | values is mapped. 31 | 32 | If `Minimum > Maximum`, the destination value range is inverted. 33 | * Any unmatched Control Change messages or other events are kept in the 34 | plugin's output. 35 | * The original source Control Change messages can be optionally kept in the 36 | plugin's output too (useful, for example, to cascade several instances of 37 | this plugin). 38 | 39 | 40 | ## MIDI CC Recorder 41 | 42 | Store received Control Change messages and replay them when triggered. 43 | 44 | **Note:** *This plugin is still considered to be in beta stage. I recommend not 45 | using it in an important project. The LV2 version should work as described, but 46 | there may be issues with the VST2 version due to poor support for trigger 47 | inputs.* 48 | 49 | ![MIDI CC Recorder screenshot](screenshots/MIDICCRecorder.png) 50 | 51 | * While "Record" is enabled, the plugin stores the last seen value of each 52 | Control Change message on each MIDI channel sent to its input. 53 | * When the "Send" trigger input is activated, all stored Control Change 54 | messages on the channel selected with "Send Channel" are sent to its 55 | output. 56 | * Sending can also be triggered when the transport state of the host 57 | changes to "playing", optionally only when the transport position is zero. 58 | * Lastly, sending can be triggered when receiving a selected MIDI Program 59 | Change event. There are parameters to set the program number and the MIDI 60 | channel of the PC event, which will trigger sending, when received. 61 | * If "Send Channel" is set to "All", all stored Control Change messages on all 62 | channels are sent. 63 | * The interval between sending each Control Change event can be set to 64 | between 1 and 200 milliseconds. 65 | * While sending is in progress, all send triggers are ignored. 66 | * While sending is in progress, no new Control Change messages are stored 67 | and Control Change messages received on the "Send Channel" are not 68 | passed through to the output. 69 | * When the "Clear" trigger input is activated, all stored Control Change 70 | messages are cleared. 71 | * The plugin state including all stored Control Change messages will be stored 72 | by the host and, if the host supports it, will be restored with the host 73 | session or when a preset is loaded. 74 | 75 | 76 | ## MIDI CC to Pressure 77 | 78 | Convert Control Change messages into (monophonic) Channel Pressure (Aftertouch). 79 | 80 | ![MIDI CC to Pressure screenshot](screenshots/MIDICCToPressure.png) 81 | 82 | * Can act on all MIDI channels or a specific one. 83 | * Configurable source controller number (0-127): modulation, breath, foot 84 | controller, expression, etc. 85 | * Any unconverted messages are kept in the plugin's output. 86 | * Original source Control Change messages can be optionally kept as well (useful, 87 | for example, to cascade other plugins handling the same CC after this plugin). 88 | 89 | 90 | ## MIDI PB to CC 91 | 92 | Convert Pitch Bend into Control Change messages. 93 | 94 | ![MIDI PB to CC screenshot](screenshots/MIDIPBToCC.png) 95 | 96 | * Can act on all MIDI channels or a specific one. 97 | * Supports different destination Control Change numbers for positive *(CC A)* 98 | and negative *(CC B)* Pitch Bend range. 99 | * Affected input Pitch Bend value range can be set, allowing, for example, to 100 | convert only positive Pitch Bend values. If *PB min. value* is higher than 101 | *PB max. value*, then only Pitch Bend values *outside* of the range are 102 | converted. 103 | * Allows to set the output value range for the Control Change messages. 104 | Positive Pitch Bend values within the input range set with *PB min. value* 105 | and *PB max. value* are mapped to the range set with *CC A min. value* and 106 | *CC A max. value*, negative Pitch Bend values within the input range are 107 | mapped to the range set with *CC B min. value* and *CC B max. value*. 108 | * Any unconverted messages are kept in the plugin's output. 109 | * Original Pitch Bend messages in the input range can be optionally kept as 110 | well (useful, for example, to cascade several instances of this plugin). 111 | 112 | 113 | ## MIDI Pressure to CC 114 | 115 | Convert (monophonic) Channel Pressure (Aftertouch) into Control Change 116 | messages. 117 | 118 | ![MIDI Pressure to CC screenshot](screenshots/MIDIPressureToCC.png) 119 | 120 | * Can act on all MIDI channels or a specific one. 121 | * Configurable destination controller number (0-127): modulation, breath, foot 122 | controller, expression, etc. 123 | * Any unconverted messages are kept in the plugin's output. 124 | * Original Channel Pressure messages can be optionally kept as well (useful, 125 | for example, to cascade several instances of this plugin). 126 | 127 | 128 | ## MIDI Sys Filter 129 | 130 | Filter out MIDI System Messages. 131 | 132 | ![MIDI Sys Filter screenshot](screenshots/MIDISysFilter.png) 133 | -------------------------------------------------------------------------------- /plugins/MIDICCMapX4/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI CC Map X4 plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2020 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef DISTRHO_PLUGIN_INFO_H 28 | #define DISTRHO_PLUGIN_INFO_H 29 | 30 | #define DISTRHO_PLUGIN_BRAND "chrisarndt.de" 31 | #define DISTRHO_PLUGIN_NAME "MIDI CC Map X4" 32 | #define DISTRHO_PLUGIN_URI "https://chrisarndt.de/plugins/midiccmapx4" 33 | #define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:MIDIPlugin" 34 | 35 | #define DISTRHO_PLUGIN_HAS_UI 0 36 | #define DISTRHO_UI_USE_NANOVG 0 37 | 38 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 39 | #define DISTRHO_PLUGIN_NUM_INPUTS 0 40 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 0 41 | #define DISTRHO_PLUGIN_WANT_TIMEPOS 0 42 | #define DISTRHO_PLUGIN_WANT_PORT_GROUPS 1 43 | #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 44 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 45 | #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 46 | 47 | #endif // DISTRHO_PLUGIN_INFO_H 48 | -------------------------------------------------------------------------------- /plugins/MIDICCMapX4/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Makefile for DISTRHO Plugins # 3 | # ---------------------------- # 4 | # Created by falkTX, Christopher Arndt, and Patrick Desaulniers 5 | # 6 | 7 | # -------------------------------------------------------------- 8 | # Installation directories 9 | 10 | PREFIX ?= /usr/local 11 | BINDIR ?= $(PREFIX)/bin 12 | LIBDIR ?= $(PREFIX)/lib 13 | DSSI_DIR ?= $(LIBDIR)/dssi 14 | LADSPA_DIR ?= $(LIBDIR)/ladspa 15 | LV2_DIR ?= $(LIBDIR)/lv2 16 | VST_DIR ?= $(LIBDIR)/vst 17 | 18 | # -------------------------------------------------------------- 19 | # Project name, used for binaries 20 | 21 | NAME = midiccmapx4 22 | 23 | # -------------------------------------------------------------- 24 | # Plugin types to build 25 | 26 | BUILD_LV2 ?= true 27 | BUILD_VST2 ?= true 28 | BUILD_JACK ?= false 29 | BUILD_DSSI ?= false 30 | BUILD_LADSPA ?= false 31 | 32 | # -------------------------------------------------------------- 33 | # Files to build 34 | 35 | FILES_DSP = \ 36 | PluginMIDICCMapX4.cpp 37 | 38 | # -------------------------------------------------------------- 39 | # Do some magic 40 | 41 | include ../../dpf/Makefile.plugins.mk 42 | 43 | # -------------------------------------------------------------- 44 | # Enable all selected plugin types 45 | 46 | ifeq ($(BUILD_LV2),true) 47 | ifeq ($(HAVE_DGL),true) 48 | TARGETS += lv2_sep 49 | else 50 | TARGETS += lv2_dsp 51 | endif 52 | endif 53 | 54 | ifeq ($(BUILD_VST2),true) 55 | TARGETS += vst 56 | endif 57 | 58 | ifeq ($(BUILD_JACK),true) 59 | ifeq ($(HAVE_JACK),true) 60 | TARGETS += jack 61 | endif 62 | endif 63 | 64 | ifeq ($(BUILD_DSSI),true) 65 | ifeq ($(HAVE_DGL),true) 66 | ifeq ($(HAVE_LIBLO),true) 67 | TARGETS += dssi 68 | endif 69 | endif 70 | endif 71 | 72 | ifeq ($(BUILD_LADSPA),true) 73 | TARGETS += ladspa 74 | endif 75 | 76 | all: $(TARGETS) 77 | 78 | install: all 79 | ifeq ($(BUILD_DSSI),true) 80 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(DESTDIR)$(DSSI_DIR) 81 | endif 82 | ifeq ($(BUILD_LADSPA),true) 83 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(DESTDIR)$(LADSPA_DIR) 84 | endif 85 | ifeq ($(BUILD_VST2),true) 86 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(DESTDIR)$(VST_DIR) 87 | endif 88 | ifeq ($(BUILD_LV2),true) 89 | @install -dm755 $(DESTDIR)$(LV2_DIR) && \ 90 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(DESTDIR)$(LV2_DIR) 91 | endif 92 | ifeq ($(BUILD_JACK),true) 93 | ifeq ($(HAVE_JACK),true) 94 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(DESTDIR)$(BINDIR) 95 | endif 96 | endif 97 | 98 | install-user: all 99 | ifeq ($(BUILD_DSSI),true) 100 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(HOME)/.dssi 101 | endif 102 | ifeq ($(BUILD_LADSPA),true) 103 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(HOME)/.ladspa 104 | endif 105 | ifeq ($(BUILD_VST2),true) 106 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(HOME)/.vst 107 | endif 108 | ifeq ($(BUILD_LV2),true) 109 | @install -dm755 $(HOME)/.lv2 && \ 110 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(HOME)/.lv2 111 | endif 112 | ifeq ($(BUILD_JACK),true) 113 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(HOME)/bin 114 | endif 115 | 116 | # -------------------------------------------------------------- 117 | 118 | .PHONY: all install install-user 119 | -------------------------------------------------------------------------------- /plugins/MIDICCMapX4/PluginMIDICCMapX4.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI CC Map X4 plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2020 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #include "PluginMIDICCMapX4.hpp" 28 | #include 29 | 30 | START_NAMESPACE_DISTRHO 31 | 32 | enum { 33 | kPortGroupSource, 34 | kPortGroupCC1, 35 | kPortGroupCC2, 36 | kPortGroupCC3, 37 | kPortGroupCC4, 38 | }; 39 | 40 | const ParameterEnumerationValue paramEnumSrcChannels[] = { 41 | {0,"Any"}, 42 | {1, "Channel 1"}, 43 | {2, "Channel 2"}, 44 | {3, "Channel 3"}, 45 | {4, "Channel 4"}, 46 | {5, "Channel 5"}, 47 | {6, "Channel 6"}, 48 | {7, "Channel 7"}, 49 | {8, "Channel 8"}, 50 | {9, "Channel 9"}, 51 | {10, "Channel 10"}, 52 | {11, "Channel 11"}, 53 | {12, "Channel 12"}, 54 | {13, "Channel 13"}, 55 | {14, "Channel 14"}, 56 | {15, "Channel 15"}, 57 | {16, "Channel 16"} 58 | }; 59 | 60 | const ParameterEnumerationValue paramEnumDstChannels[] = { 61 | {0,"Same as source"}, 62 | {1, "Channel 1"}, 63 | {2, "Channel 2"}, 64 | {3, "Channel 3"}, 65 | {4, "Channel 4"}, 66 | {5, "Channel 5"}, 67 | {6, "Channel 6"}, 68 | {7, "Channel 7"}, 69 | {8, "Channel 8"}, 70 | {9, "Channel 9"}, 71 | {10, "Channel 10"}, 72 | {11, "Channel 11"}, 73 | {12, "Channel 12"}, 74 | {13, "Channel 13"}, 75 | {14, "Channel 14"}, 76 | {15, "Channel 15"}, 77 | {16, "Channel 16"} 78 | }; 79 | 80 | const ParameterEnumerationValue paramEnumModes[] { 81 | {0, "Disabled"}, 82 | {1, "Map start/end range to min/max"}, 83 | {2, "Map full value range to min/max"} 84 | }; 85 | 86 | // ----------------------------------------------------------------------- 87 | 88 | template 89 | static inline void fillEnumValues(ParameterEnumerationValues& pev, 90 | const ParameterEnumerationValue(& list)[N]) { 91 | ParameterEnumerationValue* values = new ParameterEnumerationValue[N]; 92 | pev.count = N; 93 | pev.values = values; 94 | std::copy(list, list + N, values); 95 | } 96 | 97 | // ----------------------------------------------------------------------- 98 | 99 | PluginMIDICCMapX4::PluginMIDICCMapX4() 100 | : Plugin(paramCount, presetCount, 0) // 0 states 101 | { 102 | for (uint8_t ch=0; ch<16; ch++) { 103 | for (uint8_t cc=0; cc<128; cc++) { 104 | lastCCValue[ch][cc] = -1; 105 | } 106 | } 107 | loadProgram(0); 108 | } 109 | 110 | // ----------------------------------------------------------------------- 111 | // Init 112 | 113 | void PluginMIDICCMapX4::initParameter(uint32_t index, Parameter& parameter) { 114 | if (index >= paramCount) 115 | return; 116 | 117 | parameter.hints = kParameterIsAutomable | kParameterIsInteger; 118 | parameter.ranges.def = 0; 119 | parameter.ranges.min = 0; 120 | parameter.ranges.max = 127; 121 | 122 | switch (index) { 123 | case paramFilterChannel: 124 | parameter.name = "Filter Channel"; 125 | parameter.symbol = "channelf"; 126 | parameter.ranges.max = 16; 127 | parameter.enumValues.restrictedMode = true; 128 | fillEnumValues(parameter.enumValues, paramEnumSrcChannels); 129 | parameter.group = kPortGroupSource; 130 | break; 131 | case paramCCSource: 132 | parameter.name = "Source CC"; 133 | parameter.symbol = "cc_source"; 134 | parameter.ranges.def = 1; 135 | parameter.group = kPortGroupSource; 136 | break; 137 | case paramKeepOriginal: 138 | parameter.name = "Keep Source CC Events"; 139 | parameter.shortName = "Keep src. CC"; 140 | parameter.symbol = "keep_original"; 141 | parameter.hints |= kParameterIsBoolean; 142 | parameter.ranges.max = 1; 143 | parameter.group = kPortGroupSource; 144 | break; 145 | case paramCC1Mode: 146 | parameter.name = "CC 1 Mode"; 147 | parameter.shortName = "CC1 Mode"; 148 | parameter.symbol = "cc1_mode"; 149 | parameter.ranges.max = 2; 150 | parameter.enumValues.restrictedMode = true; 151 | fillEnumValues(parameter.enumValues, paramEnumModes); 152 | parameter.group = kPortGroupCC1; 153 | break; 154 | case paramCC1Dest: 155 | parameter.name = "CC 1 Destination"; 156 | parameter.shortName = "CC1 Dest."; 157 | parameter.symbol = "cc1_dest"; 158 | parameter.ranges.def = 14; 159 | parameter.group = kPortGroupCC1; 160 | break; 161 | case paramCC1Channel: 162 | parameter.name = "CC1 Channel"; 163 | parameter.symbol = "cc1_chan"; 164 | parameter.ranges.max = 16; 165 | parameter.enumValues.restrictedMode = true; 166 | fillEnumValues(parameter.enumValues, paramEnumDstChannels); 167 | parameter.group = kPortGroupCC1; 168 | break; 169 | case paramCC1FilterDups: 170 | parameter.name = "CC 1 Filter repeated values"; 171 | parameter.shortName = "CC1 Filter dups"; 172 | parameter.symbol = "cc1_filterdups"; 173 | parameter.ranges.def = 1; 174 | parameter.ranges.max = 1; 175 | parameter.hints |= kParameterIsBoolean; 176 | parameter.group = kPortGroupCC1; 177 | break; 178 | case paramCC1Start: 179 | parameter.name = "CC 1 Start"; 180 | parameter.shortName = "CC1 Start"; 181 | parameter.symbol = "cc1_start"; 182 | parameter.group = kPortGroupCC1; 183 | break; 184 | case paramCC1End: 185 | parameter.name = "CC 1 End"; 186 | parameter.shortName = "CC1 End"; 187 | parameter.symbol = "cc1_end"; 188 | parameter.ranges.def = 127; 189 | parameter.group = kPortGroupCC1; 190 | break; 191 | case paramCC1Min: 192 | parameter.name = "CC 1 Minimum value"; 193 | parameter.shortName = "CC1 Min. value"; 194 | parameter.symbol = "cc1_min"; 195 | parameter.group = kPortGroupCC1; 196 | break; 197 | case paramCC1Max: 198 | parameter.name = "CC 1 Maximum value"; 199 | parameter.shortName = "CC1 Max. value"; 200 | parameter.symbol = "cc1_max"; 201 | parameter.ranges.def = 127; 202 | parameter.group = kPortGroupCC1; 203 | break; 204 | case paramCC2Mode: 205 | parameter.name = "CC 2 Mode"; 206 | parameter.shortName = "CC2 Mode"; 207 | parameter.symbol = "cc2_mode"; 208 | parameter.ranges.max = 2; 209 | parameter.enumValues.restrictedMode = true; 210 | fillEnumValues(parameter.enumValues, paramEnumModes); 211 | parameter.group = kPortGroupCC2; 212 | break; 213 | case paramCC2Dest: 214 | parameter.name = "CC 2 Destination"; 215 | parameter.shortName = "CC2 Dest."; 216 | parameter.symbol = "cc2_dest"; 217 | parameter.ranges.def = 15; 218 | parameter.group = kPortGroupCC2; 219 | break; 220 | case paramCC2Channel: 221 | parameter.name = "CC2 Channel"; 222 | parameter.symbol = "cc2_chan"; 223 | parameter.ranges.max = 16; 224 | parameter.enumValues.restrictedMode = true; 225 | fillEnumValues(parameter.enumValues, paramEnumDstChannels); 226 | parameter.group = kPortGroupCC2; 227 | break; 228 | case paramCC2FilterDups: 229 | parameter.name = "CC 2 Filter repeated values"; 230 | parameter.shortName = "CC2 Filter dups"; 231 | parameter.symbol = "cc2_filterdups"; 232 | parameter.ranges.def = 1; 233 | parameter.ranges.max = 1; 234 | parameter.hints |= kParameterIsBoolean; 235 | parameter.ranges.max = 1; 236 | parameter.group = kPortGroupCC2; 237 | break; 238 | case paramCC2Start: 239 | parameter.name = "CC 2 Start"; 240 | parameter.shortName = "CC2 Start"; 241 | parameter.symbol = "cc2_start"; 242 | parameter.group = kPortGroupCC2; 243 | break; 244 | case paramCC2End: 245 | parameter.name = "CC 2 End"; 246 | parameter.shortName = "CC2 End"; 247 | parameter.symbol = "cc2_end"; 248 | parameter.ranges.def = 127; 249 | parameter.group = kPortGroupCC2; 250 | break; 251 | case paramCC2Min: 252 | parameter.name = "CC 2 Minimum value"; 253 | parameter.shortName = "CC2 Min. value"; 254 | parameter.symbol = "cc2_min"; 255 | parameter.group = kPortGroupCC2; 256 | break; 257 | case paramCC2Max: 258 | parameter.name = "CC 2 Maximum value"; 259 | parameter.shortName = "CC2 Max. value"; 260 | parameter.symbol = "cc2_max"; 261 | parameter.ranges.def = 127; 262 | parameter.group = kPortGroupCC2; 263 | break; 264 | case paramCC3Mode: 265 | parameter.name = "CC 3 Mode"; 266 | parameter.shortName = "CC3 Mode"; 267 | parameter.symbol = "cc3_mode"; 268 | parameter.ranges.max = 2; 269 | parameter.enumValues.restrictedMode = true; 270 | fillEnumValues(parameter.enumValues, paramEnumModes); 271 | parameter.group = kPortGroupCC3; 272 | break; 273 | case paramCC3Dest: 274 | parameter.name = "CC 3 Destination"; 275 | parameter.shortName = "CC3 Dest."; 276 | parameter.symbol = "cc3_dest"; 277 | parameter.ranges.def = 16; 278 | parameter.group = kPortGroupCC3; 279 | break; 280 | case paramCC3Channel: 281 | parameter.name = "CC3 Channel"; 282 | parameter.symbol = "cc3_chan"; 283 | parameter.ranges.max = 16; 284 | parameter.enumValues.restrictedMode = true; 285 | fillEnumValues(parameter.enumValues, paramEnumDstChannels); 286 | parameter.group = kPortGroupCC3; 287 | break; 288 | case paramCC3FilterDups: 289 | parameter.name = "CC 3 Filter repeated values"; 290 | parameter.shortName = "CC3 Filter dups"; 291 | parameter.symbol = "cc3_filterdups"; 292 | parameter.ranges.def = 1; 293 | parameter.ranges.max = 1; 294 | parameter.hints |= kParameterIsBoolean; 295 | parameter.group = kPortGroupCC3; 296 | break; 297 | case paramCC3Start: 298 | parameter.name = "CC 3 Start"; 299 | parameter.shortName = "CC3 Start"; 300 | parameter.symbol = "cc3_start"; 301 | parameter.group = kPortGroupCC3; 302 | break; 303 | case paramCC3End: 304 | parameter.name = "CC 3 End"; 305 | parameter.shortName = "CC3 End"; 306 | parameter.symbol = "cc3_end"; 307 | parameter.ranges.def = 127; 308 | parameter.group = kPortGroupCC3; 309 | break; 310 | case paramCC3Min: 311 | parameter.name = "CC 3 Minimum value"; 312 | parameter.shortName = "CC3 Min. value"; 313 | parameter.symbol = "cc3_min"; 314 | parameter.group = kPortGroupCC3; 315 | break; 316 | case paramCC3Max: 317 | parameter.name = "CC 3 Maximum value"; 318 | parameter.shortName = "CC3 Max. value"; 319 | parameter.symbol = "cc3_max"; 320 | parameter.ranges.def = 127; 321 | parameter.group = kPortGroupCC3; 322 | break; 323 | case paramCC4Mode: 324 | parameter.name = "CC 4 Mode"; 325 | parameter.shortName = "CC4 Mode"; 326 | parameter.symbol = "cc4_mode"; 327 | parameter.ranges.max = 2; 328 | parameter.enumValues.restrictedMode = true; 329 | fillEnumValues(parameter.enumValues, paramEnumModes); 330 | parameter.group = kPortGroupCC4; 331 | break; 332 | case paramCC4Dest: 333 | parameter.name = "CC 4 Destination"; 334 | parameter.shortName = "CC4 Dest."; 335 | parameter.symbol = "cc4_dest"; 336 | parameter.ranges.def = 17; 337 | parameter.group = kPortGroupCC4; 338 | break; 339 | case paramCC4Channel: 340 | parameter.name = "CC4 Channel"; 341 | parameter.symbol = "cc4_chan"; 342 | parameter.ranges.max = 16; 343 | parameter.enumValues.restrictedMode = true; 344 | fillEnumValues(parameter.enumValues, paramEnumDstChannels); 345 | parameter.group = kPortGroupCC4; 346 | break; 347 | case paramCC4FilterDups: 348 | parameter.name = "CC 4 Filter repeated values"; 349 | parameter.shortName = "CC4 Filter dups"; 350 | parameter.symbol = "cc4_filterdups"; 351 | parameter.ranges.def = 1; 352 | parameter.ranges.max = 1; 353 | parameter.hints |= kParameterIsBoolean; 354 | parameter.group = kPortGroupCC4; 355 | break; 356 | case paramCC4Start: 357 | parameter.name = "CC Start"; 358 | parameter.shortName = "CC4 Start"; 359 | parameter.symbol = "cc4_start"; 360 | parameter.group = kPortGroupCC4; 361 | break; 362 | case paramCC4End: 363 | parameter.name = "CC 4 End"; 364 | parameter.shortName = "CC4 End"; 365 | parameter.symbol = "cc4_end"; 366 | parameter.ranges.def = 127; 367 | parameter.group = kPortGroupCC4; 368 | break; 369 | case paramCC4Min: 370 | parameter.name = "CC 4 Minimum value"; 371 | parameter.shortName = "CC4 Min. value"; 372 | parameter.symbol = "cc4_min"; 373 | parameter.group = kPortGroupCC4; 374 | break; 375 | case paramCC4Max: 376 | parameter.name = "CC 4 Maximum value"; 377 | parameter.shortName = "CC4 Max. value"; 378 | parameter.symbol = "cc4_max"; 379 | parameter.ranges.def = 127; 380 | parameter.group = kPortGroupCC4; 381 | break; 382 | } 383 | } 384 | 385 | /** 386 | Set the name and symbol of the port group @a index. 387 | This function will be called once for every port group, shortly after the plugin is created. 388 | */ 389 | void PluginMIDICCMapX4::initPortGroup(uint32_t index, PortGroup& pgroup) { 390 | switch (index) { 391 | case kPortGroupSource: 392 | pgroup.name = "Source"; 393 | pgroup.symbol = "source"; 394 | break; 395 | case kPortGroupCC1: 396 | pgroup.name = "Destination #1"; 397 | pgroup.symbol = "dest1"; 398 | break; 399 | case kPortGroupCC2: 400 | pgroup.name = "Destination #2"; 401 | pgroup.symbol = "dest2"; 402 | break; 403 | case kPortGroupCC3: 404 | pgroup.name = "Destination #3"; 405 | pgroup.symbol = "dest3"; 406 | break; 407 | case kPortGroupCC4: 408 | pgroup.name = "Destination #4"; 409 | pgroup.symbol = "dest4"; 410 | break; 411 | } 412 | } 413 | 414 | 415 | /** 416 | Set the name of the program @a index. 417 | This function will be called once, shortly after the plugin is created. 418 | */ 419 | void PluginMIDICCMapX4::initProgramName(uint32_t index, String& programName) { 420 | if (index < presetCount) { 421 | programName = factoryPresets[index].name; 422 | } 423 | } 424 | 425 | // ----------------------------------------------------------------------- 426 | // Internal data 427 | 428 | /** 429 | Optional callback to inform the plugin about a sample rate change. 430 | */ 431 | void PluginMIDICCMapX4::sampleRateChanged(double newSampleRate) { 432 | (void) newSampleRate; 433 | } 434 | 435 | /** 436 | Get the current value of a parameter. 437 | */ 438 | float PluginMIDICCMapX4::getParameterValue(uint32_t index) const { 439 | return fParams[index]; 440 | } 441 | 442 | /** 443 | Change a parameter value. 444 | */ 445 | void PluginMIDICCMapX4::setParameterValue(uint32_t index, float value) { 446 | switch (index) { 447 | case paramFilterChannel: 448 | fParams[index] = CLAMP(value, 0.0f, 16.0f); 449 | filterChannel = (int8_t) fParams[index] - 1; 450 | break; 451 | case paramCC1Channel: 452 | case paramCC2Channel: 453 | case paramCC3Channel: 454 | case paramCC4Channel: 455 | fParams[index] = CLAMP(value, 0.0f, 16.0f); 456 | break; 457 | case paramKeepOriginal: 458 | case paramCC1FilterDups: 459 | case paramCC2FilterDups: 460 | case paramCC3FilterDups: 461 | case paramCC4FilterDups: 462 | fParams[index] = CLAMP(value, 0.0f, 1.0f); 463 | break; 464 | case paramCC1Mode: 465 | case paramCC2Mode: 466 | case paramCC3Mode: 467 | case paramCC4Mode: 468 | fParams[index] = CLAMP(value, 0.0f, 2.0f); 469 | break; 470 | case paramCCSource: 471 | case paramCC1Dest: 472 | case paramCC1Min: 473 | case paramCC1Max: 474 | case paramCC1Start: 475 | case paramCC1End: 476 | case paramCC2Dest: 477 | case paramCC2Min: 478 | case paramCC2Max: 479 | case paramCC2Start: 480 | case paramCC2End: 481 | case paramCC3Dest: 482 | case paramCC3Min: 483 | case paramCC3Max: 484 | case paramCC3Start: 485 | case paramCC3End: 486 | case paramCC4Dest: 487 | case paramCC4Min: 488 | case paramCC4Max: 489 | case paramCC4Start: 490 | case paramCC4End: 491 | fParams[index] = CLAMP(value, 0.0f, 127.0f); 492 | break; 493 | } 494 | } 495 | 496 | /** 497 | Load a program. 498 | The host may call this function from any context, 499 | including realtime processing. 500 | */ 501 | void PluginMIDICCMapX4::loadProgram(uint32_t index) { 502 | if (index < presetCount) { 503 | for (int i=0; i < paramCount; i++) { 504 | setParameterValue(i, factoryPresets[index].params[i]); 505 | } 506 | 507 | } 508 | } 509 | 510 | // ----------------------------------------------------------------------- 511 | // Process 512 | 513 | void PluginMIDICCMapX4::activate() { 514 | // plugin is activated 515 | } 516 | 517 | 518 | void PluginMIDICCMapX4::run(const float**, float**, uint32_t, 519 | const MidiEvent* events, uint32_t eventCount) { 520 | bool pass; 521 | uint8_t status, chan, cc_mode, cc_dest, cc_end, cc_min, cc_max, cc_no_dups, 522 | cc_num, cc_start, cc_val, new_val, param_offset; 523 | int8_t cc_chan; 524 | uint8_t cc_src = (uint8_t) fParams[paramCCSource]; 525 | struct MidiEvent cc_event; 526 | 527 | for (uint32_t i=0; i 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef PLUGIN_MIDICCMAPX4_H 28 | #define PLUGIN_MIDICCMAPX4_H 29 | 30 | #include "DistrhoPlugin.hpp" 31 | 32 | START_NAMESPACE_DISTRHO 33 | 34 | #ifndef IN_RANGE 35 | #define IN_RANGE(v, min, max) ((min) <= (max) ? ((v) >= (min) && (v) <= (max)) : ((v) >= (min) || (v) <= (max))) 36 | #endif 37 | 38 | #ifndef MAP 39 | #define MAP(v, imin, imax, omin, omax) (((v) - (imin)) * ((omax) - (omin)) / ((imax) - (imin)) + (omin)) 40 | #endif 41 | 42 | #ifndef MIN 43 | #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) 44 | #endif 45 | 46 | #ifndef MAX 47 | #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) 48 | #endif 49 | 50 | #ifndef CLAMP 51 | #define CLAMP(v, min, max) (MIN((max), MAX((min), (v)))) 52 | #endif 53 | 54 | #define MIDI_CONTROL_CHANGE 0xB0 55 | 56 | // ----------------------------------------------------------------------- 57 | 58 | class PluginMIDICCMapX4 : public Plugin { 59 | public: 60 | enum Parameters { 61 | paramFilterChannel, 62 | paramCCSource, 63 | paramKeepOriginal, 64 | paramCC1Mode, 65 | paramCC1Dest, 66 | paramCC1Channel, 67 | paramCC1FilterDups, 68 | paramCC1Start, 69 | paramCC1End, 70 | paramCC1Min, 71 | paramCC1Max, 72 | paramCC2Mode, 73 | paramCC2Dest, 74 | paramCC2Channel, 75 | paramCC2FilterDups, 76 | paramCC2Start, 77 | paramCC2End, 78 | paramCC2Min, 79 | paramCC2Max, 80 | paramCC3Mode, 81 | paramCC3Dest, 82 | paramCC3Channel, 83 | paramCC3FilterDups, 84 | paramCC3Start, 85 | paramCC3End, 86 | paramCC3Min, 87 | paramCC3Max, 88 | paramCC4Mode, 89 | paramCC4Dest, 90 | paramCC4Channel, 91 | paramCC4FilterDups, 92 | paramCC4Start, 93 | paramCC4End, 94 | paramCC4Min, 95 | paramCC4Max, 96 | paramCount 97 | }; 98 | 99 | PluginMIDICCMapX4(); 100 | 101 | protected: 102 | // ------------------------------------------------------------------- 103 | // Information 104 | 105 | const char* getLabel() const noexcept override { 106 | return "MIDICCMapX4"; 107 | } 108 | 109 | const char* getDescription() const override { 110 | return "Map a single input CC to up to four output CCs"; 111 | } 112 | 113 | const char* getMaker() const noexcept override { 114 | return "chrisarndt.de"; 115 | } 116 | 117 | const char* getHomePage() const override { 118 | return DISTRHO_PLUGIN_URI; 119 | } 120 | 121 | const char* getLicense() const noexcept override { 122 | return "https://spdx.org/licenses/MIT"; 123 | } 124 | 125 | uint32_t getVersion() const noexcept override { 126 | return d_version(1, 0, 0); 127 | } 128 | 129 | // Go to: 130 | // 131 | // http://service.steinberg.de/databases/plugin.nsf/plugIn 132 | // 133 | // Get a proper plugin UID and fill it in here! 134 | int64_t getUniqueId() const noexcept override { 135 | return d_cconst('M', 'C', 'C', '4'); 136 | } 137 | 138 | // ------------------------------------------------------------------- 139 | // Init 140 | 141 | void initParameter(uint32_t index, Parameter& parameter) override; 142 | void initPortGroup(uint32_t index, PortGroup& pgroup) override; 143 | void initProgramName(uint32_t index, String& programName) override; 144 | 145 | // ------------------------------------------------------------------- 146 | // Internal data 147 | 148 | float getParameterValue(uint32_t index) const override; 149 | void setParameterValue(uint32_t index, float value) override; 150 | void loadProgram(uint32_t index) override; 151 | 152 | // ------------------------------------------------------------------- 153 | // Optional 154 | 155 | // Optional callback to inform the plugin about a sample rate change. 156 | void sampleRateChanged(double newSampleRate) override; 157 | 158 | // ------------------------------------------------------------------- 159 | // Process 160 | 161 | void activate() override; 162 | 163 | void run(const float**, float**, uint32_t, 164 | const MidiEvent* midiEvents, uint32_t midiEventCount) override; 165 | 166 | 167 | // ------------------------------------------------------------------- 168 | 169 | private: 170 | float fParams[paramCount]; 171 | int8_t filterChannel; 172 | int8_t lastCCValue[16][128]; 173 | 174 | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginMIDICCMapX4) 175 | }; 176 | 177 | struct Preset { 178 | const char* name; 179 | const float params[PluginMIDICCMapX4::paramCount]; 180 | }; 181 | 182 | const Preset factoryPresets[] = { 183 | { 184 | "Default", 185 | { 186 | 0.0, // Filter Channel 187 | 0.0, // Keep Source CC 188 | 1.0, // Source CC 189 | 0.0, // CC1 Mode 190 | 14.0, // CC1 Dest 191 | 0.0, // CC1 Channel 192 | 1.0, // CC1 Keep Dups 193 | 0.0, // CC1 Start 194 | 127.0, // CC1 End 195 | 0.0, // CC1 Min 196 | 127.0, // CC1 Max 197 | 0.0, // CC2 Mode 198 | 15.0, // CC2 Dest 199 | 0.0, // CC2 Channel 200 | 1.0, // CC2 Keep Dups 201 | 0.0, // CC2 Start 202 | 127.0, // CC2 End 203 | 0.0, // CC2 Min 204 | 127.0, // CC2 Max 205 | 0.0, // CC3 Mode 206 | 16.0, // CC3 Dest 207 | 0.0, // CC3 Channel 208 | 1.0, // CC3 Keep Dups 209 | 0.0, // CC3 Start 210 | 127.0, // CC3 End 211 | 0.0, // CC3 Min 212 | 127.0, // CC3 Max 213 | 0.0, // CC4 Mode 214 | 17.0, // CC4 Dest 215 | 0.0, // CC4 Channel 216 | 1.0, // CC4 Keep Dups 217 | 0.0, // CC4 Start 218 | 127.0, // CC4 End 219 | 0.0, // CC4 Min 220 | 127.0, // CC4 Max 221 | } 222 | }, 223 | }; 224 | 225 | const uint presetCount = sizeof(factoryPresets) / sizeof(Preset); 226 | 227 | // ----------------------------------------------------------------------- 228 | 229 | END_NAMESPACE_DISTRHO 230 | 231 | #endif // #ifndef PLUGIN_MIDICCMAPX4_H 232 | -------------------------------------------------------------------------------- /plugins/MIDICCRecorder/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI CCRecorder plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2019 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef DISTRHO_PLUGIN_INFO_H 28 | #define DISTRHO_PLUGIN_INFO_H 29 | 30 | #define DISTRHO_PLUGIN_BRAND "chrisarndt.de" 31 | #define DISTRHO_PLUGIN_NAME "MIDI CC Recorder" 32 | #define DISTRHO_PLUGIN_URI "https://chrisarndt.de/plugins/midiccrecorder" 33 | #define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:MIDIPlugin" 34 | 35 | #define DISTRHO_PLUGIN_HAS_UI 0 36 | #define DISTRHO_UI_USE_NANOVG 0 37 | 38 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 39 | #define DISTRHO_PLUGIN_NUM_INPUTS 0 40 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 0 41 | #define DISTRHO_PLUGIN_WANT_TIMEPOS 1 42 | #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 43 | #define DISTRHO_PLUGIN_WANT_STATE 1 44 | #define DISTRHO_PLUGIN_WANT_FULL_STATE 1 45 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 46 | #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 47 | 48 | #endif // DISTRHO_PLUGIN_INFO_H 49 | -------------------------------------------------------------------------------- /plugins/MIDICCRecorder/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Makefile for DISTRHO Plugins # 3 | # ---------------------------- # 4 | # Created by falkTX, Christopher Arndt, and Patrick Desaulniers 5 | # 6 | 7 | # -------------------------------------------------------------- 8 | # Installation directories 9 | 10 | PREFIX ?= /usr/local 11 | BINDIR ?= $(PREFIX)/bin 12 | LIBDIR ?= $(PREFIX)/lib 13 | DSSI_DIR ?= $(LIBDIR)/dssi 14 | LADSPA_DIR ?= $(LIBDIR)/ladspa 15 | LV2_DIR ?= $(LIBDIR)/lv2 16 | VST_DIR ?= $(LIBDIR)/vst 17 | 18 | # -------------------------------------------------------------- 19 | # Project name, used for binaries 20 | 21 | NAME = midiccrecorder 22 | 23 | # -------------------------------------------------------------- 24 | # Plugin types to build 25 | 26 | BUILD_LV2 ?= true 27 | BUILD_VST2 ?= true 28 | BUILD_JACK ?= false 29 | BUILD_DSSI ?= false 30 | BUILD_LADSPA ?= false 31 | 32 | # -------------------------------------------------------------- 33 | # Files to build 34 | 35 | FILES_DSP = \ 36 | PluginMIDICCRecorder.cpp 37 | 38 | # -------------------------------------------------------------- 39 | # Do some magic 40 | 41 | include ../../dpf/Makefile.plugins.mk 42 | 43 | # -------------------------------------------------------------- 44 | # Enable all selected plugin types 45 | 46 | ifeq ($(BUILD_LV2),true) 47 | ifeq ($(HAVE_DGL),true) 48 | TARGETS += lv2_sep 49 | else 50 | TARGETS += lv2_dsp 51 | endif 52 | endif 53 | 54 | ifeq ($(BUILD_VST2),true) 55 | TARGETS += vst 56 | endif 57 | 58 | ifeq ($(BUILD_JACK),true) 59 | ifeq ($(HAVE_JACK),true) 60 | TARGETS += jack 61 | endif 62 | endif 63 | 64 | ifeq ($(BUILD_DSSI),true) 65 | ifeq ($(HAVE_DGL),true) 66 | ifeq ($(HAVE_LIBLO),true) 67 | TARGETS += dssi 68 | endif 69 | endif 70 | endif 71 | 72 | ifeq ($(BUILD_LADSPA),true) 73 | TARGETS += ladspa 74 | endif 75 | 76 | all: $(TARGETS) 77 | 78 | install: all 79 | ifeq ($(BUILD_DSSI),true) 80 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(DESTDIR)$(DSSI_DIR) 81 | endif 82 | ifeq ($(BUILD_LADSPA),true) 83 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(DESTDIR)$(LADSPA_DIR) 84 | endif 85 | ifeq ($(BUILD_VST2),true) 86 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(DESTDIR)$(VST_DIR) 87 | endif 88 | ifeq ($(BUILD_LV2),true) 89 | @install -dm755 $(DESTDIR)$(LV2_DIR) && \ 90 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(DESTDIR)$(LV2_DIR) 91 | endif 92 | ifeq ($(BUILD_JACK),true) 93 | ifeq ($(HAVE_JACK),true) 94 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(DESTDIR)$(BINDIR) 95 | endif 96 | endif 97 | 98 | install-user: all 99 | ifeq ($(BUILD_DSSI),true) 100 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(HOME)/.dssi 101 | endif 102 | ifeq ($(BUILD_LADSPA),true) 103 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(HOME)/.ladspa 104 | endif 105 | ifeq ($(BUILD_VST2),true) 106 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(HOME)/.vst 107 | endif 108 | ifeq ($(BUILD_LV2),true) 109 | @install -dm755 $(HOME)/.lv2 && \ 110 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(HOME)/.lv2 111 | endif 112 | ifeq ($(BUILD_JACK),true) 113 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(HOME)/bin 114 | endif 115 | 116 | # -------------------------------------------------------------- 117 | 118 | .PHONY: all install install-user 119 | -------------------------------------------------------------------------------- /plugins/MIDICCRecorder/PluginMIDICCRecorder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI CCRecorder plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2019 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | // #include 28 | #include 29 | #include 30 | 31 | #include "PluginMIDICCRecorder.hpp" 32 | #include "extra/Base64.hpp" 33 | 34 | START_NAMESPACE_DISTRHO 35 | 36 | // ----------------------------------------------------------------------- 37 | 38 | PluginMIDICCRecorder::PluginMIDICCRecorder() 39 | : Plugin(paramCount, presetCount, stateCount), playing(false) 40 | { 41 | clearState(); 42 | loadProgram(0); 43 | } 44 | 45 | // ----------------------------------------------------------------------- 46 | // Init 47 | 48 | void PluginMIDICCRecorder::initParameter(uint32_t index, Parameter& parameter) { 49 | if (index >= paramCount) 50 | return; 51 | 52 | parameter.hints = kParameterIsAutomable | kParameterIsInteger; 53 | parameter.ranges.def = 0; 54 | parameter.ranges.min = 0; 55 | parameter.ranges.max = 1; 56 | 57 | switch (index) { 58 | case paramRecordEnable: 59 | parameter.name = "Record"; 60 | parameter.symbol = "rec_enable"; 61 | parameter.hints |= kParameterIsBoolean; 62 | break; 63 | case paramTrigClear: 64 | parameter.name = "Clear"; 65 | parameter.symbol = "trig_clear"; 66 | parameter.hints |= kParameterIsTrigger; 67 | break; 68 | case paramTrigSend: 69 | parameter.name = "Send"; 70 | parameter.symbol = "trig_send"; 71 | parameter.hints |= kParameterIsTrigger; 72 | break; 73 | case paramTrigTransport: 74 | parameter.name = "Trigger send on transport start?"; 75 | parameter.shortName = "Transport"; 76 | parameter.symbol = "trig_transport"; 77 | parameter.ranges.max = 2; 78 | parameter.enumValues.count = 3; 79 | parameter.enumValues.restrictedMode = true; 80 | { 81 | ParameterEnumerationValue* const channels = new ParameterEnumerationValue[3]; 82 | parameter.enumValues.values = channels; 83 | channels[0].label = "Disabled"; 84 | channels[0].value = 0; 85 | channels[1].label = "Always"; 86 | channels[1].value = 1; 87 | channels[2].label = "Only at Position 0"; 88 | channels[2].value = 2; 89 | } 90 | break; 91 | case paramTrigPCChannel: 92 | parameter.name = "Trigger send on PC?"; 93 | parameter.shortName = "Trigger by PC?"; 94 | parameter.symbol = "trig_pc_chan"; 95 | parameter.ranges.def = 17; 96 | parameter.ranges.max = 17; 97 | parameter.enumValues.count = 18; 98 | parameter.enumValues.restrictedMode = true; 99 | { 100 | ParameterEnumerationValue* const channels = new ParameterEnumerationValue[18]; 101 | parameter.enumValues.values = channels; 102 | channels[0].label = "Any channel"; 103 | channels[0].value = 0; 104 | channels[1].label = "Channel 1"; 105 | channels[1].value = 1; 106 | channels[2].label = "Channel 2"; 107 | channels[2].value = 2; 108 | channels[3].label = "Channel 3"; 109 | channels[3].value = 3; 110 | channels[4].label = "Channel 4"; 111 | channels[4].value = 4; 112 | channels[5].label = "Channel 5"; 113 | channels[5].value = 5; 114 | channels[6].label = "Channel 6"; 115 | channels[6].value = 6; 116 | channels[7].label = "Channel 7"; 117 | channels[7].value = 7; 118 | channels[8].label = "Channel 8"; 119 | channels[8].value = 8; 120 | channels[9].label = "Channel 9"; 121 | channels[9].value = 9; 122 | channels[10].label = "Channel 10"; 123 | channels[10].value = 10; 124 | channels[11].label = "Channel 11"; 125 | channels[11].value = 11; 126 | channels[12].label = "Channel 12"; 127 | channels[12].value = 12; 128 | channels[13].label = "Channel 13"; 129 | channels[13].value = 13; 130 | channels[14].label = "Channel 14"; 131 | channels[14].value = 14; 132 | channels[15].label = "Channel 15"; 133 | channels[15].value = 15; 134 | channels[16].label = "Channel 16"; 135 | channels[16].value = 16; 136 | channels[17].label = "Disabled"; 137 | channels[17].value = 17; 138 | } 139 | break; 140 | case paramTrigPC: 141 | parameter.name = "Program number"; 142 | parameter.symbol = "trig_pc"; 143 | parameter.ranges.max = 127; 144 | break; 145 | case paramSendChannel: 146 | parameter.name = "Send channel"; 147 | parameter.symbol = "send_chan"; 148 | parameter.ranges.max = 16; 149 | parameter.enumValues.count = 17; 150 | parameter.enumValues.restrictedMode = true; 151 | { 152 | ParameterEnumerationValue* const channels = new ParameterEnumerationValue[17]; 153 | parameter.enumValues.values = channels; 154 | channels[0].label = "All"; 155 | channels[0].value = 0; 156 | channels[1].label = "Channel 1"; 157 | channels[1].value = 1; 158 | channels[2].label = "Channel 2"; 159 | channels[2].value = 2; 160 | channels[3].label = "Channel 3"; 161 | channels[3].value = 3; 162 | channels[4].label = "Channel 4"; 163 | channels[4].value = 4; 164 | channels[5].label = "Channel 5"; 165 | channels[5].value = 5; 166 | channels[6].label = "Channel 6"; 167 | channels[6].value = 6; 168 | channels[7].label = "Channel 7"; 169 | channels[7].value = 7; 170 | channels[8].label = "Channel 8"; 171 | channels[8].value = 8; 172 | channels[9].label = "Channel 9"; 173 | channels[9].value = 9; 174 | channels[10].label = "Channel 10"; 175 | channels[10].value = 10; 176 | channels[11].label = "Channel 11"; 177 | channels[11].value = 11; 178 | channels[12].label = "Channel 12"; 179 | channels[12].value = 12; 180 | channels[13].label = "Channel 13"; 181 | channels[13].value = 13; 182 | channels[14].label = "Channel 14"; 183 | channels[14].value = 14; 184 | channels[15].label = "Channel 15"; 185 | channels[15].value = 15; 186 | channels[16].label = "Channel 16"; 187 | channels[16].value = 16; 188 | } 189 | break; 190 | case paramSendInterval: 191 | parameter.name = "Send interval (ms)"; 192 | parameter.unit = "ms"; 193 | parameter.symbol = "send_interval"; 194 | parameter.ranges.def = 1; 195 | parameter.ranges.min = 1; 196 | parameter.ranges.max = 200; 197 | break; 198 | } 199 | } 200 | 201 | /** 202 | Set the state key and default value of @a index. 203 | This function will be called once, shortly after the plugin is created. 204 | */ 205 | void PluginMIDICCRecorder::initState(uint32_t index, String& stateKey, String& defaultStateValue) { 206 | if (index < stateCount) { 207 | char key[6]; 208 | snprintf(key, 6, "ch-%02d", index); 209 | stateKey = key; 210 | defaultStateValue = "false"; 211 | } 212 | } 213 | 214 | /** 215 | Set the name of the program @a index. 216 | This function will be called once, shortly after the plugin is created. 217 | */ 218 | void PluginMIDICCRecorder::initProgramName(uint32_t index, String& programName) { 219 | if (index < presetCount) { 220 | programName = factoryPresets[index].name; 221 | } 222 | } 223 | 224 | // ----------------------------------------------------------------------- 225 | // Internal data 226 | 227 | /** 228 | Optional callback to inform the plugin about a sample rate change. 229 | */ 230 | void PluginMIDICCRecorder::sampleRateChanged(double newSampleRate) { 231 | fSampleRate = newSampleRate; 232 | } 233 | 234 | /** 235 | Get the current value of a parameter. 236 | */ 237 | float PluginMIDICCRecorder::getParameterValue(uint32_t index) const { 238 | return fParams[index]; 239 | } 240 | 241 | /** 242 | Change a parameter value. 243 | */ 244 | void PluginMIDICCRecorder::setParameterValue(uint32_t index, float value) { 245 | switch (index) { 246 | case paramRecordEnable: 247 | fParams[index] = CLAMP(value, 0, 1); 248 | break; 249 | case paramTrigClear: 250 | fParams[index] = CLAMP(value, 0, 1); 251 | 252 | if (fParams[index] > 0.0f) 253 | clearState(); 254 | 255 | break; 256 | case paramTrigSend: 257 | fParams[index] = CLAMP(value, 0, 1); 258 | 259 | if (fParams[index] > 0.0f) 260 | startSend(); 261 | 262 | break; 263 | case paramTrigTransport: 264 | fParams[index] = CLAMP(value, 0, 2); 265 | break; 266 | case paramTrigPCChannel: 267 | fParams[index] = CLAMP(value, 0, 17); 268 | break; 269 | case paramTrigPC: 270 | fParams[index] = CLAMP(value, 0, 127); 271 | break; 272 | case paramSendChannel: 273 | fParams[index] = CLAMP(value, 0, 16); 274 | break; 275 | case paramSendInterval: 276 | fParams[index] = CLAMP(value, 0, 200); 277 | break; 278 | } 279 | } 280 | 281 | /** 282 | Get the value of an internal state. 283 | The host may call this function from any non-realtime context. 284 | */ 285 | String PluginMIDICCRecorder::getState(const char* key) const { 286 | static const String sFalse("false"); 287 | int index; 288 | 289 | if (std::strncmp(key, "ch-", 3) == 0) { 290 | try { 291 | index = std::stoi(key+3); 292 | } catch (...) { 293 | return sFalse; 294 | } 295 | 296 | if (index < (int) stateCount) { 297 | return String::asBase64((char *) stateCC[index], 128); 298 | } 299 | } 300 | 301 | return sFalse; 302 | } 303 | 304 | 305 | /** 306 | Change an internal state. 307 | */ 308 | void PluginMIDICCRecorder::setState(const char* key, const char* value) { 309 | int index; 310 | 311 | if (std::strncmp(key, "ch-", 3) == 0) { 312 | try { 313 | index = std::stoi(key+3); 314 | } catch (...) { 315 | return; 316 | } 317 | 318 | if (index < (int) stateCount) { 319 | if (!std::strcmp(value, "false") == 0) { 320 | std::vector state = d_getChunkFromBase64String(value); 321 | 322 | for (uint i=0; i < state.size(); i++) { 323 | stateCC[index][i] = state[i]; 324 | } 325 | 326 | // std::cerr << "State 'ch-" << index << "': "; 327 | // for (uint i=0; i < 128; i++) { 328 | // std::cerr << (uint) stateCC[index][i] << ","; 329 | // } 330 | // std::cerr << std::endl; 331 | } 332 | } 333 | } 334 | } 335 | 336 | 337 | /** 338 | Clear all internal state. 339 | */ 340 | void PluginMIDICCRecorder::clearState() { 341 | for (uint i=0; i < 16; i++) { 342 | for (uint j=0; j < 128; j++) { 343 | stateCC[i][j] = 0xFF; 344 | } 345 | } 346 | } 347 | 348 | /** 349 | Load a program. 350 | The host may call this function from any context, 351 | including realtime processing. 352 | */ 353 | void PluginMIDICCRecorder::loadProgram(uint32_t index) { 354 | if (index < presetCount) { 355 | for (int i=0; i < paramCount; i++) { 356 | setParameterValue(i, factoryPresets[index].params[i]); 357 | } 358 | 359 | } 360 | } 361 | 362 | // ----------------------------------------------------------------------- 363 | // Process 364 | 365 | /* 366 | * Plugin is activated. 367 | */ 368 | void PluginMIDICCRecorder::activate() { 369 | fSampleRate = getSampleRate(); 370 | sendInProgress = false; 371 | curChan = 0; 372 | curCC = 0; 373 | } 374 | 375 | /* 376 | * Start sending. 377 | */ 378 | void PluginMIDICCRecorder::startSend() { 379 | if (!sendInProgress) { 380 | sendChannel = fParams[paramSendChannel]; 381 | curChan = 0; 382 | curCC = 0; 383 | } 384 | 385 | sendInProgress = true; 386 | } 387 | 388 | 389 | 390 | void PluginMIDICCRecorder::run(const float**, float**, uint32_t nframes, 391 | const MidiEvent* events, uint32_t eventCount) { 392 | uint8_t cc, chan, status; 393 | struct MidiEvent cc_event; 394 | bool block, start_send = false; 395 | static uint32_t next_frame = 0; 396 | 397 | const TimePosition& pos(getTimePosition()); 398 | uint8_t trig_pc = (uint8_t) fParams[paramTrigPC]; 399 | uint8_t trig_pc_chan = (uint8_t) fParams[paramTrigPCChannel]; 400 | 401 | for (uint32_t i=0; i next_frame) { 406 | next_frame = events[i].frame; 407 | } 408 | 409 | if (status >= 0xF0) { 410 | writeMidiEvent(events[i]); 411 | continue; 412 | } 413 | 414 | if (status < 0xF0) { 415 | chan = events[i].data[0] & 0x0F; 416 | 417 | if (status == MIDI_CONTROL_CHANGE) { 418 | if (sendInProgress && (sendChannel == 0 || sendChannel == chan + 1)) 419 | block = true; 420 | 421 | if (fParams[paramRecordEnable] && ! sendInProgress) { 422 | cc = events[i].data[1] & 0x7F; 423 | stateCC[chan][cc] = events[i].data[2] & 0x7F; 424 | } 425 | } 426 | else if (status == MIDI_PROGRAM_CHANGE && 427 | (trig_pc_chan == 0 || trig_pc_chan == chan + 1) && 428 | events[i].data[1] == trig_pc) { 429 | // trigger start of sending after MIDI events have been handled 430 | start_send = true; 431 | } 432 | } 433 | 434 | if (!block) writeMidiEvent(events[i]); 435 | } 436 | 437 | if (pos.playing and !playing) { 438 | playing = true; 439 | 440 | if (fParams[paramTrigTransport] == 1 || 441 | (fParams[paramTrigTransport] == 2 && pos.frame == 0)) { 442 | startSend(); 443 | } 444 | } 445 | else if (!pos.playing && playing) { 446 | playing = false; 447 | } 448 | 449 | if (start_send) { 450 | startSend(); 451 | } 452 | 453 | if (sendInProgress) { 454 | bool sendOk = true; 455 | 456 | do { 457 | if (next_frame >= nframes) { 458 | next_frame -= nframes; 459 | break; 460 | } 461 | 462 | if ((sendChannel == 0 || sendChannel == curChan + 1) && 463 | stateCC[curChan][curCC] != 0xFF) 464 | { 465 | cc_event.frame = next_frame; 466 | cc_event.size = 3; 467 | cc_event.data[0] = MIDI_CONTROL_CHANGE | (curChan & 0xF); 468 | cc_event.data[1] = curCC & 0x7F; 469 | cc_event.data[2] = stateCC[curChan][curCC] & 0x7F; 470 | sendOk = writeMidiEvent(cc_event); 471 | next_frame += cc_event.frame + (int) (fSampleRate / 1000 * fParams[paramSendInterval]); 472 | } 473 | 474 | curCC++; 475 | 476 | if (curCC >= NUM_CONTROLLERS) { 477 | curCC = 0; 478 | curChan++; 479 | 480 | if (curChan >= NUM_CHANNELS) { 481 | curChan = 0; 482 | sendInProgress = false; 483 | next_frame = 0; 484 | break; 485 | } 486 | } 487 | 488 | } while (sendOk); 489 | } 490 | } 491 | 492 | // ----------------------------------------------------------------------- 493 | 494 | Plugin* createPlugin() { 495 | return new PluginMIDICCRecorder(); 496 | } 497 | 498 | // ----------------------------------------------------------------------- 499 | 500 | END_NAMESPACE_DISTRHO 501 | -------------------------------------------------------------------------------- /plugins/MIDICCRecorder/PluginMIDICCRecorder.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI CCRecorder plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2019 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef PLUGIN_MIDICCRECORDER_H 28 | #define PLUGIN_MIDICCRECORDER_H 29 | 30 | #include "DistrhoPlugin.hpp" 31 | 32 | START_NAMESPACE_DISTRHO 33 | 34 | #ifndef MAP 35 | #define MAP(v, imin, imax, omin, omax) (((v) - (imin)) * ((omax) - (omin)) / ((imax) - (imin)) + (omin)) 36 | #endif 37 | 38 | #ifndef MIN 39 | #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) 40 | #endif 41 | 42 | #ifndef MAX 43 | #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) 44 | #endif 45 | 46 | #ifndef CLAMP 47 | #define CLAMP(v, min, max) (MIN((max), MAX((min), (v)))) 48 | #endif 49 | 50 | #define MIDI_CONTROL_CHANGE 0xB0 51 | #define MIDI_PROGRAM_CHANGE 0xC0 52 | #define NUM_CHANNELS 16 53 | #define NUM_CONTROLLERS 128 54 | 55 | // ----------------------------------------------------------------------- 56 | 57 | class PluginMIDICCRecorder : public Plugin { 58 | public: 59 | enum Parameters { 60 | paramRecordEnable = 0, 61 | paramTrigClear, 62 | paramTrigSend, 63 | paramTrigTransport, 64 | paramTrigPCChannel, 65 | paramTrigPC, 66 | paramSendChannel, 67 | paramSendInterval, 68 | paramCount 69 | }; 70 | 71 | PluginMIDICCRecorder(); 72 | 73 | protected: 74 | // ------------------------------------------------------------------- 75 | // Information 76 | 77 | const char* getLabel() const noexcept override { 78 | return "MIDICCRecorder"; 79 | } 80 | 81 | const char* getDescription() const override { 82 | return "Store received Control Change messages and replay them when triggered"; 83 | } 84 | 85 | const char* getMaker() const noexcept override { 86 | return "chrisarndt.de"; 87 | } 88 | 89 | const char* getHomePage() const override { 90 | return "https://chrisarndt.de/plugins/midiccrecorder"; 91 | } 92 | 93 | const char* getLicense() const noexcept override { 94 | return "https://spdx.org/licenses/MIT"; 95 | } 96 | 97 | uint32_t getVersion() const noexcept override { 98 | return d_version(0, 3, 1); 99 | } 100 | 101 | // Go to: 102 | // 103 | // http://service.steinberg.de/databases/plugin.nsf/plugIn 104 | // 105 | // Get a proper plugin UID and fill it in here! 106 | int64_t getUniqueId() const noexcept override { 107 | return d_cconst('M', 'C', 'c', 'r'); 108 | } 109 | 110 | // ------------------------------------------------------------------- 111 | // Init 112 | 113 | void initParameter(uint32_t index, Parameter& parameter) override; 114 | void initProgramName(uint32_t index, String& programName) override; 115 | void initState(uint32_t index, String& stateKey, String& defaultStateValue) override; 116 | 117 | // ------------------------------------------------------------------- 118 | // Internal data 119 | 120 | float getParameterValue(uint32_t index) const override; 121 | void setParameterValue(uint32_t index, float value) override; 122 | void loadProgram(uint32_t index) override; 123 | String getState(const char* key) const override; 124 | void setState(const char* key, const char* value) override; 125 | void clearState(); 126 | 127 | // ------------------------------------------------------------------- 128 | // Optional 129 | 130 | // Optional callback to inform the plugin about a sample rate change. 131 | void sampleRateChanged(double newSampleRate) override; 132 | 133 | // ------------------------------------------------------------------- 134 | // Process 135 | 136 | void activate() override; 137 | void startSend(); 138 | void run(const float**, float**, uint32_t, 139 | const MidiEvent* midiEvents, uint32_t midiEventCount) override; 140 | 141 | // ------------------------------------------------------------------- 142 | 143 | private: 144 | float fParams[paramCount]; 145 | double fSampleRate; 146 | uint8_t stateCC[NUM_CHANNELS][NUM_CONTROLLERS]; 147 | uint8_t curChan, curCC, sendChannel; 148 | bool playing, sendInProgress; 149 | 150 | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginMIDICCRecorder) 151 | }; 152 | 153 | struct Preset { 154 | const char* name; 155 | float params[PluginMIDICCRecorder::paramCount]; 156 | }; 157 | 158 | Preset factoryPresets[] = { 159 | { 160 | "Default", 161 | {0, 0, 0, 0, 17, 0, 0, 1.0} 162 | }, 163 | }; 164 | 165 | constexpr uint presetCount = sizeof(factoryPresets) / sizeof(Preset); 166 | constexpr uint stateCount = NUM_CHANNELS; 167 | 168 | // ----------------------------------------------------------------------- 169 | 170 | END_NAMESPACE_DISTRHO 171 | 172 | #endif // #ifndef PLUGIN_MIDICCRECORDER_H 173 | -------------------------------------------------------------------------------- /plugins/MIDICCToPressure/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI CC To Pressure plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2020 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef DISTRHO_PLUGIN_INFO_H 28 | #define DISTRHO_PLUGIN_INFO_H 29 | 30 | #define DISTRHO_PLUGIN_BRAND "chrisarndt.de" 31 | #define DISTRHO_PLUGIN_NAME "MIDI CC to Pressure" 32 | #define DISTRHO_PLUGIN_URI "https://chrisarndt.de/plugins/midicctopressure" 33 | #define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:MIDIPlugin" 34 | 35 | #define DISTRHO_PLUGIN_HAS_UI 0 36 | #define DISTRHO_UI_USE_NANOVG 0 37 | 38 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 39 | #define DISTRHO_PLUGIN_NUM_INPUTS 0 40 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 0 41 | #define DISTRHO_PLUGIN_WANT_TIMEPOS 0 42 | #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 43 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 44 | #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 45 | 46 | #endif // DISTRHO_PLUGIN_INFO_H 47 | -------------------------------------------------------------------------------- /plugins/MIDICCToPressure/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Makefile for DISTRHO Plugins # 3 | # ---------------------------- # 4 | # Created by falkTX, Christopher Arndt, and Patrick Desaulniers 5 | # 6 | 7 | # -------------------------------------------------------------- 8 | # Installation directories 9 | 10 | PREFIX ?= /usr/local 11 | BINDIR ?= $(PREFIX)/bin 12 | LIBDIR ?= $(PREFIX)/lib 13 | DSSI_DIR ?= $(LIBDIR)/dssi 14 | LADSPA_DIR ?= $(LIBDIR)/ladspa 15 | LV2_DIR ?= $(LIBDIR)/lv2 16 | VST_DIR ?= $(LIBDIR)/vst 17 | 18 | # -------------------------------------------------------------- 19 | # Project name, used for binaries 20 | 21 | NAME = midicctopressure 22 | 23 | # -------------------------------------------------------------- 24 | # Plugin types to build 25 | 26 | BUILD_LV2 ?= true 27 | BUILD_VST2 ?= true 28 | BUILD_JACK ?= false 29 | BUILD_DSSI ?= false 30 | BUILD_LADSPA ?= false 31 | 32 | # -------------------------------------------------------------- 33 | # Files to build 34 | 35 | FILES_DSP = \ 36 | PluginMIDICCToPressure.cpp 37 | 38 | # -------------------------------------------------------------- 39 | # Do some magic 40 | 41 | include ../../dpf/Makefile.plugins.mk 42 | 43 | # -------------------------------------------------------------- 44 | # Enable all selected plugin types 45 | 46 | ifeq ($(BUILD_LV2),true) 47 | ifeq ($(HAVE_DGL),true) 48 | TARGETS += lv2_sep 49 | else 50 | TARGETS += lv2_dsp 51 | endif 52 | endif 53 | 54 | ifeq ($(BUILD_VST2),true) 55 | TARGETS += vst 56 | endif 57 | 58 | ifeq ($(BUILD_JACK),true) 59 | ifeq ($(HAVE_JACK),true) 60 | TARGETS += jack 61 | endif 62 | endif 63 | 64 | ifeq ($(BUILD_DSSI),true) 65 | ifeq ($(HAVE_DGL),true) 66 | ifeq ($(HAVE_LIBLO),true) 67 | TARGETS += dssi 68 | endif 69 | endif 70 | endif 71 | 72 | ifeq ($(BUILD_LADSPA),true) 73 | TARGETS += ladspa 74 | endif 75 | 76 | all: $(TARGETS) 77 | 78 | install: all 79 | ifeq ($(BUILD_DSSI),true) 80 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(DESTDIR)$(DSSI_DIR) 81 | endif 82 | ifeq ($(BUILD_LADSPA),true) 83 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(DESTDIR)$(LADSPA_DIR) 84 | endif 85 | ifeq ($(BUILD_VST2),true) 86 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(DESTDIR)$(VST_DIR) 87 | endif 88 | ifeq ($(BUILD_LV2),true) 89 | @install -dm755 $(DESTDIR)$(LV2_DIR) && \ 90 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(DESTDIR)$(LV2_DIR) 91 | endif 92 | ifeq ($(BUILD_JACK),true) 93 | ifeq ($(HAVE_JACK),true) 94 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(DESTDIR)$(BINDIR) 95 | endif 96 | endif 97 | 98 | install-user: all 99 | ifeq ($(BUILD_DSSI),true) 100 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(HOME)/.dssi 101 | endif 102 | ifeq ($(BUILD_LADSPA),true) 103 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(HOME)/.ladspa 104 | endif 105 | ifeq ($(BUILD_VST2),true) 106 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(HOME)/.vst 107 | endif 108 | ifeq ($(BUILD_LV2),true) 109 | @install -dm755 $(HOME)/.lv2 && \ 110 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(HOME)/.lv2 111 | endif 112 | ifeq ($(BUILD_JACK),true) 113 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(HOME)/bin 114 | endif 115 | 116 | # -------------------------------------------------------------- 117 | 118 | .PHONY: all install install-user 119 | -------------------------------------------------------------------------------- /plugins/MIDICCToPressure/PluginMIDICCToPressure.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI CC To Pressure plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2020 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #include "PluginMIDICCToPressure.hpp" 28 | 29 | START_NAMESPACE_DISTRHO 30 | 31 | // ----------------------------------------------------------------------- 32 | 33 | PluginMIDICCToPressure::PluginMIDICCToPressure() 34 | : Plugin(paramCount, presetCount, 0) // 0 states 35 | { 36 | loadProgram(0); 37 | } 38 | 39 | // ----------------------------------------------------------------------- 40 | // Init 41 | 42 | void PluginMIDICCToPressure::initParameter(uint32_t index, Parameter& parameter) { 43 | if (index >= paramCount) 44 | return; 45 | 46 | parameter.hints = kParameterIsAutomable | kParameterIsInteger; 47 | parameter.ranges.def = 0; 48 | parameter.ranges.min = 0; 49 | parameter.ranges.max = 127; 50 | 51 | switch (index) { 52 | case paramFilterChannel: 53 | parameter.name = "Filter Channel"; 54 | parameter.symbol = "channelf"; 55 | parameter.ranges.def = 0; 56 | parameter.ranges.max = 16; 57 | parameter.enumValues.count = 17; 58 | parameter.enumValues.restrictedMode = true; 59 | { 60 | ParameterEnumerationValue* const channels = new ParameterEnumerationValue[17]; 61 | parameter.enumValues.values = channels; 62 | channels[0].label = "Any"; 63 | channels[0].value = 0; 64 | channels[1].label = "Channel 1"; 65 | channels[1].value = 1; 66 | channels[2].label = "Channel 2"; 67 | channels[2].value = 2; 68 | channels[3].label = "Channel 3"; 69 | channels[3].value = 3; 70 | channels[4].label = "Channel 4"; 71 | channels[4].value = 4; 72 | channels[5].label = "Channel 5"; 73 | channels[5].value = 5; 74 | channels[6].label = "Channel 6"; 75 | channels[6].value = 6; 76 | channels[7].label = "Channel 7"; 77 | channels[7].value = 7; 78 | channels[8].label = "Channel 8"; 79 | channels[8].value = 8; 80 | channels[9].label = "Channel 9"; 81 | channels[9].value = 9; 82 | channels[10].label = "Channel 10"; 83 | channels[10].value = 10; 84 | channels[11].label = "Channel 11"; 85 | channels[11].value = 11; 86 | channels[12].label = "Channel 12"; 87 | channels[12].value = 12; 88 | channels[13].label = "Channel 13"; 89 | channels[13].value = 13; 90 | channels[14].label = "Channel 14"; 91 | channels[14].value = 14; 92 | channels[15].label = "Channel 15"; 93 | channels[15].value = 15; 94 | channels[16].label = "Channel 16"; 95 | channels[16].value = 16; 96 | } 97 | break; 98 | case paramKeepOriginal: 99 | parameter.name = "Keep original source CC events"; 100 | parameter.shortName = "Keep original"; 101 | parameter.symbol = "keep_original"; 102 | parameter.hints |= kParameterIsBoolean; 103 | parameter.ranges.max = 1; 104 | break; 105 | case paramSrcCC: 106 | parameter.name = "Source CC"; 107 | parameter.symbol = "src_cc"; 108 | parameter.ranges.def = 1; 109 | break; 110 | } 111 | } 112 | 113 | /** 114 | Set the name of the program @a index. 115 | This function will be called once, shortly after the plugin is created. 116 | */ 117 | void PluginMIDICCToPressure::initProgramName(uint32_t index, String& programName) { 118 | if (index < presetCount) { 119 | programName = factoryPresets[index].name; 120 | } 121 | } 122 | 123 | // ----------------------------------------------------------------------- 124 | // Internal data 125 | 126 | /** 127 | Optional callback to inform the plugin about a sample rate change. 128 | */ 129 | void PluginMIDICCToPressure::sampleRateChanged(double newSampleRate) { 130 | (void) newSampleRate; 131 | } 132 | 133 | /** 134 | Get the current value of a parameter. 135 | */ 136 | float PluginMIDICCToPressure::getParameterValue(uint32_t index) const { 137 | return fParams[index]; 138 | } 139 | 140 | /** 141 | Change a parameter value. 142 | */ 143 | void PluginMIDICCToPressure::setParameterValue(uint32_t index, float value) { 144 | switch (index) { 145 | case paramFilterChannel: 146 | fParams[index] = CLAMP(value, 0.0f, 16.0f); 147 | filterChannel = (int8_t) fParams[index] - 1; 148 | break; 149 | case paramKeepOriginal: 150 | fParams[index] = CLAMP(value, 0.0f, 1.0f); 151 | break; 152 | case paramSrcCC: 153 | fParams[index] = CLAMP(value, 0.0f, 127.0f); 154 | break; 155 | } 156 | } 157 | 158 | /** 159 | Load a program. 160 | The host may call this function from any context, 161 | including realtime processing. 162 | */ 163 | void PluginMIDICCToPressure::loadProgram(uint32_t index) { 164 | if (index < presetCount) { 165 | for (int i=0; i < paramCount; i++) { 166 | setParameterValue(i, factoryPresets[index].params[i]); 167 | } 168 | 169 | } 170 | } 171 | 172 | // ----------------------------------------------------------------------- 173 | // Process 174 | 175 | void PluginMIDICCToPressure::activate() { 176 | // plugin is activated 177 | } 178 | 179 | 180 | void PluginMIDICCToPressure::run(const float**, float**, uint32_t, 181 | const MidiEvent* events, uint32_t eventCount) { 182 | uint8_t chan; 183 | struct MidiEvent cc_event; 184 | bool pass = true; 185 | 186 | for (uint32_t i=0; i 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef PLUGIN_MIDICCTOPRESSURE_H 28 | #define PLUGIN_MIDICCTOPRESSURE_H 29 | 30 | #include "DistrhoPlugin.hpp" 31 | 32 | START_NAMESPACE_DISTRHO 33 | 34 | #ifndef MIN 35 | #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) 36 | #endif 37 | 38 | #ifndef MAX 39 | #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) 40 | #endif 41 | 42 | #ifndef CLAMP 43 | #define CLAMP(v, min, max) (MIN((max), MAX((min), (v)))) 44 | #endif 45 | 46 | #define MIDI_CONTROL_CHANGE 0xB0 47 | #define MIDI_CHANNEL_PRESSURE 0xD0 48 | 49 | // ----------------------------------------------------------------------- 50 | 51 | class PluginMIDICCToPressure : public Plugin { 52 | public: 53 | enum Parameters { 54 | paramFilterChannel, 55 | paramKeepOriginal, 56 | paramSrcCC, 57 | paramCount 58 | }; 59 | 60 | PluginMIDICCToPressure(); 61 | 62 | protected: 63 | // ------------------------------------------------------------------- 64 | // Information 65 | 66 | const char* getLabel() const noexcept override { 67 | return "MIDICCToPressure"; 68 | } 69 | 70 | const char* getDescription() const override { 71 | return "Convert Control Change messages into monophonic Channel Pressure (Aftertouch)"; 72 | } 73 | 74 | const char* getMaker() const noexcept override { 75 | return "chrisarndt.de"; 76 | } 77 | 78 | const char* getHomePage() const override { 79 | return "https://chrisarndt.de/plugins/midicctopressure"; 80 | } 81 | 82 | const char* getLicense() const noexcept override { 83 | return "https://spdx.org/licenses/MIT"; 84 | } 85 | 86 | uint32_t getVersion() const noexcept override { 87 | return d_version(1, 0, 0); 88 | } 89 | 90 | // Go to: 91 | // 92 | // http://service.steinberg.de/databases/plugin.nsf/plugIn 93 | // 94 | // Get a proper plugin UID and fill it in here! 95 | int64_t getUniqueId() const noexcept override { 96 | return d_cconst('M', 'C', 't', 'A'); 97 | } 98 | 99 | // ------------------------------------------------------------------- 100 | // Init 101 | 102 | void initParameter(uint32_t index, Parameter& parameter) override; 103 | void initProgramName(uint32_t index, String& programName) override; 104 | 105 | // ------------------------------------------------------------------- 106 | // Internal data 107 | 108 | float getParameterValue(uint32_t index) const override; 109 | void setParameterValue(uint32_t index, float value) override; 110 | void loadProgram(uint32_t index) override; 111 | 112 | // ------------------------------------------------------------------- 113 | // Optional 114 | 115 | // Optional callback to inform the plugin about a sample rate change. 116 | void sampleRateChanged(double newSampleRate) override; 117 | 118 | // ------------------------------------------------------------------- 119 | // Process 120 | 121 | void activate() override; 122 | 123 | void run(const float**, float**, uint32_t, 124 | const MidiEvent* midiEvents, uint32_t midiEventCount) override; 125 | 126 | 127 | // ------------------------------------------------------------------- 128 | 129 | private: 130 | float fParams[paramCount]; 131 | int8_t filterChannel; 132 | 133 | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginMIDICCToPressure) 134 | }; 135 | 136 | struct Preset { 137 | const char* name; 138 | const float params[PluginMIDICCToPressure::paramCount]; 139 | }; 140 | 141 | const Preset factoryPresets[] = { 142 | { 143 | "Modulation", 144 | {0.0, 0.0, 1.0} 145 | }, 146 | { 147 | "Breath", 148 | {0.0, 0.0, 2.0} 149 | }, 150 | { 151 | "Foot Controller", 152 | {0.0, 0.0, 4.0} 153 | }, 154 | { 155 | "Expression", 156 | {0.0, 0.0, 11.0} 157 | } 158 | }; 159 | 160 | const uint presetCount = sizeof(factoryPresets) / sizeof(Preset); 161 | 162 | // ----------------------------------------------------------------------- 163 | 164 | END_NAMESPACE_DISTRHO 165 | 166 | #endif // #ifndef PLUGIN_MIDICCTOPRESSURE_H 167 | -------------------------------------------------------------------------------- /plugins/MIDIPBToCC/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI PBToCC plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2019 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef DISTRHO_PLUGIN_INFO_H 28 | #define DISTRHO_PLUGIN_INFO_H 29 | 30 | #define DISTRHO_PLUGIN_BRAND "chrisarndt.de" 31 | #define DISTRHO_PLUGIN_NAME "MIDI PB to CC" 32 | #define DISTRHO_PLUGIN_URI "https://chrisarndt.de/plugins/midipbtocc" 33 | #define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:MIDIPlugin" 34 | 35 | #define DISTRHO_PLUGIN_HAS_UI 0 36 | #define DISTRHO_UI_USE_NANOVG 0 37 | 38 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 39 | #define DISTRHO_PLUGIN_NUM_INPUTS 0 40 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 0 41 | #define DISTRHO_PLUGIN_WANT_TIMEPOS 0 42 | #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 43 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 44 | #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 45 | 46 | #endif // DISTRHO_PLUGIN_INFO_H 47 | -------------------------------------------------------------------------------- /plugins/MIDIPBToCC/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Makefile for DISTRHO Plugins # 3 | # ---------------------------- # 4 | # Created by falkTX, Christopher Arndt, and Patrick Desaulniers 5 | # 6 | 7 | # -------------------------------------------------------------- 8 | # Installation directories 9 | 10 | PREFIX ?= /usr/local 11 | BINDIR ?= $(PREFIX)/bin 12 | LIBDIR ?= $(PREFIX)/lib 13 | DSSI_DIR ?= $(LIBDIR)/dssi 14 | LADSPA_DIR ?= $(LIBDIR)/ladspa 15 | LV2_DIR ?= $(LIBDIR)/lv2 16 | VST_DIR ?= $(LIBDIR)/vst 17 | 18 | # -------------------------------------------------------------- 19 | # Project name, used for binaries 20 | 21 | NAME = midipbtocc 22 | 23 | # -------------------------------------------------------------- 24 | # Plugin types to build 25 | 26 | BUILD_LV2 ?= true 27 | BUILD_VST2 ?= true 28 | BUILD_JACK ?= false 29 | BUILD_DSSI ?= false 30 | BUILD_LADSPA ?= false 31 | 32 | # -------------------------------------------------------------- 33 | # Files to build 34 | 35 | FILES_DSP = \ 36 | PluginMIDIPBToCC.cpp 37 | 38 | # -------------------------------------------------------------- 39 | # Do some magic 40 | 41 | include ../../dpf/Makefile.plugins.mk 42 | 43 | # -------------------------------------------------------------- 44 | # Enable all selected plugin types 45 | 46 | ifeq ($(BUILD_LV2),true) 47 | ifeq ($(HAVE_DGL),true) 48 | TARGETS += lv2_sep 49 | else 50 | TARGETS += lv2_dsp 51 | endif 52 | endif 53 | 54 | ifeq ($(BUILD_VST2),true) 55 | TARGETS += vst 56 | endif 57 | 58 | ifeq ($(BUILD_JACK),true) 59 | ifeq ($(HAVE_JACK),true) 60 | TARGETS += jack 61 | endif 62 | endif 63 | 64 | ifeq ($(BUILD_DSSI),true) 65 | ifeq ($(HAVE_DGL),true) 66 | ifeq ($(HAVE_LIBLO),true) 67 | TARGETS += dssi 68 | endif 69 | endif 70 | endif 71 | 72 | ifeq ($(BUILD_LADSPA),true) 73 | TARGETS += ladspa 74 | endif 75 | 76 | all: $(TARGETS) 77 | 78 | install: all 79 | ifeq ($(BUILD_DSSI),true) 80 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(DESTDIR)$(DSSI_DIR) 81 | endif 82 | ifeq ($(BUILD_LADSPA),true) 83 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(DESTDIR)$(LADSPA_DIR) 84 | endif 85 | ifeq ($(BUILD_VST2),true) 86 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(DESTDIR)$(VST_DIR) 87 | endif 88 | ifeq ($(BUILD_LV2),true) 89 | @install -dm755 $(DESTDIR)$(LV2_DIR) && \ 90 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(DESTDIR)$(LV2_DIR) 91 | endif 92 | ifeq ($(BUILD_JACK),true) 93 | ifeq ($(HAVE_JACK),true) 94 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(DESTDIR)$(BINDIR) 95 | endif 96 | endif 97 | 98 | install-user: all 99 | ifeq ($(BUILD_DSSI),true) 100 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(HOME)/.dssi 101 | endif 102 | ifeq ($(BUILD_LADSPA),true) 103 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(HOME)/.ladspa 104 | endif 105 | ifeq ($(BUILD_VST2),true) 106 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(HOME)/.vst 107 | endif 108 | ifeq ($(BUILD_LV2),true) 109 | @install -dm755 $(HOME)/.lv2 && \ 110 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(HOME)/.lv2 111 | endif 112 | ifeq ($(BUILD_JACK),true) 113 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(HOME)/bin 114 | endif 115 | 116 | # -------------------------------------------------------------- 117 | 118 | .PHONY: all install install-user 119 | -------------------------------------------------------------------------------- /plugins/MIDIPBToCC/PluginMIDIPBToCC.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI PBToCC plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2019 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #include "PluginMIDIPBToCC.hpp" 28 | 29 | START_NAMESPACE_DISTRHO 30 | 31 | // ----------------------------------------------------------------------- 32 | 33 | PluginMIDIPBToCC::PluginMIDIPBToCC() 34 | : Plugin(paramCount, presetCount, 0) // 0 states 35 | { 36 | loadProgram(0); 37 | } 38 | 39 | // ----------------------------------------------------------------------- 40 | // Init 41 | 42 | void PluginMIDIPBToCC::initParameter(uint32_t index, Parameter& parameter) { 43 | if (index >= paramCount) 44 | return; 45 | 46 | parameter.hints = kParameterIsAutomable | kParameterIsInteger; 47 | parameter.ranges.def = 0; 48 | parameter.ranges.min = 0; 49 | parameter.ranges.max = 127; 50 | 51 | switch (index) { 52 | case paramFilterChannel: 53 | parameter.name = "Filter Channel"; 54 | parameter.symbol = "channelf"; 55 | parameter.ranges.def = 0; 56 | parameter.ranges.min = 0; 57 | parameter.ranges.max = 16; 58 | parameter.enumValues.count = 17; 59 | parameter.enumValues.restrictedMode = true; 60 | { 61 | ParameterEnumerationValue* const channels = new ParameterEnumerationValue[17]; 62 | parameter.enumValues.values = channels; 63 | channels[0].label = "Any"; 64 | channels[0].value = 0; 65 | channels[1].label = "Channel 1"; 66 | channels[1].value = 1; 67 | channels[2].label = "Channel 2"; 68 | channels[2].value = 2; 69 | channels[3].label = "Channel 3"; 70 | channels[3].value = 3; 71 | channels[4].label = "Channel 4"; 72 | channels[4].value = 4; 73 | channels[5].label = "Channel 5"; 74 | channels[5].value = 5; 75 | channels[6].label = "Channel 6"; 76 | channels[6].value = 6; 77 | channels[7].label = "Channel 7"; 78 | channels[7].value = 7; 79 | channels[8].label = "Channel 8"; 80 | channels[8].value = 8; 81 | channels[9].label = "Channel 9"; 82 | channels[9].value = 9; 83 | channels[10].label = "Channel 10"; 84 | channels[10].value = 10; 85 | channels[11].label = "Channel 11"; 86 | channels[11].value = 11; 87 | channels[12].label = "Channel 12"; 88 | channels[12].value = 12; 89 | channels[13].label = "Channel 13"; 90 | channels[13].value = 13; 91 | channels[14].label = "Channel 14"; 92 | channels[14].value = 14; 93 | channels[15].label = "Channel 15"; 94 | channels[15].value = 15; 95 | channels[16].label = "Channel 16"; 96 | channels[16].value = 16; 97 | } 98 | break; 99 | case paramKeepOriginal: 100 | parameter.name = "Keep original PB events"; 101 | parameter.shortName = "Keep PB"; 102 | parameter.symbol = "keep_original"; 103 | parameter.hints |= kParameterIsBoolean; 104 | parameter.ranges.max = 1; 105 | break; 106 | case paramPBMin: 107 | parameter.name = "PB min. value"; 108 | parameter.symbol = "pb_min"; 109 | parameter.ranges.def = -8192; 110 | parameter.ranges.min = -8192; 111 | parameter.ranges.max = 8191; 112 | break; 113 | case paramPBMax: 114 | parameter.name = "PB max. value"; 115 | parameter.symbol = "pb_max"; 116 | parameter.ranges.def = 8191; 117 | parameter.ranges.min = -8192; 118 | parameter.ranges.max = 8191; 119 | break; 120 | case paramCC1: 121 | parameter.name = "Pos. PB -> CC A"; 122 | parameter.symbol = "cc1"; 123 | parameter.ranges.def = 1; 124 | break; 125 | case paramCC1Min: 126 | parameter.name = "CC A min. value"; 127 | parameter.symbol = "cc1_min"; 128 | break; 129 | case paramCC1Max: 130 | parameter.name = "CC A max. value"; 131 | parameter.symbol = "cc1_max"; 132 | parameter.ranges.def = 127; 133 | break; 134 | case paramCC2: 135 | parameter.name = "Neg. PB -> CC B"; 136 | parameter.symbol = "cc2"; 137 | parameter.ranges.def = 1; 138 | break; 139 | case paramCC2Min: 140 | parameter.name = "CC B min. value"; 141 | parameter.symbol = "cc2_min"; 142 | break; 143 | case paramCC2Max: 144 | parameter.name = "CC B max. value"; 145 | parameter.symbol = "cc2_max"; 146 | parameter.ranges.def = 127; 147 | break; 148 | } 149 | } 150 | 151 | /** 152 | Set the name of the program @a index. 153 | This function will be called once, shortly after the plugin is created. 154 | */ 155 | void PluginMIDIPBToCC::initProgramName(uint32_t index, String& programName) { 156 | if (index < presetCount) { 157 | programName = factoryPresets[index].name; 158 | } 159 | } 160 | 161 | // ----------------------------------------------------------------------- 162 | // Internal data 163 | 164 | /** 165 | Optional callback to inform the plugin about a sample rate change. 166 | */ 167 | void PluginMIDIPBToCC::sampleRateChanged(double newSampleRate) { 168 | (void) newSampleRate; 169 | } 170 | 171 | /** 172 | Get the current value of a parameter. 173 | */ 174 | float PluginMIDIPBToCC::getParameterValue(uint32_t index) const { 175 | return fParams[index]; 176 | } 177 | 178 | /** 179 | Change a parameter value. 180 | */ 181 | void PluginMIDIPBToCC::setParameterValue(uint32_t index, float value) { 182 | switch (index) { 183 | case paramFilterChannel: 184 | fParams[index] = CLAMP(value, 0.0f, 16.0f); 185 | filterChannel = (int8_t) fParams[index] - 1; 186 | break; 187 | case paramKeepOriginal: 188 | fParams[index] = CLAMP(value, 0.0f, 1.0f); 189 | break; 190 | case paramPBMin: 191 | case paramPBMax: 192 | fParams[index] = CLAMP(value, -8192.0f, 8191.0f); 193 | break; 194 | case paramCC1: 195 | case paramCC1Min: 196 | case paramCC1Max: 197 | case paramCC2: 198 | case paramCC2Min: 199 | case paramCC2Max: 200 | fParams[index] = CLAMP(value, 0.0f, 127.0f); 201 | break; 202 | } 203 | } 204 | 205 | /** 206 | Load a program. 207 | The host may call this function from any context, 208 | including realtime processing. 209 | */ 210 | void PluginMIDIPBToCC::loadProgram(uint32_t index) { 211 | if (index < presetCount) { 212 | for (int i=0; i < paramCount; i++) { 213 | setParameterValue(i, factoryPresets[index].params[i]); 214 | } 215 | 216 | } 217 | } 218 | 219 | // ----------------------------------------------------------------------- 220 | // Process 221 | 222 | void PluginMIDIPBToCC::activate() { 223 | // plugin is activated 224 | } 225 | 226 | 227 | void PluginMIDIPBToCC::run(const float**, float**, uint32_t, 228 | const MidiEvent* events, uint32_t eventCount) { 229 | bool pass; 230 | uint8_t chan; 231 | int16_t pb_value, 232 | pb_min = (uint16_t) fParams[paramPBMin], 233 | pb_max = (uint16_t) fParams[paramPBMax]; 234 | struct MidiEvent cc_event; 235 | 236 | for (uint32_t i=0; i= 0) { 256 | cc_event.data[1] = (uint8_t) fParams[paramCC1]; 257 | 258 | if (pb_min <= pb_max) 259 | cc_event.data[2] = ((uint8_t) MAP(pb_value, 0, pb_max, fParams[paramCC1Min], fParams[paramCC1Max])) & 0x7f; 260 | else 261 | cc_event.data[2] = ((uint8_t) MAP(pb_value, pb_min, 8191, fParams[paramCC2Min], fParams[paramCC1Max])) & 0x7f; 262 | } 263 | else { 264 | cc_event.data[1] = (uint8_t) fParams[paramCC2]; 265 | 266 | if (pb_min <= pb_max) 267 | cc_event.data[2] = ((uint8_t) MAP(pb_value, -1, pb_min, fParams[paramCC2Min], fParams[paramCC2Max])) & 0x7f; 268 | else 269 | cc_event.data[2] = ((uint8_t) MAP(pb_value, pb_max, -8192, fParams[paramCC2Min], fParams[paramCC2Max])) & 0x7f; 270 | } 271 | 272 | writeMidiEvent(cc_event); 273 | } 274 | } 275 | 276 | if (pass) writeMidiEvent(events[i]); 277 | } 278 | } 279 | 280 | // ----------------------------------------------------------------------- 281 | 282 | Plugin* createPlugin() { 283 | return new PluginMIDIPBToCC(); 284 | } 285 | 286 | // ----------------------------------------------------------------------- 287 | 288 | END_NAMESPACE_DISTRHO 289 | -------------------------------------------------------------------------------- /plugins/MIDIPBToCC/PluginMIDIPBToCC.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI PBToCC plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2019 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef PLUGIN_MIDIPBTOCC_H 28 | #define PLUGIN_MIDIPBTOCC_H 29 | 30 | #include "DistrhoPlugin.hpp" 31 | 32 | START_NAMESPACE_DISTRHO 33 | 34 | #ifndef IN_RANGE 35 | #define IN_RANGE(v, min, max) ((min) <= (max) ? ((v) >= (min) && (v) <= (max)) : ((v) >= (min) || (v) <= (max))) 36 | #endif 37 | 38 | #ifndef MAP 39 | #define MAP(v, imin, imax, omin, omax) (((v) - (imin)) * ((omax) - (omin)) / ((imax) - (imin)) + (omin)) 40 | #endif 41 | 42 | #ifndef MIN 43 | #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) 44 | #endif 45 | 46 | #ifndef MAX 47 | #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) 48 | #endif 49 | 50 | #ifndef CLAMP 51 | #define CLAMP(v, min, max) (MIN((max), MAX((min), (v)))) 52 | #endif 53 | 54 | #define MIDI_CONTROL_CHANGE 0xB0 55 | #define MIDI_PITCH_BEND 0xE0 56 | 57 | // ----------------------------------------------------------------------- 58 | 59 | class PluginMIDIPBToCC : public Plugin { 60 | public: 61 | enum Parameters { 62 | paramFilterChannel, 63 | paramKeepOriginal, 64 | paramPBMin, 65 | paramPBMax, 66 | paramCC1, 67 | paramCC1Min, 68 | paramCC1Max, 69 | paramCC2, 70 | paramCC2Min, 71 | paramCC2Max, 72 | paramCount 73 | }; 74 | 75 | PluginMIDIPBToCC(); 76 | 77 | protected: 78 | // ------------------------------------------------------------------- 79 | // Information 80 | 81 | const char* getLabel() const noexcept override { 82 | return "MIDIPBToCC"; 83 | } 84 | 85 | const char* getDescription() const override { 86 | return "Convert Pitch Bend into Control Change messages"; 87 | } 88 | 89 | const char* getMaker() const noexcept override { 90 | return "chrisarndt.de"; 91 | } 92 | 93 | const char* getHomePage() const override { 94 | return "https://chrisarndt.de/plugins/midipbtocc"; 95 | } 96 | 97 | const char* getLicense() const noexcept override { 98 | return "https://spdx.org/licenses/MIT"; 99 | } 100 | 101 | uint32_t getVersion() const noexcept override { 102 | return d_version(1, 2, 3); 103 | } 104 | 105 | // Go to: 106 | // 107 | // http://service.steinberg.de/databases/plugin.nsf/plugIn 108 | // 109 | // Get a proper plugin UID and fill it in here! 110 | int64_t getUniqueId() const noexcept override { 111 | return d_cconst('M', 'P', 'b', 'C'); 112 | } 113 | 114 | // ------------------------------------------------------------------- 115 | // Init 116 | 117 | void initParameter(uint32_t index, Parameter& parameter) override; 118 | void initProgramName(uint32_t index, String& programName) override; 119 | 120 | // ------------------------------------------------------------------- 121 | // Internal data 122 | 123 | float getParameterValue(uint32_t index) const override; 124 | void setParameterValue(uint32_t index, float value) override; 125 | void loadProgram(uint32_t index) override; 126 | 127 | // ------------------------------------------------------------------- 128 | // Optional 129 | 130 | // Optional callback to inform the plugin about a sample rate change. 131 | void sampleRateChanged(double newSampleRate) override; 132 | 133 | // ------------------------------------------------------------------- 134 | // Process 135 | 136 | void activate() override; 137 | 138 | void run(const float**, float**, uint32_t, 139 | const MidiEvent* midiEvents, uint32_t midiEventCount) override; 140 | 141 | 142 | // ------------------------------------------------------------------- 143 | 144 | private: 145 | float fParams[paramCount]; 146 | int8_t filterChannel; 147 | 148 | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginMIDIPBToCC) 149 | }; 150 | 151 | struct Preset { 152 | const char* name; 153 | const float params[PluginMIDIPBToCC::paramCount]; 154 | }; 155 | 156 | const Preset factoryPresets[] = { 157 | { 158 | "Modulation +", 159 | {0.0, 0.0, -8192.0, 8191.0, 1.0, 64.0, 127.0, 1.0, 63.0, 0.0} 160 | }, 161 | { 162 | "Modulation +/-", 163 | {0.0, 0.0, -8192.0, 8191.0, 1.0, 0.0, 127.0, 1.0, 0.0, 127.0} 164 | }, 165 | { 166 | "PB + / Mod -", 167 | {0.0, 0.0, -8192.0, -1, 1.0, 0.0, 127.0, 1.0, 0.0, 127.0} 168 | } 169 | }; 170 | 171 | const uint presetCount = sizeof(factoryPresets) / sizeof(Preset); 172 | 173 | // ----------------------------------------------------------------------- 174 | 175 | END_NAMESPACE_DISTRHO 176 | 177 | #endif // #ifndef PLUGIN_MIDIPBTOCC_H 178 | -------------------------------------------------------------------------------- /plugins/MIDIPressureToCC/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI PressureToCC plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2020 Christopher Arndt , Jorik Jonker 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef DISTRHO_PLUGIN_INFO_H 28 | #define DISTRHO_PLUGIN_INFO_H 29 | 30 | #define DISTRHO_PLUGIN_BRAND "chrisarndt.de" 31 | #define DISTRHO_PLUGIN_NAME "MIDI Pressure to CC" 32 | #define DISTRHO_PLUGIN_URI "https://chrisarndt.de/plugins/midipressuretocc" 33 | #define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:MIDIPlugin" 34 | 35 | #define DISTRHO_PLUGIN_HAS_UI 0 36 | #define DISTRHO_UI_USE_NANOVG 0 37 | 38 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 39 | #define DISTRHO_PLUGIN_NUM_INPUTS 0 40 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 0 41 | #define DISTRHO_PLUGIN_WANT_TIMEPOS 0 42 | #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 43 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 44 | #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 45 | 46 | #endif // DISTRHO_PLUGIN_INFO_H 47 | -------------------------------------------------------------------------------- /plugins/MIDIPressureToCC/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Makefile for DISTRHO Plugins # 3 | # ---------------------------- # 4 | # Created by falkTX, Christopher Arndt, and Patrick Desaulniers 5 | # 6 | 7 | # -------------------------------------------------------------- 8 | # Installation directories 9 | 10 | PREFIX ?= /usr/local 11 | BINDIR ?= $(PREFIX)/bin 12 | LIBDIR ?= $(PREFIX)/lib 13 | DSSI_DIR ?= $(LIBDIR)/dssi 14 | LADSPA_DIR ?= $(LIBDIR)/ladspa 15 | LV2_DIR ?= $(LIBDIR)/lv2 16 | VST_DIR ?= $(LIBDIR)/vst 17 | 18 | # -------------------------------------------------------------- 19 | # Project name, used for binaries 20 | 21 | NAME = midipressuretocc 22 | 23 | # -------------------------------------------------------------- 24 | # Plugin types to build 25 | 26 | BUILD_LV2 ?= true 27 | BUILD_VST2 ?= true 28 | BUILD_JACK ?= false 29 | BUILD_DSSI ?= false 30 | BUILD_LADSPA ?= false 31 | 32 | # -------------------------------------------------------------- 33 | # Files to build 34 | 35 | FILES_DSP = \ 36 | PluginMIDIPressureToCC.cpp 37 | 38 | # -------------------------------------------------------------- 39 | # Do some magic 40 | 41 | include ../../dpf/Makefile.plugins.mk 42 | 43 | # -------------------------------------------------------------- 44 | # Enable all selected plugin types 45 | 46 | ifeq ($(BUILD_LV2),true) 47 | ifeq ($(HAVE_DGL),true) 48 | TARGETS += lv2_sep 49 | else 50 | TARGETS += lv2_dsp 51 | endif 52 | endif 53 | 54 | ifeq ($(BUILD_VST2),true) 55 | TARGETS += vst 56 | endif 57 | 58 | ifeq ($(BUILD_JACK),true) 59 | ifeq ($(HAVE_JACK),true) 60 | TARGETS += jack 61 | endif 62 | endif 63 | 64 | ifeq ($(BUILD_DSSI),true) 65 | ifeq ($(HAVE_DGL),true) 66 | ifeq ($(HAVE_LIBLO),true) 67 | TARGETS += dssi 68 | endif 69 | endif 70 | endif 71 | 72 | ifeq ($(BUILD_LADSPA),true) 73 | TARGETS += ladspa 74 | endif 75 | 76 | all: $(TARGETS) 77 | 78 | install: all 79 | ifeq ($(BUILD_DSSI),true) 80 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(DESTDIR)$(DSSI_DIR) 81 | endif 82 | ifeq ($(BUILD_LADSPA),true) 83 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(DESTDIR)$(LADSPA_DIR) 84 | endif 85 | ifeq ($(BUILD_VST2),true) 86 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(DESTDIR)$(VST_DIR) 87 | endif 88 | ifeq ($(BUILD_LV2),true) 89 | @install -dm755 $(DESTDIR)$(LV2_DIR) && \ 90 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(DESTDIR)$(LV2_DIR) 91 | endif 92 | ifeq ($(BUILD_JACK),true) 93 | ifeq ($(HAVE_JACK),true) 94 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(DESTDIR)$(BINDIR) 95 | endif 96 | endif 97 | 98 | install-user: all 99 | ifeq ($(BUILD_DSSI),true) 100 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(HOME)/.dssi 101 | endif 102 | ifeq ($(BUILD_LADSPA),true) 103 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(HOME)/.ladspa 104 | endif 105 | ifeq ($(BUILD_VST2),true) 106 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(HOME)/.vst 107 | endif 108 | ifeq ($(BUILD_LV2),true) 109 | @install -dm755 $(HOME)/.lv2 && \ 110 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(HOME)/.lv2 111 | endif 112 | ifeq ($(BUILD_JACK),true) 113 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(HOME)/bin 114 | endif 115 | 116 | # -------------------------------------------------------------- 117 | 118 | .PHONY: all install install-user 119 | -------------------------------------------------------------------------------- /plugins/MIDIPressureToCC/PluginMIDIPressureToCC.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI PressureToCC plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2020 Christopher Arndt , Jorik Jonker 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #include "PluginMIDIPressureToCC.hpp" 28 | 29 | START_NAMESPACE_DISTRHO 30 | 31 | // ----------------------------------------------------------------------- 32 | 33 | PluginMIDIPressureToCC::PluginMIDIPressureToCC() 34 | : Plugin(paramCount, presetCount, 0) // 0 states 35 | { 36 | loadProgram(0); 37 | } 38 | 39 | // ----------------------------------------------------------------------- 40 | // Init 41 | 42 | void PluginMIDIPressureToCC::initParameter(uint32_t index, Parameter& parameter) { 43 | if (index >= paramCount) 44 | return; 45 | 46 | parameter.hints = kParameterIsAutomable | kParameterIsInteger; 47 | parameter.ranges.def = 0; 48 | parameter.ranges.min = 0; 49 | parameter.ranges.max = 127; 50 | 51 | switch (index) { 52 | case paramFilterChannel: 53 | parameter.name = "Filter Channel"; 54 | parameter.symbol = "channelf"; 55 | parameter.ranges.def = 0; 56 | parameter.ranges.min = 0; 57 | parameter.ranges.max = 16; 58 | parameter.enumValues.count = 17; 59 | parameter.enumValues.restrictedMode = true; 60 | { 61 | ParameterEnumerationValue* const channels = new ParameterEnumerationValue[17]; 62 | parameter.enumValues.values = channels; 63 | channels[0].label = "Any"; 64 | channels[0].value = 0; 65 | channels[1].label = "Channel 1"; 66 | channels[1].value = 1; 67 | channels[2].label = "Channel 2"; 68 | channels[2].value = 2; 69 | channels[3].label = "Channel 3"; 70 | channels[3].value = 3; 71 | channels[4].label = "Channel 4"; 72 | channels[4].value = 4; 73 | channels[5].label = "Channel 5"; 74 | channels[5].value = 5; 75 | channels[6].label = "Channel 6"; 76 | channels[6].value = 6; 77 | channels[7].label = "Channel 7"; 78 | channels[7].value = 7; 79 | channels[8].label = "Channel 8"; 80 | channels[8].value = 8; 81 | channels[9].label = "Channel 9"; 82 | channels[9].value = 9; 83 | channels[10].label = "Channel 10"; 84 | channels[10].value = 10; 85 | channels[11].label = "Channel 11"; 86 | channels[11].value = 11; 87 | channels[12].label = "Channel 12"; 88 | channels[12].value = 12; 89 | channels[13].label = "Channel 13"; 90 | channels[13].value = 13; 91 | channels[14].label = "Channel 14"; 92 | channels[14].value = 14; 93 | channels[15].label = "Channel 15"; 94 | channels[15].value = 15; 95 | channels[16].label = "Channel 16"; 96 | channels[16].value = 16; 97 | } 98 | break; 99 | case paramKeepOriginal: 100 | parameter.name = "Keep original Pressure events"; 101 | parameter.shortName = "Keep original"; 102 | parameter.symbol = "keep_original"; 103 | parameter.hints |= kParameterIsBoolean; 104 | parameter.ranges.max = 1; 105 | break; 106 | case paramDestCC: 107 | parameter.name = "Destination CC"; 108 | parameter.symbol = "dest_cc"; 109 | parameter.ranges.def = 1; 110 | break; 111 | } 112 | } 113 | 114 | /** 115 | Set the name of the program @a index. 116 | This function will be called once, shortly after the plugin is created. 117 | */ 118 | void PluginMIDIPressureToCC::initProgramName(uint32_t index, String& programName) { 119 | if (index < presetCount) { 120 | programName = factoryPresets[index].name; 121 | } 122 | } 123 | 124 | // ----------------------------------------------------------------------- 125 | // Internal data 126 | 127 | /** 128 | Optional callback to inform the plugin about a sample rate change. 129 | */ 130 | void PluginMIDIPressureToCC::sampleRateChanged(double newSampleRate) { 131 | (void) newSampleRate; 132 | } 133 | 134 | /** 135 | Get the current value of a parameter. 136 | */ 137 | float PluginMIDIPressureToCC::getParameterValue(uint32_t index) const { 138 | return fParams[index]; 139 | } 140 | 141 | /** 142 | Change a parameter value. 143 | */ 144 | void PluginMIDIPressureToCC::setParameterValue(uint32_t index, float value) { 145 | switch (index) { 146 | case paramFilterChannel: 147 | fParams[index] = CLAMP(value, 0.0f, 16.0f); 148 | filterChannel = (int8_t) fParams[index] - 1; 149 | break; 150 | case paramKeepOriginal: 151 | fParams[index] = CLAMP(value, 0.0f, 1.0f); 152 | break; 153 | case paramDestCC: 154 | fParams[index] = CLAMP(value, 0.0f, 127.0f); 155 | break; 156 | } 157 | } 158 | 159 | /** 160 | Load a program. 161 | The host may call this function from any context, 162 | including realtime processing. 163 | */ 164 | void PluginMIDIPressureToCC::loadProgram(uint32_t index) { 165 | if (index < presetCount) { 166 | for (int i=0; i < paramCount; i++) { 167 | setParameterValue(i, factoryPresets[index].params[i]); 168 | } 169 | 170 | } 171 | } 172 | 173 | // ----------------------------------------------------------------------- 174 | // Process 175 | 176 | void PluginMIDIPressureToCC::activate() { 177 | // plugin is activated 178 | } 179 | 180 | 181 | void PluginMIDIPressureToCC::run(const float**, float**, uint32_t, 182 | const MidiEvent* events, uint32_t eventCount) { 183 | uint8_t chan; 184 | struct MidiEvent cc_event; 185 | 186 | for (uint32_t i=0; i, Jorik Jonker 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef PLUGIN_MIDIPRESSURETOCC_H 28 | #define PLUGIN_MIDIPRESSURETOCC_H 29 | 30 | #include "DistrhoPlugin.hpp" 31 | 32 | START_NAMESPACE_DISTRHO 33 | 34 | #ifndef MIN 35 | #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) 36 | #endif 37 | 38 | #ifndef MAX 39 | #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) 40 | #endif 41 | 42 | #ifndef CLAMP 43 | #define CLAMP(v, min, max) (MIN((max), MAX((min), (v)))) 44 | #endif 45 | 46 | #define MIDI_CONTROL_CHANGE 0xB0 47 | #define MIDI_CHANNEL_PRESSURE 0xD0 48 | 49 | // ----------------------------------------------------------------------- 50 | 51 | class PluginMIDIPressureToCC : public Plugin { 52 | public: 53 | enum Parameters { 54 | paramFilterChannel, 55 | paramKeepOriginal, 56 | paramDestCC, 57 | paramCount 58 | }; 59 | 60 | PluginMIDIPressureToCC(); 61 | 62 | protected: 63 | // ------------------------------------------------------------------- 64 | // Information 65 | 66 | const char* getLabel() const noexcept override { 67 | return "MIDIPressureToCC"; 68 | } 69 | 70 | const char* getDescription() const override { 71 | return "Convert monophonic Channel Pressure (Aftertouch) into Control Change messages"; 72 | } 73 | 74 | const char* getMaker() const noexcept override { 75 | return "chrisarndt.de"; 76 | } 77 | 78 | const char* getHomePage() const override { 79 | return "https://chrisarndt.de/plugins/midipressuretocc"; 80 | } 81 | 82 | const char* getLicense() const noexcept override { 83 | return "https://spdx.org/licenses/MIT"; 84 | } 85 | 86 | uint32_t getVersion() const noexcept override { 87 | return d_version(1, 0, 0); 88 | } 89 | 90 | // Go to: 91 | // 92 | // http://service.steinberg.de/databases/plugin.nsf/plugIn 93 | // 94 | // Get a proper plugin UID and fill it in here! 95 | int64_t getUniqueId() const noexcept override { 96 | return d_cconst('M', 'A', 't', 'C'); 97 | } 98 | 99 | // ------------------------------------------------------------------- 100 | // Init 101 | 102 | void initParameter(uint32_t index, Parameter& parameter) override; 103 | void initProgramName(uint32_t index, String& programName) override; 104 | 105 | // ------------------------------------------------------------------- 106 | // Internal data 107 | 108 | float getParameterValue(uint32_t index) const override; 109 | void setParameterValue(uint32_t index, float value) override; 110 | void loadProgram(uint32_t index) override; 111 | 112 | // ------------------------------------------------------------------- 113 | // Optional 114 | 115 | // Optional callback to inform the plugin about a sample rate change. 116 | void sampleRateChanged(double newSampleRate) override; 117 | 118 | // ------------------------------------------------------------------- 119 | // Process 120 | 121 | void activate() override; 122 | 123 | void run(const float**, float**, uint32_t, 124 | const MidiEvent* midiEvents, uint32_t midiEventCount) override; 125 | 126 | 127 | // ------------------------------------------------------------------- 128 | 129 | private: 130 | float fParams[paramCount]; 131 | int8_t filterChannel; 132 | 133 | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginMIDIPressureToCC) 134 | }; 135 | 136 | struct Preset { 137 | const char* name; 138 | const float params[PluginMIDIPressureToCC::paramCount]; 139 | }; 140 | 141 | const Preset factoryPresets[] = { 142 | { 143 | "Modulation", 144 | {0.0, 0.0, 1.0} 145 | }, 146 | { 147 | "Breath", 148 | {0.0, 0.0, 2.0} 149 | }, 150 | { 151 | "Foot Controller", 152 | {0.0, 0.0, 4.0} 153 | }, 154 | { 155 | "Expression", 156 | {0.0, 0.0, 11.0} 157 | } 158 | }; 159 | 160 | const uint presetCount = sizeof(factoryPresets) / sizeof(Preset); 161 | 162 | // ----------------------------------------------------------------------- 163 | 164 | END_NAMESPACE_DISTRHO 165 | 166 | #endif // #ifndef PLUGIN_MIDIPRESSURETOCC_H 167 | -------------------------------------------------------------------------------- /plugins/MIDISysFilter/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI SysFilter plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2019 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef DISTRHO_PLUGIN_INFO_H 28 | #define DISTRHO_PLUGIN_INFO_H 29 | 30 | #define DISTRHO_PLUGIN_BRAND "chrisarndt.de" 31 | #define DISTRHO_PLUGIN_NAME "MIDI Sys Filter" 32 | #define DISTRHO_PLUGIN_URI "https://chrisarndt.de/plugins/midisysfilter" 33 | #define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:MIDIPlugin" 34 | 35 | #define DISTRHO_PLUGIN_HAS_UI 0 36 | #define DISTRHO_UI_USE_NANOVG 0 37 | 38 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 39 | #define DISTRHO_PLUGIN_NUM_INPUTS 0 40 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 0 41 | #define DISTRHO_PLUGIN_WANT_TIMEPOS 0 42 | #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 43 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 44 | #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 45 | 46 | #endif // DISTRHO_PLUGIN_INFO_H 47 | -------------------------------------------------------------------------------- /plugins/MIDISysFilter/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Makefile for DISTRHO Plugins # 3 | # ---------------------------- # 4 | # Created by falkTX, Christopher Arndt, and Patrick Desaulniers 5 | # 6 | 7 | # -------------------------------------------------------------- 8 | # Installation directories 9 | 10 | PREFIX ?= /usr/local 11 | BINDIR ?= $(PREFIX)/bin 12 | LIBDIR ?= $(PREFIX)/lib 13 | DSSI_DIR ?= $(LIBDIR)/dssi 14 | LADSPA_DIR ?= $(LIBDIR)/ladspa 15 | LV2_DIR ?= $(LIBDIR)/lv2 16 | VST_DIR ?= $(LIBDIR)/vst 17 | 18 | # -------------------------------------------------------------- 19 | # Project name, used for binaries 20 | 21 | NAME = midisysfilter 22 | 23 | # -------------------------------------------------------------- 24 | # Plugin types to build 25 | 26 | BUILD_LV2 ?= true 27 | BUILD_VST2 ?= true 28 | BUILD_JACK ?= false 29 | BUILD_DSSI ?= false 30 | BUILD_LADSPA ?= false 31 | 32 | # -------------------------------------------------------------- 33 | # Files to build 34 | 35 | FILES_DSP = \ 36 | PluginMIDISysFilter.cpp 37 | 38 | # -------------------------------------------------------------- 39 | # Do some magic 40 | 41 | include ../../dpf/Makefile.plugins.mk 42 | 43 | # -------------------------------------------------------------- 44 | # Enable all selected plugin types 45 | 46 | ifeq ($(BUILD_LV2),true) 47 | ifeq ($(HAVE_DGL),true) 48 | TARGETS += lv2_sep 49 | else 50 | TARGETS += lv2_dsp 51 | endif 52 | endif 53 | 54 | ifeq ($(BUILD_VST2),true) 55 | TARGETS += vst 56 | endif 57 | 58 | ifeq ($(BUILD_JACK),true) 59 | ifeq ($(HAVE_JACK),true) 60 | TARGETS += jack 61 | endif 62 | endif 63 | 64 | ifeq ($(BUILD_DSSI),true) 65 | ifeq ($(HAVE_DGL),true) 66 | ifeq ($(HAVE_LIBLO),true) 67 | TARGETS += dssi 68 | endif 69 | endif 70 | endif 71 | 72 | ifeq ($(BUILD_LADSPA),true) 73 | TARGETS += ladspa 74 | endif 75 | 76 | all: $(TARGETS) 77 | 78 | install: all 79 | ifeq ($(BUILD_DSSI),true) 80 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(DESTDIR)$(DSSI_DIR) 81 | endif 82 | ifeq ($(BUILD_LADSPA),true) 83 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(DESTDIR)$(LADSPA_DIR) 84 | endif 85 | ifeq ($(BUILD_VST2),true) 86 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(DESTDIR)$(VST_DIR) 87 | endif 88 | ifeq ($(BUILD_LV2),true) 89 | @install -dm755 $(DESTDIR)$(LV2_DIR) && \ 90 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(DESTDIR)$(LV2_DIR) 91 | endif 92 | ifeq ($(BUILD_JACK),true) 93 | ifeq ($(HAVE_JACK),true) 94 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(DESTDIR)$(BINDIR) 95 | endif 96 | endif 97 | 98 | install-user: all 99 | ifeq ($(BUILD_DSSI),true) 100 | @install -Dm755 $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -t $(HOME)/.dssi 101 | endif 102 | ifeq ($(BUILD_LADSPA),true) 103 | @install -Dm755 $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -t $(HOME)/.ladspa 104 | endif 105 | ifeq ($(BUILD_VST2),true) 106 | @install -Dm755 $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -t $(HOME)/.vst 107 | endif 108 | ifeq ($(BUILD_LV2),true) 109 | @install -dm755 $(HOME)/.lv2 && \ 110 | cp -rf $(TARGET_DIR)/$(NAME).lv2 $(HOME)/.lv2 111 | endif 112 | ifeq ($(BUILD_JACK),true) 113 | @install -Dm755 $(TARGET_DIR)/$(NAME)$(APP_EXT) -t $(HOME)/bin 114 | endif 115 | 116 | # -------------------------------------------------------------- 117 | 118 | .PHONY: all install install-user 119 | -------------------------------------------------------------------------------- /plugins/MIDISysFilter/PluginMIDISysFilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI SysFilter plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2019 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #include "PluginMIDISysFilter.hpp" 28 | 29 | START_NAMESPACE_DISTRHO 30 | 31 | // ----------------------------------------------------------------------- 32 | 33 | PluginMIDISysFilter::PluginMIDISysFilter() 34 | : Plugin(paramCount, 12, 0) // paramCount params, 12 program(s), 0 states 35 | { 36 | loadProgram(0); 37 | } 38 | 39 | // ----------------------------------------------------------------------- 40 | // Init 41 | 42 | void PluginMIDISysFilter::initParameter(uint32_t index, Parameter& parameter) { 43 | if (index >= paramCount) 44 | return; 45 | 46 | parameter.hints = kParameterIsAutomable | kParameterIsBoolean | kParameterIsInteger; 47 | parameter.ranges.def = 0.0f; 48 | parameter.ranges.min = 0.0f; 49 | parameter.ranges.max = 1.0f; 50 | 51 | switch (index) { 52 | case paramFilterMode: 53 | parameter.name = "Filter Mode"; 54 | parameter.symbol = "filter_mode"; 55 | parameter.hints = kParameterIsAutomable | kParameterIsInteger; 56 | parameter.enumValues.count = 2; 57 | parameter.enumValues.restrictedMode = true; 58 | { 59 | ParameterEnumerationValue* const modes = new ParameterEnumerationValue[2]; 60 | parameter.enumValues.values = modes; 61 | modes[0].label = "Block disabled events (pass all others)"; 62 | modes[0].value = 0; 63 | modes[1].label = "Pass enabled events (block all others)"; 64 | modes[1].value = 1; 65 | } 66 | break; 67 | case paramSystemExclusive: 68 | parameter.name = "System Exclusive (F0)"; 69 | parameter.shortName = "F0: SysEx"; 70 | parameter.symbol = "sysex"; 71 | break; 72 | case paramMTCQuarterFrame: 73 | parameter.name = "MTC Quarter Frame (F1)"; 74 | parameter.shortName = "F1: MTC"; 75 | parameter.symbol = "mtc_quarter_frame"; 76 | break; 77 | case paramSongPositionPointer: 78 | parameter.name = "Song Position Pointer (F2)"; 79 | parameter.shortName = "F2: SPP"; 80 | parameter.symbol = "song_position_pointer"; 81 | break; 82 | case paramSongSelect: 83 | parameter.name = "Song Select (F3)"; 84 | parameter.shortName = "F3: SS"; 85 | parameter.symbol = "song_select"; 86 | break; 87 | case paramTuneRequest: 88 | parameter.name = "Tune Request (F6)"; 89 | parameter.shortName = "F6: Tune Req."; 90 | parameter.symbol = "tune_request"; 91 | break; 92 | case paramTimingClock: 93 | parameter.name = "Timing Clock (F8)"; 94 | parameter.shortName = "F8: Clock"; 95 | parameter.symbol = "timing_clock"; 96 | break; 97 | case paramStart: 98 | parameter.name = "Start (FA)"; 99 | parameter.shortName = "FA: Start"; 100 | parameter.symbol = "start"; 101 | break; 102 | case paramContinue: 103 | parameter.name = "Continue (FB)"; 104 | parameter.shortName = "FB: Cont."; 105 | parameter.symbol = "continue"; 106 | break; 107 | case paramStop: 108 | parameter.name = "Stop (FC)"; 109 | parameter.shortName = "FC: Stop"; 110 | parameter.symbol = "stop"; 111 | break; 112 | case paramActiveSensing: 113 | parameter.name = "Active Sensing (FE)"; 114 | parameter.shortName = "FE: Act. Sens."; 115 | parameter.symbol = "active_sensing"; 116 | break; 117 | case paramSystemReset: 118 | parameter.name = "System Reset (FF)"; 119 | parameter.shortName = "FF: Reset"; 120 | parameter.symbol = "system_reset"; 121 | break; 122 | case paramUndefined: 123 | parameter.name = "Undefined (F4/F5/F9/FD)"; 124 | parameter.shortName = "F4/F5/F9/FD"; 125 | parameter.symbol = "undefined"; 126 | break; 127 | } 128 | } 129 | 130 | /** 131 | Set the name of the program @a index. 132 | This function will be called once, shortly after the plugin is created. 133 | */ 134 | void PluginMIDISysFilter::initProgramName(uint32_t index, String& programName) { 135 | switch (index) { 136 | case 0: 137 | programName = "Pass-through"; 138 | break; 139 | case 1: 140 | programName = "Block any System message"; 141 | break; 142 | case 2: 143 | programName = "Block System Exclusive"; 144 | break; 145 | case 3: 146 | programName = "Block System Common"; 147 | break; 148 | case 4: 149 | programName = "Block System Real-Time"; 150 | break; 151 | case 5: 152 | programName = "Block Undefined"; 153 | break; 154 | case 6: 155 | programName = "Block Active Sensing"; 156 | break; 157 | case 7: 158 | programName = "Block Timing Clock"; 159 | break; 160 | case 8: 161 | programName = "Pass System messages only"; 162 | break; 163 | case 9: 164 | programName = "Pass System Exclusive only"; 165 | break; 166 | case 10: 167 | programName = "Pass System Common only"; 168 | break; 169 | case 11: 170 | programName = "Pass System Real-Time only"; 171 | break; 172 | } 173 | } 174 | 175 | // ----------------------------------------------------------------------- 176 | // Internal data 177 | 178 | /** 179 | Optional callback to inform the plugin about a sample rate change. 180 | */ 181 | void PluginMIDISysFilter::sampleRateChanged(double newSampleRate) { 182 | (void) newSampleRate; 183 | } 184 | 185 | /** 186 | Get the current value of a parameter. 187 | */ 188 | float PluginMIDISysFilter::getParameterValue(uint32_t index) const { 189 | return fParams[index]; 190 | } 191 | 192 | /** 193 | Change a parameter value. 194 | */ 195 | void PluginMIDISysFilter::setParameterValue(uint32_t index, float value) { 196 | fParams[index] = value; 197 | } 198 | 199 | /** 200 | Load a program. 201 | The host may call this function from any context, 202 | including realtime processing. 203 | */ 204 | void PluginMIDISysFilter::loadProgram(uint32_t index) { 205 | switch (index) { 206 | case 0: // Pass-through 207 | setParameterValue(paramFilterMode, 0.0f); 208 | for (int i=1; i MidiEvent::kDataSize && 321 | events[i].dataExt[0] == MIDI_SYSTEM_EXCLUSIVE) 322 | { 323 | pass = (bool) fParams[paramSystemExclusive]; 324 | } 325 | else { 326 | uint8_t status = events[i].data[0] & 0xFF; 327 | 328 | switch(status) { 329 | case MIDI_MTC_QUARTER_FRAME: 330 | pass = (bool) fParams[paramMTCQuarterFrame]; 331 | break; 332 | case MIDI_SONG_POSITION_POINTER: 333 | pass = (bool) fParams[paramSongPositionPointer]; 334 | break; 335 | case MIDI_SONG_SELECT: 336 | pass = (bool) fParams[paramSongSelect]; 337 | break; 338 | case MIDI_TUNE_REQUEST: 339 | pass = (bool) fParams[paramTuneRequest]; 340 | break; 341 | case MIDI_TIMING_CLOCK: 342 | pass = (bool) fParams[paramTimingClock]; 343 | break; 344 | case MIDI_START: 345 | pass = (bool) fParams[paramStart]; 346 | break; 347 | case MIDI_CONTINUE: 348 | pass = (bool) fParams[paramContinue]; 349 | break; 350 | case MIDI_STOP: 351 | pass = (bool) fParams[paramStop]; 352 | break; 353 | case MIDI_ACTIVE_SENSING: 354 | pass = (bool) fParams[paramActiveSensing]; 355 | break; 356 | case MIDI_SYSTEM_RESET: 357 | pass = (bool) fParams[paramSystemReset]; 358 | break; 359 | case MIDI_UNDEFINED_F4: 360 | case MIDI_UNDEFINED_F5: 361 | case MIDI_UNDEFINED_F9: 362 | case MIDI_UNDEFINED_FD: 363 | pass = (bool) fParams[paramUndefined]; 364 | break; 365 | default: 366 | pass = fParams[paramFilterMode] == 0 ? true : false; 367 | } 368 | } 369 | 370 | if (pass) writeMidiEvent(events[i]); 371 | } 372 | } 373 | 374 | // ----------------------------------------------------------------------- 375 | 376 | Plugin* createPlugin() { 377 | return new PluginMIDISysFilter(); 378 | } 379 | 380 | // ----------------------------------------------------------------------- 381 | 382 | END_NAMESPACE_DISTRHO 383 | -------------------------------------------------------------------------------- /plugins/MIDISysFilter/PluginMIDISysFilter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIDI SysFilter plugin based on DISTRHO Plugin Framework (DPF) 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * Copyright (C) 2019 Christopher Arndt 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef PLUGIN_MIDISYSFILTER_H 28 | #define PLUGIN_MIDISYSFILTER_H 29 | 30 | #include "DistrhoPlugin.hpp" 31 | 32 | START_NAMESPACE_DISTRHO 33 | 34 | #define MIDI_SYSTEM_EXCLUSIVE 0xF0 35 | #define MIDI_MTC_QUARTER_FRAME 0xF1 36 | #define MIDI_SONG_POSITION_POINTER 0xF2 37 | #define MIDI_SONG_SELECT 0xF3 38 | #define MIDI_UNDEFINED_F4 0xF4 39 | #define MIDI_UNDEFINED_F5 0xF5 40 | #define MIDI_TUNE_REQUEST 0xF6 41 | #define MIDI_END_OF_EXCLUSIVE 0xF7 42 | #define MIDI_TIMING_CLOCK 0xF8 43 | #define MIDI_UNDEFINED_F9 0xF9 44 | #define MIDI_START 0xFA 45 | #define MIDI_CONTINUE 0xFB 46 | #define MIDI_STOP 0xFC 47 | #define MIDI_UNDEFINED_FD 0xFD 48 | #define MIDI_ACTIVE_SENSING 0xFE 49 | #define MIDI_SYSTEM_RESET 0xFF 50 | 51 | // ----------------------------------------------------------------------- 52 | 53 | class PluginMIDISysFilter : public Plugin { 54 | public: 55 | enum Parameters { 56 | paramFilterMode, 57 | paramSystemExclusive, 58 | paramMTCQuarterFrame, 59 | paramSongPositionPointer, 60 | paramSongSelect, 61 | paramTuneRequest, 62 | paramTimingClock, 63 | paramStart, 64 | paramContinue, 65 | paramStop, 66 | paramActiveSensing, 67 | paramSystemReset, 68 | paramUndefined, 69 | paramCount 70 | }; 71 | 72 | PluginMIDISysFilter(); 73 | 74 | protected: 75 | // ------------------------------------------------------------------- 76 | // Information 77 | 78 | const char* getLabel() const noexcept override { 79 | return "MIDISysFilter"; 80 | } 81 | 82 | const char* getDescription() const override { 83 | return "A filter for MIDI System Messages"; 84 | } 85 | 86 | const char* getMaker() const noexcept override { 87 | return "chrisarndt.de"; 88 | } 89 | 90 | const char* getHomePage() const override { 91 | return "https://chrisarndt.de/plugins/midisysfilter"; 92 | } 93 | 94 | const char* getLicense() const noexcept override { 95 | return "https://spdx.org/licenses/MIT"; 96 | } 97 | 98 | uint32_t getVersion() const noexcept override { 99 | return d_version(1, 2, 0); 100 | } 101 | 102 | // Go to: 103 | // 104 | // http://service.steinberg.de/databases/plugin.nsf/plugIn 105 | // 106 | // Get a proper plugin UID and fill it in here! 107 | int64_t getUniqueId() const noexcept override { 108 | return d_cconst('M', 'S', 'y', 'F'); 109 | } 110 | 111 | // ------------------------------------------------------------------- 112 | // Init 113 | 114 | void initParameter(uint32_t index, Parameter& parameter) override; 115 | void initProgramName(uint32_t index, String& programName) override; 116 | 117 | // ------------------------------------------------------------------- 118 | // Internal data 119 | 120 | float getParameterValue(uint32_t index) const override; 121 | void setParameterValue(uint32_t index, float value) override; 122 | void loadProgram(uint32_t index) override; 123 | 124 | // ------------------------------------------------------------------- 125 | // Optional 126 | 127 | // Optional callback to inform the plugin about a sample rate change. 128 | void sampleRateChanged(double newSampleRate) override; 129 | 130 | // ------------------------------------------------------------------- 131 | // Process 132 | 133 | void activate() override; 134 | 135 | void run(const float**, float**, uint32_t, 136 | const MidiEvent* midiEvents, uint32_t midiEventCount) override; 137 | 138 | 139 | // ------------------------------------------------------------------- 140 | 141 | private: 142 | float fParams[paramCount]; 143 | 144 | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginMIDISysFilter) 145 | }; 146 | 147 | // ----------------------------------------------------------------------- 148 | 149 | END_NAMESPACE_DISTRHO 150 | 151 | #endif // #ifndef PLUGIN_MIDISYSFILTER_H 152 | -------------------------------------------------------------------------------- /screenshots/MIDICCMapX4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpotlightKid/midiomatic/574b5492c30fbe2f46193db0b310e7e5f5631f15/screenshots/MIDICCMapX4.png -------------------------------------------------------------------------------- /screenshots/MIDICCRecorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpotlightKid/midiomatic/574b5492c30fbe2f46193db0b310e7e5f5631f15/screenshots/MIDICCRecorder.png -------------------------------------------------------------------------------- /screenshots/MIDICCToPressure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpotlightKid/midiomatic/574b5492c30fbe2f46193db0b310e7e5f5631f15/screenshots/MIDICCToPressure.png -------------------------------------------------------------------------------- /screenshots/MIDIPBToCC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpotlightKid/midiomatic/574b5492c30fbe2f46193db0b310e7e5f5631f15/screenshots/MIDIPBToCC.png -------------------------------------------------------------------------------- /screenshots/MIDIPressureToCC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpotlightKid/midiomatic/574b5492c30fbe2f46193db0b310e7e5f5631f15/screenshots/MIDIPressureToCC.png -------------------------------------------------------------------------------- /screenshots/MIDISysFilter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpotlightKid/midiomatic/574b5492c30fbe2f46193db0b310e7e5f5631f15/screenshots/MIDISysFilter.png -------------------------------------------------------------------------------- /screenshots/allplugins-carla-800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpotlightKid/midiomatic/574b5492c30fbe2f46193db0b310e7e5f5631f15/screenshots/allplugins-carla-800.png -------------------------------------------------------------------------------- /screenshots/allplugins-carla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpotlightKid/midiomatic/574b5492c30fbe2f46193db0b310e7e5f5631f15/screenshots/allplugins-carla.png -------------------------------------------------------------------------------- /scripts/build-win32.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Preparation 6 | _FLAGS="-DPTW32_STATIC_LIB -Werror" 7 | _ARCH=i686-w64-mingw32 8 | _PREFIX="/usr/${_ARCH}" 9 | export PATH=${_PREFIX}/bin:$PATH 10 | export AR=${_ARCH}-ar 11 | export CC=${_ARCH}-gcc 12 | export CXX=${_ARCH}-g++ 13 | export PKG_CONFIG_PATH=${_PREFIX}/lib/pkgconfig 14 | export WIN32=true 15 | export CFLAGS="${_FLAGS}" 16 | export CXXFLAGS="${_FLAGS}" 17 | export CROSS_COMPILING=true 18 | 19 | # Start clean 20 | make clean > /dev/null 21 | make -C dpf clean > /dev/null 22 | rm -rf bin-w32 23 | 24 | # Build now 25 | make 26 | mv bin bin-w32 27 | -------------------------------------------------------------------------------- /scripts/build-win64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Preparation 6 | _FLAGS="-DPTW32_STATIC_LIB -Werror" 7 | _ARCH=x86_64-w64-mingw32 8 | _PREFIX="/usr/${_ARCH}" 9 | export PATH=${_PREFIX}/bin:$PATH 10 | export AR=${_ARCH}-ar 11 | export CC=${_ARCH}-gcc 12 | export CXX=${_ARCH}-g++ 13 | export PKG_CONFIG_PATH=${_PREFIX}/lib/pkgconfig 14 | export WIN32=true 15 | export WIN64=true 16 | export CFLAGS="${_FLAGS}" 17 | export CXXFLAGS="${_FLAGS}" 18 | export LDFLAGS="-static" 19 | export CROSS_COMPILING=true 20 | 21 | # Start clean 22 | make clean > /dev/null 23 | make -C dpf clean > /dev/null 24 | rm -rf bin-w64 25 | 26 | # Build now 27 | make 28 | #mv bin bin-w64 29 | -------------------------------------------------------------------------------- /scripts/create-git-archive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Create versioned gzipped tarball of current branch of git repo 4 | # including all sub-modules. 5 | 6 | set -e 7 | 8 | url=$(git remote get-url origin) 9 | project="${url##*/}" 10 | project="${project%.git}" 11 | #version="$(git describe --abbrev=0)$(git log -n 1 --pretty=format:"+%cd~git%h" --date=format:%Y%m%d master)" 12 | version="$(git describe --abbrev=0)" 13 | version="${version#v}" 14 | 15 | # https://github.com/Kentzo/git-archive-all (install with e.g. `pipx install git-archive-all`) 16 | git-archive-all --verbose --prefix "$project-$version/" $project-$version.tar 17 | gzip "$project-$version.tar" 18 | rm -rf "$project-$version.tar" 19 | --------------------------------------------------------------------------------