diff --git a/mainwindow.cpp b/mainwindow.cpp index 400eb7b..d8df364 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -18,7 +18,7 @@ namespace { 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 }; @@ -125,8 +125,8 @@ MainWindow::MainWindow(QWidget *parent) : auto layout = new QFormLayout(widget); { auto input = new QDoubleSpinBox; - input->setRange(0, 1.0); - input->setSingleStep(0.1); + input->setRange(0, 10000.0); + input->setSingleStep(1.0); input->setValue(m_ui->widget->afterglow()); connect(input, qOverload(&QDoubleSpinBox::valueChanged), m_ui->widget, &OsciWidget::setAfterglow); layout->addRow(tr("Afterglow:"), input); diff --git a/osciwidget.cpp b/osciwidget.cpp index 6891231..4c81d10 100644 --- a/osciwidget.cpp +++ b/osciwidget.cpp @@ -16,11 +16,12 @@ qint32 framesForDuration(qint64 duration){ OsciWidget::OsciWidget(QWidget *parent) : QOpenGLWidget{parent} + , m_bufferOffset{m_buffer.begin()} + , m_lastTime{0} , m_redrawTimerId(startTimer(1000/m_fps, Qt::PreciseTimer)) - , m_lastFrame{0} { - m_bufferTimer.start(); m_statsTimer.start(); + m_bufferTimer.start(); } int OsciWidget::lightspeed() const @@ -39,9 +40,11 @@ void OsciWidget::setFps(int fps) void OsciWidget::setAfterglow(float afterglow) { - m_afterglow = afterglow; + m_decayTime = afterglow; // percentage of the image that should be visible after one second // 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) @@ -57,7 +60,20 @@ void OsciWidget::renderSamples(const SamplePair *begin, const SamplePair *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_bufferOffset = m_buffer.begin() + offset; //m_bufferTimer.restart(); //qDebug() << m_statsTimer.elapsed(); } @@ -69,7 +85,7 @@ void OsciWidget::paintEvent(QPaintEvent *event) m_frameCounter++; 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_callbacksCounter = 0; m_samplesCounter = 0; @@ -86,15 +102,18 @@ void OsciWidget::darkenFrame() painter.setCompositionMode(QPainter::CompositionMode_Multiply); 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(255 * pow(exp(-1), 1000.0/m_decayTime/m_fps)); + qDebug() << afterglowColor; painter.setBrush(QColor(afterglowColor, afterglowColor, afterglowColor)); painter.drawRect(m_pixmap.rect()); + //m_pixmap.fill(Qt::black); } void OsciWidget::updateDrawBuffer() { - // Workaround for flickering (do not update, when there is no new data) - //if(m_buffer.empty()) return; + // If there is no new data do not update + if(m_buffer.empty()) return; if (m_pixmap.size() != size()) { @@ -114,17 +133,18 @@ void OsciWidget::updateDrawBuffer() pen.setColor(QColor(0, 255, 0)); painter.setPen(pen); - qint64 samplesUsed = 0; - double timeConstant = log(m_afterglow); - auto duration = m_bufferTimer.elapsed()*1000; - qint32 framesCnt = framesForDuration(duration); - //qDebug() << framesCnt << duration; + // persistance time is the time it needs to decay to 1/e ~ 36,7% - 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]; - double time = (m_buffer.size() - samplesUsed) / 44100.0; + const auto &frame = *m_bufferOffset; const QPointF p{ float(frame.first) / std::numeric_limits::max(), @@ -132,8 +152,14 @@ void OsciWidget::updateDrawBuffer() }; 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 beamDecay = exp(time*timeConstant); + + + double time = 1000.0 * std::distance(m_bufferOffset, bufferEnd) / 44100.0; + auto beamDecay = exp(-time/m_decayTime); //qDebug() << time << beamDecay; painter.setOpacity(beamDecay*beamOpacity); @@ -141,10 +167,7 @@ void OsciWidget::updateDrawBuffer() painter.drawLine(m_lastPoint, p); m_lastPoint = p; - ++samplesUsed; } - - //m_buffer.clear(); } void OsciWidget::timerEvent(QTimerEvent *event) diff --git a/osciwidget.h b/osciwidget.h index cd9fe9f..297ed1a 100644 --- a/osciwidget.h +++ b/osciwidget.h @@ -21,7 +21,7 @@ public: float factor() const { return m_factor; } int fps() const { return m_fps; } - float afterglow() const { return m_afterglow; } + float afterglow() const { return m_decayTime; } int lightspeed() const; signals: @@ -44,19 +44,20 @@ private: private: float m_factor{1.f}; - int m_fps{30}; - float m_afterglow{0.01}; + int m_fps{60}; + float m_decayTime{25.0}; float m_lightspeed{35.f}; std::vector m_buffer; + std::vector::iterator m_bufferOffset; int m_frameCounter{0}, m_callbacksCounter{0}, m_samplesCounter{0}; QElapsedTimer m_statsTimer; QElapsedTimer m_bufferTimer; + qint64 m_lastTime; int m_redrawTimerId; QPointF m_lastPoint; - size_t m_lastFrame; QPixmap m_pixmap; void darkenFrame(); };