Infrastructure for incoming MIDI messages ready

This commit is contained in:
Robert Bendun 2022-06-19 00:27:02 +02:00
parent bf3f388acb
commit 7a3d211d09
4 changed files with 68 additions and 3 deletions

View File

@ -6,7 +6,7 @@ DEBUG_FLAGS=-O0 -ggdb -fsanitize=undefined
CXX=g++ CXX=g++
LDFLAGS=-L./lib/midi/ LDFLAGS=-L./lib/midi/
LDLIBS=-lmidi-alsa -lasound -lreadline LDLIBS=-lmidi-alsa -lasound -lpthread
Obj= \ Obj= \
context.o \ context.o \

View File

@ -5,6 +5,46 @@
#include <random> #include <random>
#include <thread> #include <thread>
struct Interpreter::Incoming_Midi_Callbacks
{
Value note_on{};
Value note_off{};
Incoming_Midi_Callbacks() = default;
Incoming_Midi_Callbacks(Incoming_Midi_Callbacks &&) = delete;
Incoming_Midi_Callbacks(Incoming_Midi_Callbacks const&) = delete;
Incoming_Midi_Callbacks& operator=(Incoming_Midi_Callbacks &&) = delete;
Incoming_Midi_Callbacks& operator=(Incoming_Midi_Callbacks const&) = delete;
void add_callbacks(midi::Connection &midi, Interpreter &interpreter)
{
register_callback(midi.note_on_callback, note_on, interpreter);
register_callback(midi.note_off_callback, note_off, interpreter);
}
template<typename ...T>
void register_callback(std::function<void(T...)> &target, Value &callback, Interpreter &i)
{
target = [interpreter = &i, callback = &callback](T ...source_args)
{
std::cout << "hello from callback!" << std::endl;
if (callback->type != Value::Type::Nil) {
auto result = (*callback)(*interpreter, { Value::from(Number(source_args))... });
// We discard this since callback is running in another thread.
(void) result;
}
};
}
};
void Interpreter::register_callbacks()
{
assert(callbacks != nullptr, "Interpreter constructor should initialize this field");
callbacks->add_callbacks(*midi_connection, *this);
}
/// Intrinsic implementation primitive providing a short way to check if arguments match required type signature /// Intrinsic implementation primitive providing a short way to check if arguments match required type signature
static inline bool typecheck(std::vector<Value> const& args, auto const& ...expected_types) static inline bool typecheck(std::vector<Value> const& args, auto const& ...expected_types)
{ {
@ -340,6 +380,9 @@ Interpreter::Interpreter()
assert(!bool(Env::global), "Only one instance of interpreter can be at one time"); assert(!bool(Env::global), "Only one instance of interpreter can be at one time");
env = Env::global = Env::make(); env = Env::global = Env::make();
} }
{ // MIDI input callbacks initialization
callbacks = std::make_unique<Interpreter::Incoming_Midi_Callbacks>();
}
{ // Global default functions initialization { // Global default functions initialization
auto &global = *Env::global; auto &global = *Env::global;

View File

@ -4,6 +4,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <span> #include <span>
#include <thread>
#include <musique.hh> #include <musique.hh>
#include <midi.hh> #include <midi.hh>
@ -79,10 +80,12 @@ struct Runner
midi::ALSA alsa; midi::ALSA alsa;
Interpreter interpreter; Interpreter interpreter;
std::thread midi_input_event_loop;
/// Setup interpreter and midi connection with given port /// Setup interpreter and midi connection with given port
Runner(std::string input_port, std::string output_port) Runner(std::string input_port, std::string output_port)
: alsa("musique") : alsa("musique")
, interpreter{}
{ {
assert(the == nullptr, "Only one instance of runner is supported"); assert(the == nullptr, "Only one instance of runner is supported");
the = this; the = this;
@ -92,8 +95,18 @@ struct Runner
alsa.init_sequencer(); alsa.init_sequencer();
interpreter.midi_connection = &alsa; interpreter.midi_connection = &alsa;
} }
if (output_port.size()) alsa.connect_output(output_port); if (output_port.size()) {
if (input_port.size()) alsa.connect_input(input_port); std::cout << "Connected MIDI output to port " << output_port << ". Ready to play!" << std::endl;
alsa.connect_output(output_port);
}
if (input_port.size()) {
std::cout << "Connected MIDI input to port " << input_port << ". Ready for incoming messages!" << std::endl;
alsa.connect_input(input_port);
}
if (alsa_go) {
interpreter.register_callbacks();
midi_input_event_loop = std::thread([this] { handle_midi_event_loop(); });
}
Env::global->force_define("say", +[](Interpreter&, std::vector<Value> args) -> Result<Value> { Env::global->force_define("say", +[](Interpreter&, std::vector<Value> args) -> Result<Value> {
for (auto it = args.begin(); it != args.end(); ++it) { for (auto it = args.begin(); it != args.end(); ++it) {
@ -111,6 +124,11 @@ struct Runner
Runner& operator=(Runner const&) = delete; Runner& operator=(Runner const&) = delete;
Runner& operator=(Runner &&) = delete; Runner& operator=(Runner &&) = delete;
void handle_midi_event_loop()
{
alsa.input_event_loop();
}
/// Run given source /// Run given source
Result<void> run(std::string_view source, std::string_view filename, bool output = false) Result<void> run(std::string_view source, std::string_view filename, bool output = false)
{ {

View File

@ -920,6 +920,10 @@ struct Interpreter
/// There is always at least one context /// There is always at least one context
std::vector<Context> context_stack; std::vector<Context> context_stack;
struct Incoming_Midi_Callbacks;
std::unique_ptr<Incoming_Midi_Callbacks> callbacks;
void register_callbacks();
Interpreter(); Interpreter();
~Interpreter(); ~Interpreter();
Interpreter(Interpreter const&) = delete; Interpreter(Interpreter const&) = delete;