andrewm@0: /* andrewm@0: This code accompanies the textbook: andrewm@0: andrewm@0: Digital Audio Effects: Theory, Implementation and Application andrewm@0: Joshua D. Reiss and Andrew P. McPherson andrewm@0: andrewm@0: --- andrewm@0: andrewm@0: Distortion: distortion effect using different characteristic curves andrewm@0: See textbook Chapter 7: Overdrive, Distortion and Fuzz andrewm@0: andrewm@0: Code by Brecht De Man, Joshua Reiss and Andrew McPherson andrewm@0: andrewm@0: When using this code (or a modified version thereof), please cite: andrewm@0: andrewm@0: Brecht De Man and Joshua D. Reiss, "Adaptive Control of Amplitude andrewm@0: Distortion Effects," 53rd Conference of the Audio Engineering Society, andrewm@0: 2014. andrewm@0: andrewm@0: --- andrewm@0: andrewm@0: This program is free software: you can redistribute it and/or modify andrewm@0: it under the terms of the GNU General Public License as published by andrewm@0: the Free Software Foundation, either version 3 of the License, or andrewm@0: (at your option) any later version. andrewm@0: andrewm@0: This program is distributed in the hope that it will be useful, andrewm@0: but WITHOUT ANY WARRANTY; without even the implied warranty of andrewm@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrewm@0: GNU General Public License for more details. andrewm@0: andrewm@0: You should have received a copy of the GNU General Public License andrewm@0: along with this program. If not, see . andrewm@0: */ andrewm@0: andrewm@0: #include "PluginProcessor.h" andrewm@0: #include "PluginEditor.h" andrewm@0: andrewm@0: #if JUCE_INTEL andrewm@0: #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8 || n > 1.0e-8)) n = 0; andrewm@0: #else andrewm@0: #define JUCE_SNAP_TO_ZERO(n) andrewm@0: #endif andrewm@0: andrewm@0: DistortionAudioProcessor::DistortionAudioProcessor() andrewm@0: : andrewm@0: _numChannels (1) andrewm@0: ,_numSamples (1) // dummy - will be set in prepareToPlay andrewm@0: ,_sampleRate (1) // dummy - will be set in prepareToPlay andrewm@0: ,_typeNumber (_hardClipping) // standard andrewm@0: ,_currentTrackBuffer (1,1) andrewm@0: ,_lastUIWidth (850) andrewm@0: ,_lastUIHeight (650) andrewm@0: andrewm@0: { andrewm@0: Reset(); andrewm@0: } andrewm@0: andrewm@0: DistortionAudioProcessor::~DistortionAudioProcessor() andrewm@0: { andrewm@0: } andrewm@0: andrewm@0: //----------------------------------------------------------------------------- andrewm@0: // P R E P A R E T O P L A Y andrewm@0: void DistortionAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) andrewm@0: { andrewm@0: // If sample rate/block size changes or straight after construction andrewm@0: if (_numSamples != samplesPerBlock || _sampleRate != sampleRate) andrewm@0: { andrewm@0: _sampleRate = sampleRate; andrewm@0: _numSamples = samplesPerBlock; andrewm@0: _numChannels = getNumInputChannels(); andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: andrewm@0: //----------------------------------------------------------------------------- andrewm@0: // P R O C E S S B L O C K andrewm@0: void DistortionAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) andrewm@0: { andrewm@0: float gain = powf(10.0f, _gainIndB/20.f); andrewm@0: andrewm@0: andrewm@0: for (int channel = 0; channel < _numChannels; ++channel) andrewm@0: { andrewm@0: // Apply gain andrewm@0: buffer.applyGain(channel, 0, buffer.getNumSamples(), gain); andrewm@0: andrewm@0: // Put track audio data into _currentTrackBuffer andrewm@0: float * originalData = new float; b@1: originalData = buffer.getWritePointer(channel); andrewm@0: andrewm@0: // Apply distortion (sample per sample) andrewm@0: switch (_typeNumber) { andrewm@0: case _hardClipping: andrewm@0: { andrewm@0: float threshold = 0.5f; andrewm@0: for (int sample = 0; sample < buffer.getNumSamples(); ++sample) andrewm@0: { andrewm@0: if(originalData[sample] > threshold) // positive hard clipping andrewm@0: { andrewm@0: originalData[sample] = threshold; andrewm@0: } andrewm@0: else andrewm@0: { andrewm@0: if(originalData[sample] < - threshold) // negative hard clipping andrewm@0: { andrewm@0: originalData[sample] = - threshold; andrewm@0: } andrewm@0: } andrewm@0: } andrewm@0: break; andrewm@0: } andrewm@0: andrewm@0: case _softClipping: andrewm@0: { andrewm@0: float threshold1 = 1.0f/3.0f; andrewm@0: float threshold2 = 2.0f/3.0f; andrewm@0: for (int sample = 0; sample < buffer.getNumSamples(); ++sample) andrewm@0: { andrewm@0: if(originalData[sample] > threshold1) andrewm@0: { andrewm@0: if(originalData[sample] > threshold2) // positive clipping andrewm@0: { andrewm@0: originalData[sample] = 1.0f; andrewm@0: } andrewm@0: else // soft knee (positive) andrewm@0: { andrewm@0: originalData[sample] = (3.0f - (2.0f - 3.0f*originalData[sample])*(2.0f - 3.0f*originalData[sample]))/3.0f; andrewm@0: } andrewm@0: } andrewm@0: else andrewm@0: { andrewm@0: if(originalData[sample] < -threshold1) andrewm@0: { andrewm@0: if(originalData[sample] < -threshold2) // negative clipping andrewm@0: { andrewm@0: originalData[sample] = -1.0f; andrewm@0: } andrewm@0: else // soft knee (negative) andrewm@0: { andrewm@0: originalData[sample] = - (3.0f - (2.0f + 3.0f*originalData[sample])*(2.0f + 3.0f*originalData[sample]))/3.0f; andrewm@0: } andrewm@0: } andrewm@0: else // linear region (-1/3..1/3) andrewm@0: { andrewm@0: originalData[sample] *= 2.0f; andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: originalData[sample] /= 2.0f; // divide all by 2 to compensate for extra 6 dB gain boost andrewm@0: } andrewm@0: break; andrewm@0: } andrewm@0: andrewm@0: case _softClippingExp: andrewm@0: { andrewm@0: for (int sample = 0; sample < buffer.getNumSamples(); ++sample) andrewm@0: { andrewm@0: if (originalData[sample] > 0.0f) // positive andrewm@0: { andrewm@0: originalData[sample] = 1.0f - expf(-originalData[sample]); andrewm@0: } andrewm@0: else // negative andrewm@0: { andrewm@0: originalData[sample] = - 1.0f + expf(originalData[sample]); andrewm@0: } andrewm@0: } andrewm@0: break; andrewm@0: } andrewm@0: andrewm@0: case _fullWaveRectifier: andrewm@0: { andrewm@0: for (int sample = 0; sample < buffer.getNumSamples(); ++sample) andrewm@0: { andrewm@0: originalData[sample] = fabs(originalData[sample]); andrewm@0: } andrewm@0: break; andrewm@0: } andrewm@0: andrewm@0: case _halfWaveRectifier: andrewm@0: { andrewm@0: for (int sample = 0; sample < buffer.getNumSamples(); ++sample) andrewm@0: { andrewm@0: originalData[sample] = 0.5f*(fabs(originalData[sample])+originalData[sample]); andrewm@0: } andrewm@0: break; andrewm@0: } andrewm@0: andrewm@0: default: andrewm@0: break; andrewm@0: } andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: andrewm@0: //----------------------------------------------------------------------------- andrewm@0: // R E S E T andrewm@0: void DistortionAudioProcessor::Reset() andrewm@0: { andrewm@0: _gainIndB = 0.0f; andrewm@0: _typeNumber = _hardClipping; andrewm@0: } andrewm@0: andrewm@0: andrewm@0: //----------------------------------------------------------------------------- andrewm@0: // andrewm@0: void DistortionAudioProcessor::releaseResources() andrewm@0: { andrewm@0: // When playback stops, you can use this to free up any spare memory, etc. andrewm@0: } andrewm@0: andrewm@0: andrewm@0: bool DistortionAudioProcessor::hasEditor() const andrewm@0: { andrewm@0: return true; // (change this to false if you choose to not supply an editor) andrewm@0: } andrewm@0: andrewm@0: AudioProcessorEditor* DistortionAudioProcessor::createEditor() andrewm@0: { andrewm@0: return new DistortionAudioProcessorEditor (this); andrewm@0: } andrewm@0: andrewm@0: andrewm@0: //============================================================================== andrewm@0: void DistortionAudioProcessor::getStateInformation (MemoryBlock& destData) andrewm@0: { andrewm@0: // SAVE STATE INFO andrewm@0: XmlElement xml("Distortion_XML"); andrewm@0: andrewm@0: // Knobs andrewm@0: xml.setAttribute("_gain" ,_gainIndB); andrewm@0: andrewm@0: // Combo box andrewm@0: xml.setAttribute("_type" ,(int) _typeNumber); andrewm@0: andrewm@0: // then use this helper function to stuff it into the binary blob and return it.. andrewm@0: copyXmlToBinary(xml, destData); andrewm@0: } andrewm@0: andrewm@0: void DistortionAudioProcessor::setStateInformation (const void* data, int sizeInBytes) andrewm@0: { andrewm@0: // LOAD STATE INFO andrewm@0: ScopedPointer xmlState (getXmlFromBinary (data, sizeInBytes)); andrewm@0: andrewm@0: // make sure that it's actually our type of XML object.. andrewm@0: if(xmlState->hasTagName("Distortion_XML")) andrewm@0: { andrewm@0: andrewm@0: // Knobs andrewm@0: _gainIndB = (float) xmlState->getDoubleAttribute("_gain",true); andrewm@0: andrewm@0: //Combo box andrewm@0: _typeNumber = (DistortionAudioProcessor::Types) xmlState->getIntAttribute("_type",true); andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: // This creates new instances of the plugin.. andrewm@0: AudioProcessor* JUCE_CALLTYPE createPluginFilter() andrewm@0: { andrewm@0: return new DistortionAudioProcessor(); andrewm@0: } andrewm@0: andrewm@0: const String DistortionAudioProcessor::getName() const andrewm@0: { andrewm@0: return JucePlugin_Name; andrewm@0: } andrewm@0: andrewm@0: bool DistortionAudioProcessor::silenceInProducesSilenceOut() const andrewm@0: { andrewm@0: return true; andrewm@0: } andrewm@0: andrewm@0: int DistortionAudioProcessor::getNumParameters() andrewm@0: { andrewm@0: return _PARAMtotalNumParams; andrewm@0: } andrewm@0: andrewm@0: float DistortionAudioProcessor::getParameter (int index) // externally accessible andrewm@0: { andrewm@0: switch (index) andrewm@0: { andrewm@0: case _PARAMdeviceReset: return 0.0f; andrewm@0: case _PARAMgain: return (GetGainIndB()); andrewm@0: case _PARAMtype: return (GetType()); andrewm@0: default: return 0.0f; andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: void DistortionAudioProcessor::setParameter (int index, float newValue) // externally accessible andrewm@0: { andrewm@0: switch (index) andrewm@0: { andrewm@0: case _PARAMdeviceReset: andrewm@0: Reset(); andrewm@0: break; andrewm@0: case _PARAMgain: andrewm@0: SetGainIndB(newValue); andrewm@0: break; andrewm@0: case _PARAMtype: andrewm@0: SetType((DistortionAudioProcessor::Types) roundFloatToInt(newValue*_numberOfTypes)); andrewm@0: break; andrewm@0: default: andrewm@0: break; andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: const String DistortionAudioProcessor::getParameterName (int index) // externally accessible andrewm@0: { andrewm@0: switch (index) andrewm@0: { andrewm@0: case _PARAMdeviceReset: return "Reset"; andrewm@0: case _PARAMgain: return "Gain (dB)"; andrewm@0: case _PARAMtype: return "Type"; andrewm@0: default: break; andrewm@0: } andrewm@0: return String::empty; andrewm@0: } andrewm@0: andrewm@0: const String DistortionAudioProcessor::getParameterText (int index) andrewm@0: { andrewm@0: return String (getParameter (index), 2); andrewm@0: } andrewm@0: andrewm@0: const String DistortionAudioProcessor::getInputChannelName (int channelIndex) const andrewm@0: { andrewm@0: return String (channelIndex + 1); andrewm@0: } andrewm@0: andrewm@0: const String DistortionAudioProcessor::getOutputChannelName (int channelIndex) const andrewm@0: { andrewm@0: return String (channelIndex + 1); andrewm@0: } andrewm@0: andrewm@0: bool DistortionAudioProcessor::isInputChannelStereoPair (int index) const andrewm@0: { andrewm@0: return true; andrewm@0: } andrewm@0: andrewm@0: bool DistortionAudioProcessor::isOutputChannelStereoPair (int index) const andrewm@0: { andrewm@0: return true; andrewm@0: } andrewm@0: andrewm@0: bool DistortionAudioProcessor::acceptsMidi() const andrewm@0: { andrewm@0: #if JucePlugin_WantsMidiInput andrewm@0: return true; andrewm@0: #else andrewm@0: return false; andrewm@0: #endif andrewm@0: } andrewm@0: andrewm@0: bool DistortionAudioProcessor::producesMidi() const andrewm@0: { andrewm@0: #if JucePlugin_ProducesMidiOutput andrewm@0: return true; andrewm@0: #else andrewm@0: return false; andrewm@0: #endif andrewm@0: } andrewm@0: andrewm@0: int DistortionAudioProcessor::getNumPrograms() andrewm@0: { andrewm@0: return 0; andrewm@0: } andrewm@0: andrewm@0: int DistortionAudioProcessor::getCurrentProgram() andrewm@0: { andrewm@0: return 0; andrewm@0: } andrewm@0: andrewm@0: void DistortionAudioProcessor::setCurrentProgram (int index) andrewm@0: { andrewm@0: } andrewm@0: andrewm@0: const String DistortionAudioProcessor::getProgramName (int index) andrewm@0: { andrewm@0: return String::empty; andrewm@0: } andrewm@0: andrewm@0: void DistortionAudioProcessor::changeProgramName (int index, const String& newName) andrewm@0: { andrewm@0: }