├── .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 |
--------------------------------------------------------------------------------