git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@2061 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.71
@@ -20,8 +20,11 @@ Fernando Lopez-Lezcano | |||
Jackdmp changes log | |||
--------------------------- | |||
2008-03-20 Stephane Letz <letz@grame.fr> | |||
* Transport timebase fix (in progress). | |||
2008-03-10 Stephane Letz <letz@grame.fr> | |||
2008-03-19 Stephane Letz <letz@grame.fr> | |||
* Synchronise transport.h with latest jackd version (Video handling). | |||
@@ -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); | |||
} | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
@@ -44,11 +44,12 @@ JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t | |||
// 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); | |||
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) { | |||
@@ -62,6 +63,7 @@ int JackTransportEngine::ResetTimebase(int refnum) | |||
} | |||
} | |||
// Server | |||
int JackTransportEngine::SetTimebase(int refnum, bool conditionnal) | |||
{ | |||
if (conditionnal && fTimeBaseMaster > 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; | |||
@@ -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<jack_position_t> | |||
@@ -65,9 +102,11 @@ class JackTransportEngine : public JackAtomicArrayState<jack_position_t> | |||
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: | |||
@@ -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 | |||
} | |||
@@ -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; | |||
@@ -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. | |||