Move envelope to MidiComponent and get parameters working

pull/170/head
James Ball 2023-11-25 15:37:33 +00:00
rodzic 5047979ff4
commit 6d08a00b33
12 zmienionych plików z 224 dodań i 31 usunięć

Wyświetl plik

@ -100,9 +100,6 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
frequencyLabel.setText(juce::String(roundedFrequency) + "Hz", juce::dontSendNotification);
}
);
addAndMakeVisible(envelope);
envelope.setEnv(audioProcessor.adsrEnv);
}
MainComponent::~MainComponent() {
@ -147,7 +144,6 @@ void MainComponent::resized() {
bounds.removeFromTop(padding);
openOscilloscope.setBounds(bounds.removeFromBottom(buttonHeight).withSizeKeepingCentre(160, buttonHeight));
bounds.removeFromBottom(padding);
envelope.setBounds(bounds.removeFromTop(200));
auto minDim = juce::jmin(bounds.getWidth(), bounds.getHeight());
visualiser.setBounds(bounds.withSizeKeepingCentre(minDim, minDim));
}

Wyświetl plik

@ -33,8 +33,6 @@ private:
VisualiserComponent visualiser{2, audioProcessor};
juce::TextButton openOscilloscope{"Open Oscilloscope"};
EnvelopeContainerComponent envelope;
juce::Label frequencyLabel;
int callbackIndex = -1;

Wyświetl plik

@ -10,12 +10,50 @@ MidiComponent::MidiComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
midiToggle.onClick = [this]() {
audioProcessor.midiEnabled->setBoolValueNotifyingHost(midiToggle.getToggleState());
};
addAndMakeVisible(envelope);
envelope.setAdsrMode(true);
envelope.setEnv(audioProcessor.adsrEnv);
envelope.addListener(&audioProcessor);
audioProcessor.attack->addListener(this);
audioProcessor.decay->addListener(this);
audioProcessor.sustain->addListener(this);
audioProcessor.release->addListener(this);
}
MidiComponent::~MidiComponent() {
envelope.removeListener(&audioProcessor);
audioProcessor.attack->removeListener(this);
audioProcessor.decay->removeListener(this);
audioProcessor.sustain->removeListener(this);
audioProcessor.release->removeListener(this);
}
void MidiComponent::parameterValueChanged(int parameterIndex, float newValue) {
triggerAsyncUpdate();
}
void MidiComponent::parameterGestureChanged(int parameterIndex, bool gestureIsStarting) {}
void MidiComponent::handleAsyncUpdate() {
DBG("MidiComponent::handleAsyncUpdate");
Env newEnv = Env::adsr(
audioProcessor.attack->getValueUnnormalised(),
audioProcessor.decay->getValueUnnormalised(),
audioProcessor.sustain->getValueUnnormalised(),
audioProcessor.release->getValueUnnormalised(),
1.0,
std::vector<EnvCurve>{ audioProcessor.attackShape->getValueUnnormalised(), audioProcessor.decayShape->getValueUnnormalised(), audioProcessor.releaseShape->getValueUnnormalised() }
);
envelope.setEnv(newEnv);
}
void MidiComponent::resized() {
auto area = getLocalBounds().reduced(5);
midiToggle.setBounds(area.removeFromTop(50));
envelope.setBounds(area.removeFromTop(200));
keyboard.setBounds(area.removeFromBottom(100));
}

Wyświetl plik

@ -4,9 +4,14 @@
#include "PluginProcessor.h"
class OscirenderAudioProcessorEditor;
class MidiComponent : public juce::Component {
class MidiComponent : public juce::Component, public juce::AudioProcessorParameter::Listener, public juce::AsyncUpdater {
public:
MidiComponent(OscirenderAudioProcessor&, OscirenderAudioProcessorEditor&);
~MidiComponent() override;
void parameterValueChanged(int parameterIndex, float newValue) override;
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
void handleAsyncUpdate() override;
void resized() override;
void paint(juce::Graphics& g) override;
@ -18,5 +23,7 @@ private:
juce::MidiKeyboardState keyboardState;
juce::MidiKeyboardComponent keyboard{keyboardState, juce::MidiKeyboardComponent::horizontalKeyboard};
EnvelopeContainerComponent envelope;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiComponent)
};

Wyświetl plik

@ -141,6 +141,11 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
addParameter(parameter);
}
addParameter(attack);
addParameter(decay);
addParameter(sustain);
addParameter(release);
for (int i = 0; i < 4; i++) {
synth.addVoice(new ShapeVoice(*this));
}
@ -694,6 +699,39 @@ void OscirenderAudioProcessor::parameterValueChanged(int parameterIndex, float n
void OscirenderAudioProcessor::parameterGestureChanged(int parameterIndex, bool gestureIsStarting) {}
void OscirenderAudioProcessor::envelopeChanged(EnvelopeComponent* changedEnvelope) {
Env env = changedEnvelope->getEnv();
std::vector<double> levels = env.getLevels();
std::vector<double> times = env.getTimes();
EnvCurveList curves = env.getCurves();
if (levels.size() == 4 && times.size() == 3 && curves.size() == 3) {
this->adsrEnv = env;
if (attack->getValueUnnormalised() != times[0]) {
attack->setUnnormalisedValueNotifyingHost(times[0]);
}
if (decay->getValueUnnormalised() != times[1]) {
decay->setUnnormalisedValueNotifyingHost(times[1]);
}
if (sustain->getValueUnnormalised() != levels[2]) {
sustain->setUnnormalisedValueNotifyingHost(levels[2]);
}
if (release->getValueUnnormalised() != times[2]) {
release->setUnnormalisedValueNotifyingHost(times[2]);
}
if (attackShape->getValueUnnormalised() != curves[0].getCurve()) {
attackShape->setUnnormalisedValueNotifyingHost(curves[0].getCurve());
}
if (decayShape->getValueUnnormalised() != curves[1].getCurve()) {
decayShape->setUnnormalisedValueNotifyingHost(curves[1].getCurve());
}
if (releaseShape->getValueUnnormalised() != curves[2].getCurve()) {
releaseShape->setUnnormalisedValueNotifyingHost(curves[2].getCurve());
}
DBG("adsr changed");
}
}
//==============================================================================
// This creates new instances of the plugin..

Wyświetl plik

@ -22,11 +22,12 @@
#include "audio/PerspectiveEffect.h"
#include "obj/ObjectServer.h"
#include "UGen/Env.h"
#include "UGen/ugen_JuceEnvelopeComponent.h"
//==============================================================================
/**
*/
class OscirenderAudioProcessor : public juce::AudioProcessor, juce::AudioProcessorParameter::Listener
class OscirenderAudioProcessor : public juce::AudioProcessor, juce::AudioProcessorParameter::Listener, public EnvelopeComponentListener
#if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension
#endif
@ -65,6 +66,7 @@ public:
void consumerRead(std::shared_ptr<BufferConsumer> consumer);
void parameterValueChanged(int parameterIndex, float newValue) override;
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
void envelopeChanged(EnvelopeComponent* changedEnvelope) override;
int VERSION_HINT = 1;
@ -193,7 +195,23 @@ public:
std::atomic<bool> objectServerRendering = false;
juce::ChangeBroadcaster fileChangeBroadcaster;
Env adsrEnv = Env::adsr(0.1, 0.1, 0.1, 0.1, 0.1);
FloatParameter* attack = new FloatParameter("Attack", "attack", VERSION_HINT, 0.1, 0.0, 1.0);
FloatParameter* attackLevel = new FloatParameter("Attack Level", "attackLevel", VERSION_HINT, 1.0, 0.0, 1.0);
FloatParameter* decay = new FloatParameter("Decay", "decay", VERSION_HINT, 0.1, 0.0, 1.0);
FloatParameter* sustain = new FloatParameter("Sustain", "sustain", VERSION_HINT, 0.1, 0.0, 1.0);
FloatParameter* release = new FloatParameter("Release", "release", VERSION_HINT, 0.1, 0.0, 1.0);
FloatParameter* attackShape = new FloatParameter("Attack Shape", "attackShape", VERSION_HINT, 0.0, 0.0, 1.0);
FloatParameter* decayShape = new FloatParameter("Decay Shape", "decayShape", VERSION_HINT, 0.0, 0.0, 1.0);
FloatParameter* releaseShape = new FloatParameter("Release Shape", "releaseShape", VERSION_HINT, 0.0, 0.0, 1.0);
Env adsrEnv = Env::adsr(
attack->getValueUnnormalised(),
decay->getValueUnnormalised(),
sustain->getValueUnnormalised(),
release->getValueUnnormalised(),
1.0,
std::vector<EnvCurve>{ attackShape->getValueUnnormalised(), decayShape->getValueUnnormalised(), releaseShape->getValueUnnormalised() }
);
private:
juce::SpinLock consumerLock;

Wyświetl plik

@ -52,6 +52,48 @@ Env::Env(std::vector<double> levels,
{
}
Env& Env::operator=(Env const& other) throw() {
if (this != &other) {
levels_ = other.levels_;
times_ = other.times_;
curves_ = other.curves_;
releaseNode_ = other.releaseNode_;
loopNode_ = other.loopNode_;
}
return *this;
}
bool Env::operator==(const Env& other) const throw() {
// approximate comparison
bool levelsEqual = true;
for (int i = 0; i < levels_.size(); i++) {
if (std::abs(levels_[i] - other.levels_[i]) > 0.001) {
levelsEqual = false;
break;
}
}
// approximate comparison
bool timesEqual = true;
for (int i = 0; i < times_.size(); i++) {
if (std::abs(times_[i] - other.times_[i]) > 0.001) {
timesEqual = false;
break;
}
}
// approximate comparison
bool curvesEqual = true;
for (int i = 0; i < curves_.size(); i++) {
if (std::abs(curves_[i].getCurve() - other.curves_[i].getCurve()) > 0.001) {
curvesEqual = false;
break;
}
}
return levelsEqual && timesEqual && curvesEqual;
}
Env::~Env() throw() {}
double Env::duration() const throw() {
@ -272,11 +314,11 @@ Env Env::adsr(const double attackTime,
const double sustainLevel,
const double releaseTime,
const double level,
EnvCurve const& curve) throw()
EnvCurveList const& curves) throw()
{
return Env({ 0.0, level, (level * sustainLevel), 0.0 },
{ attackTime, decayTime, releaseTime },
curve, 2);
curves, 2);
}
Env Env::asr(const double attackTime,

Wyświetl plik

@ -84,6 +84,7 @@ public:
Env(Env const& copy) throw();
Env& operator= (Env const& other) throw();
bool operator== (const Env& other) const throw();
~Env() throw();
@ -133,14 +134,14 @@ public:
@param sustainLevel The level of the sustain portion as a ratio of the peak level.
@param releaseTime The duration of the release portion.
@param level The peak level of the envelope.
@param curve The curvature of the envelope.
@param curves The curves of the envelope.
@return The Env envelope specification. */
static Env adsr(const double attackTime = 0.01,
const double decayTime = 0.3,
const double sustainLevel = 0.5,
const double releaseTime = 1.0,
const double level = 1.0,
EnvCurve const& curve = -4.0) throw();
EnvCurveList const& curves = -4.0) throw();
/** Creates a new envelope specification which is shaped like traditional analog attack-sustain-release (asr) envelopes.
@param attackTime The duration of the attack portion.

Wyświetl plik

@ -69,6 +69,10 @@ EnvCurveList& EnvCurveList::operator= (EnvCurveList const& other) throw()
return *this;
}
bool EnvCurveList::operator==(EnvCurveList const& other) const throw() {
return data == other.data;
}
EnvCurve& EnvCurveList::operator[] (const int index) throw() {
return data[index % size()];
}

Wyświetl plik

@ -121,6 +121,7 @@ public:
EnvCurveList(EnvCurveList const& copy) throw();
EnvCurveList& operator= (EnvCurveList const& other) throw();
bool operator== (EnvCurveList const& other) const throw();
/// @} <!-- end Construction and destruction ------------------------------------------- -->

Wyświetl plik

@ -107,19 +107,31 @@ EnvelopeComponent* EnvelopeHandleComponent::getParentComponent() const
void EnvelopeHandleComponent::updateTimeAndValue()
{
bool envChanged = false;
if (shouldLockTime)
{
setTopLeftPosition(getParentComponent()->convertDomainToPixels(time),
getY());
}
else time = getParentComponent()->convertPixelsToDomain(getX());
else {
envChanged = true;
time = getParentComponent()->convertPixelsToDomain(getX());
}
if (shouldLockValue)
{
setTopLeftPosition(getX(),
getParentComponent()->convertValueToPixels(value));
}
else value = getParentComponent()->convertPixelsToValue(getY());
else {
envChanged = true;
value = getParentComponent()->convertPixelsToValue(getY());
}
if (envChanged == true) {
((EnvelopeComponent*)getParentComponent())->sendChangeMessage();
}
#ifdef MYDEBUG
printf("MyEnvelopeHandleComponent::updateTimeAndValue(%f, %f)\n", time, value);
@ -303,7 +315,6 @@ void EnvelopeHandleComponent::mouseDrag(const juce::MouseEvent& e)
updateLegend();
getParentComponent()->repaint();
getParentComponent()->sendChangeMessage();
if(lastX == getX() && lastY == getY()) {
setMousePositionToThisHandle();
@ -1045,6 +1056,14 @@ EnvelopeHandleComponent* EnvelopeComponent::addHandle(double newDomain, double n
handle->setTimeAndValue(newDomain, newValue, 0.0);
handle->setCurve(curve);
handles.insert(i, handle);
if (adsrMode) {
if (i == 0) {
handle->lockTime(0);
}
if (i == 0 || i == 3) {
handle->lockValue(0);
}
}
// sendChangeMessage();
return handle;
}
@ -1165,6 +1184,14 @@ bool EnvelopeComponent::getAllowNodeEditing() const
return allowNodeEditing;
}
void EnvelopeComponent::setAdsrMode(const bool adsrMode) {
this->adsrMode = adsrMode;
}
bool EnvelopeComponent::getAdsrMode() const {
return adsrMode;
}
void EnvelopeComponent::setReleaseNode(EnvelopeHandleComponent* thisHandle)
{
setReleaseNode(handles.indexOf(thisHandle));
@ -1237,24 +1264,42 @@ Env EnvelopeComponent::getEnv() const
void EnvelopeComponent::setEnv(Env const& env)
{
clear();
double time = 0.0;
std::vector<double> levels = env.getLevels();
std::vector<double> times = env.getTimes();
const EnvCurveList& curves = env.getCurves();
EnvelopeHandleComponent* handle = addHandle(time, (double)levels[0], EnvCurve::Linear);
quantiseHandle(handle);
if (env.getLevels().size() == handles.size()) {
double time = 0.0;
for(int i = 0; i < times.size(); i++)
{
time += times[i];
handle = addHandle(time, (double)levels[i+1], curves[i]);
std::vector<double> levels = env.getLevels();
std::vector<double> times = env.getTimes();
const EnvCurveList& curves = env.getCurves();
for (int i = 0; i < handles.size(); i++) {
EnvelopeHandleComponent* handle = handles.getUnchecked(i);
handle->setTimeAndValue(time, (double)levels[i], 0.0);
handle->setCurve(curves[i]);
quantiseHandle(handle);
if (i < times.size()) {
time += times[i];
}
}
} else {
clear();
double time = 0.0;
std::vector<double> levels = env.getLevels();
std::vector<double> times = env.getTimes();
const EnvCurveList& curves = env.getCurves();
EnvelopeHandleComponent* handle = addHandle(time, (double)levels[0], EnvCurve::Linear);
quantiseHandle(handle);
for(int i = 0; i < times.size(); i++)
{
time += times[i];
handle = addHandle(time, (double)levels[i+1], curves[i]);
quantiseHandle(handle);
}
}
releaseNode = env.getReleaseNode();
loopNode = env.getLoopNode();
}

Wyświetl plik

@ -215,6 +215,9 @@ public:
bool getAllowCurveEditing() const;
void setAllowNodeEditing(const bool flag);
bool getAllowNodeEditing() const;
void setAdsrMode(const bool adsrMode);
bool getAdsrMode() const;
double convertPixelsToDomain(int pixelsX, int pixelsXMax = -1) const;
double convertPixelsToValue(int pixelsY, int pixelsYMax = -1) const;
@ -258,6 +261,7 @@ private:
bool allowCurveEditing = false;
bool allowNodeEditing = false;
bool adsrMode = false;
juce::Colour colours[NumEnvColours];
};
@ -345,6 +349,7 @@ public:
bool getAllowCurveEditing() const { return envelope->getAllowCurveEditing(); }
void setAllowNodeEditing(const bool flag) { envelope->setAllowNodeEditing(flag); }
bool getAllowNodeEditing() const { return envelope->getAllowNodeEditing(); }
void setAdsrMode(const bool adsrMode) { envelope->setAdsrMode(adsrMode); }
private: