package adsr sliders into an envelopeGenerator widget

This commit is contained in:
2025-12-26 13:38:13 -06:00
parent 9874d5983b
commit c417801547
9 changed files with 362 additions and 84 deletions

View File

@@ -2,7 +2,7 @@
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "SmartSlider.h"
#include "SmartSlider/SmartSlider.h"
#include "../ParameterStore.h"
MainWindow::MainWindow(QWidget *parent) :
@@ -11,41 +11,38 @@ MainWindow::MainWindow(QWidget *parent) :
audio_(new AudioEngine()),
keyboard_(audio_->noteQueue()) {
// Initialize UI
ui_->setupUi(this);
setFocusPolicy(Qt::StrongFocus);
// Initialize UI
// Connect buttons to slots
connect(ui_->buttonReset, &QPushButton::clicked, this, &MainWindow::onResetClicked);
// synth business
audio_->start();
// slider business
connect(ui_->sliderGainA, &SmartSlider::valueChanged, this, [this](float v) {
audio_->parameters()->set(ParamId::Osc1VolumeEnvA, v); // bind valueChanged signal to the ParameterStore
});
connect(ui_->sliderGainD, &SmartSlider::valueChanged, this, [this](float v) {
audio_->parameters()->set(ParamId::Osc1VolumeEnvD, v); // bind valueChanged signal to the ParameterStore
});
connect(ui_->sliderGainS, &SmartSlider::valueChanged, this, [this](float v) {
audio_->parameters()->set(ParamId::Osc1VolumeEnvS, v); // bind valueChanged signal to the ParameterStore
});
connect(ui_->sliderGainR, &SmartSlider::valueChanged, this, [this](float v) {
audio_->parameters()->set(ParamId::Osc1VolumeEnvR, v); // bind valueChanged signal to the ParameterStore
});
connect(ui_->envelopeOsc1Volume, &EnvelopeGenerator::envelopeChanged,
this, [this](float a, float d, float s, float r) {
audio_->parameters()->set(ParamId::Osc1VolumeEnvA, a);
audio_->parameters()->set(ParamId::Osc1VolumeEnvD, d);
audio_->parameters()->set(ParamId::Osc1VolumeEnvS, s);
audio_->parameters()->set(ParamId::Osc1VolumeEnvR, r);
// TODO: parameters()->setEnv(ENV_ID, a, d, s ,r)
});
// initialize to defaults
ui_->sliderGainA->setRange(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].max);
ui_->sliderGainA->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].def);
ui_->sliderGainD->setRange(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].max);
ui_->sliderGainD->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].def);
ui_->sliderGainS->setRange(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].max);
ui_->sliderGainS->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].def);
ui_->sliderGainR->setRange(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].max);
ui_->sliderGainR->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].def);
// TODO: package a smartSlider into an envelope controller widget
// then intialization and parameter bindings can be done from the envelope controller
ui_->envelopeOsc1Volume->setRanges(
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].max
);
ui_->envelopeOsc1Volume->setAttack(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].def);
ui_->envelopeOsc1Volume->setDecay(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].def);
ui_->envelopeOsc1Volume->setSustain(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].def);
ui_->envelopeOsc1Volume->setRelease(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].def);
// TODO: assign envelope widget a ParamID so that intialization and parameter bindings can be done from the envelope controller
}
MainWindow::~MainWindow() {
@@ -63,12 +60,14 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) {
void MainWindow::onResetClicked() {
// initialize to defaults
ui_->sliderGainA->setRange(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].max);
ui_->sliderGainA->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].def);
ui_->sliderGainD->setRange(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].max);
ui_->sliderGainD->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].def);
ui_->sliderGainS->setRange(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].max);
ui_->sliderGainS->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].def);
ui_->sliderGainR->setRange(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].max);
ui_->sliderGainR->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].def);
ui_->envelopeOsc1Volume->setRanges(
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].max
);
ui_->envelopeOsc1Volume->setAttack(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].def);
ui_->envelopeOsc1Volume->setDecay(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].def);
ui_->envelopeOsc1Volume->setSustain(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].def);
ui_->envelopeOsc1Volume->setRelease(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].def);
}

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>739</width>
<height>605</height>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
@@ -35,53 +35,17 @@
<string>Reset</string>
</property>
</widget>
<widget class="SmartSlider" name="sliderGainR" native="true">
<widget class="EnvelopeGenerator" name="envelopeOsc1Volume" native="true">
<property name="geometry">
<rect>
<x>480</x>
<y>180</y>
<width>120</width>
<height>300</height>
<x>150</x>
<y>130</y>
<width>500</width>
<height>360</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
<widget class="SmartSlider" name="sliderGainA" native="true">
<property name="geometry">
<rect>
<x>120</x>
<y>180</y>
<width>120</width>
<height>300</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
<widget class="SmartSlider" name="sliderGainD" native="true">
<property name="geometry">
<rect>
<x>240</x>
<y>180</y>
<width>120</width>
<height>300</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
<widget class="SmartSlider" name="sliderGainS" native="true">
<property name="geometry">
<rect>
<x>360</x>
<y>180</y>
<width>120</width>
<height>300</height>
</rect>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="autoFillBackground">
<bool>true</bool>
@@ -91,9 +55,9 @@
</widget>
<customwidgets>
<customwidget>
<class>SmartSlider</class>
<class>EnvelopeGenerator</class>
<extends>QWidget</extends>
<header>SmartSlider.h</header>
<header>EnvelopeGenerator/EnvelopeGenerator.h</header>
<container>1</container>
</customwidget>
</customwidgets>

View File

@@ -0,0 +1,73 @@
#include "EnvelopeGenerator.h"
#include "ui_EnvelopeGenerator.h"
#include <iostream>
EnvelopeGenerator::EnvelopeGenerator(QWidget* parent) : QWidget(parent), ui_(new Ui::EnvelopeGenerator) {
ui_->setupUi(this);
auto connectSlider = [this](SmartSlider* s) {
connect(s, &SmartSlider::valueChanged,
this, &EnvelopeGenerator::emitEnvelope);
};
connectSlider(ui_->sliderAttack);
connectSlider(ui_->sliderDecay);
connectSlider(ui_->sliderSustain);
connectSlider(ui_->sliderRelease);
}
EnvelopeGenerator::~EnvelopeGenerator() {
delete ui_;
}
// getters are here to separate ui from header
float EnvelopeGenerator::attack() const {
return ui_->sliderAttack->value();
}
float EnvelopeGenerator::decay() const {
return ui_->sliderDecay->value();
}
float EnvelopeGenerator::sustain() const {
return ui_->sliderSustain->value();
}
float EnvelopeGenerator::release() const {
return ui_->sliderRelease->value();
}
void EnvelopeGenerator::setAttack(float v) {
ui_->sliderAttack->setValue(v);
}
void EnvelopeGenerator::setDecay(float v) {
ui_->sliderDecay->setValue(v);
}
void EnvelopeGenerator::setSustain(float v) {
ui_->sliderSustain->setValue(v);
}
void EnvelopeGenerator::setRelease(float v) {
ui_->sliderRelease->setValue(v);
}
void EnvelopeGenerator::setRanges(float minA, float maxA, float minD, float maxD, float minS, float maxS, float minR, float maxR) {
ui_->sliderAttack->setRange(minA, maxA);
ui_->sliderDecay->setRange(minD, maxD);
ui_->sliderSustain->setRange(minS, maxS);
ui_->sliderRelease->setRange(minR, maxR);
}
void EnvelopeGenerator::emitEnvelope() {
emit envelopeChanged(
attack(),
decay(),
sustain(),
release()
);
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class EnvelopeGenerator; }
QT_END_NAMESPACE
// this is for the widget, not the synth object named Envelope
class EnvelopeGenerator : public QWidget {
Q_OBJECT
public:
explicit EnvelopeGenerator(QWidget* parent = nullptr);
~EnvelopeGenerator();
void setAttack(float v);
void setDecay(float v);
void setSustain(float v);
void setRelease(float v);
void setRanges(float minA, float maxA, float minD, float maxD, float minS, float maxS, float minR, float maxR);
float attack() const;
float decay() const;
float sustain() const;
float release() const;
signals:
void envelopeChanged(double a, double d, double s, double r);
private:
Ui::EnvelopeGenerator* ui_;
void emitEnvelope();
};

View File

@@ -0,0 +1,195 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EnvelopeGenerator</class>
<widget class="QWidget" name="EnvelopeGenerator">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>360</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="SmartSlider" name="sliderDecay" native="true">
<property name="geometry">
<rect>
<x>130</x>
<y>50</y>
<width>120</width>
<height>300</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
<widget class="SmartSlider" name="sliderRelease" native="true">
<property name="geometry">
<rect>
<x>370</x>
<y>50</y>
<width>120</width>
<height>300</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
<widget class="SmartSlider" name="sliderSustain" native="true">
<property name="geometry">
<rect>
<x>250</x>
<y>50</y>
<width>120</width>
<height>300</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
<widget class="SmartSlider" name="sliderAttack" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>120</width>
<height>300</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
<widget class="Line" name="line">
<property name="geometry">
<rect>
<x>0</x>
<y>340</y>
<width>491</width>
<height>20</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
<widget class="Line" name="line_2">
<property name="geometry">
<rect>
<x>0</x>
<y>30</y>
<width>491</width>
<height>20</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>50</x>
<y>0</y>
<width>21</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string>A</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>170</x>
<y>0</y>
<width>30</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string>D</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>300</x>
<y>0</y>
<width>20</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string>S</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>420</x>
<y>0</y>
<width>20</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string>R</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>SmartSlider</class>
<extends>QWidget</extends>
<header>SmartSlider/SmartSlider.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -57,6 +57,10 @@ void SmartSlider::setValue(float value) {
emit valueChanged(value);
}
float SmartSlider::value() {
return ui_->spinValue->value();
}
void SmartSlider::onSliderChanged(int32_t v) {
float min = ui_->spinMin->value();
float max = ui_->spinMax->value();

View File

@@ -19,6 +19,9 @@ public:
void setRange(float min, float max);
void setValue(float value);
// getters
float value();
signals:
void valueChanged(float value);