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.

195 lines
7.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "DemoEditorComponent.h"
  24. //==============================================================================
  25. // quick-and-dirty function to format a timecode string
  26. static const String timeToTimecodeString (const double seconds)
  27. {
  28. const double absSecs = fabs (seconds);
  29. const tchar* const sign = (seconds < 0) ? T("-") : T("");
  30. const int hours = (int) (absSecs / (60.0 * 60.0));
  31. const int mins = ((int) (absSecs / 60.0)) % 60;
  32. const int secs = ((int) absSecs) % 60;
  33. return String::formatted (T("%s%02d:%02d:%02d:%03d"),
  34. sign, hours, mins, secs,
  35. roundDoubleToInt (absSecs * 1000) % 1000);
  36. }
  37. // quick-and-dirty function to format a bars/beats string
  38. static const String ppqToBarsBeatsString (const double ppq,
  39. const double lastBarPPQ,
  40. const int numerator,
  41. const int denominator)
  42. {
  43. if (numerator == 0 || denominator == 0)
  44. return T("1|1|0");
  45. const int ppqPerBar = (numerator * 4 / denominator);
  46. const double beats = (fmod (ppq, ppqPerBar) / ppqPerBar) * numerator;
  47. const int bar = ((int) ppq) / ppqPerBar + 1;
  48. const int beat = ((int) beats) + 1;
  49. const int ticks = ((int) (fmod (beats, 1.0) * 960.0));
  50. String s;
  51. s << bar << T('|') << beat << T('|') << ticks;
  52. return s;
  53. }
  54. //==============================================================================
  55. DemoEditorComponent::DemoEditorComponent (DemoJuceFilter* const ownerFilter)
  56. : AudioFilterEditor (ownerFilter)
  57. {
  58. // create our gain slider..
  59. addAndMakeVisible (gainSlider = new Slider (T("gain")));
  60. gainSlider->addListener (this);
  61. gainSlider->setRange (0.0, 1.0, 0.01);
  62. gainSlider->setTooltip (T("changes the volume of the audio that runs through the plugin.."));
  63. // get the gain parameter from the filter and use it to set up our slider
  64. gainSlider->setValue (ownerFilter->getParameter (0), false);
  65. // create and add the midi keyboard component..
  66. addAndMakeVisible (midiKeyboard
  67. = new MidiKeyboardComponent (ownerFilter->keyboardState,
  68. MidiKeyboardComponent::horizontalKeyboard));
  69. // add a label that will display the current timecode and status..
  70. addAndMakeVisible (infoLabel = new Label (String::empty, String::empty));
  71. // add the triangular resizer component for the bottom-right of the UI
  72. addAndMakeVisible (resizer = new ResizableCornerComponent (this, &resizeLimits));
  73. resizeLimits.setSizeLimits (150, 150, 800, 300);
  74. // set our component's initial size to be the last one that was stored in the filter's settings
  75. setSize (ownerFilter->lastUIWidth,
  76. ownerFilter->lastUIHeight);
  77. // register ourselves with the filter - it will use its ChangeBroadcaster base
  78. // class to tell us when something has changed, and this will call our changeListenerCallback()
  79. // method.
  80. ownerFilter->addChangeListener (this);
  81. }
  82. DemoEditorComponent::~DemoEditorComponent()
  83. {
  84. getFilter()->removeChangeListener (this);
  85. deleteAllChildren();
  86. }
  87. //==============================================================================
  88. void DemoEditorComponent::paint (Graphics& g)
  89. {
  90. // just clear the window
  91. g.fillAll (Colour::greyLevel (0.9f));
  92. }
  93. void DemoEditorComponent::resized()
  94. {
  95. gainSlider->setBounds (10, 10, 200, 22);
  96. infoLabel->setBounds (10, 35, 450, 20);
  97. const int keyboardHeight = 70;
  98. midiKeyboard->setBounds (4, getHeight() - keyboardHeight - 4,
  99. getWidth() - 8, keyboardHeight);
  100. resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16);
  101. // if we've been resized, tell the filter so that it can store the new size
  102. // in its settings
  103. getFilter()->lastUIWidth = getWidth();
  104. getFilter()->lastUIHeight = getHeight();
  105. }
  106. //==============================================================================
  107. void DemoEditorComponent::changeListenerCallback (void* source)
  108. {
  109. // this is the filter telling us that it's changed, so we'll update our
  110. // display of the time, midi message, etc.
  111. updateParametersFromFilter();
  112. }
  113. void DemoEditorComponent::sliderValueChanged (Slider*)
  114. {
  115. getFilter()->setParameterNotifyingHost (0, (float) gainSlider->getValue());
  116. }
  117. //==============================================================================
  118. void DemoEditorComponent::updateParametersFromFilter()
  119. {
  120. DemoJuceFilter* const filter = getFilter();
  121. // we use this lock to make sure the processBlock() method isn't writing to the
  122. // lastMidiMessage variable while we're trying to read it, but be extra-careful to
  123. // only hold the lock for a minimum amount of time..
  124. filter->getCallbackLock().enter();
  125. // take a local copy of the info we need while we've got the lock..
  126. const AudioFilterBase::CurrentPositionInfo positionInfo (filter->lastPosInfo);
  127. const float newGain = filter->getParameter (0);
  128. // ..release the lock ASAP
  129. filter->getCallbackLock().exit();
  130. // ..and after releasing the lock, we're free to do the time-consuming UI stuff..
  131. String infoText;
  132. infoText << String (positionInfo.bpm, 2) << T(" bpm, ")
  133. << positionInfo.timeSigNumerator << T("/") << positionInfo.timeSigDenominator
  134. << T(" - ") << timeToTimecodeString (positionInfo.timeInSeconds)
  135. << T(" - ") << ppqToBarsBeatsString (positionInfo.ppqPosition,
  136. positionInfo.ppqPositionOfLastBarStart,
  137. positionInfo.timeSigNumerator,
  138. positionInfo.timeSigDenominator);
  139. if (positionInfo.isPlaying)
  140. infoText << T(" (playing)");
  141. infoLabel->setText (infoText, false);
  142. /* Update our slider.
  143. (note that it's important here to tell the slider not to send a change
  144. message, because that would cause it to call the filter with a parameter
  145. change message again, and the values would drift out.
  146. */
  147. gainSlider->setValue (newGain, false);
  148. setSize (filter->lastUIWidth,
  149. filter->lastUIHeight);
  150. }