allow audio engine to react to keyboard inputs

This commit is contained in:
2026-06-10 20:44:04 -05:00
parent 7231d948bc
commit 8eb5a619ad
8 changed files with 67 additions and 29 deletions

View File

@@ -75,7 +75,8 @@ struct KeymapConfig {
if(item.getType() == libconfig::Setting::TypeString) note = std::string(item.c_str());
int8_t noteId = static_cast<int>(notesGroup[note]) % INT8_MAX;
int32_t keyId = static_cast<int>(keysGroup[key]) % INT8_MAX;
int32_t keyId = static_cast<int>(keysGroup[key]) % INT32_MAX;
keymap.emplace(keyId, noteId);
}

View File

@@ -17,8 +17,20 @@ int main(int argc, char* argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// create app objects
ConfigService config = ConfigService("config/sonobulus.cfg");
LoggerService logger = LoggerService(&config, "Engine");
NoteQueue queue = NoteQueue();
KeyboardController keyboard(&config, &logger, &queue);
// audio synthesizer doohickey
AudioEngine audioEngine = AudioEngine(&config, &logger, &queue);
audioEngine.start();
// attach backend gui components
qmlRegisterType<TimerComponent>("AppDemo", 1, 0, "TimerComponent");
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
@@ -31,16 +43,6 @@ int main(int argc, char* argv[]) {
return -1;
}
// create app objects
ConfigService config = ConfigService("config/sonobulus.cfg");
LoggerService logger = LoggerService(&config, "Engine");
NoteQueue queue = NoteQueue();
KeyboardController keyboard = KeyboardController(&config, &logger, &queue);
// audio synthesizer doohickey
AudioEngine audioEngine = AudioEngine(&logger);
audioEngine.start();
// execute app
return app.exec();
}

View File

@@ -4,8 +4,9 @@
#include <iostream>
#include <cmath>
#include <numbers>
#include <string>
AudioEngine::AudioEngine(LoggerService* logger) : logger_(logger) {
AudioEngine::AudioEngine(ConfigService* config, LoggerService* logger, NoteQueue* noteQueue) : config_(config), logger_(logger), noteQueue_(noteQueue) {
if(audioDevice_.getDeviceCount() < 1) {
std::cout << "No audio devices found" << std::endl;
@@ -69,6 +70,12 @@ int32_t AudioEngine::audioCallback(void* outputBuffer, void* inputBuffer, uint32
int32_t AudioEngine::process(float* out, size_t nFrames) {
NoteEvent noteEvent;
while(noteQueue_->pop(noteEvent)) {
std::string msg = "[NoteEvent] Type: " + std::to_string(noteEvent.type) +" Velocity: " + std::to_string(noteEvent.velocity) + " Note:" + std::to_string(noteEvent.note);
logger_->log("Audio", LogFlag::Debug, msg);
}
for(size_t i = 0; i < nFrames; i++) {
// simulate a sine wave

View File

@@ -6,6 +6,8 @@
#include <RtAudio.h>
#include "LoggerService.hpp"
#include "ConfigService.hpp"
#include "NoteQueue.hpp"
#if defined(_WIN32)
@@ -18,7 +20,7 @@ class AudioEngine {
public:
AudioEngine(LoggerService* logger);
AudioEngine(ConfigService* config, LoggerService* logger, NoteQueue* noteQueue);
~AudioEngine();
bool start();
@@ -40,5 +42,7 @@ private:
float phase_ = 0.0f;
LoggerService* logger_;
ConfigService* config_;
NoteQueue* noteQueue_;
};

View File

@@ -5,6 +5,10 @@
// #include <yaml-cpp/yaml.h>
#include <filesystem>
KeyboardController::KeyboardController(QObject* parent) : QObject(parent) {
}
KeyboardController::KeyboardController(ConfigService* config, LoggerService* logger, NoteQueue* queue) : config_(config), logger_(logger), queue_(queue) {
// load keymap from config service
@@ -15,11 +19,10 @@ KeyboardController::KeyboardController(ConfigService* config, LoggerService* log
}
void KeyboardController::handleKeyPress(QKeyEvent* e) {
if (e->isAutoRepeat()) return;
void KeyboardController::keyDownEvent(int key, int modifiers, const QString& text) {
auto it = keymap_.find(e->key());
if (it == keymap_.end()) return;
auto it = configuration_.keymap.find(key);
if (it == configuration_.keymap.end()) return;
queue_->push({
NoteEventType::NoteOn,
@@ -27,13 +30,13 @@ void KeyboardController::handleKeyPress(QKeyEvent* e) {
0.8f,
std::chrono::high_resolution_clock::now()
});
}
void KeyboardController::handleKeyRelease(QKeyEvent* e) {
if (e->isAutoRepeat()) return;
void KeyboardController::keyUpEvent(int key, int modifiers, const QString& text) {
auto it = keymap_.find(e->key());
if (it == keymap_.end()) return;
auto it = configuration_.keymap.find(key);
if (it == configuration_.keymap.end()) return;
queue_->push({
NoteEventType::NoteOff,

View File

@@ -2,6 +2,7 @@
// the keyboard controller acts as an instrument input device for creating note events from a computer keyboard
#pragma once
#include <QObject>
#include <QKeyEvent>
#include <unordered_map>
@@ -10,14 +11,17 @@
#include "LoggerService.hpp"
// The keyboardcontroller handles user inputs from a keyboard and maps them to note events
class KeyboardController {
class KeyboardController : public QObject {
Q_OBJECT
public:
explicit KeyboardController(ConfigService* config, LoggerService* logger, NoteQueue* queue);
explicit KeyboardController(QObject* parent = nullptr);
KeyboardController(ConfigService* config, LoggerService* logger, NoteQueue* queue);
~KeyboardController() = default;
void handleKeyPress(QKeyEvent* e);
void handleKeyRelease(QKeyEvent* e);
Q_INVOKABLE void keyDownEvent(int key, int modifiers, const QString& text);
Q_INVOKABLE void keyUpEvent(int key, int modifiers, const QString& text);
private:
@@ -26,8 +30,6 @@ private:
LoggerService* logger_;
// keymap is key -> midi note id
std::unordered_map<int32_t, uint8_t> keymap_;
KeymapConfig configuration_;
};

View File

@@ -9,8 +9,8 @@
#define SYNTH_NOTE_QUEUE_SIZE 128
enum class NoteEventType {
NoteOn,
enum NoteEventType {
NoteOn = 0,
NoteOff
};