jsweeper/src/portmanager.cpp

463 lines
10 KiB
C++

#include <iostream>
#include <fstream>
#include <cstring>
#include <pcrecpp.h>
#include "jack.h"
#include "portmanager.h"
#include "config.h"
Alias::Alias(std::string match, std::string replace)
{
m_match = match;
m_replace = replace;
}
bool Alias::Match(std::string name)
{
pcrecpp::RE re(m_match);
return re.FullMatch(name);
}
std::string Alias::Replace(std::string name)
{
pcrecpp::RE re(m_match);
re.Replace(m_replace, &name);
return name;
}
bool Connection::Match(std::string port1, std::string port2)
{
return false;
}
void PortManager::Refresh()
{
// Clear all information
m_clients.clear();
m_ports.clear();
if (!jack.IsConnected()) return;
jack_client_t *client = jack.GetClient();
const char **ports = jack_get_ports(client, NULL, NULL, 0);
if (ports != NULL) {
for (int i = 0; ports[i] != NULL; ++i) {
Add(jack_port_by_name(client, ports[i]));
}
jack_free(ports);
}
RefreshConnections();
}
void PortManager::RefreshConnections()
{
jack_client_t *client = jack.GetClient();
PortList::iterator it;
for (it = m_ports.begin(); it != m_ports.end(); ++it) {
Port *p = *it;
p->m_connections.clear();
}
for (it = m_ports.begin(); it != m_ports.end(); ++it) {
Port *p = *it;
if (p->m_is_input) {
const char **connections = jack_port_get_all_connections(client, p->m_port);
if (connections != NULL) {
for (int j = 0; connections[j] != NULL; ++j) {
Connect(p->m_port, jack_port_by_name(client, connections[j]));
}
free(connections);
}
}
}
}
void PortManager::Add(jack_port_t *port)
{
std::string jack_name = jack_port_name(port);
std::string client_name;
std::string group_name;
std::string port_name;
if (cfg.UseAliases) {
if (cfg.LocalAliases) {
jack_name = GetAlias(jack_name);
} else {
char *aliases[2];
aliases[0] = new char[jack_port_name_size()];
aliases[1] = new char[jack_port_name_size()];
int num_aliases = jack_port_get_aliases(port, aliases);
if (num_aliases < 2) {
AliasBay(port, num_aliases, aliases);
num_aliases = jack_port_get_aliases(port, aliases);
}
if (num_aliases == 2) {
jack_name = aliases[1];
} else if (num_aliases == 1) {
jack_name = aliases[0];
}
delete aliases[0];
delete aliases[1];
}
}
size_t pos1 = jack_name.find_first_of(':');
size_t pos2 = jack_name.find_last_of('.');
// All ports must have a client part
if (pos1 == std::string::npos) return;
Port *p = new Port();
p->m_port = port;
p->m_is_input = (JackPortFlags)jack_port_flags(port) & JackPortIsInput;
p->m_real_name = jack_port_name(port);
std::string type = jack_port_type(port);
p->m_is_midi = type == JACK_DEFAULT_MIDI_TYPE;
// Client
client_name = jack_name.substr(0, pos1);
p->m_client = FindOrMakeClient(client_name, p->m_is_input, p->m_is_midi);
if (pos2 != std::string::npos) {
// Group
group_name = jack_name.substr(pos1 + 1, pos2 - pos1 - 1);
port_name = jack_name.substr(pos2 + 1);
} else {
group_name = "";
port_name = jack_name.substr(pos1 + 1);
}
p->m_group = FindOrMakeGroup(p->m_client, group_name);
p->m_group->m_ports.push_back(p);
p->m_name = port_name;
//std::clog << "New port (" << client_name << ") " << port_name << std::endl;
p->m_client->m_ports.push_back(p);
m_ports.push_back(p);
}
void PortManager::Delete(jack_port_t *port)
{
Port *p = FindPort(port);
if (p == NULL) {
std::cerr << "Deleting unknown port!" << std::endl;
return;
}
if (p->m_group != NULL) {
// Clean up groups
p->m_group->m_ports.remove(p);
if (p->m_group->m_ports.size() == 0) {
p->m_client->m_groups.remove(p->m_group);
delete p->m_group;
}
}
// Clean up clients
p->m_client->m_ports.remove(p);
if (p->m_client->m_ports.size() == 0) {
m_clients.remove(p->m_client);
delete p->m_client;
}
// Remove port
m_ports.remove(p);
//std::clog << "Port '" << p->m_name << "' removed" << std::endl;
delete p;
}
void PortManager::Connect(jack_port_t *port_a, jack_port_t *port_b)
{
Port *p1 = FindPort(port_a);
Port *p2 = FindPort(port_b);
if (p1 == NULL || p2 == NULL) {
std::cerr << "Connecting unknown ports!" << std::endl;
return;
}
if (p1->m_is_input) {
p1->m_connections.push_back(p2);
} else {
p2->m_connections.push_back(p1);
}
//std::clog << "Connecting '" << jack_port_name(port_a) << "' to '" << jack_port_name(port_b) << "'" << std::endl;
}
void PortManager::Disconnect(jack_port_t *port_a, jack_port_t *port_b)
{
Port *p1 = FindPort(port_a);
Port *p2 = FindPort(port_b);
if (p1 == NULL || p2 == NULL) {
std::cerr << "Disconnecting unknown ports!" << std::endl;
return;
}
if (p1->m_is_input) {
p1->m_connections.remove(p2);
} else {
p2->m_connections.remove(p1);
}
//std::clog << "Disconnecting '" << jack_port_name(port_a) << "' from '" << jack_port_name(port_b) << "'" << std::endl;
}
void PortManager::Rename(jack_port_t *port)
{
Delete(port);
Add(port);
RefreshConnections();
}
bool PortManager::PollPortRenames()
{
// Queue for storing ports to be renamed, to avoid messing up the
// port iterator.
PortList queue;
PortList::iterator it;
for (it = m_ports.begin(); it != m_ports.end(); ++it) {
Port *p = *it;
if (strcmp(p->m_real_name.c_str(), jack_port_name(p->m_port))) {
// Queue for rename.
queue.push_back(p);
}
}
// Now push the renames through.
for (it = queue.begin(); it != queue.end(); ++it) {
Port *p = *it;
Rename(p->m_port);
}
return queue.size() > 0;
}
Client *PortManager::FindOrMakeClient(std::string name, bool is_input, bool is_midi)
{
ClientList::iterator it;
for (it = m_clients.begin(); it != m_clients.end(); ++it) {
Client *c = *it;
if (c->m_is_input == is_input && c->m_is_midi == is_midi && c->m_name == name) return c;
}
Client *c = new Client();
c->m_name = name;
c->m_is_input = is_input;
c->m_is_midi = is_midi;
c->m_expanded = cfg.ExpandClients;
m_clients.push_back(c);
return c;
}
PortGroup *PortManager::FindOrMakeGroup(Client *client, std::string name)
{
PortGroupList::iterator it;
for (it = client->m_groups.begin(); it != client->m_groups.end(); ++it) {
PortGroup *pg = *it;
if (pg->m_name == name) return pg;
}
PortGroup *pg = new PortGroup();
pg->m_name = name;
pg->m_is_input = client->m_is_input;
pg->m_is_midi = client->m_is_midi;
pg->m_expanded = cfg.ExpandGroups;
client->m_groups.push_back(pg);
return pg;
}
Port *PortManager::FindPort(jack_port_t *port)
{
PortList::iterator it;
for (it = m_ports.begin(); it != m_ports.end(); ++it) {
Port *p = *it;
if (p->m_port == port) return p;
}
return NULL;
}
static bool BaseSortPredicate(const Base *lhs, const Base *rhs)
{
const char *a = lhs->m_name.c_str();
const char *b = rhs->m_name.c_str();
size_t len = std::max(lhs->m_name.length(), rhs->m_name.length());
for (size_t p = 0; p < len; p++) {
char ac = (p < lhs->m_name.length()) ? a[p] : '\0';
char bc = (p < rhs->m_name.length()) ? b[p] : '\0';
if (ac >= '0' && ac <= '9' && bc >= '0' && bc <= '9') {
// Numeric sort
long int av = strtol(&a[p], NULL, 0);
long int bv = strtol(&b[p], NULL, 0);
if (av != bv) {
return av < bv;
}
} else {
// Alphabetic sort
if (ac >= 'a' && ac <= 'z') ac -= 'a' - 'A';
if (bc >= 'a' && bc <= 'z') bc -= 'a' - 'A';
if (ac != bc) {
return ac < bc;
}
}
}
return false;
}
void PortManager::Sort()
{
m_clients.sort(&BaseSortPredicate);
ClientList::iterator clit;
for (clit = m_clients.begin(); clit != m_clients.end(); ++clit) {
Client *c = *clit;
c->m_groups.sort(&BaseSortPredicate);
PortGroupList::iterator pglit;
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
PortGroup *pg = *pglit;
pg->m_ports.sort(&BaseSortPredicate);
}
}
}
void PortManager::ToggleConnect(Port *a, Port *b)
{
ConnectionMode cm = a->ConnectedTo(b);
if (cm == CM_NONE) {
jack.Connect(b->m_port, a->m_port);
} else {
jack.Disconnect(b->m_port, a->m_port);
}
}
void PortManager::ToggleConnect(Port *a, PortGroup *b)
{
PortList::iterator plitb;
ConnectionMode cm = a->ConnectedTo(b);
for (plitb = b->m_ports.begin(); plitb != b->m_ports.end(); ++plitb) {
Port *pb = *plitb;
// if (connect == 0) {
// connect = a->ConnectedTo(pb) ? -1 : 1;
// }
if (cm == CM_NONE) {
jack.Connect(pb->m_port, a->m_port);
} else {
jack.Disconnect(pb->m_port, a->m_port);
}
}
}
void PortManager::ToggleConnect(PortGroup *a, Port *b)
{
PortList::iterator plita;
ConnectionMode cm = a->ConnectedTo(b);
for (plita = a->m_ports.begin(); plita != a->m_ports.end(); ++plita) {
Port *pa = *plita;
// if (connect == 0) {
// connect = pa->ConnectedTo(b) ? -1 : 1;
// }
if (cm == CM_NONE) {
jack.Connect(b->m_port, pa->m_port);
} else {
jack.Disconnect(b->m_port, pa->m_port);
}
}
}
void PortManager::ToggleConnect(PortGroup *a, PortGroup *b)
{
PortList::iterator plita;
PortList::iterator plitb;
ConnectionMode cm = a->ConnectedTo(b);
for (plita = a->m_ports.begin(), plitb = b->m_ports.begin(); plita != a->m_ports.end() && plitb != b->m_ports.end(); ++plita, ++plitb) {
Port *pa = *plita;
Port *pb = *plitb;
if (cm == CM_NONE) {
jack.Connect(pb->m_port, pa->m_port);
} else {
jack.Disconnect(pb->m_port, pa->m_port);
}
}
}
std::string PortManager::GetAlias(std::string port_name)
{
AliasList::iterator it;
for (it = m_aliases.begin(); it != m_aliases.end(); ++it) {
Alias &a = *it;
if (a.Match(port_name)) {
return a.Replace(port_name);
}
}
return port_name;
}
void PortManager::AliasBay(jack_port_t *port, int num_aliases, char *aliases[2])
{
std::string port_name = jack_port_name(port);
AliasList::iterator it;
for (it = m_aliases.begin(); it != m_aliases.end(); ++it) {
Alias &a = *it;
if (a.Match(port_name)) {
std::string alias = a.Replace(port_name);
bool got = false;
for (int i = 0; i < num_aliases; i++) {
if (alias == aliases[i]) got = true;
}
if (!got) {
jack_port_set_alias(port, alias.c_str());
}
}
}
}
void PortManager::AliasClear()
{
m_aliases.clear();
}
void PortManager::AliasAdd(std::string source, std::string target)
{
m_aliases.push_back(Alias(source, target));
}