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.

187 lines
6.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE examples.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. The code included in this file is provided under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  7. To use, copy, modify, and/or distribute this software for any purpose with or
  8. without fee is hereby granted provided that the above copyright notice and
  9. this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
  11. WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
  12. PURPOSE, ARE DISCLAIMED.
  13. ==============================================================================
  14. */
  15. #pragma once
  16. class ADSRComponent final : public Component
  17. {
  18. public:
  19. ADSRComponent()
  20. : envelope { *this }
  21. {
  22. for (Slider* slider : { &adsrAttack, &adsrDecay, &adsrSustain, &adsrRelease })
  23. {
  24. if (slider == &adsrSustain)
  25. {
  26. slider->textFromValueFunction = [slider] (double value)
  27. {
  28. String text;
  29. text << slider->getName();
  30. const auto val = (int) jmap (value, 0.0, 1.0, 0.0, 100.0);
  31. text << String::formatted (": %d%%", val);
  32. return text;
  33. };
  34. }
  35. else
  36. {
  37. slider->textFromValueFunction = [slider] (double value)
  38. {
  39. String text;
  40. text << slider->getName();
  41. text << ": " << ((value < 0.4f) ? String::formatted ("%dms", (int) std::round (value * 1000))
  42. : String::formatted ("%0.2lf Sec", value));
  43. return text;
  44. };
  45. slider->setSkewFactor (0.3);
  46. }
  47. slider->setRange (0, 1);
  48. slider->setTextBoxStyle (Slider::TextBoxBelow, true, 300, 25);
  49. slider->onValueChange = [this]
  50. {
  51. NullCheckedInvocation::invoke (onChange);
  52. repaint();
  53. };
  54. addAndMakeVisible (slider);
  55. }
  56. adsrAttack.setName ("Attack");
  57. adsrDecay.setName ("Decay");
  58. adsrSustain.setName ("Sustain");
  59. adsrRelease.setName ("Release");
  60. adsrAttack.setValue (0.1, dontSendNotification);
  61. adsrDecay.setValue (0.3, dontSendNotification);
  62. adsrSustain.setValue (0.3, dontSendNotification);
  63. adsrRelease.setValue (0.2, dontSendNotification);
  64. addAndMakeVisible (envelope);
  65. }
  66. std::function<void()> onChange;
  67. ADSR::Parameters getParameters() const
  68. {
  69. return
  70. {
  71. (float) adsrAttack.getValue(),
  72. (float) adsrDecay.getValue(),
  73. (float) adsrSustain.getValue(),
  74. (float) adsrRelease.getValue(),
  75. };
  76. }
  77. void resized() final
  78. {
  79. auto bounds = getLocalBounds();
  80. const auto knobWidth = bounds.getWidth() / 4;
  81. auto knobBounds = bounds.removeFromBottom (bounds.getHeight() / 2);
  82. {
  83. adsrAttack.setBounds (knobBounds.removeFromLeft (knobWidth));
  84. adsrDecay.setBounds (knobBounds.removeFromLeft (knobWidth));
  85. adsrSustain.setBounds (knobBounds.removeFromLeft (knobWidth));
  86. adsrRelease.setBounds (knobBounds.removeFromLeft (knobWidth));
  87. }
  88. envelope.setBounds (bounds);
  89. }
  90. Slider adsrAttack { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
  91. Slider adsrDecay { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
  92. Slider adsrSustain { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
  93. Slider adsrRelease { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
  94. private:
  95. class Envelope final : public Component
  96. {
  97. public:
  98. Envelope (ADSRComponent& adsr) : parent { adsr } {}
  99. void paint (Graphics& g) final
  100. {
  101. const auto env = parent.getParameters();
  102. // sustain isn't a length but we use a fixed value here to give
  103. // sustain some visual width in the envelope
  104. constexpr auto sustainLength = 0.1;
  105. const auto adsrLength = env.attack
  106. + env.decay
  107. + sustainLength
  108. + env.release;
  109. auto bounds = getLocalBounds().toFloat();
  110. const auto attackWidth = bounds.proportionOfWidth (env.attack / adsrLength);
  111. const auto decayWidth = bounds.proportionOfWidth (env.decay / adsrLength);
  112. const auto sustainWidth = bounds.proportionOfWidth (sustainLength / adsrLength);
  113. const auto releaseWidth = bounds.proportionOfWidth (env.release / adsrLength);
  114. const auto sustainHeight = bounds.proportionOfHeight (1 - env.sustain);
  115. const auto attackBounds = bounds.removeFromLeft (attackWidth);
  116. const auto decayBounds = bounds.removeFromLeft (decayWidth);
  117. const auto sustainBounds = bounds.removeFromLeft (sustainWidth);
  118. const auto releaseBounds = bounds.removeFromLeft (releaseWidth);
  119. g.setColour (Colours::black.withAlpha (0.1f));
  120. g.fillRect (bounds);
  121. const auto alpha = 0.4f;
  122. g.setColour (Colour (246, 98, 92).withAlpha (alpha));
  123. g.fillRect (attackBounds);
  124. g.setColour (Colour (242, 187, 60).withAlpha (alpha));
  125. g.fillRect (decayBounds);
  126. g.setColour (Colour (109, 234, 166).withAlpha (alpha));
  127. g.fillRect (sustainBounds);
  128. g.setColour (Colour (131, 61, 183).withAlpha (alpha));
  129. g.fillRect (releaseBounds);
  130. Path envelopePath;
  131. envelopePath.startNewSubPath (attackBounds.getBottomLeft());
  132. envelopePath.lineTo (decayBounds.getTopLeft());
  133. envelopePath.lineTo (sustainBounds.getX(), sustainHeight);
  134. envelopePath.lineTo (releaseBounds.getX(), sustainHeight);
  135. envelopePath.lineTo (releaseBounds.getBottomRight());
  136. const auto lineThickness = 4.0f;
  137. g.setColour (Colours::white);
  138. g.strokePath (envelopePath, PathStrokeType { lineThickness });
  139. }
  140. private:
  141. ADSRComponent& parent;
  142. };
  143. Envelope envelope;
  144. };