sfizz/tests/DemoFilters.cpp

326 lines
10 KiB
C++
Raw Normal View History

2020-02-07 12:44:12 +01:00
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "sfizz/SfzFilter.h"
#include "ui_DemoFilters.h"
#include <QApplication>
#include <QMainWindow>
#include <QMessageBox>
#include <QButtonGroup>
#include <QDebug>
#include <jack/jack.h>
#include <memory>
///
struct jack_delete {
void operator()(jack_client_t *x) const noexcept { jack_client_close(x); }
};
typedef std::unique_ptr<jack_client_t, jack_delete> jack_client_u;
///
class DemoApp : public QApplication {
public:
DemoApp(int &argc, char **argv);
bool initSound();
void initWindow();
private:
static int processAudio(jack_nframes_t nframes, void *cbdata);
private:
void valueChangedType(int value);
void valueChangedCutoff(int value);
void valueChangedResonance(int value);
void valueChangedPkShGain(int value);
void valueChangedBandwidth(int value);
void valueChangedFilterMode(int value);
private:
QMainWindow *fWindow = nullptr;
Ui::DemoFiltersWindow fUi;
static constexpr int cutoffMin = 10.0;
static constexpr int cutoffMax = 20000.0;
static constexpr int resoMin = 0.0;
static constexpr int resoMax = 40.0;
static constexpr int pkshMin = -40.0;
static constexpr int pkshMax = 40.0;
static constexpr int bwMin = 1.0;
static constexpr int bwMax = 10.0;
int fType = sfz::kFilterNone;
int fCutoff = 500.0;
int fReso = 0.0;
int fPksh = 20.0;
int fBw = 1.0;
sfz::Filter fFilter;
sfz::FilterEq fFilterEq;
enum FilterMode {
kFilterModeMulti,
kFilterModeEq,
};
FilterMode fFilterMode = kFilterModeMulti;
jack_client_u fClient;
jack_port_t *fPorts[4] = {};
};
DemoApp::DemoApp(int &argc, char **argv)
: QApplication(argc, argv)
{
setApplicationName(tr("Sfizz Filters"));
}
bool DemoApp::initSound()
{
jack_client_t *client = jack_client_open(
applicationName().toUtf8().data(), JackNoStartServer, nullptr);
if (!client) {
QMessageBox::critical(nullptr, tr("Error"), tr("Cannot open JACK audio."));
return false;
}
fClient.reset(client);
double sampleRate = jack_get_sample_rate(client);
fFilter.init(sampleRate);
fFilter.setChannels(2);
fFilterEq.init(sampleRate);
fFilterEq.setChannels(2);
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);
fPorts[3] = jack_port_register(client, "out_right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if (!(fPorts[0] && fPorts[1] && fPorts[2] && fPorts[3])) {
QMessageBox::critical(nullptr, tr("Error"), tr("Cannot register JACK ports."));
return false;
}
jack_set_process_callback(client, &processAudio, this);
if (jack_activate(client) != 0) {
QMessageBox::critical(nullptr, tr("Error"), tr("Cannot activate JACK client."));
return false;
}
return true;
}
void DemoApp::initWindow()
{
QMainWindow *window = new QMainWindow;
fWindow = window;
fUi.setupUi(window);
window->setWindowTitle(applicationDisplayName());
QComboBox *cbTypes = fUi.comboBox;
cbTypes->addItem("None", static_cast<int>(sfz::kFilterNone));
cbTypes->addItem("Apf1p", static_cast<int>(sfz::kFilterApf1p));
cbTypes->addItem("Bpf1p", static_cast<int>(sfz::kFilterBpf1p));
cbTypes->addItem("Bpf2p", static_cast<int>(sfz::kFilterBpf2p));
cbTypes->addItem("Bpf4p", static_cast<int>(sfz::kFilterBpf4p));
cbTypes->addItem("Bpf6p", static_cast<int>(sfz::kFilterBpf6p));
cbTypes->addItem("Brf1p", static_cast<int>(sfz::kFilterBrf1p));
cbTypes->addItem("Brf2p", static_cast<int>(sfz::kFilterBrf2p));
cbTypes->addItem("Hpf1p", static_cast<int>(sfz::kFilterHpf1p));
cbTypes->addItem("Hpf2p", static_cast<int>(sfz::kFilterHpf2p));
cbTypes->addItem("Hpf4p", static_cast<int>(sfz::kFilterHpf4p));
cbTypes->addItem("Hpf6p", static_cast<int>(sfz::kFilterHpf6p));
cbTypes->addItem("Lpf1p", static_cast<int>(sfz::kFilterLpf1p));
cbTypes->addItem("Lpf2p", static_cast<int>(sfz::kFilterLpf2p));
cbTypes->addItem("Lpf4p", static_cast<int>(sfz::kFilterLpf4p));
cbTypes->addItem("Lpf6p", static_cast<int>(sfz::kFilterLpf6p));
cbTypes->addItem("Pink", static_cast<int>(sfz::kFilterPink));
cbTypes->addItem("Lpf2pSv", static_cast<int>(sfz::kFilterLpf2pSv));
cbTypes->addItem("Hpf2pSv", static_cast<int>(sfz::kFilterHpf2pSv));
cbTypes->addItem("Bpf2pSv", static_cast<int>(sfz::kFilterBpf2pSv));
cbTypes->addItem("Brf2pSv", static_cast<int>(sfz::kFilterBrf2pSv));
cbTypes->addItem("Lsh", static_cast<int>(sfz::kFilterLsh));
cbTypes->addItem("Hsh", static_cast<int>(sfz::kFilterHsh));
cbTypes->addItem("Peq", static_cast<int>(sfz::kFilterPeq));
cbTypes->setCurrentIndex(cbTypes->findData(fType));
fUi.lcdType->display(fType);
connect(
cbTypes, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this, cbTypes](int index) { valueChangedType(cbTypes->itemData(index).toInt()); });
fUi.dialCutoff->setRange(cutoffMin, cutoffMax);
fUi.dialResonance->setRange(resoMin, resoMax);
fUi.dialPkShGain->setRange(pkshMin, pkshMax);
fUi.dialBandwidth->setRange(bwMin, bwMax);
fUi.spinCutoff->setRange(cutoffMin, cutoffMax);
fUi.spinResonance->setRange(resoMin, resoMax);
fUi.spinPkShGain->setRange(pkshMin, pkshMax);
fUi.spinBandwidth->setRange(bwMin, bwMax);
fUi.dialCutoff->setValue(fCutoff);
fUi.dialResonance->setValue(fReso);
fUi.dialPkShGain->setValue(fPksh);
fUi.dialBandwidth->setValue(fBw);
fUi.spinCutoff->setValue(fCutoff);
fUi.spinResonance->setValue(fReso);
fUi.spinPkShGain->setValue(fPksh);
fUi.spinBandwidth->setValue(fBw);
connect(
fUi.dialCutoff, &QDial::valueChanged,
this, [this](int value) { valueChangedCutoff(value); });
connect(
fUi.spinCutoff, QOverload<int>::of(&QSpinBox::valueChanged),
this, [this](int value) { valueChangedCutoff(value); });
connect(
fUi.dialResonance, &QDial::valueChanged,
this, [this](int value) { valueChangedResonance(value); });
connect(
fUi.spinResonance, QOverload<int>::of(&QSpinBox::valueChanged),
this, [this](int value) { valueChangedResonance(value); });
connect(
fUi.dialPkShGain, &QDial::valueChanged,
this, [this](int value) { valueChangedPkShGain(value); });
connect(
fUi.spinPkShGain, QOverload<int>::of(&QSpinBox::valueChanged),
this, [this](int value) { valueChangedPkShGain(value); });
connect(
fUi.dialBandwidth, &QDial::valueChanged,
this, [this](int value) { valueChangedBandwidth(value); });
connect(
fUi.spinBandwidth, QOverload<int>::of(&QSpinBox::valueChanged),
this, [this](int value) { valueChangedBandwidth(value); });
QButtonGroup *grpMode = new QButtonGroup(this);
grpMode->addButton(fUi.btnMultiMode, kFilterModeMulti);
grpMode->addButton(fUi.btnEqMode, kFilterModeEq);
fUi.btnMultiMode->setChecked(true);
grpMode->setExclusive(true);
connect(
grpMode, QOverload<int, bool>::of(&QButtonGroup::buttonToggled), this,
[this](int id, bool toggled) {
if (toggled)
valueChangedFilterMode(id);
});
window->adjustSize();
window->setFixedSize(window->size());
window->show();
}
int DemoApp::processAudio(jack_nframes_t nframes, void *cbdata)
{
DemoApp *self = reinterpret_cast<DemoApp *>(cbdata);
const float *ins[2];
float *outs[2];
ins[0] = reinterpret_cast<float *>(jack_port_get_buffer(self->fPorts[0], nframes));
ins[1] = reinterpret_cast<float *>(jack_port_get_buffer(self->fPorts[1], 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));
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);
break;
case kFilterModeEq:
self->fFilterEq.process(ins, outs, self->fCutoff, self->fBw, self->fPksh, nframes);
break;
}
return 0;
}
void DemoApp::valueChangedType(int value)
{
fType = value;
fUi.lcdType->display(value);
}
void DemoApp::valueChangedCutoff(int value)
{
fUi.dialCutoff->blockSignals(true);
fUi.dialCutoff->setValue(value);
fUi.dialCutoff->blockSignals(false);
fUi.spinCutoff->blockSignals(true);
fUi.spinCutoff->setValue(value);
fUi.spinCutoff->blockSignals(false);
fCutoff = value;
}
void DemoApp::valueChangedResonance(int value)
{
fUi.dialResonance->blockSignals(true);
fUi.dialResonance->setValue(value);
fUi.dialResonance->blockSignals(false);
fUi.spinResonance->blockSignals(true);
fUi.spinResonance->setValue(value);
fUi.spinResonance->blockSignals(false);
fReso = value;
}
void DemoApp::valueChangedPkShGain(int value)
{
fUi.dialPkShGain->blockSignals(true);
fUi.dialPkShGain->setValue(value);
fUi.dialPkShGain->blockSignals(false);
fUi.spinPkShGain->blockSignals(true);
fUi.spinPkShGain->setValue(value);
fUi.spinPkShGain->blockSignals(false);
fPksh = value;
}
void DemoApp::valueChangedBandwidth(int value)
{
fUi.dialBandwidth->blockSignals(true);
fUi.dialBandwidth->setValue(value);
fUi.dialBandwidth->blockSignals(false);
fUi.spinBandwidth->blockSignals(true);
fUi.spinBandwidth->setValue(value);
fUi.spinBandwidth->blockSignals(false);
fBw = value;
}
void DemoApp::valueChangedFilterMode(int value)
{
fUi.stackedWidget->setCurrentIndex(value);
fFilterMode = static_cast<FilterMode>(value);
}
int main(int argc, char *argv[])
{
DemoApp app(argc, argv);
if (!app.initSound())
return 1;
app.initWindow();
return app.exec();
}