From e9d67178dabd728961570bec1d81cd5bb9b5ecf3 Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Fri, 14 Oct 2022 16:24:42 +0200 Subject: [PATCH] Added virtual port creation as default action. Restored port connection Removed input ports support --- CHANGELOG.md | 1 + musique/interpreter/builtin_functions.cc | 35 +------------- musique/interpreter/incoming_midi.hh | 60 ------------------------ musique/interpreter/interpreter.cc | 38 ++++----------- musique/interpreter/interpreter.hh | 7 +-- musique/main.cc | 30 +++--------- musique/midi/midi.hh | 12 ++--- musique/midi/rt_midi.cc | 19 +++----- 8 files changed, 30 insertions(+), 172 deletions(-) delete mode 100644 musique/interpreter/incoming_midi.hh diff --git a/CHANGELOG.md b/CHANGELOG.md index b708a3b..70ab677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Release package now with compiled Windows binary * `:load` REPL command to load Musique files inside Musique session. Allows for delayed file execution after a connection * `:quit` REPL command that mirrors `:exit` command +* Virtual MIDI output port creation as default action (--output connects to existing one) ### Changed diff --git a/musique/interpreter/builtin_functions.cc b/musique/interpreter/builtin_functions.cc index ab0e255..74f2af2 100644 --- a/musique/interpreter/builtin_functions.cc +++ b/musique/interpreter/builtin_functions.cc @@ -1,7 +1,6 @@ #include #include #include -#include #include #include @@ -12,13 +11,6 @@ #include #include -void Interpreter::register_callbacks() -{ - ensure(callbacks == nullptr, "This field should be uninitialized"); - callbacks = std::make_unique(); - callbacks->add_callbacks(*midi_connection, *this); -} - /// Check if type has index method template concept With_Index_Method = requires (T &t, Interpreter interpreter, usize position) { @@ -276,7 +268,7 @@ static std::optional action_play(Interpreter &i, Value v) template> static inline Result builtin_play(Interpreter &i, Container args) { - Try(ensure_midi_connection_available(i, Midi_Connection_Type::Output, "play")); + Try(ensure_midi_connection_available(i, "play")); auto previous_action = std::exchange(i.default_action, action_play); i.context_stack.push_back(i.context_stack.back()); @@ -298,7 +290,7 @@ static inline Result builtin_play(Interpreter &i, Container args) /// Play first argument while playing all others static Result builtin_par(Interpreter &i, std::vector args) { - Try(ensure_midi_connection_available(i, Midi_Connection_Type::Output, "par")); + Try(ensure_midi_connection_available(i, "par")); ensure(args.size() >= 1, "par only makes sense for at least one argument"); // TODO(assert) if (args.size() == 1) { @@ -911,28 +903,6 @@ static Result builtin_note_off(Interpreter &i, std::vector args) }; } -/// Add handler for incoming midi messages -static Result builtin_incoming(Interpreter &i, std::vector args) -{ - if (auto a = match(args)) { - auto& [symbol, fun] = *a; - if (symbol == "note_on" || symbol == "noteon") { - i.callbacks->note_on = std::move(args[1]); - } else if (symbol == "note_off" || symbol == "noteoff") { - i.callbacks->note_off = std::move(args[1]); - } else { - - } - return Value{}; - } - - return errors::Unsupported_Types_For { - .type = errors::Unsupported_Types_For::Function, - .name = "incoming", - .possibilities = { "(symbol, function) -> nil" } - }; -} - /// Interleaves arguments static Result builtin_mix(Interpreter &i, std::vector args) { @@ -999,7 +969,6 @@ void Interpreter::register_builtin_functions() global.force_define("for", builtin_for); global.force_define("hash", builtin_hash); global.force_define("if", builtin_if); - global.force_define("incoming", builtin_incoming); global.force_define("instrument", builtin_program_change); global.force_define("len", builtin_len); global.force_define("max", builtin_max); diff --git a/musique/interpreter/incoming_midi.hh b/musique/interpreter/incoming_midi.hh deleted file mode 100644 index b068f3d..0000000 --- a/musique/interpreter/incoming_midi.hh +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef MUSIQUE_INCOMING_MIDI -#define MUSIQUE_INCOMING_MIDI - -#include - -struct Interpreter::Incoming_Midi_Callbacks -{ - Value note_on{}; - Value note_off{}; - - inline 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; - - - inline 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 - inline void register_callback(std::function &target, Value &callback, Interpreter &i) - { - if (&callback == ¬e_on || &callback == ¬e_off) { - // This messages have MIDI note number as second value, so they should be represented - // in our own note abstraction, not as numbers. - target = [interpreter = &i, callback = &callback](T ...source_args) - { - if (!std::holds_alternative(callback->data)) { - std::vector args { Number(source_args)... }; - args[1] = Note { - .base = i32(std::get(args[1].data).num % 12), - .octave = std::get(args[1].data).num / 12 - }; - auto result = (*callback)(*interpreter, std::move(args)); - // We discard this since callback is running in another thread. - (void) result; - } - }; - } else { - // Generic case, preserve all passed parameters as numbers - target = [interpreter = &i, callback = &callback](T ...source_args) - { - if (!std::holds_alternative(callback->data)) { - auto result = (*callback)(*interpreter, { Number(source_args)... }); - // We discard this since callback is running in another thread. - (void) result; - } - }; - } - } -}; - - -#endif diff --git a/musique/interpreter/interpreter.cc b/musique/interpreter/interpreter.cc index 1132676..5866b6b 100644 --- a/musique/interpreter/interpreter.cc +++ b/musique/interpreter/interpreter.cc @@ -1,5 +1,4 @@ #include -#include #include #include @@ -223,7 +222,7 @@ void Interpreter::leave_scope() std::optional Interpreter::play(Chord chord) { - Try(ensure_midi_connection_available(*this, Midi_Connection_Type::Output, "play")); + Try(ensure_midi_connection_available(*this, "play")); auto &ctx = context_stack.back(); if (chord.notes.size() == 0) { @@ -260,33 +259,16 @@ std::optional Interpreter::play(Chord chord) return {}; } -std::optional ensure_midi_connection_available(Interpreter &i, Midi_Connection_Type m, std::string_view operation_name) +std::optional ensure_midi_connection_available(Interpreter &i, std::string_view operation_name) { - switch (m) { - break; case Midi_Connection_Type::Output: - if (i.midi_connection == nullptr || !i.midi_connection->supports_output()) { - return Error { - .details = errors::Operation_Requires_Midi_Connection { - .is_input = false, - .name = std::string(operation_name), - }, - .location = {} - }; - } - - break; case Midi_Connection_Type::Input: - if (i.midi_connection == nullptr || !i.midi_connection->supports_input()) { - return Error { - .details = errors::Operation_Requires_Midi_Connection { - .is_input = false, - .name = std::string(operation_name), - }, - .location = {} - }; - } - break; default: - unreachable(); + if (i.midi_connection == nullptr || !i.midi_connection->supports_output()) { + return Error { + .details = errors::Operation_Requires_Midi_Connection { + .is_input = false, + .name = std::string(operation_name), + }, + .location = {} + }; } - return {}; } diff --git a/musique/interpreter/interpreter.hh b/musique/interpreter/interpreter.hh index 83cb729..78bd090 100644 --- a/musique/interpreter/interpreter.hh +++ b/musique/interpreter/interpreter.hh @@ -25,10 +25,6 @@ struct Interpreter std::function(Interpreter&, Value)> default_action; - struct Incoming_Midi_Callbacks; - std::unique_ptr callbacks; - void register_callbacks(); - Interpreter(); ~Interpreter(); Interpreter(Interpreter const&) = delete; @@ -57,7 +53,6 @@ struct Interpreter void register_builtin_operators(); }; -enum class Midi_Connection_Type { Output, Input }; -std::optional ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name); +std::optional ensure_midi_connection_available(Interpreter&, std::string_view operation_name); #endif diff --git a/musique/main.cc b/musique/main.cc index 657b1e0..f061192 100644 --- a/musique/main.cc +++ b/musique/main.cc @@ -66,8 +66,6 @@ static T pop(std::span &span) "usage: musique [filename]\n" " where filename is path to file with Musique code that will be executed\n" " where options are:\n" - " -i,--input PORT\n" - " provides input port, a place where Musique receives MIDI messages\n" " -o,--output PORT\n" " provides output port, a place where Musique produces MIDI messages\n" " -l,--list\n" @@ -130,24 +128,20 @@ struct Runner Interpreter interpreter; /// Setup interpreter and midi connection with given port - Runner(std::optional input_port, std::optional output_port) + explicit Runner(std::optional output_port) : midi() , interpreter{} { ensure(the == nullptr, "Only one instance of runner is supported"); the = this; - bool const midi_go = bool(input_port) || bool(output_port); - if (midi_go) { - interpreter.midi_connection = &midi; - } + interpreter.midi_connection = &midi; if (output_port) { - std::cout << "Connected MIDI output to port " << *output_port << ". Ready to play!" << std::endl; midi.connect_output(*output_port); - } - if (input_port) { - std::cout << "Connected MIDI input to port " << *input_port << ". Ready for incoming messages!" << std::endl; - midi.connect_input(*input_port); + std::cout << "Connected MIDI output to port " << *output_port << ". Ready to play!" << std::endl; + } else { + midi.connect_output(); + std::cout << "Created new MIDI output port 'Musique'. Ready to play!" << std::endl; } Env::global->force_define("say", +[](Interpreter &interpreter, std::vector args) -> Result { @@ -297,7 +291,6 @@ static std::optional Main(std::span args) }; // Arbitraly chosen for conviniance of the author - std::optional input_port{}; std::optional output_port{}; std::vector runnables; @@ -334,15 +327,6 @@ static std::optional Main(std::span args) continue; } - if (arg == "-i" || arg == "--input") { - if (args.empty()) { - std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl; - std::exit(1); - } - input_port = pop(args); - continue; - } - if (arg == "-o" || arg == "--output") { if (args.empty()) { std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl; @@ -360,7 +344,7 @@ static std::optional Main(std::span args) std::exit(1); } - Runner runner{input_port, output_port}; + Runner runner{output_port}; for (auto const& [is_file, argument] : runnables) { if (!is_file) { diff --git a/musique/midi/midi.hh b/musique/midi/midi.hh index ac3ee18..bdfc18e 100644 --- a/musique/midi/midi.hh +++ b/musique/midi/midi.hh @@ -13,7 +13,6 @@ namespace midi virtual ~Connection() = default; virtual bool supports_output() const = 0; - virtual bool supports_input () const = 0; virtual void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) = 0; virtual void send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity) = 0; @@ -21,33 +20,28 @@ namespace midi virtual void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) = 0; void send_all_sounds_off(uint8_t channel); - - std::function note_on_callback = nullptr; - std::function note_off_callback = nullptr; }; struct Rt_Midi : Connection { ~Rt_Midi() override = default; + /// Connect with MIDI virtual port + void connect_output(); + /// Connect with specific MIDI port for outputing MIDI messages void connect_output(unsigned target); - /// Connect with specific MIDI port for reading MIDI messages - void connect_input(unsigned target); - /// List available ports void list_ports(std::ostream &out) const; bool supports_output() const override; - bool supports_input () const override; void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) override; void send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity) override; void send_program_change(uint8_t channel, uint8_t program) override; void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) override; - std::optional input; std::optional output; }; diff --git a/musique/midi/rt_midi.cc b/musique/midi/rt_midi.cc index a8da393..0307e19 100644 --- a/musique/midi/rt_midi.cc +++ b/musique/midi/rt_midi.cc @@ -34,24 +34,22 @@ try { std::exit(33); } -void midi::Rt_Midi::connect_output(unsigned target) +void midi::Rt_Midi::connect_output() try { ensure(not output.has_value(), "Reconeccting is not supported yet"); output.emplace(); - output->openVirtualPort("Musique output port"); - - // output->openPort(target, "Musique output port"); + output->openVirtualPort("Musique"); } catch (RtMidiError &error) { // TODO(error) std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl; std::exit(33); } -void midi::Rt_Midi::connect_input(unsigned target) +void midi::Rt_Midi::connect_output(unsigned target) try { - ensure(not input.has_value(), "Reconeccting is not supported yet"); - input.emplace(); - input->openPort(target, "Musique input port"); + ensure(not output.has_value(), "Reconeccting is not supported yet"); + output.emplace(); + output->openPort(target); } catch (RtMidiError &error) { // TODO(error) std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl; @@ -63,11 +61,6 @@ bool midi::Rt_Midi::supports_output() const return bool(output); } -bool midi::Rt_Midi::supports_input() const -{ - return bool(input); -} - template inline void send_message(RtMidiOut &out, std::array message) try {