1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

AudioWorkgroup: Add demo app

This commit is contained in:
Oliver James 2023-10-16 11:09:44 +01:00 committed by reuk
parent 82e1c7483e
commit 3624346e90
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
5 changed files with 1220 additions and 3 deletions

View file

@ -0,0 +1,186 @@
/*
==============================================================================
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;
};