2 Copyright (C) 2004-2006 Grame
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "JackEngine.h"
25 #include "JackExternalClient.h"
26 #include "JackEngineControl.h"
27 #include "JackClientControl.h"
28 #include "JackEngineTiming.h"
29 #include "JackGlobals.h"
30 #include "JackChannel.h"
31 #include "JackSyncInterface.h"
36 JackEngine::JackEngine(JackGraphManager
* manager
, JackSynchro
** table
, JackEngineControl
* control
, JackSyncInterface
* signal
, bool sync
, long time_out_ms
, bool rt
, long priority
, bool ve
)
38 fGraphManager
= manager
;
39 fSynchroTable
= table
;
40 fEngineControl
= control
;
41 fEngineControl
->fSyncMode
= sync
;
42 fEngineControl
->fTimeOutUsecs
= time_out_ms
* 1000;
43 fEngineControl
->fRealTime
= rt
;
44 fEngineControl
->fPriority
= priority
;
45 fEngineControl
->fVerbose
= ve
;
46 fChannel
= JackGlobals::MakeServerNotifyChannel();
47 fEngineTiming
= new JackEngineTiming(fClientTable
, fGraphManager
, fEngineControl
);
49 for (int i
= 0; i
< CLIENT_NUM
; i
++)
50 fClientTable
[i
] = NULL
;
51 fEngineTiming
->ClearTimeMeasures();
52 fEngineTiming
->ResetRollingUsecs();
55 JackEngine::~JackEngine()
65 int JackEngine::Open()
67 JackLog("JackEngine::Open\n");
69 // Open audio thread => request thread communication channel
70 if (fChannel
->Open() < 0) {
71 jack_error("Cannot connect to server");
78 int JackEngine::Close()
80 JackLog("JackEngine::Close\n");
83 // Close (possibly) remaining clients (RT is stopped)
84 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
85 JackClientInterface
* client
= fClientTable
[i
];
87 JackLog("JackEngine::Close remaining client %ld\n", i
);
88 ClientCloseAux(i
, client
, false);
97 int JackEngine::Allocate()
99 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
100 if (!fClientTable
[i
]) {
101 JackLog("JackEngine::AllocateRefNum ref = %ld\n", i
);
113 bool JackEngine::Process(jack_time_t callback_usecs
)
116 fEngineControl
->fTransport
.CycleBegin(fEngineControl
->fSampleRate
, callback_usecs
);
120 fEngineControl
->fFrameTimer
.IncFrameTime(fEngineControl
->fBufferSize
, callback_usecs
, fEngineControl
->fPeriodUsecs
);
121 fEngineTiming
->UpdateTiming(callback_usecs
);
124 if (fGraphManager
->IsFinishedGraph()) {
125 fLastSwitchUsecs
= callback_usecs
;
126 if (fGraphManager
->RunNextGraph()) // True if the graph actually switched to a new state
127 fChannel
->ClientNotify(ALL_CLIENTS
, JackNotifyChannelInterface::kGraphOrderCallback
, 0);
128 fSignal
->SignalAll(); // Signal for threads waiting for next cycle
131 JackLog("Process: graph not finished!\n");
132 if (callback_usecs
> fLastSwitchUsecs
+ fEngineControl
->fTimeOutUsecs
) {
133 JackLog("Process: switch to next state %ld\n", long(callback_usecs
- fLastSwitchUsecs
));
134 //RemoveZombifiedClients(callback_usecs); TODO
135 fLastSwitchUsecs
= callback_usecs
;
136 if (fGraphManager
->RunNextGraph())
137 fChannel
->ClientNotify(ALL_CLIENTS
, JackNotifyChannelInterface::kGraphOrderCallback
, 0);
138 fSignal
->SignalAll(); // Signal for threads waiting for next cycle
141 JackLog("Process: waiting to switch %ld\n", long(callback_usecs
- fLastSwitchUsecs
));
142 if (callback_usecs
< fLastSwitchUsecs
+ 2 * fEngineControl
->fPeriodUsecs
) // Signal XRun only for the first failling cycle
143 CheckXRun(callback_usecs
);
144 fGraphManager
->RunCurrentGraph();
150 fEngineControl
->fTransport
.CycleEnd(fClientTable
, fEngineControl
->fSampleRate
, fEngineControl
->fBufferSize
);
155 Client that finish *after* the callback date are considered late even if their output buffers may have been
156 correctly mixed in the time window: callbackUsecs <==> Read <==> Write.
159 void JackEngine::CheckXRun(jack_time_t callback_usecs
) // REVOIR les conditions de fin
161 for (int i
= REAL_REFNUM
; i
< CLIENT_NUM
; i
++) {
162 JackClientInterface
* client
= fClientTable
[i
];
163 if (client
&& client
->GetClientControl()->fActive
) {
164 JackClientTiming
* timing
= fGraphManager
->GetClientTiming(i
);
165 jack_client_state_t status
= timing
->fStatus
;
166 jack_time_t finished_date
= timing
->fFinishedAt
;
168 if (status
!= NotTriggered
&& status
!= Finished
) {
169 jack_error("JackEngine::XRun: client = %s was not runned: state = %ld", client
->GetClientControl()->fName
, status
);
170 //fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client
171 fChannel
->ClientNotify(ALL_CLIENTS
, JackNotifyChannelInterface::kXRunCallback
, 0); // Notify all clients
173 if (status
== Finished
&& (long)(finished_date
- callback_usecs
) > 0) {
174 jack_error("JackEngine::XRun: client %s finished after current callback", client
->GetClientControl()->fName
);
175 //fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client
176 fChannel
->ClientNotify(ALL_CLIENTS
, JackNotifyChannelInterface::kXRunCallback
, 0); // Notify all clients
186 bool JackEngine::IsZombie(JackClientInterface
* client
, jack_time_t current_time
)
188 return ((current_time
- fGraphManager
->GetClientTiming(client
->GetClientControl()->fRefNum
)->fFinishedAt
) > 2 * fEngineControl
->fTimeOutUsecs
); // A VERIFIER
191 // TODO : check what happens with looped sub-graph....
192 void JackEngine::GetZombifiedClients(bool zombi_clients
[CLIENT_NUM
], jack_time_t current_time
)
194 for (int i
= REAL_REFNUM
; i
< CLIENT_NUM
; i
++) {
195 JackClientInterface
* client1
= fClientTable
[i
];
196 if (client1
&& IsZombie(client1
, current_time
)) {
197 JackLog("JackEngine::GetZombifiedClients: %s\n", client1
->GetClientControl()->fName
);
198 zombi_clients
[i
] = true; // Assume client is dead
199 // If another dead client is connected to the scanned one, then the scanned one is not the first of the dead subgraph
200 for (int j
= REAL_REFNUM
; j
< CLIENT_NUM
; j
++) {
201 JackClientInterface
* client2
= fClientTable
[j
];
202 if (client2
&& IsZombie(client2
, current_time
) && fGraphManager
->IsDirectConnection(j
, i
)) {
203 zombi_clients
[i
] = false;
208 zombi_clients
[i
] = false;
213 void JackEngine::RemoveZombifiedClients(jack_time_t current_time
)
215 bool zombi_clients
[CLIENT_NUM
];
216 GetZombifiedClients(zombi_clients
, current_time
);
218 for (int i
= REAL_REFNUM
; i
< CLIENT_NUM
; i
++) {
219 if (zombi_clients
[i
] && !fClientTable
[i
]->GetClientControl()->fZombie
) {
220 fClientTable
[i
]->GetClientControl()->fZombie
= true;
221 JackLog("RemoveZombifiedCients: name = %s\n", fClientTable
[i
]->GetClientControl()->fName
);
222 fGraphManager
->DirectDisconnect(FREEWHEEL_DRIVER_REFNUM
, i
);
223 fGraphManager
->DirectDisconnect(i
, FREEWHEEL_DRIVER_REFNUM
);
224 fGraphManager
->DisconnectAllPorts(i
);
225 fChannel
->ClientNotify(i
, JackNotifyChannelInterface::kZombifyClient
, 0); // Signal engine
230 void JackEngine::ZombifyClient(int refnum
)
232 NotifyClient(refnum
, JackNotifyChannelInterface::kZombifyClient
, false, 0);
239 void JackEngine::NotifyClient(int refnum
, int event
, int sync
, int value
)
241 JackClientInterface
* client
= fClientTable
[refnum
];
242 // The client may be notified by the RT thread while closing
243 if (client
&& (client
->ClientNotify(refnum
, client
->GetClientControl()->fName
, event
, sync
, value
) < 0)) {
244 jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client
->GetClientControl()->fName
, event
, value
);
246 JackLog("JackEngine::NotifyClient: client not available anymore\n");
250 void JackEngine::NotifyClients(int event
, int sync
, int value
)
252 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
253 JackClientInterface
* client
= fClientTable
[i
];
254 if (client
&& (client
->ClientNotify(i
, client
->GetClientControl()->fName
, event
, sync
, value
) < 0)) {
255 jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client
->GetClientControl()->fName
, event
, value
);
260 int JackEngine::NotifyAddClient(JackClientInterface
* new_client
, const char* name
, int refnum
)
262 // Notify existing clients of the new client and new client of existing clients.
263 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
264 JackClientInterface
* old_client
= fClientTable
[i
];
266 if (old_client
->ClientNotify(refnum
, name
, JackNotifyChannelInterface::kAddClient
, true, 0) < 0)
268 if (new_client
->ClientNotify(i
, old_client
->GetClientControl()->fName
, JackNotifyChannelInterface::kAddClient
, true, 0) < 0)
276 void JackEngine::NotifyRemoveClient(const char* name
, int refnum
)
278 // Notify existing clients (including the one beeing suppressed) of the removed client
279 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
280 JackClientInterface
* client
= fClientTable
[i
];
282 client
->ClientNotify(refnum
, name
, JackNotifyChannelInterface::kRemoveClient
, true, 0);
287 // Coming from the driver
288 void JackEngine::NotifyXRun(jack_time_t callback_usecs
)
290 // Use the audio thread => request thread communication channel
291 fEngineControl
->fFrameTimer
.ResetFrameTime(fEngineControl
->fSampleRate
, callback_usecs
, fEngineControl
->fPeriodUsecs
);
292 fChannel
->ClientNotify(ALL_CLIENTS
, JackNotifyChannelInterface::kXRunCallback
, 0);
295 void JackEngine::NotifyXRun(int refnum
)
297 if (refnum
== ALL_CLIENTS
) {
298 NotifyClients(JackNotifyChannelInterface::kXRunCallback
, false, 0);
300 NotifyClient(refnum
, JackNotifyChannelInterface::kXRunCallback
, false, 0);
304 void JackEngine::NotifyGraphReorder()
306 NotifyClients(JackNotifyChannelInterface::kGraphOrderCallback
, false, 0);
309 void JackEngine::NotifyBufferSize(jack_nframes_t nframes
)
311 NotifyClients(JackNotifyChannelInterface::kBufferSizeCallback
, true, nframes
);
314 void JackEngine::NotifyFreewheel(bool onoff
)
316 fEngineControl
->fRealTime
= !onoff
;
317 NotifyClients((onoff
? JackNotifyChannelInterface::kStartFreewheel
: JackNotifyChannelInterface::kStopFreewheel
), true, 0);
320 void JackEngine::NotifyPortRegistation(jack_port_id_t port_index
, bool onoff
)
322 NotifyClients((onoff
? JackNotifyChannelInterface::kPortRegistrationOn
: JackNotifyChannelInterface::kPortRegistrationOff
), false, port_index
);
325 //-------------------
327 //-------------------
329 bool JackEngine::ClientCheckName(const char* name
)
331 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
332 JackClientInterface
* client
= fClientTable
[i
];
333 if (client
&& (strcmp(client
->GetClientControl()->fName
, name
) == 0))
340 // Used for external clients
341 int JackEngine::ClientNew(const char* name
, int* ref
, int* shared_engine
, int* shared_client
, int* shared_graph_manager
)
343 if (ClientCheckName(name
)) {
344 jack_error("client %s already registered", name
);
348 JackExternalClient
* client
= new JackExternalClient();
349 if (ClientExternalNew(name
, ref
, shared_engine
, shared_client
, shared_graph_manager
, client
) < 0) {
357 // Used for external clients
358 int JackEngine::ClientExternalNew(const char* name
, int* ref
, int* shared_engine
, int* shared_client
, int* shared_graph_manager
, JackExternalClient
* client
)
360 JackLog("JackEngine::ClientNew: name = %s \n", name
);
361 int refnum
= Allocate();
364 jack_error("No more refnum available");
368 if (!fSynchroTable
[refnum
]->Allocate(name
, 0)) {
369 jack_error("Cannot allocate synchro");
373 if (client
->Open(name
, refnum
, shared_client
) < 0) {
374 jack_error("Cannot open client");
378 if (!fSignal
->TimedWait(5 * 1000000)) {
379 // Failure if RT thread is not running (problem with the driver...)
380 jack_error("Driver is not running");
384 if (NotifyAddClient(client
, name
, refnum
) < 0) {
385 jack_error("Cannot notify add client");
389 fClientTable
[refnum
] = client
;
390 fGraphManager
->InitRefNum(refnum
);
391 fEngineTiming
->ResetRollingUsecs();
392 *shared_engine
= fEngineControl
->GetShmIndex();
393 *shared_graph_manager
= fGraphManager
->GetShmIndex();
398 ClientCloseAux(refnum
, client
, false);
403 // Used for server driver clients
404 int JackEngine::ClientInternalNew(const char* name
, int* ref
, JackEngineControl
** shared_engine
, JackGraphManager
** shared_manager
, JackClientInterface
* client
)
406 JackLog("JackEngine::ClientInternalNew: name = %s\n", name
);
407 int refnum
= Allocate();
410 jack_error("No more refnum available");
414 if (!fSynchroTable
[refnum
]->Allocate(name
, 0)) {
415 jack_error("Cannot allocate synchro");
419 if (NotifyAddClient(client
, name
, refnum
) < 0) {
420 jack_error("Cannot notify add client");
424 fClientTable
[refnum
] = client
;
425 fGraphManager
->InitRefNum(refnum
);
426 fEngineTiming
->ResetRollingUsecs();
427 *shared_engine
= fEngineControl
;
428 *shared_manager
= fGraphManager
;
433 // Used for externall clients
434 int JackEngine::ClientClose(int refnum
)
436 JackClientInterface
* client
= fClientTable
[refnum
];
438 fEngineControl
->fTransport
.ResetTimebase(refnum
);
439 int res
= ClientCloseAux(refnum
, client
, true);
448 // Used for server internal clients
449 int JackEngine::ClientInternalClose(int refnum
)
451 JackClientInterface
* client
= fClientTable
[refnum
];
452 return (client
) ? ClientCloseAux(refnum
, client
, true) : -1;
455 // Used for drivers that close when the RT thread is stopped
456 int JackEngine::ClientInternalCloseIm(int refnum
)
458 JackClientInterface
* client
= fClientTable
[refnum
];
459 return (client
) ? ClientCloseAux(refnum
, client
, false) : -1;
462 int JackEngine::ClientCloseAux(int refnum
, JackClientInterface
* client
, bool wait
)
464 JackLog("JackEngine::ClientCloseAux ref = %ld name = %s\n", refnum
, client
->GetClientControl()->fName
);
466 // Remove the client from the table
467 fClientTable
[refnum
] = NULL
;
470 fGraphManager
->RemoveAllPorts(refnum
);
472 // Wait until next cycle to be sure client is not used anymore
474 if (!fSignal
->TimedWait(fEngineControl
->fTimeOutUsecs
* 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure
475 JackLog("JackEngine::ClientCloseAux wait error ref = %ld \n", refnum
);
479 // Notify running clients
480 NotifyRemoveClient(client
->GetClientControl()->fName
, client
->GetClientControl()->fRefNum
);
483 fSynchroTable
[refnum
]->Destroy();
484 fEngineTiming
->ResetRollingUsecs();
488 int JackEngine::ClientActivate(int refnum
)
490 JackClientInterface
* client
= fClientTable
[refnum
];
491 assert(fClientTable
[refnum
]);
493 JackLog("JackEngine::ClientActivate ref = %ld name = %s\n", refnum
, client
->GetClientControl()->fName
);
494 // Wait for graph state change to be effective
495 if (!fSignal
->TimedWait(fEngineControl
->fPeriodUsecs
* 10)) {
496 JackLog("JackEngine::ClientActivate wait error ref = %ld name = %s\n", refnum
, client
->GetClientControl()->fName
);
503 // May be called without client
504 int JackEngine::ClientDeactivate(int refnum
)
506 JackClientInterface
* client
= fClientTable
[refnum
];
510 JackLog("JackEngine::ClientDeactivate ref = %ld name = %s\n", refnum
, client
->GetClientControl()->fName
);
511 fGraphManager
->DisconnectAllPorts(refnum
);
512 // Wait for graph state change to be effective
513 if (!fSignal
->TimedWait(fEngineControl
->fPeriodUsecs
* 10)) {
514 JackLog("JackEngine::ClientDeactivate wait error ref = %ld name = %s\n", refnum
, client
->GetClientControl()->fName
);
525 int JackEngine::PortRegister(int refnum
, const char* name
, unsigned int flags
, unsigned int buffer_size
, jack_port_id_t
* port_index
)
527 JackLog("JackEngine::PortRegister ref = %ld name = %s flags = %d buffer_size = %d\n", refnum
, name
, flags
, buffer_size
);
528 assert(fClientTable
[refnum
]);
530 *port_index
= fGraphManager
->AllocatePort(refnum
, name
, (JackPortFlags
)flags
);
531 if (*port_index
!= NO_PORT
) {
532 NotifyPortRegistation(*port_index
, true);
539 int JackEngine::PortUnRegister(int refnum
, jack_port_id_t port_index
)
541 JackLog("JackEngine::PortUnRegister ref = %ld port_index = %ld\n", refnum
, port_index
);
542 assert(fClientTable
[refnum
]);
544 if (fGraphManager
->RemovePort(refnum
, port_index
) == 0) {
545 fGraphManager
->ReleasePort(port_index
);
546 NotifyPortRegistation(port_index
, false);
553 int JackEngine::PortConnect(int refnum
, const char* src
, const char* dst
)
555 JackLog("JackEngine::PortConnect src = %s dst = %s\n", src
, dst
);
556 jack_port_id_t port_src
, port_dst
;
558 return (fGraphManager
->CheckPorts(src
, dst
, &port_src
, &port_dst
) < 0)
560 : PortConnect(refnum
, port_src
, port_dst
);
563 int JackEngine::PortDisconnect(int refnum
, const char* src
, const char* dst
)
565 JackLog("JackEngine::PortDisconnect src = %s dst = %s\n", src
, dst
);
566 jack_port_id_t port_src
, port_dst
;
568 return (fGraphManager
->CheckPorts(src
, dst
, &port_src
, &port_dst
) < 0)
570 : fGraphManager
->Disconnect(port_src
, port_dst
);
573 int JackEngine::PortConnect(int refnum
, jack_port_id_t src
, jack_port_id_t dst
)
575 JackLog("JackEngine::PortConnect src = %d dst = %d\n", src
, dst
);
576 JackClientInterface
* client
;
579 if (fGraphManager
->CheckPorts(src
, dst
) < 0)
582 ref
= fGraphManager
->GetOutputRefNum(src
);
584 client
= fClientTable
[ref
];
586 if (!client
->GetClientControl()->fActive
) {
587 jack_error("Cannot connect ports owned by inactive clients:"
588 " \"%s\" is not active", client
->GetClientControl()->fName
);
592 ref
= fGraphManager
->GetInputRefNum(dst
);
594 client
= fClientTable
[ref
];
596 if (!client
->GetClientControl()->fActive
) {
597 jack_error("Cannot connect ports owned by inactive clients:"
598 " \"%s\" is not active", client
->GetClientControl()->fName
);
602 return fGraphManager
->Connect(src
, dst
);
605 int JackEngine::PortDisconnect(int refnum
, jack_port_id_t src
, jack_port_id_t dst
)
607 JackLog("JackEngine::PortDisconnect src = %d dst = %d\n", src
, dst
);
609 if (dst
== ALL_PORTS
) {
610 return (fGraphManager
->CheckPort(src
) < 0)
612 : fGraphManager
->DisconnectAll(src
);
614 return (fGraphManager
->CheckPorts(src
, dst
) < 0)
616 : fGraphManager
->Disconnect(src
, dst
);
620 //----------------------
621 // Transport management
622 //----------------------
624 int JackEngine::ReleaseTimebase(int refnum
)
626 return fEngineControl
->fTransport
.ResetTimebase(refnum
);
629 int JackEngine::SetTimebaseCallback(int refnum
, int conditional
)
631 return fEngineControl
->fTransport
.SetTimebase(refnum
, conditional
);
638 void JackEngine::PrintState()
640 std::cout
<< "Engine State" << std::endl
;
642 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
643 JackClientInterface
* client
= fClientTable
[i
];
645 std::cout
<< "Client : " << client
->GetClientControl()->fName
<< " : " << i
<< std::endl
;
648 //fGraphManager->PrintState();
649 fEngineTiming
->PrintState();
652 } // end of namespace