diff --git a/src/main.cpp b/src/main.cpp index 9617964..d83a4ba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,18 +24,17 @@ int main(int argc, char* argv[]) { NoteQueue queue = NoteQueue(); ScopeBuffer scopeBuffer = ScopeBuffer(512); KeyboardController keyboard(&config, &logger, &queue); - Synth synth(&config, &logger, nullptr, &queue); + Synth synth(&config, &logger, &scopeBuffer, &queue); // audio synthesizer doohickey AudioEngine audioEngine = AudioEngine(&config, &logger, &synth); 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); + engine.rootContext()->setContextProperty("scopeBuffer", &scopeBuffer); // load qml //engine.loadFromModule("sonobulus", "Main"); diff --git a/src/synth/Instrument.cpp b/src/synth/Instrument.cpp index 09ba614..6f46d52 100644 --- a/src/synth/Instrument.cpp +++ b/src/synth/Instrument.cpp @@ -31,7 +31,10 @@ float Instrument::process(bool& scopeTrigger) { if(!active_ && envelope_ > 0.0f) envelope_ -= 0.01f; phase_ += phaseIncrement_; - if(phase_ > 2.0f * pi) phase_ -= 2.0f * pi; + if(phase_ > 2.0f * pi) { + phase_ -= 2.0f * pi; + scopeTrigger = true; + } if(!isActive()) return 0.0f; return sin(phase_) * envelope_; diff --git a/src/synth/Scope.cpp b/src/synth/Scope.cpp index 4ccebed..49da767 100644 --- a/src/synth/Scope.cpp +++ b/src/synth/Scope.cpp @@ -3,6 +3,10 @@ #include +ScopeBuffer::ScopeBuffer(QObject *parent) : QObject(parent) { + +} + ScopeBuffer::ScopeBuffer(size_t size) : buffer_(size) { } @@ -27,21 +31,45 @@ Scope::Scope(QQuickItem *parent) : QQuickPaintedItem(parent) { setAntialiasing(true); } +void Scope::setBuffer(ScopeBuffer* scopeBuffer) { + scopeBuffer_ = scopeBuffer; + buffer_ = std::vector(scopeBuffer_->size()); +} + void Scope::paint(QPainter *painter) { - //std::cout << "onPaint" << std::endl; + while(scopeBuffer_->spinlock()) { + // wait + } - static float phase = 0.0f; - phase += 0.1f; - float sample = sin(phase); + if(scopeBuffer_ != nullptr) scopeBuffer_->read(buffer_); - QPen pen(Qt::blue, 4); + // TODO: scale to max amplitude ? + float maxAmp = 1.0f; + /* + for(float s : buffer_) { + maxAmp = std::max(maxAmp, std::abs(s)); + } + */ + + float scaleY = (height() * 0.45f) / maxAmp; + float midY = height() / 2.0f; + + painter->fillRect(0, 0, width(), height(), QColor(20, 20, 20)); + + QPen pen; + pen.setWidthF(3.0f); + QColor green(50, 255, 70); + pen.setColor(green); 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()); - + for(size_t i = 1; i < buffer_.size(); i++) { + painter->drawLine( + i * width() / buffer_.size(), + midY + buffer_[i-1] * scaleY, + i * width() / buffer_.size(), + midY + buffer_[i] * scaleY + ); + } } diff --git a/src/synth/Scope.hpp b/src/synth/Scope.hpp index c8eca1a..f3b8826 100644 --- a/src/synth/Scope.hpp +++ b/src/synth/Scope.hpp @@ -8,10 +8,13 @@ #include #include -class ScopeBuffer { +class ScopeBuffer : public QObject { + + Q_OBJECT // needed to attach to a qml component public: + explicit ScopeBuffer(QObject* parent = nullptr); ScopeBuffer(size_t size); ~ScopeBuffer() = default; @@ -22,7 +25,8 @@ public: void setWavelength(size_t wavelength) { wavelength_ = wavelength; } size_t trigger() { return trigger_; } size_t wavelength() { return wavelength_; } - void spinlock(bool lock) { spinlock_ = lock; } + void spinlock(bool lock) { spinlock_.store(lock, std::memory_order_relaxed); } + bool spinlock() { return spinlock_.load(std::memory_order_relaxed); } size_t size() { return buffer_.size(); } private: @@ -33,20 +37,21 @@ private: size_t trigger_ = 0; size_t wavelength_ = 400; - bool spinlock_ = false; + std::atomic spinlock_ = false; }; class Scope : public QQuickPaintedItem { Q_OBJECT - QML_ELEMENT + Q_PROPERTY(ScopeBuffer* buffer WRITE setBuffer) public: explicit Scope(QQuickItem* parent = nullptr); void paint(QPainter* painter) override; - void setBuffer(ScopeBuffer* scopeBuffer) { scopeBuffer_ = scopeBuffer; } + + void setBuffer(ScopeBuffer* scopeBuffer); std::vector buffer_; ScopeBuffer* scopeBuffer_; diff --git a/src/synth/Synth.cpp b/src/synth/Synth.cpp index 154aa52..b16d9c2 100644 --- a/src/synth/Synth.cpp +++ b/src/synth/Synth.cpp @@ -28,18 +28,22 @@ void Synth::process(float* out, size_t nFrames) { handleNoteEvent(noteEvent); } - // size_t lowestVoice = 0; - // float lowestFreq = 100000.0f; - // for(size_t i = 0; i < voices_.size(); i++) { - // if(!voices_[i].isActive()) continue; - // float currentFreq = voices_[i].frequency(); - // if(currentFreq < lowestFreq) { - // lowestVoice = i; - // lowestFreq = currentFreq; - // } - // } + size_t lowestVoice = 0; + float lowestFreq = 100000.0f; + for(size_t i = 0; i < voices_.size(); i++) { + if(!voices_[i].isActive()) continue; + float currentFreq = voices_[i].frequency(); + if(currentFreq < lowestFreq) { + lowestVoice = i; + lowestFreq = currentFreq; + } + } + + scope_->spinlock(true); float sampleOut = 0.0f; + bool triggered = false; + bool once = false; for(size_t i = 0; i < nFrames; i++) { float mix = 0.0f; @@ -47,7 +51,7 @@ void Synth::process(float* out, size_t nFrames) { bool temp = false; //if(!voices_[j].isActive()) continue; mix += voices_[j].process(temp); - // if(j == lowestVoice) triggered = temp; + if(j == lowestVoice) triggered = temp; } mix = tanh(mix/4.0f); // prevent clipping @@ -55,14 +59,15 @@ void Synth::process(float* out, size_t nFrames) { out[2*i] = sampleOut; out[2*i+1] = sampleOut; - // if(scope_) scope_->push(sampleOut); - // if(triggered && !once) { - // scope_->setTrigger(i); - // once = true; - // } - + if(scope_) scope_->push(sampleOut); + if(triggered && !once) { + scope_->setTrigger(i); + once = true; + } } + scope_->spinlock(false); + } diff --git a/src/synth/Synth.hpp b/src/synth/Synth.hpp index 80f48cd..7fdeb74 100644 --- a/src/synth/Synth.hpp +++ b/src/synth/Synth.hpp @@ -32,7 +32,7 @@ private: ConfigService* config_; LoggerService* logger_; - ScopeBuffer* scope_ = nullptr; + ScopeBuffer* scope_;; NoteQueue* noteQueue_; }; diff --git a/src/synth/Voice.hpp b/src/synth/Voice.hpp index 9974965..9484580 100644 --- a/src/synth/Voice.hpp +++ b/src/synth/Voice.hpp @@ -21,6 +21,7 @@ public: float process(bool& scopeTrigger); uint8_t note() { return note_; } + float frequency() { return noteToFrequency(note_); } private: diff --git a/ui/Main.qml b/ui/Main.qml index 347a462..74dc1ba 100644 --- a/ui/Main.qml +++ b/ui/Main.qml @@ -63,6 +63,8 @@ ApplicationWindow { repeat: true onTriggered: scope.update() } + + buffer: scopeBuffer } }