diff --git a/ChangeLog b/ChangeLog index 121cb9ed..3be82cff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -39,6 +39,7 @@ Valerio Pilo * Revert r4119 (RT notification in the server). JackAudioDriver::ProcessSync now skip backend write in case of graph process failure. * Fix incorrect error codes in alsa/usx2y.c and alsa/JackAlsaDriver.cpp. * Synchronize public headers with JACK1. Update OSX project. + * New latency API implementation (in progress). 2011-02-09 Stephane Letz diff --git a/common/JackAPI.cpp b/common/JackAPI.cpp index 0c634f31..8bf5ca9b 100644 --- a/common/JackAPI.cpp +++ b/common/JackAPI.cpp @@ -115,6 +115,9 @@ extern "C" void *); EXPORT int jack_set_xrun_callback (jack_client_t *, JackXRunCallback xrun_callback, void *arg); + EXPORT int jack_set_latency_callback (jack_client_t *client, + JackLatencyCallback callback, void *arg); + EXPORT int jack_activate (jack_client_t *client); EXPORT int jack_deactivate (jack_client_t *client); EXPORT jack_port_t * jack_port_register (jack_client_t *client, @@ -138,12 +141,19 @@ extern "C" const jack_port_t *port); EXPORT int jack_port_tie (jack_port_t *src, jack_port_t *dst); EXPORT int jack_port_untie (jack_port_t *port); + + // Old latency API EXPORT jack_nframes_t jack_port_get_latency (jack_port_t *port); EXPORT jack_nframes_t jack_port_get_total_latency (jack_client_t *, jack_port_t *port); EXPORT void jack_port_set_latency (jack_port_t *, jack_nframes_t); EXPORT int jack_recompute_total_latency (jack_client_t*, jack_port_t* port); + + // New latency API + EXPORT void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range); + EXPORT void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range); EXPORT int jack_recompute_total_latencies (jack_client_t*); + EXPORT int jack_port_set_name (jack_port_t *port, const char *port_name); EXPORT int jack_port_set_alias (jack_port_t *port, const char *alias); EXPORT int jack_port_unset_alias (jack_port_t *port, const char *alias); @@ -526,6 +536,40 @@ EXPORT void jack_port_set_latency(jack_port_t* port, jack_nframes_t frames) } } +EXPORT void jack_port_get_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) +{ +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_port_get_latency_range"); +#endif + uintptr_t port_aux = (uintptr_t)port; + jack_port_id_t myport = (jack_port_id_t)port_aux; + if (!CheckPort(myport)) { + jack_error("jack_port_get_latency_range called with an incorrect port %ld", myport); + } else { + WaitGraphChange(); + JackGraphManager* manager = GetGraphManager(); + if (manager) + manager->GetPort(myport)->GetLatencyRange(mode, range); + } +} + +EXPORT void jack_port_set_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) +{ +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_port_set_latency_range"); +#endif + uintptr_t port_aux = (uintptr_t)port; + jack_port_id_t myport = (jack_port_id_t)port_aux; + if (!CheckPort(myport)) { + jack_error("jack_port_set_latency_range called with an incorrect port %ld", myport); + } else { + WaitGraphChange(); + JackGraphManager* manager = GetGraphManager(); + if (manager) + manager->GetPort(myport)->SetLatencyRange(mode, range); + } +} + EXPORT int jack_recompute_total_latency(jack_client_t* ext_client, jack_port_t* port) { #ifdef __CLIENTDEBUG__ @@ -988,6 +1032,20 @@ EXPORT int jack_set_xrun_callback(jack_client_t* ext_client, JackXRunCallback xr } } +EXPORT int jack_set_latency_callback(jack_client_t *ext_client, JackLatencyCallback latency_callback, void *arg) +{ +#ifdef __CLIENTDEBUG__ + JackGlobals::CheckContext("jack_set_latency_callback"); +#endif + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_latency_callback called with a NULL client"); + return -1; + } else { + return client->SetLatencyCallback(latency_callback, arg); + } +} + EXPORT int jack_set_thread_init_callback(jack_client_t* ext_client, JackThreadInitCallback init_callback, void *arg) { #ifdef __CLIENTDEBUG__ diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp index 7e1389c2..b9bd008a 100644 --- a/common/JackAudioDriver.cpp +++ b/common/JackAudioDriver.cpp @@ -335,4 +335,37 @@ jack_default_audio_sample_t* JackAudioDriver::GetMonitorBuffer(int port_index) return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_index], fEngineControl->fBufferSize); } +int JackAudioDriver::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) +{ + switch (notify) { + + case kLatencyCallback: + HandleLatencyCallback(value1); + break; + + default: + JackDriver::ClientNotify(refnum, name, notify, sync, message, value1, value2); + break; + } + + return 0; +} + +void JackAudioDriver::HandleLatencyCallback(int status) +{ + jack_latency_callback_mode_t mode = (status == 0) ? JackCaptureLatency : JackPlaybackLatency; + + for (int i = 0; i < fCaptureChannels; i++) { + if (mode == JackPlaybackLatency) { + fGraphManager->RecalculateLatency(fCapturePortList[i], mode); + } + } + + for (int i = 0; i < fPlaybackChannels; i++) { + if (mode == JackCaptureLatency) { + fGraphManager->RecalculateLatency(fPlaybackPortList[i], mode); + } + } +} + } // end of namespace diff --git a/common/JackAudioDriver.h b/common/JackAudioDriver.h index a495097b..3127f4c1 100644 --- a/common/JackAudioDriver.h +++ b/common/JackAudioDriver.h @@ -57,6 +57,8 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver jack_default_audio_sample_t* GetOutputBuffer(int port_index); jack_default_audio_sample_t* GetMonitorBuffer(int port_index); + void HandleLatencyCallback(int status); + public: JackAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); @@ -95,6 +97,8 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetSampleRate(jack_nframes_t sample_rate); + virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); + }; } // end of namespace diff --git a/common/JackClient.cpp b/common/JackClient.cpp index 7bbfb2a8..729626d8 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -60,6 +60,9 @@ JackClient::JackClient(JackSynchro* table):fThread(this) fTimebase = NULL; fSync = NULL; fThreadFun = NULL; + fSession = NULL; + fLatency = NULL; + fProcessArg = NULL; fGraphOrderArg = NULL; fXrunArg = NULL; @@ -75,6 +78,8 @@ JackClient::JackClient(JackSynchro* table):fThread(this) fSyncArg = NULL; fTimebaseArg = NULL; fThreadFunArg = NULL; + fSessionArg = NULL; + fLatencyArg = NULL; } JackClient::~JackClient() @@ -289,12 +294,113 @@ int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, res = (fImmediateSessionReply) ? 1 : 2; } break; + + case kLatencyCallback: + res = HandleLatencyCallback(value1); + break; } } return res; } +int JackClient::HandleLatencyCallback(int status) +{ + jack_latency_callback_mode_t mode = (status == 0) ? JackCaptureLatency : JackPlaybackLatency; + jack_latency_range_t latency = { UINT32_MAX, 0 }; + + /* first setup all latency values of the ports. + * this is based on the connections of the ports. + */ + list::iterator it; + + for (it = fPortList.begin(); it != fPortList.end(); it++) { + JackPort* port = GetGraphManager()->GetPort(*it); + + if ((port->GetFlags() & JackPortIsOutput) && (mode == JackPlaybackLatency)) { + GetGraphManager()->RecalculateLatency(*it, mode); + } + if ((port->GetFlags() & JackPortIsInput) && (mode == JackCaptureLatency)) { + GetGraphManager()->RecalculateLatency(*it, mode); + } + } + + if (!fLatency) { + /* + * default action is to assume all ports depend on each other. + * then always take the maximum latency. + */ + + if (mode == JackPlaybackLatency) { + /* iterate over all OutputPorts, to find maximum playback latency + */ + for (it = fPortList.begin(); it != fPortList.end(); it++) { + JackPort* port = GetGraphManager()->GetPort(*it); + + if (port->GetFlags() & JackPortIsOutput) { + jack_latency_range_t other_latency; + + port->GetLatencyRange(mode, &other_latency); + if (other_latency.max > latency.max) + latency.max = other_latency.max; + if (other_latency.min < latency.min) + latency.min = other_latency.min; + } + } + + if (latency.min == UINT32_MAX) + latency.min = 0; + + /* now set the found latency on all input ports + */ + for (it = fPortList.begin(); it != fPortList.end(); it++) { + JackPort* port = GetGraphManager()->GetPort(*it); + + if (port->GetFlags() & JackPortIsInput) { + port->SetLatencyRange(mode, &latency); + } + } + } + if (mode == JackCaptureLatency) { + /* iterate over all InputPorts, to find maximum playback latency + */ + for (it = fPortList.begin(); it != fPortList.end(); it++) { + JackPort* port = GetGraphManager()->GetPort(*it); + + if (port->GetFlags() & JackPortIsInput) { + jack_latency_range_t other_latency; + + port->GetLatencyRange(mode, &other_latency); + if (other_latency.max > latency.max) + latency.max = other_latency.max; + if (other_latency.min < latency.min) + latency.min = other_latency.min; + } + } + + if (latency.min == UINT32_MAX) + latency.min = 0; + + /* now set the found latency on all output ports + */ + for (it = fPortList.begin(); it != fPortList.end(); it++) { + JackPort* port = GetGraphManager()->GetPort(*it); + + if (port->GetFlags() & JackPortIsOutput) { + port->SetLatencyRange(mode, &latency); + } + } + } + return 0; + } + + /* we have a latency callback setup by the client, + * lets use it... + */ + fLatency(mode, fLatencyArg); + return 0; +} + /*! \brief We need to start thread before activating in the server, otherwise the FW driver connected to the client may not be activated. @@ -1011,6 +1117,19 @@ int JackClient::SetSessionCallback(JackSessionCallback callback, void *arg) } } +int JackClient::SetLatencyCallback(JackLatencyCallback callback, void *arg) +{ + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + return -1; + } else { + GetClientControl()->fCallback[kLatencyCallback] = (callback != NULL); + fLatencyArg = arg; + fLatency = callback; + return 0; + } +} + //------------------ // Internal clients //------------------ diff --git a/common/JackClient.h b/common/JackClient.h index c5a3b9aa..2ba0237c 100644 --- a/common/JackClient.h +++ b/common/JackClient.h @@ -67,6 +67,7 @@ class JackClient : public JackClientInterface, public JackRunnableInterface JackSyncCallback fSync; JackThreadCallback fThreadFun; JackSessionCallback fSession; + JackLatencyCallback fLatency; void* fProcessArg; void* fGraphOrderArg; @@ -85,6 +86,7 @@ class JackClient : public JackClientInterface, public JackRunnableInterface void* fSyncArg; void* fThreadFunArg; void* fSessionArg; + void* fLatencyArg; char fServerName[64]; JackThread fThread; /*! Thread to execute the Process function */ @@ -116,6 +118,8 @@ class JackClient : public JackClientInterface, public JackRunnableInterface inline void CallTimebaseCallbackAux(); inline int ActivateAux(); + int HandleLatencyCallback(int status); + public: JackClient(); @@ -178,6 +182,7 @@ class JackClient : public JackClientInterface, public JackRunnableInterface virtual int SetPortConnectCallback(JackPortConnectCallback callback, void *arg); virtual int SetPortRenameCallback(JackPortRenameCallback callback, void *arg); virtual int SetSessionCallback(JackSessionCallback callback, void *arg); + virtual int SetLatencyCallback(JackLatencyCallback callback, void *arg); // Internal clients virtual char* GetInternalClientName(int ref); diff --git a/common/JackDriver.cpp b/common/JackDriver.cpp index a485d810..a2c6ccd6 100644 --- a/common/JackDriver.cpp +++ b/common/JackDriver.cpp @@ -167,7 +167,7 @@ int JackDriver::Open(jack_nframes_t buffer_size, int JackDriver::Close() { - if (fClientControl.fRefNum >= 0) { + if (fClientControl.fRefNum >= 0) { jack_log("JackDriver::Close"); fGraphManager->DirectDisconnect(fClientControl.fRefNum, fClientControl.fRefNum); // Disconnect driver from itself for sync fClientControl.fActive = false; @@ -207,7 +207,7 @@ int JackDriver::ClientNotify(int refnum, const char* name, int notify, int sync, jack_log("JackDriver::kStopFreewheel"); SetupDriverSync(fClientControl.fRefNum, false); break; - } + } return 0; } @@ -223,13 +223,13 @@ void JackDriver::CycleIncTime() } void JackDriver::CycleTakeBeginTime() -{ +{ fBeginDateUst = GetMicroSeconds(); // Take callback date here fEngineControl->CycleIncTime(fBeginDateUst); } void JackDriver::CycleTakeEndTime() -{ +{ fEndDateUst = GetMicroSeconds(); // Take end date here } @@ -254,7 +254,7 @@ void JackDriver::NotifySampleRate(jack_nframes_t sample_rate) fEngine->NotifySampleRate(sample_rate); fEngineControl->InitFrameTime(); } - + void JackDriver::NotifyFailure(int code, const char* reason) { fEngine->NotifyFailure(code, reason); diff --git a/common/JackGraphManager.cpp b/common/JackGraphManager.cpp index 684a0dfe..22166317 100644 --- a/common/JackGraphManager.cpp +++ b/common/JackGraphManager.cpp @@ -245,7 +245,7 @@ int JackGraphManager::RequestMonitor(jack_port_id_t port_index, bool onoff) // C // Client jack_nframes_t JackGraphManager::ComputeTotalLatencyAux(jack_port_id_t port_index, jack_port_id_t src_port_index, JackConnectionManager* manager, int hop_count) { - const jack_int_t* connections = manager->GetConnections(port_index); + const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index); jack_nframes_t max_latency = 0; jack_port_id_t dst_index; @@ -296,6 +296,46 @@ int JackGraphManager::ComputeTotalLatencies() return 0; } +void JackGraphManager::RecalculateLatencyAux(jack_port_id_t port_index, jack_latency_callback_mode_t mode) +{ + const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index); + JackPort* port = GetPort(port_index); + jack_latency_range_t latency = { UINT32_MAX, 0 }; + jack_port_id_t dst_index; + + for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((dst_index = connections[i]) != EMPTY); i++) { + AssertPort(dst_index); + JackPort* dst_port = GetPort(dst_index); + jack_latency_range_t other_latency; + + dst_port->GetLatencyRange(mode, &other_latency); + + if (other_latency.max > latency.max) + latency.max = other_latency.max; + if (other_latency.min < latency.min) + latency.min = other_latency.min; + } + + if (latency.min == UINT32_MAX) + latency.min = 0; + + port->SetLatencyRange(mode, &latency); +} + +void JackGraphManager::RecalculateLatency(jack_port_id_t port_index, jack_latency_callback_mode_t mode) +{ + UInt16 cur_index; + UInt16 next_index; + + do { + cur_index = GetCurrentIndex(); + RecalculateLatencyAux(port_index, mode); + next_index = GetCurrentIndex(); + } while (cur_index != next_index); // Until a coherent state has been read + + jack_log("JackGraphManager::RecalculateLatency port_index = %ld", port_index); +} + // Server void JackGraphManager::SetBufferSize(jack_nframes_t buffer_size) { diff --git a/common/JackGraphManager.h b/common/JackGraphManager.h index 98e95b82..da5bf276 100644 --- a/common/JackGraphManager.h +++ b/common/JackGraphManager.h @@ -53,6 +53,7 @@ class SERVER_EXPORT JackGraphManager : public JackShmMem, public JackAtomicState float* GetBuffer(jack_port_id_t port_index); void* GetBufferAux(JackConnectionManager* manager, jack_port_id_t port_index, jack_nframes_t frames); jack_nframes_t ComputeTotalLatencyAux(jack_port_id_t port_index, jack_port_id_t src_port_index, JackConnectionManager* manager, int hop_count); + void RecalculateLatencyAux(jack_port_id_t port_index, jack_latency_callback_mode_t mode); public: @@ -72,8 +73,11 @@ class SERVER_EXPORT JackGraphManager : public JackShmMem, public JackAtomicState JackPort* GetPort(jack_port_id_t index); jack_port_id_t GetPort(const char* name); + int ComputeTotalLatency(jack_port_id_t port_index); int ComputeTotalLatencies(); + void RecalculateLatency(jack_port_id_t port_index, jack_latency_callback_mode_t mode); + int RequestMonitor(jack_port_id_t port_index, bool onoff); // Connections management diff --git a/common/JackNotification.h b/common/JackNotification.h index bca178af..673ab838 100644 --- a/common/JackNotification.h +++ b/common/JackNotification.h @@ -12,7 +12,7 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -46,6 +46,7 @@ enum NotificationType { kShutDownCallback = 15, kQUIT = 16, kSessionCallback = 17, + kLatencyCallback = 18, kMaxNotification }; diff --git a/common/JackPort.cpp b/common/JackPort.cpp index f8c62d67..5a4ced5f 100644 --- a/common/JackPort.cpp +++ b/common/JackPort.cpp @@ -13,7 +13,7 @@ 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 +along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -44,6 +44,8 @@ bool JackPort::Allocate(int refnum, const char* port_name, const char* port_type fInUse = true; fLatency = 0; fTotalLatency = 0; + fPlaybackLatency.min = fPlaybackLatency.max = 0; + fCaptureLatency.min = fCaptureLatency.max = 0; fTied = NO_PORT; // DB: At this point we do not know current buffer size in frames, // but every time buffer will be returned to any user, @@ -86,6 +88,48 @@ jack_nframes_t JackPort::GetTotalLatency() const void JackPort::SetLatency(jack_nframes_t nframes) { fLatency = nframes; + + /* setup the new latency values here, + * so we dont need to change the backend codes. + */ + if (fFlags & JackPortIsOutput) { + fCaptureLatency.min = nframes; + fCaptureLatency.max = nframes; + } + if (fFlags & JackPortIsInput) { + fPlaybackLatency.min = nframes; + fPlaybackLatency.max = nframes; + } +} + +void JackPort::SetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_range_t* range) +{ + if (mode == JackCaptureLatency) { + fCaptureLatency = *range; + + /* hack to set port->shared->latency up for + * backend ports + */ + if ((fFlags & JackPortIsOutput) && (fFlags & JackPortIsPhysical)) + fLatency = (range->min + range->max) / 2; + } else { + fPlaybackLatency = *range; + + /* hack to set port->shared->latency up for + * backend ports + */ + if ((fFlags & JackPortIsInput) && (fFlags & JackPortIsPhysical)) + fLatency = (range->min + range->max) / 2; + } +} + +void JackPort::GetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_range_t* range) const +{ + if (mode == JackCaptureLatency) { + *range = fCaptureLatency; + } else { + *range = fPlaybackLatency; + } } int JackPort::Tie(jack_port_id_t port_index) @@ -103,10 +147,10 @@ int JackPort::UnTie() int JackPort::RequestMonitor(bool onoff) { /** - jackd.h + jackd.h * If @ref JackPortCanMonitor is set for this @a port, turn input * monitoring on or off. Otherwise, do nothing. - + if (!(fFlags & JackPortCanMonitor)) return -1; */ @@ -123,10 +167,10 @@ int JackPort::RequestMonitor(bool onoff) int JackPort::EnsureMonitor(bool onoff) { /** - jackd.h + jackd.h * If @ref JackPortCanMonitor is set for this @a port, turn input * monitoring on or off. Otherwise, do nothing. - + if (!(fFlags & JackPortCanMonitor)) return -1; */ diff --git a/common/JackPort.h b/common/JackPort.h index ed54d65d..758e402b 100644 --- a/common/JackPort.h +++ b/common/JackPort.h @@ -51,6 +51,8 @@ class SERVER_EXPORT JackPort jack_nframes_t fLatency; jack_nframes_t fTotalLatency; + jack_latency_range_t fPlaybackLatency; + jack_latency_range_t fCaptureLatency; uint8_t fMonitorRequests; bool fInUse; @@ -88,9 +90,13 @@ class SERVER_EXPORT JackPort int UnTie(); jack_nframes_t GetLatency() const; - jack_nframes_t GetTotalLatency() const; void SetLatency(jack_nframes_t latency); + void SetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_range_t* range); + void GetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_range_t* range) const; + + jack_nframes_t GetTotalLatency() const; + int RequestMonitor(bool onoff); int EnsureMonitor(bool onoff); bool MonitoringInput()