From dbc1db37e1eee6a17f8f0d130b9945b59a909ebc Mon Sep 17 00:00:00 2001 From: homeburger Date: Sat, 13 Jun 2026 16:36:33 -0500 Subject: [PATCH] scope checkpoint --- src/main.cpp | 8 +++++--- src/synth/Scope.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++ src/synth/Scope.hpp | 31 ++++++++++++++++++++++++++++-- ui/Main.qml | 20 +++++++++++++++++-- 4 files changed, 99 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index dc0c972..9617964 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,7 @@ #include "ConfigService.hpp" #include "synth/AudioEngine.hpp" #include "synth/KeyboardController.hpp" -//#include "synth/ScopeBuffer.hpp" +#include "synth/Scope.hpp" int main(int argc, char* argv[]) { @@ -22,7 +22,7 @@ int main(int argc, char* argv[]) { ConfigService config = ConfigService("config/sonobulus.cfg"); LoggerService logger = LoggerService(&config, "Engine"); NoteQueue queue = NoteQueue(); - //ScopeBuffer scope = ScopeBuffer(); + ScopeBuffer scopeBuffer = ScopeBuffer(512); KeyboardController keyboard(&config, &logger, &queue); Synth synth(&config, &logger, nullptr, &queue); @@ -31,9 +31,11 @@ int main(int argc, char* argv[]) { audioEngine.start(); // attach backend gui components + //Scope scope; //scope.setBuffer(&scopeBuffer); qmlRegisterType("AppDemo", 1, 0, "TimerComponent"); + //qmlRegisterSingletonInstance("AppDemo", 1, 0, "Scope", &scope); + qmlRegisterType("AppDemo", 1, 0, "Scope"); engine.rootContext()->setContextProperty("keyboardController", &keyboard); - // adds the TimerComponent type (exposed in qml as "TimerComponent") to the module named"AppDemo" (numbers mean version 1.0) // load qml //engine.loadFromModule("sonobulus", "Main"); diff --git a/src/synth/Scope.cpp b/src/synth/Scope.cpp index e69de29..4ccebed 100644 --- a/src/synth/Scope.cpp +++ b/src/synth/Scope.cpp @@ -0,0 +1,47 @@ + +#include "Scope.hpp" + +#include + +ScopeBuffer::ScopeBuffer(size_t size) : buffer_(size) { + +} + +void ScopeBuffer::push(float sample) { + + size_t w = writeIndex_.fetch_add(1, std::memory_order_relaxed); + buffer_[w % buffer_.size()] = sample; +} + +void ScopeBuffer::read(std::vector& out) { + + size_t w = writeIndex_.load(std::memory_order_relaxed); + for(size_t i = 0; i < buffer_.size(); i++) { + size_t idx = (w + trigger_ + i * wavelength_ / out.size()) % buffer_.size(); + out[i] = buffer_[idx]; + } + +} + +Scope::Scope(QQuickItem *parent) : QQuickPaintedItem(parent) { + setAntialiasing(true); +} + +void Scope::paint(QPainter *painter) { + + //std::cout << "onPaint" << std::endl; + + static float phase = 0.0f; + phase += 0.1f; + float sample = sin(phase); + + QPen pen(Qt::blue, 4); + painter->setPen(pen); + painter->setRenderHint(QPainter::Antialiasing); + + painter->drawRect(10, 10, width() - 20*sample, height() - 20*sample); + + painter->setPen(QPen(Qt::red, 2)); + painter->drawLine(0, 0, width(), height()); + +} diff --git a/src/synth/Scope.hpp b/src/synth/Scope.hpp index 904fb74..c8eca1a 100644 --- a/src/synth/Scope.hpp +++ b/src/synth/Scope.hpp @@ -1,6 +1,10 @@ #pragma once +#include +#include +#include + #include #include @@ -14,14 +18,37 @@ public: void push(float sample); void read(std::vector& out); + void setTrigger(size_t trigger) { trigger_ = trigger; } + void setWavelength(size_t wavelength) { wavelength_ = wavelength; } + size_t trigger() { return trigger_; } + size_t wavelength() { return wavelength_; } + void spinlock(bool lock) { spinlock_ = lock; } + size_t size() { return buffer_.size(); } + private: std::vector buffer_; std::atomic writeIndex_{0}; size_t trigger_ = 0; - uint32_t wavelgnth = 400; + size_t wavelength_ = 400; - bool spinLock_ = false; + bool spinlock_ = false; + +}; + +class Scope : public QQuickPaintedItem { + + Q_OBJECT + QML_ELEMENT + +public: + explicit Scope(QQuickItem* parent = nullptr); + + void paint(QPainter* painter) override; + void setBuffer(ScopeBuffer* scopeBuffer) { scopeBuffer_ = scopeBuffer; } + + std::vector buffer_; + ScopeBuffer* scopeBuffer_; }; diff --git a/ui/Main.qml b/ui/Main.qml index 8e4bdbe..347a462 100644 --- a/ui/Main.qml +++ b/ui/Main.qml @@ -6,8 +6,8 @@ import AppDemo ApplicationWindow { visible: true - width: 600 - height: 400 + width: 1200 + height: 800 title: "sonobulus" TimerComponent { @@ -49,4 +49,20 @@ ApplicationWindow { } } + Scope { + id: scope + + width: 600 + height: 300 + + // this timer triggers a constant update on the scope's canvas, calls its draw() each time + Timer { + id: updateTimer + interval: 16 // ~60 fps + running: true + repeat: true + onTriggered: scope.update() + } + } + }