Experiment with afterglow, and partial buffer painting
This commit is contained in:
@@ -18,7 +18,7 @@
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr int samplerates[] = { 44100, 48000, 96000, 192000 };
|
constexpr int samplerates[] = { 44100, 48000, 96000, 192000 };
|
||||||
|
|
||||||
constexpr int refreshrates[] = { 15, 30, 50, 60 };
|
constexpr int refreshrates[] = { 1, 15, 30, 50, 60 };
|
||||||
|
|
||||||
constexpr int zoomlevels[] = { 50, 75, 100, 200, 400, 800 };
|
constexpr int zoomlevels[] = { 50, 75, 100, 200, 400, 800 };
|
||||||
|
|
||||||
@@ -125,8 +125,8 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
auto layout = new QFormLayout(widget);
|
auto layout = new QFormLayout(widget);
|
||||||
{
|
{
|
||||||
auto input = new QDoubleSpinBox;
|
auto input = new QDoubleSpinBox;
|
||||||
input->setRange(0, 1.0);
|
input->setRange(0, 10000.0);
|
||||||
input->setSingleStep(0.1);
|
input->setSingleStep(1.0);
|
||||||
input->setValue(m_ui->widget->afterglow());
|
input->setValue(m_ui->widget->afterglow());
|
||||||
connect(input, qOverload<double>(&QDoubleSpinBox::valueChanged), m_ui->widget, &OsciWidget::setAfterglow);
|
connect(input, qOverload<double>(&QDoubleSpinBox::valueChanged), m_ui->widget, &OsciWidget::setAfterglow);
|
||||||
layout->addRow(tr("Afterglow:"), input);
|
layout->addRow(tr("Afterglow:"), input);
|
||||||
|
@@ -16,11 +16,12 @@ qint32 framesForDuration(qint64 duration){
|
|||||||
|
|
||||||
OsciWidget::OsciWidget(QWidget *parent)
|
OsciWidget::OsciWidget(QWidget *parent)
|
||||||
: QOpenGLWidget{parent}
|
: QOpenGLWidget{parent}
|
||||||
|
, m_bufferOffset{m_buffer.begin()}
|
||||||
|
, m_lastTime{0}
|
||||||
, m_redrawTimerId(startTimer(1000/m_fps, Qt::PreciseTimer))
|
, m_redrawTimerId(startTimer(1000/m_fps, Qt::PreciseTimer))
|
||||||
, m_lastFrame{0}
|
|
||||||
{
|
{
|
||||||
m_bufferTimer.start();
|
|
||||||
m_statsTimer.start();
|
m_statsTimer.start();
|
||||||
|
m_bufferTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
int OsciWidget::lightspeed() const
|
int OsciWidget::lightspeed() const
|
||||||
@@ -39,9 +40,11 @@ void OsciWidget::setFps(int fps)
|
|||||||
|
|
||||||
void OsciWidget::setAfterglow(float afterglow)
|
void OsciWidget::setAfterglow(float afterglow)
|
||||||
{
|
{
|
||||||
m_afterglow = afterglow;
|
m_decayTime = afterglow;
|
||||||
// percentage of the image that should be visible after one second
|
// percentage of the image that should be visible after one second
|
||||||
// i.e. factor^fps=afterglow -> factor = afterglow^(1/fps)
|
// i.e. factor^fps=afterglow -> factor = afterglow^(1/fps)
|
||||||
|
// i.e. factor^(fps*persistence)=1/e -> factor = 1/e^(1/(fps*persistence/1000.0))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OsciWidget::setLightspeed(int lightspeed)
|
void OsciWidget::setLightspeed(int lightspeed)
|
||||||
@@ -57,7 +60,20 @@ void OsciWidget::renderSamples(const SamplePair *begin, const SamplePair *end)
|
|||||||
|
|
||||||
m_samplesCounter += std::distance(begin, end);
|
m_samplesCounter += std::distance(begin, end);
|
||||||
|
|
||||||
|
auto offset = std::distance(m_buffer.begin(), m_bufferOffset);
|
||||||
|
|
||||||
|
if(m_bufferTimer.elapsed()-m_lastTime > 5000)
|
||||||
|
{
|
||||||
|
qDebug() << "deleting: " << m_bufferOffset - m_buffer.begin();
|
||||||
|
//m_buffer.erase(m_buffer.begin(), m_bufferOffset);
|
||||||
|
m_buffer.clear();
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
m_lastTime = m_bufferTimer.elapsed();
|
||||||
|
}
|
||||||
|
//qDebug () << " inserting " << std::distance(begin, end);
|
||||||
m_buffer.insert(m_buffer.end(), begin, end);
|
m_buffer.insert(m_buffer.end(), begin, end);
|
||||||
|
m_bufferOffset = m_buffer.begin() + offset;
|
||||||
//m_bufferTimer.restart();
|
//m_bufferTimer.restart();
|
||||||
//qDebug() << m_statsTimer.elapsed();
|
//qDebug() << m_statsTimer.elapsed();
|
||||||
}
|
}
|
||||||
@@ -69,7 +85,7 @@ void OsciWidget::paintEvent(QPaintEvent *event)
|
|||||||
m_frameCounter++;
|
m_frameCounter++;
|
||||||
if (m_statsTimer.hasExpired(1000))
|
if (m_statsTimer.hasExpired(1000))
|
||||||
{
|
{
|
||||||
emit statusUpdate(QString("%0FPS (%1 callbacks, %2 samples, %3 avg per callback)").arg(m_frameCounter).arg(m_callbacksCounter).arg(m_samplesCounter).arg(m_callbacksCounter>0?m_samplesCounter/m_callbacksCounter:0));
|
emit statusUpdate(QString("%0FPS (%1 callbacks, %2 samples, %3 avg per callback, bufferSize %4, elapsed %5)").arg(m_frameCounter).arg(m_callbacksCounter).arg(m_samplesCounter).arg(m_callbacksCounter>0?m_samplesCounter/m_callbacksCounter:0).arg(m_buffer.size()).arg(m_bufferTimer.elapsed()));
|
||||||
m_frameCounter = 0;
|
m_frameCounter = 0;
|
||||||
m_callbacksCounter = 0;
|
m_callbacksCounter = 0;
|
||||||
m_samplesCounter = 0;
|
m_samplesCounter = 0;
|
||||||
@@ -86,15 +102,18 @@ void OsciWidget::darkenFrame()
|
|||||||
|
|
||||||
painter.setCompositionMode(QPainter::CompositionMode_Multiply);
|
painter.setCompositionMode(QPainter::CompositionMode_Multiply);
|
||||||
painter.setPen({});
|
painter.setPen({});
|
||||||
auto afterglowColor = 255 * pow(m_afterglow, 1.0/m_fps);
|
//auto afterglowColor = 255 * pow(m_decayTime, 1.0/m_fps);
|
||||||
|
auto afterglowColor = static_cast<int>(255 * pow(exp(-1), 1000.0/m_decayTime/m_fps));
|
||||||
|
qDebug() << afterglowColor;
|
||||||
painter.setBrush(QColor(afterglowColor, afterglowColor, afterglowColor));
|
painter.setBrush(QColor(afterglowColor, afterglowColor, afterglowColor));
|
||||||
painter.drawRect(m_pixmap.rect());
|
painter.drawRect(m_pixmap.rect());
|
||||||
|
//m_pixmap.fill(Qt::black);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OsciWidget::updateDrawBuffer()
|
void OsciWidget::updateDrawBuffer()
|
||||||
{
|
{
|
||||||
// Workaround for flickering (do not update, when there is no new data)
|
// If there is no new data do not update
|
||||||
//if(m_buffer.empty()) return;
|
if(m_buffer.empty()) return;
|
||||||
|
|
||||||
if (m_pixmap.size() != size())
|
if (m_pixmap.size() != size())
|
||||||
{
|
{
|
||||||
@@ -114,17 +133,18 @@ void OsciWidget::updateDrawBuffer()
|
|||||||
pen.setColor(QColor(0, 255, 0));
|
pen.setColor(QColor(0, 255, 0));
|
||||||
painter.setPen(pen);
|
painter.setPen(pen);
|
||||||
|
|
||||||
qint64 samplesUsed = 0;
|
|
||||||
double timeConstant = log(m_afterglow);
|
|
||||||
|
|
||||||
auto duration = m_bufferTimer.elapsed()*1000;
|
// persistance time is the time it needs to decay to 1/e ~ 36,7%
|
||||||
qint32 framesCnt = framesForDuration(duration);
|
|
||||||
//qDebug() << framesCnt << duration;
|
|
||||||
|
|
||||||
for (size_t idx = 0; idx < framesCnt && idx < m_buffer.size(); ++idx)
|
auto duration = 1000*(m_bufferTimer.elapsed()-m_lastTime);
|
||||||
|
auto framesOffset = framesForDuration(duration);
|
||||||
|
//qDebug() << framesOffset << m_buffer.size()-framesOffset << m_bufferOffset - m_buffer.begin();
|
||||||
|
|
||||||
|
//m_bufferBegin = m_buffer.begin();
|
||||||
|
auto bufferEnd = m_buffer.begin() + framesOffset;
|
||||||
|
for (;m_bufferOffset < bufferEnd && m_bufferOffset != m_buffer.end(); ++m_bufferOffset)
|
||||||
{
|
{
|
||||||
const auto &frame = m_buffer[idx];
|
const auto &frame = *m_bufferOffset;
|
||||||
double time = (m_buffer.size() - samplesUsed) / 44100.0;
|
|
||||||
|
|
||||||
const QPointF p{
|
const QPointF p{
|
||||||
float(frame.first) / std::numeric_limits<qint16>::max(),
|
float(frame.first) / std::numeric_limits<qint16>::max(),
|
||||||
@@ -132,8 +152,14 @@ void OsciWidget::updateDrawBuffer()
|
|||||||
};
|
};
|
||||||
|
|
||||||
const QLineF line(m_lastPoint, p);
|
const QLineF line(m_lastPoint, p);
|
||||||
|
|
||||||
|
// the time of one sample is 1/samplerate
|
||||||
|
// the brightness
|
||||||
auto beamOpacity = std::min(1.0, 1. / ((line.length() * m_lightspeed) + 1));
|
auto beamOpacity = std::min(1.0, 1. / ((line.length() * m_lightspeed) + 1));
|
||||||
auto beamDecay = exp(time*timeConstant);
|
|
||||||
|
|
||||||
|
double time = 1000.0 * std::distance(m_bufferOffset, bufferEnd) / 44100.0;
|
||||||
|
auto beamDecay = exp(-time/m_decayTime);
|
||||||
//qDebug() << time << beamDecay;
|
//qDebug() << time << beamDecay;
|
||||||
|
|
||||||
painter.setOpacity(beamDecay*beamOpacity);
|
painter.setOpacity(beamDecay*beamOpacity);
|
||||||
@@ -141,10 +167,7 @@ void OsciWidget::updateDrawBuffer()
|
|||||||
painter.drawLine(m_lastPoint, p);
|
painter.drawLine(m_lastPoint, p);
|
||||||
|
|
||||||
m_lastPoint = p;
|
m_lastPoint = p;
|
||||||
++samplesUsed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//m_buffer.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OsciWidget::timerEvent(QTimerEvent *event)
|
void OsciWidget::timerEvent(QTimerEvent *event)
|
||||||
|
@@ -21,7 +21,7 @@ public:
|
|||||||
|
|
||||||
float factor() const { return m_factor; }
|
float factor() const { return m_factor; }
|
||||||
int fps() const { return m_fps; }
|
int fps() const { return m_fps; }
|
||||||
float afterglow() const { return m_afterglow; }
|
float afterglow() const { return m_decayTime; }
|
||||||
int lightspeed() const;
|
int lightspeed() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -44,19 +44,20 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
float m_factor{1.f};
|
float m_factor{1.f};
|
||||||
int m_fps{30};
|
int m_fps{60};
|
||||||
float m_afterglow{0.01};
|
float m_decayTime{25.0};
|
||||||
float m_lightspeed{35.f};
|
float m_lightspeed{35.f};
|
||||||
|
|
||||||
std::vector<SamplePair> m_buffer;
|
std::vector<SamplePair> m_buffer;
|
||||||
|
std::vector<SamplePair>::iterator m_bufferOffset;
|
||||||
|
|
||||||
int m_frameCounter{0}, m_callbacksCounter{0}, m_samplesCounter{0};
|
int m_frameCounter{0}, m_callbacksCounter{0}, m_samplesCounter{0};
|
||||||
QElapsedTimer m_statsTimer;
|
QElapsedTimer m_statsTimer;
|
||||||
QElapsedTimer m_bufferTimer;
|
QElapsedTimer m_bufferTimer;
|
||||||
|
qint64 m_lastTime;
|
||||||
|
|
||||||
int m_redrawTimerId;
|
int m_redrawTimerId;
|
||||||
QPointF m_lastPoint;
|
QPointF m_lastPoint;
|
||||||
size_t m_lastFrame;
|
|
||||||
QPixmap m_pixmap;
|
QPixmap m_pixmap;
|
||||||
void darkenFrame();
|
void darkenFrame();
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user