allow audio engine to react to keyboard inputs
This commit is contained in:
@@ -75,7 +75,8 @@ struct KeymapConfig {
|
|||||||
if(item.getType() == libconfig::Setting::TypeString) note = std::string(item.c_str());
|
if(item.getType() == libconfig::Setting::TypeString) note = std::string(item.c_str());
|
||||||
|
|
||||||
int8_t noteId = static_cast<int>(notesGroup[note]) % INT8_MAX;
|
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);
|
keymap.emplace(keyId, noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
22
src/main.cpp
22
src/main.cpp
@@ -17,8 +17,20 @@ int main(int argc, char* argv[]) {
|
|||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
QQmlApplicationEngine engine;
|
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
|
// attach backend gui components
|
||||||
qmlRegisterType<TimerComponent>("AppDemo", 1, 0, "TimerComponent");
|
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)
|
// adds the TimerComponent type (exposed in qml as "TimerComponent") to the module named"AppDemo" (numbers mean version 1.0)
|
||||||
|
|
||||||
// load qml
|
// load qml
|
||||||
@@ -31,16 +43,6 @@ int main(int argc, char* argv[]) {
|
|||||||
return -1;
|
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
|
// execute app
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numbers>
|
#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) {
|
if(audioDevice_.getDeviceCount() < 1) {
|
||||||
std::cout << "No audio devices found" << std::endl;
|
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) {
|
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++) {
|
for(size_t i = 0; i < nFrames; i++) {
|
||||||
|
|
||||||
// simulate a sine wave
|
// simulate a sine wave
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <RtAudio.h>
|
#include <RtAudio.h>
|
||||||
|
|
||||||
#include "LoggerService.hpp"
|
#include "LoggerService.hpp"
|
||||||
|
#include "ConfigService.hpp"
|
||||||
|
#include "NoteQueue.hpp"
|
||||||
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
@@ -18,7 +20,7 @@ class AudioEngine {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AudioEngine(LoggerService* logger);
|
AudioEngine(ConfigService* config, LoggerService* logger, NoteQueue* noteQueue);
|
||||||
~AudioEngine();
|
~AudioEngine();
|
||||||
|
|
||||||
bool start();
|
bool start();
|
||||||
@@ -40,5 +42,7 @@ private:
|
|||||||
float phase_ = 0.0f;
|
float phase_ = 0.0f;
|
||||||
|
|
||||||
LoggerService* logger_;
|
LoggerService* logger_;
|
||||||
|
ConfigService* config_;
|
||||||
|
NoteQueue* noteQueue_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
// #include <yaml-cpp/yaml.h>
|
// #include <yaml-cpp/yaml.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
KeyboardController::KeyboardController(QObject* parent) : QObject(parent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
KeyboardController::KeyboardController(ConfigService* config, LoggerService* logger, NoteQueue* queue) : config_(config), logger_(logger), queue_(queue) {
|
KeyboardController::KeyboardController(ConfigService* config, LoggerService* logger, NoteQueue* queue) : config_(config), logger_(logger), queue_(queue) {
|
||||||
|
|
||||||
// load keymap from config service
|
// load keymap from config service
|
||||||
@@ -15,11 +19,10 @@ KeyboardController::KeyboardController(ConfigService* config, LoggerService* log
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardController::handleKeyPress(QKeyEvent* e) {
|
void KeyboardController::keyDownEvent(int key, int modifiers, const QString& text) {
|
||||||
if (e->isAutoRepeat()) return;
|
|
||||||
|
|
||||||
auto it = keymap_.find(e->key());
|
auto it = configuration_.keymap.find(key);
|
||||||
if (it == keymap_.end()) return;
|
if (it == configuration_.keymap.end()) return;
|
||||||
|
|
||||||
queue_->push({
|
queue_->push({
|
||||||
NoteEventType::NoteOn,
|
NoteEventType::NoteOn,
|
||||||
@@ -27,13 +30,13 @@ void KeyboardController::handleKeyPress(QKeyEvent* e) {
|
|||||||
0.8f,
|
0.8f,
|
||||||
std::chrono::high_resolution_clock::now()
|
std::chrono::high_resolution_clock::now()
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardController::handleKeyRelease(QKeyEvent* e) {
|
void KeyboardController::keyUpEvent(int key, int modifiers, const QString& text) {
|
||||||
if (e->isAutoRepeat()) return;
|
|
||||||
|
|
||||||
auto it = keymap_.find(e->key());
|
auto it = configuration_.keymap.find(key);
|
||||||
if (it == keymap_.end()) return;
|
if (it == configuration_.keymap.end()) return;
|
||||||
|
|
||||||
queue_->push({
|
queue_->push({
|
||||||
NoteEventType::NoteOff,
|
NoteEventType::NoteOff,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// the keyboard controller acts as an instrument input device for creating note events from a computer keyboard
|
// the keyboard controller acts as an instrument input device for creating note events from a computer keyboard
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -10,14 +11,17 @@
|
|||||||
#include "LoggerService.hpp"
|
#include "LoggerService.hpp"
|
||||||
|
|
||||||
// The keyboardcontroller handles user inputs from a keyboard and maps them to note events
|
// The keyboardcontroller handles user inputs from a keyboard and maps them to note events
|
||||||
class KeyboardController {
|
class KeyboardController : public QObject {
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit KeyboardController(ConfigService* config, LoggerService* logger, NoteQueue* queue);
|
explicit KeyboardController(QObject* parent = nullptr);
|
||||||
|
KeyboardController(ConfigService* config, LoggerService* logger, NoteQueue* queue);
|
||||||
~KeyboardController() = default;
|
~KeyboardController() = default;
|
||||||
|
|
||||||
void handleKeyPress(QKeyEvent* e);
|
Q_INVOKABLE void keyDownEvent(int key, int modifiers, const QString& text);
|
||||||
void handleKeyRelease(QKeyEvent* e);
|
Q_INVOKABLE void keyUpEvent(int key, int modifiers, const QString& text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -26,8 +30,6 @@ private:
|
|||||||
LoggerService* logger_;
|
LoggerService* logger_;
|
||||||
|
|
||||||
// keymap is key -> midi note id
|
// keymap is key -> midi note id
|
||||||
std::unordered_map<int32_t, uint8_t> keymap_;
|
|
||||||
|
|
||||||
KeymapConfig configuration_;
|
KeymapConfig configuration_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
#define SYNTH_NOTE_QUEUE_SIZE 128
|
#define SYNTH_NOTE_QUEUE_SIZE 128
|
||||||
|
|
||||||
enum class NoteEventType {
|
enum NoteEventType {
|
||||||
NoteOn,
|
NoteOn = 0,
|
||||||
NoteOff
|
NoteOff
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
19
ui/Main.qml
19
ui/Main.qml
@@ -30,4 +30,23 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
Keys.onPressed: (event) => {
|
||||||
|
if(!event.isAutoRepeat) {
|
||||||
|
keyboardController.keyDownEvent(event.key, event.modifiers, event.text)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onReleased: (event) => {
|
||||||
|
if(!event.isAutoRepeat) {
|
||||||
|
keyboardController.keyUpEvent(event.key, event.modifiers, event.text)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user