diff --git a/ChangeLog b/ChangeLog index 52f0c10a..05c61aed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,8 +20,11 @@ Fernando Lopez-Lezcano Jackdmp changes log --------------------------- +2008-03-20 Stephane Letz + + * Transport timebase fix (in progress). -2008-03-10 Stephane Letz +2008-03-19 Stephane Letz * Synchronise transport.h with latest jackd version (Video handling). diff --git a/common/JackClient.cpp b/common/JackClient.cpp index 3c99bbf6..544ef34a 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -254,19 +254,15 @@ int JackClient::Activate() if (StartThread() < 0) return -1; - /* seems just useless - if (fSync != NULL) // If a SyncCallback is pending... - SetSyncCallback(fSync, fSyncArg); - - if (fTimebase != NULL) // If a TimebaseCallback is pending... - SetTimebaseCallback(fConditionnal, fTimebase, fTimebaseArg); - */ - /* Insertion of client in the graph will cause a kGraphOrderCallback notification to be delivered by the server, the client wants to receive it. */ GetClientControl()->fActive = true; + + // Transport related callback become "active" + GetClientControl()->fTransportSync = true; + GetClientControl()->fTransportTimebase = true; int result = -1; fChannel->ClientActivate(GetClientControl()->fRefNum, &result); @@ -283,6 +279,11 @@ int JackClient::Deactivate() return 0; GetClientControl()->fActive = false; + + // Transport related callback become "unactive" + GetClientControl()->fTransportSync = false; + GetClientControl()->fTransportTimebase = false; + int result = -1; fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result); @@ -606,6 +607,7 @@ int JackClient::ReleaseTimebase() int result = -1; fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result); if (result == 0) { + GetClientControl()->fTransportTimebase = false; fTimebase = NULL; fTimebaseArg = NULL; } @@ -615,10 +617,9 @@ int JackClient::ReleaseTimebase() /* Call the server if the client is active, otherwise keeps the arguments */ int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg) { - if (IsActive()) - GetClientControl()->fTransportState = (sync_callback == NULL) ? JackTransportStopped : JackTransportSynching; - fSync = sync_callback; + GetClientControl()->fTransportSync = (fSync != NULL); fSyncArg = arg; + fSync = sync_callback; return 0; } @@ -628,38 +629,13 @@ int JackClient::SetSyncTimeout(jack_time_t timeout) return 0; } -/* Call the server if the client is active, otherwise keeps the arguments */ -/* -int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg) -{ - if (IsActive()) { - int result = -1; - fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result); - jack_log("SetTimebaseCallback result = %ld", result); - if (result == 0) { - fTimebase = timebase_callback; - fTimebaseArg = arg; - } else { - fTimebase = NULL; - fTimebaseArg = NULL; - } - jack_log("SetTimebaseCallback OK result = %ld", result); - return result; - } else { - fTimebase = timebase_callback; - fTimebaseArg = arg; - fConditionnal = conditional; - return 0; - } -} -*/ - int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg) { int result = -1; fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result); jack_log("SetTimebaseCallback result = %ld", result); if (result == 0) { + GetClientControl()->fTransportTimebase = true; fTimebase = timebase_callback; fTimebaseArg = arg; } else { @@ -731,68 +707,45 @@ void JackClient::TransportStop() } // Never called concurently with the server -// TODO check concurency with SetSyncCallback - +// TODO check concurrency with SetSyncCallback void JackClient::CallSyncCallback() { - JackTransportEngine& transport = GetEngineControl()->fTransport; - jack_position_t* cur_pos = transport.ReadCurrentState(); - jack_transport_state_t transport_state = transport.GetState(); - - switch (transport_state) { - - case JackTransportStarting: // Starting... - if (fSync == NULL) { + if (GetClientControl()->fTransportSync) { + + JackTransportEngine& transport = GetEngineControl()->fTransport; + jack_position_t* cur_pos = transport.ReadCurrentState(); + jack_transport_state_t transport_state = transport.GetState(); + + if (fSync != NULL) { + if (fSync(transport_state, cur_pos, fSyncArg)) { GetClientControl()->fTransportState = JackTransportRolling; - } else if (GetClientControl()->fTransportState == JackTransportStarting) { - if (fSync(transport_state, cur_pos, fSyncArg)) - GetClientControl()->fTransportState = JackTransportRolling; + GetClientControl()->fTransportSync = false; } - break; - - case JackTransportRolling: - if (fSync != NULL && GetClientControl()->fTransportState == JackTransportStarting) { // Client still not ready - if (fSync(transport_state, cur_pos, fSyncArg)) - GetClientControl()->fTransportState = JackTransportRolling; - } - break; - - case JackTransportSynching: - // New pos when transport engine is stopped... - if (fSync != NULL) { - fSync(JackTransportStopped, cur_pos, fSyncArg); - GetClientControl()->fTransportState = JackTransportStopped; - } - break; - - default: - break; + } else { + GetClientControl()->fTransportState = JackTransportRolling; + GetClientControl()->fTransportSync = false; + } } } void JackClient::CallTimebaseCallback() { JackTransportEngine& transport = GetEngineControl()->fTransport; - - if (fTimebase != NULL && GetClientControl()->fRefNum == transport.GetTimebaseMaster()) { - + + if (GetClientControl()->fRefNum == transport.GetTimebaseMaster()) { // Client *is* timebase... + jack_transport_state_t transport_state = transport.GetState(); jack_position_t* cur_pos = transport.WriteNextStateStart(1); - - switch (transport_state) { - - case JackTransportRolling: - fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg); - break; - - case JackTransportSynching: - fTimebase(JackTransportStopped, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg); - break; - - default: - break; + + if (transport_state == JackTransportRolling) { + assert(fTimebase); + fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg); + } else if (GetClientControl()->fTransportTimebase) { + assert(fTimebase); + fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg); + GetClientControl()->fTransportTimebase = true; // Callback is called only once with "new_pos" = true } - + transport.WriteNextStateStop(1); } } diff --git a/common/JackClientControl.h b/common/JackClientControl.h index 6e8506c5..f4a4a99a 100644 --- a/common/JackClientControl.h +++ b/common/JackClientControl.h @@ -39,6 +39,8 @@ struct JackClientControl : public JackShmMem char fName[JACK_CLIENT_NAME_SIZE + 1]; bool fCallback[kMaxNotification]; volatile jack_transport_state_t fTransportState; + volatile bool fTransportSync; /* Will be true when slow-sync cb has to be called */ + volatile bool fTransportTimebase; /* Will be true when timebase cb is called with new_pos on */ int fRefNum; bool fActive; @@ -71,6 +73,8 @@ struct JackClientControl : public JackShmMem fCallback[kStopFreewheelCallback] = true; fRefNum = refnum; fTransportState = JackTransportStopped; + fTransportSync = false; + fTransportTimebase = false; fActive = false; } diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp index 621cff0d..169dcf3d 100644 --- a/common/JackEngine.cpp +++ b/common/JackEngine.cpp @@ -374,7 +374,7 @@ int JackEngine::ClientCheck(const char* name, char* name_res, int protocol, int *status = 0; strcpy(name_res, name); - jack_log("Check protocol client %ld server = %ld", protocol, JACK_PROTOCOL_VERSION); + jack_log("Check protocol client %ld server = %ld", protocol, JACK_PROTOCOL_VERSION); if (protocol != JACK_PROTOCOL_VERSION) { *status |= (JackFailure | JackVersionError); @@ -561,7 +561,7 @@ int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wai // Remove the client from the table ReleaseRefnum(refnum); - // Notiy unregister + // Notify unregister jack_int_t ports[PORT_NUM_FOR_CLIENT]; int i; diff --git a/common/JackTransportEngine.cpp b/common/JackTransportEngine.cpp index dd7779c9..13a25b20 100644 --- a/common/JackTransportEngine.cpp +++ b/common/JackTransportEngine.cpp @@ -44,11 +44,12 @@ JackTransportEngine::JackTransportEngine(): JackAtomicArrayState 0) { @@ -79,44 +81,50 @@ int JackTransportEngine::SetTimebase(int refnum, bool conditionnal) } } -bool JackTransportEngine::CheckOneSynching(JackClientInterface** table) +// RT +bool JackTransportEngine::CheckAllRolling(JackClientInterface** table) { for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; - if (client && client->GetClientControl()->fTransportState == JackTransportSynching) { - jack_log("CheckOneSynching"); - return true; + if (client && client->GetClientControl()->fTransportState != JackTransportRolling) { + jack_log("CheckAllRolling ref = %ld is not rolling", i); + return false; } } - return false; + jack_log("CheckAllRolling"); + return true; } -bool JackTransportEngine::CheckAllRolling(JackClientInterface** table) +// RT +void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table) { for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; - if (client && client->GetClientControl()->fTransportState != JackTransportRolling) { - jack_log("CheckAllRolling refnum = %ld is not rolling", i); - return false; + if (client) { + // Inactive clients don't have their process function called at all, so they appear as already "rolling" for the transport.... + client->GetClientControl()->fTransportState = (client->GetClientControl()->fActive) ? JackTransportStarting : JackTransportRolling; + client->GetClientControl()->fTransportSync = true; + client->GetClientControl()->fTransportTimebase = true; + jack_log("MakeAllStartingLocating ref = %ld", i); } } - jack_log("CheckAllRolling"); - return true; } -void JackTransportEngine::MakeAllStarting(JackClientInterface** table) +// RT +void JackTransportEngine::MakeAllStopping(JackClientInterface** table) { for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; if (client) { - // Unactive clients don't have their process function called at all, they appear as already "rolling" for the transport.... - client->GetClientControl()->fTransportState = (client->GetClientControl()->fActive) ? JackTransportStarting : JackTransportRolling; - jack_log("MakeAllStarting refnum = %ld", i); + client->GetClientControl()->fTransportSync = false; + client->GetClientControl()->fTransportTimebase = false; + client->GetClientControl()->fTransportState = JackTransportStopped; + jack_log("MakeAllStopping ref = %ld", i); } } - jack_log("MakeAllStarting"); } +// RT void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time) // really needed?? (would be done in CycleEnd...) { jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state @@ -125,6 +133,7 @@ void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time 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 @@ -141,59 +150,42 @@ void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t f /* state transition switch */ switch (fTransportState) { - case JackTransportSynching: - if (cmd == TransportCommandStart) { - fTransportState = JackTransportStarting; - MakeAllStarting(table); - SyncTimeout(frame_rate, buffer_size); - jack_log("transport locate ==> starting...."); - } else if (fPendingPos) { - fTransportState = JackTransportSynching; - jack_log("transport locate ==> locate...."); - } else { - fTransportState = JackTransportStopped; - jack_log("transport locate ==> stopped...."); - } - break; - case JackTransportStopped: - // Set a JackTransportStarting for the current cycle, if all clients are ready (now slow_sync) ==> JackTransportRolling next state + // 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"); fTransportState = JackTransportStarting; - MakeAllStarting(table); + MakeAllStartingLocating(table); SyncTimeout(frame_rate, buffer_size); - jack_log("transport stopped ==> starting...."); - } else if (fPendingPos || CheckOneSynching(table)) { - fTransportState = JackTransportSynching; - jack_log("transport stopped ==> locate...."); - } + } break; case JackTransportStarting: - jack_log("transport starting fSyncTimeLeft %ld", fSyncTimeLeft); - if (cmd == TransportCommandStop) { - fTransportState = JackTransportStopped; jack_log("transport starting ==> stopped"); + fTransportState = JackTransportStopped; + MakeAllStopping(table); } else if (fPendingPos) { + jack_log("transport starting ==> starting"); fTransportState = JackTransportStarting; - MakeAllStarting(table); + MakeAllStartingLocating(table); SyncTimeout(frame_rate, buffer_size); - } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { + } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { // Slow clients may still catch up + jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft); fTransportState = JackTransportRolling; - jack_log("transport starting ==> rolling.... fSyncTimeLeft %ld", fSyncTimeLeft); } break; case JackTransportRolling: if (cmd == TransportCommandStop) { - fTransportState = JackTransportStopped; jack_log("transport rolling ==> stopped"); - } else if (fPendingPos || CheckOneSynching(table)) { + fTransportState = JackTransportStopped; + MakeAllStopping(table); + } else if (fPendingPos) { + jack_log("transport rolling ==> starting"); fTransportState = JackTransportStarting; - MakeAllStarting(table); + MakeAllStartingLocating(table); SyncTimeout(frame_rate, buffer_size); - jack_log("transport rolling ==> starting...."); } break; @@ -218,6 +210,7 @@ void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t f } } +// Client void JackTransportEngine::ReadCurrentPos(jack_position_t* pos) { UInt16 next_index = GetCurrentIndex(); @@ -229,6 +222,7 @@ void JackTransportEngine::ReadCurrentPos(jack_position_t* pos) } while (cur_index != next_index); // Until a coherent state has been read } +// RT, client void JackTransportEngine::TransportCopyPosition(jack_position_t* from, jack_position_t* to) { int tries = 0; diff --git a/common/JackTransportEngine.h b/common/JackTransportEngine.h index 2f635b77..76e002e3 100644 --- a/common/JackTransportEngine.h +++ b/common/JackTransportEngine.h @@ -49,6 +49,43 @@ We have: The current position can be read by clients. We use a JackAtomicArrayState pattern that allows to manage several "next" states independantly. + + In jack1 implementation, transport code (jack_transport_cycle_end) was not called if the graph could not be locked (see jack_run_one_cycle). + Here transport cycle (CycleBegin, CycleEnd) has to run in the RT thread concurrently with code executed from the "command" thread. + + Each client maintains a state in it's shared memory area defined by: + + - it's current transport state + - a boolean that is "true" when slow-sync cb has to be called + - a boolean that is "true" when timebase cb is called with new_pos on + + Several operations set the "slow-sync cb" flag to true: + + - setting a new cb (client) + - activate (client) + - transport start (server) + - new pos (server) + + Slow-sync cb calls stops when: + + - the cb return true (client) + - desactivate (client) + - transport stop (server) + + Several operations set the "timebase cb" flag to true: + + - setting a new cb (client) + - activate (client) + - transport start (server) ?? + - new pos (server) + + Timebase cb "new_pos" argument calls stops when: + + - after one cb call with "new_pos" argument true (client) + - desactivate (client) + - release (client) + - transport stop (server) + */ class JackTransportEngine : public JackAtomicArrayState @@ -65,9 +102,11 @@ class JackTransportEngine : public JackAtomicArrayState bool fPendingPos; SInt32 fWriteCounter; - bool CheckOneSynching(JackClientInterface** table); bool CheckAllRolling(JackClientInterface** table); - void MakeAllStarting(JackClientInterface** table); + + void MakeAllStartingLocating(JackClientInterface** table); + void MakeAllStopping(JackClientInterface** table); + void SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size); public: diff --git a/common/jack/jack.h b/common/jack/jack.h index 45b81c4e..b657b9e5 100644 --- a/common/jack/jack.h +++ b/common/jack/jack.h @@ -923,20 +923,20 @@ extern "C" */ void jack_set_error_function (void (*func)(const char *)); -/** - * Display JACK info message. - * - * Set via jack_set_info_function(), otherwise a JACK-provided - * default will print @a msg (plus a newline) to stdout. - * - * @param msg info message text (no newline at end). - */ -extern void (*jack_info_callback)(const char *msg); - -/** - * Set the @ref jack_info_callback for info message display. - */ -void jack_set_info_function (void (*func)(const char *)); + /** + * Display JACK info message. + * + * Set via jack_set_info_function(), otherwise a JACK-provided + * default will print @a msg (plus a newline) to stdout. + * + * @param msg info message text (no newline at end). + */ + extern void (*jack_info_callback)(const char *msg); + + /** + * Set the @ref jack_info_callback for info message display. + */ + void jack_set_info_function (void (*func)(const char *)); #ifdef __cplusplus } diff --git a/common/jack/transport.h b/common/jack/transport.h index d41de3b0..51e5dc16 100644 --- a/common/jack/transport.h +++ b/common/jack/transport.h @@ -37,7 +37,6 @@ typedef enum { JackTransportRolling = 1, /**< Transport playing */ JackTransportLooping = 2, /**< For OLD_TRANSPORT, now ignored */ JackTransportStarting = 3, /**< Waiting for sync ready */ - JackTransportSynching = 4 /**< internal use*/ } jack_transport_state_t; diff --git a/common/transport_types.h b/common/transport_types.h index 52f92661..5bcc9c32 100644 --- a/common/transport_types.h +++ b/common/transport_types.h @@ -39,7 +39,7 @@ extern "C" JackTransportRolling = 1, /**< Transport playing */ JackTransportLooping = 2, /**< For OLD_TRANSPORT, now ignored */ JackTransportStarting = 3, /**< Waiting for sync ready */ - JackTransportSynching = 4 /**< temporary*/ + //JackTransportSynching = 4 /**< temporary*/ } jack_transport_state_t; @@ -51,53 +51,84 @@ extern "C" typedef enum { JackPositionBBT = 0x10, /**< Bar, Beat, Tick */ - JackPositionTimecode = 0x20 /**< External timecode */ - + JackPositionTimecode = 0x20, /**< External timecode */ + JackBBTFrameOffset = 0x40, /**< Frame offset of BBT information */ + JackAudioVideoRatio = 0x80, /**< audio frames per video frame */ + JackVideoFrameOffset = 0x100 /**< frame offset of first video frame */ } jack_position_bits_t; /** all valid position bits */ #define JACK_POSITION_MASK (JackPositionBBT|JackPositionTimecode) #define EXTENDED_TIME_INFO - /** - * Struct for transport position information. - */ typedef struct { - + /* these four cannot be set from clients: the server sets them */ jack_unique_t unique_1; /**< unique ID */ - jack_time_t usecs; /**< monotonic, free-rolling */ + jack_time_t usecs; /**< monotonic, free-rolling */ jack_nframes_t frame_rate; /**< current frame rate (per second) */ jack_nframes_t frame; /**< frame number, always present */ jack_position_bits_t valid; /**< which other fields are valid */ /* JackPositionBBT fields: */ - int32_t bar; /**< current bar */ - int32_t beat; /**< current beat-within-bar */ - int32_t tick; /**< current tick-within-beat */ - double bar_start_tick; + int32_t bar; /**< current bar */ + int32_t beat; /**< current beat-within-bar */ + int32_t tick; /**< current tick-within-beat */ + double bar_start_tick; - float beats_per_bar; /**< time signature "numerator" */ - float beat_type; /**< time signature "denominator" */ - double ticks_per_beat; - double beats_per_minute; + float beats_per_bar; /**< time signature "numerator" */ + float beat_type; /**< time signature "denominator" */ + double ticks_per_beat; + double beats_per_minute; /* JackPositionTimecode fields: (EXPERIMENTAL: could change) */ - double frame_time; /**< current time in seconds */ - double next_time; /**< next sequential frame_time - (unless repositioned) */ + double frame_time; /**< current time in seconds */ + double next_time; /**< next sequential frame_time + (unless repositioned) */ + + /* JackBBTFrameOffset fields: */ + jack_nframes_t bbt_offset; /**< frame offset for the BBT fields + (the given bar, beat, and tick + values actually refer to a time + frame_offset frames before the + start of the cycle), should + be assumed to be 0 if + JackBBTFrameOffset is not + set. If JackBBTFrameOffset is + set and this value is zero, the BBT + time refers to the first frame of this + cycle. If the value is positive, + the BBT time refers to a frame that + many frames before the start of the + cycle. */ + + /* JACK video positional data (experimental) */ + + float audio_frames_per_video_frame; /**< number of audio frames + per video frame. Should be assumed + zero if JackAudioVideoRatio is not + set. If JackAudioVideoRatio is set + and the value is zero, no video + data exists within the JACK graph */ + + jack_nframes_t video_offset; /**< audio frame at which the first video + frame in this cycle occurs. Should + be assumed to be 0 if JackVideoFrameOffset + is not set. If JackVideoFrameOffset is + set, but the value is zero, there is + no video frame within this cycle. */ /* For binary compatibility, new fields should be allocated from * this padding area with new valid bits controlling access, so * the existing structure size and offsets are preserved. */ - int32_t padding[10]; + int32_t padding[7]; /* When (unique_1 == unique_2) the contents are consistent. */ jack_unique_t unique_2; /**< unique ID */ - } - jack_position_t; + } jack_position_t; + /** * Prototype for the @a sync_callback defined by slow-sync clients.