├── .DS_Store ├── BitCrusher DEMO.jucer └── Source ├── PluginEditor.cpp ├── PluginEditor.h ├── PluginProcessor.cpp └── PluginProcessor.h /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAudioProgrammer/bitcrusherDemo/c79526a82d36c7e51c79d0503792f46d662bd67b/.DS_Store -------------------------------------------------------------------------------- /BitCrusher DEMO.jucer: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 18 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Source/PluginEditor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated! 5 | 6 | It contains the basic framework code for a JUCE plugin editor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "PluginProcessor.h" 12 | #include "PluginEditor.h" 13 | 14 | //============================================================================== 15 | NewProjectAudioProcessorEditor::NewProjectAudioProcessorEditor (NewProjectAudioProcessor& p) 16 | : AudioProcessorEditor (&p), processor (p) 17 | { 18 | 19 | getLookAndFeel().setColour(Slider::trackColourId, Colours::orange); 20 | 21 | getLookAndFeel().setColour(Slider::thumbColourId, Colours::yellowgreen); 22 | 23 | 24 | noise.setName("Noise Amount"); 25 | noise.setRange(0, 100); // % 26 | noise.setValue(processor.getParameters()[0]->getValue()); 27 | noise.setSliderStyle(Slider::LinearVertical); 28 | noise.setColour(Slider::textBoxTextColourId, Colours::white); 29 | noise.setTextBoxStyle(Slider::TextBoxBelow, false, 200, 20); 30 | noise.setValue(0); 31 | noise.addListener(this); 32 | addAndMakeVisible(&noise); 33 | 34 | bitRedux.setName("Bits"); 35 | bitRedux.setRange(2, 32); 36 | bitRedux.setValue(processor.getParameters()[0]->getValue()); 37 | bitRedux.setSliderStyle(Slider::LinearVertical); 38 | bitRedux.setColour(Slider::textBoxTextColourId, Colours::white); 39 | bitRedux.setTextBoxStyle(Slider::TextBoxBelow, false, 200, 20); 40 | bitRedux.setValue(32); 41 | bitRedux.addListener(this); 42 | addAndMakeVisible(&bitRedux); 43 | 44 | rateRedux.setName("Rate"); 45 | rateRedux.setRange(1, 50); // division rate (rate / x) 46 | rateRedux.setValue(processor.getParameters()[0]->getValue()); 47 | rateRedux.setSliderStyle(Slider::LinearVertical); 48 | rateRedux.setColour(Slider::textBoxTextColourId, Colours::white); 49 | rateRedux.setTextBoxStyle(Slider::TextBoxBelow, false, 200, 20); 50 | rateRedux.setValue(1); 51 | rateRedux.addListener(this); 52 | addAndMakeVisible(&rateRedux); 53 | 54 | 55 | // Make sure that before the constructor has finished, you've set the 56 | // editor's size to whatever you need it to be. 57 | setSize (400, 300); 58 | } 59 | 60 | NewProjectAudioProcessorEditor::~NewProjectAudioProcessorEditor() 61 | { 62 | } 63 | 64 | void NewProjectAudioProcessorEditor::sliderValueChanged (Slider* slider) 65 | { 66 | 67 | if ( slider == &noise) 68 | { 69 | processor.getParameters()[0]->setValue(slider->getValue()); 70 | } 71 | else if ( slider == &rateRedux) 72 | { 73 | processor.getParameters()[1]->setValue(slider->getValue()); 74 | 75 | } 76 | else if ( slider == &bitRedux) 77 | { 78 | processor.getParameters()[2]->setValue(slider->getValue()); 79 | 80 | } 81 | 82 | 83 | } 84 | 85 | 86 | //============================================================================== 87 | void NewProjectAudioProcessorEditor::paint (Graphics& g) 88 | { 89 | // (Our component is opaque, so we must completely fill the background with a solid colour) 90 | g.fillAll (Colours::midnightblue.withMultipliedBrightness(.4)); 91 | 92 | g.setColour(Colours::beige); 93 | 94 | int labelW = 100; 95 | g.drawText("Noise", noise.getX() + noise.getWidth()/2 - labelW/2, 10, labelW, 20, Justification::centred); 96 | 97 | g.drawText("Bit", bitRedux.getX() + bitRedux.getWidth()/2 - labelW/2, 10, labelW, 20, Justification::centred); 98 | 99 | g.drawText("Rate", rateRedux.getX() + rateRedux.getWidth()/2 - labelW/2, 10, labelW, 20, Justification::centred); 100 | 101 | 102 | } 103 | 104 | void NewProjectAudioProcessorEditor::resized() 105 | { 106 | 107 | int margin = 10; 108 | int w = 60; 109 | int y = 50; 110 | 111 | noise.setBounds(getWidth()/4 - w/2, y, w, getHeight() - y - margin); 112 | 113 | bitRedux.setBounds(2*getWidth()/4 - w/2, y, w, getHeight() - y - margin); 114 | 115 | rateRedux.setBounds(3*getWidth()/4 - w/2, y, w, getHeight() - y - margin); 116 | 117 | 118 | } 119 | -------------------------------------------------------------------------------- /Source/PluginEditor.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated! 5 | 6 | It contains the basic framework code for a JUCE plugin editor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "../JuceLibraryCode/JuceHeader.h" 14 | #include "PluginProcessor.h" 15 | 16 | 17 | //============================================================================== 18 | /** 19 | */ 20 | class NewProjectAudioProcessorEditor : public AudioProcessorEditor, public Slider::Listener 21 | { 22 | public: 23 | NewProjectAudioProcessorEditor (NewProjectAudioProcessor&); 24 | ~NewProjectAudioProcessorEditor(); 25 | 26 | //============================================================================== 27 | void paint (Graphics&) override; 28 | void resized() override; 29 | 30 | Slider bitRedux, rateRedux, noise; 31 | 32 | private: 33 | 34 | void sliderValueChanged (Slider* slider) override; 35 | 36 | // This reference is provided as a quick way for your editor to 37 | // access the processor object that created it. 38 | NewProjectAudioProcessor& processor; 39 | 40 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NewProjectAudioProcessorEditor) 41 | }; 42 | -------------------------------------------------------------------------------- /Source/PluginProcessor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated! 5 | 6 | It contains the basic framework code for a JUCE plugin processor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "PluginProcessor.h" 12 | #include "PluginEditor.h" 13 | 14 | static Array getSimpleNoise(int numSamples) 15 | { 16 | Random r = Random::getSystemRandom(); 17 | Array noise; 18 | 19 | for (int s=0; s < numSamples; s++) 20 | { 21 | noise.add((r.nextFloat() - .5)*2); 22 | } 23 | 24 | return noise; 25 | 26 | } 27 | 28 | static Array getWhiteNoise(int numSamples) { 29 | 30 | Array noise; 31 | 32 | float z0 = 0; 33 | float z1 = 0; 34 | bool generate = false; 35 | 36 | float mu = 0; // center (0) 37 | float sigma = 1; // spread -1 <-> 1 38 | 39 | float output = 0; 40 | float u1 = 0; 41 | float u2 = 0; 42 | 43 | Random r = Random::getSystemRandom(); 44 | r.setSeed(Time::getCurrentTime().getMilliseconds()); 45 | const float epsilon = std::numeric_limits::min(); 46 | 47 | for (int s=0; s < numSamples; s++) 48 | { 49 | 50 | // GENERATE :::: 51 | // using box muller method 52 | // https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform 53 | generate = !generate; 54 | 55 | if (!generate) 56 | output = z1 * sigma + mu; 57 | else 58 | { 59 | do 60 | { 61 | u1 = r.nextFloat(); 62 | u2 = r.nextFloat(); 63 | } 64 | while ( u1 <= epsilon ); 65 | 66 | z0 = sqrtf(-2.0 * logf(u1)) * cosf(2*float(double_Pi) * u2); 67 | z1 = sqrtf(-2.0 * logf(u1)) * sinf(2*float(double_Pi) * u2); 68 | 69 | output = z0 * sigma + mu; 70 | } 71 | 72 | // NAN check ... 73 | jassert(output == output); 74 | jassert(output > -50 && output < 50); 75 | 76 | // 77 | noise.add(output); 78 | 79 | } 80 | 81 | return noise; 82 | 83 | } 84 | 85 | 86 | 87 | //============================================================================== 88 | NewProjectAudioProcessor::NewProjectAudioProcessor() 89 | #ifndef JucePlugin_PreferredChannelConfigurations 90 | : AudioProcessor (BusesProperties() 91 | #if ! JucePlugin_IsMidiEffect 92 | #if ! JucePlugin_IsSynth 93 | .withInput ("Input", AudioChannelSet::stereo(), true) 94 | #endif 95 | .withOutput ("Output", AudioChannelSet::stereo(), true) 96 | #endif 97 | ) 98 | #endif 99 | { 100 | 101 | 102 | noiseAmount = new BitCrush_Parameter(); 103 | noiseAmount->defaultValue = 0; 104 | noiseAmount->currentValue = 0; 105 | noiseAmount->name = "Noise"; 106 | addParameter(noiseAmount); 107 | 108 | rateRedux = new BitCrush_Parameter(); 109 | rateRedux->defaultValue = 1; 110 | rateRedux->currentValue = 1; 111 | rateRedux->name = "Rate"; 112 | addParameter(rateRedux); 113 | 114 | 115 | bitRedux = new BitCrush_Parameter(); 116 | bitRedux->defaultValue = 32; 117 | bitRedux->currentValue = 32; 118 | bitRedux->name = "Bits"; 119 | addParameter(bitRedux); 120 | 121 | 122 | 123 | } 124 | 125 | NewProjectAudioProcessor::~NewProjectAudioProcessor() 126 | { 127 | } 128 | 129 | //============================================================================== 130 | const String NewProjectAudioProcessor::getName() const 131 | { 132 | return JucePlugin_Name; 133 | } 134 | 135 | bool NewProjectAudioProcessor::acceptsMidi() const 136 | { 137 | #if JucePlugin_WantsMidiInput 138 | return true; 139 | #else 140 | return false; 141 | #endif 142 | } 143 | 144 | bool NewProjectAudioProcessor::producesMidi() const 145 | { 146 | #if JucePlugin_ProducesMidiOutput 147 | return true; 148 | #else 149 | return false; 150 | #endif 151 | } 152 | 153 | bool NewProjectAudioProcessor::isMidiEffect() const 154 | { 155 | #if JucePlugin_IsMidiEffect 156 | return true; 157 | #else 158 | return false; 159 | #endif 160 | } 161 | 162 | double NewProjectAudioProcessor::getTailLengthSeconds() const 163 | { 164 | return 0.0; 165 | } 166 | 167 | int NewProjectAudioProcessor::getNumPrograms() 168 | { 169 | return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, 170 | // so this should be at least 1, even if you're not really implementing programs. 171 | } 172 | 173 | int NewProjectAudioProcessor::getCurrentProgram() 174 | { 175 | return 0; 176 | } 177 | 178 | void NewProjectAudioProcessor::setCurrentProgram (int index) 179 | { 180 | } 181 | 182 | const String NewProjectAudioProcessor::getProgramName (int index) 183 | { 184 | return {}; 185 | } 186 | 187 | void NewProjectAudioProcessor::changeProgramName (int index, const String& newName) 188 | { 189 | } 190 | 191 | //============================================================================== 192 | void NewProjectAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) 193 | { 194 | // Use this method as the place to do any pre-playback 195 | // initialisation that you need.. 196 | } 197 | 198 | void NewProjectAudioProcessor::releaseResources() 199 | { 200 | // When playback stops, you can use this as an opportunity to free up any 201 | // spare memory, etc. 202 | } 203 | 204 | #ifndef JucePlugin_PreferredChannelConfigurations 205 | bool NewProjectAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const 206 | { 207 | #if JucePlugin_IsMidiEffect 208 | ignoreUnused (layouts); 209 | return true; 210 | #else 211 | // This is the place where you check if the layout is supported. 212 | // In this template code we only support mono or stereo. 213 | if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono() 214 | && layouts.getMainOutputChannelSet() != AudioChannelSet::stereo()) 215 | return false; 216 | 217 | // This checks if the input layout matches the output layout 218 | #if ! JucePlugin_IsSynth 219 | if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) 220 | return false; 221 | #endif 222 | 223 | return true; 224 | #endif 225 | } 226 | #endif 227 | 228 | void NewProjectAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) 229 | { 230 | ScopedNoDenormals noDenormals; 231 | const int totalNumInputChannels = getTotalNumInputChannels(); 232 | const int totalNumOutputChannels = getTotalNumOutputChannels(); 233 | 234 | // In case we have more outputs than inputs, this code clears any output 235 | // channels that didn't contain input data, (because these aren't 236 | // guaranteed to be empty - they may contain garbage). 237 | // This is here to avoid people getting screaming feedback 238 | // when they first compile a plugin, but obviously you don't need to keep 239 | // this code if your algorithm always overwrites all the output channels. 240 | for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i) 241 | buffer.clear (i, 0, buffer.getNumSamples()); 242 | 243 | 244 | 245 | int numSamples = buffer.getNumSamples(); 246 | 247 | float noiseAmt = -120 + 120*(noiseAmount->getValue()/100); // dB 248 | float bitDepth = bitRedux->getValue(); 249 | int rateDivide = rateRedux->getValue(); 250 | 251 | // GET PARAMETERS ::::: 252 | noiseAmt = jlimit(-120, 0, noiseAmt); 253 | noiseAmt = Decibels::decibelsToGain(noiseAmt); 254 | 255 | 256 | 257 | // SAFETY CHECK :::: since some hosts will change buffer sizes without calling prepToPlay (Bitwig) 258 | if (noiseBuffer.getNumSamples() != numSamples) 259 | { 260 | noiseBuffer.setSize(2, numSamples, false, true, true); // clears 261 | currentOutputBuffer.setSize(2, numSamples, false, true, true); // clears 262 | } 263 | 264 | 265 | // COPY for processing ... 266 | currentOutputBuffer.copyFrom(0, 0, buffer.getReadPointer(0), numSamples); 267 | if (buffer.getNumChannels() > 1) currentOutputBuffer.copyFrom(1, 0, buffer.getReadPointer(1), numSamples); 268 | 269 | 270 | 271 | // BUILD NOISE :::::: 272 | { 273 | noiseBuffer.clear(); 274 | 275 | Array noise = getWhiteNoise(numSamples); 276 | 277 | // range bound 278 | noiseAmt = jlimit(0, 1, noiseAmt); 279 | 280 | FloatVectorOperations::multiply(noise.getRawDataPointer(), noiseAmt, numSamples); 281 | 282 | // ADD the noise ... 283 | FloatVectorOperations::add(noiseBuffer.getWritePointer(0), noise.getRawDataPointer(), numSamples); 284 | FloatVectorOperations::add(noiseBuffer.getWritePointer(1), noise.getRawDataPointer(), numSamples); // STEREO 285 | 286 | // MULTIPLY MODE ::::: 287 | // Multiply the noise by the signal ... so 0 signal -> 0 noise 288 | // { 289 | // FloatVectorOperations::multiply(noiseBuffer.getWritePointer(0), currentOutputBuffer.getWritePointer(0), numSamples); 290 | // FloatVectorOperations::multiply(noiseBuffer.getWritePointer(1), currentOutputBuffer.getWritePointer(1), numSamples); 291 | // } 292 | 293 | } 294 | 295 | 296 | // ADD NOISE to the incoming AUDIO :::: 297 | currentOutputBuffer.addFrom(0, 0, noiseBuffer.getReadPointer(0), numSamples); 298 | currentOutputBuffer.addFrom(1, 0, noiseBuffer.getReadPointer(1), numSamples); 299 | 300 | 301 | 302 | // RESAMPLE AS NEEDED ::::: 303 | for (int chan=0; chan < currentOutputBuffer.getNumChannels(); chan++) 304 | { 305 | float* data = currentOutputBuffer.getWritePointer(chan); 306 | 307 | for (int i=0; i < numSamples; i++) 308 | { 309 | // REDUCE BIT DEPTH ::::: 310 | float totalQLevels = powf(2, bitDepth); 311 | float val = data[i]; 312 | float remainder = fmodf(val, 1/totalQLevels); 313 | 314 | // Quantize ... 315 | data[i] = val - remainder; 316 | 317 | if (rateDivide > 1) 318 | { 319 | if (i%rateDivide != 0) data[i] = data[i - i%rateDivide]; 320 | } 321 | } 322 | } 323 | 324 | 325 | 326 | // COPY to the actual output buffer ::: 327 | buffer.copyFrom(0, 0, currentOutputBuffer, 0, 0, numSamples); 328 | buffer.copyFrom(1, 0, currentOutputBuffer, 1, 0, numSamples); 329 | 330 | 331 | } 332 | 333 | //============================================================================== 334 | bool NewProjectAudioProcessor::hasEditor() const 335 | { 336 | return true; // (change this to false if you choose to not supply an editor) 337 | } 338 | 339 | AudioProcessorEditor* NewProjectAudioProcessor::createEditor() 340 | { 341 | return new NewProjectAudioProcessorEditor (*this); 342 | } 343 | 344 | //============================================================================== 345 | void NewProjectAudioProcessor::getStateInformation (MemoryBlock& destData) 346 | { 347 | // You should use this method to store your parameters in the memory block. 348 | // You could do that either as raw data, or use the XML or ValueTree classes 349 | // as intermediaries to make it easy to save and load complex data. 350 | } 351 | 352 | void NewProjectAudioProcessor::setStateInformation (const void* data, int sizeInBytes) 353 | { 354 | // You should use this method to restore your parameters from this memory block, 355 | // whose contents will have been created by the getStateInformation() call. 356 | } 357 | 358 | //============================================================================== 359 | // This creates new instances of the plugin.. 360 | AudioProcessor* JUCE_CALLTYPE createPluginFilter() 361 | { 362 | return new NewProjectAudioProcessor(); 363 | } 364 | -------------------------------------------------------------------------------- /Source/PluginProcessor.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated! 5 | 6 | It contains the basic framework code for a JUCE plugin processor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "../JuceLibraryCode/JuceHeader.h" 14 | 15 | class BitCrush_Parameter : public AudioProcessorParameter 16 | { 17 | 18 | public: 19 | 20 | float defaultValue{0}; 21 | float currentValue{0}; 22 | String name; 23 | 24 | float getValue() const override 25 | { 26 | return currentValue; 27 | } 28 | 29 | void setValue (float newValue) override 30 | { 31 | currentValue = newValue; 32 | } 33 | 34 | float getDefaultValue () const override 35 | { 36 | return defaultValue; 37 | } 38 | 39 | String getName (int maximumStringLength) const override 40 | { 41 | return name; 42 | } 43 | 44 | String getLabel () const override 45 | { 46 | return getName(10); 47 | } 48 | 49 | float getValueForText (const String &text) const override 50 | { 51 | return 1; 52 | } 53 | }; 54 | 55 | //============================================================================== 56 | /** 57 | */ 58 | class NewProjectAudioProcessor : public AudioProcessor 59 | { 60 | 61 | AudioSampleBuffer noiseBuffer, currentOutputBuffer; 62 | 63 | BitCrush_Parameter* noiseAmount; 64 | BitCrush_Parameter* rateRedux; 65 | BitCrush_Parameter* bitRedux; 66 | 67 | public: 68 | //============================================================================== 69 | NewProjectAudioProcessor(); 70 | ~NewProjectAudioProcessor(); 71 | 72 | //============================================================================== 73 | void prepareToPlay (double sampleRate, int samplesPerBlock) override; 74 | void releaseResources() override; 75 | 76 | #ifndef JucePlugin_PreferredChannelConfigurations 77 | bool isBusesLayoutSupported (const BusesLayout& layouts) const override; 78 | #endif 79 | 80 | void processBlock (AudioSampleBuffer&, MidiBuffer&) override; 81 | 82 | //============================================================================== 83 | AudioProcessorEditor* createEditor() override; 84 | bool hasEditor() const override; 85 | 86 | //============================================================================== 87 | const String getName() const override; 88 | 89 | bool acceptsMidi() const override; 90 | bool producesMidi() const override; 91 | bool isMidiEffect () const override; 92 | double getTailLengthSeconds() const override; 93 | 94 | //============================================================================== 95 | int getNumPrograms() override; 96 | int getCurrentProgram() override; 97 | void setCurrentProgram (int index) override; 98 | const String getProgramName (int index) override; 99 | void changeProgramName (int index, const String& newName) override; 100 | 101 | //============================================================================== 102 | void getStateInformation (MemoryBlock& destData) override; 103 | void setStateInformation (const void* data, int sizeInBytes) override; 104 | 105 | private: 106 | //============================================================================== 107 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NewProjectAudioProcessor) 108 | }; 109 | --------------------------------------------------------------------------------