The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
8.0KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "PluginProcessor.h"
  20. #include "PluginEditor.h"
  21. //==============================================================================
  22. // This is a handy slider subclass that controls an AudioProcessorParameter
  23. // (may move this class into the library itself at some point in the future..)
  24. class JuceDemoPluginAudioProcessorEditor::ParameterSlider : public Slider,
  25. private Timer
  26. {
  27. public:
  28. ParameterSlider (AudioProcessorParameter& p)
  29. : Slider (p.getName (256)), param (p)
  30. {
  31. setRange (0.0, 1.0, 0.0);
  32. startTimerHz (30);
  33. updateSliderPos();
  34. }
  35. void valueChanged() override
  36. {
  37. if (isMouseButtonDown())
  38. param.setValueNotifyingHost ((float) Slider::getValue());
  39. else
  40. param.setValue ((float) Slider::getValue());
  41. }
  42. void timerCallback() override { updateSliderPos(); }
  43. void startedDragging() override { param.beginChangeGesture(); }
  44. void stoppedDragging() override { param.endChangeGesture(); }
  45. double getValueFromText (const String& text) override { return param.getValueForText (text); }
  46. String getTextFromValue (double value) override { return param.getText ((float) value, 1024); }
  47. void updateSliderPos()
  48. {
  49. const float newValue = param.getValue();
  50. if (newValue != (float) Slider::getValue() && ! isMouseButtonDown())
  51. Slider::setValue (newValue);
  52. }
  53. AudioProcessorParameter& param;
  54. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterSlider)
  55. };
  56. //==============================================================================
  57. JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemoPluginAudioProcessor& owner)
  58. : AudioProcessorEditor (owner),
  59. midiKeyboard (owner.keyboardState, MidiKeyboardComponent::horizontalKeyboard),
  60. timecodeDisplayLabel (String()),
  61. gainLabel (String(), "Throughput level:"),
  62. delayLabel (String(), "Delay:")
  63. {
  64. // add some sliders..
  65. addAndMakeVisible (gainSlider = new ParameterSlider (*owner.gainParam));
  66. gainSlider->setSliderStyle (Slider::Rotary);
  67. addAndMakeVisible (delaySlider = new ParameterSlider (*owner.delayParam));
  68. delaySlider->setSliderStyle (Slider::Rotary);
  69. // add some labels for the sliders..
  70. gainLabel.attachToComponent (gainSlider, false);
  71. gainLabel.setFont (Font (11.0f));
  72. delayLabel.attachToComponent (delaySlider, false);
  73. delayLabel.setFont (Font (11.0f));
  74. // add the midi keyboard component..
  75. addAndMakeVisible (midiKeyboard);
  76. // add a label that will display the current timecode and status..
  77. addAndMakeVisible (timecodeDisplayLabel);
  78. timecodeDisplayLabel.setFont (Font (Font::getDefaultMonospacedFontName(), 15.0f, Font::plain));
  79. // set resize limits for this plug-in
  80. setResizeLimits (400, 200, 1024, 700);
  81. // set our component's initial size to be the last one that was stored in the filter's settings
  82. setSize (owner.lastUIWidth,
  83. owner.lastUIHeight);
  84. updateTrackProperties();
  85. // start a timer which will keep our timecode display updated
  86. startTimerHz (30);
  87. }
  88. JuceDemoPluginAudioProcessorEditor::~JuceDemoPluginAudioProcessorEditor()
  89. {
  90. }
  91. //==============================================================================
  92. void JuceDemoPluginAudioProcessorEditor::paint (Graphics& g)
  93. {
  94. g.setColour (backgroundColour);
  95. g.fillAll();
  96. }
  97. void JuceDemoPluginAudioProcessorEditor::resized()
  98. {
  99. // This lays out our child components...
  100. Rectangle<int> r (getLocalBounds().reduced (8));
  101. timecodeDisplayLabel.setBounds (r.removeFromTop (26));
  102. midiKeyboard.setBounds (r.removeFromBottom (70));
  103. r.removeFromTop (20);
  104. Rectangle<int> sliderArea (r.removeFromTop (60));
  105. gainSlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth() / 2)));
  106. delaySlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth())));
  107. getProcessor().lastUIWidth = getWidth();
  108. getProcessor().lastUIHeight = getHeight();
  109. }
  110. //==============================================================================
  111. void JuceDemoPluginAudioProcessorEditor::timerCallback()
  112. {
  113. updateTimecodeDisplay (getProcessor().lastPosInfo);
  114. }
  115. void JuceDemoPluginAudioProcessorEditor::hostMIDIControllerIsAvailable (bool controllerIsAvailable)
  116. {
  117. midiKeyboard.setVisible (! controllerIsAvailable);
  118. }
  119. void JuceDemoPluginAudioProcessorEditor::updateTrackProperties ()
  120. {
  121. auto trackColour = getProcessor().trackProperties.colour;
  122. auto& lf = getLookAndFeel();
  123. backgroundColour = (trackColour == Colour() ? lf.findColour (ResizableWindow::backgroundColourId)
  124. : trackColour.withAlpha (1.0f).withBrightness (0.266f));
  125. repaint();
  126. }
  127. //==============================================================================
  128. // quick-and-dirty function to format a timecode string
  129. static String timeToTimecodeString (double seconds)
  130. {
  131. const int millisecs = roundToInt (seconds * 1000.0);
  132. const int absMillisecs = std::abs (millisecs);
  133. return String::formatted ("%02d:%02d:%02d.%03d",
  134. millisecs / 3600000,
  135. (absMillisecs / 60000) % 60,
  136. (absMillisecs / 1000) % 60,
  137. absMillisecs % 1000);
  138. }
  139. // quick-and-dirty function to format a bars/beats string
  140. static String quarterNotePositionToBarsBeatsString (double quarterNotes, int numerator, int denominator)
  141. {
  142. if (numerator == 0 || denominator == 0)
  143. return "1|1|000";
  144. const int quarterNotesPerBar = (numerator * 4 / denominator);
  145. const double beats = (fmod (quarterNotes, quarterNotesPerBar) / quarterNotesPerBar) * numerator;
  146. const int bar = ((int) quarterNotes) / quarterNotesPerBar + 1;
  147. const int beat = ((int) beats) + 1;
  148. const int ticks = ((int) (fmod (beats, 1.0) * 960.0 + 0.5));
  149. return String::formatted ("%d|%d|%03d", bar, beat, ticks);
  150. }
  151. // Updates the text in our position label.
  152. void JuceDemoPluginAudioProcessorEditor::updateTimecodeDisplay (AudioPlayHead::CurrentPositionInfo pos)
  153. {
  154. MemoryOutputStream displayText;
  155. displayText << "[" << SystemStats::getJUCEVersion() << "] "
  156. << String (pos.bpm, 2) << " bpm, "
  157. << pos.timeSigNumerator << '/' << pos.timeSigDenominator
  158. << " - " << timeToTimecodeString (pos.timeInSeconds)
  159. << " - " << quarterNotePositionToBarsBeatsString (pos.ppqPosition,
  160. pos.timeSigNumerator,
  161. pos.timeSigDenominator);
  162. if (pos.isRecording)
  163. displayText << " (recording)";
  164. else if (pos.isPlaying)
  165. displayText << " (playing)";
  166. timecodeDisplayLabel.setText (displayText.toString(), dontSendNotification);
  167. }