builtin port fix

This commit is contained in:
Robert Bendun 2023-02-15 00:23:50 +01:00
parent 25b446a5cc
commit f46a866613
7 changed files with 64 additions and 65 deletions

View File

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Fixed
- `port` function have proper semantics now
## [0.4.0] ## [0.4.0]
### Added ### Added

View File

@ -1637,8 +1637,8 @@ static Result<Value> builtin_port(Interpreter &interpreter, std::vector<Value> a
for (auto const& [key, port] : Context::established_connections) { for (auto const& [key, port] : Context::established_connections) {
if (port == interpreter.current_context->port) { if (port == interpreter.current_context->port) {
return std::visit(Overloaded { return std::visit(Overloaded {
[](midi::connections::Virtual_Port) { return Value(Symbol("virtual")); }, [](midi::Virtual_Port) { return Value(Symbol("virtual")); },
[](midi::connections::Established_Port port) { return Value(Number(port)); }, [](midi::Established_Port port) { return Value(Number(port)); },
}, key); }, key);
} }
} }
@ -1647,14 +1647,14 @@ static Result<Value> builtin_port(Interpreter &interpreter, std::vector<Value> a
if (auto a = match<Number>(args)) { if (auto a = match<Number>(args)) {
auto [port_number] = *a; auto [port_number] = *a;
Try(interpreter.current_context->connect(port_number.floor().as_int())); Try(interpreter.current_context->connect(midi::Established_Port(port_number.floor().as_int())));
return {}; return {};
} }
if (auto a = match<Symbol>(args)) { if (auto a = match<Symbol>(args)) {
auto [port_type] = *a; auto [port_type] = *a;
if (port_type == "virtual") { if (port_type == "virtual") {
Try(interpreter.current_context->connect(std::nullopt)); Try(interpreter.current_context->connect(midi::Virtual_Port{}));
return {}; return {};
} }

View File

@ -14,11 +14,11 @@ std::chrono::duration<float> Context::length_to_duration(std::optional<Number> l
} }
template<> template<>
struct std::hash<midi::connections::Key> struct std::hash<midi::Port>
{ {
std::size_t operator()(midi::connections::Key const& value) const std::size_t operator()(midi::Port const& value) const
{ {
using namespace midi::connections; using namespace midi;
return hash_combine(value.index(), std::visit(Overloaded { return hash_combine(value.index(), std::visit(Overloaded {
[](Virtual_Port) { return 0u; }, [](Virtual_Port) { return 0u; },
[](Established_Port port) { return port; }, [](Established_Port port) { return port; },
@ -26,35 +26,33 @@ struct std::hash<midi::connections::Key>
} }
}; };
std::unordered_map<midi::connections::Key, std::shared_ptr<midi::Connection>> Context::established_connections; std::unordered_map<midi::Port, std::shared_ptr<midi::Connection>> Context::established_connections;
/// Establish connection to given port /// Establish connection to given port
std::optional<Error> Context::connect(std::optional<Port_Number> port_number) std::optional<Error> Context::connect(std::optional<midi::Port> desired_port)
{ {
// FIXME This function doesn't support creating virtual ports when established ports are available // FIXME This function doesn't support creating virtual ports when established ports are available
using namespace midi::connections; if (!desired_port) {
auto const key = port_number ? Key(*port_number) : Key(Virtual_Port{}); if (not established_connections.empty()) {
port = established_connections.begin()->second;
if (auto it = established_connections.find(key); it != established_connections.end()) { return std::nullopt;
port = it->second; }
return std::nullopt;
}
if (port_number) {
auto connection = std::make_shared<midi::Rt_Midi>(); auto connection = std::make_shared<midi::Rt_Midi>();
connection->connect_output(*port_number); established_connections[connection->establish_any_connection()] = connection;
established_connections[*port_number] = connection;
port = connection; port = connection;
return std::nullopt; return std::nullopt;
} }
auto connection = std::make_shared<midi::Rt_Midi>(); if (auto it = established_connections.find(*desired_port); it != established_connections.end()) {
if (connection->connect_or_create_output()) { port = it->second;
established_connections[0u] = connection; return std::nullopt;
} else {
established_connections[Virtual_Port{}] = connection;
} }
auto connection = std::make_shared<midi::Rt_Midi>();
connection->connect(*desired_port);
established_connections[*desired_port] = connection;
port = connection; port = connection;
return std::nullopt; return std::nullopt;
} }

View File

@ -9,17 +9,6 @@
#include <musique/value/note.hh> #include <musique/value/note.hh>
#include <musique/value/number.hh> #include <musique/value/number.hh>
namespace midi::connections
{
using Established_Port = unsigned int;
struct Virtual_Port
{
bool operator==(Virtual_Port const&) const = default;
};
using Key = std::variant<Established_Port, Virtual_Port>;
}
/// Context holds default values for music related actions /// Context holds default values for music related actions
struct Context struct Context
@ -39,12 +28,12 @@ struct Context
using Port_Number = unsigned int; using Port_Number = unsigned int;
/// Connections that have been established so far /// Connections that have been established so far
static std::unordered_map<midi::connections::Key, std::shared_ptr<midi::Connection>> established_connections; static std::unordered_map<midi::Port, std::shared_ptr<midi::Connection>> established_connections;
/// Establish connection to given port /// Establish connection to given port
/// ///
/// If port number wasn't provided connect to first existing one or create one /// If port number wasn't provided connect to first existing one or create one
std::optional<Error> connect(std::optional<Port_Number>); std::optional<Error> connect(std::optional<midi::Port>);
/// Fills empty places in Note like octave and length with default values from context /// Fills empty places in Note like octave and length with default values from context
Note fill(Note) const; Note fill(Note) const;

View File

@ -169,7 +169,7 @@ struct Runner
ensure(the == nullptr, "Only one instance of runner is supported"); ensure(the == nullptr, "Only one instance of runner is supported");
the = this; the = this;
interpreter.current_context->connect(std::nullopt); // interpreter.current_context->connect(std::nullopt);
Env::global->force_define("say", +[](Interpreter &interpreter, std::vector<Value> args) -> Result<Value> { Env::global->force_define("say", +[](Interpreter &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) {

View File

@ -4,10 +4,20 @@
#include <functional> #include <functional>
#include <optional> #include <optional>
#include <string> #include <string>
#include <variant>
// Documentation of midi messages available at http://midi.teragonaudio.com/tech/midispec.htm // Documentation of midi messages available at http://midi.teragonaudio.com/tech/midispec.htm
namespace midi namespace midi
{ {
using Established_Port = unsigned int;
struct Virtual_Port
{
bool operator==(Virtual_Port const&) const = default;
};
using Port = std::variant<Established_Port, Virtual_Port>;
struct Connection struct Connection
{ {
virtual ~Connection() = default; virtual ~Connection() = default;
@ -26,18 +36,15 @@ namespace midi
{ {
~Rt_Midi() override = default; ~Rt_Midi() override = default;
bool connect_or_create_output(); /// Connect to existing MIDI output or create virtual port
Port establish_any_connection();
/// Connect with MIDI virtual port /// Connect to given port
void connect_output(); void connect(Port port);
/// Connect with specific MIDI port for outputing MIDI messages
void connect_output(unsigned target);
/// List available ports /// List available ports
void list_ports(std::ostream &out) const; void list_ports(std::ostream &out) const;
bool supports_output() const override; bool supports_output() const override;
void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) override; void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) override;
@ -45,6 +52,7 @@ namespace midi
void send_program_change(uint8_t channel, uint8_t program) 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; void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) override;
std::optional<RtMidiOut> output; std::optional<RtMidiOut> output;
}; };

View File

@ -34,15 +34,16 @@ try {
std::exit(33); std::exit(33);
} }
bool midi::Rt_Midi::connect_or_create_output() midi::Port midi::Rt_Midi::establish_any_connection()
try { try {
output.emplace(); output.emplace();
if (output->getPortCount()) { if (output->getPortCount()) {
output->openPort(0); output->openPort(0, "Musique");
return true; return Established_Port { 0 };
} }
output->openVirtualPort("Musique"); output->openVirtualPort("Musique");
return false; return Virtual_Port{};
} }
catch (RtMidiError &error) { catch (RtMidiError &error) {
// TODO(error) // TODO(error)
@ -50,23 +51,22 @@ catch (RtMidiError &error) {
std::exit(33); std::exit(33);
} }
void midi::Rt_Midi::connect_output() void midi::Rt_Midi::connect(midi::Port port)
try { try {
ensure(not output.has_value(), "Reconeccting is not supported yet"); std::visit(Overloaded{
output.emplace(); [this](midi::Virtual_Port) {
output->openVirtualPort("Musique"); output.emplace();
} catch (RtMidiError &error) { output->openVirtualPort("Musique");
// TODO(error) },
std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl; [this](midi::Established_Port port) {
std::exit(33); output.emplace();
output->openPort(port, "Musique");
},
}, port);
output->setClientName("Musique");
output->setPortName("Musique");
} }
catch (RtMidiError &error) {
void midi::Rt_Midi::connect_output(unsigned target)
try {
ensure(not output.has_value(), "Reconeccting is not supported yet");
output.emplace();
output->openPort(target);
} catch (RtMidiError &error) {
// TODO(error) // TODO(error)
std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl; std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl;
std::exit(33); std::exit(33);