|
- /*
- Copyright (C) 2001 Paul Davis
- Copyright (C) 2004-2008 Grame
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- */
-
- #include "JackTransportEngine.h"
- #include "JackClientInterface.h"
- #include "JackClientControl.h"
- #include "JackEngineControl.h"
- #include "JackGlobals.h"
- #include "JackError.h"
- #include "JackTime.h"
- #include <assert.h>
- #include <math.h>
- #include <stdlib.h>
-
- using namespace std;
-
- namespace Jack
- {
-
- JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t>()
- {
- static_assert(offsetof(JackTransportEngine, fWriteCounter) % sizeof(fWriteCounter) == 0,
- "fWriteCounter must be first member of JackTransportEngine to ensure its alignment");
- fTransportState = JackTransportStopped;
- fTransportCmd = fPreviousCmd = TransportCommandStop;
- fSyncTimeout = 10000000; /* 10 seconds default...
- in case of big netjack1 roundtrip */
- fSyncTimeLeft = 0;
- fTimeBaseMaster = -1;
- fWriteCounter = 0;
- fConditionnal = false;
- fPendingPos = false;
- fNetworkSync = false;
- }
-
- // compute the number of cycle for timeout
- void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
- {
- long buf_usecs = (long)((buffer_size * (jack_time_t)1000000) / frame_rate);
- fSyncTimeLeft = fSyncTimeout / buf_usecs;
- jack_log("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld", (long)fSyncTimeout, (long)fSyncTimeLeft);
- }
-
- // Server
- int JackTransportEngine::ResetTimebase(int refnum)
- {
- if (fTimeBaseMaster == refnum) {
- jack_position_t* request = WriteNextStateStart(2); // To check
- request->valid = (jack_position_bits_t)0;
- WriteNextStateStop(2);
- fTimeBaseMaster = -1;
- return 0;
- } else {
- return EINVAL;
- }
- }
-
- // Server
- int JackTransportEngine::SetTimebaseMaster(int refnum, bool conditionnal)
- {
- if (conditionnal && fTimeBaseMaster > 0) {
- if (refnum != fTimeBaseMaster) {
- jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster);
- return EBUSY;
- } else {
- jack_log("ref = %ld was already timebase master", refnum);
- return 0;
- }
- } else {
- fTimeBaseMaster = refnum;
- fConditionnal = conditionnal;
- jack_log("new timebase master: ref = %ld", refnum);
- return 0;
- }
- }
-
- // RT
- bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
- {
- for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
- JackClientInterface* client = table[i];
- if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
- jack_log("CheckAllRolling ref = %ld is not rolling", i);
- return false;
- }
- }
- jack_log("CheckAllRolling");
- return true;
- }
-
- // RT
- void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table)
- {
- for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
- JackClientInterface* client = table[i];
- if (client) {
- JackClientControl* control = client->GetClientControl();
- // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport....
- control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling;
- control->fTransportSync = true;
- control->fTransportTimebase = true;
- jack_log("MakeAllStartingLocating ref = %ld", i);
- }
- }
- }
-
- // RT
- void JackTransportEngine::MakeAllStopping(JackClientInterface** table)
- {
- for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
- JackClientInterface* client = table[i];
- if (client) {
- JackClientControl* control = client->GetClientControl();
- control->fTransportState = JackTransportStopped;
- control->fTransportSync = false;
- control->fTransportTimebase = false;
- jack_log("MakeAllStopping ref = %ld", i);
- }
- }
- }
-
- // RT
- void JackTransportEngine::MakeAllLocating(JackClientInterface** table)
- {
- for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
- JackClientInterface* client = table[i];
- if (client) {
- JackClientControl* control = client->GetClientControl();
- control->fTransportState = JackTransportStopped;
- control->fTransportSync = true;
- control->fTransportTimebase = true;
- jack_log("MakeAllLocating ref = %ld", i);
- }
- }
- }
-
- // RT
- void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time)
- {
- jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
- pending->usecs = time;
- pending->frame_rate = frame_rate;
- WriteNextStateStop(1);
- }
-
- // RT
- void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
- {
- TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state
-
- /* Handle any new transport command from the last cycle. */
- transport_command_t cmd = fTransportCmd;
- if (cmd != fPreviousCmd) {
- fPreviousCmd = cmd;
- jack_log("transport command: %s", (cmd == TransportCommandStart ? "Transport start" : "Transport stop"));
- } else {
- cmd = TransportCommandNone;
- }
-
- /* state transition switch */
- switch (fTransportState) {
-
- case JackTransportStopped:
- // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state
- if (cmd == TransportCommandStart) {
- jack_log("transport stopped ==> starting frame = %d", ReadCurrentState()->frame);
- fTransportState = JackTransportStarting;
- MakeAllStartingLocating(table);
- SyncTimeout(frame_rate, buffer_size);
- } else if (fPendingPos) {
- jack_log("transport stopped ==> stopped (locating) frame = %d", ReadCurrentState()->frame);
- MakeAllLocating(table);
- }
- break;
-
- case JackTransportStarting:
- if (cmd == TransportCommandStop) {
- jack_log("transport starting ==> stopped frame = %d", ReadCurrentState()->frame);
- fTransportState = JackTransportStopped;
- MakeAllStopping(table);
- } else if (fPendingPos) {
- jack_log("transport starting ==> starting frame = %d", ReadCurrentState()->frame);
- fTransportState = JackTransportStarting;
- MakeAllStartingLocating(table);
- SyncTimeout(frame_rate, buffer_size);
- } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { // Slow clients may still catch up
- if (fNetworkSync) {
- jack_log("transport starting ==> netstarting frame = %d");
- fTransportState = JackTransportNetStarting;
- } else {
- jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft);
- fTransportState = JackTransportRolling;
- }
- }
- break;
-
- case JackTransportRolling:
- if (cmd == TransportCommandStop) {
- jack_log("transport rolling ==> stopped");
- fTransportState = JackTransportStopped;
- MakeAllStopping(table);
- } else if (fPendingPos) {
- jack_log("transport rolling ==> starting");
- fTransportState = JackTransportStarting;
- MakeAllStartingLocating(table);
- SyncTimeout(frame_rate, buffer_size);
- }
- break;
-
- case JackTransportNetStarting:
- break;
-
- default:
- jack_error("Invalid JACK transport state: %d", fTransportState);
- }
-
- /* Update timebase, if needed. */
- if (fTransportState == JackTransportRolling) {
- jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
- pending->frame += buffer_size;
- WriteNextStateStop(1);
- }
-
- /* See if an asynchronous position request arrived during the last cycle. */
- jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
- if (fPendingPos) {
- jack_log("New pos = %ld", request->frame);
- jack_position_t* pending = WriteNextStateStart(1);
- CopyPosition(request, pending);
- WriteNextStateStop(1);
- }
- }
-
- // Client
- void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
- {
- UInt16 next_index = GetCurrentIndex();
- UInt16 cur_index;
- do {
- cur_index = next_index;
- memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
- next_index = GetCurrentIndex();
- } while (cur_index != next_index); // Until a coherent state has been read
- }
-
- void JackTransportEngine::RequestNewPos(jack_position_t* pos)
- {
- jack_position_t* request = WriteNextStateStart(2);
- pos->unique_1 = pos->unique_2 = GenerateUniqueID();
- CopyPosition(pos, request);
- jack_log("RequestNewPos pos = %ld", pos->frame);
- WriteNextStateStop(2);
- }
-
- jack_transport_state_t JackTransportEngine::Query(jack_position_t* pos)
- {
- if (pos)
- ReadCurrentPos(pos);
- return GetState();
- }
-
- jack_nframes_t JackTransportEngine::GetCurrentFrame()
- {
- jack_position_t pos;
- ReadCurrentPos(&pos);
-
- if (fTransportState == JackTransportRolling) {
- float usecs = GetMicroSeconds() - pos.usecs;
- jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
- return pos.frame + elapsed;
- } else {
- return pos.frame;
- }
- }
-
- // RT, client
- void JackTransportEngine::CopyPosition(jack_position_t* from, jack_position_t* to)
- {
- int tries = 0;
- long timeout = 1000;
-
- do {
- /* throttle the busy wait if we don't get the answer
- * very quickly. See comment above about this
- * design.
- */
- if (tries > 10) {
- JackSleep(20);
- tries = 0;
-
- /* debug code to avoid system hangs... */
- if (--timeout == 0) {
- jack_error("hung in loop copying position B");
- abort();
- }
- }
- *to = *from;
- tries++;
-
- } while (to->unique_1 != to->unique_2);
- }
-
-
- } // end of namespace
|