Compare commits
2 Commits
9f79f11a19
...
dbc1db37e1
| Author | SHA1 | Date | |
|---|---|---|---|
| dbc1db37e1 | |||
| fcc24c5e3e |
@@ -27,6 +27,11 @@ FetchContent_Declare(
|
|||||||
GIT_REPOSITORY https://github.com/hyperrealm/libconfig.git
|
GIT_REPOSITORY https://github.com/hyperrealm/libconfig.git
|
||||||
GIT_TAG v1.8.2
|
GIT_TAG v1.8.2
|
||||||
)
|
)
|
||||||
|
FetchContent_Declare(
|
||||||
|
eigen
|
||||||
|
GIT_REPOSITORY git@gitlab.com:libeigen/eigen.git
|
||||||
|
GIT_TAG 5.0
|
||||||
|
)
|
||||||
FetchContent_MakeAvailable(rtaudio)
|
FetchContent_MakeAvailable(rtaudio)
|
||||||
FetchContent_MakeAvailable(rtmidi)
|
FetchContent_MakeAvailable(rtmidi)
|
||||||
FetchContent_MakeAvailable(libconfig)
|
FetchContent_MakeAvailable(libconfig)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ to produce somewhat well-sounding instruments and music performance.
|
|||||||
to external midi controllers/apps (like musescore)
|
to external midi controllers/apps (like musescore)
|
||||||
- [ ] Create a UI scope to visualize the synthesized composite waveform
|
- [ ] Create a UI scope to visualize the synthesized composite waveform
|
||||||
- [ ] Check cross-platform combatibility for Windows & Linux, especially MIDI interfacing
|
- [ ] Check cross-platform combatibility for Windows & Linux, especially MIDI interfacing
|
||||||
- [ ] Checkpoint at a rudimentary keyboard instrument producing a basic sine output
|
- [x] Checkpoint at a rudimentary keyboard instrument producing a basic sine output
|
||||||
- [ ] Will flesh out future goals when I do the math on how complicated implementing
|
- [ ] Will flesh out future goals when I do the math on how complicated implementing
|
||||||
state-space modelling in c++ is
|
state-space modelling in c++ is
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include "ConfigService.hpp"
|
#include "ConfigService.hpp"
|
||||||
#include "synth/AudioEngine.hpp"
|
#include "synth/AudioEngine.hpp"
|
||||||
#include "synth/KeyboardController.hpp"
|
#include "synth/KeyboardController.hpp"
|
||||||
//#include "synth/ScopeBuffer.hpp"
|
#include "synth/Scope.hpp"
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ int main(int argc, char* argv[]) {
|
|||||||
ConfigService config = ConfigService("config/sonobulus.cfg");
|
ConfigService config = ConfigService("config/sonobulus.cfg");
|
||||||
LoggerService logger = LoggerService(&config, "Engine");
|
LoggerService logger = LoggerService(&config, "Engine");
|
||||||
NoteQueue queue = NoteQueue();
|
NoteQueue queue = NoteQueue();
|
||||||
//ScopeBuffer scope = ScopeBuffer();
|
ScopeBuffer scopeBuffer = ScopeBuffer(512);
|
||||||
KeyboardController keyboard(&config, &logger, &queue);
|
KeyboardController keyboard(&config, &logger, &queue);
|
||||||
Synth synth(&config, &logger, nullptr, &queue);
|
Synth synth(&config, &logger, nullptr, &queue);
|
||||||
|
|
||||||
@@ -31,9 +31,11 @@ int main(int argc, char* argv[]) {
|
|||||||
audioEngine.start();
|
audioEngine.start();
|
||||||
|
|
||||||
// attach backend gui components
|
// attach backend gui components
|
||||||
|
//Scope scope; //scope.setBuffer(&scopeBuffer);
|
||||||
qmlRegisterType<TimerComponent>("AppDemo", 1, 0, "TimerComponent");
|
qmlRegisterType<TimerComponent>("AppDemo", 1, 0, "TimerComponent");
|
||||||
|
//qmlRegisterSingletonInstance("AppDemo", 1, 0, "Scope", &scope);
|
||||||
|
qmlRegisterType<Scope>("AppDemo", 1, 0, "Scope");
|
||||||
engine.rootContext()->setContextProperty("keyboardController", &keyboard);
|
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
|
// load qml
|
||||||
//engine.loadFromModule("sonobulus", "Main");
|
//engine.loadFromModule("sonobulus", "Main");
|
||||||
|
|||||||
@@ -10,11 +10,10 @@ Instrument::Instrument(ConfigService* config, LoggerService* logger) :
|
|||||||
|
|
||||||
void Instrument::noteOn(float frequency, float velocity) {
|
void Instrument::noteOn(float frequency, float velocity) {
|
||||||
|
|
||||||
// std::string msg = "NoteOn Frequency = " + std::to_string(frequency);
|
|
||||||
// if(logger_ != nullptr) logger_->log("Instrument", LogFlag::Debug, msg);
|
|
||||||
|
|
||||||
phaseIncrement_ = 2.0f * pi * frequency / sampleRate_;
|
phaseIncrement_ = 2.0f * pi * frequency / sampleRate_;
|
||||||
|
|
||||||
|
envelope_ += 0.01f; // so it triggers as active
|
||||||
|
|
||||||
active_ = true;
|
active_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,14 +22,18 @@ void Instrument::noteOff() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Instrument::isActive() {
|
bool Instrument::isActive() {
|
||||||
return active_;
|
return (envelope_ > 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Instrument::process(bool& scopeTrigger) {
|
float Instrument::process(bool& scopeTrigger) {
|
||||||
|
|
||||||
|
if(active_ && envelope_ < 1.0f) envelope_ += 0.01f;
|
||||||
|
if(!active_ && envelope_ > 0.0f) envelope_ -= 0.01f;
|
||||||
|
|
||||||
phase_ += phaseIncrement_;
|
phase_ += phaseIncrement_;
|
||||||
if(phase_ > 2.0f * pi) phase_ -= 2.0f * pi;
|
if(phase_ > 2.0f * pi) phase_ -= 2.0f * pi;
|
||||||
|
|
||||||
return sin(phase_);
|
if(!isActive()) return 0.0f;
|
||||||
|
return sin(phase_) * envelope_;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,5 +32,6 @@ private:
|
|||||||
static constexpr float pi = 3.14159265358979323846f;
|
static constexpr float pi = 3.14159265358979323846f;
|
||||||
float phase_ = 0.0f;
|
float phase_ = 0.0f;
|
||||||
float phaseIncrement_ = 0.0f;
|
float phaseIncrement_ = 0.0f;
|
||||||
|
float envelope_ = 0.0f;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
#include "Scope.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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<float>& 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());
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtQuick/QQuickPaintedItem>
|
||||||
|
#include <QtGui/QPainter>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
@@ -14,14 +18,37 @@ public:
|
|||||||
void push(float sample);
|
void push(float sample);
|
||||||
void read(std::vector<float>& out);
|
void read(std::vector<float>& 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:
|
private:
|
||||||
|
|
||||||
std::vector<float> buffer_;
|
std::vector<float> buffer_;
|
||||||
std::atomic<size_t> writeIndex_{0};
|
std::atomic<size_t> writeIndex_{0};
|
||||||
|
|
||||||
size_t trigger_ = 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<float> buffer_;
|
||||||
|
ScopeBuffer* scopeBuffer_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ void Synth::process(float* out, size_t nFrames) {
|
|||||||
float mix = 0.0f;
|
float mix = 0.0f;
|
||||||
for(size_t j = 0; j < voices_.size(); j++) {
|
for(size_t j = 0; j < voices_.size(); j++) {
|
||||||
bool temp = false;
|
bool temp = false;
|
||||||
if(!voices_[j].isActive()) continue;
|
//if(!voices_[j].isActive()) continue;
|
||||||
mix += voices_[j].process(temp);
|
mix += voices_[j].process(temp);
|
||||||
// if(j == lowestVoice) triggered = temp;
|
// if(j == lowestVoice) triggered = temp;
|
||||||
}
|
}
|
||||||
|
|||||||
20
ui/Main.qml
20
ui/Main.qml
@@ -6,8 +6,8 @@ import AppDemo
|
|||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
visible: true
|
visible: true
|
||||||
width: 600
|
width: 1200
|
||||||
height: 400
|
height: 800
|
||||||
title: "sonobulus"
|
title: "sonobulus"
|
||||||
|
|
||||||
TimerComponent {
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user