JackConnectionManager.cpp
/*
Copyright (C) 2004-2008 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "JackConnectionManager.h"
#include "JackClientControl.h"
#include "JackEngineControl.h"
#include "JackGlobals.h"
#include "JackError.h"
#include <set>
#include <iostream>
#include <assert.h>
namespace Jack
{
JackConnectionManager::JackConnectionManager()
{
int i;
jack_log("JackConnectionManager::InitConnections size = %ld ", sizeof(JackConnectionManager));
for (i = 0; i < PORT_NUM_MAX; i++) {
fConnection[i].Init();
}
fLoopFeedback.Init();
jack_log("JackConnectionManager::InitClients");
for (i = 0; i < CLIENT_NUM; i++) {
InitRefNum(i);
}
}
JackConnectionManager::~JackConnectionManager()
{}
//--------------
// Internal API
//--------------
bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const
{
jack_log("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld", ref1, ref2);
if (ref1 < GetEngineControl()->fDriverNum || ref2 < GetEngineControl()->fDriverNum) {
return false;
} else if (ref1 == ref2) { // Same refnum
return true;
} else {
jack_int_t output[CLIENT_NUM];
fConnectionRef.GetOutputTable(ref1, output);
if (fConnectionRef.IsInsideTable(ref2, output)) { // If ref2 is contained in the outputs of ref1
return true;
} else {
for (int i = 0; i < CLIENT_NUM && output[i] != EMPTY; i++) { // Otherwise recurse for all ref1 outputs
if (IsLoopPathAux(output[i], ref2)) {
return true; // Stop when a path is found
}
}
return false;
}
}
}
//--------------
// External API
//--------------
int JackConnectionManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
{
jack_log("JackConnectionManager::Connect port_src = %ld port_dst = %ld", port_src, port_dst);
if (fConnection[port_src].AddItem(port_dst)) {
return 0;
} else {
jack_error("Connection table is full !!");
return -1;
}
}
int JackConnectionManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
{
jack_log("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld", port_src, port_dst);
if (fConnection[port_src].RemoveItem(port_dst)) {
return 0;
} else {
jack_error("Connection not found !!");
return -1;
}
}
bool JackConnectionManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const
{
return fConnection[port_src].CheckItem(port_dst);
}
const jack_int_t* JackConnectionManager::GetConnections(jack_port_id_t port_index) const
{
return fConnection[port_index].GetItems();
}
//------------------------
// Client port management
//------------------------
int JackConnectionManager::AddInputPort(int refnum, jack_port_id_t port_index)
{
if (fInputPort[refnum].AddItem(port_index)) {
jack_log("JackConnectionManager::AddInputPort ref = %ld port = %ld", refnum, port_index);
return 0;
} else {
jack_error("Maximum number of input ports is reached for application ref = %ld", refnum);
return -1;
}
}
int JackConnectionManager::AddOutputPort(int refnum, jack_port_id_t port_index)
{
if (fOutputPort[refnum].AddItem(port_index)) {
jack_log("JackConnectionManager::AddOutputPort ref = %ld port = %ld", refnum, port_index);
return 0;
} else {
jack_error("Maximum number of output ports is reached for application ref = %ld", refnum);
return -1;
}
}
int JackConnectionManager::RemoveInputPort(int refnum, jack_port_id_t port_index)
{
jack_log("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld ", refnum, port_index);
if (fInputPort[refnum].RemoveItem(port_index)) {
return 0;
} else {
jack_error("Input port index = %ld not found for application ref = %ld", port_index, refnum);
return -1;
}
}
int JackConnectionManager::RemoveOutputPort(int refnum, jack_port_id_t port_index)
{
jack_log("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld ", refnum, port_index);
if (fOutputPort[refnum].RemoveItem(port_index)) {
return 0;
} else {
jack_error("Output port index = %ld not found for application ref = %ld", port_index, refnum);
return -1;
}
}
const jack_int_t* JackConnectionManager::GetInputPorts(int refnum)
{
return fInputPort[refnum].GetItems();
}
const jack_int_t* JackConnectionManager::GetOutputPorts(int refnum)
{
return fOutputPort[refnum].GetItems();
}
void JackConnectionManager::InitRefNum(int refnum)
{
fInputPort[refnum].Init();
fOutputPort[refnum].Init();
fConnectionRef.Init(refnum);
fInputCounter[refnum].SetValue(0);
}
void JackConnectionManager::ResetGraph(JackClientTiming* timing)
{
// Reset activation counter : must be done *before* starting to resume clients
for (int i = 0; i < CLIENT_NUM; i++) {
fInputCounter[i].Reset();
timing[i].fStatus = NotTriggered;
}
}
int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing, long time_out_usec)
{
bool res;
if ((res = table[control->fRefNum].TimedWait(time_out_usec))) {
timing[control->fRefNum].fStatus = Running;
timing[control->fRefNum].fAwakeAt = GetMicroSeconds();
}
return (res) ? 0 : -1;
}
int JackConnectionManager::ResumeRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing)
{
jack_time_t current_date = GetMicroSeconds();
const jack_int_t* output_ref = fConnectionRef.GetItems(control->fRefNum);
int res = 0;
// Update state and timestamp of current client
timing[control->fRefNum].fStatus = Finished;
timing[control->fRefNum].fFinishedAt = current_date;
for (int i = 0; i < CLIENT_NUM; i++) {
// Signal connected clients or drivers
if (output_ref[i] > 0) {
// Update state and timestamp of destination clients
timing[i].fStatus = Triggered;
timing[i].fSignaledAt = current_date;
if (!fInputCounter[i].Signal(table + i, control)) {
jack_log("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld ", control->fRefNum, i);
res = -1;
}
}
}
return res;
}
static bool HasNoConnection(jack_int_t* table)
{
for (int ref = 0; ref < CLIENT_NUM; ref++) {
if (table[ref] > 0) return false;
}
return true;
}
// Using http://en.wikipedia.org/wiki/Topological_sorting
void JackConnectionManager::TopologicalSort(std::vector<jack_int_t>& sorted)
{
JackFixedMatrix<CLIENT_NUM>* tmp = new JackFixedMatrix<CLIENT_NUM>;
std::set<jack_int_t> level;
fConnectionRef.Copy(*tmp);
// Inputs of the graph
level.insert(AUDIO_DRIVER_REFNUM);
level.insert(FREEWHEEL_DRIVER_REFNUM);
while (level.size() > 0) {
jack_int_t refnum = *level.begin();
sorted.push_back(refnum);
level.erase(level.begin());
const jack_int_t* output_ref1 = tmp->GetItems(refnum);
for (int dst = 0; dst < CLIENT_NUM; dst++) {
if (output_ref1[dst] > 0) {
tmp->ClearItem(refnum, dst);
jack_int_t output_ref2[CLIENT_NUM];
tmp->GetOutputTable1(dst, output_ref2);
if (HasNoConnection(output_ref2)) {
level.insert(dst);
}
}
}
}
delete tmp;
}
void JackConnectionManager::IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
int ref1 = GetOutputRefNum(port_src);
int ref2 = GetInputRefNum(port_dst);
assert(ref1 >= 0 && ref2 >= 0);
DirectConnect(ref1, ref2);
jack_log("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
}
void JackConnectionManager::DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
int ref1 = GetOutputRefNum(port_src);
int ref2 = GetInputRefNum(port_dst);
assert(ref1 >= 0 && ref2 >= 0);
DirectDisconnect(ref1, ref2);
jack_log("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
}
void JackConnectionManager::DirectConnect(int ref1, int ref2)
{
assert(ref1 >= 0 && ref2 >= 0);
if (fConnectionRef.IncItem(ref1, ref2) == 1) { // First connection between client ref1 and client ref2
jack_log("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld", ref1, ref2);
fInputCounter[ref2].IncValue();
}
}
void JackConnectionManager::DirectDisconnect(int ref1, int ref2)
{
assert(ref1 >= 0 && ref2 >= 0);
if (fConnectionRef.DecItem(ref1, ref2) == 0) { // Last connection between client ref1 and client ref2
jack_log("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld", ref1, ref2);
fInputCounter[ref2].DecValue();
}
}
bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const
{
assert(ref1 >= 0 && ref2 >= 0);
return (fConnectionRef.GetItemCount(ref1, ref2) > 0);
}
int JackConnectionManager::GetInputRefNum(jack_port_id_t port_index) const
{
for (int i = 0; i < CLIENT_NUM; i++) {
if (fInputPort[i].CheckItem(port_index)) {
return i;
}
}
return -1;
}
int JackConnectionManager::GetOutputRefNum(jack_port_id_t port_index) const
{
for (int i = 0; i < CLIENT_NUM; i++) {
if (fOutputPort[i].CheckItem(port_index)) {
return i;
}
}
return -1;
}
bool JackConnectionManager::IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const
{
return IsLoopPathAux(GetInputRefNum(port_dst), GetOutputRefNum(port_src));
}
bool JackConnectionManager::IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const
{
return (fLoopFeedback.GetConnectionIndex(GetOutputRefNum(port_src), GetInputRefNum(port_dst)) >= 0);
}
bool JackConnectionManager::IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
int ref1 = GetOutputRefNum(port_src);
int ref2 = GetInputRefNum(port_dst);
// Add an activation connection in the other direction
jack_log("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
assert(ref1 >= 0 && ref2 >= 0);
if (ref1 != ref2) {
DirectConnect(ref2, ref1);
}
return fLoopFeedback.IncConnection(ref1, ref2); // Add the feedback connection
}
bool JackConnectionManager::DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
int ref1 = GetOutputRefNum(port_src);
int ref2 = GetInputRefNum(port_dst);
// Remove an activation connection in the other direction
jack_log("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
assert(ref1 >= 0 && ref2 >= 0);
if (ref1 != ref2) {
DirectDisconnect(ref2, ref1);
}
return fLoopFeedback.DecConnection(ref1, ref2); // Remove the feedback connection
}
} // end of namespace
Jack::JackConnectionManager::DirectDisconnect
void DirectDisconnect(int ref1, int ref2)
Directly disconnect 2 reference numbers.
Definition: JackConnectionManager.cpp:362
Jack::JackConnectionManager::IsDirectConnection
bool IsDirectConnection(int ref1, int ref2) const
Returns the connections state between 2 refnum.
Definition: JackConnectionManager.cpp:375
Jack::JackConnectionManager::IsLoopPath
bool IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const
Test is a connection path exists between port_src and port_dst.
Definition: JackConnectionManager.cpp:412
Jack::JackConnectionManager::Disconnect
int Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
Disconnect port_src from port_dst.
Definition: JackConnectionManager.cpp:103
Jack
Definition: JackAC3Encoder.cpp:30
Jack::JackConnectionManager::GetOutputRefNum
int GetOutputRefNum(jack_port_id_t port_index) const
Get the client refnum of a given ouput port.
Definition: JackConnectionManager.cpp:398
Jack::JackConnectionManager::IncDirectConnection
void IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
Increment the number of ports between 2 clients, if the 2 clients become connected, then the Activation counter is updated.
Definition: JackConnectionManager.cpp:321
Jack::JackConnectionManager::SuspendRefNum
int SuspendRefNum(JackClientControl *control, JackSynchro *table, JackClientTiming *timing, long time_out_usec)
Wait on the input synchro.
Definition: JackConnectionManager.cpp:235
Jack::JackPosixSemaphore
Inter process synchronization using POSIX semaphore.
Definition: JackPosixSemaphore.h:36
Jack::JackConnectionManager::ResetGraph
void ResetGraph(JackClientTiming *timing)
Reset all clients activation.
Definition: JackConnectionManager.cpp:223
Jack::JackLoopFeedback::GetConnectionIndex
int GetConnectionIndex(int ref1, int ref2) const
Test if a connection between 2 refnum is a feedback connection.
Definition: JackConnectionManager.h:358
jack_error
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
Jack::JackConnectionManager::RemoveInputPort
int RemoveInputPort(int refnum, jack_port_id_t port_index)
Remove an input port from a client.
Definition: JackConnectionManager.cpp:166
Jack::JackConnectionManager::AddOutputPort
int AddOutputPort(int refnum, jack_port_id_t port_index)
Add an output port to a client.
Definition: JackConnectionManager.cpp:152
Jack::JackConnectionManager::GetInputRefNum
int GetInputRefNum(jack_port_id_t port_index) const
Get the client refnum of a given input port.
Definition: JackConnectionManager.cpp:384
Jack::JackConnectionManager::DecDirectConnection
void DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
Decrement the number of ports between 2 clients, if the 2 clients become disconnected, then the Activation counter is updated.
Definition: JackConnectionManager.cpp:335
Jack::JackConnectionManager::Connect
int Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
Connect port_src to port_dst.
Definition: JackConnectionManager.cpp:88
Jack::JackFixedMatrix::GetOutputTable
void GetOutputTable(jack_int_t index, jack_int_t *output) const
Get the output indexes of a given index.
Definition: JackConnectionManager.h:214
Jack::JackConnectionManager::GetOutputPorts
const jack_int_t * GetOutputPorts(int refnum)
Get the output port array of a given refnum.
Definition: JackConnectionManager.cpp:204
Jack::JackConnectionManager::IsConnected
bool IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const
Check if port_src and port_dst are connected.
Definition: JackConnectionManager.cpp:118
Jack::JackConnectionManager::DirectConnect
void DirectConnect(int ref1, int ref2)
Directly connect 2 reference numbers.
Definition: JackConnectionManager.cpp:349
Jack::JackConnectionManager::GetConnections
const jack_int_t * GetConnections(jack_port_id_t port_index) const
Get the connection port array.
Definition: JackConnectionManager.cpp:126
Jack::JackConnectionManager::RemoveOutputPort
int RemoveOutputPort(int refnum, jack_port_id_t port_index)
Remove an output port from a client.
Definition: JackConnectionManager.cpp:181
Jack::JackClientTiming
For client timing measurements.
Definition: JackConnectionManager.h:374
Jack::JackConnectionManager::InitRefNum
void InitRefNum(int refnum)
Init the refnum.
Definition: JackConnectionManager.cpp:212
Jack::JackConnectionManager::AddInputPort
int AddInputPort(int refnum, jack_port_id_t port_index)
Add an input port to a client.
Definition: JackConnectionManager.cpp:138
Jack::JackConnectionManager::ResumeRefNum
int ResumeRefNum(JackClientControl *control, JackSynchro *table, JackClientTiming *timing)
Signal clients connected to the given client.
Definition: JackConnectionManager.cpp:248
jack_log
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:108
Jack::JackConnectionManager::GetInputPorts
const jack_int_t * GetInputPorts(int refnum)
Get the input port array of a given refnum.
Definition: JackConnectionManager.cpp:196
Jack::JackClientControl
Client control possibly in shared memory.
Definition: JackClientControl.h:38