Add cutoff LFO to the filter demo

This commit is contained in:
Jean Pierre Cimalando 2020-02-17 14:33:51 +01:00 committed by Paul Ferrand
parent 8d44b85466
commit 87347f690f
2 changed files with 163 additions and 4 deletions

View file

@ -13,6 +13,7 @@
#include <QDebug> #include <QDebug>
#include <jack/jack.h> #include <jack/jack.h>
#include <memory> #include <memory>
#include <cmath>
/// ///
struct jack_delete { struct jack_delete {
@ -38,6 +39,8 @@ private:
void valueChangedPkShGain(int value); void valueChangedPkShGain(int value);
void valueChangedBandwidth(int value); void valueChangedBandwidth(int value);
void valueChangedFilterMode(int value); void valueChangedFilterMode(int value);
void valueChangedCutoffModSpeed(int value);
void valueChangedCutoffModRange(int value);
private: private:
QMainWindow *fWindow = nullptr; QMainWindow *fWindow = nullptr;
@ -55,11 +58,21 @@ private:
static constexpr int bwMin = 1.0; static constexpr int bwMin = 1.0;
static constexpr int bwMax = 10.0; static constexpr int bwMax = 10.0;
static constexpr float lfoRateMin = 0.1;
static constexpr float lfoRateMax = 10.0;
static constexpr int cutoffModMin = 1;
static constexpr int cutoffModMax = 48;
int fType = sfz::kFilterNone; int fType = sfz::kFilterNone;
int fCutoff = 500.0; int fCutoff = 500.0;
int fReso = 0.0; int fReso = 0.0;
int fPksh = 20.0; int fPksh = 20.0;
int fBw = 1.0; int fBw = 1.0;
float fCutoffRate = 1.0;
int fCutoffMod = 24.0;
float fCutoffLfoPhase = 0.0f;
sfz::Filter fFilter; sfz::Filter fFilter;
sfz::FilterEq fFilterEq; sfz::FilterEq fFilterEq;
@ -72,6 +85,11 @@ private:
jack_client_u fClient; jack_client_u fClient;
jack_port_t *fPorts[4] = {}; jack_port_t *fPorts[4] = {};
std::unique_ptr<float[]> fTempCutoff;
std::unique_ptr<float[]> fTempReso;
std::unique_ptr<float[]> fTempBw;
std::unique_ptr<float[]> fTempPksh;
}; };
DemoApp::DemoApp(int &argc, char **argv) DemoApp::DemoApp(int &argc, char **argv)
@ -92,6 +110,7 @@ bool DemoApp::initSound()
fClient.reset(client); fClient.reset(client);
double sampleRate = jack_get_sample_rate(client); double sampleRate = jack_get_sample_rate(client);
unsigned bufferSize = jack_get_buffer_size(client);
fFilter.init(sampleRate); fFilter.init(sampleRate);
fFilter.setChannels(2); fFilter.setChannels(2);
@ -99,6 +118,11 @@ bool DemoApp::initSound()
fFilterEq.init(sampleRate); fFilterEq.init(sampleRate);
fFilterEq.setChannels(2); fFilterEq.setChannels(2);
fTempCutoff.reset(new float[bufferSize]);
fTempReso.reset(new float[bufferSize]);
fTempBw.reset(new float[bufferSize]);
fTempPksh.reset(new float[bufferSize]);
fPorts[0] = jack_port_register(client, "in_left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); fPorts[0] = jack_port_register(client, "in_left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
fPorts[1] = jack_port_register(client, "in_right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); fPorts[1] = jack_port_register(client, "in_right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
fPorts[2] = jack_port_register(client, "out_left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); fPorts[2] = jack_port_register(client, "out_left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
@ -167,6 +191,8 @@ void DemoApp::initWindow()
fUi.spinResonance->setRange(resoMin, resoMax); fUi.spinResonance->setRange(resoMin, resoMax);
fUi.spinPkShGain->setRange(pkshMin, pkshMax); fUi.spinPkShGain->setRange(pkshMin, pkshMax);
fUi.spinBandwidth->setRange(bwMin, bwMax); fUi.spinBandwidth->setRange(bwMin, bwMax);
fUi.valCutoffModSpeed->setRange(lfoRateMin * 1e3, lfoRateMax * 1e3);
fUi.valCutoffModRange->setRange(cutoffModMin, cutoffModMax);
fUi.dialCutoff->setValue(fCutoff); fUi.dialCutoff->setValue(fCutoff);
fUi.dialResonance->setValue(fReso); fUi.dialResonance->setValue(fReso);
@ -176,6 +202,10 @@ void DemoApp::initWindow()
fUi.spinResonance->setValue(fReso); fUi.spinResonance->setValue(fReso);
fUi.spinPkShGain->setValue(fPksh); fUi.spinPkShGain->setValue(fPksh);
fUi.spinBandwidth->setValue(fBw); fUi.spinBandwidth->setValue(fBw);
fUi.valCutoffModSpeed->setValue(fCutoffRate * 1e3);
fUi.valCutoffModRange->setValue(fCutoffMod);
fUi.lblCutoffModSpeed->setText(QString::number(fCutoffRate));
fUi.lblCutoffModRange->setText(QString::number(fCutoffMod));
connect( connect(
fUi.dialCutoff, &QDial::valueChanged, fUi.dialCutoff, &QDial::valueChanged,
@ -201,6 +231,12 @@ void DemoApp::initWindow()
connect( connect(
fUi.spinBandwidth, QOverload<int>::of(&QSpinBox::valueChanged), fUi.spinBandwidth, QOverload<int>::of(&QSpinBox::valueChanged),
this, [this](int value) { valueChangedBandwidth(value); }); this, [this](int value) { valueChangedBandwidth(value); });
connect(
fUi.valCutoffModSpeed, &QSlider::valueChanged,
this, [this](int value) { valueChangedCutoffModSpeed(value); });
connect(
fUi.valCutoffModRange, &QSlider::valueChanged,
this, [this](int value) { valueChangedCutoffModRange(value); });
QButtonGroup *grpMode = new QButtonGroup(this); QButtonGroup *grpMode = new QButtonGroup(this);
grpMode->addButton(fUi.btnMultiMode, kFilterModeMulti); grpMode->addButton(fUi.btnMultiMode, kFilterModeMulti);
@ -233,17 +269,49 @@ int DemoApp::processAudio(jack_nframes_t nframes, void *cbdata)
outs[0] = reinterpret_cast<float *>(jack_port_get_buffer(self->fPorts[2], nframes)); outs[0] = reinterpret_cast<float *>(jack_port_get_buffer(self->fPorts[2], nframes));
outs[1] = reinterpret_cast<float *>(jack_port_get_buffer(self->fPorts[3], nframes)); outs[1] = reinterpret_cast<float *>(jack_port_get_buffer(self->fPorts[3], nframes));
float *tempCutoff = self->fTempCutoff.get();
float *tempReso = self->fTempReso.get();
float *tempBw = self->fTempBw.get();
float *tempPksh = self->fTempPksh.get();
std::fill(tempCutoff, tempCutoff + nframes, self->fCutoff);
std::fill(tempReso, tempReso + nframes, self->fReso);
std::fill(tempBw, tempBw + nframes, self->fBw);
std::fill(tempPksh, tempPksh + nframes, self->fPksh);
float cutoffLfoPhase = self->fCutoffLfoPhase;
float cutoffRate = self->fCutoffRate;
float cutoffMod = self->fCutoffMod;
float sampleTime = 1.0 / jack_get_sample_rate(self->fClient.get());
auto triangleLfo = [](float phase) -> float {
float y = -4 * phase + 2;
y = (phase < 0.25f) ? (4 * phase) : y;
y = (phase > 0.75f) ? (4 * phase - 4) : y;
return y;
};
for (jack_nframes_t i = 0; i < nframes; ++i) {
float lfo = cutoffMod * triangleLfo(cutoffLfoPhase);
tempCutoff[i] *= std::exp2(lfo * (1.0f / 12.0f));
cutoffLfoPhase += cutoffRate * sampleTime;
cutoffLfoPhase -= (int)cutoffLfoPhase;
}
switch (self->fFilterMode) { switch (self->fFilterMode) {
default: default:
case kFilterModeMulti: case kFilterModeMulti:
self->fFilter.setType(static_cast<sfz::FilterType>(self->fType)); self->fFilter.setType(static_cast<sfz::FilterType>(self->fType));
self->fFilter.process(ins, outs, self->fCutoff, self->fReso, self->fPksh, nframes); self->fFilter.processModulated(ins, outs, tempCutoff, tempReso, tempPksh, nframes);
break; break;
case kFilterModeEq: case kFilterModeEq:
self->fFilterEq.process(ins, outs, self->fCutoff, self->fBw, self->fPksh, nframes); self->fFilterEq.processModulated(ins, outs, tempCutoff, tempBw, tempPksh, nframes);
break; break;
} }
self->fCutoffLfoPhase = cutoffLfoPhase;
return 0; return 0;
} }
@ -312,6 +380,20 @@ void DemoApp::valueChangedFilterMode(int value)
fFilterMode = static_cast<FilterMode>(value); fFilterMode = static_cast<FilterMode>(value);
} }
void DemoApp::valueChangedCutoffModSpeed(int value)
{
fUi.lblCutoffModSpeed->setText(QString::number(value * 1e-3));
fCutoffRate = value * 1e-3;
}
void DemoApp::valueChangedCutoffModRange(int value)
{
fUi.lblCutoffModRange->setText(QString::number(value));
fCutoffMod = value;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
DemoApp app(argc, argv); DemoApp app(argc, argv);

View file

@ -7,11 +7,11 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>415</width> <width>415</width>
<height>216</height> <height>304</height>
</rect> </rect>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QDial" name="dialCutoff"/> <widget class="QDial" name="dialCutoff"/>
</item> </item>
@ -231,6 +231,83 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="4" column="0" colspan="3">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Cutoff modulation</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Speed</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="valCutoffModSpeed">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="lblCutoffModSpeed">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Hz</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Range</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="valCutoffModRange">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="lblCutoffModRange">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_9">
<property name="text">
<string>semitones</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>