mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
569 lines
25 KiB
C++
569 lines
25 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE examples.
|
|
Copyright (c) 2017 - ROLI Ltd.
|
|
|
|
The code included in this file is provided under the terms of the ISC license
|
|
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
|
To use, copy, modify, and/or distribute this software for any purpose with or
|
|
without fee is hereby granted provided that the above copyright notice and
|
|
this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
|
|
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
|
|
PURPOSE, ARE DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
/*******************************************************************************
|
|
The block below describes the properties of this PIP. A PIP is a short snippet
|
|
of code that can be read by the Projucer and used to generate a JUCE project.
|
|
|
|
BEGIN_JUCE_PIP_METADATA
|
|
|
|
name: DSPModulePluginDemo
|
|
version: 1.0.0
|
|
vendor: JUCE
|
|
website: http://juce.com
|
|
description: Audio plugin using the DSP module.
|
|
|
|
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
|
|
juce_audio_plugin_client, juce_audio_processors,
|
|
juce_audio_utils, juce_core, juce_data_structures, juce_dsp,
|
|
juce_events, juce_graphics, juce_gui_basics, juce_gui_extra
|
|
exporters: xcode_mac, vs2019
|
|
|
|
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
|
|
|
|
type: AudioProcessor
|
|
mainClass: DspModulePluginDemoAudioProcessor
|
|
|
|
useLocalCopy: 1
|
|
|
|
END_JUCE_PIP_METADATA
|
|
|
|
*******************************************************************************/
|
|
|
|
#pragma once
|
|
|
|
#include "../Assets/DemoUtilities.h"
|
|
|
|
//==============================================================================
|
|
struct ParameterSlider : public Slider,
|
|
public Timer
|
|
{
|
|
ParameterSlider (AudioProcessorParameter& p)
|
|
: Slider (p.getName (256)), param (p)
|
|
{
|
|
setRange (0.0, 1.0, 0.0);
|
|
startTimerHz (30);
|
|
updateSliderPos();
|
|
}
|
|
|
|
void valueChanged() override
|
|
{
|
|
if (isMouseButtonDown())
|
|
param.setValueNotifyingHost ((float) Slider::getValue());
|
|
else
|
|
param.setValue ((float) Slider::getValue());
|
|
}
|
|
|
|
void timerCallback() override { updateSliderPos(); }
|
|
|
|
void startedDragging() override { param.beginChangeGesture(); }
|
|
void stoppedDragging() override { param.endChangeGesture(); }
|
|
|
|
double getValueFromText (const String& text) override { return param.getValueForText (text); }
|
|
String getTextFromValue (double value) override { return param.getText ((float) value, 1024) + " " + param.getLabel(); }
|
|
|
|
void updateSliderPos()
|
|
{
|
|
auto newValue = param.getValue();
|
|
|
|
if (newValue != (float) Slider::getValue() && ! isMouseButtonDown())
|
|
Slider::setValue (newValue);
|
|
}
|
|
|
|
AudioProcessorParameter& param;
|
|
};
|
|
|
|
//==============================================================================
|
|
/**
|
|
This class handles the audio processing for the DSP module plugin demo.
|
|
*/
|
|
class DspModulePluginDemoAudioProcessor : public AudioProcessor
|
|
{
|
|
public:
|
|
//==============================================================================
|
|
DspModulePluginDemoAudioProcessor()
|
|
: AudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::stereo(), true)
|
|
.withOutput ("Output", AudioChannelSet::stereo(), true)),
|
|
lowPassFilter (dsp::IIR::Coefficients<float>::makeFirstOrderLowPass (48000.0, 20000.0f)),
|
|
highPassFilter (dsp::IIR::Coefficients<float>::makeFirstOrderHighPass (48000.0, 20.0f)),
|
|
waveShapers { { std::tanh }, { dsp::FastMathApproximations::tanh } },
|
|
clipping { clip }
|
|
{
|
|
// Oversampling 2 times with IIR filtering
|
|
oversampling.reset (new dsp::Oversampling<float> (2, 1, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, false));
|
|
|
|
addParameter (inputVolumeParam = new AudioParameterFloat ("INPUT", "Input Volume", { 0.0f, 60.0f, 0.0f, 1.0f }, 0.0f, "dB"));
|
|
addParameter (highPassFilterFreqParam = new AudioParameterFloat ("HPFREQ", "Pre Highpass Freq.", { 20.0f, 20000.0f, 0.0f, 0.5f }, 20.0f, "Hz"));
|
|
addParameter (lowPassFilterFreqParam = new AudioParameterFloat ("LPFREQ", "Post Lowpass Freq.", { 20.0f, 20000.0f, 0.0f, 0.5f }, 20000.0f, "Hz"));
|
|
|
|
addParameter (stereoParam = new AudioParameterChoice ("STEREO", "Stereo Processing", { "Always mono", "Yes" }, 1));
|
|
addParameter (slopeParam = new AudioParameterChoice ("SLOPE", "Slope", { "-6 dB / octave", "-12 dB / octave" }, 0));
|
|
addParameter (waveshaperParam = new AudioParameterChoice ("WVSHP", "Waveshaper", { "std::tanh", "Fast tanh approx." }, 0));
|
|
|
|
addParameter (cabinetTypeParam = new AudioParameterChoice ("CABTYPE", "Cabinet Type", { "Guitar amplifier 8'' cabinet ",
|
|
"Cassette recorder cabinet" }, 0));
|
|
|
|
addParameter (cabinetSimParam = new AudioParameterBool ("CABSIM", "Cabinet Sim", false));
|
|
addParameter (oversamplingParam = new AudioParameterBool ("OVERS", "Oversampling", false));
|
|
|
|
addParameter (outputVolumeParam = new AudioParameterFloat ("OUTPUT", "Output Volume", { -40.0f, 40.0f, 0.0f, 1.0f }, 0.0f, "dB"));
|
|
|
|
cabinetType.set (0);
|
|
}
|
|
|
|
~DspModulePluginDemoAudioProcessor() {}
|
|
|
|
//==============================================================================
|
|
bool isBusesLayoutSupported (const BusesLayout& layouts) const override
|
|
{
|
|
// This is the place where you check if the layout is supported.
|
|
// In this template code we only support mono or stereo.
|
|
if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono() && layouts.getMainOutputChannelSet() != AudioChannelSet::stereo())
|
|
return false;
|
|
|
|
// This checks if the input layout matches the output layout
|
|
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void prepareToPlay (double sampleRate, int samplesPerBlock) override
|
|
{
|
|
auto channels = static_cast<uint32> (jmin (getMainBusNumInputChannels(), getMainBusNumOutputChannels()));
|
|
dsp::ProcessSpec spec { sampleRate, static_cast<uint32> (samplesPerBlock), channels };
|
|
|
|
lowPassFilter .prepare (spec);
|
|
highPassFilter.prepare (spec);
|
|
|
|
inputVolume .prepare (spec);
|
|
outputVolume.prepare (spec);
|
|
|
|
convolution.prepare (spec);
|
|
cabinetType.set (-1);
|
|
|
|
oversampling->initProcessing (static_cast<size_t> (samplesPerBlock));
|
|
|
|
updateParameters();
|
|
reset();
|
|
}
|
|
|
|
void releaseResources() override {}
|
|
|
|
void processBlock (AudioBuffer<float>& inoutBuffer, MidiBuffer&) override
|
|
{
|
|
auto totalNumInputChannels = getTotalNumInputChannels();
|
|
auto totalNumOutputChannels = getTotalNumOutputChannels();
|
|
|
|
auto numSamples = inoutBuffer.getNumSamples();
|
|
|
|
for (auto i = jmin (2, totalNumInputChannels); i < totalNumOutputChannels; ++i)
|
|
inoutBuffer.clear (i, 0, numSamples);
|
|
|
|
updateParameters();
|
|
|
|
dsp::AudioBlock<float> block (inoutBuffer);
|
|
|
|
if (stereoParam->getIndex() == 1)
|
|
{
|
|
// Stereo processing mode:
|
|
if (block.getNumChannels() > 2)
|
|
block = block.getSubsetChannelBlock (0, 2);
|
|
|
|
process (dsp::ProcessContextReplacing<float> (block));
|
|
}
|
|
else
|
|
{
|
|
// Mono processing mode:
|
|
auto firstChan = block.getSingleChannelBlock (0);
|
|
|
|
process (dsp::ProcessContextReplacing<float> (firstChan));
|
|
|
|
for (size_t chan = 1; chan < block.getNumChannels(); ++chan)
|
|
block.getSingleChannelBlock (chan).copyFrom (firstChan);
|
|
}
|
|
}
|
|
|
|
void reset() override
|
|
{
|
|
lowPassFilter .reset();
|
|
highPassFilter.reset();
|
|
convolution .reset();
|
|
oversampling->reset();
|
|
}
|
|
|
|
//==============================================================================
|
|
bool hasEditor() const override { return true; }
|
|
|
|
AudioProcessorEditor* createEditor() override
|
|
{
|
|
return new DspModulePluginDemoAudioProcessorEditor (*this);
|
|
}
|
|
|
|
//==============================================================================
|
|
bool acceptsMidi() const override { return false; }
|
|
bool producesMidi() const override { return false; }
|
|
const String getName() const override { return JucePlugin_Name; }
|
|
double getTailLengthSeconds() const override { return 0.0; }
|
|
|
|
//==============================================================================
|
|
int getNumPrograms() override { return 1; }
|
|
int getCurrentProgram() override { return 0; }
|
|
void setCurrentProgram (int) override {}
|
|
const String getProgramName (int) override { return {}; }
|
|
void changeProgramName (int, const String&) override {}
|
|
|
|
//==============================================================================
|
|
void getStateInformation (MemoryBlock&) override {}
|
|
void setStateInformation (const void*, int) override {}
|
|
|
|
//==============================================================================
|
|
void updateParameters()
|
|
{
|
|
auto newOversampling = oversamplingParam->get();
|
|
if (newOversampling != audioCurrentlyOversampled)
|
|
{
|
|
audioCurrentlyOversampled = newOversampling;
|
|
oversampling->reset();
|
|
}
|
|
|
|
//==============================================================================
|
|
auto inputdB = Decibels::decibelsToGain (inputVolumeParam->get());
|
|
auto outputdB = Decibels::decibelsToGain (outputVolumeParam->get());
|
|
|
|
if (inputVolume .getGainLinear() != inputdB) inputVolume.setGainLinear (inputdB);
|
|
if (outputVolume.getGainLinear() != outputdB) outputVolume.setGainLinear (outputdB);
|
|
|
|
auto newSlopeType = slopeParam->getIndex();
|
|
|
|
if (newSlopeType == 0)
|
|
{
|
|
*lowPassFilter .state = *dsp::IIR::Coefficients<float>::makeFirstOrderLowPass (getSampleRate(), lowPassFilterFreqParam->get());
|
|
*highPassFilter.state = *dsp::IIR::Coefficients<float>::makeFirstOrderHighPass (getSampleRate(), highPassFilterFreqParam->get());
|
|
}
|
|
else
|
|
{
|
|
*lowPassFilter .state = *dsp::IIR::Coefficients<float>::makeLowPass (getSampleRate(), lowPassFilterFreqParam->get());
|
|
*highPassFilter.state = *dsp::IIR::Coefficients<float>::makeHighPass (getSampleRate(), highPassFilterFreqParam->get());
|
|
}
|
|
|
|
//==============================================================================
|
|
auto type = cabinetTypeParam->getIndex();
|
|
auto currentType = cabinetType.get();
|
|
|
|
if (type != currentType)
|
|
{
|
|
cabinetType.set (type);
|
|
|
|
auto maxSize = static_cast<size_t> (roundToInt (getSampleRate() * (8192.0 / 44100.0)));
|
|
auto assetName = (type == 0 ? "Impulse1.wav" : "Impulse2.wav");
|
|
|
|
std::unique_ptr<InputStream> assetInputStream (createAssetInputStream (assetName));
|
|
|
|
if (assetInputStream != nullptr)
|
|
{
|
|
currentCabinetData.reset();
|
|
assetInputStream->readIntoMemoryBlock (currentCabinetData);
|
|
|
|
convolution.loadImpulseResponse (currentCabinetData.getData(), currentCabinetData.getSize(),
|
|
false, true, maxSize);
|
|
}
|
|
}
|
|
|
|
cabinetIsBypassed = ! cabinetSimParam->get();
|
|
|
|
}
|
|
|
|
static inline float clip (float x) { return jmax (-1.0f, jmin (1.0f, x)); }
|
|
|
|
//==============================================================================
|
|
AudioParameterFloat* inputVolumeParam;
|
|
AudioParameterFloat* outputVolumeParam;
|
|
AudioParameterFloat* lowPassFilterFreqParam;
|
|
AudioParameterFloat* highPassFilterFreqParam;
|
|
|
|
AudioParameterChoice* stereoParam;
|
|
AudioParameterChoice* slopeParam;
|
|
AudioParameterChoice* waveshaperParam;
|
|
AudioParameterChoice* cabinetTypeParam;
|
|
|
|
AudioParameterBool* cabinetSimParam;
|
|
AudioParameterBool* oversamplingParam;
|
|
|
|
private:
|
|
//==============================================================================
|
|
/**
|
|
This is the editor component that will be displayed.
|
|
*/
|
|
class DspModulePluginDemoAudioProcessorEditor : public AudioProcessorEditor
|
|
{
|
|
public:
|
|
//==============================================================================
|
|
DspModulePluginDemoAudioProcessorEditor (DspModulePluginDemoAudioProcessor& p)
|
|
: AudioProcessorEditor (&p),
|
|
processor (p),
|
|
inputVolumeLabel ({}, processor.inputVolumeParam->name),
|
|
outputVolumeLabel ({}, processor.outputVolumeParam->name),
|
|
lowPassFilterFreqLabel ({}, processor.lowPassFilterFreqParam->name),
|
|
highPassFilterFreqLabel ({}, processor.highPassFilterFreqParam->name),
|
|
stereoLabel ({}, processor.stereoParam->name),
|
|
slopeLabel ({}, processor.slopeParam->name),
|
|
waveshaperLabel ({}, processor.waveshaperParam->name),
|
|
cabinetTypeLabel ({}, processor.cabinetTypeParam->name)
|
|
{
|
|
//==============================================================================
|
|
inputVolumeSlider .reset (new ParameterSlider (*processor.inputVolumeParam));
|
|
outputVolumeSlider .reset (new ParameterSlider (*processor.outputVolumeParam));
|
|
lowPassFilterFreqSlider .reset (new ParameterSlider (*processor.lowPassFilterFreqParam));
|
|
highPassFilterFreqSlider.reset (new ParameterSlider (*processor.highPassFilterFreqParam));
|
|
|
|
addAndMakeVisible (inputVolumeSlider .get());
|
|
addAndMakeVisible (outputVolumeSlider .get());
|
|
addAndMakeVisible (lowPassFilterFreqSlider .get());
|
|
addAndMakeVisible (highPassFilterFreqSlider.get());
|
|
|
|
addAndMakeVisible (inputVolumeLabel);
|
|
inputVolumeLabel.setJustificationType (Justification::centredLeft);
|
|
inputVolumeLabel.attachToComponent (inputVolumeSlider.get(), true);
|
|
|
|
addAndMakeVisible (outputVolumeLabel);
|
|
outputVolumeLabel.setJustificationType (Justification::centredLeft);
|
|
outputVolumeLabel.attachToComponent (outputVolumeSlider.get(), true);
|
|
|
|
addAndMakeVisible (lowPassFilterFreqLabel);
|
|
lowPassFilterFreqLabel.setJustificationType (Justification::centredLeft);
|
|
lowPassFilterFreqLabel.attachToComponent (lowPassFilterFreqSlider.get(), true);
|
|
|
|
addAndMakeVisible (highPassFilterFreqLabel);
|
|
highPassFilterFreqLabel.setJustificationType (Justification::centredLeft);
|
|
highPassFilterFreqLabel.attachToComponent (highPassFilterFreqSlider.get(), true);
|
|
|
|
//==============================================================================
|
|
addAndMakeVisible (stereoBox);
|
|
|
|
auto i = 1;
|
|
for (auto choice : processor.stereoParam->choices)
|
|
stereoBox.addItem (choice, i++);
|
|
|
|
stereoBox.onChange = [this] { processor.stereoParam->operator= (stereoBox.getSelectedItemIndex()); };
|
|
stereoBox.setSelectedId (processor.stereoParam->getIndex() + 1);
|
|
|
|
addAndMakeVisible (stereoLabel);
|
|
stereoLabel.setJustificationType (Justification::centredLeft);
|
|
stereoLabel.attachToComponent (&stereoBox, true);
|
|
|
|
//==============================================================================
|
|
addAndMakeVisible(slopeBox);
|
|
|
|
i = 1;
|
|
for (auto choice : processor.slopeParam->choices)
|
|
slopeBox.addItem(choice, i++);
|
|
|
|
slopeBox.onChange = [this] { processor.slopeParam->operator= (slopeBox.getSelectedItemIndex()); };
|
|
slopeBox.setSelectedId(processor.slopeParam->getIndex() + 1);
|
|
|
|
addAndMakeVisible(slopeLabel);
|
|
slopeLabel.setJustificationType(Justification::centredLeft);
|
|
slopeLabel.attachToComponent(&slopeBox, true);
|
|
|
|
//==============================================================================
|
|
addAndMakeVisible (waveshaperBox);
|
|
|
|
i = 1;
|
|
for (auto choice : processor.waveshaperParam->choices)
|
|
waveshaperBox.addItem (choice, i++);
|
|
|
|
waveshaperBox.onChange = [this] { processor.waveshaperParam->operator= (waveshaperBox.getSelectedItemIndex()); };
|
|
waveshaperBox.setSelectedId (processor.waveshaperParam->getIndex() + 1);
|
|
|
|
addAndMakeVisible (waveshaperLabel);
|
|
waveshaperLabel.setJustificationType (Justification::centredLeft);
|
|
waveshaperLabel.attachToComponent (&waveshaperBox, true);
|
|
|
|
//==============================================================================
|
|
addAndMakeVisible (cabinetTypeBox);
|
|
|
|
i = 1;
|
|
for (auto choice : processor.cabinetTypeParam->choices)
|
|
cabinetTypeBox.addItem (choice, i++);
|
|
|
|
cabinetTypeBox.onChange = [this] { processor.cabinetTypeParam->operator= (cabinetTypeBox.getSelectedItemIndex()); };
|
|
cabinetTypeBox.setSelectedId (processor.cabinetTypeParam->getIndex() + 1);
|
|
|
|
addAndMakeVisible (cabinetTypeLabel);
|
|
cabinetTypeLabel.setJustificationType (Justification::centredLeft);
|
|
cabinetTypeLabel.attachToComponent (&cabinetTypeBox, true);
|
|
|
|
//==============================================================================
|
|
addAndMakeVisible (cabinetSimButton);
|
|
cabinetSimButton.onClick = [this] { processor.cabinetSimParam->operator= (cabinetSimButton.getToggleState()); };
|
|
cabinetSimButton.setButtonText (processor.cabinetSimParam->name);
|
|
cabinetSimButton.setToggleState (processor.cabinetSimParam->get(), NotificationType::dontSendNotification);
|
|
|
|
addAndMakeVisible (oversamplingButton);
|
|
oversamplingButton.onClick = [this] { processor.oversamplingParam->operator= (oversamplingButton.getToggleState()); };
|
|
oversamplingButton.setButtonText (processor.oversamplingParam->name);
|
|
oversamplingButton.setToggleState (processor.oversamplingParam->get(), NotificationType::dontSendNotification);
|
|
|
|
//==============================================================================
|
|
setSize (600, 400);
|
|
}
|
|
|
|
~DspModulePluginDemoAudioProcessorEditor() {}
|
|
|
|
//==============================================================================
|
|
void paint (Graphics& g) override
|
|
{
|
|
g.setColour (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
|
g.fillAll();
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
auto bounds = getLocalBounds().reduced (10);
|
|
bounds.removeFromTop (10);
|
|
bounds.removeFromLeft (125);
|
|
|
|
//==============================================================================
|
|
inputVolumeSlider->setBounds (bounds.removeFromTop (30));
|
|
bounds.removeFromTop (5);
|
|
|
|
outputVolumeSlider->setBounds (bounds.removeFromTop (30));
|
|
bounds.removeFromTop (15);
|
|
|
|
highPassFilterFreqSlider->setBounds (bounds.removeFromTop (30));
|
|
bounds.removeFromTop (5);
|
|
|
|
lowPassFilterFreqSlider->setBounds (bounds.removeFromTop (30));
|
|
bounds.removeFromTop (15);
|
|
|
|
//==============================================================================
|
|
stereoBox.setBounds (bounds.removeFromTop(30));
|
|
bounds.removeFromTop (5);
|
|
|
|
slopeBox.setBounds (bounds.removeFromTop (30));
|
|
bounds.removeFromTop (5);
|
|
|
|
waveshaperBox.setBounds (bounds.removeFromTop (30));
|
|
bounds.removeFromTop (5);
|
|
|
|
cabinetTypeBox.setBounds (bounds.removeFromTop (30));
|
|
bounds.removeFromTop (15);
|
|
|
|
//==============================================================================
|
|
auto buttonSlice = bounds.removeFromTop (30);
|
|
cabinetSimButton.setSize (200, buttonSlice.getHeight());
|
|
cabinetSimButton.setCentrePosition (buttonSlice.getCentre());
|
|
bounds.removeFromTop(5);
|
|
|
|
buttonSlice = bounds.removeFromTop (30);
|
|
oversamplingButton.setSize(200, buttonSlice.getHeight());
|
|
oversamplingButton.setCentrePosition(buttonSlice.getCentre());
|
|
}
|
|
|
|
private:
|
|
//==============================================================================
|
|
DspModulePluginDemoAudioProcessor& processor;
|
|
|
|
std::unique_ptr<ParameterSlider> inputVolumeSlider, outputVolumeSlider,
|
|
lowPassFilterFreqSlider, highPassFilterFreqSlider;
|
|
ComboBox stereoBox, slopeBox, waveshaperBox, cabinetTypeBox;
|
|
ToggleButton cabinetSimButton, oversamplingButton;
|
|
|
|
Label inputVolumeLabel, outputVolumeLabel, lowPassFilterFreqLabel,
|
|
highPassFilterFreqLabel, stereoLabel, slopeLabel, waveshaperLabel,
|
|
cabinetTypeLabel;
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DspModulePluginDemoAudioProcessorEditor)
|
|
};
|
|
|
|
//==============================================================================
|
|
void process (dsp::ProcessContextReplacing<float> context) noexcept
|
|
{
|
|
ScopedNoDenormals noDenormals;
|
|
|
|
// Input volume applied with a SmoothedValue
|
|
inputVolume.process (context);
|
|
|
|
// Pre-highpass filtering, very useful for distortion audio effects
|
|
// Note : try frequencies around 700 Hz
|
|
highPassFilter.process (context);
|
|
|
|
// Upsampling
|
|
dsp::AudioBlock<float> oversampledBlock;
|
|
|
|
setLatencySamples (audioCurrentlyOversampled ? roundToInt (oversampling->getLatencyInSamples()) : 0);
|
|
|
|
if (audioCurrentlyOversampled)
|
|
oversampledBlock = oversampling->processSamplesUp (context.getOutputBlock());
|
|
|
|
auto waveshaperContext = audioCurrentlyOversampled ? dsp::ProcessContextReplacing<float> (oversampledBlock)
|
|
: context;
|
|
|
|
// Waveshaper processing, for distortion generation, thanks to the input gain
|
|
// The fast tanh can be used instead of std::tanh to reduce the CPU load
|
|
auto waveshaperIndex = waveshaperParam->getIndex();
|
|
|
|
if (isPositiveAndBelow (waveshaperIndex, numWaveShapers) )
|
|
{
|
|
waveShapers[waveshaperIndex].process (waveshaperContext);
|
|
|
|
if (waveshaperIndex == 1)
|
|
clipping.process (waveshaperContext);
|
|
|
|
waveshaperContext.getOutputBlock() *= 0.7f;
|
|
}
|
|
|
|
// Downsampling
|
|
if (audioCurrentlyOversampled)
|
|
oversampling->processSamplesDown (context.getOutputBlock());
|
|
|
|
// Post-lowpass filtering
|
|
lowPassFilter.process (context);
|
|
|
|
// Convolution with the impulse response of a guitar cabinet
|
|
auto wasBypassed = context.isBypassed;
|
|
context.isBypassed = context.isBypassed || cabinetIsBypassed;
|
|
convolution.process (context);
|
|
context.isBypassed = wasBypassed;
|
|
|
|
// Output volume applied with a SmoothedValue
|
|
outputVolume.process (context);
|
|
}
|
|
|
|
//==============================================================================
|
|
dsp::ProcessorDuplicator<dsp::IIR::Filter<float>, dsp::IIR::Coefficients<float>> lowPassFilter, highPassFilter;
|
|
dsp::Convolution convolution;
|
|
MemoryBlock currentCabinetData;
|
|
|
|
static constexpr size_t numWaveShapers = 2;
|
|
dsp::WaveShaper<float> waveShapers[numWaveShapers];
|
|
dsp::WaveShaper<float> clipping;
|
|
|
|
dsp::Gain<float> inputVolume, outputVolume;
|
|
|
|
std::unique_ptr<dsp::Oversampling<float>> oversampling;
|
|
bool audioCurrentlyOversampled = false;
|
|
|
|
Atomic<int> cabinetType;
|
|
bool cabinetIsBypassed = false;
|
|
|
|
//==============================================================================
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DspModulePluginDemoAudioProcessor)
|
|
};
|