diff effects/vibrato/Source/PluginEditor.cpp @ 0:e32fe563e124

First commit
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 10 Oct 2014 15:41:23 +0100
parents
children 04e171d2a747
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/effects/vibrato/Source/PluginEditor.cpp	Fri Oct 10 15:41:23 2014 +0100
@@ -0,0 +1,249 @@
+/*
+  This code accompanies the textbook:
+ 
+  Digital Audio Effects: Theory, Implementation and Application
+  Joshua D. Reiss and Andrew P. McPherson
+ 
+  ---
+ 
+  Vibrato: frequency modulation using delay lines
+  See textbook Chapter 2: Delay Line Effects
+ 
+  Code by Andrew McPherson, Brecht De Man and Joshua Reiss
+ 
+  ---
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+ 
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "PluginProcessor.h"
+#include "PluginEditor.h"
+#include <cstring>
+
+#ifdef _MSC_VER
+#define snprintf _snprintf_s //support for pre-2014 versions of Visual Studio
+#endif // _MSC_VER
+
+//==============================================================================
+VibratoAudioProcessorEditor::VibratoAudioProcessorEditor (VibratoAudioProcessor* ownerFilter)
+    : AudioProcessorEditor (ownerFilter),
+      sweepWidthLabel_("", "Sweep Width (sec.):"),
+      frequencyLabel_("", "LFO Frequency:"),
+      waveformLabel_("", "LFO Waveform:"),
+      interpolationLabel_("", "Interpolation Type:"),
+      pitchShiftLabel_("", "Maximum Pitch Shift:")
+{
+    // Initialise variables
+    oldSweepWidth_ = oldFrequency_ = -1.0;
+    oldWaveform_ = -1;
+    
+    // Set up the sliders
+    addAndMakeVisible (&sweepWidthSlider_);
+    sweepWidthSlider_.setSliderStyle (Slider::Rotary);
+    sweepWidthSlider_.addListener (this);
+    sweepWidthSlider_.setRange (.001, VibratoAudioProcessor::kMaximumSweepWidth, 0.0005);
+
+    addAndMakeVisible (&frequencySlider_);
+    frequencySlider_.setSliderStyle (Slider::Rotary);
+    frequencySlider_.addListener (this);
+    frequencySlider_.setRange (0.5, 5.0, 0.025);
+    
+    addAndMakeVisible(&waveformComboBox_);
+    waveformComboBox_.setEditableText(false);
+    waveformComboBox_.setJustificationType(Justification::left);
+    waveformComboBox_.addItem("Sine", VibratoAudioProcessor::kWaveformSine);
+    waveformComboBox_.addItem("Triangle", VibratoAudioProcessor::kWaveformTriangle);
+    waveformComboBox_.addItem("Sawtooth (rising)", VibratoAudioProcessor::kWaveformSawtooth);
+    waveformComboBox_.addItem("Sawtooth (falling)", VibratoAudioProcessor::kWaveformInverseSawtooth);
+    waveformComboBox_.addListener(this);
+    
+    addAndMakeVisible(&interpolationComboBox_);
+    interpolationComboBox_.setEditableText(false);
+    interpolationComboBox_.setJustificationType(Justification::left);
+    interpolationComboBox_.addItem("None", VibratoAudioProcessor::kInterpolationNearestNeighbour);
+    interpolationComboBox_.addItem("Linear", VibratoAudioProcessor::kInterpolationLinear);
+    interpolationComboBox_.addItem("Cubic", VibratoAudioProcessor::kInterpolationCubic);
+    interpolationComboBox_.addListener(this);
+    
+    // This label is informational and exists apart from other controls
+    // The other labels are attached to sliders and combo boxes
+    addAndMakeVisible(&pitchShiftLabel_);
+    pitchShiftLabel_.setFont(Font (12.0f));
+
+    sweepWidthLabel_.attachToComponent(&sweepWidthSlider_, false);
+    sweepWidthLabel_.setFont(Font (11.0f));
+    
+    frequencyLabel_.attachToComponent(&frequencySlider_, false);
+    frequencyLabel_.setFont(Font (11.0f));
+    
+    waveformLabel_.attachToComponent(&waveformComboBox_, false);
+    waveformLabel_.setFont(Font (11.0f));
+    
+    interpolationLabel_.attachToComponent(&interpolationComboBox_, false);
+    interpolationLabel_.setFont(Font (11.0f));
+    
+    // add the triangular resizer component for the bottom-right of the UI
+    addAndMakeVisible(resizer_ = new ResizableCornerComponent (this, &resizeLimits_));
+    resizeLimits_.setSizeLimits(370, 160, 600, 300);
+    
+    // set our component's initial size to be the last one that was stored in the filter's settings
+    setSize(ownerFilter->lastUIWidth_,
+            ownerFilter->lastUIHeight_);
+    
+    startTimer(50);
+}
+
+VibratoAudioProcessorEditor::~VibratoAudioProcessorEditor()
+{
+}
+
+//==============================================================================
+void VibratoAudioProcessorEditor::paint (Graphics& g)
+{
+    g.fillAll (Colours::grey);
+}
+
+void VibratoAudioProcessorEditor::resized()
+{
+    sweepWidthSlider_.setBounds (20, 20, 150, 40);
+    frequencySlider_.setBounds(200, 20, 150, 40);
+    pitchShiftLabel_.setBounds(20, 60, 350, 20);
+    waveformComboBox_.setBounds(20, 100, 150, 30);
+    interpolationComboBox_.setBounds(200, 100, 150, 30);
+    
+    resizer_->setBounds(getWidth() - 16, getHeight() - 16, 16, 16);
+    
+    getProcessor()->lastUIWidth_ = getWidth();
+    getProcessor()->lastUIHeight_ = getHeight();
+}
+
+//==============================================================================
+// This timer periodically checks whether any of the filter's parameters have changed...
+void VibratoAudioProcessorEditor::timerCallback()
+{
+    VibratoAudioProcessor* ourProcessor = getProcessor();
+    
+    sweepWidthSlider_.setValue(ourProcessor->sweepWidth_, dontSendNotification);
+    frequencySlider_.setValue(ourProcessor->frequency_, dontSendNotification);
+    waveformComboBox_.setSelectedId(ourProcessor->waveform_, false);
+    interpolationComboBox_.setSelectedId(ourProcessor->interpolation_, false);
+    
+    // Update the pitch shift label only when something changes to avoid
+    // needless calculations
+    if(ourProcessor->sweepWidth_ != oldSweepWidth_ ||
+       ourProcessor->frequency_ != oldFrequency_ ||
+       ourProcessor->waveform_ != oldWaveform_)
+    {
+        updatePitchShiftLabel();
+        oldSweepWidth_ = ourProcessor->sweepWidth_;
+        oldFrequency_ = ourProcessor->frequency_;
+        oldWaveform_ = ourProcessor->waveform_;
+    }
+}
+
+// This is our Slider::Listener callback, when the user drags a slider.
+void VibratoAudioProcessorEditor::sliderValueChanged (Slider* slider)
+{
+    // It's vital to use setParameterNotifyingHost to change any parameters that are automatable
+    // by the host, rather than just modifying them directly, otherwise the host won't know
+    // that they've changed.
+    
+    if (slider == &sweepWidthSlider_)
+    {
+        getProcessor()->setParameterNotifyingHost (VibratoAudioProcessor::kSweepWidthParam,
+                                                   (float)sweepWidthSlider_.getValue());
+        updatePitchShiftLabel();
+    }
+    else if (slider == &frequencySlider_)
+    {
+        getProcessor()->setParameterNotifyingHost (VibratoAudioProcessor::kFrequencyParam,
+                                                   (float)frequencySlider_.getValue());
+        updatePitchShiftLabel();
+    }
+}
+
+// Similar callback to sliderValueChanged for ComboBox updates
+void VibratoAudioProcessorEditor::comboBoxChanged (ComboBox *comboBox)
+{
+    if(comboBox == &waveformComboBox_)
+    {
+        getProcessor()->setParameterNotifyingHost (VibratoAudioProcessor::kWaveformParam,
+                                                   (float)waveformComboBox_.getSelectedId());
+        updatePitchShiftLabel();
+    }
+    else if(comboBox == &interpolationComboBox_)
+    {
+        getProcessor()->setParameterNotifyingHost (VibratoAudioProcessor::kInterpolationParam,
+                                                   (float)interpolationComboBox_.getSelectedId());
+        // This parameter doesn't affect the maximum pitch shift
+    }
+}
+
+// Update the content of the pitch shift label (whenever controls change)
+void VibratoAudioProcessorEditor::updatePitchShiftLabel()
+{
+    VibratoAudioProcessor* ourProcessor = getProcessor();
+    
+    // The amount of pitch shift depends on the derivative of the delay, which
+    // is given by: delay = width * f(frequency * t)
+    // where f(x) is one of:
+    //   sine --> 0.5 + 0.5*sin(2*pi*x) --> derivative pi*cos(x)*dx
+    //   triangle --> {2.0*x or 1.0-(2.0*(x-0.5)) ---> derivative +/- 2.0*dx
+    //   sawtooth rising --> x --> derivative 1.0*dx
+    //   sawtooth falling --> 1.0 - x --> derivative -1.0*dx
+    // For f(frequency*t), "dx" = frequency
+    
+    float maxSpeed = 1.0, minSpeed = 1.0;
+    float maxPitch = 0.0, minPitch = 0.0;
+    char str[256];
+    
+    switch(ourProcessor->waveform_)
+    {
+        case VibratoAudioProcessor::kWaveformSine:
+            maxSpeed = 1.0 + M_PI * ourProcessor->frequency_ * ourProcessor->sweepWidth_;
+            minSpeed = 1.0 - M_PI * ourProcessor->frequency_ * ourProcessor->sweepWidth_;
+            break;
+        case VibratoAudioProcessor::kWaveformTriangle:
+            maxSpeed = 1.0 + 2.0 * ourProcessor->frequency_ * ourProcessor->sweepWidth_;
+            minSpeed = 1.0 - 2.0 * ourProcessor->frequency_ * ourProcessor->sweepWidth_;
+            break;
+        case VibratoAudioProcessor::kWaveformSawtooth:
+            // Standard (rising) sawtooth means delay is increasing --> pitch is lower
+            maxSpeed = 1.0;
+            minSpeed = 1.0 - ourProcessor->frequency_ * ourProcessor->sweepWidth_;
+            break;
+        case VibratoAudioProcessor::kWaveformInverseSawtooth:
+            // Inverse (falling) sawtooth means delay is decreasing --> pitch is higher
+            maxSpeed = 1.0 + ourProcessor->frequency_ * ourProcessor->sweepWidth_;
+            minSpeed = 1.0;
+            break;
+    }
+    
+    // Convert speed to pitch shift --> semitones = 12*log2(speed)
+    maxPitch = 12.0*logf(maxSpeed)/logf(2.0);
+    
+    if(minSpeed > 0)
+    {
+        minPitch = 12.0*logf(minSpeed)/logf(2.0);
+        snprintf(str, 256, "Vibrato range: %+.2f to %+.2f semitones (speed %.3f to %.3f)",
+                 minPitch, maxPitch, minSpeed, maxSpeed);
+    }
+    else
+    {
+        snprintf(str, 256, "Vibrato range: --- to %+.2f semitones (speed %.3f to %.3f)",
+                 maxPitch, minSpeed, maxSpeed);
+    }
+    
+    pitchShiftLabel_.setText(str, dontSendNotification);
+}
\ No newline at end of file