Jack2 1.9.10

JackClient.cpp

00001 /*
00002 Copyright (C) 2001 Paul Davis
00003 Copyright (C) 2004-2008 Grame
00004 
00005 This program is free software; you can redistribute it and/or modify
00006 it under the terms of the GNU Lesser General Public License as published by
00007 the Free Software Foundation; either version 2.1 of the License, or
00008 (at your option) any later version.
00009 
00010 This program is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 GNU Lesser General Public License for more details.
00014 
00015 You should have received a copy of the GNU Lesser General Public License
00016 along with this program; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018 
00019 */
00020 
00021 #include "JackSystemDeps.h"
00022 #include "JackGraphManager.h"
00023 #include "JackClientControl.h"
00024 #include "JackEngineControl.h"
00025 #include "JackGlobals.h"
00026 #include "JackChannel.h"
00027 #include "JackTransportEngine.h"
00028 #include "driver_interface.h"
00029 #include "JackLibGlobals.h"
00030 
00031 #include <math.h>
00032 #include <string>
00033 #include <algorithm>
00034 
00035 using namespace std;
00036 
00037 namespace Jack
00038 {
00039 
00040 #define IsRealTime() ((fProcess != NULL) | (fThreadFun != NULL) | (fSync != NULL) | (fTimebase != NULL))
00041 
00042 JackClient::JackClient():fThread(this)
00043 {}
00044 
00045 JackClient::JackClient(JackSynchro* table):fThread(this)
00046 {
00047     fSynchroTable = table;
00048     fProcess = NULL;
00049     fGraphOrder = NULL;
00050     fXrun = NULL;
00051     fShutdown = NULL;
00052     fInfoShutdown = NULL;
00053     fInit = NULL;
00054     fBufferSize = NULL;
00055     fClientRegistration = NULL;
00056     fFreewheel = NULL;
00057     fPortRegistration = NULL;
00058     fPortConnect = NULL;
00059     fPortRename = NULL;
00060     fTimebase = NULL;
00061     fSync = NULL;
00062     fThreadFun = NULL;
00063     fSession = NULL;
00064     fLatency = NULL;
00065 
00066     fProcessArg = NULL;
00067     fGraphOrderArg = NULL;
00068     fXrunArg = NULL;
00069     fShutdownArg = NULL;
00070     fInfoShutdownArg = NULL;
00071     fInitArg = NULL;
00072     fBufferSizeArg = NULL;
00073     fFreewheelArg = NULL;
00074     fClientRegistrationArg = NULL;
00075     fPortRegistrationArg = NULL;
00076     fPortConnectArg = NULL;
00077     fPortRenameArg = NULL;
00078     fSyncArg = NULL;
00079     fTimebaseArg = NULL;
00080     fThreadFunArg = NULL;
00081     fSessionArg = NULL;
00082     fLatencyArg = NULL;
00083 
00084     fSessionReply = kPendingSessionReply;
00085 }
00086 
00087 JackClient::~JackClient()
00088 {}
00089 
00090 void JackClient::ShutDown(jack_status_t code, const char* message)
00091 {
00092     jack_log("JackClient::ShutDown");
00093  
00094     // If "fInfoShutdown" callback, then call it
00095     if (fInfoShutdown) {
00096         fInfoShutdown(code, message, fInfoShutdownArg);
00097         fInfoShutdown = NULL;
00098     // Otherwise possibly call the normal "fShutdown"
00099     } else if (fShutdown) {
00100         fShutdown(fShutdownArg);
00101         fShutdown = NULL;
00102     }
00103 }
00104 
00105 int JackClient::Close()
00106 {
00107     jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
00108     int result = 0;
00109 
00110     Deactivate();
00111     
00112     // Channels is stopped first to avoid receiving notifications while closing
00113     fChannel->Stop();  
00114     // Then close client
00115     fChannel->ClientClose(GetClientControl()->fRefNum, &result);
00116   
00117     fChannel->Close();
00118     assert(JackGlobals::fSynchroMutex);
00119     JackGlobals::fSynchroMutex->Lock();
00120     fSynchroTable[GetClientControl()->fRefNum].Disconnect();
00121     JackGlobals::fSynchroMutex->Unlock();
00122     JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
00123     return result;
00124 }
00125 
00126 bool JackClient::IsActive()
00127 {
00128     return (GetClientControl()) ? GetClientControl()->fActive : false;
00129 }
00130 
00131 jack_native_thread_t JackClient::GetThreadID()
00132 {
00133     return fThread.GetThreadID();
00134 }
00135 
00141 void JackClient::SetupDriverSync(bool freewheel)
00142 {
00143     if (!freewheel && !GetEngineControl()->fSyncMode) {
00144         jack_log("JackClient::SetupDriverSync driver sem in flush mode");
00145         for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
00146             fSynchroTable[i].SetFlush(true);
00147         }
00148     } else {
00149         jack_log("JackClient::SetupDriverSync driver sem in normal mode");
00150         for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
00151             fSynchroTable[i].SetFlush(false);
00152         }
00153     }
00154 }
00155 
00160 int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
00161 {
00162     return 0;
00163 }
00164 
00165 int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
00166 {
00167     int res = 0;
00168 
00169     jack_log("JackClient::ClientNotify ref = %ld name = %s notify = %ld", refnum, name, notify);
00170 
00171     // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
00172     switch (notify) {
00173 
00174         case kAddClient:
00175             res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
00176             break;
00177 
00178         case kRemoveClient:
00179             res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
00180             break;
00181 
00182         case kActivateClient:
00183             jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
00184             InitAux();
00185             break;
00186     }
00187 
00188     /*
00189     The current semantic is that notifications can only be received when the client has been activated,
00190     although is this implementation, one could imagine calling notifications as soon as the client has be opened.
00191     */
00192     if (IsActive()) {
00193 
00194         switch (notify) {
00195 
00196             case kAddClient:
00197                 jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name);
00198                 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) {      // Don't call the callback for the registering client itself
00199                     fClientRegistration(name, 1, fClientRegistrationArg);
00200                 }
00201                 break;
00202 
00203             case kRemoveClient:
00204                 jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
00205                 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
00206                     fClientRegistration(name, 0, fClientRegistrationArg);
00207                 }
00208                 break;
00209 
00210             case kBufferSizeCallback:
00211                 jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
00212                 if (fBufferSize) {
00213                     res = fBufferSize(value1, fBufferSizeArg);
00214                 }
00215                 break;
00216 
00217             case kSampleRateCallback:
00218                 jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
00219                 if (fSampleRate) {
00220                     res = fSampleRate(value1, fSampleRateArg);
00221                 }
00222                 break;
00223 
00224             case kGraphOrderCallback:
00225                 jack_log("JackClient::kGraphOrderCallback");
00226                 if (fGraphOrder) {
00227                     res = fGraphOrder(fGraphOrderArg);
00228                 }
00229                 break;
00230 
00231             case kStartFreewheelCallback:
00232                 jack_log("JackClient::kStartFreewheel");
00233                 SetupDriverSync(true);
00234                 // Drop RT only when the RT thread is actually running
00235                 if (fThread.GetStatus() == JackThread::kRunning) {
00236                     fThread.DropRealTime();     
00237                 }
00238                 if (fFreewheel) {
00239                     fFreewheel(1, fFreewheelArg);
00240                 }
00241                 break;
00242 
00243             case kStopFreewheelCallback:
00244                 jack_log("JackClient::kStopFreewheel");
00245                 SetupDriverSync(false);
00246                 if (fFreewheel) {
00247                     fFreewheel(0, fFreewheelArg);
00248                 }
00249                 // Acquire RT only when the RT thread is actually running
00250                 if (GetEngineControl()->fRealTime && fThread.GetStatus() == JackThread::kRunning) {
00251                     if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) {
00252                         jack_error("JackClient::AcquireRealTime error");
00253                     }
00254                 }
00255                 break;
00256 
00257             case kPortRegistrationOnCallback:
00258                 jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
00259                 if (fPortRegistration) {
00260                     fPortRegistration(value1, 1, fPortRegistrationArg);
00261                 }
00262                 break;
00263 
00264             case kPortRegistrationOffCallback:
00265                 jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
00266                 if (fPortRegistration) {
00267                     fPortRegistration(value1, 0, fPortRegistrationArg);
00268                 }
00269                 break;
00270 
00271             case kPortConnectCallback:
00272                 jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
00273                 if (fPortConnect) {
00274                     fPortConnect(value1, value2, 1, fPortConnectArg);
00275                 }
00276                 break;
00277 
00278             case kPortDisconnectCallback:
00279                 jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
00280                 if (fPortConnect) {
00281                     fPortConnect(value1, value2, 0, fPortConnectArg);
00282                 }
00283                 break;
00284 
00285              case kPortRenameCallback:
00286                 jack_log("JackClient::kPortRenameCallback port = %ld", value1);
00287                 if (fPortRename) {
00288                     fPortRename(value1, message, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
00289                 }
00290                 break;
00291 
00292             case kXRunCallback:
00293                 jack_log("JackClient::kXRunCallback");
00294                 if (fXrun) {
00295                     res = fXrun(fXrunArg);
00296                 }
00297                 break;
00298 
00299             case kShutDownCallback:
00300                 jack_log("JackClient::kShutDownCallback");
00301                 ShutDown(jack_status_t(value1), message);
00302                 break;
00303 
00304             case kSessionCallback:
00305                 jack_log("JackClient::kSessionCallback");
00306                 if (fSession) {
00307                     jack_session_event_t* event = (jack_session_event_t*)malloc( sizeof(jack_session_event_t));
00308                     char uuid_buf[JACK_UUID_SIZE];
00309                     event->type = (jack_session_event_type_t)value1;
00310                     event->session_dir = strdup(message);
00311                     event->command_line = NULL;
00312                     event->flags = (jack_session_flags_t)0;
00313                     snprintf(uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID);
00314                     event->client_uuid = strdup(uuid_buf);
00315                     fSessionReply = kPendingSessionReply;
00316                     // Session callback may change fSessionReply by directly using jack_session_reply
00317                     fSession(event, fSessionArg);
00318                     res = fSessionReply;
00319                 }
00320                 break;
00321 
00322             case kLatencyCallback:
00323                 res = HandleLatencyCallback(value1);
00324                 break;
00325         }
00326     }
00327 
00328     return res;
00329 }
00330 
00331 int JackClient::HandleLatencyCallback(int status)
00332 {
00333     jack_latency_callback_mode_t mode = (status == 0) ? JackCaptureLatency : JackPlaybackLatency;
00334         jack_latency_range_t latency = { UINT32_MAX, 0 };
00335 
00336         /* first setup all latency values of the ports.
00337          * this is based on the connections of the ports.
00338          */
00339     list<jack_port_id_t>::iterator it;
00340 
00341         for (it = fPortList.begin(); it != fPortList.end(); it++) {
00342         JackPort* port = GetGraphManager()->GetPort(*it);
00343         if ((port->GetFlags() & JackPortIsOutput) && (mode == JackPlaybackLatency)) {
00344             GetGraphManager()->RecalculateLatency(*it, mode);
00345                 }
00346                 if ((port->GetFlags() & JackPortIsInput) && (mode == JackCaptureLatency)) {
00347             GetGraphManager()->RecalculateLatency(*it, mode);
00348                 }
00349         }
00350 
00351         if (!fLatency) {
00352                 /*
00353                  * default action is to assume all ports depend on each other.
00354                  * then always take the maximum latency.
00355                  */
00356 
00357                 if (mode == JackPlaybackLatency) {
00358                         /* iterate over all OutputPorts, to find maximum playback latency
00359                          */
00360                         for (it = fPortList.begin(); it != fPortList.end(); it++) {
00361                 JackPort* port = GetGraphManager()->GetPort(*it);
00362                 if (port->GetFlags() & JackPortIsOutput) {
00363                                         jack_latency_range_t other_latency;
00364                                         port->GetLatencyRange(mode, &other_latency);
00365                                         if (other_latency.max > latency.max) {
00366                                                 latency.max = other_latency.max;
00367                     }
00368                                         if (other_latency.min < latency.min) {
00369                                                 latency.min = other_latency.min;
00370                     }
00371                                 }
00372                         }
00373 
00374                         if (latency.min == UINT32_MAX) {
00375                                 latency.min = 0;
00376             }
00377 
00378                         /* now set the found latency on all input ports
00379                          */
00380                         for (it = fPortList.begin(); it != fPortList.end(); it++) {
00381                 JackPort* port = GetGraphManager()->GetPort(*it);
00382                 if (port->GetFlags() & JackPortIsInput) {
00383                                         port->SetLatencyRange(mode, &latency);
00384                                 }
00385                         }
00386                 }
00387                 if (mode == JackCaptureLatency) {
00388                         /* iterate over all InputPorts, to find maximum playback latency
00389                          */
00390                         for (it = fPortList.begin(); it != fPortList.end(); it++) {
00391                 JackPort* port = GetGraphManager()->GetPort(*it);
00392                                 if (port->GetFlags() & JackPortIsInput) {
00393                                         jack_latency_range_t other_latency;
00394                     port->GetLatencyRange(mode, &other_latency);
00395                                         if (other_latency.max > latency.max) {
00396                                                 latency.max = other_latency.max;
00397                     }
00398                                         if (other_latency.min < latency.min) {
00399                                                 latency.min = other_latency.min;
00400                     }
00401                                 }
00402                         }
00403 
00404                         if (latency.min == UINT32_MAX) {
00405                                 latency.min = 0;
00406             }
00407 
00408                         /* now set the found latency on all output ports
00409                          */
00410                         for (it = fPortList.begin(); it != fPortList.end(); it++) {
00411                 JackPort* port = GetGraphManager()->GetPort(*it);
00412                 if (port->GetFlags() & JackPortIsOutput) {
00413                                         port->SetLatencyRange(mode, &latency);
00414                                 }
00415                         }
00416                 }
00417                 return 0;
00418         }
00419 
00420         /* we have a latency callback setup by the client,
00421          * lets use it...
00422          */
00423         fLatency(mode, fLatencyArg);
00424         return 0;
00425 }
00426 
00431 int JackClient::Activate()
00432 {
00433     jack_log("JackClient::Activate");
00434     if (IsActive()) {
00435         return 0;
00436     }
00437 
00438     // RT thread is started only when needed...
00439     if (IsRealTime()) {
00440         if (StartThread() < 0) {
00441             return -1;
00442         }
00443     }
00444 
00445     /*
00446     Insertion of client in the graph will cause a kGraphOrderCallback notification
00447     to be delivered by the server, the client wants to receive it.
00448     */
00449     GetClientControl()->fActive = true;
00450 
00451     // Transport related callback become "active"
00452     GetClientControl()->fTransportSync = true;
00453     GetClientControl()->fTransportTimebase = true;
00454 
00455     int result = -1;
00456     GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
00457     fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
00458     return result;
00459 }
00460 
00464 int JackClient::Deactivate()
00465 {
00466     jack_log("JackClient::Deactivate");
00467     if (!IsActive()) {
00468         return 0;
00469     }
00470 
00471     GetClientControl()->fActive = false;
00472 
00473     // Transport related callback become "unactive"
00474     GetClientControl()->fTransportSync = false;
00475     GetClientControl()->fTransportTimebase = false;
00476 
00477     // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
00478     int result = -1;
00479     fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
00480     jack_log("JackClient::Deactivate res = %ld", result);
00481 
00482     // RT thread is stopped only when needed...
00483     if (IsRealTime()) {
00484         fThread.Kill();
00485     }
00486     return result;
00487 }
00488 
00489 //----------------------
00490 // RT thread management
00491 //----------------------
00492 
00493 void JackClient::InitAux()
00494 {
00495     if (fInit) {
00496         jack_log("JackClient::Init calling client thread init callback");
00497         fInit(fInitArg);
00498     }
00499 }
00500 
00504 bool JackClient::Init()
00505 {
00506     /*
00507         Execute buffer_size callback.
00508 
00509         Since StartThread uses fThread.StartSync, we are sure that buffer_size callback
00510         is executed before StartThread returns (and then IsActive will be true).
00511         So no RT callback can be called at the same time.
00512     */
00513     jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", GetEngineControl()->fBufferSize);
00514     if (fBufferSize) {
00515         fBufferSize(GetEngineControl()->fBufferSize, fBufferSizeArg);
00516     }
00517 
00518     // Init callback
00519     InitAux();
00520 
00521     // Setup context
00522     if (!jack_tls_set(JackGlobals::fRealTimeThread, this)) {
00523         jack_error("Failed to set thread realtime key");
00524     }
00525 
00526     // Setup RT
00527     if (GetEngineControl()->fRealTime) {
00528         set_threaded_log_function();
00529         SetupRealTime();
00530     }
00531 
00532     return true;
00533 }
00534 
00535 void JackClient::SetupRealTime()
00536 {
00537     jack_log("JackClient::Init : period = %ld computation = %ld constraint = %ld",
00538              long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
00539              long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
00540              long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
00541 
00542     // Will do "something" on OSX only...
00543     fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
00544 
00545     if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) {
00546         jack_error("JackClient::AcquireSelfRealTime error");
00547     }
00548 }
00549 
00550 int JackClient::StartThread()
00551 {
00552     if (fThread.StartSync() < 0) {
00553         jack_error("Start thread error");
00554         return -1;
00555     }
00556 
00557     return 0;
00558 }
00559 
00564 bool JackClient::Execute()
00565 {
00566     // Execute a dummy cycle to be sure thread has the correct properties
00567     DummyCycle();
00568 
00569     if (fThreadFun) {
00570         fThreadFun(fThreadFunArg);
00571     } else {
00572         ExecuteThread();
00573     }
00574     return false;
00575 }
00576 
00577 void JackClient::DummyCycle()
00578 {
00579     WaitSync();
00580     SignalSync();
00581 }
00582 
00583 inline void JackClient::ExecuteThread()
00584 {
00585     while (true) {
00586         CycleWaitAux();
00587         CycleSignalAux(CallProcessCallback());
00588     }
00589 }
00590 
00591 inline jack_nframes_t JackClient::CycleWaitAux()
00592 {
00593     if (!WaitSync()) {
00594         Error();   // Terminates the thread
00595     }
00596     CallSyncCallbackAux();
00597     return GetEngineControl()->fBufferSize;
00598 }
00599 
00600 inline void JackClient::CycleSignalAux(int status)
00601 {
00602     if (status == 0) {
00603         CallTimebaseCallbackAux();
00604     }
00605     SignalSync();
00606     if (status != 0) {
00607         End();     // Terminates the thread
00608     }
00609 }
00610 
00611 jack_nframes_t JackClient::CycleWait()
00612 {
00613     return CycleWaitAux();
00614 }
00615 
00616 void JackClient::CycleSignal(int status)
00617 {
00618     CycleSignalAux(status);
00619 }
00620 
00621 inline int JackClient::CallProcessCallback()
00622 {
00623     return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
00624 }
00625 
00626 inline bool JackClient::WaitSync()
00627 {
00628     // Suspend itself: wait on the input synchro
00629     if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
00630         jack_error("SuspendRefNum error");
00631         return false;
00632     } else {
00633         return true;
00634     }
00635 }
00636 
00637 inline void JackClient::SignalSync()
00638 {
00639     // Resume: signal output clients connected to the running client
00640     if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
00641         jack_error("ResumeRefNum error");
00642     }
00643 }
00644 
00645 inline void JackClient::End()
00646 {
00647     jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
00648     // Hum... not sure about this, the following "close" code is called in the RT thread...
00649     int result;
00650     fThread.DropSelfRealTime();
00651     GetClientControl()->fActive = false;
00652     fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
00653     fThread.Terminate();
00654 }
00655 
00656 inline void JackClient::Error()
00657 {
00658     jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
00659     // Hum... not sure about this, the following "close" code is called in the RT thread...
00660     int result;
00661     fThread.DropSelfRealTime();
00662     GetClientControl()->fActive = false;
00663     fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
00664     ShutDown(jack_status_t(JackFailure | JackServerError), JACK_SERVER_FAILURE);
00665     fThread.Terminate();
00666 }
00667 
00668 //-----------------
00669 // Port management
00670 //-----------------
00671 
00672 int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
00673 {
00674     // Check if port name is empty
00675     string port_short_name_str = string(port_name);
00676     if (port_short_name_str.size() == 0) {
00677         jack_error("port_name is empty");
00678         return 0; // Means failure here...
00679     }
00680 
00681     // Check port name length
00682     string port_full_name_str = string(GetClientControl()->fName) + string(":") + port_short_name_str;
00683     if (port_full_name_str.size() >= REAL_JACK_PORT_NAME_SIZE) {
00684         jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
00685                    "Please use %lu characters or less",
00686                    GetClientControl()->fName,
00687                    port_name,
00688                    JACK_PORT_NAME_SIZE - 1);
00689         return 0; // Means failure here...
00690     }
00691 
00692     int result = -1;
00693     jack_port_id_t port_index = NO_PORT;
00694     fChannel->PortRegister(GetClientControl()->fRefNum, port_full_name_str.c_str(), port_type, flags, buffer_size, &port_index, &result);
00695 
00696     if (result == 0) {
00697         jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, port_full_name_str.c_str(), port_type, port_index);
00698         fPortList.push_back(port_index);
00699         return port_index;
00700     } else {
00701         return 0;
00702     }
00703 }
00704 
00705 int JackClient::PortUnRegister(jack_port_id_t port_index)
00706 {
00707     jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
00708     list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
00709 
00710     if (it != fPortList.end()) {
00711         fPortList.erase(it);
00712         int result = -1;
00713         fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
00714         return result;
00715     } else {
00716         jack_error("unregistering a port %ld that is not own by the client", port_index);
00717         return -1;
00718     }
00719 }
00720 
00721 int JackClient::PortConnect(const char* src, const char* dst)
00722 {
00723     jack_log("JackClient::Connect src = %s dst = %s", src, dst);
00724     if (strlen(src) >= REAL_JACK_PORT_NAME_SIZE) {
00725         jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
00726         return -1; 
00727     }
00728     if (strlen(dst) >= REAL_JACK_PORT_NAME_SIZE) {
00729         jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
00730         return -1; 
00731     }
00732     int result = -1;
00733     fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
00734     return result;
00735 }
00736 
00737 int JackClient::PortDisconnect(const char* src, const char* dst)
00738 {
00739     jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
00740     if (strlen(src) >= REAL_JACK_PORT_NAME_SIZE) {
00741         jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
00742         return -1; 
00743     }
00744     if (strlen(dst) >= REAL_JACK_PORT_NAME_SIZE) {
00745         jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
00746         return -1; 
00747     }
00748     int result = -1;
00749     fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
00750     return result;
00751 }
00752 
00753 int JackClient::PortDisconnect(jack_port_id_t src)
00754 {
00755     jack_log("JackClient::PortDisconnect src = %ld", src);
00756     int result = -1;
00757     fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
00758     return result;
00759 }
00760 
00761 int JackClient::PortIsMine(jack_port_id_t port_index)
00762 {
00763     JackPort* port = GetGraphManager()->GetPort(port_index);
00764     return GetClientControl()->fRefNum == port->GetRefNum();
00765 }
00766 
00767 int JackClient::PortRename(jack_port_id_t port_index, const char* name)
00768 {
00769     int result = -1;
00770     fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
00771     return result;
00772 }
00773 
00774 //--------------------
00775 // Context management
00776 //--------------------
00777 
00778 int JackClient::SetBufferSize(jack_nframes_t buffer_size)
00779 {
00780     int result = -1;
00781     fChannel->SetBufferSize(buffer_size, &result);
00782     return result;
00783 }
00784 
00785 int JackClient::SetFreeWheel(int onoff)
00786 {
00787     int result = -1;
00788     fChannel->SetFreewheel(onoff, &result);
00789     return result;
00790 }
00791 
00792 int JackClient::ComputeTotalLatencies()
00793 {
00794     int result = -1;
00795     fChannel->ComputeTotalLatencies(&result);
00796     return result;
00797 }
00798 
00799 //----------------------
00800 // Transport management
00801 //----------------------
00802 
00803 inline int JackClient::ActivateAux()
00804 {
00805     // If activated without RT thread...
00806     if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
00807 
00808         jack_log("JackClient::ActivateAux");
00809 
00810         // RT thread is started
00811         if (StartThread() < 0) {
00812             return -1;
00813         }
00814 
00815         int result = -1;
00816         GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
00817         fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
00818         return result;
00819 
00820     } else {
00821         return 0;
00822     }
00823 }
00824 
00825 int JackClient::ReleaseTimebase()
00826 {
00827     int result = -1;
00828     fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
00829     if (result == 0) {
00830         GetClientControl()->fTransportTimebase = false;
00831         fTimebase = NULL;
00832         fTimebaseArg = NULL;
00833     }
00834     return result;
00835 }
00836 
00837 /* Call the server if the client is active, otherwise keeps the arguments */
00838 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
00839 {
00840     GetClientControl()->fTransportSync = (fSync != NULL);
00841     fSyncArg = arg;
00842     fSync = sync_callback;
00843     return ActivateAux();
00844 }
00845 
00846 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
00847 {
00848     int result = -1;
00849     fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
00850 
00851     if (result == 0) {
00852         GetClientControl()->fTransportTimebase = true;
00853         fTimebase = timebase_callback;
00854         fTimebaseArg = arg;
00855         return ActivateAux();
00856     } else {
00857         fTimebase = NULL;
00858         fTimebaseArg = NULL;
00859         return -1;
00860     }
00861 }
00862 
00863 int JackClient::SetSyncTimeout(jack_time_t timeout)
00864 {
00865     GetEngineControl()->fTransport.SetSyncTimeout(timeout);
00866     return 0;
00867 }
00868 
00869 // Must be RT safe
00870 
00871 void JackClient::TransportLocate(jack_nframes_t frame)
00872 {
00873     jack_position_t pos;
00874     pos.frame = frame;
00875     pos.valid = (jack_position_bits_t)0;
00876     jack_log("JackClient::TransportLocate pos = %ld", pos.frame);
00877     GetEngineControl()->fTransport.RequestNewPos(&pos);
00878 }
00879 
00880 int JackClient::TransportReposition(const jack_position_t* pos)
00881 {
00882     jack_position_t tmp = *pos;
00883     jack_log("JackClient::TransportReposition pos = %ld", pos->frame);
00884     if (tmp.valid & ~JACK_POSITION_MASK) {
00885         return EINVAL;
00886     } else {
00887         GetEngineControl()->fTransport.RequestNewPos(&tmp);
00888         return 0;
00889     }
00890 }
00891 
00892 jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
00893 {
00894     return GetEngineControl()->fTransport.Query(pos);
00895 }
00896 
00897 jack_nframes_t JackClient::GetCurrentTransportFrame()
00898 {
00899     return GetEngineControl()->fTransport.GetCurrentFrame();
00900 }
00901 
00902 // Must be RT safe: directly write in the transport shared mem
00903 void JackClient::TransportStart()
00904 {
00905     GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
00906 }
00907 
00908 // Must be RT safe: directly write in the transport shared mem
00909 void JackClient::TransportStop()
00910 {
00911     GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
00912 }
00913 
00914 // Never called concurently with the server
00915 // TODO check concurrency with SetSyncCallback
00916 
00917 void JackClient::CallSyncCallback()
00918 {
00919     CallSyncCallbackAux();
00920 }
00921 
00922 inline void JackClient::CallSyncCallbackAux()
00923 {
00924     if (GetClientControl()->fTransportSync) {
00925 
00926         JackTransportEngine& transport = GetEngineControl()->fTransport;
00927         jack_position_t* cur_pos = transport.ReadCurrentState();
00928         jack_transport_state_t transport_state = transport.GetState();
00929 
00930         if (fSync != NULL) {
00931             if (fSync(transport_state, cur_pos, fSyncArg)) {
00932                 GetClientControl()->fTransportState = JackTransportRolling;
00933                 GetClientControl()->fTransportSync = false;
00934             }
00935         } else {
00936             GetClientControl()->fTransportState = JackTransportRolling;
00937             GetClientControl()->fTransportSync = false;
00938         }
00939     }
00940 }
00941 
00942 void JackClient::CallTimebaseCallback()
00943 {
00944     CallTimebaseCallbackAux();
00945 }
00946 
00947 inline void JackClient::CallTimebaseCallbackAux()
00948 {
00949     JackTransportEngine& transport = GetEngineControl()->fTransport;
00950     int master;
00951     bool unused;
00952 
00953     transport.GetTimebaseMaster(master, unused);
00954 
00955     if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
00956 
00957         jack_transport_state_t transport_state = transport.GetState();
00958         jack_position_t* cur_pos = transport.WriteNextStateStart(1);
00959 
00960         if (GetClientControl()->fTransportTimebase) {
00961             fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
00962             GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
00963         } else if (transport_state == JackTransportRolling) {
00964             fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
00965         }
00966 
00967         transport.WriteNextStateStop(1);
00968     }
00969 }
00970 
00971 //---------------------
00972 // Callback management
00973 //---------------------
00974 
00975 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
00976 {
00977     if (IsActive()) {
00978         jack_error("You cannot set callbacks on an active client");
00979     } else {
00980         // Shutdown callback will either be an old API version or the new version (with info) 
00981         GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
00982         fShutdownArg = arg;
00983         fShutdown = callback;
00984     }
00985 }
00986 
00987 void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
00988 {
00989     if (IsActive()) {
00990         jack_error("You cannot set callbacks on an active client");
00991     } else {
00992         // Shutdown callback will either be an old API version or the new version (with info)
00993         GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
00994         fInfoShutdownArg = arg;
00995         fInfoShutdown = callback;
00996     }
00997 }
00998 
00999 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
01000 {
01001     if (IsActive()) {
01002         jack_error("You cannot set callbacks on an active client");
01003         return -1;
01004     } else if (fThreadFun) {
01005         jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
01006         return -1;
01007     } else {
01008         fProcessArg = arg;
01009         fProcess = callback;
01010         return 0;
01011     }
01012 }
01013 
01014 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
01015 {
01016     if (IsActive()) {
01017         jack_error("You cannot set callbacks on an active client");
01018         return -1;
01019     } else {
01020         GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
01021         fXrunArg = arg;
01022         fXrun = callback;
01023         return 0;
01024     }
01025 }
01026 
01027 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
01028 {
01029     if (IsActive()) {
01030         jack_error("You cannot set callbacks on an active client");
01031         return -1;
01032     } else {
01033         fInitArg = arg;
01034         fInit = callback;
01035         /* make sure that the message buffer thread is initialized too */
01036         return JackMessageBuffer::fInstance->SetInitCallback(callback, arg);
01037     }
01038 }
01039 
01040 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
01041 {
01042     if (IsActive()) {
01043         jack_error("You cannot set callbacks on an active client");
01044         return -1;
01045     } else {
01046         GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
01047         fGraphOrder = callback;
01048         fGraphOrderArg = arg;
01049         return 0;
01050     }
01051 }
01052 
01053 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
01054 {
01055     if (IsActive()) {
01056         jack_error("You cannot set callbacks on an active client");
01057         return -1;
01058     } else {
01059         GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL);
01060         fBufferSizeArg = arg;
01061         fBufferSize = callback;
01062         return 0;
01063     }
01064 }
01065 
01066 int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
01067 {
01068     if (IsActive()) {
01069         jack_error("You cannot set callbacks on an active client");
01070         return -1;
01071     } else {
01072         GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
01073         fSampleRateArg = arg;
01074         fSampleRate = callback;
01075         // Now invoke it
01076         if (callback) {
01077             callback(GetEngineControl()->fSampleRate, arg);
01078         }
01079         return 0;
01080     }
01081 }
01082 
01083 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
01084 {
01085     if (IsActive()) {
01086         jack_error("You cannot set callbacks on an active client");
01087         return -1;
01088     } else {
01089         // kAddClient and kRemoveClient notifications must be delivered by the server in any case
01090         fClientRegistrationArg = arg;
01091         fClientRegistration = callback;
01092         return 0;
01093     }
01094 }
01095 
01096 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
01097 {
01098     if (IsActive()) {
01099         jack_error("You cannot set callbacks on an active client");
01100         return -1;
01101     } else {
01102         GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL);
01103         GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
01104         fFreewheelArg = arg;
01105         fFreewheel = callback;
01106         return 0;
01107     }
01108 }
01109 
01110 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
01111 {
01112     if (IsActive()) {
01113         jack_error("You cannot set callbacks on an active client");
01114         return -1;
01115     } else {
01116         GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
01117         GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
01118         fPortRegistrationArg = arg;
01119         fPortRegistration = callback;
01120         return 0;
01121     }
01122 }
01123 
01124 int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
01125 {
01126     if (IsActive()) {
01127         jack_error("You cannot set callbacks on an active client");
01128         return -1;
01129     } else {
01130         GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
01131         GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
01132         fPortConnectArg = arg;
01133         fPortConnect = callback;
01134         return 0;
01135     }
01136 }
01137 
01138 int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
01139 {
01140     if (IsActive()) {
01141         jack_error("You cannot set callbacks on an active client");
01142         return -1;
01143     } else {
01144         GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
01145         fPortRenameArg = arg;
01146         fPortRename = callback;
01147         return 0;
01148     }
01149 }
01150 
01151 int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
01152 {
01153     if (IsActive()) {
01154         jack_error("You cannot set callbacks on an active client");
01155         return -1;
01156     } else if (fProcess) {
01157         jack_error("A process callback has already been setup, both models cannot be used at the same time!");
01158         return -1;
01159     } else {
01160         fThreadFun = fun;
01161         fThreadFunArg = arg;
01162         return 0;
01163     }
01164 }
01165 
01166 int JackClient::SetSessionCallback(JackSessionCallback callback, void *arg)
01167 {
01168     if (IsActive()) {
01169         jack_error("You cannot set callbacks on an active client");
01170         return -1;
01171     } else {
01172         GetClientControl()->fCallback[kSessionCallback] = (callback != NULL);
01173         fSessionArg = arg;
01174         fSession = callback;
01175         return 0;
01176     }
01177 }
01178 
01179 int JackClient::SetLatencyCallback(JackLatencyCallback callback, void *arg)
01180 {
01181     if (IsActive()) {
01182         jack_error("You cannot set callbacks on an active client");
01183         return -1;
01184     } else {
01185         // fCallback[kLatencyCallback] must always be 'true'
01186         fLatencyArg = arg;
01187         fLatency = callback;
01188         return 0;
01189     }
01190 }
01191 
01192 //------------------
01193 // Internal clients
01194 //------------------
01195 
01196 char* JackClient::GetInternalClientName(int ref)
01197 {
01198     char name_res[JACK_CLIENT_NAME_SIZE + 1];
01199     int result = -1;
01200     fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
01201     return (result < 0) ? NULL : strdup(name_res);
01202 }
01203 
01204 int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
01205 {
01206     int int_ref, result = -1;
01207     fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
01208     return int_ref;
01209 }
01210 
01211 int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va)
01212 {
01213     if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) {
01214         jack_error ("\"%s\" is too long for a JACK client name.\n"
01215                     "Please use %lu characters or less.",
01216                     client_name, JACK_CLIENT_NAME_SIZE);
01217         return 0;
01218     }
01219 
01220     if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) {
01221         jack_error("\"%s\" is too long for a shared object name.\n"
01222                    "Please use %lu characters or less.",
01223                    va->load_name, JACK_PATH_MAX);
01224         int my_status1 = *status | (JackFailure | JackInvalidOption);
01225         *status = (jack_status_t)my_status1;
01226         return 0;
01227     }
01228 
01229     if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) {
01230         jack_error ("\"%s\" is too long for internal client init "
01231                     "string.\nPlease use %lu characters or less.",
01232                     va->load_init, JACK_LOAD_INIT_LIMIT);
01233         int my_status1 = *status | (JackFailure | JackInvalidOption);
01234         *status = (jack_status_t)my_status1;
01235         return 0;
01236     }
01237 
01238     int int_ref, result = -1;
01239     fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, -1, &result);
01240     return int_ref;
01241 }
01242 
01243 void JackClient::InternalClientUnload(int ref, jack_status_t* status)
01244 {
01245     int result = -1;
01246     fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result);
01247 }
01248 
01249 //------------------
01250 // Session API
01251 //------------------
01252 
01253 jack_session_command_t* JackClient::SessionNotify(const char* target, jack_session_event_type_t type, const char* path)
01254 {
01255     jack_session_command_t* res;
01256     fChannel->SessionNotify(GetClientControl()->fRefNum, target, type, path, &res);
01257     return res;
01258 }
01259 
01260 int JackClient::SessionReply(jack_session_event_t* ev)
01261 {
01262     if (ev->command_line) {
01263         strncpy(GetClientControl()->fSessionCommand, ev->command_line, sizeof(GetClientControl()->fSessionCommand));
01264     } else {
01265         GetClientControl()->fSessionCommand[0] = '\0';
01266     }
01267 
01268     GetClientControl()->fSessionFlags = ev->flags;
01269 
01270     jack_log("JackClient::SessionReply... we are here");
01271     if (fChannel->IsChannelThread()) {
01272         jack_log("JackClient::SessionReply... in callback reply");
01273         // OK, immediate reply...
01274         fSessionReply = kImmediateSessionReply;
01275         return 0;
01276     }
01277 
01278     jack_log("JackClient::SessionReply... out of cb");
01279 
01280     int result = -1;
01281     fChannel->SessionReply(GetClientControl()->fRefNum, &result);
01282     return result;
01283 }
01284 
01285 char* JackClient::GetUUIDForClientName(const char* client_name)
01286 {
01287     char uuid_res[JACK_UUID_SIZE];
01288     int result = -1;
01289     fChannel->GetUUIDForClientName(GetClientControl()->fRefNum, client_name, uuid_res, &result);
01290     return (result) ? NULL : strdup(uuid_res);
01291 }
01292 
01293 char* JackClient::GetClientNameByUUID(const char* uuid)
01294 {
01295     char name_res[JACK_CLIENT_NAME_SIZE + 1];
01296     int result = -1;
01297     fChannel->GetClientNameForUUID(GetClientControl()->fRefNum, uuid, name_res, &result);
01298     return (result) ? NULL : strdup(name_res);
01299 }
01300 
01301 int JackClient::ReserveClientName(const char* client_name, const char* uuid)
01302 {
01303     int result = -1;
01304     fChannel->ReserveClientName( GetClientControl()->fRefNum, client_name, uuid, &result);
01305     return result;
01306 }
01307 
01308 int JackClient::ClientHasSessionCallback(const char* client_name)
01309 {
01310     int result = -1;
01311     fChannel->ClientHasSessionCallback(client_name, &result);
01312     return result;
01313 }
01314 
01315 } // end of namespace
01316