Jack2 1.9.7
JackEngine.cpp
00001 /*
00002 Copyright (C) 2004-2008 Grame
00003 
00004 This program is free software; you can redistribute it and/or modify
00005 it under the terms of the GNU General Public License as published by
00006 the Free Software Foundation; either version 2 of the License, or
00007 (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017 
00018 */
00019 
00020 #include <iostream>
00021 #include <fstream>
00022 #include <set>
00023 #include <assert.h>
00024 
00025 #include "JackSystemDeps.h"
00026 #include "JackLockedEngine.h"
00027 #include "JackExternalClient.h"
00028 #include "JackInternalClient.h"
00029 #include "JackEngineControl.h"
00030 #include "JackClientControl.h"
00031 #include "JackServerGlobals.h"
00032 #include "JackGlobals.h"
00033 #include "JackChannel.h"
00034 #include "JackError.h"
00035 
00036 namespace Jack
00037 {
00038 
00039 JackEngine::JackEngine(JackGraphManager* manager,
00040                        JackSynchro* table,
00041                        JackEngineControl* control)
00042 {
00043     fGraphManager = manager;
00044     fSynchroTable = table;
00045     fEngineControl = control;
00046     for (int i = 0; i < CLIENT_NUM; i++)
00047         fClientTable[i] = NULL;
00048     fLastSwitchUsecs = 0;
00049     fMaxUUID = 0;
00050     fSessionPendingReplies = 0;
00051     fSessionTransaction = NULL;
00052     fSessionResult = NULL;
00053 }
00054 
00055 JackEngine::~JackEngine()
00056 {}
00057 
00058 int JackEngine::Open()
00059 {
00060     jack_log("JackEngine::Open");
00061 
00062     // Open audio thread => request thread communication channel
00063     if (fChannel.Open(fEngineControl->fServerName) < 0) {
00064         jack_error("Cannot connect to server");
00065         return -1;
00066     } else {
00067         return 0;
00068     }
00069 }
00070 
00071 int JackEngine::Close()
00072 {
00073     jack_log("JackEngine::Close");
00074     fChannel.Close();
00075 
00076     // Close remaining clients (RT is stopped)
00077     for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
00078         if (JackLoadableInternalClient* loadable_client = dynamic_cast<JackLoadableInternalClient*>(fClientTable[i])) {
00079             jack_log("JackEngine::Close loadable client = %s", loadable_client->GetClientControl()->fName);
00080             loadable_client->Close();
00081             // Close does not delete the pointer for internal clients
00082             fClientTable[i] = NULL;
00083             delete loadable_client;
00084         } else if (JackExternalClient* external_client = dynamic_cast<JackExternalClient*>(fClientTable[i])) {
00085             jack_log("JackEngine::Close external client = %s", external_client->GetClientControl()->fName);
00086             external_client->Close();
00087             // Close deletes the pointer for external clients
00088             fClientTable[i] = NULL;
00089         }
00090     }
00091 
00092     return 0;
00093 }
00094 
00095 void JackEngine::NotifyQuit()
00096 {
00097     fChannel.NotifyQuit();
00098 }
00099 
00100 //-----------------------------
00101 // Client ressource management
00102 //-----------------------------
00103 
00104 int JackEngine::AllocateRefnum()
00105 {
00106     for (int i = 0; i < CLIENT_NUM; i++) {
00107         if (!fClientTable[i]) {
00108             jack_log("JackEngine::AllocateRefNum ref = %ld", i);
00109             return i;
00110         }
00111     }
00112     return -1;
00113 }
00114 
00115 void JackEngine::ReleaseRefnum(int ref)
00116 {
00117     fClientTable[ref] = NULL;
00118 
00119     if (fEngineControl->fTemporary) {
00120         int i;
00121         for (i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
00122             if (fClientTable[i])
00123                 break;
00124         }
00125         if (i == CLIENT_NUM) {
00126             // last client and temporay case: quit the server
00127             jack_log("JackEngine::ReleaseRefnum server quit");
00128             fEngineControl->fTemporary = false;
00129             throw JackTemporaryException();
00130         }
00131     }
00132 }
00133 
00134 //------------------
00135 // Graph management
00136 //------------------
00137 
00138 void JackEngine::ProcessNext(jack_time_t cur_cycle_begin)
00139 {
00140     fLastSwitchUsecs = cur_cycle_begin;
00141     if (fGraphManager->RunNextGraph())  { // True if the graph actually switched to a new state
00142         fChannel.Notify(ALL_CLIENTS, kGraphOrderCallback, 0);
00143         //NotifyGraphReorder();
00144     }
00145     fSignal.Signal();                   // Signal for threads waiting for next cycle
00146 }
00147 
00148 void JackEngine::ProcessCurrent(jack_time_t cur_cycle_begin)
00149 {
00150     if (cur_cycle_begin < fLastSwitchUsecs + 2 * fEngineControl->fPeriodUsecs) // Signal XRun only for the first failing cycle
00151         CheckXRun(cur_cycle_begin);
00152     fGraphManager->RunCurrentGraph();
00153 }
00154 
00155 bool JackEngine::Process(jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end)
00156 {
00157     bool res = true;
00158 
00159     // Cycle  begin
00160     fEngineControl->CycleBegin(fClientTable, fGraphManager, cur_cycle_begin, prev_cycle_end);
00161 
00162     // Graph
00163     if (fGraphManager->IsFinishedGraph()) {
00164         ProcessNext(cur_cycle_begin);
00165         res = true;
00166     } else {
00167         jack_log("Process: graph not finished!");
00168         if (cur_cycle_begin > fLastSwitchUsecs + fEngineControl->fTimeOutUsecs) {
00169             jack_log("Process: switch to next state delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs));
00170             ProcessNext(cur_cycle_begin);
00171             res = true;
00172         } else {
00173             jack_log("Process: waiting to switch delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs));
00174             ProcessCurrent(cur_cycle_begin);
00175             res = false;
00176         }
00177     }
00178 
00179     // Cycle end
00180     fEngineControl->CycleEnd(fClientTable);
00181     return res;
00182 }
00183 
00184 /*
00185 Client that finish *after* the callback date are considered late even if their output buffers may have been
00186 correctly mixed in the time window: callbackUsecs <==> Read <==> Write.
00187 */
00188 
00189 void JackEngine::CheckXRun(jack_time_t callback_usecs)  // REVOIR les conditions de fin
00190 {
00191     for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
00192         JackClientInterface* client = fClientTable[i];
00193         if (client && client->GetClientControl()->fActive) {
00194             JackClientTiming* timing = fGraphManager->GetClientTiming(i);
00195             jack_client_state_t status = timing->fStatus;
00196             jack_time_t finished_date = timing->fFinishedAt;
00197 
00198             if (status != NotTriggered && status != Finished) {
00199                 jack_error("JackEngine::XRun: client = %s was not run: state = %ld", client->GetClientControl()->fName, status);
00200                 fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);  // Notify all clients
00201                 //NotifyXRun(ALL_CLIENTS);
00202             }
00203 
00204             if (status == Finished && (long)(finished_date - callback_usecs) > 0) {
00205                 jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName);
00206                 fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);  // Notify all clients
00207                 //NotifyXRun(ALL_CLIENTS);
00208             }
00209         }
00210     }
00211 }
00212 
00213 int JackEngine::ComputeTotalLatencies()
00214 {
00215     std::vector<jack_int_t> sorted;
00216     std::vector<jack_int_t>::iterator it;
00217     std::vector<jack_int_t>::reverse_iterator rit;
00218 
00219     fGraphManager->TopologicalSort(sorted);
00220 
00221     /* iterate over all clients in graph order, and emit
00222          * capture latency callback.
00223          */
00224 
00225     for (it = sorted.begin(); it != sorted.end(); it++) {
00226         NotifyClient(*it, kLatencyCallback, true, "", 0, 0);
00227     }
00228 
00229     /* now issue playback latency callbacks in reverse graph order.
00230          */
00231     for (rit = sorted.rbegin(); rit != sorted.rend(); rit++) {
00232         NotifyClient(*rit, kLatencyCallback, true, "", 1, 0);
00233     }
00234 
00235     return 0;
00236 }
00237 
00238 //---------------
00239 // Notifications
00240 //---------------
00241 
00242 void JackEngine::NotifyClient(int refnum, int event, int sync, const char* message, int value1, int value2)
00243 {
00244     JackClientInterface* client = fClientTable[refnum];
00245 
00246     // The client may be notified by the RT thread while closing
00247     if (client) {
00248 
00249         if (client->GetClientControl()->fCallback[event]) {
00250             /*
00251                 Important for internal clients : unlock before calling the notification callbacks.
00252             */
00253             bool res = fMutex.Unlock();
00254             if (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, message, value1, value2) < 0)
00255                 jack_error("NotifyClient fails name = %s event = %ld val1 = %ld val2 = %ld", client->GetClientControl()->fName, event, value1, value2);
00256             if (res)
00257                 fMutex.Lock();
00258 
00259         } else {
00260             jack_log("JackEngine::NotifyClient: no callback for event = %ld", event);
00261         }
00262     }
00263 }
00264 
00265 void JackEngine::NotifyClients(int event, int sync, const char* message, int value1, int value2)
00266 {
00267     for (int i = 0; i < CLIENT_NUM; i++) {
00268         NotifyClient(i, event, sync, message, value1, value2);
00269     }
00270 }
00271 
00272 int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum)
00273 {
00274     jack_log("JackEngine::NotifyAddClient: name = %s", name);
00275     // Notify existing clients of the new client and new client of existing clients.
00276     for (int i = 0; i < CLIENT_NUM; i++) {
00277         JackClientInterface* old_client = fClientTable[i];
00278         if (old_client) {
00279             if (old_client->ClientNotify(refnum, name, kAddClient, true, "", 0, 0) < 0) {
00280                 jack_error("NotifyAddClient old_client fails name = %s", old_client->GetClientControl()->fName);
00281                 return -1;
00282             }
00283             if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, kAddClient, true, "", 0, 0) < 0) {
00284                 jack_error("NotifyAddClient new_client fails name = %s", name);
00285                 return -1;
00286             }
00287         }
00288     }
00289 
00290     return 0;
00291 }
00292 
00293 void JackEngine::NotifyRemoveClient(const char* name, int refnum)
00294 {
00295     // Notify existing clients (including the one beeing suppressed) of the removed client
00296     for (int i = 0; i < CLIENT_NUM; i++) {
00297         JackClientInterface* client = fClientTable[i];
00298         if (client) {
00299             client->ClientNotify(refnum, name, kRemoveClient, true, "",0, 0);
00300         }
00301     }
00302 }
00303 
00304 // Coming from the driver
00305 void JackEngine::NotifyXRun(jack_time_t callback_usecs, float delayed_usecs)
00306 {
00307     // Use the audio thread => request thread communication channel
00308     fEngineControl->NotifyXRun(callback_usecs, delayed_usecs);
00309     fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);
00310     //NotifyXRun(ALL_CLIENTS);
00311 }
00312 
00313 void JackEngine::NotifyXRun(int refnum)
00314 {
00315     if (refnum == ALL_CLIENTS) {
00316         NotifyClients(kXRunCallback, false, "", 0, 0);
00317     } else {
00318         NotifyClient(refnum, kXRunCallback, false, "", 0, 0);
00319     }
00320 }
00321 
00322 void JackEngine::NotifyGraphReorder()
00323 {
00324     NotifyClients(kGraphOrderCallback, false, "", 0, 0);
00325     ComputeTotalLatencies();
00326 }
00327 
00328 void JackEngine::NotifyBufferSize(jack_nframes_t buffer_size)
00329 {
00330     NotifyClients(kBufferSizeCallback, true, "", buffer_size, 0);
00331 }
00332 
00333 void JackEngine::NotifySampleRate(jack_nframes_t sample_rate)
00334 {
00335     NotifyClients(kSampleRateCallback, true, "", sample_rate, 0);
00336 }
00337 
00338 void JackEngine::NotifyFailure(int code, const char* reason)
00339 {
00340     NotifyClients(kShutDownCallback, false, reason, code, 0);
00341 }
00342 
00343 void JackEngine::NotifyFreewheel(bool onoff)
00344 {
00345     if (onoff) {
00346         // Save RT state
00347         fEngineControl->fSavedRealTime = fEngineControl->fRealTime;
00348         fEngineControl->fRealTime = false;
00349     } else {
00350         // Restore RT state
00351         fEngineControl->fRealTime = fEngineControl->fSavedRealTime;
00352         fEngineControl->fSavedRealTime = false;
00353     }
00354     NotifyClients((onoff ? kStartFreewheelCallback : kStopFreewheelCallback), true, "", 0, 0);
00355 }
00356 
00357 void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff)
00358 {
00359     NotifyClients((onoff ? kPortRegistrationOnCallback : kPortRegistrationOffCallback), false, "", port_index, 0);
00360 }
00361 
00362 void JackEngine::NotifyPortRename(jack_port_id_t port, const char* old_name)
00363 {
00364     NotifyClients(kPortRenameCallback, false, old_name, port, 0);
00365 }
00366 
00367 void JackEngine::NotifyPortConnect(jack_port_id_t src, jack_port_id_t dst, bool onoff)
00368 {
00369     NotifyClients((onoff ? kPortConnectCallback : kPortDisconnectCallback), false, "", src, dst);
00370 }
00371 
00372 void JackEngine::NotifyActivate(int refnum)
00373 {
00374     NotifyClient(refnum, kActivateClient, true, "", 0, 0);
00375 }
00376 
00377 //----------------------------
00378 // Loadable client management
00379 //----------------------------
00380 
00381 int JackEngine::GetInternalClientName(int refnum, char* name_res)
00382 {
00383     JackClientInterface* client = fClientTable[refnum];
00384     strncpy(name_res, client->GetClientControl()->fName, JACK_CLIENT_NAME_SIZE);
00385     return 0;
00386 }
00387 
00388 int JackEngine::InternalClientHandle(const char* client_name, int* status, int* int_ref)
00389 {
00390     // Clear status
00391     *status = 0;
00392 
00393     for (int i = 0; i < CLIENT_NUM; i++) {
00394         JackClientInterface* client = fClientTable[i];
00395         if (client && dynamic_cast<JackLoadableInternalClient*>(client) && (strcmp(client->GetClientControl()->fName, client_name) == 0)) {
00396             jack_log("InternalClientHandle found client name = %s ref = %ld",  client_name, i);
00397             *int_ref = i;
00398             return 0;
00399         }
00400     }
00401 
00402     *status |= (JackNoSuchClient | JackFailure);
00403     return -1;
00404 }
00405 
00406 int JackEngine::InternalClientUnload(int refnum, int* status)
00407 {
00408     JackClientInterface* client = fClientTable[refnum];
00409     if (client) {
00410         int res = client->Close();
00411         delete client;
00412         *status = 0;
00413         return res;
00414     } else {
00415         *status = (JackNoSuchClient | JackFailure);
00416         return -1;
00417     }
00418 }
00419 
00420 //-------------------
00421 // Client management
00422 //-------------------
00423 
00424 int JackEngine::ClientCheck(const char* name, int uuid, char* name_res, int protocol, int options, int* status)
00425 {
00426     // Clear status
00427     *status = 0;
00428     strcpy(name_res, name);
00429 
00430     jack_log("Check protocol client %ld server = %ld", protocol, JACK_PROTOCOL_VERSION);
00431 
00432     if (protocol != JACK_PROTOCOL_VERSION) {
00433         *status |= (JackFailure | JackVersionError);
00434         jack_error("JACK protocol mismatch (%d vs %d)", protocol, JACK_PROTOCOL_VERSION);
00435         return -1;
00436     }
00437 
00438     std::map<int,std::string>::iterator res = fReservationMap.find(uuid);
00439 
00440     if (res != fReservationMap.end()) {
00441         strncpy(name_res, res->second.c_str(), JACK_CLIENT_NAME_SIZE);
00442     } else if (ClientCheckName(name)) {
00443 
00444         *status |= JackNameNotUnique;
00445 
00446         if (options & JackUseExactName) {
00447             jack_error("cannot create new client; %s already exists", name);
00448             *status |= JackFailure;
00449             return -1;
00450         }
00451 
00452         if (GenerateUniqueName(name_res)) {
00453             *status |= JackFailure;
00454             return -1;
00455         }
00456     }
00457 
00458     return 0;
00459 }
00460 
00461 bool JackEngine::GenerateUniqueName(char* name)
00462 {
00463     int tens, ones;
00464     int length = strlen(name);
00465 
00466     if (length > JACK_CLIENT_NAME_SIZE - 4) {
00467         jack_error("%s exists and is too long to make unique", name);
00468         return true;            /* failure */
00469     }
00470 
00471     /*  generate a unique name by appending "-01".."-99" */
00472     name[length++] = '-';
00473     tens = length++;
00474     ones = length++;
00475     name[tens] = '0';
00476     name[ones] = '1';
00477     name[length] = '\0';
00478 
00479     while (ClientCheckName(name)) {
00480         if (name[ones] == '9') {
00481             if (name[tens] == '9') {
00482                 jack_error("client %s has 99 extra instances already", name);
00483                 return true; /* give up */
00484             }
00485             name[tens]++;
00486             name[ones] = '0';
00487         } else {
00488             name[ones]++;
00489         }
00490     }
00491     return false;
00492 }
00493 
00494 bool JackEngine::ClientCheckName(const char* name)
00495 {
00496     for (int i = 0; i < CLIENT_NUM; i++) {
00497         JackClientInterface* client = fClientTable[i];
00498         if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
00499             return true;
00500     }
00501 
00502     for (std::map<int,std::string>::iterator i = fReservationMap.begin(); i != fReservationMap.end(); i++) {
00503         if (i->second == name)
00504             return true;
00505     }
00506 
00507     return false;
00508 }
00509 
00510 int JackEngine::GetNewUUID()
00511 {
00512     return fMaxUUID++;
00513 }
00514 
00515 void JackEngine::EnsureUUID(int uuid)
00516 {
00517     if (uuid > fMaxUUID)
00518         fMaxUUID = uuid+1;
00519 
00520     for (int i = 0; i < CLIENT_NUM; i++) {
00521         JackClientInterface* client = fClientTable[i];
00522         if (client && (client->GetClientControl()->fSessionID == uuid)) {
00523             client->GetClientControl()->fSessionID = GetNewUUID();
00524         }
00525     }
00526 }
00527 
00528 int JackEngine::GetClientPID(const char* name)
00529 {
00530     for (int i = 0; i < CLIENT_NUM; i++) {
00531         JackClientInterface* client = fClientTable[i];
00532         if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
00533             return client->GetClientControl()->fPID;
00534     }
00535 
00536     return 0;
00537 }
00538 
00539 int JackEngine::GetClientRefNum(const char* name)
00540 {
00541     for (int i = 0; i < CLIENT_NUM; i++) {
00542         JackClientInterface* client = fClientTable[i];
00543         if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
00544             return client->GetClientControl()->fRefNum;
00545     }
00546 
00547     return -1;
00548 }
00549 
00550 // Used for external clients
00551 int JackEngine::ClientExternalOpen(const char* name, int pid, int uuid, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager)
00552 {
00553     char real_name[JACK_CLIENT_NAME_SIZE + 1];
00554 
00555     if (uuid < 0) {
00556         uuid = GetNewUUID();
00557         strncpy(real_name, name, JACK_CLIENT_NAME_SIZE);
00558     } else {
00559         std::map<int, std::string>::iterator res = fReservationMap.find(uuid);
00560         if (res != fReservationMap.end()) {
00561             strncpy(real_name, res->second.c_str(), JACK_CLIENT_NAME_SIZE);
00562             fReservationMap.erase(uuid);
00563         } else {
00564             strncpy(real_name, name, JACK_CLIENT_NAME_SIZE);
00565         }
00566 
00567         EnsureUUID(uuid);
00568     }
00569 
00570     jack_log("JackEngine::ClientExternalOpen: uuid = %d, name = %s ", uuid, real_name);
00571 
00572     int refnum = AllocateRefnum();
00573     if (refnum < 0) {
00574         jack_error("No more refnum available");
00575         return -1;
00576     }
00577 
00578     JackExternalClient* client = new JackExternalClient();
00579 
00580     if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0)) {
00581         jack_error("Cannot allocate synchro");
00582         goto error;
00583     }
00584 
00585     if (client->Open(real_name, pid, refnum, uuid, shared_client) < 0) {
00586         jack_error("Cannot open client");
00587         goto error;
00588     }
00589 
00590     if (!fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
00591         // Failure if RT thread is not running (problem with the driver...)
00592         jack_error("Driver is not running");
00593         goto error;
00594     }
00595 
00596     fClientTable[refnum] = client;
00597 
00598     if (NotifyAddClient(client, real_name, refnum) < 0) {
00599         jack_error("Cannot notify add client");
00600         goto error;
00601     }
00602 
00603     fGraphManager->InitRefNum(refnum);
00604     fEngineControl->ResetRollingUsecs();
00605     *shared_engine = fEngineControl->GetShmIndex();
00606     *shared_graph_manager = fGraphManager->GetShmIndex();
00607     *ref = refnum;
00608     return 0;
00609 
00610 error:
00611     // Cleanup...
00612     fSynchroTable[refnum].Destroy();
00613     fClientTable[refnum] = 0;
00614     client->Close();
00615     delete client;
00616     return -1;
00617 }
00618 
00619 // Used for server driver clients
00620 int JackEngine::ClientInternalOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, bool wait)
00621 {
00622     jack_log("JackEngine::ClientInternalOpen: name = %s", name);
00623 
00624     int refnum = AllocateRefnum();
00625     if (refnum < 0) {
00626         jack_error("No more refnum available");
00627         goto error;
00628     }
00629 
00630     if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) {
00631         jack_error("Cannot allocate synchro");
00632         goto error;
00633     }
00634 
00635     if (wait && !fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
00636         // Failure if RT thread is not running (problem with the driver...)
00637         jack_error("Driver is not running");
00638         goto error;
00639     }
00640 
00641     fClientTable[refnum] = client;
00642 
00643     if (NotifyAddClient(client, name, refnum) < 0) {
00644         jack_error("Cannot notify add client");
00645         goto error;
00646     }
00647 
00648     fGraphManager->InitRefNum(refnum);
00649     fEngineControl->ResetRollingUsecs();
00650     *shared_engine = fEngineControl;
00651     *shared_manager = fGraphManager;
00652     *ref = refnum;
00653     return 0;
00654 
00655 error:
00656     // Cleanup...
00657     fSynchroTable[refnum].Destroy();
00658     fClientTable[refnum] = 0;
00659     return -1;
00660 }
00661 
00662 // Used for external clients
00663 int JackEngine::ClientExternalClose(int refnum)
00664 {
00665     JackClientInterface* client = fClientTable[refnum];
00666     fEngineControl->fTransport.ResetTimebase(refnum);
00667     int res = ClientCloseAux(refnum, client, true);
00668     client->Close();
00669     delete client;
00670     return res;
00671 }
00672 
00673 // Used for server internal clients or drivers when the RT thread is stopped
00674 int JackEngine::ClientInternalClose(int refnum, bool wait)
00675 {
00676     JackClientInterface* client = fClientTable[refnum];
00677     return ClientCloseAux(refnum, client, wait);
00678 }
00679 
00680 int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wait)
00681 {
00682     jack_log("JackEngine::ClientCloseAux ref = %ld", refnum);
00683 
00684     // Unregister all ports ==> notifications are sent
00685     jack_int_t ports[PORT_NUM_FOR_CLIENT];
00686     int i;
00687 
00688     fGraphManager->GetInputPorts(refnum, ports);
00689     for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) {
00690         PortUnRegister(refnum, ports[i]);
00691     }
00692 
00693     fGraphManager->GetOutputPorts(refnum, ports);
00694     for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) {
00695         PortUnRegister(refnum, ports[i]);
00696     }
00697 
00698     // Remove the client from the table
00699     ReleaseRefnum(refnum);
00700 
00701     // Remove all ports
00702     fGraphManager->RemoveAllPorts(refnum);
00703 
00704     // Wait until next cycle to be sure client is not used anymore
00705     if (wait) {
00706         if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure
00707             jack_error("JackEngine::ClientCloseAux wait error ref = %ld", refnum);
00708         }
00709     }
00710 
00711     // Notify running clients
00712     NotifyRemoveClient(client->GetClientControl()->fName, client->GetClientControl()->fRefNum);
00713 
00714     // Cleanup...
00715     fSynchroTable[refnum].Destroy();
00716     fEngineControl->ResetRollingUsecs();
00717     return 0;
00718 }
00719 
00720 int JackEngine::ClientActivate(int refnum, bool is_real_time)
00721 {
00722     JackClientInterface* client = fClientTable[refnum];
00723     jack_log("JackEngine::ClientActivate ref = %ld name = %s", refnum, client->GetClientControl()->fName);
00724 
00725     if (is_real_time)
00726         fGraphManager->Activate(refnum);
00727 
00728     // Wait for graph state change to be effective
00729     if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
00730         jack_error("JackEngine::ClientActivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
00731         return -1;
00732     } else {
00733         jack_int_t input_ports[PORT_NUM_FOR_CLIENT];
00734         jack_int_t output_ports[PORT_NUM_FOR_CLIENT];
00735         fGraphManager->GetInputPorts(refnum, input_ports);
00736         fGraphManager->GetOutputPorts(refnum, output_ports);
00737 
00738         // Notify client
00739         NotifyActivate(refnum);
00740 
00741         // Then issue port registration notification
00742         for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) {
00743             NotifyPortRegistation(input_ports[i], true);
00744         }
00745         for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) {
00746             NotifyPortRegistation(output_ports[i], true);
00747         }
00748 
00749         return 0;
00750     }
00751 }
00752 
00753 // May be called without client
00754 int JackEngine::ClientDeactivate(int refnum)
00755 {
00756     JackClientInterface* client = fClientTable[refnum];
00757     jack_log("JackEngine::ClientDeactivate ref = %ld name = %s", refnum, client->GetClientControl()->fName);
00758 
00759     jack_int_t input_ports[PORT_NUM_FOR_CLIENT];
00760     jack_int_t output_ports[PORT_NUM_FOR_CLIENT];
00761     fGraphManager->GetInputPorts(refnum, input_ports);
00762     fGraphManager->GetOutputPorts(refnum, output_ports);
00763 
00764     // First disconnect all ports
00765     for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) {
00766         PortDisconnect(refnum, input_ports[i], ALL_PORTS);
00767     }
00768     for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) {
00769         PortDisconnect(refnum, output_ports[i], ALL_PORTS);
00770     }
00771 
00772     // Then issue port registration notification
00773     for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) {
00774         NotifyPortRegistation(input_ports[i], false);
00775     }
00776     for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) {
00777         NotifyPortRegistation(output_ports[i], false);
00778     }
00779 
00780     fGraphManager->Deactivate(refnum);
00781     fLastSwitchUsecs = 0; // Force switch to occur next cycle, even when called with "dead" clients
00782 
00783     // Wait for graph state change to be effective
00784     if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
00785         jack_error("JackEngine::ClientDeactivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
00786         return -1;
00787     } else {
00788         return 0;
00789     }
00790 }
00791 
00792 //-----------------
00793 // Port management
00794 //-----------------
00795 
00796 int JackEngine::PortRegister(int refnum, const char* name, const char *type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index)
00797 {
00798     jack_log("JackEngine::PortRegister ref = %ld name = %s type = %s flags = %d buffer_size = %d", refnum, name, type, flags, buffer_size);
00799     JackClientInterface* client = fClientTable[refnum];
00800 
00801     // Check if port name already exists
00802     if (fGraphManager->GetPort(name) != NO_PORT) {
00803         jack_error("port_name \"%s\" already exists", name);
00804         return -1;
00805     }
00806 
00807     *port_index = fGraphManager->AllocatePort(refnum, name, type, (JackPortFlags)flags, fEngineControl->fBufferSize);
00808     if (*port_index != NO_PORT) {
00809         if (client->GetClientControl()->fActive)
00810             NotifyPortRegistation(*port_index, true);
00811         return 0;
00812     } else {
00813         return -1;
00814     }
00815 }
00816 
00817 int JackEngine::PortUnRegister(int refnum, jack_port_id_t port_index)
00818 {
00819     jack_log("JackEngine::PortUnRegister ref = %ld port_index = %ld", refnum, port_index);
00820     JackClientInterface* client = fClientTable[refnum];
00821 
00822     // Disconnect port ==> notification is sent
00823     PortDisconnect(refnum, port_index, ALL_PORTS);
00824 
00825     if (fGraphManager->ReleasePort(refnum, port_index) == 0) {
00826         if (client->GetClientControl()->fActive)
00827             NotifyPortRegistation(port_index, false);
00828         return 0;
00829     } else {
00830         return -1;
00831     }
00832 }
00833 
00834 int JackEngine::PortConnect(int refnum, const char* src, const char* dst)
00835 {
00836     jack_log("JackEngine::PortConnect src = %s dst = %s", src, dst);
00837     jack_port_id_t port_src, port_dst;
00838 
00839     return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0)
00840            ? -1
00841            : PortConnect(refnum, port_src, port_dst);
00842 }
00843 
00844 int JackEngine::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
00845 {
00846     jack_log("JackEngine::PortConnect src = %d dst = %d", src, dst);
00847     JackClientInterface* client;
00848     int ref;
00849 
00850     if (fGraphManager->CheckPorts(src, dst) < 0)
00851         return -1;
00852 
00853     ref = fGraphManager->GetOutputRefNum(src);
00854     assert(ref >= 0);
00855     client = fClientTable[ref];
00856     assert(client);
00857     if (!client->GetClientControl()->fActive) {
00858         jack_error("Cannot connect ports owned by inactive clients:"
00859                    " \"%s\" is not active", client->GetClientControl()->fName);
00860         return -1;
00861     }
00862 
00863     ref = fGraphManager->GetInputRefNum(dst);
00864     assert(ref >= 0);
00865     client = fClientTable[ref];
00866     assert(client);
00867     if (!client->GetClientControl()->fActive) {
00868         jack_error("Cannot connect ports owned by inactive clients:"
00869                    " \"%s\" is not active", client->GetClientControl()->fName);
00870         return -1;
00871     }
00872 
00873     int res = fGraphManager->Connect(src, dst);
00874     if (res == 0)
00875         NotifyPortConnect(src, dst, true);
00876     return res;
00877 }
00878 
00879 int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst)
00880 {
00881     jack_log("JackEngine::PortDisconnect src = %s dst = %s", src, dst);
00882     jack_port_id_t port_src, port_dst;
00883 
00884     return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0)
00885            ? -1
00886            : PortDisconnect(refnum, port_src, port_dst);
00887 }
00888 
00889 int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
00890 {
00891     jack_log("JackEngine::PortDisconnect src = %d dst = %d", src, dst);
00892 
00893     if (dst == ALL_PORTS) {
00894 
00895         jack_int_t connections[CONNECTION_NUM_FOR_PORT];
00896         fGraphManager->GetConnections(src, connections);
00897 
00898         JackPort* port = fGraphManager->GetPort(src);
00899         int ret = 0;
00900         if (port->GetFlags() & JackPortIsOutput) {
00901             for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
00902                 if (PortDisconnect(refnum, src, connections[i]) != 0) {
00903                     ret = -1;
00904                 }
00905             }
00906         } else {
00907             for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
00908                 if (PortDisconnect(refnum, connections[i], src) != 0) {
00909                     ret = -1;
00910                 }
00911             }
00912         }
00913 
00914         return ret;
00915     } else if (fGraphManager->CheckPorts(src, dst) < 0) {
00916         return -1;
00917     } else if (fGraphManager->Disconnect(src, dst) == 0) {
00918         // Notifications
00919         NotifyPortConnect(src, dst, false);
00920         return 0;
00921     } else {
00922         return -1;
00923     }
00924 }
00925 
00926 int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name)
00927 {
00928     char old_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
00929     strcpy(old_name, fGraphManager->GetPort(port)->GetName());
00930     fGraphManager->GetPort(port)->SetName(name);
00931     NotifyPortRename(port, old_name);
00932     return 0;
00933 }
00934 
00935 //--------------------
00936 // Session management
00937 //--------------------
00938 
00939 void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket)
00940 {
00941     if (fSessionPendingReplies != 0) {
00942         JackSessionNotifyResult res(-1);
00943         res.Write(socket);
00944         jack_log("JackEngine::SessionNotify ... busy");
00945         return;
00946     }
00947 
00948     for (int i = 0; i < CLIENT_NUM; i++) {
00949         JackClientInterface* client = fClientTable[i];
00950         if (client && (client->GetClientControl()->fSessionID < 0)) {
00951             client->GetClientControl()->fSessionID = GetNewUUID();
00952         }
00953     }
00954     fSessionResult = new JackSessionNotifyResult();
00955 
00956     for (int i = 0; i < CLIENT_NUM; i++) {
00957         JackClientInterface* client = fClientTable[i];
00958         if (client && client->GetClientControl()->fCallback[kSessionCallback]) {
00959 
00960             // check if this is a notification to a specific client.
00961             if (target != NULL && strlen(target) != 0) {
00962                 if (strcmp(target, client->GetClientControl()->fName)) {
00963                     continue;
00964                 }
00965             }
00966 
00967             char path_buf[JACK_PORT_NAME_SIZE];
00968             snprintf( path_buf, sizeof(path_buf), "%s%s%c", path, client->GetClientControl()->fName, DIR_SEPARATOR );
00969 
00970             int res = JackTools::MkDir(path_buf);
00971             if (res)
00972                 jack_error( "JackEngine::SessionNotify: can not create session directory '%s'", path_buf );
00973 
00974             int result = client->ClientNotify(i, client->GetClientControl()->fName, kSessionCallback, true, path_buf, (int) type, 0);
00975 
00976             if (result == 2) {
00977                 fSessionPendingReplies += 1;
00978             } else if (result == 1) {
00979                 char uuid_buf[JACK_UUID_SIZE];
00980                 snprintf( uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID );
00981                 fSessionResult->fCommandList.push_back( JackSessionCommand( uuid_buf,
00982                                                                             client->GetClientControl()->fName,
00983                                                                             client->GetClientControl()->fSessionCommand,
00984                                                                             client->GetClientControl()->fSessionFlags ));
00985             }
00986         }
00987     }
00988 
00989     if (fSessionPendingReplies == 0) {
00990         fSessionResult->Write(socket);
00991         delete fSessionResult;
00992         fSessionResult = NULL;
00993     } else {
00994         fSessionTransaction = socket;
00995     }
00996 }
00997 
00998 void JackEngine::SessionReply(int refnum)
00999 {
01000     JackClientInterface* client = fClientTable[refnum];
01001     char uuid_buf[JACK_UUID_SIZE];
01002     snprintf( uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID);
01003     fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf,
01004                                                             client->GetClientControl()->fName,
01005                                                             client->GetClientControl()->fSessionCommand,
01006                                                             client->GetClientControl()->fSessionFlags));
01007     fSessionPendingReplies -= 1;
01008 
01009     if (fSessionPendingReplies == 0) {
01010         fSessionResult->Write(fSessionTransaction);
01011         delete fSessionResult;
01012         fSessionResult = NULL;
01013     }
01014 }
01015 
01016 void JackEngine::GetUUIDForClientName(const char *client_name, char *uuid_res, int *result)
01017 {
01018     for (int i = 0; i < CLIENT_NUM; i++) {
01019         JackClientInterface* client = fClientTable[i];
01020 
01021         if (client && (strcmp(client_name, client->GetClientControl()->fName) == 0)) {
01022             snprintf(uuid_res, JACK_UUID_SIZE, "%d", client->GetClientControl()->fSessionID);
01023             *result = 0;
01024             return;
01025         }
01026     }
01027     // Did not find name.
01028     *result = -1;
01029 }
01030 
01031 void JackEngine::GetClientNameForUUID(const char *uuid, char *name_res, int *result)
01032 {
01033     for (int i = 0; i < CLIENT_NUM; i++) {
01034         JackClientInterface* client = fClientTable[i];
01035 
01036         if (!client)
01037             continue;
01038 
01039         char uuid_buf[JACK_UUID_SIZE];
01040         snprintf(uuid_buf, JACK_UUID_SIZE, "%d", client->GetClientControl()->fSessionID);
01041 
01042         if (strcmp(uuid,uuid_buf) == 0) {
01043             strncpy(name_res, client->GetClientControl()->fName, JACK_CLIENT_NAME_SIZE);
01044             *result = 0;
01045             return;
01046         }
01047     }
01048     // Did not find uuid.
01049     *result = -1;
01050 }
01051 
01052 void JackEngine::ReserveClientName(const char *name, const char *uuid, int *result)
01053 {
01054     jack_log("JackEngine::ReserveClientName ( name = %s, uuid = %s )", name, uuid);
01055 
01056     if (ClientCheckName(name)) {
01057         *result = -1;
01058         jack_log("name already taken");
01059         return;
01060     }
01061 
01062     EnsureUUID(atoi(uuid));
01063     fReservationMap[atoi(uuid)] = name;
01064     *result = 0;
01065 }
01066 
01067 void JackEngine::ClientHasSessionCallbackRequest(const char *name, int *result)
01068 {
01069     JackClientInterface* client = NULL;
01070     for (int i = 0; i < CLIENT_NUM; i++) {
01071         JackClientInterface* client = fClientTable[i];
01072         if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
01073             break;
01074     }
01075 
01076     if (client) {
01077         *result = client->GetClientControl()->fCallback[kSessionCallback];
01078      } else {
01079         *result = -1;
01080     }
01081 }
01082 
01083 } // end of namespace
01084