/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2017 - ROLI Ltd. JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 5 End-User License Agreement and JUCE 5 Privacy Policy (both updated and effective as of the 27th April 2017). End User License Agreement: www.juce.com/juce-5-licence Privacy Policy: www.juce.com/juce-5-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #include "PluginProcessor.h" #include "PluginEditor.h" //============================================================================== DspModulePluginDemoAudioProcessor::DspModulePluginDemoAudioProcessor() : AudioProcessor (BusesProperties() .withInput ("Input", AudioChannelSet::stereo(), true) .withOutput ("Output", AudioChannelSet::stereo(), true)), lowPassFilter (dsp::IIR::Coefficients::makeFirstOrderLowPass (48000.0, 20000.f)), highPassFilter (dsp::IIR::Coefficients::makeFirstOrderHighPass (48000.0, 20.0f)), waveShapers { {std::tanh}, {dsp::FastMathApproximations::tanh} }, clipping { clip } { addParameter (inputVolumeParam = new AudioParameterFloat ("INPUT", "Input Volume", { 0.f, 60.f, 0.f, 1.0f }, 0.f, "dB")); addParameter (highPassFilterFreqParam = new AudioParameterFloat ("HPFREQ", "Pre Highpass Freq.", { 20.f, 20000.f, 0.f, 0.5f }, 20.f, "Hz")); addParameter (lowPassFilterFreqParam = new AudioParameterFloat ("LPFREQ", "Post Lowpass Freq.", { 20.f, 20000.f, 0.f, 0.5f }, 20000.f, "Hz")); addParameter (stereoParam = new AudioParameterChoice ("STEREO", "Stereo Processing", { "Always mono", "Yes" }, 1)); addParameter (slopeParam = new AudioParameterChoice ("SLOPE", "Slope", { "-6 dB / octave", "-12 dB / octave" }, 0)); addParameter (waveshaperParam = new AudioParameterChoice ("WVSHP", "Waveshaper", { "std::tanh", "Fast tanh approx." }, 0)); addParameter (cabinetTypeParam = new AudioParameterChoice ("CABTYPE", "Cabinet Type", { "Guitar amplifier 8'' cabinet ", "Cassette recorder cabinet" }, 0)); addParameter (cabinetSimParam = new AudioParameterBool ("CABSIM", "Cabinet Sim", false)); addParameter (outputVolumeParam = new AudioParameterFloat ("OUTPUT", "Output Volume", { -40.f, 40.f, 0.f, 1.0f }, 0.f, "dB")); cabinetType.set (0); } DspModulePluginDemoAudioProcessor::~DspModulePluginDemoAudioProcessor() { } //============================================================================== bool DspModulePluginDemoAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const { // This is the place where you check if the layout is supported. // In this template code we only support mono or stereo. if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono() && layouts.getMainOutputChannelSet() != AudioChannelSet::stereo()) return false; // This checks if the input layout matches the output layout if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) return false; return true; } void DspModulePluginDemoAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) { auto channels = static_cast (jmin (getMainBusNumInputChannels(), getMainBusNumOutputChannels())); dsp::ProcessSpec spec { sampleRate, static_cast (samplesPerBlock), channels }; updateParameters(); lowPassFilter.prepare (spec); highPassFilter.prepare (spec); inputVolume.prepare (spec); outputVolume.prepare (spec); convolution.prepare (spec); cabinetType.set (-1); } void DspModulePluginDemoAudioProcessor::reset() { lowPassFilter.reset(); highPassFilter.reset(); convolution.reset(); } void DspModulePluginDemoAudioProcessor::releaseResources() { } void DspModulePluginDemoAudioProcessor::process (dsp::ProcessContextReplacing context) noexcept { // Input volume applied with a LinearSmoothedValue inputVolume.process (context); // Pre-highpass filtering, very useful for distortion audio effects // Note : try frequencies around 700 Hz highPassFilter.process (context); // Waveshaper processing, for distortion generation, thanks to the input gain // The fast tanh can be used instead of std::tanh to reduce the CPU load auto waveshaperIndex = waveshaperParam->getIndex(); if (isPositiveAndBelow (waveshaperIndex, (int) numWaveShapers) ) { waveShapers[waveshaperIndex].process (context); if (waveshaperIndex == 1) clipping.process(context); context.getOutputBlock() *= 0.7f; } // Post-lowpass filtering lowPassFilter.process (context); // Convolution with the impulse response of a guitar cabinet auto wasBypassed = context.isBypassed; context.isBypassed = context.isBypassed || cabinetIsBypassed; convolution.process (context); context.isBypassed = wasBypassed; // Output volume applied with a LinearSmoothedValue outputVolume.process (context); } void DspModulePluginDemoAudioProcessor::processBlock (AudioSampleBuffer& inoutBuffer, MidiBuffer&) { auto totalNumInputChannels = getTotalNumInputChannels(); auto totalNumOutputChannels = getTotalNumOutputChannels(); auto numSamples = inoutBuffer.getNumSamples(); for (auto i = jmin (2, totalNumInputChannels); i < totalNumOutputChannels; ++i) inoutBuffer.clear (i, 0, numSamples); updateParameters(); dsp::AudioBlock block (inoutBuffer); if (stereoParam->getIndex() == 1) { // Stereo processing mode: if (block.getNumChannels() > 2) block = block.getSubsetChannelBlock (0, 2); process (dsp::ProcessContextReplacing (block)); } else { // Mono processing mode: auto firstChan = block.getSingleChannelBlock (0); process (dsp::ProcessContextReplacing (firstChan)); for (size_t chan = 1; chan < block.getNumChannels(); ++chan) block.getSingleChannelBlock (chan).copy (firstChan); } } //============================================================================== bool DspModulePluginDemoAudioProcessor::hasEditor() const { return true; } AudioProcessorEditor* DspModulePluginDemoAudioProcessor::createEditor() { return new DspModulePluginDemoAudioProcessorEditor (*this); } //============================================================================== bool DspModulePluginDemoAudioProcessor::acceptsMidi() const { #if JucePlugin_WantsMidiInput return true; #else return false; #endif } bool DspModulePluginDemoAudioProcessor::producesMidi() const { #if JucePlugin_ProducesMidiOutput return true; #else return false; #endif } //============================================================================== void DspModulePluginDemoAudioProcessor::updateParameters() { auto inputdB = Decibels::decibelsToGain (inputVolumeParam->get()); auto outputdB = Decibels::decibelsToGain (outputVolumeParam->get()); if (inputVolume.getGainLinear() != inputdB) inputVolume.setGainLinear (inputdB); if (outputVolume.getGainLinear() != outputdB) outputVolume.setGainLinear (outputdB); dsp::IIR::Coefficients::Ptr newHighPassCoeffs, newLowPassCoeffs; auto newSlopeType = slopeParam->getIndex(); if (newSlopeType == 0) { *lowPassFilter.state = *dsp::IIR::Coefficients::makeFirstOrderLowPass (getSampleRate(), lowPassFilterFreqParam->get()); *highPassFilter.state = *dsp::IIR::Coefficients::makeFirstOrderHighPass (getSampleRate(), highPassFilterFreqParam->get()); } else { *lowPassFilter.state = *dsp::IIR::Coefficients::makeLowPass (getSampleRate(), lowPassFilterFreqParam->get()); *highPassFilter.state = *dsp::IIR::Coefficients::makeHighPass (getSampleRate(), highPassFilterFreqParam->get()); } //============================================================================== auto type = cabinetTypeParam->getIndex(); auto currentType = cabinetType.get(); if (type != currentType) { cabinetType.set(type); auto maxSize = static_cast (roundDoubleToInt (8192 * getSampleRate() / 44100)); if (type == 0) convolution.loadImpulseResponse (BinaryData::Impulse1_wav, BinaryData::Impulse1_wavSize, false, maxSize); else convolution.loadImpulseResponse (BinaryData::Impulse2_wav, BinaryData::Impulse2_wavSize, false, maxSize); } cabinetIsBypassed = ! cabinetSimParam->get(); } //============================================================================== // This creates new instances of the plugin.. AudioProcessor* JUCE_CALLTYPE createPluginFilter() { return new DspModulePluginDemoAudioProcessor(); }