Add cutoff LFO to the filter demo
This commit is contained in:
parent
8d44b85466
commit
87347f690f
2 changed files with 163 additions and 4 deletions
|
|
@ -13,6 +13,7 @@
|
|||
#include <QDebug>
|
||||
#include <jack/jack.h>
|
||||
#include <memory>
|
||||
#include <cmath>
|
||||
|
||||
///
|
||||
struct jack_delete {
|
||||
|
|
@ -38,6 +39,8 @@ private:
|
|||
void valueChangedPkShGain(int value);
|
||||
void valueChangedBandwidth(int value);
|
||||
void valueChangedFilterMode(int value);
|
||||
void valueChangedCutoffModSpeed(int value);
|
||||
void valueChangedCutoffModRange(int value);
|
||||
|
||||
private:
|
||||
QMainWindow *fWindow = nullptr;
|
||||
|
|
@ -55,11 +58,21 @@ private:
|
|||
static constexpr int bwMin = 1.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 fCutoff = 500.0;
|
||||
int fReso = 0.0;
|
||||
int fPksh = 20.0;
|
||||
int fBw = 1.0;
|
||||
float fCutoffRate = 1.0;
|
||||
int fCutoffMod = 24.0;
|
||||
|
||||
float fCutoffLfoPhase = 0.0f;
|
||||
|
||||
sfz::Filter fFilter;
|
||||
sfz::FilterEq fFilterEq;
|
||||
|
|
@ -72,6 +85,11 @@ private:
|
|||
|
||||
jack_client_u fClient;
|
||||
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)
|
||||
|
|
@ -92,6 +110,7 @@ bool DemoApp::initSound()
|
|||
fClient.reset(client);
|
||||
|
||||
double sampleRate = jack_get_sample_rate(client);
|
||||
unsigned bufferSize = jack_get_buffer_size(client);
|
||||
|
||||
fFilter.init(sampleRate);
|
||||
fFilter.setChannels(2);
|
||||
|
|
@ -99,6 +118,11 @@ bool DemoApp::initSound()
|
|||
fFilterEq.init(sampleRate);
|
||||
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[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);
|
||||
|
|
@ -167,6 +191,8 @@ void DemoApp::initWindow()
|
|||
fUi.spinResonance->setRange(resoMin, resoMax);
|
||||
fUi.spinPkShGain->setRange(pkshMin, pkshMax);
|
||||
fUi.spinBandwidth->setRange(bwMin, bwMax);
|
||||
fUi.valCutoffModSpeed->setRange(lfoRateMin * 1e3, lfoRateMax * 1e3);
|
||||
fUi.valCutoffModRange->setRange(cutoffModMin, cutoffModMax);
|
||||
|
||||
fUi.dialCutoff->setValue(fCutoff);
|
||||
fUi.dialResonance->setValue(fReso);
|
||||
|
|
@ -176,6 +202,10 @@ void DemoApp::initWindow()
|
|||
fUi.spinResonance->setValue(fReso);
|
||||
fUi.spinPkShGain->setValue(fPksh);
|
||||
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(
|
||||
fUi.dialCutoff, &QDial::valueChanged,
|
||||
|
|
@ -201,6 +231,12 @@ void DemoApp::initWindow()
|
|||
connect(
|
||||
fUi.spinBandwidth, QOverload<int>::of(&QSpinBox::valueChanged),
|
||||
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);
|
||||
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[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) {
|
||||
default:
|
||||
case kFilterModeMulti:
|
||||
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;
|
||||
case kFilterModeEq:
|
||||
self->fFilterEq.process(ins, outs, self->fCutoff, self->fBw, self->fPksh, nframes);
|
||||
self->fFilterEq.processModulated(ins, outs, tempCutoff, tempBw, tempPksh, nframes);
|
||||
break;
|
||||
}
|
||||
|
||||
self->fCutoffLfoPhase = cutoffLfoPhase;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -312,6 +380,20 @@ void DemoApp::valueChangedFilterMode(int 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[])
|
||||
{
|
||||
DemoApp app(argc, argv);
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>415</width>
|
||||
<height>216</height>
|
||||
<height>304</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QDial" name="dialCutoff"/>
|
||||
</item>
|
||||
|
|
@ -231,6 +231,83 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</widget>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue