Skip to content

Commit 72f23c8

Browse files
committedJun 22, 2023
added differentiator. added some new experimental phosphors. tweaked some constants etc
1 parent f4d8327 commit 72f23c8

10 files changed

+317
-153
lines changed
 

‎audiocontroller.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
/*
2+
* Copyright (C) 2020 - 2023 Judd Niemann - All Rights Reserved.
3+
* You may use, distribute and modify this code under the
4+
* terms of the GNU Lesser General Public License, version 2.1
5+
*
6+
* You should have received a copy of GNU Lesser General Public License v2.1
7+
* with this file. If not, please refer to: https://github.com/jniemann66/ReSampler
8+
*/
9+
110
#include "audiocontroller.h"
211

312
#include <QDebug>

‎audiocontroller.h

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
/*
2+
* Copyright (C) 2020 - 2023 Judd Niemann - All Rights Reserved.
3+
* You may use, distribute and modify this code under the
4+
* terms of the GNU Lesser General Public License, version 2.1
5+
*
6+
* You should have received a copy of GNU Lesser General Public License v2.1
7+
* with this file. If not, please refer to: https://github.com/jniemann66/ReSampler
8+
*/
9+
110
#ifndef AUDIOCONTROLLER_H
211
#define AUDIOCONTROLLER_H
312

‎differentiator.h

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright (C) 2020 - 2023 Judd Niemann - All Rights Reserved.
3+
* You may use, distribute and modify this code under the
4+
* terms of the GNU Lesser General Public License, version 2.1
5+
*
6+
* You should have received a copy of GNU Lesser General Public License v2.1
7+
* with this file. If not, please refer to: https://github.com/jniemann66/ReSampler
8+
*/
9+
10+
#ifndef DIFFERENTIATOR_H
11+
#define DIFFERENTIATOR_H
12+
13+
#include <vector>
14+
15+
template <typename FloatType>
16+
class Differentiator
17+
{
18+
19+
private:
20+
std::vector<FloatType> history;
21+
std::vector<FloatType> coeffs{
22+
0.0209,
23+
0.0,
24+
-0.1128,
25+
0.0,
26+
1.2411,
27+
0.0,
28+
-1.2411,
29+
0.0,
30+
0.1128,
31+
0.0,
32+
-0.0209
33+
};
34+
size_t differentiatorLength{coeffs.size()};
35+
size_t differentiatorDelay{differentiatorLength / 2};
36+
size_t differentiatorIndex{differentiatorLength - 1};
37+
38+
public:
39+
Differentiator()
40+
{
41+
history.resize(differentiatorLength, 0.0);
42+
differentiatorDelay = differentiatorLength / 2;
43+
}
44+
45+
FloatType get(const FloatType& input)
46+
{
47+
// place input into history
48+
history[differentiatorIndex] = input;
49+
50+
// perform the convolution
51+
FloatType dP{0.0}; // differentiator result
52+
size_t p = differentiatorIndex;
53+
for(size_t j = 0 ; j < differentiatorLength; j++) {
54+
FloatType vP = history.at(p);
55+
if(++p == differentiatorLength) {
56+
p = 0; // wrap
57+
}
58+
dP += coeffs[j] * vP;
59+
}
60+
61+
// update the current index
62+
if(differentiatorIndex == 0) {
63+
differentiatorIndex = differentiatorLength - 1; // wrap
64+
} else {
65+
differentiatorIndex--;
66+
}
67+
68+
return dP;
69+
}
70+
71+
};
72+
73+
#endif // DIFFERENTIATOR_H

‎displaysettingswidget.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ DisplaySettingsWidget::DisplaySettingsWidget(QWidget *parent) : QWidget(parent)
8787
emit persistenceChanged(value);
8888
});
8989

90-
connect(phosphorSelectControl, QOverload<int>::of(&QComboBox::activated), this, [this]{
91-
QString name = phosphorSelectControl->currentText();
90+
connect(phosphorSelectControl, &QComboBox::currentTextChanged, this, [this](const QString& name){
91+
9292
if(phosphors.contains(name)) {
9393
Phosphor phosphor = phosphors.value(name);
9494
QVector<QColor> phosphorColors;

‎mainwindow.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
107107
for(const QAudioDeviceInfo& device : qAsConst(devices)) {
108108
qDebug().noquote() << device.deviceName();
109109
}
110+
111+
scopeWidget->setBrightness(66.0);
112+
scopeWidget->setFocus(40.0);
113+
scopeWidget->setPersistence(48);
114+
110115
}
111116

112117
MainWindow::~MainWindow()

‎phosphors.json

+187-120
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,189 @@
11
{
2-
"phosphors":[
3-
{
4-
"name":"P1",
5-
"layers":[
6-
{
7-
"red":0,
8-
"green":255,
9-
"blue":0,
10-
"persistence":95
11-
}
12-
]
13-
},
14-
{
15-
"name":"P2",
16-
"layers":[
17-
{
18-
"red":20,
19-
"green":255,
20-
"blue":192,
21-
"persistence":440
22-
},
23-
{
24-
"red":0,
25-
"green":32,
26-
"blue":0,
27-
"persistence":440
28-
}
29-
]
30-
},
31-
{
32-
"name":"P3",
33-
"layers":[
34-
{
35-
"red":255,
36-
"green":176,
37-
"blue":0,
38-
"persistence":130
39-
}
40-
]
41-
},
42-
{
43-
"name":"Firegaze",
44-
"layers":[
45-
{
46-
"red":255,
47-
"green":176,
48-
"blue":0,
49-
"persistence":200
50-
},
51-
{
52-
"red":96,
53-
"green":96,
54-
"blue":0,
55-
"persistence":200
56-
}
57-
]
58-
},
59-
{
60-
"name":"P4",
61-
"layers":[
62-
{
63-
"red":246,
64-
"green":250,
65-
"blue":255,
66-
"persistence":10
67-
}
68-
]
69-
},
70-
{
71-
"name":"P7",
72-
"layers":[
73-
{
74-
"red":160,
75-
"green":160,
76-
"blue":255,
77-
"persistence":1000
78-
},
79-
{
80-
"red":85,
81-
"green":85,
82-
"blue":0,
83-
"persistence":1000
84-
}
85-
]
86-
},
87-
{
88-
"name":"P11",
89-
"layers":[
90-
{
91-
"red":0,
92-
"green":56,
93-
"blue":255,
94-
"persistence":20
95-
}
96-
]
97-
},
98-
{
99-
"name":"P31",
100-
"layers":[
101-
{
102-
"red":62,
103-
"green":255,
104-
"blue":111,
105-
"persistence":32
106-
}
107-
]
108-
},
109-
{
110-
"name":"P5",
111-
"layers":[
112-
{
113-
"red":0,
114-
"green":112,
115-
"blue":255,
116-
"persistence":20
117-
}
118-
]
119-
}
120-
121-
]
2+
"phosphors":[
3+
{
4+
"name":"P1",
5+
"layers":[
6+
{
7+
"red":0,
8+
"green":255,
9+
"blue":0,
10+
"persistence":95
11+
}
12+
]
13+
},
14+
{
15+
"name":"P2",
16+
"layers":[
17+
{
18+
"red":20,
19+
"green":255,
20+
"blue":192,
21+
"persistence":220
22+
},
23+
{
24+
"red":0,
25+
"green":32,
26+
"blue":0,
27+
"persistence":220
28+
}
29+
]
30+
},
31+
{
32+
"name":"P3",
33+
"layers":[
34+
{
35+
"red":255,
36+
"green":176,
37+
"blue":0,
38+
"persistence":130
39+
}
40+
]
41+
},
42+
{
43+
"name":"Firegaze",
44+
"layers":[
45+
{
46+
"red":255,
47+
"green":176,
48+
"blue":0,
49+
"persistence":100
50+
},
51+
{
52+
"red":96,
53+
"green":96,
54+
"blue":0,
55+
"persistence":100
56+
}
57+
]
58+
},
59+
{
60+
"name":"P4",
61+
"layers":[
62+
{
63+
"red":246,
64+
"green":250,
65+
"blue":255,
66+
"persistence":32
67+
}
68+
]
69+
},
70+
{
71+
"name":"P5",
72+
"layers":[
73+
{
74+
"red":0,
75+
"green":112,
76+
"blue":255,
77+
"persistence":20
78+
}
79+
]
80+
},
81+
{
82+
"name":"P7",
83+
"layers":[
84+
{
85+
"red":160,
86+
"green":160,
87+
"blue":255,
88+
"persistence":500
89+
},
90+
{
91+
"red":85,
92+
"green":85,
93+
"blue":0,
94+
"persistence":500
95+
}
96+
]
97+
},
98+
{
99+
"name":"P11",
100+
"layers":[
101+
{
102+
"red":0,
103+
"green":56,
104+
"blue":255,
105+
"persistence":32
106+
}
107+
]
108+
},
109+
{
110+
"name":"P31",
111+
"layers":[
112+
{
113+
"red":62,
114+
"green":255,
115+
"blue":111,
116+
"persistence":32
117+
}
118+
]
119+
},
120+
{
121+
"name":"Ink Pink",
122+
"layers":[
123+
{
124+
"red":64,
125+
"green":0,
126+
"blue":64,
127+
"persistence":200
128+
},
129+
{
130+
"red":255,
131+
"green":255,
132+
"blue":255,
133+
"persistence":200
134+
}
135+
]
136+
},
137+
{
138+
"name":"Bloodbath",
139+
"layers":[
140+
{
141+
"red":255,
142+
"green":160,
143+
"blue":160,
144+
"persistence":500
145+
},
146+
{
147+
"red":0,
148+
"green":85,
149+
"blue":85,
150+
"persistence":500
151+
}
152+
]
153+
},
154+
{
155+
"name":"Iodine",
156+
"layers":[
157+
{
158+
"red":160,
159+
"green":255,
160+
"blue":160,
161+
"persistence":500
162+
},
163+
{
164+
"red":85,
165+
"green":0,
166+
"blue":85,
167+
"persistence":500
168+
}
169+
]
170+
},
171+
{
172+
"name":"Tropical",
173+
"layers":[
174+
{
175+
"red":160,
176+
"green":255,
177+
"blue":255,
178+
"persistence":500
179+
},
180+
{
181+
"red":85,
182+
"green":85,
183+
"blue":0,
184+
"persistence":500
185+
}
186+
]
187+
}
188+
]
122189
}

‎scopewidget.cpp

+28-20
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <QPainter>
1414
#include <QEvent>
1515
#include <QDebug>
16+
#include <differentiator.h>
17+
1618
#include <cmath>
1719

1820
ScopeWidget::ScopeWidget(QWidget *parent) : QWidget(parent), pixmap(640, 640)
@@ -53,9 +55,6 @@ ScopeWidget::ScopeWidget(QWidget *parent) : QWidget(parent), pixmap(640, 640)
5355
mainLayout->addLayout(screenLayout);
5456
setLayout(mainLayout);
5557

56-
setBrightness(66.0);
57-
setFocus(50.0);
58-
setPersistence(32);
5958
plotTimer.start();
6059
}
6160

@@ -145,6 +144,7 @@ void ScopeWidget::setBackgroundColor(const QColor &value)
145144

146145
void ScopeWidget::setPhosporColors(const QVector<QColor>& colors)
147146
{
147+
// qDebug() << __func__;
148148
if(!colors.isEmpty()) {
149149
phosphorColor = colors.at(0);
150150
if(colors.count() > 1) {
@@ -162,21 +162,25 @@ double ScopeWidget::getPersistence() const
162162
return persistence;
163163
}
164164

165-
void ScopeWidget::setPersistence(double value)
165+
void ScopeWidget::setPersistence(double time_ms)
166166
{
167-
persistence = value;
168-
// define fraction of original brightness (10%)
169-
constexpr double decayTarget = 0.1;
167+
// qDebug() << QStringLiteral("setting persistence to %1").arg(time_ms);
168+
persistence = time_ms;
169+
170+
// define fraction of original brightness
171+
constexpr double decayTarget = 0.2;
172+
170173
// set minimum darkening amount threshold. (If the darkening amount is too low, traces will never completely disappear)
171174
constexpr int minDarkenAlpha = 32;
175+
172176
darkenAlpha = 0;
173177
darkenNthFrame = 0;
174178
do {
175-
++darkenNthFrame;
176-
double n = std::max(0.01, value / plotTimer.interval()) / darkenNthFrame; // number of frames to reach decayTarget (can't be zero)
179+
++darkenNthFrame; // for really long persistence, darkening operation may need to occur less often than once per frame
180+
double n = std::max(1.0, time_ms / plotTimer.interval()) / darkenNthFrame; // number of frames to reach decayTarget (can't be zero)
177181
darkenAlpha = std::min(std::max(1, static_cast<int>(255 * (1.0 - std::pow(decayTarget, (1.0 / n))))), 255);
178-
179182
} while (darkenAlpha < minDarkenAlpha);
183+
180184
darkencolor.setAlpha(darkenAlpha);
181185
darkenCooldownCounter = darkenNthFrame;
182186
}
@@ -228,6 +232,8 @@ void ScopeWidget::setTotalFrames(const int64_t &value)
228232

229233
void ScopeWidget::render()
230234
{
235+
static Differentiator<double> d;
236+
231237
const int64_t toFrame = qMin(totalFrames - 1, startFrame + static_cast<int64_t>(elapsedTimer.elapsed() * framesPerMillisecond));
232238
const int64_t framesRead = sndfile->readf(inputBuffer.data(), qMin(maxFramesToRead, toFrame - currentFrame));
233239

@@ -260,29 +266,31 @@ void ScopeWidget::render()
260266
break;
261267
case MidSide:
262268
{
263-
constexpr double rsqrt2 = 0.707; // 1 / sqrt(2)
269+
constexpr double rsqrt2 = 0.707;
264270
pt = {(1.0 + rsqrt2*(ch0val - ch1val)) * cx,
265271
(1.0 - rsqrt2*(ch0val + ch1val)) * cy};
266272
painter.drawPoint(pt);
267273
break;
268274
}
269275
case Single:
270276
{
271-
constexpr double thresh = 100.0 / 32767;
272-
static const double sweepAdvance = 0.02 * cx / framesPerMillisecond;
277+
constexpr double thresh = 0.01;
278+
static const double sweepAdvance = 0.1 * cx / framesPerMillisecond;
273279
static bool triggered = false;
274-
static double x = 0;
275-
static QPointF lastPoint;
280+
static double x = 0.0;
281+
static QPointF lastPoint{x, cy};
282+
283+
double slope = d.get(ch0val);
284+
triggered = triggered || (std::abs(ch0val) < thresh && slope > 0);
276285

277-
if(triggered || (triggered == (ch0val >= 0.0 && ch0val < thresh))) {
278-
pt = {x , cy *(1.0 - ch0val)};
286+
if(triggered) {
287+
pt = {x, cy * (1.0 - ch0val)};
279288
painter.drawLine(lastPoint, pt);
280289
lastPoint = pt;
281290
x += sweepAdvance;
282-
if(x >= 2.0 * cx) {
291+
if(x >= 2.0 * cx) { // sweep completed
283292
triggered = false;
284-
x = 0;
285-
lastPoint = {x, cy};
293+
lastPoint = {x = 0.0, cy};
286294
}
287295
}
288296
}

‎scopewidget.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class ScopeWidget : public QWidget
118118
void setTotalFrames(const int64_t &value);
119119
void setBrightness(double value);
120120
void setFocus(double value);
121-
void setPersistence(double value);
121+
void setPersistence(double time_ms);
122122
void setPhosporColors(const QVector<QColor> &colors);
123123
void setBackgroundColor(const QColor &value);
124124
void setConstrainToSquare(bool value);
@@ -146,7 +146,7 @@ public slots:
146146
QTimer plotTimer;
147147
QElapsedTimer elapsedTimer;
148148

149-
ChannelMode channelMode{XY};
149+
ChannelMode channelMode{XY};
150150
int framesPerMillisecond{0};
151151
double millisecondsPerFrame{0.0};
152152
int64_t startFrame{0};

‎sndscope.pro

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ SOURCES += \
4545

4646
HEADERS += \
4747
audiocontroller.h \
48+
differentiator.h \
4849
displaysettingswidget.h \
4950
mainwindow.h \
5051
phosphor.h \

‎transportwidget.cpp

+1-9
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,13 @@ TransportWidget::TransportWidget(QWidget *parent) : QWidget(parent)
2424
slider = new QSlider(Qt::Horizontal);
2525
rtsButton = new QPushButton;
2626
playPauseButton = new QPushButton;
27-
28-
29-
auto set7SegStyle = [this](QLCDNumber* l) {
27+
auto set7SegStyle = [](QLCDNumber* l) {
3028
auto p = l->palette();
3129

3230
p.setColor(QPalette::WindowText, QColor(0,255,255,255));
3331
p.setColor(QPalette::Window, QColor(0,10,10,192));
3432
p.setColor(QPalette::Dark, QColor(32,32,32));
3533
p.setColor(QPalette::Light, QColor(128,128,128));
36-
37-
38-
// l->setContentsMargins(5,5,5,5);
3934
l->setAutoFillBackground(true);
4035
l->setPalette(p);
4136
l->setLineWidth(1);
@@ -80,9 +75,6 @@ TransportWidget::TransportWidget(QWidget *parent) : QWidget(parent)
8075
buttonLayout->addWidget(new QLabel("."));
8176
buttonLayout->addWidget(ms);
8277

83-
84-
85-
8678
mainLayout->addLayout(sliderLayout);
8779
mainLayout->addLayout(buttonLayout);
8880

0 commit comments

Comments
 (0)
Please sign in to comment.