mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
2037 lines
98 KiB
C++
2037 lines
98 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 (convolutionCabEnabled)
|
|
PARAMETER_ID (convolutionReverbEnabled)
|
|
PARAMETER_ID (convolutionReverbMix)
|
|
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()
|
|
: DspModulePluginDemo (AudioProcessorValueTreeState::ParameterLayout{}) {}
|
|
|
|
//==============================================================================
|
|
void prepareToPlay (double sampleRate, int samplesPerBlock) override
|
|
{
|
|
const auto channels = jmax (getTotalNumInputChannels(), getTotalNumOutputChannels());
|
|
|
|
if (channels == 0)
|
|
return;
|
|
|
|
chain.prepare ({ sampleRate, (uint32) samplesPerBlock, (uint32) channels });
|
|
|
|
reset();
|
|
}
|
|
|
|
void reset() override
|
|
{
|
|
chain.reset();
|
|
update();
|
|
}
|
|
|
|
void releaseResources() override {}
|
|
|
|
void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
|
|
{
|
|
if (jmax (getTotalNumInputChannels(), getTotalNumOutputChannels()) == 0)
|
|
return;
|
|
|
|
ScopedNoDenormals noDenormals;
|
|
|
|
if (requiresUpdate.load())
|
|
update();
|
|
|
|
irSize = dsp::get<convolutionIndex> (chain).reverb.getCurrentIRSize();
|
|
|
|
const auto totalNumInputChannels = getTotalNumInputChannels();
|
|
const auto totalNumOutputChannels = getTotalNumOutputChannels();
|
|
|
|
setLatencySamples (dsp::get<convolutionIndex> (chain).getLatency()
|
|
+ (dsp::isBypassed<distortionIndex> (chain) ? 0 : roundToInt (dsp::get<distortionIndex> (chain).getLatency())));
|
|
|
|
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)));
|
|
}
|
|
|
|
int getCurrentIRSize() const { return irSize; }
|
|
|
|
using Parameter = AudioProcessorValueTreeState::Parameter;
|
|
|
|
// This struct holds references to the raw parameters, so that we don't have to search
|
|
// the APVTS (involving string comparisons and map lookups!) every time a parameter
|
|
// changes.
|
|
struct ParameterReferences
|
|
{
|
|
template <typename Param>
|
|
static Param& addToLayout (AudioProcessorValueTreeState::ParameterLayout& layout,
|
|
std::unique_ptr<Param> param)
|
|
{
|
|
auto& ref = *param;
|
|
layout.add (std::move (param));
|
|
return ref;
|
|
}
|
|
|
|
static String valueToTextFunction (float x) { return String (x, 2); }
|
|
static float textToValueFunction (const String& str) { return str.getFloatValue(); }
|
|
|
|
static String valueToTextPanFunction (float x) { return getPanningTextForValue ((x + 100.0f) / 200.0f); }
|
|
static float textToValuePanFunction (const String& str) { return getPanningValueForText (str) * 200.0f - 100.0f; }
|
|
|
|
// Creates parameters, adds them to the layout, and stores references to the parameters
|
|
// in this struct.
|
|
explicit ParameterReferences (AudioProcessorValueTreeState::ParameterLayout& layout)
|
|
: inputGain (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::inputGain,
|
|
"Input",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
outputGain (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::outputGain,
|
|
"Output",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
pan (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::pan,
|
|
"Panning",
|
|
"",
|
|
NormalisableRange<float> (-100.0f, 100.0f),
|
|
0.0f,
|
|
valueToTextPanFunction,
|
|
textToValuePanFunction))),
|
|
distortionEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::distortionEnabled,
|
|
"Distortion",
|
|
true,
|
|
""))),
|
|
distortionType (addToLayout (layout,
|
|
std::make_unique<AudioParameterChoice> (ID::distortionType,
|
|
"Waveshaper",
|
|
StringArray { "std::tanh", "Approx. tanh" },
|
|
0))),
|
|
distortionInGain (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::distortionInGain,
|
|
"Gain",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
distortionLowpass (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::distortionLowpass,
|
|
"Post Low-pass",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
|
|
22000.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
distortionHighpass (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::distortionHighpass,
|
|
"Pre High-pass",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
|
|
20.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
distortionCompGain (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::distortionCompGain,
|
|
"Compensat.",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
distortionMix (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::distortionMix,
|
|
"Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
distortionOversampler (addToLayout (layout,
|
|
std::make_unique<AudioParameterChoice> (ID::distortionOversampler,
|
|
"Oversampling",
|
|
StringArray { "2X", "4X", "8X", "2X compensated", "4X compensated", "8X compensated" },
|
|
1))),
|
|
multiBandEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::multiBandEnabled,
|
|
"Multi-band",
|
|
false,
|
|
""))),
|
|
multiBandFreq (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::multiBandFreq,
|
|
"Sep. Freq.",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
|
|
2000.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
multiBandLowVolume (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::multiBandLowVolume,
|
|
"Low volume",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
multiBandHighVolume (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::multiBandHighVolume,
|
|
"High volume",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
convolutionCabEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::convolutionCabEnabled,
|
|
"Cabinet",
|
|
false,
|
|
""))),
|
|
convolutionReverbEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::convolutionReverbEnabled,
|
|
"Reverb",
|
|
false,
|
|
""))),
|
|
convolutionReverbMix (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::convolutionReverbMix,
|
|
"Reverb Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
compressorEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::compressorEnabled,
|
|
"Comp.",
|
|
false,
|
|
""))),
|
|
compressorThreshold (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::compressorThreshold,
|
|
"Threshold",
|
|
"dB",
|
|
NormalisableRange<float> (-100.0f, 0.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
compressorRatio (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::compressorRatio,
|
|
"Ratio",
|
|
":1",
|
|
NormalisableRange<float> (1.0f, 100.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
compressorAttack (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::compressorAttack,
|
|
"Attack",
|
|
"ms",
|
|
NormalisableRange<float> (0.01f, 1000.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
compressorRelease (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::compressorRelease,
|
|
"Release",
|
|
"ms",
|
|
NormalisableRange<float> (10.0f, 10000.0f, 0.0f, 0.25f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
noiseGateEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::noiseGateEnabled,
|
|
"Gate",
|
|
false,
|
|
""))),
|
|
noiseGateThreshold (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::noiseGateThreshold,
|
|
"Threshold",
|
|
"dB",
|
|
NormalisableRange<float> (-100.0f, 0.0f),
|
|
-100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
noiseGateRatio (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::noiseGateRatio,
|
|
"Ratio",
|
|
":1",
|
|
NormalisableRange<float> (1.0f, 100.0f, 0.0f, 0.25f),
|
|
10.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
noiseGateAttack (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::noiseGateAttack,
|
|
"Attack",
|
|
"ms",
|
|
NormalisableRange<float> (0.01f, 1000.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
noiseGateRelease (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::noiseGateRelease,
|
|
"Release",
|
|
"ms",
|
|
NormalisableRange<float> (10.0f, 10000.0f, 0.0f, 0.25f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
limiterEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::limiterEnabled,
|
|
"Limiter",
|
|
false,
|
|
""))),
|
|
limiterThreshold (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::limiterThreshold,
|
|
"Threshold",
|
|
"dB",
|
|
NormalisableRange<float> (-40.0f, 0.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
limiterRelease (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::limiterRelease,
|
|
"Release",
|
|
"ms",
|
|
NormalisableRange<float> (10.0f, 10000.0f, 0.0f, 0.25f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
directDelayEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::directDelayEnabled,
|
|
"DL Dir.",
|
|
false,
|
|
""))),
|
|
directDelayType (addToLayout (layout,
|
|
std::make_unique<AudioParameterChoice> (ID::directDelayType,
|
|
"DL Type",
|
|
StringArray { "None", "Linear", "Lagrange", "Thiran" },
|
|
1))),
|
|
directDelayValue (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::directDelayValue,
|
|
"Delay",
|
|
"smps",
|
|
NormalisableRange<float> (0.0f, 44100.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
directDelaySmoothing (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::directDelaySmoothing,
|
|
"Smooth",
|
|
"ms",
|
|
NormalisableRange<float> (20.0f, 10000.0f, 0.0f, 0.25f),
|
|
200.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
directDelayMix (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::directDelayMix,
|
|
"Delay Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
delayEffectEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::delayEffectEnabled,
|
|
"DL Effect",
|
|
false,
|
|
""))),
|
|
delayEffectType (addToLayout (layout,
|
|
std::make_unique<AudioParameterChoice> (ID::delayEffectType,
|
|
"DL Type",
|
|
StringArray { "None", "Linear", "Lagrange", "Thiran" },
|
|
1))),
|
|
delayEffectValue (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::delayEffectValue,
|
|
"Delay",
|
|
"ms",
|
|
NormalisableRange<float> (0.01f, 1000.0f),
|
|
100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
delayEffectSmoothing (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::delayEffectSmoothing,
|
|
"Smooth",
|
|
"ms",
|
|
NormalisableRange<float> (20.0f, 10000.0f, 0.0f, 0.25f),
|
|
400.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
delayEffectLowpass (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::delayEffectLowpass,
|
|
"Low-pass",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
|
|
22000.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
delayEffectMix (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::delayEffectMix,
|
|
"Delay Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
delayEffectFeedback (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::delayEffectFeedback,
|
|
"Feedback",
|
|
"dB",
|
|
NormalisableRange<float> (-100.0f, 0.0f),
|
|
-100.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
phaserEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::phaserEnabled,
|
|
"Phaser",
|
|
false,
|
|
""))),
|
|
phaserRate (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::phaserRate,
|
|
"Rate",
|
|
"Hz",
|
|
NormalisableRange<float> (0.05f, 20.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
phaserDepth (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::phaserDepth,
|
|
"Depth",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
phaserCentreFrequency (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::phaserCentreFrequency,
|
|
"Center",
|
|
"Hz",
|
|
NormalisableRange<float> (20.0f, 20000.0f, 0.0f, 0.25f),
|
|
600.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
phaserFeedback (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::phaserFeedback,
|
|
"Feedback",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
phaserMix (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::phaserMix,
|
|
"Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
chorusEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::chorusEnabled,
|
|
"Chorus",
|
|
false,
|
|
""))),
|
|
chorusRate (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::chorusRate,
|
|
"Rate",
|
|
"Hz",
|
|
NormalisableRange<float> (0.05f, 20.0f, 0.0f, 0.25f),
|
|
1.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
chorusDepth (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::chorusDepth,
|
|
"Depth",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
chorusCentreDelay (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::chorusCentreDelay,
|
|
"Center",
|
|
"ms",
|
|
NormalisableRange<float> (1.0f, 100.0f, 0.0f, 0.25f),
|
|
7.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
chorusFeedback (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::chorusFeedback,
|
|
"Feedback",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
chorusMix (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::chorusMix,
|
|
"Mix",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
50.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
ladderEnabled (addToLayout (layout,
|
|
std::make_unique<AudioParameterBool> (ID::ladderEnabled,
|
|
"Ladder",
|
|
false,
|
|
""))),
|
|
ladderMode (addToLayout (layout,
|
|
std::make_unique<AudioParameterChoice> (ID::ladderMode,
|
|
"Mode",
|
|
StringArray { "LP12", "LP24", "HP12", "HP24", "BP12", "BP24" },
|
|
1))),
|
|
ladderCutoff (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::ladderCutoff,
|
|
"Frequency",
|
|
"Hz",
|
|
NormalisableRange<float> (10.0f, 22000.0f, 0.0f, 0.25f),
|
|
1000.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
ladderResonance (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::ladderResonance,
|
|
"Resonance",
|
|
"%",
|
|
NormalisableRange<float> (0.0f, 100.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction))),
|
|
ladderDrive (addToLayout (layout,
|
|
std::make_unique<Parameter> (ID::ladderDrive,
|
|
"Drive",
|
|
"dB",
|
|
NormalisableRange<float> (0.0f, 40.0f),
|
|
0.0f,
|
|
valueToTextFunction,
|
|
textToValueFunction)))
|
|
{}
|
|
|
|
Parameter& inputGain;
|
|
Parameter& outputGain;
|
|
Parameter& pan;
|
|
|
|
AudioParameterBool& distortionEnabled;
|
|
AudioParameterChoice& distortionType;
|
|
Parameter& distortionInGain;
|
|
Parameter& distortionLowpass;
|
|
Parameter& distortionHighpass;
|
|
Parameter& distortionCompGain;
|
|
Parameter& distortionMix;
|
|
AudioParameterChoice& distortionOversampler;
|
|
|
|
AudioParameterBool& multiBandEnabled;
|
|
Parameter& multiBandFreq;
|
|
Parameter& multiBandLowVolume;
|
|
Parameter& multiBandHighVolume;
|
|
|
|
AudioParameterBool& convolutionCabEnabled;
|
|
AudioParameterBool& convolutionReverbEnabled;
|
|
Parameter& convolutionReverbMix;
|
|
|
|
AudioParameterBool& compressorEnabled;
|
|
Parameter& compressorThreshold;
|
|
Parameter& compressorRatio;
|
|
Parameter& compressorAttack;
|
|
Parameter& compressorRelease;
|
|
|
|
AudioParameterBool& noiseGateEnabled;
|
|
Parameter& noiseGateThreshold;
|
|
Parameter& noiseGateRatio;
|
|
Parameter& noiseGateAttack;
|
|
Parameter& noiseGateRelease;
|
|
|
|
AudioParameterBool& limiterEnabled;
|
|
Parameter& limiterThreshold;
|
|
Parameter& limiterRelease;
|
|
|
|
AudioParameterBool& directDelayEnabled;
|
|
AudioParameterChoice& directDelayType;
|
|
Parameter& directDelayValue;
|
|
Parameter& directDelaySmoothing;
|
|
Parameter& directDelayMix;
|
|
|
|
AudioParameterBool& delayEffectEnabled;
|
|
AudioParameterChoice& delayEffectType;
|
|
Parameter& delayEffectValue;
|
|
Parameter& delayEffectSmoothing;
|
|
Parameter& delayEffectLowpass;
|
|
Parameter& delayEffectMix;
|
|
Parameter& delayEffectFeedback;
|
|
|
|
AudioParameterBool& phaserEnabled;
|
|
Parameter& phaserRate;
|
|
Parameter& phaserDepth;
|
|
Parameter& phaserCentreFrequency;
|
|
Parameter& phaserFeedback;
|
|
Parameter& phaserMix;
|
|
|
|
AudioParameterBool& chorusEnabled;
|
|
Parameter& chorusRate;
|
|
Parameter& chorusDepth;
|
|
Parameter& chorusCentreDelay;
|
|
Parameter& chorusFeedback;
|
|
Parameter& chorusMix;
|
|
|
|
AudioParameterBool& ladderEnabled;
|
|
AudioParameterChoice& ladderMode;
|
|
Parameter& ladderCutoff;
|
|
Parameter& ladderResonance;
|
|
Parameter& ladderDrive;
|
|
};
|
|
|
|
const ParameterReferences& getParameterValues() const noexcept { return parameters; }
|
|
|
|
//==============================================================================
|
|
// We store this here so that the editor retains its state if it is closed and reopened
|
|
int indexTab = 0;
|
|
|
|
private:
|
|
struct LayoutAndReferences
|
|
{
|
|
AudioProcessorValueTreeState::ParameterLayout layout;
|
|
ParameterReferences references;
|
|
};
|
|
|
|
explicit DspModulePluginDemo (AudioProcessorValueTreeState::ParameterLayout layout)
|
|
: AudioProcessor (BusesProperties().withInput ("In", AudioChannelSet::stereo())
|
|
.withOutput ("Out", AudioChannelSet::stereo())),
|
|
parameters { layout },
|
|
apvts { *this, nullptr, "state", std::move (layout) }
|
|
{
|
|
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 valueTreePropertyChanged (ValueTree&, const Identifier&) override
|
|
{
|
|
requiresUpdate.store (true);
|
|
}
|
|
|
|
//==============================================================================
|
|
void update()
|
|
{
|
|
{
|
|
DistortionProcessor& distortion = dsp::get<distortionIndex> (chain);
|
|
|
|
if (distortion.currentIndexOversampling != parameters.distortionOversampler.getIndex())
|
|
{
|
|
distortion.currentIndexOversampling = parameters.distortionOversampler.getIndex();
|
|
prepareToPlay (getSampleRate(), getBlockSize());
|
|
return;
|
|
}
|
|
|
|
distortion.currentIndexWaveshaper = parameters.distortionType.getIndex();
|
|
distortion.lowpass .setCutoffFrequency (parameters.distortionLowpass.get());
|
|
distortion.highpass.setCutoffFrequency (parameters.distortionHighpass.get());
|
|
distortion.distGain.setGainDecibels (parameters.distortionInGain.get());
|
|
distortion.compGain.setGainDecibels (parameters.distortionCompGain.get());
|
|
distortion.mixer.setWetMixProportion (parameters.distortionMix.get() / 100.0f);
|
|
dsp::setBypassed<distortionIndex> (chain, ! parameters.distortionEnabled);
|
|
}
|
|
|
|
{
|
|
ConvolutionProcessor& convolution = dsp::get<convolutionIndex> (chain);
|
|
convolution.cabEnabled = parameters.convolutionCabEnabled;
|
|
convolution.reverbEnabled = parameters.convolutionReverbEnabled;
|
|
convolution.mixer.setWetMixProportion (parameters.convolutionReverbMix.get() / 100.0f);
|
|
}
|
|
|
|
dsp::get<inputGainIndex> (chain).setGainDecibels (parameters.inputGain.get());
|
|
dsp::get<outputGainIndex> (chain).setGainDecibels (parameters.outputGain.get());
|
|
dsp::get<pannerIndex> (chain).setPan (parameters.pan.get() / 100.0f);
|
|
|
|
{
|
|
MultiBandProcessor& multiband = dsp::get<multiBandIndex> (chain);
|
|
const auto multibandFreq = parameters.multiBandFreq.get();
|
|
multiband.lowpass .setCutoffFrequency (multibandFreq);
|
|
multiband.highpass.setCutoffFrequency (multibandFreq);
|
|
const bool enabled = parameters.multiBandEnabled;
|
|
multiband.lowVolume .setGainDecibels (enabled ? parameters.multiBandLowVolume .get() : 0.0f);
|
|
multiband.highVolume.setGainDecibels (enabled ? parameters.multiBandHighVolume.get() : 0.0f);
|
|
dsp::setBypassed<multiBandIndex> (chain, ! enabled);
|
|
}
|
|
|
|
{
|
|
dsp::Compressor<float>& compressor = dsp::get<compressorIndex> (chain);
|
|
compressor.setThreshold (parameters.compressorThreshold.get());
|
|
compressor.setRatio (parameters.compressorRatio.get());
|
|
compressor.setAttack (parameters.compressorAttack.get());
|
|
compressor.setRelease (parameters.compressorRelease.get());
|
|
dsp::setBypassed<compressorIndex> (chain, ! parameters.compressorEnabled);
|
|
}
|
|
|
|
{
|
|
dsp::NoiseGate<float>& noiseGate = dsp::get<noiseGateIndex> (chain);
|
|
noiseGate.setThreshold (parameters.noiseGateThreshold.get());
|
|
noiseGate.setRatio (parameters.noiseGateRatio.get());
|
|
noiseGate.setAttack (parameters.noiseGateAttack.get());
|
|
noiseGate.setRelease (parameters.noiseGateRelease.get());
|
|
dsp::setBypassed<noiseGateIndex> (chain, ! parameters.noiseGateEnabled);
|
|
}
|
|
|
|
{
|
|
dsp::Limiter<float>& limiter = dsp::get<limiterIndex> (chain);
|
|
limiter.setThreshold (parameters.limiterThreshold.get());
|
|
limiter.setRelease (parameters.limiterRelease.get());
|
|
dsp::setBypassed<limiterIndex> (chain, ! parameters.limiterEnabled);
|
|
}
|
|
|
|
{
|
|
DirectDelayProcessor& delay = dsp::get<directDelayIndex> (chain);
|
|
delay.delayLineDirectType = parameters.directDelayType.getIndex();
|
|
|
|
std::fill (delay.delayDirectValue.begin(),
|
|
delay.delayDirectValue.end(),
|
|
(double) parameters.directDelayValue.get());
|
|
|
|
delay.smoothFilter.setCutoffFrequency (1000.0 / parameters.directDelaySmoothing.get());
|
|
delay.mixer.setWetMixProportion (parameters.directDelayMix.get() / 100.0f);
|
|
dsp::setBypassed<directDelayIndex> (chain, ! parameters.directDelayEnabled);
|
|
}
|
|
|
|
{
|
|
DelayEffectProcessor& delay = dsp::get<delayEffectIndex> (chain);
|
|
delay.delayEffectType = parameters.delayEffectType.getIndex();
|
|
|
|
std::fill (delay.delayEffectValue.begin(),
|
|
delay.delayEffectValue.end(),
|
|
(double) parameters.delayEffectValue.get() / 1000.0 * getSampleRate());
|
|
|
|
const auto feedbackGain = Decibels::decibelsToGain (parameters.delayEffectFeedback.get(), -100.0f);
|
|
|
|
for (auto& volume : delay.delayFeedbackVolume)
|
|
volume.setTargetValue (feedbackGain);
|
|
|
|
delay.smoothFilter.setCutoffFrequency (1000.0 / parameters.delayEffectSmoothing.get());
|
|
delay.lowpass.setCutoffFrequency (parameters.delayEffectLowpass.get());
|
|
delay.mixer.setWetMixProportion (parameters.delayEffectMix.get() / 100.0f);
|
|
dsp::setBypassed<delayEffectIndex> (chain, ! parameters.delayEffectEnabled);
|
|
}
|
|
|
|
{
|
|
dsp::Phaser<float>& phaser = dsp::get<phaserIndex> (chain);
|
|
phaser.setRate (parameters.phaserRate.get());
|
|
phaser.setDepth (parameters.phaserDepth.get() / 100.0f);
|
|
phaser.setCentreFrequency (parameters.phaserCentreFrequency.get());
|
|
phaser.setFeedback (parameters.phaserFeedback.get() / 100.0f * 0.95f);
|
|
phaser.setMix (parameters.phaserMix.get() / 100.0f);
|
|
dsp::setBypassed<phaserIndex> (chain, ! parameters.phaserEnabled);
|
|
}
|
|
|
|
{
|
|
dsp::Chorus<float>& chorus = dsp::get<chorusIndex> (chain);
|
|
chorus.setRate (parameters.chorusRate.get());
|
|
chorus.setDepth (parameters.chorusDepth.get() / 100.0f);
|
|
chorus.setCentreDelay (parameters.chorusCentreDelay.get());
|
|
chorus.setFeedback (parameters.chorusFeedback.get() / 100.0f * 0.95f);
|
|
chorus.setMix (parameters.chorusMix.get() / 100.0f);
|
|
dsp::setBypassed<chorusIndex> (chain, ! parameters.chorusEnabled);
|
|
}
|
|
|
|
{
|
|
dsp::LadderFilter<float>& ladder = dsp::get<ladderIndex> (chain);
|
|
|
|
ladder.setCutoffFrequencyHz (parameters.ladderCutoff.get());
|
|
ladder.setResonance (parameters.ladderResonance.get() / 100.0f);
|
|
ladder.setDrive (Decibels::decibelsToGain (parameters.ladderDrive.get()));
|
|
|
|
ladder.setMode ([&]
|
|
{
|
|
switch (parameters.ladderMode.getIndex())
|
|
{
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//==============================================================================
|
|
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 },
|
|
} };
|
|
|
|
static float clip (float in) { return juce::jlimit (-1.0f, 1.0f, in); }
|
|
|
|
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 { clip };
|
|
int currentIndexOversampling = 0;
|
|
int currentIndexWaveshaper = 0;
|
|
};
|
|
|
|
struct ConvolutionProcessor
|
|
{
|
|
ConvolutionProcessor()
|
|
{
|
|
loadImpulseResponse (cabinet, "guitar_amp.wav");
|
|
loadImpulseResponse (reverb, "reverb_ir.wav");
|
|
mixer.setMixingRule (dsp::DryWetMixingRule::balanced);
|
|
}
|
|
|
|
void prepare (const dsp::ProcessSpec& spec)
|
|
{
|
|
prepareAll (spec, cabinet, reverb, mixer);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
resetAll (cabinet, reverb, mixer);
|
|
}
|
|
|
|
template <typename Context>
|
|
void process (Context& context)
|
|
{
|
|
auto contextConv = context;
|
|
contextConv.isBypassed = (! cabEnabled) || context.isBypassed;
|
|
cabinet.process (contextConv);
|
|
|
|
if (cabEnabled)
|
|
context.getOutputBlock().multiplyBy (4.0f);
|
|
|
|
if (reverbEnabled)
|
|
mixer.pushDrySamples (context.getInputBlock());
|
|
|
|
contextConv.isBypassed = (! reverbEnabled) || context.isBypassed;
|
|
reverb.process (contextConv);
|
|
|
|
if (reverbEnabled)
|
|
{
|
|
const auto& outputBlock = context.getOutputBlock();
|
|
outputBlock.multiplyBy (4.0f);
|
|
mixer.mixWetSamples (outputBlock);
|
|
}
|
|
}
|
|
|
|
int getLatency() const
|
|
{
|
|
auto latency = 0;
|
|
|
|
if (cabEnabled)
|
|
latency += cabinet.getLatency();
|
|
|
|
if (reverbEnabled)
|
|
latency += reverb.getLatency();
|
|
|
|
return latency;
|
|
}
|
|
|
|
dsp::ConvolutionMessageQueue queue;
|
|
dsp::Convolution cabinet { dsp::Convolution::NonUniform { 512 }, queue };
|
|
dsp::Convolution reverb { dsp::Convolution::NonUniform { 512 }, queue };
|
|
dsp::DryWetMixer<float> mixer;
|
|
bool cabEnabled = false, reverbEnabled = false;
|
|
|
|
private:
|
|
static void loadImpulseResponse (dsp::Convolution& convolution, const char* filename)
|
|
{
|
|
auto stream = createAssetInputStream (filename);
|
|
|
|
if (stream == nullptr)
|
|
{
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
AudioFormatManager manager;
|
|
manager.registerBasicFormats();
|
|
std::unique_ptr<AudioFormatReader> reader { manager.createReaderFor (std::move (stream)) };
|
|
|
|
if (reader == nullptr)
|
|
{
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
AudioBuffer<float> buffer (static_cast<int> (reader->numChannels),
|
|
static_cast<int> (reader->lengthInSamples));
|
|
reader->read (buffer.getArrayOfWritePointers(), buffer.getNumChannels(), 0, buffer.getNumSamples());
|
|
|
|
convolution.loadImpulseResponse (std::move (buffer),
|
|
reader->sampleRate,
|
|
dsp::Convolution::Stereo::yes,
|
|
dsp::Convolution::Trim::yes,
|
|
dsp::Convolution::Normalise::yes);
|
|
}
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
ParameterReferences parameters;
|
|
AudioProcessorValueTreeState apvts;
|
|
|
|
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,
|
|
ConvolutionProcessor,
|
|
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,
|
|
convolutionIndex,
|
|
limiterIndex,
|
|
outputGainIndex,
|
|
pannerIndex
|
|
};
|
|
|
|
//==============================================================================
|
|
std::atomic<bool> requiresUpdate { true };
|
|
std::atomic<int> irSize { 0 };
|
|
|
|
//==============================================================================
|
|
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 ("Convolution", TabConvolution);
|
|
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,
|
|
convolutionControls,
|
|
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);
|
|
|
|
g.setFont (Font (14.0f));
|
|
String strText = "IR length (reverb): " + String (proc.getCurrentIRSize()) + " samples";
|
|
g.drawFittedText (strText, rectBottom.reduced (10, 0), Justification::centredRight, 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,
|
|
convolutionControls,
|
|
multibandControls,
|
|
compressorControls,
|
|
noiseGateControls,
|
|
limiterControls,
|
|
directDelayControls,
|
|
delayEffectControls,
|
|
phaserControls,
|
|
chorusControls,
|
|
ladderControls);
|
|
}
|
|
|
|
private:
|
|
class AttachedSlider : public Component
|
|
{
|
|
public:
|
|
explicit AttachedSlider (RangedAudioParameter& param)
|
|
: label ("", param.name),
|
|
attachment (param, slider)
|
|
{
|
|
addAllAndMakeVisible (*this, slider, label);
|
|
|
|
slider.setTextValueSuffix (" " + param.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;
|
|
SliderParameterAttachment attachment;
|
|
};
|
|
|
|
class AttachedToggle : public Component
|
|
{
|
|
public:
|
|
explicit AttachedToggle (RangedAudioParameter& param)
|
|
: toggle (param.name),
|
|
attachment (param, toggle)
|
|
{
|
|
addAndMakeVisible (toggle);
|
|
}
|
|
|
|
void resized() override { toggle.setBounds (getLocalBounds()); }
|
|
|
|
private:
|
|
ToggleButton toggle;
|
|
ButtonParameterAttachment attachment;
|
|
};
|
|
|
|
class AttachedCombo : public Component
|
|
{
|
|
public:
|
|
explicit AttachedCombo (RangedAudioParameter& param)
|
|
: combo (param),
|
|
label ("", param.name),
|
|
attachment (param, 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
|
|
{
|
|
explicit ComboWithItems (RangedAudioParameter& param)
|
|
{
|
|
// Adding the list here in the constructor means that the combo
|
|
// is already populated when we construct the attachment below
|
|
addItemList (dynamic_cast<AudioParameterChoice&> (param).choices, 1);
|
|
}
|
|
};
|
|
|
|
ComboWithItems combo;
|
|
Label label;
|
|
ComboBoxParameterAttachment 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 (convolutionControls, TabConvolution),
|
|
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,
|
|
TabConvolution,
|
|
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 (const DspModulePluginDemo::ParameterReferences& state)
|
|
: pan (state.pan),
|
|
input (state.inputGain),
|
|
output (state.outputGain)
|
|
{
|
|
addAllAndMakeVisible (*this, pan, input, output);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), input, output, pan);
|
|
}
|
|
|
|
AttachedSlider pan, input, output;
|
|
};
|
|
|
|
struct DistortionControls : public Component
|
|
{
|
|
explicit DistortionControls (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.distortionEnabled),
|
|
lowpass (state.distortionLowpass),
|
|
highpass (state.distortionHighpass),
|
|
mix (state.distortionMix),
|
|
gain (state.distortionInGain),
|
|
compv (state.distortionCompGain),
|
|
type (state.distortionType),
|
|
oversampling (state.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 ConvolutionControls : public Component
|
|
{
|
|
explicit ConvolutionControls (const DspModulePluginDemo::ParameterReferences& state)
|
|
: cab (state.convolutionCabEnabled),
|
|
reverb (state.convolutionReverbEnabled),
|
|
mix (state.convolutionReverbMix)
|
|
{
|
|
addAllAndMakeVisible (*this, cab, reverb, mix);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
performLayout (getLocalBounds(), cab, reverb, mix);
|
|
}
|
|
|
|
AttachedToggle cab, reverb;
|
|
AttachedSlider mix;
|
|
};
|
|
|
|
struct MultiBandControls : public Component
|
|
{
|
|
explicit MultiBandControls (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.multiBandEnabled),
|
|
low (state.multiBandLowVolume),
|
|
high (state.multiBandHighVolume),
|
|
lRFreq (state.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 (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.compressorEnabled),
|
|
threshold (state.compressorThreshold),
|
|
ratio (state.compressorRatio),
|
|
attack (state.compressorAttack),
|
|
release (state.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 (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.noiseGateEnabled),
|
|
threshold (state.noiseGateThreshold),
|
|
ratio (state.noiseGateRatio),
|
|
attack (state.noiseGateAttack),
|
|
release (state.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 (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.limiterEnabled),
|
|
threshold (state.limiterThreshold),
|
|
release (state.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 (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.directDelayEnabled),
|
|
type (state.directDelayType),
|
|
delay (state.directDelayValue),
|
|
smooth (state.directDelaySmoothing),
|
|
mix (state.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 (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.delayEffectEnabled),
|
|
type (state.delayEffectType),
|
|
value (state.delayEffectValue),
|
|
smooth (state.delayEffectSmoothing),
|
|
lowpass (state.delayEffectLowpass),
|
|
feedback (state.delayEffectFeedback),
|
|
mix (state.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 (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.phaserEnabled),
|
|
rate (state.phaserRate),
|
|
depth (state.phaserDepth),
|
|
centre (state.phaserCentreFrequency),
|
|
feedback (state.phaserFeedback),
|
|
mix (state.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 (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.chorusEnabled),
|
|
rate (state.chorusRate),
|
|
depth (state.chorusDepth),
|
|
centre (state.chorusCentreDelay),
|
|
feedback (state.chorusFeedback),
|
|
mix (state.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 (const DspModulePluginDemo::ParameterReferences& state)
|
|
: toggle (state.ladderEnabled),
|
|
mode (state.ladderMode),
|
|
freq (state.ladderCutoff),
|
|
resonance (state.ladderResonance),
|
|
drive (state.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.getParameterValues() };
|
|
DistortionControls distortionControls { proc.getParameterValues() };
|
|
ConvolutionControls convolutionControls { proc.getParameterValues() };
|
|
MultiBandControls multibandControls { proc.getParameterValues() };
|
|
CompressorControls compressorControls { proc.getParameterValues() };
|
|
NoiseGateControls noiseGateControls { proc.getParameterValues() };
|
|
LimiterControls limiterControls { proc.getParameterValues() };
|
|
DirectDelayControls directDelayControls { proc.getParameterValues() };
|
|
DelayEffectControls delayEffectControls { proc.getParameterValues() };
|
|
PhaserControls phaserControls { proc.getParameterValues() };
|
|
ChorusControls chorusControls { proc.getParameterValues() };
|
|
LadderControls ladderControls { proc.getParameterValues() };
|
|
|
|
//==============================================================================
|
|
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; }
|
|
};
|