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: Reverb: algorithmic reverb effect based on MVerb andrewm@0: See textbook Chapter 11: Reverberation andrewm@0: andrewm@0: Original code by Martin Eastwood: MVerb (see MVerb.h) andrewm@0: Adapted for JUCE by Brecht De Man andrewm@0: andrewm@0: When using this code (or a modified version thereof) please cite: andrewm@0: andrewm@0: R. Stables, S. Enderby, B. De Man, G. Fazekas, J. D. Reiss, "SAFE: andrewm@0: A System for the Extraction and Retrieval of Semantic Audio andrewm@0: Descriptors," 15th International Society for Music Information andrewm@0: Retrieval Conference (ISMIR 2014), 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: 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: ReverbAudioProcessor::ReverbAudioProcessor() 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: ,_density (1.0) andrewm@0: ,_decay (1.0) andrewm@0: ,_size (1.0) andrewm@0: ,_damp (1.0) andrewm@0: ,_bandwidth (1.0) andrewm@0: ,_predelay (0.0) andrewm@0: ,_gain (1.0) andrewm@0: ,_mix (0.5) andrewm@0: ,_lateEarly (0.5) andrewm@0: ,tempInput (1,1)// dummy - will be set in prepareToPlay andrewm@0: ,tempOutput (1,1)// dummy - will be set in prepareToPlay andrewm@0: ,_lastUIWidth (850) andrewm@0: ,_lastUIHeight (650) andrewm@0: andrewm@0: { andrewm@0: // Update all parameters andrewm@0: for (int index = 0; index < MVerb::NUM_PARAMS; ++index) andrewm@0: { andrewm@0: updateParameters(index); andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: ReverbAudioProcessor::~ReverbAudioProcessor() 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 ReverbAudioProcessor::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: tempInput.setSize (_numChannels,_numSamples); andrewm@0: tempOutput.setSize(_numChannels,_numSamples); andrewm@0: tempInput.clear(); andrewm@0: tempOutput.clear(); andrewm@0: andrewm@0: _mverb.reset(); andrewm@0: _mverb.setSampleRate (_sampleRate); // set reverb sample rate 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 ReverbAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) andrewm@0: { andrewm@0: int numSamples = buffer.getNumSamples(); andrewm@0: int numChannels = buffer.getNumChannels(); andrewm@0: andrewm@0: for(int channel = 0; channel < numChannels; channel++) andrewm@0: { andrewm@0: tempInput.copyFrom (channel, 0, buffer, channel, 0, numSamples); andrewm@0: } andrewm@0: andrewm@0: float** input = tempInput.getArrayOfWritePointers(); andrewm@0: float** output = tempOutput.getArrayOfWritePointers(); andrewm@0: andrewm@0: _mverb.process (input, output, numSamples); // processing by MVerb andrewm@0: andrewm@0: for(int channel = 0; channel < numChannels; channel++) andrewm@0: { andrewm@0: buffer.copyFrom (channel, 0, output [channel], numSamples); andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: andrewm@0: //----------------------------------------------------------------------------- andrewm@0: // U P D A T E P A R A M E T E R S andrewm@0: void ReverbAudioProcessor::updateParameters (int index) andrewm@0: { andrewm@0: switch(index) andrewm@0: { andrewm@0: case MVerb::DAMPINGFREQ: andrewm@0: _mverb.setParameter (index, _damp); andrewm@0: break; andrewm@0: andrewm@0: case MVerb::DENSITY: andrewm@0: _mverb.setParameter (index, _density); andrewm@0: break; andrewm@0: andrewm@0: case MVerb::BANDWIDTHFREQ: andrewm@0: _mverb.setParameter (index, _bandwidth); andrewm@0: break; andrewm@0: andrewm@0: case MVerb::PREDELAY: andrewm@0: _mverb.setParameter (index, _predelay); andrewm@0: break; andrewm@0: andrewm@0: case MVerb::DECAY: andrewm@0: _mverb.setParameter (index, _decay); andrewm@0: break; andrewm@0: andrewm@0: case MVerb::SIZE: andrewm@0: _mverb.setParameter (index, _size); andrewm@0: break; andrewm@0: andrewm@0: case MVerb::GAIN: andrewm@0: _mverb.setParameter (index, _gain); andrewm@0: break; andrewm@0: andrewm@0: case MVerb::MIX: andrewm@0: _mverb.setParameter (index, _mix); andrewm@0: break; andrewm@0: andrewm@0: case MVerb::EARLYMIX: andrewm@0: _mverb.setParameter (index, _lateEarly); andrewm@0: break; andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: andrewm@0: //----------------------------------------------------------------------------- andrewm@0: // R E S E T andrewm@0: void ReverbAudioProcessor::Reset() andrewm@0: { andrewm@0: _mverb.reset(); // not used andrewm@0: } andrewm@0: andrewm@0: andrewm@0: //----------------------------------------------------------------------------- andrewm@0: // andrewm@0: void ReverbAudioProcessor::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 ReverbAudioProcessor::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* ReverbAudioProcessor::createEditor() andrewm@0: { andrewm@0: return new ReverbAudioProcessorEditor (this); andrewm@0: } andrewm@0: andrewm@0: andrewm@0: //============================================================================== andrewm@0: void ReverbAudioProcessor::getStateInformation (MemoryBlock& destData) andrewm@0: { andrewm@0: // SAVE STATE INFO andrewm@0: XmlElement xml("JRAMReverb_XML"); andrewm@0: andrewm@0: // Knobs andrewm@0: xml.setAttribute("_density" ,_density); andrewm@0: xml.setAttribute("_decay" ,_decay); andrewm@0: xml.setAttribute("_size" ,_size); andrewm@0: xml.setAttribute("_damp" ,_damp); andrewm@0: xml.setAttribute("_bandwidth" ,_bandwidth); andrewm@0: xml.setAttribute("_predelay" ,_predelay); andrewm@0: xml.setAttribute("_gain" ,_gain); andrewm@0: xml.setAttribute("_mix" ,_mix); andrewm@0: xml.setAttribute("_lateEarly" ,_lateEarly); andrewm@0: 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 ReverbAudioProcessor::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("JRAMReverb_XML")) andrewm@0: { andrewm@0: // Knobs andrewm@0: _density = (float) xmlState->getDoubleAttribute("_density",true); andrewm@0: _decay = (float) xmlState->getDoubleAttribute("_decay",true); andrewm@0: _size = (float) xmlState->getDoubleAttribute("_size",true); andrewm@0: _damp = (float) xmlState->getDoubleAttribute("_damp",true); andrewm@0: _bandwidth = (float) xmlState->getDoubleAttribute("_bandwidth",true); andrewm@0: _predelay = (float) xmlState->getDoubleAttribute("_predelay",true); andrewm@0: _gain = (float) xmlState->getDoubleAttribute("_gain",true); andrewm@0: _mix = (float) xmlState->getDoubleAttribute("_mix",true); andrewm@0: _lateEarly = (float) xmlState->getDoubleAttribute("_lateEarly",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 ReverbAudioProcessor(); andrewm@0: } andrewm@0: andrewm@0: const String ReverbAudioProcessor::getName() const andrewm@0: { andrewm@0: return JucePlugin_Name; andrewm@0: } andrewm@0: andrewm@0: bool ReverbAudioProcessor::silenceInProducesSilenceOut() const andrewm@0: { andrewm@0: return true; andrewm@0: } andrewm@0: andrewm@0: int ReverbAudioProcessor::getNumParameters() andrewm@0: { andrewm@0: return MVerb::NUM_PARAMS; andrewm@0: } andrewm@0: andrewm@0: float ReverbAudioProcessor::getParameter (int index) // externally accessible andrewm@0: { andrewm@0: switch (index) andrewm@0: { andrewm@0: case MVerb::DENSITY: return GetDensity(); andrewm@0: case MVerb::DECAY: return GetDecay(); andrewm@0: case MVerb::SIZE: return GetSize(); andrewm@0: case MVerb::DAMPINGFREQ: return GetDamp(); andrewm@0: case MVerb::BANDWIDTHFREQ: return GetBandwidth(); andrewm@0: case MVerb::PREDELAY: return GetPredelay(); andrewm@0: case MVerb::GAIN: return GetGain(); andrewm@0: case MVerb::MIX: return GetMix(); andrewm@0: case MVerb::EARLYMIX: return GetLateEarly(); andrewm@0: default: return 0.0f; andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: void ReverbAudioProcessor::setParameter (int index, float newValue) // externally accessible andrewm@0: { andrewm@0: switch (index) andrewm@0: { andrewm@0: case MVerb::DENSITY: andrewm@0: SetDensity(newValue); andrewm@0: break; andrewm@0: case MVerb::DECAY: andrewm@0: SetDecay(newValue); andrewm@0: break; andrewm@0: case MVerb::SIZE: andrewm@0: SetSize(newValue); andrewm@0: break; andrewm@0: case MVerb::DAMPINGFREQ: andrewm@0: SetDamp(newValue); andrewm@0: break; andrewm@0: case MVerb::BANDWIDTHFREQ: andrewm@0: SetBandwidth(newValue); andrewm@0: break; andrewm@0: case MVerb::PREDELAY: andrewm@0: SetPredelay(newValue); andrewm@0: break; andrewm@0: case MVerb::GAIN: andrewm@0: SetGain(newValue); andrewm@0: break; andrewm@0: case MVerb::MIX: andrewm@0: SetMix(newValue); andrewm@0: break; andrewm@0: case MVerb::EARLYMIX: andrewm@0: SetLateEarly(newValue); andrewm@0: break; andrewm@0: default: andrewm@0: break; andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: const String ReverbAudioProcessor::getParameterName (int index) // externally accessible andrewm@0: { andrewm@0: switch (index) andrewm@0: { andrewm@0: case MVerb::DENSITY: return "Density"; andrewm@0: case MVerb::DECAY: return "Decay"; andrewm@0: case MVerb::SIZE: return "Size"; andrewm@0: case MVerb::DAMPINGFREQ: return "Damp"; andrewm@0: case MVerb::BANDWIDTHFREQ: return "Bandwidth"; andrewm@0: case MVerb::PREDELAY: return "Predelay"; andrewm@0: case MVerb::GAIN: return "Gain"; andrewm@0: case MVerb::MIX: return "Mix"; andrewm@0: case MVerb::EARLYMIX: return "Late vs. early reflections"; andrewm@0: default: break; andrewm@0: } andrewm@0: return String::empty; andrewm@0: } andrewm@0: andrewm@0: const String ReverbAudioProcessor::getParameterText (int index) andrewm@0: { andrewm@0: return String (getParameter (index), 2); andrewm@0: } andrewm@0: andrewm@0: const String ReverbAudioProcessor::getInputChannelName (int channelIndex) const andrewm@0: { andrewm@0: return String (channelIndex + 1); andrewm@0: } andrewm@0: andrewm@0: const String ReverbAudioProcessor::getOutputChannelName (int channelIndex) const andrewm@0: { andrewm@0: return String (channelIndex + 1); andrewm@0: } andrewm@0: andrewm@0: bool ReverbAudioProcessor::isInputChannelStereoPair (int index) const andrewm@0: { andrewm@0: return true; andrewm@0: } andrewm@0: andrewm@0: bool ReverbAudioProcessor::isOutputChannelStereoPair (int index) const andrewm@0: { andrewm@0: return true; andrewm@0: } andrewm@0: andrewm@0: bool ReverbAudioProcessor::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 ReverbAudioProcessor::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 ReverbAudioProcessor::getNumPrograms() andrewm@0: { andrewm@0: return 0; andrewm@0: } andrewm@0: andrewm@0: int ReverbAudioProcessor::getCurrentProgram() andrewm@0: { andrewm@0: return 0; andrewm@0: } andrewm@0: andrewm@0: void ReverbAudioProcessor::setCurrentProgram (int index) andrewm@0: { andrewm@0: } andrewm@0: andrewm@0: const String ReverbAudioProcessor::getProgramName (int index) andrewm@0: { andrewm@0: return String::empty; andrewm@0: } andrewm@0: andrewm@0: void ReverbAudioProcessor::changeProgramName (int index, const String& newName) andrewm@0: { andrewm@0: }