├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── Vagrantfile ├── common ├── Label.hpp ├── Lerp.hpp ├── MetadataGenerator.cpp ├── ToggledValue.hpp └── Version.hpp ├── plugins ├── Crush │ ├── BitrotCrush.cpp │ └── DistrhoPluginInfo.h ├── Repeat │ ├── BitrotRepeat.cpp │ └── DistrhoPluginInfo.h ├── Reverser │ ├── BitrotReverser.cpp │ └── DistrhoPluginInfo.h ├── Tapestop │ ├── BitrotTapestop.cpp │ └── DistrhoPluginInfo.h ├── ttlgen.py └── wscript ├── waf └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .*waf* 3 | .vagrant/* 4 | .default_env 5 | .vscode 6 | waf3-*/* 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "DPF"] 2 | path = DPF 3 | url = https://github.com/grejppi/DPF.git 4 | branch = custom 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Bitrot 0.7 2 | ---------- 3 | 4 | This is an early version of what will become Bitrot 1.0. 5 | As the code has been rewritten there might be unforeseen bugs, so please [report](https://github.com/grejppi/bitrot/issues) any new bugs you encounter! 😊 6 | 7 | ## How to build 8 | 9 | ### on Linux 10 | 11 | ``` 12 | ./waf configure [--platform {linux32|linux64|win32|win64}] 13 | ./waf 14 | ``` 15 | 16 | You will find the built plugins in the `build/` folder. 17 | 18 | ### anywhere else 19 | 20 | If you are not running Linux, or want to build the software in 21 | an isolated environment, you can build it in a virtual machine. 22 | 23 | First, please download and install: 24 | * [Vagrant](https://www.vagrantup.com/) 25 | * [VirtualBox](https://www.virtualbox.org/) 26 | 27 | After you have installed those, open a command line in the repository 28 | root and run these commands: 29 | 30 | ``` 31 | vagrant up 32 | vagrant ssh 33 | ``` 34 | 35 | This will initialize the virtual machine, which will take time, 36 | and then open an SSH session in the virtual machine. 37 | Once the session is open, please run: 38 | 39 | ``` 40 | cd /vagrant 41 | ./waf configure --platform {linux32|linux64|win32|win64} 42 | ./waf 43 | ``` 44 | 45 | You will find the built plugins in the `build/` folder. 46 | 47 | Finally you may shut down the virtual machine using 48 | 49 | ``` 50 | vagrant halt 51 | ``` 52 | 53 | or 54 | 55 | ``` 56 | vagrant destroy 57 | ``` 58 | 59 | if you are never going to need it again. 60 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "bento/ubuntu-16.04" 3 | 4 | config.vm.provision "shell", inline: <<-EOF 5 | apt-get install -y g++ g++-multilib mingw-w64 mingw-w64-x86-64-dev mingw-w64-i686-dev g++-mingw-w64-i686 g++-mingw-w64-x86-64 6 | EOF 7 | end 8 | -------------------------------------------------------------------------------- /common/Label.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(DISTRHO_PLUGIN_TARGET_VST) 4 | #define LABEL(X) DISTRHO_PLUGIN_NAME 5 | #else 6 | #define LABEL(X) X 7 | #endif 8 | -------------------------------------------------------------------------------- /common/Lerp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Lerp { 6 | float a; 7 | float b; 8 | float nframes; 9 | 10 | float operator[](float f) { 11 | if (nframes == 0.f) { 12 | nframes = 1.f; 13 | } 14 | float fac = (f / nframes); 15 | return a + ((b - a) * fac); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /common/MetadataGenerator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "DistrhoPlugin.hpp" 7 | #include "src/DistrhoPluginInternal.hpp" 8 | #include "DistrhoPluginInfo.h" 9 | 10 | 11 | START_NAMESPACE_DISTRHO 12 | Plugin* createPlugin(); 13 | END_NAMESPACE_DISTRHO 14 | 15 | 16 | #define BOOL(WHOMST) Syntax().keyword((WHOMST) ? "true" : "false") 17 | 18 | 19 | enum Token { 20 | Colon, 21 | Comma, 22 | PopArray, 23 | PopObject, 24 | PushArray, 25 | PushObject, 26 | Whatever, 27 | }; 28 | 29 | 30 | class Syntax { 31 | public: 32 | std::deque tokens; 33 | std::deque strings; 34 | 35 | Syntax() {} 36 | Syntax(const Syntax&) = delete; 37 | virtual ~Syntax() {} 38 | 39 | void output() { 40 | while (tokens.size() != 0) { 41 | Token token = tokens.front(); 42 | switch (token) { 43 | case Colon: std::cout << ":"; break; 44 | case Comma: std::cout << ","; break; 45 | case PopArray: std::cout << "]"; break; 46 | case PopObject: std::cout << "}"; break; 47 | case PushArray: std::cout << "["; break; 48 | case PushObject: std::cout << "{"; break; 49 | case Whatever: std::cout << strings.front(); strings.pop_front(); break; 50 | } 51 | tokens.pop_front(); 52 | } 53 | } 54 | 55 | Syntax& merge(const Syntax& other) { 56 | tokens.insert(tokens.end(), other.tokens.begin(), other.tokens.end()); 57 | strings.insert(strings.end(), other.strings.begin(), other.strings.end()); 58 | return *this; 59 | } 60 | 61 | Syntax& object(class Object& object); 62 | Syntax& array(class Array& array); 63 | Syntax& string(std::string string); 64 | Syntax& number(double number); 65 | Syntax& keyword(std::string keyword); 66 | }; 67 | 68 | 69 | class Array : public Syntax { 70 | public: 71 | Array() {} 72 | Array(const Array&) = delete; 73 | virtual ~Array() {} 74 | 75 | Array& item(Syntax& item) { 76 | if (tokens.size() != 0) { 77 | tokens.push_back(Comma); 78 | } 79 | merge(item); 80 | return *this; 81 | } 82 | }; 83 | 84 | 85 | class Object : public Syntax { 86 | public: 87 | Object() {} 88 | Object(const Object&) = delete; 89 | virtual ~Object() {} 90 | 91 | Object& item(std::string key, Syntax& item) { 92 | if (tokens.size() != 0) { 93 | tokens.push_back(Comma); 94 | } 95 | merge(Syntax().string(key)); 96 | tokens.push_back(Colon); 97 | merge(item); 98 | return *this; 99 | } 100 | }; 101 | 102 | 103 | Syntax& Syntax::object(Object& object) { 104 | tokens.push_back(PushObject); 105 | merge(object); 106 | tokens.push_back(PopObject); 107 | return *this; 108 | } 109 | 110 | 111 | Syntax& Syntax::array(Array& array) { 112 | tokens.push_back(PushArray); 113 | merge(array); 114 | tokens.push_back(PopArray); 115 | return *this; 116 | } 117 | 118 | 119 | Syntax& Syntax::string(std::string string) { 120 | tokens.push_back(Whatever); 121 | strings.push_back(std::string("\"") + string + std::string("\"")); 122 | return *this; 123 | } 124 | 125 | 126 | Syntax& Syntax::number(double number) { 127 | tokens.push_back(Whatever); 128 | strings.push_back(std::to_string(number)); 129 | return *this; 130 | } 131 | 132 | 133 | Syntax& Syntax::keyword(std::string keyword) { 134 | tokens.push_back(Whatever); 135 | strings.push_back(keyword); 136 | return *this; 137 | } 138 | 139 | 140 | int main(int argc, char** argv) { 141 | std::setlocale(LC_ALL, "C"); 142 | 143 | d_lastBufferSize = 256; 144 | d_lastSampleRate = 44100.0; 145 | 146 | PluginExporter plugin; 147 | 148 | Object obj; 149 | obj.item("uri", Syntax().string(DISTRHO_PLUGIN_URI)); 150 | obj.item("name", Syntax().string(DISTRHO_PLUGIN_NAME)); 151 | obj.item("dllname", Syntax().string(BITROT_BINARY_NAME)); 152 | obj.item("ttlname", Syntax().string(BITROT_TTL_NAME)); 153 | #if defined(DISTRHO_PLUGIN_REPLACED_URI) 154 | obj.item("replaced_uri", Syntax().string(DISTRHO_PLUGIN_REPLACED_URI)); 155 | #else 156 | obj.item("replaced_uri", Syntax().keyword("null")); 157 | #endif 158 | obj.item("description", Syntax().string(plugin.getDescription())); 159 | obj.item("license", Syntax().string(plugin.getLicense())); 160 | obj.item("maker", Syntax().string(plugin.getMaker())); 161 | obj.item("is_rt_safe", BOOL(DISTRHO_PLUGIN_IS_RT_SAFE)); 162 | 163 | Object version; 164 | version.item("major", Syntax().number(BITROT_VERSION_MAJOR)); 165 | version.item("minor", Syntax().number(BITROT_VERSION_MINOR)); 166 | version.item("micro", Syntax().number(BITROT_VERSION_MICRO)); 167 | obj.item("version", Syntax().object(version)); 168 | 169 | Array params; 170 | for (uint32_t i = 0; i < plugin.getParameterCount(); ++i) { 171 | Object param; 172 | 173 | param.item( 174 | "direction", 175 | Syntax().string( 176 | plugin.isParameterOutput(i) ? "output" : "input" 177 | ) 178 | ); 179 | 180 | param.item("symbol", Syntax().string( 181 | plugin.getParameterSymbol(i).buffer())); 182 | param.item("name", Syntax().string( 183 | plugin.getParameterName(i).buffer())); 184 | 185 | const ParameterRanges& ranges(plugin.getParameterRanges(i)); 186 | param.item("minimum", Syntax().number(ranges.min)); 187 | param.item("maximum", Syntax().number(ranges.max)); 188 | param.item("default", Syntax().number(ranges.def)); 189 | 190 | const uint32_t hints(plugin.getParameterHints(i)); 191 | param.item("trigger", BOOL( 192 | (hints & kParameterIsTrigger) == kParameterIsTrigger)); 193 | param.item("toggled", BOOL(hints & kParameterIsBoolean)); 194 | param.item("integer", BOOL(hints & kParameterIsInteger)); 195 | param.item("logarithmic", BOOL(hints & kParameterIsLogarithmic)); 196 | param.item("automatable", BOOL(hints & kParameterIsAutomatable)); 197 | 198 | params.item(Syntax().object(param)); 199 | } 200 | obj.item("params", Syntax().array(params)); 201 | 202 | Syntax().object(obj).output(); 203 | std::cout << std::endl; 204 | 205 | return EXIT_SUCCESS; 206 | } 207 | -------------------------------------------------------------------------------- /common/ToggledValue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(DISTRHO_PLUGIN_TARGET_VST) 4 | #define TOGGLED_VALUE_THRESHOLD 0.5f 5 | #else 6 | #define TOGGLED_VALUE_THRESHOLD 1.f 7 | #endif 8 | 9 | static inline bool toggledValue(float v) { 10 | return v >= TOGGLED_VALUE_THRESHOLD; 11 | } 12 | 13 | #undef TOGGLED_VALUE_THRESHOLD 14 | -------------------------------------------------------------------------------- /common/Version.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define BITROT_VERSION() d_version( \ 4 | BITROT_VERSION_MAJOR, \ 5 | BITROT_VERSION_MINOR, \ 6 | BITROT_VERSION_MICRO \ 7 | ) 8 | -------------------------------------------------------------------------------- /plugins/Crush/BitrotCrush.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BitrotCrush.cpp 3 | * 4 | * Copyright 2013-2018 Henna Haahti 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "DistrhoPlugin.hpp" 20 | #include "Label.hpp" 21 | #include "Lerp.hpp" 22 | #include "Version.hpp" 23 | 24 | #include 25 | 26 | #include "DistrhoPluginMain.cpp" 27 | 28 | 29 | START_NAMESPACE_DISTRHO 30 | 31 | class BitrotCrush : public Plugin { 32 | static constexpr uint32_t NUM_PARAMS = 6; 33 | 34 | struct LerpParams { 35 | float noisebias; 36 | float prenoise; 37 | float postnoise; 38 | float distort; 39 | float postclip; 40 | }; 41 | 42 | struct { 43 | float downsample; 44 | LerpParams current; 45 | LerpParams old; 46 | } params; 47 | 48 | std::linear_congruential_engine rng; 49 | 50 | float lcache; 51 | float rcache; 52 | uint32_t sampleCounter; 53 | 54 | 55 | // Math helpers 56 | // ------------ 57 | 58 | static float softClip(float x, float amount, float boost) { 59 | float y(x); 60 | y *= 1.f - amount; 61 | y += amount * boost * rationalTanh(x); 62 | return y; 63 | } 64 | 65 | float applyNoise(float x, float amount, float bias) { 66 | float noise((rng() / (float) 0xffffffff) - bias); 67 | float y(x); 68 | y *= 1.f - amount; 69 | y += amount * (x + (x * x * noise)); 70 | return y; 71 | } 72 | 73 | 74 | // rational tanh approximation 75 | // by cschueler 76 | // 77 | // http://www.musicdsp.org/showone.php?id=238 78 | static float rationalTanh(float x) { 79 | if (x < -3) { 80 | return -1; 81 | } else if (x > 3) { 82 | return 1; 83 | } else { 84 | return x * (27 + x * x) / (27 + 9 * x * x); 85 | } 86 | } 87 | 88 | void reset() { 89 | for (uint32_t i = 0; i < NUM_PARAMS; ++i) { 90 | Parameter p; 91 | initParameter(i, p); 92 | setParameterValue(i, p.ranges.def); 93 | } 94 | params.old = params.current; 95 | } 96 | 97 | public: 98 | BitrotCrush() : Plugin(NUM_PARAMS, 0, 0) { 99 | reset(); 100 | activate(); 101 | } 102 | 103 | protected: 104 | const char* getLabel() const override { 105 | return LABEL("crush"); 106 | } 107 | 108 | const char* getDescription() const override { 109 | return "Sample rate reduction"; 110 | } 111 | 112 | const char* getMaker() const override { 113 | return "grejppi"; 114 | } 115 | 116 | const char* getLicense() const override { 117 | return "Apache-2.0"; 118 | } 119 | 120 | uint32_t getVersion() const override { 121 | return BITROT_VERSION(); 122 | } 123 | 124 | int64_t getUniqueId() const override { 125 | return 269; 126 | } 127 | 128 | void initParameter(uint32_t index, Parameter& p) override { 129 | switch (index) { 130 | case 0: 131 | p.hints = kParameterIsAutomatable | kParameterIsInteger; 132 | p.name = "Downsample"; 133 | p.symbol = "downsample"; 134 | 135 | p.ranges.min = 1.f; 136 | p.ranges.max = 16.f; 137 | p.ranges.def = 1.f; 138 | break; 139 | case 1: 140 | p.hints = kParameterIsAutomatable; 141 | p.name = "Noise Bias"; 142 | p.symbol = "noisebias"; 143 | 144 | p.ranges.min = 0.f; 145 | p.ranges.max = 1.f; 146 | p.ranges.def = 0.5f; 147 | break; 148 | case 2: 149 | p.hints = kParameterIsAutomatable; 150 | p.name = "Input Noise"; 151 | p.symbol = "prenoise"; 152 | 153 | p.ranges.min = 0.f; 154 | p.ranges.max = 1.0f; 155 | p.ranges.def = 0.f; 156 | break; 157 | case 3: 158 | p.hints = kParameterIsAutomatable; 159 | p.name = "Output Noise"; 160 | p.symbol = "postnoise"; 161 | 162 | p.ranges.min = 0.f; 163 | p.ranges.max = 1.0f; 164 | p.ranges.def = 0.f; 165 | break; 166 | case 4: 167 | p.hints = kParameterIsAutomatable; 168 | p.name = "Distort"; 169 | p.symbol = "distort"; 170 | 171 | p.ranges.min = 0.f; 172 | p.ranges.max = 1.0f; 173 | p.ranges.def = 0.f; 174 | break; 175 | case 5: 176 | p.hints = kParameterIsAutomatable; 177 | p.name = "Post Clip"; 178 | p.symbol = "postclip"; 179 | 180 | p.ranges.min = 0.f; 181 | p.ranges.max = 1.0f; 182 | p.ranges.def = 0.f; 183 | break; 184 | default: 185 | break; 186 | } 187 | } 188 | 189 | float getParameterValue(uint32_t index) const override { 190 | switch (index) { 191 | case 0: 192 | return params.downsample; 193 | case 1: 194 | return params.current.noisebias; 195 | case 2: 196 | return params.current.prenoise; 197 | case 3: 198 | return params.current.postnoise; 199 | case 4: 200 | return params.current.distort; 201 | case 5: 202 | return params.current.postclip; 203 | default: 204 | return 0.f; 205 | } 206 | } 207 | 208 | void setParameterValue(uint32_t index, float value) override { 209 | switch (index) { 210 | case 0: 211 | params.downsample = value; 212 | break; 213 | case 1: 214 | params.current.noisebias = value; 215 | break; 216 | case 2: 217 | params.current.prenoise = value; 218 | break; 219 | case 3: 220 | params.current.postnoise = value; 221 | break; 222 | case 4: 223 | params.current.distort = value; 224 | break; 225 | case 5: 226 | params.current.postclip = value; 227 | break; 228 | default: 229 | break; 230 | } 231 | } 232 | 233 | void activate() override { 234 | lcache = 0.f; 235 | rcache = 0.f; 236 | sampleCounter = 0; 237 | params.old = params.current; 238 | } 239 | 240 | void run(const float** inputs, float** outputs, uint32_t nframes) override { 241 | Lerp distort { params.old.distort, params.current.distort, (float) nframes }; 242 | Lerp prenoise { params.old.prenoise, params.current.prenoise, (float) nframes }; 243 | Lerp postclip { params.old.postclip, params.current.postclip, (float) nframes }; 244 | Lerp postnoise { params.old.postnoise, params.current.postnoise, (float) nframes }; 245 | Lerp noisebias { params.old.noisebias, params.current.noisebias, (float) nframes }; 246 | 247 | for (uint32_t i = 0; i < nframes; ++i) { 248 | float lsample(inputs[0][i]); 249 | float rsample(inputs[1][i]); 250 | 251 | if (sampleCounter++ % (int) params.downsample == 0) { 252 | lsample = softClip(lsample, distort[i], 2.f); 253 | rsample = softClip(rsample, distort[i], 2.f); 254 | 255 | lsample = applyNoise(lsample, prenoise[i], noisebias[i]); 256 | rsample = applyNoise(rsample, prenoise[i], noisebias[i]); 257 | 258 | lcache = lsample; 259 | rcache = rsample; 260 | } 261 | 262 | lsample = lcache; 263 | rsample = rcache; 264 | 265 | lsample = softClip(lsample, postclip[i], 1.f); 266 | rsample = softClip(rsample, postclip[i], 1.f); 267 | 268 | lsample = applyNoise(lsample, postnoise[i], noisebias[i]); 269 | rsample = applyNoise(rsample, postnoise[i], noisebias[i]); 270 | 271 | outputs[0][i] = lsample; 272 | outputs[1][i] = rsample; 273 | } 274 | 275 | params.old = params.current; 276 | } 277 | }; 278 | 279 | Plugin* createPlugin() { 280 | return new BitrotCrush(); 281 | } 282 | 283 | END_NAMESPACE_DISTRHO 284 | -------------------------------------------------------------------------------- /plugins/Crush/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DISTRHO_PLUGIN_NAME \ 4 | "Bitrot Crush" 5 | 6 | #define DISTRHO_PLUGIN_URI \ 7 | "http://grejppi.github.io/plugins/bitrot/crush" 8 | 9 | #define DISTRHO_PLUGIN_NUM_INPUTS 2 10 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 11 | 12 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 13 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 0 14 | 15 | #define DISTRHO_PLUGIN_HAS_UI 0 16 | #define DISTRHO_PLUGIN_WANT_NANOVG 0 17 | -------------------------------------------------------------------------------- /plugins/Repeat/BitrotRepeat.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BitrotRepeat.cpp 3 | * 4 | * Copyright 2013-2018 Henna Haahti 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "DistrhoPlugin.hpp" 20 | #include "Label.hpp" 21 | #include "Lerp.hpp" 22 | #include "ToggledValue.hpp" 23 | #include "Version.hpp" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "DistrhoPluginMain.cpp" 30 | 31 | 32 | START_NAMESPACE_DISTRHO 33 | 34 | class BitrotRepeat : public Plugin { 35 | static constexpr uint32_t NUM_PARAMS = 10; 36 | static constexpr uint32_t OVERSAMPLING = 32; 37 | 38 | struct LerpParams { 39 | float speed; 40 | }; 41 | 42 | struct { 43 | float active; 44 | float bpm; 45 | float beats; 46 | float division; 47 | float retrigger; 48 | float attack; 49 | float hold; 50 | float release; 51 | float varispeed; 52 | float sync; 53 | LerpParams current; 54 | LerpParams old; 55 | } params; 56 | 57 | std::vector lbuffer; 58 | std::vector rbuffer; 59 | 60 | double rate; 61 | double fpb; 62 | uint32_t writePos; 63 | double readPos; 64 | double loopLength; 65 | bool looped; 66 | 67 | uint32_t bpm; 68 | uint32_t beats; 69 | uint32_t division; 70 | 71 | float attackDelta; 72 | float releaseDelta; 73 | 74 | float gain; 75 | 76 | int retriggered; 77 | 78 | void reset() { 79 | for (uint32_t i = 0; i < NUM_PARAMS; ++i) { 80 | Parameter p; 81 | initParameter(i, p); 82 | setParameterValue(i, p.ranges.def); 83 | } 84 | params.old = params.current; 85 | } 86 | 87 | void updateLoop() { 88 | fpb = (60.0 / (double) bpm) * rate; 89 | loopLength = (fpb * beats) / (double) division; 90 | } 91 | 92 | public: 93 | BitrotRepeat() : Plugin(10, 0, 0) { 94 | reset(); 95 | sampleRateChanged(getSampleRate()); 96 | activate(); 97 | } 98 | 99 | protected: 100 | const char* getLabel() const override { 101 | return LABEL("repeat"); 102 | } 103 | 104 | const char* getDescription() const override { 105 | return "Beat repeat"; 106 | } 107 | 108 | const char* getMaker() const override { 109 | return "grejppi"; 110 | } 111 | 112 | const char* getLicense() const override { 113 | return "Apache-2.0"; 114 | } 115 | 116 | uint32_t getVersion() const override { 117 | return BITROT_VERSION(); 118 | } 119 | 120 | int64_t getUniqueId() const override { 121 | return 270; 122 | } 123 | 124 | void initParameter(uint32_t index, Parameter& p) override { 125 | switch (index) { 126 | case 0: 127 | p.hints = kParameterIsAutomatable | kParameterIsBoolean; 128 | p.name = "Active"; 129 | p.symbol = "active"; 130 | 131 | p.ranges.min = 0.f; 132 | p.ranges.max = 1.f; 133 | p.ranges.def = 0.f; 134 | break; 135 | case 1: 136 | p.hints = kParameterIsAutomatable | kParameterIsInteger; 137 | p.name = "BPM"; 138 | p.symbol = "bpm"; 139 | 140 | p.ranges.min = 10.f; 141 | p.ranges.max = 480.f; 142 | p.ranges.def = 100.f; 143 | break; 144 | case 2: 145 | p.hints = kParameterIsAutomatable | kParameterIsInteger; 146 | p.name = "Beats"; 147 | p.symbol = "beats"; 148 | 149 | p.ranges.min = 1.f; 150 | p.ranges.max = 4.f; 151 | p.ranges.def = 2.f; 152 | break; 153 | case 3: 154 | p.hints = kParameterIsAutomatable | kParameterIsInteger; 155 | p.name = "Division"; 156 | p.symbol = "division"; 157 | 158 | p.ranges.min = 1.f; 159 | p.ranges.max = 16.f; 160 | p.ranges.def = 4.f; 161 | break; 162 | case 4: 163 | p.hints = kParameterIsAutomatable | kParameterIsBoolean | kParameterIsTrigger; 164 | p.name = "Retrigger"; 165 | p.symbol = "retrigger"; 166 | 167 | p.ranges.min = 0.f; 168 | p.ranges.max = 1.f; 169 | p.ranges.def = 0.f; 170 | break; 171 | case 5: 172 | p.hints = kParameterIsAutomatable; 173 | p.name = "Attack"; 174 | p.symbol = "attack"; 175 | 176 | p.ranges.min = 0.f; 177 | p.ranges.max = 1.f; 178 | p.ranges.def = 0.f; 179 | break; 180 | case 6: 181 | p.hints = kParameterIsAutomatable; 182 | p.name = "Hold"; 183 | p.symbol = "hold"; 184 | 185 | p.ranges.min = 0.f; 186 | p.ranges.max = 1.f; 187 | p.ranges.def = 1.f; 188 | break; 189 | case 7: 190 | p.hints = kParameterIsAutomatable; 191 | p.name = "Release"; 192 | p.symbol = "release"; 193 | 194 | p.ranges.min = 0.f; 195 | p.ranges.max = 1.f; 196 | p.ranges.def = 1.f; 197 | break; 198 | case 8: 199 | p.hints = kParameterIsAutomatable | kParameterIsBoolean; 200 | p.name = "Varispeed"; 201 | p.symbol = "varispeed"; 202 | 203 | p.ranges.min = 0.f; 204 | p.ranges.max = 1.f; 205 | p.ranges.def = 0.f; 206 | break; 207 | case 9: 208 | p.hints = kParameterIsAutomatable | kParameterIsLogarithmic; 209 | p.name = "Speed"; 210 | p.symbol = "speed"; 211 | 212 | p.ranges.min = 0.25f; 213 | p.ranges.max = 4.f; 214 | p.ranges.def = 1.f; 215 | break; 216 | default: 217 | break; 218 | } 219 | } 220 | 221 | float getParameterValue(uint32_t index) const override { 222 | switch (index) { 223 | case 0: 224 | return params.active; 225 | case 1: 226 | return params.bpm; 227 | case 2: 228 | return params.beats; 229 | case 3: 230 | return params.division; 231 | case 4: 232 | return params.retrigger; 233 | case 5: 234 | return params.attack; 235 | case 6: 236 | return params.hold; 237 | case 7: 238 | return params.release; 239 | case 8: 240 | return params.varispeed; 241 | case 9: 242 | return params.current.speed; 243 | default: 244 | return 0.f; 245 | } 246 | } 247 | 248 | void setParameterValue(uint32_t index, float value) override { 249 | switch (index) { 250 | case 0: 251 | params.active = value; 252 | break; 253 | case 1: 254 | params.bpm = value; 255 | bpm = (uint32_t) value; 256 | updateLoop(); 257 | break; 258 | case 2: 259 | params.beats = value; 260 | beats = (uint32_t) value; 261 | updateLoop(); 262 | break; 263 | case 3: 264 | params.division = value; 265 | division = (uint32_t) value; 266 | updateLoop(); 267 | break; 268 | case 4: 269 | params.retrigger = value; 270 | if (toggledValue(value) && !retriggered) { 271 | std::memset(&lbuffer[0], 0, sizeof(float) * lbuffer.size()); 272 | std::memset(&rbuffer[0], 0, sizeof(float) * rbuffer.size()); 273 | writePos = 0; 274 | readPos = 0.0; 275 | retriggered = true; 276 | looped = false; 277 | } else { 278 | retriggered = false; 279 | } 280 | break; 281 | case 5: 282 | params.attack = value; 283 | if (value != 0.f) { 284 | attackDelta = 1.f / (rate * (value / 10.f)); 285 | } else { 286 | attackDelta = 1.f; 287 | } 288 | break; 289 | case 6: 290 | params.hold = value; 291 | break; 292 | case 7: 293 | params.release = value; 294 | if (value!= 0.f) { 295 | releaseDelta = 1.f / (rate * (value/ 10.f)); 296 | } else { 297 | releaseDelta = 1.f; 298 | } 299 | break; 300 | case 8: 301 | params.varispeed = value; 302 | break; 303 | case 9: 304 | params.current.speed = value; 305 | break; 306 | default: 307 | break; 308 | } 309 | } 310 | 311 | void activate() override { 312 | writePos = 0; 313 | readPos = 0.0; 314 | } 315 | 316 | void sampleRateChanged(double rate) override { 317 | int newSize = std::ceil(rate) * 24; 318 | lbuffer.resize(newSize, 0.f); 319 | rbuffer.resize(newSize, 0.f); 320 | this->rate = rate; 321 | updateLoop(); 322 | } 323 | 324 | void run(const float** inputs, float** outputs, uint32_t nframes) override { 325 | Lerp speed { params.old.speed, params.current.speed, (float) nframes }; 326 | 327 | int bufSize = lbuffer.size(); 328 | 329 | if (toggledValue(params.active)) { 330 | if (writePos < bufSize) { 331 | std::memcpy(&lbuffer[writePos], inputs[0], 332 | sizeof(float) * std::min(nframes, bufSize - writePos)); 333 | std::memcpy(&rbuffer[writePos], inputs[1], 334 | sizeof(float) * std::min(nframes, bufSize - writePos)); 335 | writePos += nframes; 336 | } 337 | 338 | for (uint32_t i = 0; i < nframes; ++i) { 339 | if (toggledValue(params.varispeed) && (looped || speed[i] < 1.f)) { 340 | float l(0.f); 341 | float r(0.f); 342 | 343 | for (int o = 0; o < OVERSAMPLING; ++o) { 344 | l += lbuffer[(uint32_t) readPos] * gain; 345 | r += rbuffer[(uint32_t) readPos] * gain; 346 | readPos += speed[i] / (float) OVERSAMPLING; 347 | } 348 | 349 | outputs[0][i] = l / (float) OVERSAMPLING; 350 | outputs[1][i] = r / (float) OVERSAMPLING; 351 | } else { 352 | outputs[0][i] = lbuffer[(uint32_t) readPos] * gain; 353 | outputs[1][i] = rbuffer[(uint32_t) readPos] * gain; 354 | readPos += 1.f; 355 | } 356 | 357 | while (readPos >= loopLength) { 358 | readPos -= loopLength; 359 | gain = 0.f; 360 | looped = true; 361 | } 362 | 363 | if (readPos <= std::max(params.hold, 0.1f) * loopLength) { 364 | gain += attackDelta * speed[i]; 365 | gain = std::min(gain, 1.f); 366 | } else { 367 | gain -= releaseDelta * speed[i]; 368 | gain = std::max(0.f, gain); 369 | } 370 | } 371 | } else { 372 | writePos = 0; 373 | readPos = 0.0; 374 | retriggered = 0; 375 | looped = false; 376 | gain = 1.f; 377 | std::memset(&lbuffer[0], 0, sizeof(float) * bufSize); 378 | std::memset(&rbuffer[0], 0, sizeof(float) * bufSize); 379 | std::memcpy(outputs[0], inputs[0], sizeof(float) * nframes); 380 | std::memcpy(outputs[1], inputs[1], sizeof(float) * nframes); 381 | } 382 | 383 | params.old = params.current; 384 | } 385 | }; 386 | 387 | Plugin* createPlugin() { 388 | return new BitrotRepeat(); 389 | } 390 | 391 | END_NAMESPACE_DISTRHO 392 | -------------------------------------------------------------------------------- /plugins/Repeat/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DISTRHO_PLUGIN_NAME \ 4 | "Bitrot Repeat" 5 | 6 | #define DISTRHO_PLUGIN_URI \ 7 | "http://grejppi.github.io/plugins/bitrot/repeat" 8 | 9 | #define DISTRHO_PLUGIN_REPLACED_URI \ 10 | "http://grejppi.github.io/plugins/bitrot/stutter" 11 | 12 | #define DISTRHO_PLUGIN_NUM_INPUTS 2 13 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 14 | 15 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 16 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 0 17 | 18 | #define DISTRHO_PLUGIN_HAS_UI 0 19 | #define DISTRHO_PLUGIN_WANT_NANOVG 0 20 | -------------------------------------------------------------------------------- /plugins/Reverser/BitrotReverser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BitrotReverser.cpp 3 | * 4 | * Copyright 2013-2018 Henna Haahti 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "DistrhoPlugin.hpp" 20 | #include "Label.hpp" 21 | #include "ToggledValue.hpp" 22 | #include "Version.hpp" 23 | 24 | #include 25 | #include 26 | 27 | #include "DistrhoPluginMain.cpp" 28 | 29 | 30 | START_NAMESPACE_DISTRHO 31 | 32 | class BitrotReverser : public Plugin { 33 | static constexpr uint32_t NUM_PARAMS = 2; 34 | 35 | struct { 36 | float active; 37 | float switchDir; 38 | } params; 39 | 40 | std::vector lwork; 41 | std::vector rwork; 42 | 43 | std::vector lbuffer; 44 | std::vector rbuffer; 45 | 46 | int32_t writePos; 47 | int32_t readPos; 48 | int32_t copied; 49 | 50 | void reset() { 51 | for (uint32_t i = 0; i < NUM_PARAMS; ++i) { 52 | Parameter p; 53 | initParameter(i, p); 54 | setParameterValue(i, p.ranges.def); 55 | } 56 | } 57 | 58 | public: 59 | BitrotReverser() : Plugin(NUM_PARAMS, 0, 0) { 60 | reset(); 61 | sampleRateChanged(getSampleRate()); 62 | activate(); 63 | } 64 | 65 | protected: 66 | const char* getLabel() const override { 67 | return LABEL("reverser"); 68 | } 69 | 70 | const char* getDescription() const override { 71 | return "Play sound backwards"; 72 | } 73 | 74 | const char* getMaker() const override { 75 | return "grejppi"; 76 | } 77 | 78 | const char* getLicense() const override { 79 | return "Apache-2.0"; 80 | } 81 | 82 | uint32_t getVersion() const override { 83 | return BITROT_VERSION(); 84 | } 85 | 86 | int64_t getUniqueId() const override { 87 | return 267; 88 | } 89 | 90 | void initParameter(uint32_t index, Parameter& p) override { 91 | switch (index) { 92 | case 0: 93 | p.hints = kParameterIsAutomatable | kParameterIsBoolean; 94 | p.name = "Active"; 95 | p.symbol = "active"; 96 | 97 | p.ranges.min = 0.f; 98 | p.ranges.max = 1.f; 99 | p.ranges.def = 0.f; 100 | break; 101 | case 1: 102 | p.hints = kParameterIsAutomatable; 103 | p.name = "Switch Direction"; 104 | p.symbol = "switch"; 105 | 106 | p.ranges.min = 0.f; 107 | p.ranges.max = 1.f; 108 | p.ranges.def = 0.f; 109 | break; 110 | default: 111 | break; 112 | } 113 | } 114 | 115 | float getParameterValue(uint32_t index) const override { 116 | switch (index) { 117 | case 0: 118 | return params.active; 119 | case 1: 120 | return params.switchDir; 121 | default: 122 | return 0.f; 123 | } 124 | } 125 | 126 | void setParameterValue(uint32_t index, float value) override { 127 | switch (index) { 128 | case 0: 129 | params.active = value; 130 | break; 131 | case 1: 132 | params.switchDir = value; 133 | break; 134 | default: 135 | break; 136 | } 137 | } 138 | 139 | void activate() override { 140 | writePos = 0; 141 | readPos = 0; 142 | copied = -1; 143 | } 144 | 145 | void sampleRateChanged(double rate) override { 146 | int newSize = std::ceil(rate) * 4; 147 | lwork.resize(newSize, 0.f); 148 | rwork.resize(newSize, 0.f); 149 | lbuffer.resize(newSize, 0.f); 150 | rbuffer.resize(newSize, 0.f); 151 | } 152 | 153 | void run(const float** inputs, float** outputs, uint32_t nframes) override { 154 | bool playing(toggledValue(params.active)); 155 | int bufSize = lwork.size(); 156 | 157 | for (uint32_t i = 0; i < nframes; ++i) { 158 | int w(writePos % bufSize); 159 | 160 | lwork[w] = inputs[0][i]; 161 | rwork[w] = inputs[1][i]; 162 | 163 | if (playing) { 164 | if (copied == -1) { 165 | for (uint32_t j = 0; j < bufSize; ++j) { 166 | lbuffer[j] = lwork[j]; 167 | rbuffer[j] = rwork[j]; 168 | copied = 0; 169 | } 170 | } else if (copied < (bufSize >> 1)) { 171 | lbuffer[w] = lwork[w]; 172 | rbuffer[w] = rwork[w]; 173 | copied++; 174 | } 175 | 176 | int advance = toggledValue(params.switchDir) ? 1 : -1; 177 | readPos = (readPos + advance + lbuffer.size()) % lbuffer.size(); 178 | outputs[0][i] = lbuffer[readPos]; 179 | outputs[1][i] = rbuffer[readPos]; 180 | } else { 181 | readPos = writePos; 182 | copied = -1; 183 | outputs[0][i] = lwork[readPos]; 184 | outputs[1][i] = rwork[readPos]; 185 | } 186 | 187 | writePos = (writePos + 1) % bufSize; 188 | } 189 | } 190 | }; 191 | 192 | Plugin* createPlugin() { 193 | return new BitrotReverser(); 194 | } 195 | 196 | END_NAMESPACE_DISTRHO 197 | -------------------------------------------------------------------------------- /plugins/Reverser/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DISTRHO_PLUGIN_NAME \ 4 | "Bitrot Reverser" 5 | 6 | #define DISTRHO_PLUGIN_URI \ 7 | "http://grejppi.github.io/plugins/bitrot/reverser" 8 | 9 | #define DISTRHO_PLUGIN_NUM_INPUTS 2 10 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 11 | 12 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 13 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 0 14 | 15 | #define DISTRHO_PLUGIN_HAS_UI 0 16 | #define DISTRHO_PLUGIN_WANT_NANOVG 0 17 | -------------------------------------------------------------------------------- /plugins/Tapestop/BitrotTapestop.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BitrotTapestop.cpp 3 | * 4 | * Copyright 2013-2018 Henna Haahti 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "DistrhoPlugin.hpp" 20 | #include "Label.hpp" 21 | #include "Lerp.hpp" 22 | #include "ToggledValue.hpp" 23 | #include "Version.hpp" 24 | 25 | #include 26 | #include 27 | 28 | #include "DistrhoPluginMain.cpp" 29 | 30 | 31 | START_NAMESPACE_DISTRHO 32 | 33 | class BitrotTapestop : public Plugin { 34 | static constexpr uint32_t NUM_PARAMS = 3; 35 | static constexpr uint32_t OVERSAMPLING = 32; 36 | static constexpr uint32_t BUFFER_SIZE = 192000; 37 | 38 | struct LerpParams { 39 | float fade; 40 | }; 41 | 42 | struct { 43 | float active; 44 | float speed; 45 | LerpParams current; 46 | LerpParams old; 47 | } params; 48 | 49 | std::vector lbuffer; 50 | std::vector rbuffer; 51 | 52 | double rate; 53 | double playSpeed; 54 | double playSpeedFac; 55 | double speed; 56 | 57 | double readPos; 58 | uint32_t writePos; 59 | 60 | void reset() { 61 | for (uint32_t i = 0; i < NUM_PARAMS; ++i) { 62 | Parameter p; 63 | initParameter(i, p); 64 | setParameterValue(i, p.ranges.def); 65 | } 66 | params.old = params.current; 67 | } 68 | 69 | public: 70 | BitrotTapestop() : Plugin(NUM_PARAMS, 0, 0) { 71 | reset(); 72 | lbuffer.resize(BUFFER_SIZE, 0.f); 73 | rbuffer.resize(BUFFER_SIZE, 0.f); 74 | } 75 | 76 | protected: 77 | const char* getLabel() const override { 78 | return LABEL("tapestop"); 79 | } 80 | 81 | const char* getDescription() const override { 82 | return "Gradually slow down audio"; 83 | } 84 | 85 | const char* getMaker() const override { 86 | return "grejppi"; 87 | } 88 | 89 | const char* getLicense() const override { 90 | return "Apache-2.0"; 91 | } 92 | 93 | uint32_t getVersion() const override { 94 | return BITROT_VERSION(); 95 | } 96 | 97 | int64_t getUniqueId() const override { 98 | return 268; 99 | } 100 | 101 | void initParameter(uint32_t index, Parameter& p) override { 102 | switch (index) { 103 | case 0: 104 | p.hints = kParameterIsAutomatable | kParameterIsBoolean; 105 | p.name = "Active"; 106 | p.symbol = "active"; 107 | 108 | p.ranges.min = 0.f; 109 | p.ranges.max = 1.f; 110 | p.ranges.def = 0.f; 111 | break; 112 | case 1: 113 | p.hints = kParameterIsAutomatable; 114 | p.name = "Speed"; 115 | p.symbol = "speed"; 116 | 117 | p.ranges.min = 0.f; 118 | p.ranges.max = 1.f; 119 | p.ranges.def = 0.5f; 120 | break; 121 | case 2: 122 | p.hints = kParameterIsAutomatable | kParameterIsBoolean; 123 | p.name = "Fade"; 124 | p.symbol = "fade"; 125 | 126 | p.ranges.min = 0.f; 127 | p.ranges.max = 1.f; 128 | p.ranges.def = 1.f; 129 | break; 130 | default: 131 | break; 132 | } 133 | } 134 | 135 | float getParameterValue(uint32_t index) const override { 136 | switch (index) { 137 | case 0: 138 | return params.active; 139 | case 1: 140 | return params.speed; 141 | case 2: 142 | return params.current.fade; 143 | default: 144 | return 0.f; 145 | } 146 | } 147 | 148 | void setParameterValue(uint32_t index, float value) override { 149 | switch (index) { 150 | case 0: 151 | params.active = value; 152 | break; 153 | case 1: 154 | params.speed = value; 155 | break; 156 | case 2: 157 | params.current.fade = value; 158 | break; 159 | default: 160 | break; 161 | } 162 | } 163 | 164 | void run(const float** inputs, float** outputs, uint32_t nframes) override { 165 | Lerp fade { 166 | (float) toggledValue(params.old.fade), 167 | (float) toggledValue(params.current.fade), 168 | (float) nframes 169 | }; 170 | 171 | if (speed != params.speed) { 172 | speed = params.speed; 173 | playSpeedFac = 0.9999 + ((1.0 - speed) * 0.00009); 174 | } 175 | 176 | if (toggledValue(params.active)) { 177 | for (uint32_t i = 0; i < nframes; ++i) { 178 | if (writePos < BUFFER_SIZE) { 179 | lbuffer[writePos] = inputs[0][i]; 180 | rbuffer[writePos] = inputs[1][i]; 181 | writePos++; 182 | } 183 | 184 | float l(0.f); 185 | float r(0.f); 186 | 187 | for (uint32_t o = 0; o < OVERSAMPLING; ++o) { 188 | Lerp fadeAmount { 1.f, (float) playSpeed, 1.f }; 189 | l += lbuffer[(uint32_t) readPos] * fadeAmount[fade[i]]; 190 | r += rbuffer[(uint32_t) readPos] * fadeAmount[fade[i]]; 191 | readPos += playSpeed / (double) OVERSAMPLING; 192 | } 193 | 194 | outputs[0][i] = l / (float) OVERSAMPLING; 195 | outputs[1][i] = r / (float) OVERSAMPLING; 196 | 197 | playSpeed *= playSpeedFac; 198 | } 199 | } else { 200 | playSpeed = 1.f; 201 | writePos = 0; 202 | readPos = 0.0; 203 | std::memcpy(outputs[0], inputs[0], sizeof(float) * nframes); 204 | std::memcpy(outputs[1], inputs[1], sizeof(float) * nframes); 205 | } 206 | 207 | params.old = params.current; 208 | } 209 | }; 210 | 211 | Plugin* createPlugin() { 212 | return new BitrotTapestop(); 213 | } 214 | 215 | END_NAMESPACE_DISTRHO 216 | -------------------------------------------------------------------------------- /plugins/Tapestop/DistrhoPluginInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DISTRHO_PLUGIN_NAME \ 4 | "Bitrot Tapestop" 5 | 6 | #define DISTRHO_PLUGIN_URI \ 7 | "http://grejppi.github.io/plugins/bitrot/tapestop" 8 | 9 | #define DISTRHO_PLUGIN_NUM_INPUTS 2 10 | #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 11 | 12 | #define DISTRHO_PLUGIN_IS_RT_SAFE 1 13 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 0 14 | 15 | #define DISTRHO_PLUGIN_HAS_UI 0 16 | #define DISTRHO_PLUGIN_WANT_NANOVG 0 17 | -------------------------------------------------------------------------------- /plugins/ttlgen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import glob 4 | import json 5 | import os 6 | import subprocess 7 | import sys 8 | 9 | import os.path 10 | 11 | from collections import deque 12 | 13 | def is_string(s): 14 | if isinstance(s, str): 15 | return True 16 | try: 17 | return isinstance(s, unicode) 18 | except NameError: 19 | return False 20 | 21 | class TurtleWriter: 22 | @staticmethod 23 | def write(f, syntax): 24 | indent = 0 25 | newline = False 26 | while len(syntax.tokens) != 0: 27 | token = syntax.tokens.popleft() 28 | need = 0 if token == Token.PopBlank else 1 29 | if newline: 30 | f.write(' ' * indent * need) 31 | newline = False 32 | was_newline = True 33 | else: 34 | was_newline = False 35 | if token == Token.Prefix: 36 | newline = True 37 | f.write('@prefix {0}: <{1}> .\n'.format( 38 | syntax.strings.popleft(), 39 | syntax.strings.popleft(), 40 | )) 41 | elif token == Token.PushBlank: 42 | indent += 1 43 | newline = True 44 | f.write(' [\n') 45 | elif token == Token.PopBlank: 46 | indent -= 1 47 | f.write('{0}]'.format(' ' * indent)) 48 | elif token == Token.Comma: 49 | f.write(' ,') 50 | elif token == Token.Semicolon: 51 | newline = True 52 | f.write(' ;\n') 53 | elif token == Token.Dot: 54 | newline = True 55 | indent -= 1 56 | f.write(' .\n') 57 | elif token == Token.Newline: 58 | newline = True 59 | f.write('\n') 60 | elif token == Token.Indent: 61 | indent += 1 62 | elif token == Token.Dedent: 63 | indent -= 1 64 | elif token == Token.Comment: 65 | newline = True 66 | f.write('# {0}\n'.format(syntax.strings.popleft())) 67 | elif token == Token.Whatever: 68 | f.write('{0}{1}'.format( 69 | '' if was_newline else ' ', 70 | syntax.strings.popleft(), 71 | )) 72 | 73 | 74 | class Token: 75 | Comma = 1 76 | Comment = 2 77 | Dedent = 3 78 | Dot = 4 79 | Indent = 5 80 | Newline = 6 81 | PopBlank = 7 82 | Prefix = 8 83 | PushBlank = 9 84 | Semicolon = 10 85 | Whatever = 11 86 | 87 | 88 | class Syntax: 89 | __slots__ = 'tokens', 'strings' 90 | 91 | def __init__(self): 92 | self.tokens = deque() 93 | self.strings = deque() 94 | 95 | def merge(self, other): 96 | self.tokens += other.tokens 97 | self.strings += other.strings 98 | 99 | 100 | class Comment(Syntax): 101 | def __init__(self, comment): 102 | Syntax.__init__(self) 103 | self.tokens.append(Token.Comment) 104 | self.strings.append(comment) 105 | 106 | 107 | class Prefix(Syntax): 108 | def __init__(self, prefix, base): 109 | Syntax.__init__(self) 110 | self.tokens.append(Token.Prefix) 111 | self.strings.append(prefix) 112 | self.strings.append(base) 113 | 114 | 115 | class Statement(Syntax): 116 | def __init__(self, subject, value): 117 | Syntax.__init__(self) 118 | self.tokens.append(Token.Newline) 119 | self.tokens.append(Token.Whatever) 120 | self.strings.append(subject) 121 | self.tokens.append(Token.Indent) 122 | self.tokens.append(Token.Newline) 123 | self.merge(value) 124 | self.tokens.append(Token.Dot) 125 | 126 | 127 | class Subject(Syntax): 128 | def __init__(self): 129 | Syntax.__init__(self) 130 | 131 | def predicate(self, predicate, object): 132 | if len(self.tokens) != 0: 133 | self.tokens.append(Token.Semicolon) 134 | self.tokens.append(Token.Whatever) 135 | self.strings.append(predicate) 136 | if is_string(object): 137 | object = Object().value(object) 138 | self.merge(object) 139 | return self 140 | 141 | 142 | class Object(Syntax): 143 | def __init__(self): 144 | Syntax.__init__(self) 145 | 146 | def value(self, object): 147 | if len(self.tokens) != 0: 148 | self.tokens.append(Token.Comma) 149 | if is_string(object): 150 | self.tokens.append(Token.Whatever) 151 | self.strings.append(object) 152 | elif isinstance(object, Subject): 153 | self.tokens.append(Token.PushBlank) 154 | self.merge(object) 155 | self.tokens.append(Token.Newline) 156 | self.tokens.append(Token.PopBlank) 157 | else: 158 | raise TypeError 159 | return self 160 | 161 | 162 | def uri(uri): 163 | return uri.join('<>') 164 | 165 | 166 | def string(value, quote='"'): 167 | return str(value).join((quote, quote)) 168 | 169 | 170 | def integer(value): 171 | return str(value) 172 | 173 | 174 | def decimal(value): 175 | ret = str(value) 176 | if '.' not in ret: 177 | ret += '.0' 178 | return ret 179 | 180 | 181 | def write_ttl(metagen): 182 | metadata = subprocess.check_output(metagen) 183 | metadata = json.loads(metadata) 184 | 185 | with open('manifest.{0}'.format(metadata['ttlname']), 'w') as f: 186 | plugin = Subject() 187 | plugin.predicate('a', 'lv2:Plugin') 188 | plugin.predicate('lv2:binary', uri(metadata['dllname'])) 189 | if metadata['replaced_uri']: 190 | plugin.predicate('dc:replaces', uri(metadata['replaced_uri'])) 191 | plugin.predicate('rdfs:seeAlso', uri(metadata['ttlname'])) 192 | TurtleWriter.write(f, Statement(uri(metadata['uri']), plugin)) 193 | 194 | ttl = (Prefix('bufsz', 'http://lv2plug.in/ns/ext/buf-size#')) 195 | ttl.merge(Prefix('dc', 'http://purl.org/dc/elements/1.1/')) 196 | ttl.merge(Prefix('doap', 'http://usefulinc.com/ns/doap#')) 197 | ttl.merge(Prefix('lv2', 'http://lv2plug.in/ns/lv2core#')) 198 | ttl.merge(Prefix('opts', 'http://lv2plug.in/ns/ext/options#')) 199 | ttl.merge(Prefix('pprops', 'http://lv2plug.in/ns/ext/port-props#')) 200 | ttl.merge(Prefix('rdfs', 'http://www.w3.org/2000/01/rdf-schema#')) 201 | ttl.merge(Prefix('state', 'http://lv2plug.in/ns/ext/state#')) 202 | ttl.merge(Prefix('urid', 'http://lv2plug.in/ns/ext/urid#')) 203 | 204 | plugin = Subject() 205 | plugin.predicate('a', 'lv2:Plugin') 206 | 207 | plugin.predicate('lv2:extensionData', 'state:interface') 208 | 209 | plugin.predicate( 210 | 'lv2:requiredFeature', 211 | Object() 212 | .value('opts:options') 213 | .value('urid:map') 214 | ) 215 | 216 | if metadata['is_rt_safe']: 217 | plugin.predicate('lv2:optionalFeature', 'lv2:hardRTCapable') 218 | 219 | plugin.predicate( 220 | 'opts:supportedOption', 221 | Object() 222 | .value('bufsz:nominalBlockLength') 223 | .value('bufsz:maxBlockLength') 224 | .value('bufsz:sampleRate') 225 | ) 226 | 227 | plugin.predicate('doap:name', string(metadata['name'])) 228 | plugin.predicate('lv2:project', uri('https://github.com/grejppi/bitrot')) 229 | 230 | plugin.predicate('doap:license', uri( 231 | 'https://spdx.org/licenses/{0}'.format(metadata['license']), 232 | )) 233 | 234 | minor = integer(metadata['version']['minor']) 235 | micro = integer(metadata['version']['micro']) 236 | 237 | plugin.predicate('lv2:minorVersion', integer(int(float(minor)))) 238 | plugin.predicate('lv2:microVersion', integer(int(float(micro)))) 239 | 240 | plugin.predicate('rdfs:comment', string(metadata['description'], '"""')) 241 | 242 | ports = (Object() 243 | .value(Subject() 244 | .predicate( 245 | 'a', 246 | Object() 247 | .value('lv2:InputPort') 248 | .value('lv2:AudioPort')) 249 | .predicate('lv2:index', integer(0)) 250 | .predicate('lv2:symbol', string('lin')) 251 | .predicate('lv2:name', string('Left In'))) 252 | .value(Subject() 253 | .predicate( 254 | 'a', 255 | Object() 256 | .value('lv2:InputPort') 257 | .value('lv2:AudioPort')) 258 | .predicate('lv2:index', integer(1)) 259 | .predicate('lv2:symbol', string('rin')) 260 | .predicate('lv2:name', string('Right In'))) 261 | .value(Subject() 262 | .predicate( 263 | 'a', 264 | Object() 265 | .value('lv2:OutputPort') 266 | .value('lv2:AudioPort')) 267 | .predicate('lv2:index', integer(2)) 268 | .predicate('lv2:symbol', string('lout')) 269 | .predicate('lv2:name', string('Left Out'))) 270 | .value(Subject() 271 | .predicate( 272 | 'a', 273 | Object() 274 | .value('lv2:OutputPort') 275 | .value('lv2:AudioPort')) 276 | .predicate('lv2:index', integer(3)) 277 | .predicate('lv2:symbol', string('rout')) 278 | .predicate('lv2:name', string('Right Out')))) 279 | 280 | index = 4 281 | for param in metadata['params']: 282 | port = Subject() 283 | 284 | port.predicate('a', Object().value('lv2:InputPort').value('lv2:ControlPort')) 285 | port.predicate('lv2:index', integer(index)) 286 | port.predicate('lv2:symbol', string(param['symbol'])) 287 | port.predicate('lv2:name', string(param['name'])) 288 | port.predicate('lv2:minimum', decimal(param['minimum'])) 289 | port.predicate('lv2:maximum', decimal(param['maximum'])) 290 | port.predicate('lv2:default', decimal(param['default'])) 291 | 292 | pprops = Object() 293 | if param['trigger']: 294 | pprops.value('pprops:trigger') 295 | if param['toggled']: 296 | pprops.value('lv2:toggled') 297 | if param['integer']: 298 | pprops.value('lv2:integer') 299 | if param['logarithmic']: 300 | pprops.value('pprops:logarithmic') 301 | if not param['automatable']: 302 | pprops.value('pprops:expensive') 303 | if len(pprops.tokens) != 0: 304 | port.predicate('lv2:portProperty', pprops) 305 | 306 | ports.value(port) 307 | index += 1 308 | 309 | plugin.predicate('lv2:port', ports) 310 | ttl.merge(Statement(uri(metadata['uri']), plugin)) 311 | 312 | with open(metadata['ttlname'], 'w') as f: 313 | TurtleWriter.write(f, ttl) 314 | 315 | 316 | def finish_manifest(): 317 | with open('manifest.ttl', 'w') as f: 318 | ttl = Prefix('dc', 'http://purl.org/dc/terms/') 319 | ttl.merge(Prefix('doap', 'http://usefulinc.com/ns/doap#')) 320 | ttl.merge(Prefix('foaf', 'http://xmlns.com/foaf/0.1/')) 321 | ttl.merge(Prefix('lv2', 'http://lv2plug.in/ns/lv2core#')) 322 | ttl.merge(Prefix('rdfs', 'http://www.w3.org/2000/01/rdf-schema#')) 323 | 324 | project = Subject() 325 | project.predicate('a', 'doap:Project') 326 | project.predicate('doap:name', string('Bitrot')) 327 | 328 | maintainer = Subject() 329 | maintainer.predicate('a', 'foaf:Person') 330 | maintainer.predicate('foaf:nick', string('grejppi')) 331 | project.predicate('doap:maintainer', Object().value(maintainer)) 332 | 333 | ttl.merge(Statement( 334 | uri('https://github.com/grejppi/bitrot'), 335 | project)) 336 | 337 | TurtleWriter.write(f, ttl) 338 | 339 | for fname in glob.glob(os.path.abspath('manifest.*.ttl')): 340 | with open(fname) as part: 341 | f.write(part.read()) 342 | 343 | 344 | if __name__ == '__main__': 345 | args = deque(sys.argv) 346 | args.popleft() 347 | try: 348 | write_ttl(args.popleft()) 349 | except IndexError: 350 | finish_manifest() 351 | -------------------------------------------------------------------------------- /plugins/wscript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | top = '..' 4 | 5 | import sys 6 | 7 | def build(bld): 8 | def w32_flags(bld, target): 9 | return '' 10 | if bld.env.PLATFORM == 'win32': 11 | return '-Wl,--out-implib,{0}.a'.format(target) 12 | else: 13 | return '' 14 | 15 | plugins = ['Reverser', 'Tapestop', 'Crush', 'Repeat'] 16 | formats = [('ladspa', 'bitrot_{0}'), ('vst', 'bitrot_{0}_vst')] 17 | bundle = 'bitrot.lv2' 18 | 19 | import os.path 20 | import inspect 21 | ttlgen = os.path.dirname(os.path.abspath(inspect.stack()[0][1])) 22 | ttlgen = os.path.join(ttlgen, 'ttlgen.py') 23 | 24 | ttls = [] 25 | tasks = {} 26 | 27 | for plugin_name in plugins: 28 | source = '{0}/Bitrot{0}.cpp'.format(plugin_name) 29 | plugin = plugin_name.lower() 30 | 31 | for format, target in formats: 32 | target = target.format(plugin) 33 | 34 | # bloody windows tho 35 | w32 = w32_flags(bld, target) 36 | 37 | t = bld.shlib(features = 'cxx cxxshlib', 38 | source = [source], 39 | includes = ['../DPF/distrho', plugin_name, '../common'], 40 | cxxflags = ['-DDISTRHO_PLUGIN_TARGET_{0}'.format(format.upper())], 41 | name = '{0} ({1})'.format(plugin_name, format.upper()), 42 | target = target, 43 | install_path = '${{PREFIX}}/lib/{0}'.format(format)) 44 | tasks[t.name] = t 45 | 46 | target = '{0}/{1}'.format(bundle, plugin) 47 | w32 = w32_flags(bld, plugin) 48 | 49 | lv2 = bld.shlib(features = 'cxx cxxshlib', 50 | source = [source], 51 | includes = ['../DPF/distrho', plugin_name, '../common'], 52 | cxxflags = ['-DDISTRHO_PLUGIN_TARGET_LV2'], 53 | name = '{0} (LV2)'.format(plugin_name), 54 | target = target, 55 | install_path = '${{PREFIX}}/lib/lv2/{0}'.format(bundle)) 56 | 57 | extension = bld.env.cxxshlib_PATTERN 58 | extension = extension[(extension.rfind('.') + 1):] 59 | metasrc = bld(features = 'cxx cxxprogram', 60 | source = [source, 61 | '../common/MetadataGenerator.cpp'], 62 | includes = ['../DPF/distrho', plugin_name, '../common'], 63 | cxxflags = ['-DDISTRHO_PLUGIN_TARGET_LV2', 64 | '-DBITROT_BINARY_NAME="{0}.{1}"'.format( 65 | plugin, 66 | extension, 67 | ), 68 | '-DBITROT_TTL_NAME="{0}.ttl"'.format( 69 | plugin, 70 | ), 71 | '-Dprotected=public'], # ...pretend you didn't see that 72 | ldflags = [], 73 | name = '{0} (LV2 metadata generator)'.format(plugin), 74 | target = 'metagen/{0}'.format(plugin), 75 | install_path = None) 76 | 77 | # Restore the original environment for the metadata generator 78 | for k in metasrc.env.keys(): 79 | del metasrc.env[k] 80 | metasrc.env.load('.default_env') 81 | 82 | ttl = bld(features = 'seq', 83 | rule = '"{0}" "{1}" ${{SRC}}'.format(sys.executable, ttlgen), 84 | source = 'metagen/{0}'.format(metasrc.env.cxxprogram_PATTERN % plugin), 85 | target = ['{0}/{1}.ttl'.format(bundle, plugin), 86 | '{0}/manifest.{1}.ttl'.format(bundle, plugin)], 87 | install_path = '${{PREFIX}}/lib/lv2/{0}'.format(bundle), 88 | name = '{0}.ttl'.format(plugin), 89 | use = metasrc.get_name(), 90 | cwd = os.path.join(bld.out_dir, 'plugins', bundle)) 91 | ttls.append(ttl) 92 | 93 | bld.add_manual_dependency( 94 | bld.path.find_or_declare('{0}/manifest.ttl'.format(bundle)), 95 | bld.path.find_or_declare('{0}/manifest.{1}.ttl'.format(bundle, plugin)), 96 | ) 97 | 98 | bld.add_manual_dependency( 99 | bld.path.find_or_declare('{0}/{1}.ttl'.format(bundle, plugin)), 100 | bld.path.find_or_declare(ttlgen), 101 | ) 102 | 103 | bld.add_manual_dependency( 104 | bld.path.find_or_declare('{0}/manifest.ttl'.format(bundle, plugin)), 105 | bld.path.find_or_declare(ttlgen), 106 | ) 107 | 108 | manifest = bld(features = 'seq', 109 | rule = '"{0}" "{1}"'.format(sys.executable, ttlgen), 110 | target = '{0}/manifest.ttl'.format(bundle), 111 | source = ['{0}/manifest.{1}.ttl'.format(bundle, x.lower()) for x in plugins], 112 | install_path = '${{PREFIX}}/lib/lv2/{0}'.format(bundle), 113 | name = 'manifest.ttl', 114 | use = ttl, 115 | cwd = os.path.join(bld.out_dir, 'plugins', bundle)) 116 | 117 | if bld.env.PLATFORM == 'darwin': 118 | for plugin_name in plugins: 119 | name = '{0} (VST)'.format(plugin_name) 120 | vst = tasks[name] 121 | 122 | vst_bundle = 'Bitrot {0}.vst'.format(plugin_name) 123 | vst_bundle = bld.path.get_bld().make_node(vst_bundle) 124 | vst_bundle.mkdir() 125 | 126 | contents = vst_bundle.make_node('Contents') 127 | contents.mkdir() 128 | 129 | macos = contents.make_node('MacOS') 130 | macos.mkdir() 131 | 132 | resources = contents.make_node('Resources') 133 | resources.mkdir() 134 | 135 | pkginfo = contents.make_node('PkgInfo') 136 | pkginfo.write('BNDL????\n') 137 | 138 | dylib = bld.env.cxxshlib_PATTERN % vst.target 139 | bld(features = 'seq', 140 | rule = 'cp ${SRC} ${TGT}', 141 | source = dylib, 142 | target = macos.make_node(dylib), 143 | shell = False, 144 | use = vst) 145 | 146 | info_plist = contents.make_node('Info.plist') 147 | info_plist.write(info_plist_template.format( 148 | dylib=dylib, 149 | name=plugin_name, 150 | version=bld.env.VERSION[0], 151 | )) 152 | 153 | info_plist_template = '''\ 154 | 155 | 156 | 157 | 158 | 159 | CFBundleExecutable 160 | {dylib} 161 | CFBundleInfoDictionaryVersion 162 | 6.0 163 | CFBundleName 164 | Bitrot {name} VST 165 | CFBundleIdentifier 166 | io.github.grejppi.bitrot.{name} 167 | CFBundlePackageType 168 | BNDL 169 | CFBundleVersion 170 | {version} 171 | CSResourcesFileMapped 172 | 173 | CFBundleSignature 174 | ???? 175 | 176 | 177 | ''' 178 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grejppi/bitrot/06a72cd6cbe99b469e57a662cfb34e1cfe75e070/waf -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | 5 | top = '.' 6 | out = 'build' 7 | 8 | APPNAME = 'bitrot' 9 | VERSION = '0.7.1' 10 | 11 | import sys 12 | import os 13 | import re 14 | import shutil 15 | 16 | try: 17 | which = shutil.which 18 | except AttributeError: 19 | from distutils.spawn import find_executable 20 | def which(cmd, mode=os.F_OK | os.X_OK, path=None): 21 | path = find_executable(cmd, path) 22 | if os.access(path, mode): 23 | return path 24 | return None 25 | 26 | from waflib.Tools.compiler_cxx import cxx_compiler 27 | cxx_compiler['win32'].remove('msvc') 28 | 29 | def options(opt): 30 | opt.load('compiler_cxx') 31 | opt.add_option('-p', '--platform', dest='platform', default=None, 32 | help='cross-compilation target') 33 | opt.add_option('-d', '--debug', dest='debug', default=None, 34 | help='create debuggable build') 35 | opt.add_option('--use-upstream-dpf', dest='use_upstream_dpf', 36 | action='store_true', default=False, 37 | help='use upstream (non-customized) version of DPF') 38 | 39 | def configure(conf): 40 | conf.env.append_value('CXXFLAGS', ['-std=c++11', '-fvisibility=hidden', '-O2']) 41 | if conf.options.use_upstream_dpf: 42 | conf.env.append_value('CXXFLAGS', 43 | ['-DkParameterIsAutomatable=kParameterIsAutomable']) 44 | conf.env.append_value('CXXFLAGS', ['-DkParameterIsTrigger=0']) 45 | 46 | major, minor, micro = VERSION.split('.') 47 | conf.env.append_value('CXXFLAGS', [ 48 | '-DBITROT_VERSION_MAJOR={0}'.format(major), 49 | '-DBITROT_VERSION_MINOR={0}'.format(minor), 50 | '-DBITROT_VERSION_MICRO={0}'.format(micro), 51 | ]) 52 | conf.env.append_value('VERSION', VERSION) 53 | 54 | conf.load('compiler_cxx') 55 | conf.env.store('.default_env') 56 | 57 | cxxflags = [] 58 | ldflags = [] 59 | toolchain = '' 60 | 61 | if conf.options.debug: 62 | cxxflags.append('-g') 63 | 64 | if conf.options.platform in ('w32', 'win32', 'windows32'): 65 | toolchain = 'i686-w64-mingw32-' 66 | conf.env.PLATFORM = 'win32' 67 | ldflags.append('-static-libgcc') 68 | ldflags.append('-static-libstdc++') 69 | elif conf.options.platform in ('w64', 'win64', 'windows64'): 70 | toolchain = 'x86_64-w64-mingw32-' 71 | conf.env.PLATFORM = 'win32' 72 | ldflags.append('-static-libgcc') 73 | ldflags.append('-static-libstdc++') 74 | elif conf.options.platform in ('l32', 'lin32', 'linux32'): 75 | cxxflags.append('-m32') 76 | ldflags.append('-m32') 77 | conf.env.PLATFORM = 'linux' 78 | elif conf.options.platform in ('l64', 'lin64', 'linux64'): 79 | cxxflags.append('-m64') 80 | ldflags.append('-m64') 81 | conf.env.PLATFORM = 'linux' 82 | else: 83 | if sys.platform != 'win32': 84 | cxxflags.append('-fPIC') 85 | conf.env.PLATFORM = sys.platform 86 | 87 | conf.env.append_value('CXXFLAGS', cxxflags) 88 | conf.env.append_value('LINKFLAGS', ldflags) 89 | conf.env.AR = which(toolchain + 'ar') 90 | conf.env.CXX = which(toolchain + 'g++') 91 | conf.env.LINK_CXX = which(toolchain + 'g++') 92 | conf.load('compiler_cxx') 93 | 94 | cxx_compiler[sys.platform] = toolchain + 'gcc' 95 | 96 | if conf.env.PLATFORM.startswith('win32'): 97 | conf.env.cxxshlib_PATTERN = '%s.dll' 98 | elif conf.env.PLATFORM.startswith('linux'): 99 | conf.env.cxxshlib_PATTERN = '%s.so' 100 | 101 | try: 102 | conf.find_file('DistrhoPluginMain.cpp', ['DPF/distrho']) 103 | except conf.errors.ConfigurationError as err: 104 | raise conf.errors.ConfigurationError( 105 | "DPF submodule not found!\n" 106 | "Please run the following commands and try again:\n" 107 | " git submodule init\n" 108 | " git submodule update\n" 109 | ) 110 | 111 | def build(bld): 112 | bld.recurse('plugins') 113 | --------------------------------------------------------------------------------