mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
1892 lines
84 KiB
C++
1892 lines
84 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE examples.
|
|
Copyright (c) 2020 - Raw Material Software Limited
|
|
|
|
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: An 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, linux_make
|
|
|
|
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
|
|
|
|
type: AudioProcessor
|
|
mainClass: DspModulePluginDemoAudioProcessor
|
|
|
|
useLocalCopy: 1
|
|
|
|
END_JUCE_PIP_METADATA
|
|
|
|
*******************************************************************************/
|
|
|
|
#pragma once
|
|
|
|
#include "../Assets/DemoUtilities.h"
|
|
|
|
namespace ID
|
|
{
|
|
#define PARAMETER_ID(str) constexpr const char* str { #str };
|
|
|
|
PARAMETER_ID (inputGain)
|
|
PARAMETER_ID (outputGain)
|
|
PARAMETER_ID (pan)
|
|
PARAMETER_ID (distortionEnabled)
|
|
PARAMETER_ID (distortionType)
|
|
PARAMETER_ID (distortionOversampler)
|
|
PARAMETER_ID (distortionLowpass)
|
|
PARAMETER_ID (distortionHighpass)
|
|
PARAMETER_ID (distortionInGain)
|
|
PARAMETER_ID (distortionCompGain)
|
|
PARAMETER_ID (distortionMix)
|
|
PARAMETER_ID (multiBandEnabled)
|
|
PARAMETER_ID (multiBandFreq)
|
|
PARAMETER_ID (multiBandLowVolume)
|
|
PARAMETER_ID (multiBandHighVolume)
|
|
PARAMETER_ID (compressorEnabled)
|
|
PARAMETER_ID (compressorThreshold)
|
|
PARAMETER_ID (compressorRatio)
|
|
PARAMETER_ID (compressorAttack)
|
|
PARAMETER_ID (compressorRelease)
|
|
PARAMETER_ID (noiseGateEnabled)
|
|
PARAMETER_ID (noiseGateThreshold)
|
|
PARAMETER_ID (noiseGateRatio)
|
|
PARAMETER_ID (noiseGateAttack)
|
|
PARAMETER_ID (noiseGateRelease)
|
|
PARAMETER_ID (limiterEnabled)
|
|
PARAMETER_ID (limiterThreshold)
|
|
PARAMETER_ID (limiterRelease)
|
|
PARAMETER_ID (directDelayEnabled)
|
|
PARAMETER_ID (directDelayType)
|
|
PARAMETER_ID (directDelayValue)
|
|
PARAMETER_ID (directDelaySmoothing)
|
|
PARAMETER_ID (directDelayMix)
|
|
PARAMETER_ID (delayEffectEnabled)
|
|
PARAMETER_ID (delayEffectType)
|
|
PARAMETER_ID (delayEffectValue)
|
|
PARAMETER_ID (delayEffectSmoothing)
|
|
PARAMETER_ID (delayEffectLowpass)
|
|
PARAMETER_ID (delayEffectFeedback)
|
|
PARAMETER_ID (delayEffectMix)
|
|
PARAMETER_ID (phaserEnabled)
|
|
PARAMETER_ID (phaserRate)
|
|
PARAMETER_ID (phaserDepth)
|
|
PARAMETER_ID (phaserCentreFrequency)
|
|
PARAMETER_ID (phaserFeedback)
|
|
PARAMETER_ID (phaserMix)
|
|
PARAMETER_ID (chorusEnabled)
|
|
PARAMETER_ID (chorusRate)
|
|
PARAMETER_ID (chorusDepth)
|
|
PARAMETER_ID (chorusCentreDelay)
|
|
PARAMETER_ID (chorusFeedback)
|
|
PARAMETER_ID (chorusMix)
|
|
PARAMETER_ID (ladderEnabled)
|
|
PARAMETER_ID (ladderCutoff)
|
|
PARAMETER_ID (ladderResonance)
|
|
PARAMETER_ID (ladderDrive)
|
|
PARAMETER_ID (ladderMode)
|
|
|
|
#undef PARAMETER_ID
|
|
}
|
|
|
|
template <typename Func, typename... Items>
|
|
constexpr void forEach (Func&& func, Items&&... items)
|
|
noexcept (noexcept (std::initializer_list<int> { (func (std::forward<Items> (items)), 0)... }))
|
|
{
|
|
(void) std::initializer_list<int> { ((void) func (std::forward<Items> (items)), 0)... };
|
|
}
|
|
|
|
template <typename... Components>
|
|
void addAllAndMakeVisible (Component& target, Components&... children)
|
|
{
|
|
forEach ([&] (Component& child) { target.addAndMakeVisible (child); }, children...);
|
|
}
|
|
|
|
template <typename... Processors>
|
|
void prepareAll (const dsp::ProcessSpec& spec, Processors&... processors)
|
|
{
|
|
forEach ([&] (auto& proc) { proc.prepare (spec); }, processors...);
|
|
}
|
|
|
|
template <typename... Processors>
|
|
void resetAll (Processors&... processors)
|
|
{
|
|
forEach ([] (auto& proc) { proc.reset(); }, processors...);
|
|
}
|
|
|
|
//==============================================================================
|
|
class DspModulePluginDemo : public AudioProcessor,
|
|
private ValueTree::Listener
|
|
{
|
|
public:
|
|
DspModulePluginDemo()
|
|
{
|
|
apvts.state.addListener (this);
|
|
|
|
forEach ([] (dsp::Gain<float>& gain) { gain.setRampDurationSeconds (0.05); },
|
|
dsp::get<inputGainIndex> (chain),
|
|
dsp::get<outputGainIndex> (chain));
|
|
|
|
dsp::get<pannerIndex> (chain).setRule (dsp::PannerRule::linear);
|
|
}
|
|
|
|
//==============================================================================
|
|
void prepareToPlay (double sampleRate, int samplesPerBlock) override
|
|
{
|
|
if (jmin (getTotalNumInputChannels(), getTotalNumOutputChannels()) == 0)
|
|
return;
|
|
|
|
chain.prepare ({ sampleRate, (uint32) samplesPerBlock, (uint32) getTotalNumOutputChannels() });
|
|
|
|
reset();
|
|
}
|
|
|
|
void reset() override
|
|
{
|
|
chain.reset();
|
|
update();
|
|
}
|
|
|
|
void releaseResources() override {}
|
|
|
|
void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
|
|
{
|
|
if (jmin (getTotalNumInputChannels(), getTotalNumOutputChannels()) == 0)
|
|
return;
|
|
|
|
ScopedNoDenormals noDenormals;
|
|
|
|
if (requiresUpdate.load())
|
|
update();
|
|
|
|
const auto totalNumInputChannels = getTotalNumInputChannels();
|
|
const auto totalNumOutputChannels = getTotalNumOutputChannels();
|
|
|
|
const auto numChannels = jmax (totalNumInputChannels, totalNumOutputChannels);
|
|
|
|
auto inoutBlock = dsp::AudioBlock<float> (buffer).getSubsetChannelBlock (0, (size_t) numChannels);
|
|
chain.process (dsp::ProcessContextReplacing<float> (inoutBlock));
|
|
}
|
|
|
|
void processBlock (AudioBuffer<double>&, MidiBuffer&) override {}
|
|
|
|
//==============================================================================
|
|
AudioProcessorEditor* createEditor() override { return nullptr; }
|
|
bool hasEditor() const override { return false; }
|
|
|
|
//==============================================================================
|
|
const String getName() const override { return "DSPModulePluginDemo"; }
|
|
|
|
bool acceptsMidi() const override { return false; }
|
|
bool producesMidi() const override { return false; }
|
|
bool isMidiEffect() const override { return false; }
|
|
|
|
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& destData) override
|
|
{
|
|
copyXmlToBinary (*apvts.copyState().createXml(), destData);
|
|
}
|
|
|
|
void setStateInformation (const void* data, int sizeInBytes) override
|
|
{
|
|
apvts.replaceState (ValueTree::fromXml (*getXmlFromBinary (data, sizeInBytes)));
|
|
}
|
|
|
|
//==============================================================================
|
|
AudioProcessorValueTreeState apvts { *this, nullptr, "state", createParameters() };
|
|
|
|
// We store this here so that the editor retains its state if it is closed and reopened
|
|
int indexTab = 0;
|
|
|
|
private:
|
|
//==============================================================================
|
|
void valueTreePropertyChanged (ValueTree&, const Identifier&) override
|
|
{
|
|
requiresUpdate.store (true);
|
|
}
|
|
|
|
// This struct holds references to the raw parameter values, so that we don't have to look up
|
|
// the parameters (involving string comparisons and map lookups!) every time a parameter
|
|
// changes.
|
|
struct ParameterValues
|
|
{
|
|
explicit ParameterValues (AudioProcessorValueTreeState& state)
|
|
: inputGain (*state.getRawParameterValue (ID::inputGain)),
|
|
outputGain (*state.getRawParameterValue (ID::outputGain)),
|
|
pan (*state.getRawParameterValue (ID::pan)),
|
|
distortionEnabled (*state.getRawParameterValue (ID::distortionEnabled)),
|
|
distortionType (*state.getRawParameterValue (ID::distortionType)),
|
|
distortionOversampler (*state.getRawParameterValue (ID::distortionOversampler)),
|
|
distortionLowpass (*state.getRawParameterValue (ID::distortionLowpass)),
|
|
distortionHighpass (*state.getRawParameterValue (ID::distortionHighpass)),
|
|
distortionInGain (*state.getRawParameterValue (ID::distortionInGain)),
|
|
distortionCompGain (*state.getRawParameterValue (ID::distortionCompGain)),
|
|
distortionMix (*state.getRawParameterValue (ID::distortionMix)),
|
|
multiBandEnabled (*state.getRawParameterValue (ID::multiBandEnabled)),
|
|
multiBandFreq (*state.getRawParameterValue (ID::multiBandFreq)),
|
|
multiBandLowVolume (*state.getRawParameterValue (ID::multiBandLowVolume)),
|
|
multiBandHighVolume (*state.getRawParameterValue (ID::multiBandHighVolume)),
|
|
compressorEnabled (*state.getRawParameterValue (ID::compressorEnabled)),
|
|
compressorThreshold (*state.getRawParameterValue (ID::compressorThreshold)),
|
|
compressorRatio (*state.getRawParameterValue (ID::compressorRatio)),
|
|
compressorAttack (*state.getRawParameterValue (ID::compressorAttack)),
|
|
compressorRelease (*state.getRawParameterValue (ID::compressorRelease)),
|
|
noiseGateEnabled (*state.getRawParameterValue (ID::noiseGateEnabled)),
|
|
noiseGateThreshold (*state.getRawParameterValue (ID::noiseGateThreshold)),
|
|
noiseGateRatio (*state.getRawParameterValue (ID::noiseGateRatio)),
|
|
noiseGateAttack (*state.getRawParameterValue (ID::noiseGateAttack)),
|
|
noiseGateRelease (*state.getRawParameterValue (ID::noiseGateRelease)),
|
|
limiterEnabled (*state.getRawParameterValue (ID::limiterEnabled)),
|
|
limiterThreshold (*state.getRawParameterValue (ID::limiterThreshold)),
|
|
limiterRelease (*state.getRawParameterValue (ID::limiterRelease)),
|
|
directDelayEnabled (*state.getRawParameterValue (ID::directDelayEnabled)),
|
|
directDelayType (*state.getRawParameterValue (ID::directDelayType)),
|
|
directDelayValue (*state.getRawParameterValue (ID::directDelayValue)),
|
|
directDelaySmoothing (*state.getRawParameterValue (ID::directDelaySmoothing)),
|
|
directDelayMix (*state.getRawParameterValue (ID::directDelayMix)),
|
|
delayEffectEnabled (*state.getRawParameterValue (ID::delayEffectEnabled)),
|
|
delayEffectType (*state.getRawParameterValue (ID::delayEffectType)),
|
|
delayEffectValue (*state.getRawParameterValue (ID::delayEffectValue)),
|
|
delayEffectSmoothing (*state.getRawParameterValue (ID::delayEffectSmoothing)),
|
|
delayEffectLowpass (*state.getRawParameterValue (ID::delayEffectLowpass)),
|
|
delayEffectFeedback (*state.getRawParameterValue (ID::delayEffectFeedback)),
|
|
delayEffectMix (*state.getRawParameterValue (ID::delayEffectMix)),
|
|
phaserEnabled (*state.getRawParameterValue (ID::phaserEnabled)),
|
|
phaserRate (*state.getRawParameterValue (ID::phaserRate)),
|
|
phaserDepth (*state.getRawParameterValue (ID::phaserDepth)),
|
|
phaserCentreFrequency (*state.getRawParameterValue (ID::phaserCentreFrequency)),
|
|
phaserFeedback (*state.getRawParameterValue (ID::phaserFeedback)),
|
|
phaserMix (*state.getRawParameterValue (ID::phaserMix)),
|
|
chorusEnabled (*state.getRawParameterValue (ID::chorusEnabled)),
|
|
chorusRate (*state.getRawParameterValue (ID::chorusRate)),
|
|
chorusDepth (*state.getRawParameterValue (ID::chorusDepth)),
|
|
chorusCentreDelay (*state.getRawParameterValue (ID::chorusCentreDelay)),
|
|
chorusFeedback (*state.getRawParameterValue (ID::chorusFeedback)),
|
|
chorusMix (*state.getRawParameterValue (ID::chorusMix)),
|
|
ladderEnabled (*state.getRawParameterValue (ID::ladderEnabled)),
|
|
ladderCutoff (*state.getRawParameterValue (ID::ladderCutoff)),
|
|
ladderResonance (*state.getRawParameterValue (ID::ladderResonance)),
|
|
ladderDrive (*state.getRawParameterValue (ID::ladderDrive)),
|
|
ladderMode (*state.getRawParameterValue (ID::ladderMode))
|
|
{}
|
|
|
|
std::atomic<float>& inputGain;
|
|
std::atomic<float>& outputGain;
|
|
std::atomic<float>& pan;
|
|
|
|
std::atomic<float>& distortionEnabled;
|
|
std::atomic<float>& distortionType;
|
|
std::atomic<float>& distortionOversampler;
|
|
std::atomic<float>& distortionLowpass;
|
|
std::atomic<float>& distortionHighpass;
|
|
std::atomic<float>& distortionInGain;
|
|
std::atomic<float>& distortionCompGain;
|
|
std::atomic<float>& distortionMix;
|
|
|
|
std::atomic<float>& multiBandEnabled;
|
|
std::atomic<float>& multiBandFreq;
|
|
std::atomic<float>& multiBandLowVolume;
|
|
std::atomic<float>& multiBandHighVolume;
|
|
|
|
std::atomic<float>& compressorEnabled;
|
|
std::atomic<float>& compressorThreshold;
|
|
std::atomic<float>& compressorRatio;
|
|
std::atomic<float>& compressorAttack;
|
|
std::atomic<float>& compressorRelease;
|
|
|
|
std::atomic<float>& noiseGateEnabled;
|
|
std::atomic<float>& noiseGateThreshold;
|
|
std::atomic<float>& noiseGateRatio;
|
|
std::atomic<float>& noiseGateAttack;
|
|
std::atomic<float>& noiseGateRelease;
|
|
|
|
std::atomic<float>& limiterEnabled;
|
|
std::atomic<float>& limiterThreshold;
|
|
std::atomic<float>& limiterRelease;
|
|
|
|
std::atomic<float>& directDelayEnabled;
|
|
std::atomic<float>& directDelayType;
|
|
std::atomic<float>& directDelayValue;
|
|
std::atomic<float>& directDelaySmoothing;
|
|
std::atomic<float>& directDelayMix;
|
|
|
|
std::atomic<float>& delayEffectEnabled;
|
|
std::atomic<float>& delayEffectType;
|
|
std::atomic<float>& delayEffectValue;
|
|
std::atomic<float>& delayEffectSmoothing;
|
|
std::atomic<float>& delayEffectLowpass;
|
|
std::atomic<float>& delayEffectFeedback;
|
|
std::atomic<float>& delayEffectMix;
|
|
|
|
std::atomic<float>& phaserEnabled;
|
|
std::atomic<float>& phaserRate;
|
|
std::atomic<float>& phaserDepth;
|
|
std::atomic<float>& phaserCentreFrequency;
|
|
std::atomic<float>& phaserFeedback;
|
|
std::atomic<float>& phaserMix;
|
|
|
|
std::atomic<float>& chorusEnabled;
|
|
std::atomic<float>& chorusRate;
|
|
std::atomic<float>& chorusDepth;
|
|
std::atomic<float>& chorusCentreDelay;
|
|
std::atomic<float>& chorusFeedback;
|
|
std::atomic<float>& chorusMix;
|
|
|
|
std::atomic<float>& ladderEnabled;
|
|
std::atomic<float>& ladderCutoff;
|
|
std::atomic<float>& ladderResonance;
|
|
std::atomic<float>& ladderDrive;
|
|
std::atomic<float>& ladderMode;
|
|
};
|
|
|
|
ParameterValues parameters { apvts };
|
|
|
|
//==============================================================================
|
|
void update()
|
|
{
|
|
{
|
|
DistortionProcessor& distortion = dsp::get<distortionIndex> (chain);
|
|
|
|
if (distortion.currentIndexOversampling != parameters.distortionOversampler.load())
|
|
{
|
|
distortion.currentIndexOversampling = roundToInt (parameters.distortionOversampler.load());
|
|
prepareToPlay (getSampleRate(), getBlockSize());
|
|
return;
|
|
}
|
|
|
|
distortion.currentIndexWaveshaper = roundToInt (parameters.distortionType.load());
|
|
distortion.lowpass .setCutoffFrequency (parameters.distortionLowpass);
|
|
distortion.highpass.setCutoffFrequency (parameters.distortionHighpass);
|
|
distortion.distGain.setGainDecibels (parameters.distortionInGain);
|
|
distortion.compGain.setGainDecibels (parameters.distortionCompGain);
|
|
distortion.mixer.setWetMixProportion (parameters.distortionMix / 100.0f);
|
|
dsp::setBypassed<distortionIndex> (chain, parameters.distortionEnabled.load() == 0.0f);
|
|
}
|
|
|
|
dsp::get<inputGainIndex> (chain).setGainDecibels (parameters.inputGain);
|
|
dsp::get<outputGainIndex> (chain).setGainDecibels (parameters.outputGain);
|
|
dsp::get<pannerIndex> (chain).setPan (parameters.pan / 100.0f);
|
|
|
|
{
|
|
MultiBandProcessor& multiband = dsp::get<multiBandIndex> (chain);
|
|
const auto multibandFreq = parameters.multiBandFreq.load();
|
|
multiband.lowpass .setCutoffFrequency (multibandFreq);
|
|
multiband.highpass.setCutoffFrequency (multibandFreq);
|
|
const auto enabled = parameters.multiBandEnabled.load() != 0.0f;
|
|
multiband.lowVolume .setGainDecibels (enabled ? parameters.multiBandLowVolume .load() : 0.0f);
|
|
multiband.highVolume.setGainDecibels (enabled ? parameters.multiBandHighVolume.load() : 0.0f);
|
|
dsp::setBypassed<multiBandIndex> (chain, ! enabled);
|
|
}
|
|
|
|
{
|
|
dsp::Compressor<float>& compressor = dsp::get<compressorIndex> (chain);
|
|
compressor.setThreshold (parameters.compressorThreshold);
|
|
compressor.setRatio (parameters.compressorRatio);
|
|
compressor.setAttack (parameters.compressorAttack);
|
|
compressor.setRelease (parameters.compressorRelease);
|
|
dsp::setBypassed<compressorIndex> (chain, parameters.compressorEnabled.load() == 0.0f);
|
|
}
|
|
|
|
{
|
|
dsp::NoiseGate<float>& noiseGate = dsp::get<noiseGateIndex> (chain);
|
|
noiseGate.setThreshold (parameters.noiseGateThreshold);
|
|
noiseGate.setRatio (parameters.noiseGateRatio);
|
|
noiseGate.setAttack (parameters.noiseGateAttack);
|
|
noiseGate.setRelease (parameters.noiseGateRelease);
|
|
dsp::setBypassed<noiseGateIndex> (chain, parameters.noiseGateEnabled.load() == 0.0f);
|
|
}
|
|
|
|
{
|
|
dsp::Limiter<float>& limiter = dsp::get<limiterIndex> (chain);
|
|
limiter.setThreshold (parameters.limiterThreshold);
|
|
limiter.setRelease (parameters.limiterRelease);
|
|
dsp::setBypassed<limiterIndex> (chain, parameters.limiterEnabled.load() == 0.0f);
|
|
}
|
|
|
|
{
|
|
DirectDelayProcessor& delay = dsp::get<directDelayIndex> (chain);
|
|
delay.delayLineDirectType = roundToInt (parameters.directDelayType.load());
|
|
|
|
std::fill (delay.delayDirectValue.begin(),
|
|
delay.delayDirectValue.end(),
|
|
(double) parameters.directDelayValue);
|
|
|
|
delay.smoothFilter.setCutoffFrequency (1000.0 / parameters.directDelaySmoothing);
|
|
delay.mixer.setWetMixProportion (parameters.directDelayMix / 100.0f);
|
|
dsp::setBypassed<directDelayIndex> (chain, parameters.directDelayEnabled.load() == 0.0f);
|
|
}
|
|
|
|
{
|
|
DelayEffectProcessor& delay = dsp::get<delayEffectIndex> (chain);
|
|
delay.delayEffectType = roundToInt (parameters.delayEffectType.load());
|
|
|
|
std::fill (delay.delayEffectValue.begin(),
|
|
delay.delayEffectValue.end(),
|
|
(double) parameters.delayEffectValue / 1000.0 * getSampleRate());
|
|
|
|
const auto feedbackGain = Decibels::decibelsToGain (parameters.delayEffectFeedback.load(), -100.0f);
|
|
|
|
for (auto& volume : delay.delayFeedbackVolume)
|
|
volume.setTargetValue (feedbackGain);
|
|
|
|
delay.smoothFilter.setCutoffFrequency (1000.0 / parameters.delayEffectSmoothing);
|
|
delay.lowpass.setCutoffFrequency (parameters.delayEffectLowpass);
|
|
delay.mixer.setWetMixProportion (parameters.delayEffectMix / 100.0f);
|
|
dsp::setBypassed<delayEffectIndex> (chain, parameters.delayEffectEnabled.load() == 0.0f);
|
|
}
|
|
|
|
{
|
|
dsp::Phaser<float>& phaser = dsp::get<phaserIndex> (chain);
|
|
phaser.setRate (parameters.phaserRate);
|
|
phaser.setDepth (parameters.phaserDepth / 100.0f);
|
|
phaser.setCentreFrequency (parameters.phaserCentreFrequency);
|
|
phaser.setFeedback (parameters.phaserFeedback / 100.0f * 0.95f);
|
|
phaser.setMix (parameters.phaserMix / 100.0f);
|
|
dsp::setBypassed<phaserIndex> (chain, parameters.phaserEnabled.load() == 0.0f);
|
|
}
|
|
|
|
{
|
|
dsp::Chorus<float>& chorus = dsp::get<chorusIndex> (chain);
|
|
chorus.setRate (parameters.chorusRate);
|
|
chorus.setDepth (parameters.chorusDepth / 100.0f);
|
|
chorus.setCentreDelay (parameters.chorusCentreDelay);
|
|
chorus.setFeedback (parameters.chorusFeedback / 100.0f * 0.95f);
|
|
chorus.setMix (parameters.chorusMix / 100.0f);
|
|
dsp::setBypassed<chorusIndex> (chain, parameters.chorusEnabled.load() == 0.0f);
|
|
}
|
|
|
|
{
|
|
dsp::LadderFilter<float>& ladder = dsp::get<ladderIndex> (chain);
|
|
|
|
ladder.setCutoffFrequencyHz (parameters.ladderCutoff);
|
|
ladder.setResonance (parameters.ladderResonance / 100.0f);
|
|
ladder.setDrive (Decibels::decibelsToGain (parameters.ladderDrive.load()));
|
|
|
|
ladder.setMode ([&]
|
|
{
|
|
switch (roundToInt (parameters.ladderMode.load()))
|
|
{
|
|
case 0: return dsp::LadderFilterMode::LPF12;
|
|
case 1: return dsp::LadderFilterMode::LPF24;
|
|
case 2: return dsp::LadderFilterMode::HPF12;
|
|
case 3: return dsp::LadderFilterMode::HPF24;
|
|
case 4: return dsp::LadderFilterMode::BPF12;
|
|
|
|
default: break;
|
|
}
|
|
|
|
return dsp::LadderFilterMode::BPF24;
|
|
}());
|
|
|
|
dsp::setBypassed<ladderIndex> (chain, parameters.ladderEnabled.load() == 0.0f);
|
|
}
|
|
|
|
requiresUpdate.store (false);
|
|
}
|
|
|
|
//==============================================================================
|
|
static String getPanningTextForValue (float value)
|
|
{
|
|
if (value == 0.5f)
|
|
return "center";
|
|
|
|
if (value < 0.5f)
|
|
return String (roundToInt ((0.5f - value) * 200.0f)) + "%L";
|
|
|
|
return String (roundToInt ((value - 0.5f) * 200.0f)) + "%R";
|
|
}
|
|
|
|
static float getPanningValueForText (String strText)
|
|
{
|
|
if (strText.compareIgnoreCase ("center") == 0 || strText.compareIgnoreCase ("c") == 0)
|
|
return 0.5f;
|
|
|
|
strText = strText.trim();
|
|
|
|
if (strText.indexOfIgnoreCase ("%L") != -1)
|
|
{
|
|
auto percentage = (float) strText.substring (0, strText.indexOf ("%")).getDoubleValue();
|
|
return (100.0f - percentage) / 100.0f * 0.5f;
|
|
}
|
|
|
|
if (strText.indexOfIgnoreCase ("%R") != -1)
|
|
{
|
|
auto percentage = (float) strText.substring (0, strText.indexOf ("%")).getDoubleValue();
|
|
return percentage / 100.0f * 0.5f + 0.5f;
|
|
}
|
|
|
|
return 0.5f;
|
|
}
|
|
|
|
static AudioProcessorValueTreeState::ParameterLayout createParameters()
|
|
{
|
|
using Parameter = AudioProcessorValueTreeState::Parameter;
|
|
|
|
auto valueToTextFunction = [] (float x) { return String (x, 2); };
|
|
auto textToValueFunction = [] (const String& str) { return str.getFloatValue(); };
|
|
|
|
auto valueToTextPanFunction = [] (float x) { return getPanningTextForValue ((x + 100.0f) / 200.0f); };
|
|
auto textToValuePanFunction = [] (const String& str) { return getPanningValueForText (str) * 200.0f - 100.0f; };
|
|
|
|
AudioProcessorValueTreeState::ParameterLayout layout;
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::inputGain,
|
|
"Input",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::outputGain,
|
|
"Output",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::pan,
|
|
"Panning",
|
|
"",
|
|
NormalisableRange<float> (-100.0f, 100.0f),
|
|
0.0f,
|
|
valueToTextPanFunction,
|
|
textToValuePanFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::distortionEnabled, "Distortion", true, ""));
|
|
|
|
layout.add (std::make_unique<AudioParameterChoice> (ID::distortionType,
|
|
"Waveshaper",
|
|
StringArray { "std::tanh", "Approx. tanh" },
|
|
0));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::distortionInGain,
|
|
"Gain",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::distortionLowpass,
|
|
"Post Low-pass",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
|
|
22000.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::distortionHighpass,
|
|
"Pre High-pass",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
|
|
20.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::distortionCompGain,
|
|
"Compensat.",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::distortionMix,
|
|
"Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterChoice> (ID::distortionOversampler,
|
|
"Oversampling",
|
|
StringArray { "2X",
|
|
"4X",
|
|
"8X",
|
|
"2X compensated",
|
|
"4X compensated",
|
|
"8X compensated" },
|
|
1));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::multiBandEnabled, "Multi-band", false, ""));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::multiBandFreq,
|
|
"Sep. Freq.",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
|
|
2000.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::multiBandLowVolume,
|
|
"Low volume",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::multiBandHighVolume,
|
|
"High volume",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::compressorEnabled, "Comp.", false, ""));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::compressorThreshold,
|
|
"Threshold",
|
|
"dB",
|
|
NormalisableRange<float> (-100.0f, 0.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::compressorRatio,
|
|
"Ratio",
|
|
":1",
|
|
NormalisableRange<float> (1.0f, 100.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::compressorAttack,
|
|
"Attack",
|
|
"ms",
|
|
NormalisableRange<float> (0.01f, 1000.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::compressorRelease,
|
|
"Release",
|
|
"ms",
|
|
NormalisableRange<float> (10.0f, 10000.0f, 0.0f, 0.25f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::noiseGateEnabled, "Gate", false, ""));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::noiseGateThreshold,
|
|
"Threshold",
|
|
"dB",
|
|
NormalisableRange<float> (-100.0f, 0.0f),
|
|
-100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::noiseGateRatio,
|
|
"Ratio",
|
|
":1",
|
|
NormalisableRange<float> (1.0f, 100.0f, 0.0f, 0.25f),
|
|
10.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::noiseGateAttack,
|
|
"Attack",
|
|
"ms",
|
|
NormalisableRange<float> (0.01f, 1000.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::noiseGateRelease,
|
|
"Release",
|
|
"ms",
|
|
NormalisableRange<float> (10.0f, 10000.0f, 0.0f, 0.25f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::limiterEnabled, "Limiter", false, ""));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::limiterThreshold,
|
|
"Threshold",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 0.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::limiterRelease,
|
|
"Release",
|
|
"ms",
|
|
NormalisableRange<float> (10.0f, 10000.0f, 0.0f, 0.25f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::directDelayEnabled, "DL Dir.", false, ""));
|
|
|
|
layout.add (std::make_unique<AudioParameterChoice> (ID::directDelayType,
|
|
"DL Type",
|
|
StringArray { "None",
|
|
"Linear",
|
|
"Lagrange",
|
|
"Thiran" },
|
|
1));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::directDelayValue,
|
|
"Delay",
|
|
"smps",
|
|
NormalisableRange<float> (0.0f, 44100.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::directDelaySmoothing,
|
|
"Smooth",
|
|
"ms",
|
|
NormalisableRange<float> (20.0f, 10000.0f, 0.0f, 0.25f),
|
|
200.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::directDelayMix,
|
|
"Delay Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::delayEffectEnabled, "DL Effect", false, ""));
|
|
|
|
layout.add (std::make_unique<AudioParameterChoice> (ID::delayEffectType,
|
|
"DL Type",
|
|
StringArray { "None",
|
|
"Linear",
|
|
"Lagrange",
|
|
"Thiran" },
|
|
1));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::delayEffectValue,
|
|
"Delay",
|
|
"ms",
|
|
NormalisableRange<float> (0.01f, 1000.0f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::delayEffectSmoothing,
|
|
"Smooth",
|
|
"ms",
|
|
NormalisableRange<float> (20.0f, 10000.0f, 0.0f, 0.25f),
|
|
400.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::delayEffectLowpass,
|
|
"Low-pass",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
|
|
22000.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::delayEffectMix,
|
|
"Delay Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::delayEffectFeedback,
|
|
"Feedback",
|
|
"dB",
|
|
NormalisableRange<float> (-100.0f, 0.0f),
|
|
-100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::phaserEnabled, "Phaser", false, ""));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::phaserRate,
|
|
"Rate",
|
|
"Hz",
|
|
NormalisableRange<float> (0.05f, 20.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::phaserDepth,
|
|
"Depth",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::phaserCentreFrequency,
|
|
"Center",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 20000.0f, 0.0f, 0.25f),
|
|
600.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::phaserFeedback,
|
|
"Feedback",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::phaserMix,
|
|
"Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::chorusEnabled, "Chorus", false, ""));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::chorusRate,
|
|
"Rate",
|
|
"Hz",
|
|
NormalisableRange<float> (0.05f, 20.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::chorusDepth,
|
|
"Depth",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::chorusCentreDelay,
|
|
"Center",
|
|
"ms",
|
|
NormalisableRange<float> (1.0f, 100.0f, 0.0f, 0.25f),
|
|
7.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::chorusFeedback,
|
|
"Feedback",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::chorusMix,
|
|
"Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<AudioParameterBool> (ID::ladderEnabled, "Ladder", false, ""));
|
|
|
|
layout.add (std::make_unique<AudioParameterChoice> (ID::ladderMode,
|
|
"Mode",
|
|
StringArray { "LP12",
|
|
"LP24",
|
|
"HP12",
|
|
"HP24",
|
|
"BP12",
|
|
"BP24" },
|
|
1));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::ladderCutoff,
|
|
"Frequency",
|
|
"Hz",
|
|
NormalisableRange<float> (10.0f, 22000.0f, 0.0f, 0.25f),
|
|
1000.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::ladderResonance,
|
|
"Resonance",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
layout.add (std::make_unique<Parameter> (ID::ladderDrive,
|
|
"Drive",
|
|
"dB",
|
|
NormalisableRange<float> (0.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction));
|
|
|
|
return layout;
|
|
}
|
|
|
|
//==============================================================================
|
|
struct DistortionProcessor
|
|
{
|
|
DistortionProcessor()
|
|
{
|
|
forEach ([] (dsp::Gain<float>& gain) { gain.setRampDurationSeconds (0.05); },
|
|
distGain,
|
|
compGain);
|
|
|
|
lowpass.setType (dsp::FirstOrderTPTFilterType::lowpass);
|
|
highpass.setType (dsp::FirstOrderTPTFilterType::highpass);
|
|
mixer.setMixingRule (dsp::DryWetMixingRule::linear);
|
|
}
|
|
|
|
void prepare (const dsp::ProcessSpec& spec)
|
|
{
|
|
for (auto& oversampler : oversamplers)
|
|
oversampler.initProcessing (spec.maximumBlockSize);
|
|
|
|
prepareAll (spec, lowpass, highpass, distGain, compGain, mixer);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
for (auto& oversampler : oversamplers)
|
|
oversampler.reset();
|
|
|
|
resetAll (lowpass, highpass, distGain, compGain, mixer);
|
|
}
|
|
|
|
float getLatency() const
|
|
{
|
|
return oversamplers[size_t (currentIndexOversampling)].getLatencyInSamples();
|
|
}
|
|
|
|
template <typename Context>
|
|
void process (Context& context)
|
|
{
|
|
if (context.isBypassed)
|
|
return;
|
|
|
|
const auto& inputBlock = context.getInputBlock();
|
|
|
|
mixer.setWetLatency (getLatency());
|
|
mixer.pushDrySamples (inputBlock);
|
|
|
|
distGain.process (context);
|
|
highpass.process (context);
|
|
|
|
auto ovBlock = oversamplers[size_t (currentIndexOversampling)].processSamplesUp (inputBlock);
|
|
|
|
dsp::ProcessContextReplacing<float> waveshaperContext (ovBlock);
|
|
|
|
if (isPositiveAndBelow (currentIndexWaveshaper, waveShapers.size()))
|
|
{
|
|
waveShapers[size_t (currentIndexWaveshaper)].process (waveshaperContext);
|
|
|
|
if (currentIndexWaveshaper == 1)
|
|
clipping.process (waveshaperContext);
|
|
|
|
waveshaperContext.getOutputBlock() *= 0.7f;
|
|
}
|
|
|
|
auto& outputBlock = context.getOutputBlock();
|
|
oversamplers[size_t (currentIndexOversampling)].processSamplesDown (outputBlock);
|
|
|
|
lowpass.process (context);
|
|
compGain.process (context);
|
|
mixer.mixWetSamples (outputBlock);
|
|
}
|
|
|
|
std::array<dsp::Oversampling<float>, 6> oversamplers
|
|
{ {
|
|
{ 2, 1, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, false },
|
|
{ 2, 2, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, false },
|
|
{ 2, 3, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, false },
|
|
|
|
{ 2, 1, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, true },
|
|
{ 2, 2, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, true },
|
|
{ 2, 3, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, true },
|
|
} };
|
|
|
|
dsp::FirstOrderTPTFilter<float> lowpass, highpass;
|
|
dsp::Gain<float> distGain, compGain;
|
|
dsp::DryWetMixer<float> mixer { 10 };
|
|
std::array<dsp::WaveShaper<float>, 2> waveShapers { { { std::tanh },
|
|
{ dsp::FastMathApproximations::tanh } } };
|
|
dsp::WaveShaper<float> clipping;
|
|
int currentIndexOversampling = 0;
|
|
int currentIndexWaveshaper = 0;
|
|
};
|
|
|
|
struct MultiBandProcessor
|
|
{
|
|
MultiBandProcessor()
|
|
{
|
|
forEach ([] (dsp::Gain<float>& gain) { gain.setRampDurationSeconds (0.05); },
|
|
lowVolume,
|
|
highVolume);
|
|
|
|
lowpass .setType (dsp::LinkwitzRileyFilterType::lowpass);
|
|
highpass.setType (dsp::LinkwitzRileyFilterType::highpass);
|
|
}
|
|
|
|
void prepare (const dsp::ProcessSpec& spec)
|
|
{
|
|
prepareAll (spec, lowpass, highpass, lowVolume, highVolume);
|
|
bufferSeparation.setSize (4, int (spec.maximumBlockSize), false, false, true);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
resetAll (lowpass, highpass, lowVolume, highVolume);
|
|
}
|
|
|
|
template <typename Context>
|
|
void process (Context& context)
|
|
{
|
|
const auto& inputBlock = context.getInputBlock();
|
|
|
|
const auto numSamples = inputBlock.getNumSamples();
|
|
const auto numChannels = inputBlock.getNumChannels();
|
|
|
|
auto sepBlock = dsp::AudioBlock<float> (bufferSeparation).getSubBlock (0, (size_t) numSamples);
|
|
|
|
auto sepLowBlock = sepBlock.getSubsetChannelBlock (0, (size_t) numChannels);
|
|
auto sepHighBlock = sepBlock.getSubsetChannelBlock (2, (size_t) numChannels);
|
|
|
|
sepLowBlock .copyFrom (inputBlock);
|
|
sepHighBlock.copyFrom (inputBlock);
|
|
|
|
auto contextLow = dsp::ProcessContextReplacing<float> (sepLowBlock);
|
|
contextLow.isBypassed = context.isBypassed;
|
|
lowpass .process (contextLow);
|
|
lowVolume.process (contextLow);
|
|
|
|
auto contextHigh = dsp::ProcessContextReplacing<float> (sepHighBlock);
|
|
contextHigh.isBypassed = context.isBypassed;
|
|
highpass .process (contextHigh);
|
|
highVolume.process (contextHigh);
|
|
|
|
if (! context.isBypassed)
|
|
{
|
|
sepLowBlock.add (sepHighBlock);
|
|
context.getOutputBlock().copyFrom (sepLowBlock);
|
|
}
|
|
}
|
|
|
|
dsp::LinkwitzRileyFilter<float> lowpass, highpass;
|
|
dsp::Gain<float> lowVolume, highVolume;
|
|
AudioBuffer<float> bufferSeparation;
|
|
};
|
|
|
|
struct DirectDelayProcessor
|
|
{
|
|
DirectDelayProcessor()
|
|
{
|
|
smoothFilter.setType (dsp::FirstOrderTPTFilterType::lowpass);
|
|
mixer.setMixingRule (dsp::DryWetMixingRule::linear);
|
|
}
|
|
|
|
void prepare (const dsp::ProcessSpec& spec)
|
|
{
|
|
prepareAll (spec, noInterpolation, linear, lagrange, thiran, smoothFilter, mixer);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
resetAll (noInterpolation, linear, lagrange, thiran, smoothFilter, mixer);
|
|
}
|
|
|
|
template <typename Context>
|
|
void process (Context& context)
|
|
{
|
|
if (context.isBypassed)
|
|
return;
|
|
|
|
const auto& inputBlock = context.getInputBlock();
|
|
const auto& outputBlock = context.getOutputBlock();
|
|
|
|
mixer.pushDrySamples (inputBlock);
|
|
|
|
const auto numChannels = inputBlock.getNumChannels();
|
|
const auto numSamples = inputBlock.getNumSamples();
|
|
|
|
for (size_t channel = 0; channel < numChannels; ++channel)
|
|
{
|
|
auto* samplesIn = inputBlock .getChannelPointer (channel);
|
|
auto* samplesOut = outputBlock.getChannelPointer (channel);
|
|
|
|
for (size_t i = 0; i < numSamples; ++i)
|
|
{
|
|
const auto delay = smoothFilter.processSample (int (channel), delayDirectValue[channel]);
|
|
|
|
samplesOut[i] = [&]
|
|
{
|
|
switch (delayLineDirectType)
|
|
{
|
|
case 0:
|
|
noInterpolation.pushSample (int (channel), samplesIn[i]);
|
|
noInterpolation.setDelay ((float) delay);
|
|
return noInterpolation.popSample (int (channel));
|
|
|
|
case 1:
|
|
linear.pushSample (int (channel), samplesIn[i]);
|
|
linear.setDelay ((float) delay);
|
|
return linear.popSample (int (channel));
|
|
|
|
case 2:
|
|
lagrange.pushSample (int (channel), samplesIn[i]);
|
|
lagrange.setDelay ((float) delay);
|
|
return lagrange.popSample (int (channel));
|
|
|
|
case 3:
|
|
thiran.pushSample (int (channel), samplesIn[i]);
|
|
thiran.setDelay ((float) delay);
|
|
return thiran.popSample (int (channel));
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
jassertfalse;
|
|
return 0.0f;
|
|
}();
|
|
}
|
|
}
|
|
|
|
mixer.mixWetSamples (outputBlock);
|
|
}
|
|
|
|
static constexpr auto directDelayBufferSize = 44100;
|
|
dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::None> noInterpolation { directDelayBufferSize };
|
|
dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Linear> linear { directDelayBufferSize };
|
|
dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Lagrange3rd> lagrange { directDelayBufferSize };
|
|
dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Thiran> thiran { directDelayBufferSize };
|
|
|
|
// Double precision to avoid some approximation issues
|
|
dsp::FirstOrderTPTFilter<double> smoothFilter;
|
|
|
|
dsp::DryWetMixer<float> mixer;
|
|
std::array<double, 2> delayDirectValue { {} };
|
|
|
|
int delayLineDirectType = 1;
|
|
};
|
|
|
|
struct DelayEffectProcessor
|
|
{
|
|
DelayEffectProcessor()
|
|
{
|
|
smoothFilter.setType (dsp::FirstOrderTPTFilterType::lowpass);
|
|
lowpass.setType (dsp::FirstOrderTPTFilterType::lowpass);
|
|
mixer.setMixingRule (dsp::DryWetMixingRule::linear);
|
|
}
|
|
|
|
void prepare (const dsp::ProcessSpec& spec)
|
|
{
|
|
prepareAll (spec, noInterpolation, linear, lagrange, thiran, smoothFilter, lowpass, mixer);
|
|
|
|
for (auto& volume : delayFeedbackVolume)
|
|
volume.reset (spec.sampleRate, 0.05);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
resetAll (noInterpolation, linear, lagrange, thiran, smoothFilter, lowpass, mixer);
|
|
std::fill (lastDelayEffectOutput.begin(), lastDelayEffectOutput.end(), 0.0f);
|
|
}
|
|
|
|
template <typename Context>
|
|
void process (Context& context)
|
|
{
|
|
if (context.isBypassed)
|
|
return;
|
|
|
|
const auto& inputBlock = context.getInputBlock();
|
|
const auto& outputBlock = context.getOutputBlock();
|
|
const auto numSamples = inputBlock.getNumSamples();
|
|
const auto numChannels = inputBlock.getNumChannels();
|
|
|
|
mixer.pushDrySamples (inputBlock);
|
|
|
|
for (size_t channel = 0; channel < numChannels; ++channel)
|
|
{
|
|
auto* samplesIn = inputBlock .getChannelPointer (channel);
|
|
auto* samplesOut = outputBlock.getChannelPointer (channel);
|
|
|
|
for (size_t i = 0; i < numSamples; ++i)
|
|
{
|
|
auto input = samplesIn[i] - lastDelayEffectOutput[channel];
|
|
|
|
auto delay = smoothFilter.processSample (int (channel), delayEffectValue[channel]);
|
|
|
|
const auto output = [&]
|
|
{
|
|
switch (delayEffectType)
|
|
{
|
|
case 0:
|
|
noInterpolation.pushSample (int (channel), input);
|
|
noInterpolation.setDelay ((float) delay);
|
|
return noInterpolation.popSample (int (channel));
|
|
|
|
case 1:
|
|
linear.pushSample (int (channel), input);
|
|
linear.setDelay ((float) delay);
|
|
return linear.popSample (int (channel));
|
|
|
|
case 2:
|
|
lagrange.pushSample (int (channel), input);
|
|
lagrange.setDelay ((float) delay);
|
|
return lagrange.popSample (int (channel));
|
|
|
|
case 3:
|
|
thiran.pushSample (int (channel), input);
|
|
thiran.setDelay ((float) delay);
|
|
return thiran.popSample (int (channel));
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
jassertfalse;
|
|
return 0.0f;
|
|
}();
|
|
|
|
const auto processed = lowpass.processSample (int (channel), output);
|
|
|
|
samplesOut[i] = processed;
|
|
lastDelayEffectOutput[channel] = processed * delayFeedbackVolume[channel].getNextValue();
|
|
}
|
|
}
|
|
|
|
mixer.mixWetSamples (outputBlock);
|
|
}
|
|
|
|
static constexpr auto effectDelaySamples = 192000;
|
|
dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::None> noInterpolation { effectDelaySamples };
|
|
dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Linear> linear { effectDelaySamples };
|
|
dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Lagrange3rd> lagrange { effectDelaySamples };
|
|
dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Thiran> thiran { effectDelaySamples };
|
|
|
|
// Double precision to avoid some approximation issues
|
|
dsp::FirstOrderTPTFilter<double> smoothFilter;
|
|
|
|
std::array<double, 2> delayEffectValue;
|
|
|
|
std::array<LinearSmoothedValue<float>, 2> delayFeedbackVolume;
|
|
dsp::FirstOrderTPTFilter<float> lowpass;
|
|
dsp::DryWetMixer<float> mixer;
|
|
std::array<float, 2> lastDelayEffectOutput;
|
|
|
|
int delayEffectType = 1;
|
|
};
|
|
|
|
using Chain = dsp::ProcessorChain<dsp::NoiseGate<float>,
|
|
dsp::Gain<float>,
|
|
DirectDelayProcessor,
|
|
MultiBandProcessor,
|
|
dsp::Compressor<float>,
|
|
dsp::Phaser<float>,
|
|
dsp::Chorus<float>,
|
|
DistortionProcessor,
|
|
dsp::LadderFilter<float>,
|
|
DelayEffectProcessor,
|
|
dsp::Limiter<float>,
|
|
dsp::Gain<float>,
|
|
dsp::Panner<float>>;
|
|
Chain chain;
|
|
|
|
// We use this enum to index into the chain above
|
|
enum ProcessorIndices
|
|
{
|
|
noiseGateIndex,
|
|
inputGainIndex,
|
|
directDelayIndex,
|
|
multiBandIndex,
|
|
compressorIndex,
|
|
phaserIndex,
|
|
chorusIndex,
|
|
distortionIndex,
|
|
ladderIndex,
|
|
delayEffectIndex,
|
|
limiterIndex,
|
|
outputGainIndex,
|
|
pannerIndex
|
|
};
|
|
|
|
//==============================================================================
|
|
std::atomic<bool> requiresUpdate { true };
|
|
|
|
//==============================================================================
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DspModulePluginDemo)
|
|
};
|
|
|
|
//==============================================================================
|
|
class DspModulePluginDemoEditor : public AudioProcessorEditor
|
|
{
|
|
public:
|
|
explicit DspModulePluginDemoEditor (DspModulePluginDemo& p)
|
|
: AudioProcessorEditor (&p),
|
|
proc (p)
|
|
{
|
|
comboEffect.addSectionHeading ("Main");
|
|
comboEffect.addItem ("Distortion", TabDistortion);
|
|
comboEffect.addItem ("Multi-band", TabMultiBand);
|
|
|
|
comboEffect.addSectionHeading ("Dynamics");
|
|
comboEffect.addItem ("Compressor", TabCompressor);
|
|
comboEffect.addItem ("Noise gate", TabNoiseGate);
|
|
comboEffect.addItem ("Limiter", TabLimiter);
|
|
|
|
comboEffect.addSectionHeading ("Delay");
|
|
comboEffect.addItem ("Delay line direct", TabDelayLineDirect);
|
|
comboEffect.addItem ("Delay line effect", TabDelayLineEffect);
|
|
|
|
comboEffect.addSectionHeading ("Others");
|
|
comboEffect.addItem ("Phaser", TabPhaser);
|
|
comboEffect.addItem ("Chorus", TabChorus);
|
|
comboEffect.addItem ("Ladder filter", TabLadder);
|
|
|
|
comboEffect.setSelectedId (proc.indexTab + 1, dontSendNotification);
|
|
comboEffect.onChange = [this]
|
|
{
|
|
proc.indexTab = comboEffect.getSelectedId() - 1;
|
|
updateVisibility();
|
|
};
|
|
|
|
addAllAndMakeVisible (*this,
|
|
comboEffect,
|
|
labelEffect,
|
|
basicControls,
|
|
distortionControls,
|
|
multibandControls,
|
|
compressorControls,
|
|
noiseGateControls,
|
|
limiterControls,
|
|
directDelayControls,
|
|
delayEffectControls,
|
|
phaserControls,
|
|
chorusControls,
|
|
ladderControls);
|
|
labelEffect.setJustificationType (Justification::centredRight);
|
|
labelEffect.attachToComponent (&comboEffect, true);
|
|
|
|
updateVisibility();
|
|
|
|
setSize (800, 430);
|
|
}
|
|
|
|
//==============================================================================
|
|
void paint (Graphics& g) override
|
|
{
|
|
auto rect = getLocalBounds();
|
|
|
|
auto rectTop = rect.removeFromTop (topSize);
|
|
auto rectBottom = rect.removeFromBottom (bottomSize);
|
|
|
|
auto rectEffects = rect.removeFromBottom (tabSize);
|
|
auto rectChoice = rect.removeFromBottom (midSize);
|
|
|
|
g.setColour (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
|
g.fillRect (rect);
|
|
|
|
g.setColour (getLookAndFeel().findColour (ResizableWindow::backgroundColourId).brighter (0.2f));
|
|
g.fillRect (rectEffects);
|
|
|
|
g.setColour (getLookAndFeel().findColour (ResizableWindow::backgroundColourId).darker (0.2f));
|
|
g.fillRect (rectTop);
|
|
g.fillRect (rectBottom);
|
|
g.fillRect (rectChoice);
|
|
|
|
g.setColour (Colours::white);
|
|
g.setFont (Font (20.0f).italicised().withExtraKerningFactor (0.1f));
|
|
g.drawFittedText ("DSP MODULE DEMO", rectTop.reduced (10, 0), Justification::centredLeft, 1);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
auto rect = getLocalBounds();
|
|
rect.removeFromTop (topSize);
|
|
rect.removeFromBottom (bottomSize);
|
|
|
|
auto rectEffects = rect.removeFromBottom (tabSize);
|
|
auto rectChoice = rect.removeFromBottom (midSize);
|
|
|
|
comboEffect.setBounds (rectChoice.withSizeKeepingCentre (200, 24));
|
|
|
|
rect.reduce (80, 0);
|
|
rectEffects.reduce (20, 0);
|
|
|
|
basicControls.setBounds (rect);
|
|
|
|
forEach ([&] (Component& comp) { comp.setBounds (rectEffects); },
|
|
distortionControls,
|
|
multibandControls,
|
|
compressorControls,
|
|
noiseGateControls,
|
|
limiterControls,
|
|
directDelayControls,
|
|
delayEffectControls,
|
|
phaserControls,
|
|
chorusControls,
|
|
ladderControls);
|
|
}
|
|
|
|
private:
|
|
class AttachedSlider : public Component
|
|
{
|
|
public:
|
|
AttachedSlider (AudioProcessorValueTreeState& state, StringRef strID)
|
|
: label ("", state.getParameter (strID)->name),
|
|
attachment (state, strID, slider)
|
|
{
|
|
addAllAndMakeVisible (*this, slider, label);
|
|
|
|
slider.setTextValueSuffix (" " + state.getParameter (strID)->label);
|
|
|
|
label.attachToComponent (&slider, false);
|
|
label.setJustificationType (Justification::centred);
|
|
}
|
|
|
|
void resized() override { slider.setBounds (getLocalBounds().reduced (0, 40)); }
|
|
|
|
private:
|
|
Slider slider { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
|
|
Label label;
|
|
AudioProcessorValueTreeState::SliderAttachment attachment;
|
|
};
|
|
|
|
class AttachedToggle : public Component
|
|
{
|
|
public:
|
|
AttachedToggle (AudioProcessorValueTreeState& state, StringRef strID)
|
|
: toggle (state.getParameter (strID)->name),
|
|
attachment (state, strID, toggle)
|
|
{
|
|
addAndMakeVisible (toggle);
|
|
}
|
|
|
|
void resized() override { toggle.setBounds (getLocalBounds()); }
|
|
|
|
private:
|
|
ToggleButton toggle;
|
|
AudioProcessorValueTreeState::ButtonAttachment attachment;
|
|
};
|
|
|
|
class AttachedCombo : public Component
|
|
{
|
|
public:
|
|
AttachedCombo (AudioProcessorValueTreeState& state, StringRef strID)
|
|
: combo (state, strID),
|
|
label ("", state.getParameter (strID)->name),
|
|
attachment (state, strID, combo)
|
|
{
|
|
addAllAndMakeVisible (*this, combo, label);
|
|
|
|
label.attachToComponent (&combo, false);
|
|
label.setJustificationType (Justification::centred);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
combo.setBounds (getLocalBounds().withSizeKeepingCentre (jmin (getWidth(), 150), 24));
|
|
}
|
|
|
|
private:
|
|
struct ComboWithItems : public ComboBox
|
|
{
|
|
ComboWithItems (AudioProcessorValueTreeState& state, StringRef strID)
|
|
{
|
|
// Adding the list here in the constructor means that the combo
|
|
// is already populated when we construct the attachment below
|
|
addItemList (dynamic_cast<AudioParameterChoice*> (state.getParameter (strID))->choices, 1);
|
|
}
|
|
};
|
|
|
|
ComboWithItems combo;
|
|
Label label;
|
|
AudioProcessorValueTreeState::ComboBoxAttachment attachment;
|
|
};
|
|
|
|
//==============================================================================
|
|
void updateVisibility()
|
|
{
|
|
const auto indexEffect = comboEffect.getSelectedId();
|
|
|
|
const auto op = [&] (const std::tuple<Component&, int>& tup)
|
|
{
|
|
Component& comp = std::get<0> (tup);
|
|
const int tabIndex = std::get<1> (tup);
|
|
comp.setVisible (tabIndex == indexEffect);
|
|
};
|
|
|
|
forEach (op,
|
|
std::forward_as_tuple (distortionControls, TabDistortion),
|
|
std::forward_as_tuple (multibandControls, TabMultiBand),
|
|
std::forward_as_tuple (compressorControls, TabCompressor),
|
|
std::forward_as_tuple (noiseGateControls, TabNoiseGate),
|
|
std::forward_as_tuple (limiterControls, TabLimiter),
|
|
std::forward_as_tuple (directDelayControls, TabDelayLineDirect),
|
|
std::forward_as_tuple (delayEffectControls, TabDelayLineEffect),
|
|
std::forward_as_tuple (phaserControls, TabPhaser),
|
|
std::forward_as_tuple (chorusControls, TabChorus),
|
|
std::forward_as_tuple (ladderControls, TabLadder));
|
|
}
|
|
|
|
enum EffectsTabs
|
|
{
|
|
TabDistortion = 1,
|
|
TabMultiBand,
|
|
TabCompressor,
|
|
TabNoiseGate,
|
|
TabLimiter,
|
|
TabDelayLineDirect,
|
|
TabDelayLineEffect,
|
|
TabPhaser,
|
|
TabChorus,
|
|
TabLadder
|
|
};
|
|
|
|
//==============================================================================
|
|
ComboBox comboEffect;
|
|
Label labelEffect { "Audio effect: " };
|
|
|
|
struct GetTrackInfo
|
|
{
|
|
// Combo boxes need a lot of room
|
|
Grid::TrackInfo operator() (AttachedCombo&) const { return 120_px; }
|
|
|
|
// Toggles are a bit smaller
|
|
Grid::TrackInfo operator() (AttachedToggle&) const { return 80_px; }
|
|
|
|
// Sliders take up as much room as they can
|
|
Grid::TrackInfo operator() (AttachedSlider&) const { return 1_fr; }
|
|
};
|
|
|
|
template <typename... Components>
|
|
static void performLayout (const Rectangle<int>& bounds, Components&... components)
|
|
{
|
|
Grid grid;
|
|
using Track = Grid::TrackInfo;
|
|
|
|
grid.autoColumns = Track (1_fr);
|
|
grid.autoRows = Track (1_fr);
|
|
grid.columnGap = Grid::Px (10);
|
|
grid.rowGap = Grid::Px (0);
|
|
grid.autoFlow = Grid::AutoFlow::column;
|
|
|
|
grid.templateColumns = { GetTrackInfo{} (components)... };
|
|
grid.items = { GridItem (components)... };
|
|
|
|
grid.performLayout (bounds);
|
|
}
|
|
|
|
struct BasicControls : public Component
|
|
{
|
|
explicit BasicControls (AudioProcessorValueTreeState& state)
|
|
: pan (state, ID::pan),
|
|
input (state, ID::inputGain),
|
|
output (state, ID::outputGain)
|
|
{
|
|
addAllAndMakeVisible (*this, pan, input, output);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), input, output, pan);
|
|
}
|
|
|
|
AttachedSlider pan, input, output;
|
|
};
|
|
|
|
struct DistortionControls : public Component
|
|
{
|
|
explicit DistortionControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::distortionEnabled),
|
|
lowpass (state, ID::distortionLowpass),
|
|
highpass (state, ID::distortionHighpass),
|
|
mix (state, ID::distortionMix),
|
|
gain (state, ID::distortionInGain),
|
|
compv (state, ID::distortionCompGain),
|
|
type (state, ID::distortionType),
|
|
oversampling (state, ID::distortionOversampler)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, type, lowpass, highpass, mix, gain, compv, oversampling);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, type, gain, highpass, lowpass, compv, mix, oversampling);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedSlider lowpass, highpass, mix, gain, compv;
|
|
AttachedCombo type, oversampling;
|
|
};
|
|
|
|
struct MultiBandControls : public Component
|
|
{
|
|
explicit MultiBandControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::multiBandEnabled),
|
|
low (state, ID::multiBandLowVolume),
|
|
high (state, ID::multiBandHighVolume),
|
|
lRFreq (state, ID::multiBandFreq)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, low, high, lRFreq);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, lRFreq, low, high);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedSlider low, high, lRFreq;
|
|
};
|
|
|
|
struct CompressorControls : public Component
|
|
{
|
|
explicit CompressorControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::compressorEnabled),
|
|
threshold (state, ID::compressorThreshold),
|
|
ratio (state, ID::compressorRatio),
|
|
attack (state, ID::compressorAttack),
|
|
release (state, ID::compressorRelease)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, threshold, ratio, attack, release);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, threshold, ratio, attack, release);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedSlider threshold, ratio, attack, release;
|
|
};
|
|
|
|
struct NoiseGateControls : public Component
|
|
{
|
|
explicit NoiseGateControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::noiseGateEnabled),
|
|
threshold (state, ID::noiseGateThreshold),
|
|
ratio (state, ID::noiseGateRatio),
|
|
attack (state, ID::noiseGateAttack),
|
|
release (state, ID::noiseGateRelease)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, threshold, ratio, attack, release);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, threshold, ratio, attack, release);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedSlider threshold, ratio, attack, release;
|
|
};
|
|
|
|
struct LimiterControls : public Component
|
|
{
|
|
explicit LimiterControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::limiterEnabled),
|
|
threshold (state, ID::limiterThreshold),
|
|
release (state, ID::limiterRelease)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, threshold, release);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, threshold, release);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedSlider threshold, release;
|
|
};
|
|
|
|
struct DirectDelayControls : public Component
|
|
{
|
|
explicit DirectDelayControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::directDelayEnabled),
|
|
type (state, ID::directDelayType),
|
|
delay (state, ID::directDelayValue),
|
|
smooth (state, ID::directDelaySmoothing),
|
|
mix (state, ID::directDelayMix)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, type, delay, smooth, mix);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, type, delay, smooth, mix);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedCombo type;
|
|
AttachedSlider delay, smooth, mix;
|
|
};
|
|
|
|
struct DelayEffectControls : public Component
|
|
{
|
|
explicit DelayEffectControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::delayEffectEnabled),
|
|
type (state, ID::delayEffectType),
|
|
value (state, ID::delayEffectValue),
|
|
smooth (state, ID::delayEffectSmoothing),
|
|
lowpass (state, ID::delayEffectLowpass),
|
|
feedback (state, ID::delayEffectFeedback),
|
|
mix (state, ID::delayEffectMix)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, type, value, smooth, lowpass, feedback, mix);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, type, value, smooth, lowpass, feedback, mix);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedCombo type;
|
|
AttachedSlider value, smooth, lowpass, feedback, mix;
|
|
};
|
|
|
|
struct PhaserControls : public Component
|
|
{
|
|
explicit PhaserControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::phaserEnabled),
|
|
rate (state, ID::phaserRate),
|
|
depth (state, ID::phaserDepth),
|
|
centre (state, ID::phaserCentreFrequency),
|
|
feedback (state, ID::phaserFeedback),
|
|
mix (state, ID::phaserMix)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, rate, depth, centre, feedback, mix);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, rate, depth, centre, feedback, mix);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedSlider rate, depth, centre, feedback, mix;
|
|
};
|
|
|
|
struct ChorusControls : public Component
|
|
{
|
|
explicit ChorusControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::chorusEnabled),
|
|
rate (state, ID::chorusRate),
|
|
depth (state, ID::chorusDepth),
|
|
centre (state, ID::chorusCentreDelay),
|
|
feedback (state, ID::chorusFeedback),
|
|
mix (state, ID::chorusMix)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, rate, depth, centre, feedback, mix);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, rate, depth, centre, feedback, mix);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedSlider rate, depth, centre, feedback, mix;
|
|
};
|
|
|
|
struct LadderControls : public Component
|
|
{
|
|
explicit LadderControls (AudioProcessorValueTreeState& state)
|
|
: toggle (state, ID::ladderEnabled),
|
|
mode (state, ID::ladderMode),
|
|
freq (state, ID::ladderCutoff),
|
|
resonance (state, ID::ladderResonance),
|
|
drive (state, ID::ladderDrive)
|
|
{
|
|
addAllAndMakeVisible (*this, toggle, mode, freq, resonance, drive);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), toggle, mode, freq, resonance, drive);
|
|
}
|
|
|
|
AttachedToggle toggle;
|
|
AttachedCombo mode;
|
|
AttachedSlider freq, resonance, drive;
|
|
};
|
|
|
|
//==============================================================================
|
|
static constexpr auto topSize = 40,
|
|
bottomSize = 40,
|
|
midSize = 40,
|
|
tabSize = 155;
|
|
|
|
//==============================================================================
|
|
DspModulePluginDemo& proc;
|
|
|
|
BasicControls basicControls { proc.apvts };
|
|
DistortionControls distortionControls { proc.apvts };
|
|
MultiBandControls multibandControls { proc.apvts };
|
|
CompressorControls compressorControls { proc.apvts };
|
|
NoiseGateControls noiseGateControls { proc.apvts };
|
|
LimiterControls limiterControls { proc.apvts };
|
|
DirectDelayControls directDelayControls { proc.apvts };
|
|
DelayEffectControls delayEffectControls { proc.apvts };
|
|
PhaserControls phaserControls { proc.apvts };
|
|
ChorusControls chorusControls { proc.apvts };
|
|
LadderControls ladderControls { proc.apvts };
|
|
|
|
//==============================================================================
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DspModulePluginDemoEditor)
|
|
};
|
|
|
|
struct DspModulePluginDemoAudioProcessor : public DspModulePluginDemo
|
|
{
|
|
AudioProcessorEditor* createEditor() override
|
|
{
|
|
return new DspModulePluginDemoEditor (*this);
|
|
}
|
|
|
|
bool hasEditor() const override { return true; }
|
|
};
|