ALSA Sequencer connections.
This commit is contained in:
@@ -59,6 +59,7 @@ namespace pipedal
|
||||
|
||||
virtual void ConnectPort(int clientId, int portId) override;
|
||||
virtual void ConnectPort(const std::string &name) override;
|
||||
virtual void SetConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration) override;
|
||||
|
||||
// Read a single MIDI message from the sequencer input port. A timeout of -1 blocks indefinitely.
|
||||
// A timeout of 0 returns immediately.
|
||||
@@ -213,6 +214,15 @@ namespace pipedal
|
||||
}
|
||||
port.id = SS("seq:" << port.clientName << "/" << port.name);
|
||||
|
||||
port.displaySortOrder = port.client * 256 + port.port;
|
||||
if (port.isVirtual)
|
||||
{
|
||||
port.displaySortOrder += 1 * 256 * 256; // MIDI virtual ports after real ports.
|
||||
}
|
||||
else if (port.clientName == "Midi Through")
|
||||
{
|
||||
port.displaySortOrder += 2 * 256 * 256; // MIDI Through at the very end because it's weird.
|
||||
}
|
||||
ports.push_back(std::move(port));
|
||||
}
|
||||
}
|
||||
@@ -258,11 +268,11 @@ namespace pipedal
|
||||
}
|
||||
void AlsaSequencerImpl::RemoveAllConnections()
|
||||
{
|
||||
for (const auto &connection : connections)
|
||||
while (connections.size() != 0)
|
||||
{
|
||||
snd_seq_disconnect_from(seqHandle, inPort, connection.clientId, connection.portId);
|
||||
auto connection = connections.back();
|
||||
ModifyConnection(connection.clientId, connection.portId, ConnectAction::Unsubscribe);
|
||||
}
|
||||
connections.clear();
|
||||
}
|
||||
AlsaSequencerImpl::~AlsaSequencerImpl()
|
||||
{
|
||||
@@ -303,6 +313,25 @@ namespace pipedal
|
||||
}
|
||||
throw std::runtime_error("ALSA port not found");
|
||||
}
|
||||
void AlsaSequencerImpl::SetConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration)
|
||||
{
|
||||
this->RemoveAllConnections(); // Currently no configuration options to set
|
||||
|
||||
auto ports = EnumeratePorts();
|
||||
for (const auto &connection : alsaSequencerConfiguration.connections())
|
||||
{
|
||||
// Connect to each port specified in the configuration
|
||||
std::string id = connection.id();
|
||||
for (const auto &port : ports)
|
||||
{
|
||||
if (port.id == id)
|
||||
{
|
||||
ConnectPort(port.client, port.port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AlsaSequencerImpl::WaitForMessage(int timeoutMs)
|
||||
{
|
||||
@@ -547,6 +576,23 @@ namespace pipedal
|
||||
case SND_SEQ_EVENT_CLOCK:
|
||||
message.Set(0xF8); // MIDI Real Time Clock Tick
|
||||
break;
|
||||
#ifndef NDEBUG
|
||||
#define MSG_DEBUG_LOG(x) \
|
||||
case x:\
|
||||
Lv2Log::debug("ALSA Sequencer Message" #x);\
|
||||
break;
|
||||
#else
|
||||
#define MSG_DEBUG_LOG(x)
|
||||
#endif
|
||||
MSG_DEBUG_LOG(SND_SEQ_EVENT_CLIENT_START)
|
||||
MSG_DEBUG_LOG(SND_SEQ_EVENT_CLIENT_EXIT)
|
||||
MSG_DEBUG_LOG(SND_SEQ_EVENT_CLIENT_CHANGE)
|
||||
MSG_DEBUG_LOG(SND_SEQ_EVENT_PORT_START)
|
||||
MSG_DEBUG_LOG(SND_SEQ_EVENT_PORT_EXIT)
|
||||
MSG_DEBUG_LOG(SND_SEQ_EVENT_PORT_CHANGE)
|
||||
MSG_DEBUG_LOG(SND_SEQ_EVENT_PORT_SUBSCRIBED)
|
||||
MSG_DEBUG_LOG(SND_SEQ_EVENT_PORT_UNSUBSCRIBED)
|
||||
|
||||
case SND_SEQ_EVENT_KEYSIGN:
|
||||
case SND_SEQ_EVENT_TIMESIGN:
|
||||
// and a PASSEL of others!
|
||||
@@ -677,9 +723,8 @@ namespace pipedal
|
||||
{
|
||||
throw std::runtime_error("Failed to open ALSA sequencer for connection");
|
||||
}
|
||||
Finally seq_finally([seq]() {
|
||||
snd_seq_close(seq);
|
||||
});
|
||||
Finally seq_finally([seq]()
|
||||
{ snd_seq_close(seq); });
|
||||
|
||||
snd_seq_addr_t sender, dest;
|
||||
dest.client = myClientId;
|
||||
@@ -704,44 +749,55 @@ namespace pipedal
|
||||
"Failed to disconnect ALSA sequencer port %d:%d. Subscripton not found.",
|
||||
(int)clientId,
|
||||
(int)portId);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
int rc = snd_seq_unsubscribe_port(seq, subs);
|
||||
if (rc < 0) {
|
||||
if (rc < 0)
|
||||
{
|
||||
Lv2Log::warning(
|
||||
"Failed to disconnect ALSA sequencer port %d:%d. (%s)",
|
||||
(int)clientId,
|
||||
(int)portId,
|
||||
snd_strerror(rc));
|
||||
}
|
||||
|
||||
}
|
||||
for (auto it = this->connections.begin(); it != this->connections.end();++it)
|
||||
for (auto it = this->connections.begin(); it != this->connections.end(); ++it)
|
||||
{
|
||||
if (it->clientId == clientId && it->portId == portId)
|
||||
{
|
||||
it = this->connections.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (snd_seq_get_port_subscription(seq, subs) == 0)
|
||||
{
|
||||
Lv2Log::warning("ALSA sequencer port %d:%d is already subscribed.",(int)clientId,(int)portId);
|
||||
Lv2Log::warning("ALSA sequencer port %d:%d is already subscribed.", (int)clientId, (int)portId);
|
||||
return;
|
||||
}
|
||||
int rc = snd_seq_subscribe_port(seq, subs);
|
||||
if (rc < 0)
|
||||
{
|
||||
Lv2Log::error("Failed to connect ALSA sequencer port %d:%d. (%s)",
|
||||
(int)clientId,(int)portId,
|
||||
snd_strerror(rc));
|
||||
(int)clientId, (int)portId,
|
||||
snd_strerror(rc));
|
||||
return;
|
||||
}
|
||||
this->connections.push_back({clientId, portId});
|
||||
}
|
||||
}
|
||||
|
||||
JSON_MAP_BEGIN(AlsaSequencerPortSelection)
|
||||
JSON_MAP_REFERENCE(AlsaSequencerPortSelection, id)
|
||||
JSON_MAP_REFERENCE(AlsaSequencerPortSelection, name)
|
||||
JSON_MAP_REFERENCE(AlsaSequencerPortSelection, sortOrder)
|
||||
JSON_MAP_END()
|
||||
|
||||
JSON_MAP_BEGIN(AlsaSequencerConfiguration)
|
||||
JSON_MAP_REFERENCE(AlsaSequencerConfiguration, connections)
|
||||
JSON_MAP_END()
|
||||
|
||||
} // namespace pipedal
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "json.hpp"
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
@@ -42,6 +43,58 @@ namespace pipedal
|
||||
SetPositionTime
|
||||
};
|
||||
|
||||
class AlsaSequencerPortSelection {
|
||||
private:
|
||||
std::string id_;
|
||||
std::string name_;
|
||||
int32_t sortOrder_ = 0;
|
||||
|
||||
public:
|
||||
AlsaSequencerPortSelection() = default;
|
||||
AlsaSequencerPortSelection(const std::string &id, const std::string &name, int32_t displaySortOrder)
|
||||
: id_(id), name_(name), sortOrder_(displaySortOrder) {}
|
||||
AlsaSequencerPortSelection(const AlsaSequencerPortSelection &other) = default;
|
||||
AlsaSequencerPortSelection(AlsaSequencerPortSelection &&other) = default;
|
||||
AlsaSequencerPortSelection &operator=(const AlsaSequencerPortSelection &other) = default;
|
||||
AlsaSequencerPortSelection &operator=(AlsaSequencerPortSelection &&other) = default;
|
||||
|
||||
|
||||
const std::string& id() const { return id_; }
|
||||
void id(const std::string &value) { id_ = value; }
|
||||
const std::string &name() const { return name_; }
|
||||
void name(const std::string &value) { name_ = value; }
|
||||
|
||||
DECLARE_JSON_MAP(AlsaSequencerPortSelection);
|
||||
};
|
||||
class AlsaSequencerConfiguration {
|
||||
private:
|
||||
std::vector<AlsaSequencerPortSelection> connections_;
|
||||
public:
|
||||
const std::vector<AlsaSequencerPortSelection>& connections() const { return connections_; }
|
||||
std::vector<AlsaSequencerPortSelection>& connections() { return connections_; }
|
||||
void connections(const std::vector<AlsaSequencerPortSelection>& value) { connections_ = value; }
|
||||
|
||||
|
||||
bool operator==(const AlsaSequencerConfiguration &other) const
|
||||
{
|
||||
if (connections_.size() != other.connections_.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < connections_.size(); ++i)
|
||||
{
|
||||
if (connections_[i].id() != other.connections_[i].id() ||
|
||||
connections_[i].name() != other.connections_[i].name())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DECLARE_JSON_MAP(AlsaSequencerConfiguration);
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct AlsaSequencerPort
|
||||
{
|
||||
std::string id;
|
||||
@@ -67,6 +120,7 @@ namespace pipedal
|
||||
bool isPort = false;
|
||||
bool isVirtual = false;
|
||||
int cardNumber = -1;
|
||||
int32_t displaySortOrder = 0;
|
||||
std::string rawMidiDevice; // e.g. "hw:0,0,0" for kernel devices
|
||||
};
|
||||
|
||||
@@ -265,6 +319,8 @@ namespace pipedal
|
||||
virtual void ConnectPort(int clientId, int portId) = 0;
|
||||
virtual void ConnectPort(const std::string &id) = 0;
|
||||
|
||||
virtual void SetConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration) = 0;
|
||||
|
||||
// Read a single MIDI message from the sequencer input port. A timeout of -1 blocks indefinitely.
|
||||
// A timeout of 0 returns immediately.
|
||||
virtual bool ReadMessage(AlsaMidiMessage &message, int timeoutMs = -1) = 0;
|
||||
|
||||
Reference in New Issue
Block a user