/* ============================================================================== 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" //============================================================================== // This is a handy slider subclass that controls an AudioProcessorParameter // (may move this class into the library itself at some point in the future..) class JuceDemoPluginAudioProcessorEditor::ParameterSlider : public Slider, private Timer { public: ParameterSlider (AudioProcessorParameter& p) : Slider (p.getName (256)), param (p) { setRange (0.0, 1.0, 0.0); startTimerHz (30); updateSliderPos(); } void valueChanged() override { if (isMouseButtonDown()) param.setValueNotifyingHost ((float) Slider::getValue()); else param.setValue ((float) Slider::getValue()); } void timerCallback() override { updateSliderPos(); } void startedDragging() override { param.beginChangeGesture(); } void stoppedDragging() override { param.endChangeGesture(); } double getValueFromText (const String& text) override { return param.getValueForText (text); } String getTextFromValue (double value) override { return param.getText ((float) value, 1024); } void updateSliderPos() { const float newValue = param.getValue(); if (newValue != (float) Slider::getValue() && ! isMouseButtonDown()) Slider::setValue (newValue); } AudioProcessorParameter& param; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterSlider) }; //============================================================================== JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemoPluginAudioProcessor& owner) : AudioProcessorEditor (owner), midiKeyboard (owner.keyboardState, MidiKeyboardComponent::horizontalKeyboard), timecodeDisplayLabel (String()), gainLabel (String(), "Throughput level:"), delayLabel (String(), "Delay:") { // add some sliders.. addAndMakeVisible (gainSlider = new ParameterSlider (*owner.gainParam)); gainSlider->setSliderStyle (Slider::Rotary); addAndMakeVisible (delaySlider = new ParameterSlider (*owner.delayParam)); delaySlider->setSliderStyle (Slider::Rotary); // add some labels for the sliders.. gainLabel.attachToComponent (gainSlider, false); gainLabel.setFont (Font (11.0f)); delayLabel.attachToComponent (delaySlider, false); delayLabel.setFont (Font (11.0f)); // add the midi keyboard component.. addAndMakeVisible (midiKeyboard); // add a label that will display the current timecode and status.. addAndMakeVisible (timecodeDisplayLabel); timecodeDisplayLabel.setFont (Font (Font::getDefaultMonospacedFontName(), 15.0f, Font::plain)); // set resize limits for this plug-in setResizeLimits (400, 200, 800, 300); // set our component's initial size to be the last one that was stored in the filter's settings setSize (owner.lastUIWidth, owner.lastUIHeight); // start a timer which will keep our timecode display updated startTimerHz (30); } JuceDemoPluginAudioProcessorEditor::~JuceDemoPluginAudioProcessorEditor() { } //============================================================================== void JuceDemoPluginAudioProcessorEditor::paint (Graphics& g) { g.setColour (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); g.fillAll(); } void JuceDemoPluginAudioProcessorEditor::resized() { // This lays out our child components... Rectangle r (getLocalBounds().reduced (8)); timecodeDisplayLabel.setBounds (r.removeFromTop (26)); midiKeyboard.setBounds (r.removeFromBottom (70)); r.removeFromTop (20); Rectangle sliderArea (r.removeFromTop (60)); gainSlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth() / 2))); delaySlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth()))); getProcessor().lastUIWidth = getWidth(); getProcessor().lastUIHeight = getHeight(); } //============================================================================== void JuceDemoPluginAudioProcessorEditor::timerCallback() { updateTimecodeDisplay (getProcessor().lastPosInfo); } //============================================================================== // quick-and-dirty function to format a timecode string static String timeToTimecodeString (double seconds) { const int millisecs = roundToInt (seconds * 1000.0); const int absMillisecs = std::abs (millisecs); return String::formatted ("%02d:%02d:%02d.%03d", millisecs / 3600000, (absMillisecs / 60000) % 60, (absMillisecs / 1000) % 60, absMillisecs % 1000); } // quick-and-dirty function to format a bars/beats string static String quarterNotePositionToBarsBeatsString (double quarterNotes, int numerator, int denominator) { if (numerator == 0 || denominator == 0) return "1|1|000"; const int quarterNotesPerBar = (numerator * 4 / denominator); const double beats = (fmod (quarterNotes, quarterNotesPerBar) / quarterNotesPerBar) * numerator; const int bar = ((int) quarterNotes) / quarterNotesPerBar + 1; const int beat = ((int) beats) + 1; const int ticks = ((int) (fmod (beats, 1.0) * 960.0 + 0.5)); return String::formatted ("%d|%d|%03d", bar, beat, ticks); } // Updates the text in our position label. void JuceDemoPluginAudioProcessorEditor::updateTimecodeDisplay (AudioPlayHead::CurrentPositionInfo pos) { MemoryOutputStream displayText; displayText << "[" << SystemStats::getJUCEVersion() << "] " << String (pos.bpm, 2) << " bpm, " << pos.timeSigNumerator << '/' << pos.timeSigDenominator << " - " << timeToTimecodeString (pos.timeInSeconds) << " - " << quarterNotePositionToBarsBeatsString (pos.ppqPosition, pos.timeSigNumerator, pos.timeSigDenominator); if (pos.isRecording) displayText << " (recording)"; else if (pos.isPlaying) displayText << " (playing)"; timecodeDisplayLabel.setText (displayText.toString(), dontSendNotification); }