Skip to content

Commit 6e2a48b

Browse files
committed
Make filter sliders show the valus also in Hz
1 parent cc79bdf commit 6e2a48b

12 files changed

Lines changed: 174 additions & 58 deletions

File tree

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ set(MAIN Main.qml)
272272
set(QML_SOURCE_FILES
273273
${QML_BASE_DIR}/${MAIN}
274274
${QML_BASE_DIR}/BottomBar.qml
275+
${QML_BASE_DIR}/Components/FilterKnob.qml
276+
${QML_BASE_DIR}/Components/Knob.qml
275277
${QML_BASE_DIR}/Components/LayoutSeparator.qml
276278
${QML_BASE_DIR}/Components/MenuItemDelegate.qml
277279
${QML_BASE_DIR}/Components/MidiCcComboBox.qml

src/application/service/sampler_controller.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ SamplerController::SamplerController(SamplerDevice::SamplerDeviceS sampler, QObj
1111
, m_padModel { std::make_unique<SamplerPadModel>(m_sampler, this) }
1212
, m_selectedPad { -1 }
1313
{
14+
if (m_sampler) {
15+
connect(m_sampler.get(), &Device::dataChanged, this, &SamplerController::sampleRateChanged);
16+
}
1417
}
1518

1619
SamplerController::~SamplerController() = default;
@@ -29,12 +32,20 @@ void SamplerController::setSampler(SamplerDevice::SamplerDeviceS sampler)
2932
{
3033
if (m_sampler != sampler) {
3134
m_sampler = std::move(sampler);
35+
if (m_sampler) {
36+
connect(m_sampler.get(), &Device::dataChanged, this, &SamplerController::sampleRateChanged);
37+
}
3238
m_padModel->setSampler(m_sampler);
3339
emit samplerChanged();
3440
setSelectedPad(m_selectedPad); // Trigger updates for properties
3541
}
3642
}
3743

44+
uint32_t SamplerController::sampleRate() const
45+
{
46+
return m_sampler ? m_sampler->sampleRate() : 44100;
47+
}
48+
3849
int SamplerController::selectedPad() const
3950
{
4051
return m_selectedPad;

src/application/service/sampler_controller.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,14 @@ class SamplerController : public QObject
4141
Q_PROPERTY(int selectedPadStartOffsetMilliseconds READ selectedPadStartOffsetMilliseconds WRITE setSelectedPadStartOffsetMilliseconds NOTIFY selectedPadStartOffsetChanged)
4242
Q_PROPERTY(double selectedPadDuration READ selectedPadDuration NOTIFY selectedPadDurationChanged)
4343
Q_PROPERTY(bool channelMode READ channelMode WRITE setChannelMode NOTIFY channelModeChanged)
44+
Q_PROPERTY(uint32_t sampleRate READ sampleRate NOTIFY sampleRateChanged)
4445

4546
public:
4647
explicit SamplerController(SamplerDevice::SamplerDeviceS sampler, QObject * parent = nullptr);
4748
~SamplerController() override;
4849

50+
uint32_t sampleRate() const;
51+
4952
SamplerPadModel * padModel() const;
5053
SamplerDevice::SamplerDeviceS sampler() const;
5154
void setSampler(SamplerDevice::SamplerDeviceS sampler);
@@ -103,6 +106,7 @@ class SamplerController : public QObject
103106
void selectedPadStartOffsetChanged();
104107
void selectedPadDurationChanged();
105108
void channelModeChanged();
109+
void sampleRateChanged();
106110
void samplerChanged();
107111

108112
private:

src/application/service/synth_controller.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ SynthController::SynthController(std::shared_ptr<SynthDevice> synth, QObject * p
2525
: QObject(parent)
2626
, m_synth { std::move(synth) }
2727
{
28+
if (m_synth) {
29+
connect(m_synth.get(), &Device::dataChanged, this, &SynthController::sampleRateChanged);
30+
}
2831
}
2932

3033
SynthController::~SynthController() = default;
@@ -127,6 +130,8 @@ void SynthController::setMasterPan(int p) { if (m_synth) { m_synth->setMasterPan
127130
int SynthController::masterVolume() const { return m_synth ? static_cast<int>(std::round(m_synth->masterVolume() * 100.0f)) : 0; }
128131
void SynthController::setMasterVolume(int v) { if (m_synth) { m_synth->setMasterVolume(v / 100.0f); emit masterVolumeChanged(); } }
129132

133+
uint32_t SynthController::sampleRate() const { return m_synth ? m_synth->sampleRate() : 44100; }
134+
130135
QStringList SynthController::presetNames() const
131136
{
132137
QStringList names;
@@ -180,6 +185,9 @@ void SynthController::setSynth(std::shared_ptr<SynthDevice> synth)
180185
{
181186
if (m_synth != synth) {
182187
m_synth = std::move(synth);
188+
if (m_synth) {
189+
connect(m_synth.get(), &Device::dataChanged, this, &SynthController::sampleRateChanged);
190+
}
183191
emit synthChanged();
184192
requestSettings();
185193
}

src/application/service/synth_controller.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class SynthController : public QObject
8383
Q_PROPERTY(int panSpread READ panSpread WRITE setPanSpread NOTIFY panSpreadChanged)
8484
Q_PROPERTY(int masterPan READ masterPan WRITE setMasterPan NOTIFY masterPanChanged)
8585
Q_PROPERTY(int masterVolume READ masterVolume WRITE setMasterVolume NOTIFY masterVolumeChanged)
86+
Q_PROPERTY(uint32_t sampleRate READ sampleRate NOTIFY sampleRateChanged)
8687
Q_PROPERTY(QStringList presetNames READ presetNames CONSTANT)
8788

8889
// Delay
@@ -149,6 +150,8 @@ class SynthController : public QObject
149150
int masterPan() const; void setMasterPan(int p);
150151
int masterVolume() const; void setMasterVolume(int v);
151152

153+
uint32_t sampleRate() const;
154+
152155
QStringList presetNames() const;
153156

154157
int delayType() const; void setDelayType(int type);
@@ -180,6 +183,7 @@ class SynthController : public QObject
180183
void modAttackChanged(); void modDecayChanged(); void modIntChanged(); void modTargetChanged();
181184
void lfoWaveformChanged(); void lfoModeChanged(); void lfoRateChanged(); void lfoIntChanged(); void lfoTargetChanged();
182185
void voiceModeChanged(); void voiceDepthChanged(); void portamentoChanged(); void panSpreadChanged(); void masterPanChanged(); void masterVolumeChanged();
186+
void sampleRateChanged();
183187
void delayTypeChanged(); void delayTimeChanged(); void delayFeedbackChanged(); void delayDepthChanged(); void delayMixChanged(); void delaySyncChanged(); void delaySyncDivisionChanged();
184188

185189
public:

src/domain/devices/device.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class Device : public QObject, public ParameterContainer
5353

5454
virtual void reset() = 0;
5555

56+
uint32_t sampleRate() const { return m_sampleRate; }
57+
5658
virtual void serializeToXml(QXmlStreamWriter & writer) const;
5759
virtual void deserializeFromXml(QXmlStreamReader & reader);
5860

@@ -63,6 +65,8 @@ class Device : public QObject, public ParameterContainer
6365
void serializeAttributesToXml(QXmlStreamWriter & writer) const;
6466
void deserializeAttributesFromXml(QXmlStreamReader & reader);
6567

68+
uint32_t m_sampleRate { 44100 };
69+
6670
private:
6771
size_t m_id { 0 };
6872
};

src/domain/devices/sampler_device.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,9 @@ void SamplerDevice::processMidiAllNotesOff()
304304

305305
void SamplerDevice::processAudio(float * output, uint32_t nFrames, uint32_t sampleRate)
306306
{
307-
std::lock_guard<std::mutex> lock { m_mutex };
307+
m_sampleRate = sampleRate;
308+
const std::lock_guard<std::mutex> lock(m_mutex);
309+
308310

309311
const float fadeStep = 1.0f / 256.0f;
310312

src/domain/devices/synth_device.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ std::string SynthDevice::typeId() const
159159

160160
void SynthDevice::processAudio(float * output, uint32_t nFrames, uint32_t sampleRate)
161161
{
162-
const std::lock_guard<std::mutex> lock { m_mutex };
162+
m_sampleRate = sampleRate;
163+
const std::lock_guard<std::mutex> lock(m_mutex);
164+
163165

164166
std::vector<float> localBuffer(nFrames * 2, 0.0f);
165167

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// This file is part of Noteahead.
2+
// Copyright (C) 2026 Jussi Lind <jussi.lind@iki.fi>
3+
//
4+
// Noteahead is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
// Noteahead is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with Noteahead. If not, see <http://www.gnu.org/licenses/>.
15+
16+
import QtQuick 2.15
17+
import QtQuick.Controls 2.15
18+
import QtQuick.Controls.Universal 2.15
19+
import QtQuick.Layouts 1.15
20+
import Noteahead 1.0
21+
22+
ColumnLayout {
23+
id: knobRoot
24+
property string label: ""
25+
property real value: 0
26+
property real from: 0
27+
property real to: 100
28+
property string suffix: "%"
29+
property real sampleRate: 44100
30+
property bool isHpf: false
31+
signal moved(real val)
32+
33+
Universal.theme: Universal.Dark
34+
Universal.accent: themeService.accentColor
35+
36+
readonly property real cutoffHz: {
37+
const normalizedCutoff = value / 100.0;
38+
if (isHpf && normalizedCutoff <= 0.001) {
39+
return 0.0;
40+
}
41+
const freq = 20.0 * Math.pow(Math.min(20000.0, sampleRate * 0.49) / 20.0, normalizedCutoff);
42+
return freq;
43+
}
44+
45+
readonly property string freqString: {
46+
if (isHpf && value <= 0) {
47+
return "0 Hz";
48+
}
49+
if (!isHpf && value >= 100) {
50+
return qsTr("Bypass");
51+
}
52+
if (cutoffHz >= 1000) {
53+
return (cutoffHz / 1000.0).toFixed(1) + " kHz";
54+
}
55+
return Math.round(cutoffHz) + " Hz";
56+
}
57+
58+
spacing: 2
59+
Label {
60+
text: knobRoot.label + " (" + Math.round(knobRoot.value) + knobRoot.suffix + " / " + knobRoot.freqString + ")"
61+
font.pixelSize: 11
62+
color: themeService.accentColor
63+
Layout.alignment: Qt.AlignHCenter
64+
}
65+
Slider {
66+
from: knobRoot.from
67+
to: knobRoot.to
68+
value: knobRoot.value
69+
stepSize: 1
70+
Layout.fillWidth: true
71+
onMoved: () => knobRoot.moved(value)
72+
}
73+
}

src/view/qml/Components/Knob.qml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// This file is part of Noteahead.
2+
// Copyright (C) 2026 Jussi Lind <jussi.lind@iki.fi>
3+
//
4+
// Noteahead is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
// Noteahead is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with Noteahead. If not, see <http://www.gnu.org/licenses/>.
15+
16+
import QtQuick 2.15
17+
import QtQuick.Controls 2.15
18+
import QtQuick.Controls.Universal 2.15
19+
import QtQuick.Layouts 1.15
20+
import Noteahead 1.0
21+
22+
ColumnLayout {
23+
id: knobRoot
24+
property string label: ""
25+
property real value: 0
26+
property real from: 0
27+
property real to: 100
28+
property string suffix: "%"
29+
property alias stepSize: slider.stepSize
30+
signal moved(real val)
31+
32+
Universal.theme: Universal.Dark
33+
Universal.accent: themeService.accentColor
34+
35+
spacing: 2
36+
Label {
37+
text: knobRoot.label + " (" + Math.round(knobRoot.value) + knobRoot.suffix + ")"
38+
font.pixelSize: 11
39+
color: themeService.accentColor
40+
Layout.alignment: Qt.AlignHCenter
41+
}
42+
Slider {
43+
id: slider
44+
from: knobRoot.from
45+
to: knobRoot.to
46+
value: knobRoot.value
47+
stepSize: 1
48+
Layout.fillWidth: true
49+
onMoved: () => knobRoot.moved(value)
50+
}
51+
}

0 commit comments

Comments
 (0)