full adsr
This commit is contained in:
@@ -36,9 +36,9 @@ struct ParamDef {
|
|||||||
constexpr std::array<ParamDef, static_cast<size_t>(ParamId::Count)> PARAM_DEFS {{
|
constexpr std::array<ParamDef, static_cast<size_t>(ParamId::Count)> PARAM_DEFS {{
|
||||||
{ 100.0f, 20.0f, 600.0f}, // Osc1Freq
|
{ 100.0f, 20.0f, 600.0f}, // Osc1Freq
|
||||||
{ 0.8f, 0.0f, 1.0f}, // Osc1Gain
|
{ 0.8f, 0.0f, 1.0f}, // Osc1Gain
|
||||||
{ 10.0f, 0.0f, 1000.0f}, // Osc1VolumeEnvA,
|
{ 0.05f, 0.0f, 2.0f}, // Osc1VolumeEnvA,
|
||||||
{ 10.0f, 0.0f, 1000.0f}, // Osc1VolumeEnvD,
|
{ 0.2f, 0.0f, 2.0f}, // Osc1VolumeEnvD,
|
||||||
{ 10.0f, 0.0f, 1000.0f}, // Osc1VolumeEnvS,
|
{ 0.7f, 0.0f, 1.0f}, // Osc1VolumeEnvS,
|
||||||
{ 0.2f, 0.0f, 2.0f}, // Osc1VolumeEnvR,
|
{ 0.2f, 0.0f, 2.0f}, // Osc1VolumeEnvR,
|
||||||
{ 10.0f, 0.0f, 1000.0f}, // FilterCutoffEnvA,
|
{ 10.0f, 0.0f, 1000.0f}, // FilterCutoffEnvA,
|
||||||
{ 10.0f, 0.0f, 1000.0f}, // FilterCutoffEnvD,
|
{ 10.0f, 0.0f, 1000.0f}, // FilterCutoffEnvD,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public:
|
|||||||
|
|
||||||
// setters
|
// setters
|
||||||
void setSampleRate(float sampleRate) { sampleRate_ = sampleRate; }
|
void setSampleRate(float sampleRate) { sampleRate_ = sampleRate; }
|
||||||
void set(float a, float d, float s, float r) { setAttack(a); setDecay(a); setSustain(a); setRelease(a); }
|
void set(float a, float d, float s, float r) { setAttack(a); setDecay(d); setSustain(s); setRelease(r); }
|
||||||
void setAttack(float seconds) { attack_ = std::max(seconds, 0.0001f); }
|
void setAttack(float seconds) { attack_ = std::max(seconds, 0.0001f); }
|
||||||
void setDecay(float seconds) { decay_ = std::max(seconds, 0.0001f); }
|
void setDecay(float seconds) { decay_ = std::max(seconds, 0.0001f); }
|
||||||
void setSustain(float level) { sustain_ = level; }
|
void setSustain(float level) { sustain_ = level; }
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ inline float Synth::noteToFrequency(uint8_t note) {
|
|||||||
return SYNTH_PITCH_STANDARD * pow(2.0f, static_cast<float>(note - SYNTH_MIDI_HOME) / static_cast<float>(SYNTH_NOTES_PER_OCTAVE));
|
return SYNTH_PITCH_STANDARD * pow(2.0f, static_cast<float>(note - SYNTH_MIDI_HOME) / static_cast<float>(SYNTH_NOTES_PER_OCTAVE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: stop popping on note-offs
|
|
||||||
void Synth::handleNoteEvent(const NoteEvent& event) {
|
void Synth::handleNoteEvent(const NoteEvent& event) {
|
||||||
if(event.type == NoteEventType::NoteOn) {
|
if(event.type == NoteEventType::NoteOn) {
|
||||||
// add note to activeNotes list
|
// add note to activeNotes list
|
||||||
@@ -73,8 +72,7 @@ void Synth::process(float* out, uint32_t nFrames, uint32_t sampleRate) {
|
|||||||
for(auto& p : params_) p.update(); // TODO: profile this
|
for(auto& p : params_) p.update(); // TODO: profile this
|
||||||
|
|
||||||
// process all envelopes
|
// process all envelopes
|
||||||
//gainEnvelope_.set(0.05f, 0.2f, 0.7f, getParam(ParamId::Osc1VolumeEnvR));
|
gainEnvelope_.set(getParam(ParamId::Osc1VolumeEnvA), getParam(ParamId::Osc1VolumeEnvD), getParam(ParamId::Osc1VolumeEnvS), getParam(ParamId::Osc1VolumeEnvR));
|
||||||
gainEnvelope_.setRelease(getParam(ParamId::Osc1VolumeEnvR));
|
|
||||||
float gain = gainEnvelope_.process();
|
float gain = gainEnvelope_.process();
|
||||||
|
|
||||||
// skip if no active notes
|
// skip if no active notes
|
||||||
@@ -90,8 +88,7 @@ void Synth::process(float* out, uint32_t nFrames, uint32_t sampleRate) {
|
|||||||
// TODO: wavetables
|
// TODO: wavetables
|
||||||
// TODO: wavetables should be scaled by their RMS for equal loudness (prelim standard = 0.707)
|
// TODO: wavetables should be scaled by their RMS for equal loudness (prelim standard = 0.707)
|
||||||
float sineSample = std::sin(phase_);
|
float sineSample = std::sin(phase_);
|
||||||
float squareSample = -0.707f;
|
float squareSample = (phase_ >= M_PI) ? 0.707f : -0.707f;
|
||||||
if(phase_ >= M_PI) squareSample = 0.707f;
|
|
||||||
float sawSample = phase_ * 4.0f / M_PI * frequency_ / static_cast<float>(sampleRate);
|
float sawSample = phase_ * 4.0f / M_PI * frequency_ / static_cast<float>(sampleRate);
|
||||||
sampleOut = sawSample * gain;
|
sampleOut = sawSample * gain;
|
||||||
|
|
||||||
|
|||||||
@@ -15,20 +15,33 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
setFocusPolicy(Qt::StrongFocus);
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
|
|
||||||
// Initialize UI
|
// Initialize UI
|
||||||
updateCounterLabel();
|
|
||||||
|
|
||||||
// Connect buttons to slots
|
// Connect buttons to slots
|
||||||
connect(ui_->buttonIncrement, &QPushButton::clicked, this, &MainWindow::onIncrementClicked);
|
|
||||||
connect(ui_->buttonReset, &QPushButton::clicked, this, &MainWindow::onResetClicked);
|
connect(ui_->buttonReset, &QPushButton::clicked, this, &MainWindow::onResetClicked);
|
||||||
|
|
||||||
// synth business
|
// synth business
|
||||||
audio_->start();
|
audio_->start();
|
||||||
|
|
||||||
// slider business
|
// 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) {
|
connect(ui_->sliderGainR, &SmartSlider::valueChanged, this, [this](float v) {
|
||||||
audio_->parameters()->set(ParamId::Osc1VolumeEnvR, v); // bind valueChanged signal to the ParameterStore
|
audio_->parameters()->set(ParamId::Osc1VolumeEnvR, v); // bind valueChanged signal to the ParameterStore
|
||||||
});
|
});
|
||||||
// initialize to defaults
|
// 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->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_->sliderGainR->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].def);
|
||||||
// TODO: package a smartSlider into an envelope controller widget
|
// TODO: package a smartSlider into an envelope controller widget
|
||||||
@@ -47,16 +60,15 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) {
|
|||||||
keyboard_.handleKeyRelease(event);
|
keyboard_.handleKeyRelease(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onIncrementClicked() {
|
|
||||||
counter_++;
|
|
||||||
updateCounterLabel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onResetClicked() {
|
void MainWindow::onResetClicked() {
|
||||||
counter_ = 0;
|
|
||||||
updateCounterLabel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::updateCounterLabel() {
|
// initialize to defaults
|
||||||
ui_->labelCounter->setText(QString::number(counter_));
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,16 +23,10 @@ protected:
|
|||||||
void keyReleaseEvent(QKeyEvent* event) override;
|
void keyReleaseEvent(QKeyEvent* event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onIncrementClicked();
|
|
||||||
void onResetClicked();
|
void onResetClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui_;
|
Ui::MainWindow *ui_;
|
||||||
int counter_ = 0;
|
|
||||||
|
|
||||||
int value = 0;
|
|
||||||
|
|
||||||
void updateCounterLabel();
|
|
||||||
|
|
||||||
AudioEngine* audio_ = nullptr;
|
AudioEngine* audio_ = nullptr;
|
||||||
KeyboardController keyboard_;
|
KeyboardController keyboard_;
|
||||||
|
|||||||
@@ -13,30 +13,15 @@
|
|||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>MainWindow</string>
|
<string>MainWindow</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<widget class="QPushButton" name="buttonIncrement">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>70</y>
|
|
||||||
<width>101</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>15</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Increment</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="buttonReset">
|
<widget class="QPushButton" name="buttonReset">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>50</x>
|
<x>10</x>
|
||||||
<y>100</y>
|
<y>10</y>
|
||||||
<width>101</width>
|
<width>101</width>
|
||||||
<height>31</height>
|
<height>31</height>
|
||||||
</rect>
|
</rect>
|
||||||
@@ -50,36 +35,57 @@
|
|||||||
<string>Reset</string>
|
<string>Reset</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QLabel" name="labelCounter">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>151</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>30</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Counter</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="SmartSlider" name="sliderGainR" native="true">
|
<widget class="SmartSlider" name="sliderGainR" native="true">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>340</x>
|
<x>580</x>
|
||||||
<y>120</y>
|
<y>180</y>
|
||||||
<width>171</width>
|
<width>171</width>
|
||||||
<height>321</height>
|
<height>321</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderGainA" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>40</x>
|
||||||
|
<y>180</y>
|
||||||
|
<width>171</width>
|
||||||
|
<height>321</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderGainD" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>220</x>
|
||||||
|
<y>180</y>
|
||||||
|
<width>171</width>
|
||||||
|
<height>321</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderGainS" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>400</x>
|
||||||
|
<y>180</y>
|
||||||
|
<width>171</width>
|
||||||
|
<height>321</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
Reference in New Issue
Block a user