diff --git a/Makefile b/Makefile index 0e0d82b..2d45683 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ DEBUG_FLAGS=-O0 -ggdb -fsanitize=undefined CXX=g++ LDFLAGS=-L./lib/midi/ -LDLIBS=-lmidi-alsa -lasound -lreadline +LDLIBS=-lmidi-alsa -lasound -lpthread Obj= \ context.o \ diff --git a/src/interpreter.cc b/src/interpreter.cc index 233894c..957dd76 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -5,6 +5,46 @@ #include #include +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 + void register_callback(std::function &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 static inline bool typecheck(std::vector 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"); env = Env::global = Env::make(); } + { // MIDI input callbacks initialization + callbacks = std::make_unique(); + } { // Global default functions initialization auto &global = *Env::global; diff --git a/src/main.cc b/src/main.cc index 9c4612f..0cbc6c9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -79,10 +80,12 @@ struct Runner midi::ALSA alsa; Interpreter interpreter; + std::thread midi_input_event_loop; /// Setup interpreter and midi connection with given port Runner(std::string input_port, std::string output_port) : alsa("musique") + , interpreter{} { assert(the == nullptr, "Only one instance of runner is supported"); the = this; @@ -92,8 +95,18 @@ struct Runner alsa.init_sequencer(); interpreter.midi_connection = &alsa; } - if (output_port.size()) alsa.connect_output(output_port); - if (input_port.size()) alsa.connect_input(input_port); + if (output_port.size()) { + 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 args) -> Result { for (auto it = args.begin(); it != args.end(); ++it) { @@ -111,6 +124,11 @@ struct Runner Runner& operator=(Runner const&) = delete; Runner& operator=(Runner &&) = delete; + void handle_midi_event_loop() + { + alsa.input_event_loop(); + } + /// Run given source Result run(std::string_view source, std::string_view filename, bool output = false) { diff --git a/src/musique.hh b/src/musique.hh index ff63ff2..7f148f9 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -920,6 +920,10 @@ struct Interpreter /// There is always at least one context std::vector context_stack; + struct Incoming_Midi_Callbacks; + std::unique_ptr callbacks; + void register_callbacks(); + Interpreter(); ~Interpreter(); Interpreter(Interpreter const&) = delete;