/* 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 #include #include 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 //-------------- /*! \brief Connect port_src to port_dst. */ 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; } } /*! \brief Disconnect port_src from port_dst. */ 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; } } /*! \brief Check if port_src and port_dst are connected. */ bool JackConnectionManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const { return fConnection[port_src].CheckItem(port_dst); } /*! \brief Get the connection port array. */ const jack_int_t* JackConnectionManager::GetConnections(jack_port_id_t port_index) const { return fConnection[port_index].GetItems(); } //------------------------ // Client port management //------------------------ /*! \brief Add an input port to a client. */ 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; } } /*! \brief Add an output port to a client. */ 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; } } /*! \brief Remove an input port from a client. */ 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; } } /*! \brief Remove an output port from a client. */ 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; } } /*! \brief Get the input port array of a given refnum. */ const jack_int_t* JackConnectionManager::GetInputPorts(int refnum) { return fInputPort[refnum].GetItems(); } /*! \brief Get the output port array of a given refnum. */ const jack_int_t* JackConnectionManager::GetOutputPorts(int refnum) { return fOutputPort[refnum].GetItems(); } /*! \brief Init the refnum. */ void JackConnectionManager::InitRefNum(int refnum) { fInputPort[refnum].Init(); fOutputPort[refnum].Init(); fConnectionRef.Init(refnum); fInputCounter[refnum].SetValue(0); } /*! \brief Reset all clients activation. */ 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; } } /*! \brief Wait on the input synchro. */ 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; } /*! \brief Signal clients connected to the given client. */ 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& sorted) { JackFixedMatrix* tmp = new JackFixedMatrix; std::set 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; } /*! \brief Increment the number of ports between 2 clients, if the 2 clients become connected, then the Activation counter is updated. */ 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); } /*! \brief Decrement the number of ports between 2 clients, if the 2 clients become disconnected, then the Activation counter is updated. */ 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); } /*! \brief Directly connect 2 reference numbers. */ 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(); } } /*! \brief Directly disconnect 2 reference numbers. */ 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(); } } /*! \brief Returns the connections state between 2 refnum. */ bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const { assert(ref1 >= 0 && ref2 >= 0); return (fConnectionRef.GetItemCount(ref1, ref2) > 0); } /*! \brief Get the client refnum of a given input port. */ 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; } /*! \brief Get the client refnum of a given ouput port. */ 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; } /*! \brief Test is a connection path exists between port_src and port_dst. */ 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