| 
							- /*
 -  ==============================================================================
 - 
 -  This file is part of the JUCE examples.
 -  Copyright (c) 2022 - Raw Material Software Limited
 - 
 -  The code included in this file is provided under the terms of the ISC license
 -  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 -  To use, copy, modify, and/or distribute this software for any purpose with or
 -  without fee is hereby granted provided that the above copyright notice and
 -  this permission notice appear in all copies.
 - 
 -  THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 -  WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 -  PURPOSE, ARE DISCLAIMED.
 - 
 -  ==============================================================================
 - */
 - 
 - #pragma once
 - 
 - class ADSRComponent final : public Component
 - {
 - public:
 -     ADSRComponent()
 -         : envelope { *this }
 -     {
 -         for (Slider* slider : { &adsrAttack, &adsrDecay, &adsrSustain, &adsrRelease })
 -         {
 -             if (slider == &adsrSustain)
 -             {
 -                 slider->textFromValueFunction = [slider] (double value)
 -                 {
 -                     String text;
 - 
 -                     text << slider->getName();
 - 
 -                     const auto val = (int) jmap (value, 0.0, 1.0, 0.0, 100.0);
 -                     text << String::formatted (": %d%%", val);
 - 
 -                     return text;
 -                 };
 -             }
 -             else
 -             {
 -                 slider->textFromValueFunction = [slider] (double value)
 -                 {
 -                     String text;
 - 
 -                     text << slider->getName();
 - 
 -                     text << ": " << ((value < 0.4f) ? String::formatted ("%dms", (int) std::round (value * 1000))
 -                                                     : String::formatted ("%0.2lf Sec", value));
 - 
 -                     return text;
 -                 };
 - 
 -                 slider->setSkewFactor (0.3);
 -             }
 - 
 -             slider->setRange (0, 1);
 -             slider->setTextBoxStyle (Slider::TextBoxBelow, true, 300, 25);
 -             slider->onValueChange = [this]
 -             {
 -                 NullCheckedInvocation::invoke (onChange);
 -                 repaint();
 -             };
 - 
 -             addAndMakeVisible (slider);
 -         }
 - 
 -         adsrAttack.setName ("Attack");
 -         adsrDecay.setName ("Decay");
 -         adsrSustain.setName ("Sustain");
 -         adsrRelease.setName ("Release");
 - 
 -         adsrAttack.setValue (0.1, dontSendNotification);
 -         adsrDecay.setValue (0.3, dontSendNotification);
 -         adsrSustain.setValue (0.3, dontSendNotification);
 -         adsrRelease.setValue (0.2, dontSendNotification);
 - 
 -         addAndMakeVisible (envelope);
 -     }
 - 
 -     std::function<void()> onChange;
 - 
 -     ADSR::Parameters getParameters() const
 -     {
 -         return
 -         {
 -             (float) adsrAttack.getValue(),
 -             (float) adsrDecay.getValue(),
 -             (float) adsrSustain.getValue(),
 -             (float) adsrRelease.getValue(),
 -         };
 -     }
 - 
 -     void resized() final
 -     {
 -         auto bounds = getLocalBounds();
 - 
 -         const auto knobWidth = bounds.getWidth() / 4;
 -         auto knobBounds = bounds.removeFromBottom (bounds.getHeight() / 2);
 -         {
 -             adsrAttack.setBounds (knobBounds.removeFromLeft (knobWidth));
 -             adsrDecay.setBounds (knobBounds.removeFromLeft (knobWidth));
 -             adsrSustain.setBounds (knobBounds.removeFromLeft (knobWidth));
 -             adsrRelease.setBounds (knobBounds.removeFromLeft (knobWidth));
 -         }
 - 
 -         envelope.setBounds (bounds);
 -     }
 - 
 -     Slider adsrAttack  { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
 -     Slider adsrDecay   { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
 -     Slider adsrSustain { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
 -     Slider adsrRelease { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
 - 
 - private:
 -     class Envelope final : public Component
 -     {
 -     public:
 -         Envelope (ADSRComponent& adsr) : parent { adsr } {}
 - 
 -         void paint (Graphics& g) final
 -         {
 -             const auto env = parent.getParameters();
 - 
 -             // sustain isn't a length but we use a fixed value here to give
 -             // sustain some visual width in the envelope
 -             constexpr auto sustainLength = 0.1;
 - 
 -             const auto adsrLength = env.attack
 -                                   + env.decay
 -                                   + sustainLength
 -                                   + env.release;
 - 
 -             auto bounds = getLocalBounds().toFloat();
 - 
 -             const auto attackWidth   = bounds.proportionOfWidth (env.attack    / adsrLength);
 -             const auto decayWidth    = bounds.proportionOfWidth (env.decay     / adsrLength);
 -             const auto sustainWidth  = bounds.proportionOfWidth (sustainLength / adsrLength);
 -             const auto releaseWidth  = bounds.proportionOfWidth (env.release   / adsrLength);
 -             const auto sustainHeight = bounds.proportionOfHeight (1 - env.sustain);
 - 
 -             const auto attackBounds  = bounds.removeFromLeft (attackWidth);
 -             const auto decayBounds   = bounds.removeFromLeft (decayWidth);
 -             const auto sustainBounds = bounds.removeFromLeft (sustainWidth);
 -             const auto releaseBounds = bounds.removeFromLeft (releaseWidth);
 - 
 -             g.setColour (Colours::black.withAlpha (0.1f));
 -             g.fillRect (bounds);
 - 
 -             const auto alpha = 0.4f;
 - 
 -             g.setColour (Colour (246, 98, 92).withAlpha (alpha));
 -             g.fillRect (attackBounds);
 - 
 -             g.setColour (Colour (242, 187, 60).withAlpha (alpha));
 -             g.fillRect (decayBounds);
 - 
 -             g.setColour (Colour (109, 234, 166).withAlpha (alpha));
 -             g.fillRect (sustainBounds);
 - 
 -             g.setColour (Colour (131, 61, 183).withAlpha (alpha));
 -             g.fillRect (releaseBounds);
 - 
 -             Path envelopePath;
 -             envelopePath.startNewSubPath (attackBounds.getBottomLeft());
 -             envelopePath.lineTo (decayBounds.getTopLeft());
 -             envelopePath.lineTo (sustainBounds.getX(), sustainHeight);
 -             envelopePath.lineTo (releaseBounds.getX(), sustainHeight);
 -             envelopePath.lineTo (releaseBounds.getBottomRight());
 - 
 -             const auto lineThickness = 4.0f;
 - 
 -             g.setColour (Colours::white);
 -             g.strokePath (envelopePath, PathStrokeType { lineThickness });
 -         }
 - 
 -     private:
 -         ADSRComponent& parent;
 -     };
 - 
 -     Envelope envelope;
 - };
 
 
  |