diff options
Diffstat (limited to 'common')
111 files changed, 22984 insertions, 0 deletions
diff --git a/common/JackAPI.cpp b/common/JackAPI.cpp new file mode 100644 index 00000000..705a112b --- /dev/null +++ b/common/JackAPI.cpp @@ -0,0 +1,1114 @@ +/* +Copyright (C) 2001-2003 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackClient.h" +#include "JackError.h" +#include "JackGraphManager.h" +#include "JackEngineControl.h" +#include "JackClientControl.h" +#include "JackGlobals.h" +#include "JackTime.h" +#include "JackExports.h" +#include <math.h> + +using namespace Jack; + +#ifdef __cplusplus +extern "C" +{ +#endif + + EXPORT int jack_client_name_size (void); + EXPORT char* jack_get_client_name (jack_client_t *client); + EXPORT int jack_internal_client_new (const char *client_name, + const char *load_name, + const char *load_init); + EXPORT jack_client_t* my_jack_internal_client_new(const char* client_name); + EXPORT void jack_internal_client_close (const char *client_name); + EXPORT void my_jack_internal_client_close (jack_client_t* client); + EXPORT int jack_is_realtime (jack_client_t *client); + EXPORT void jack_on_shutdown (jack_client_t *client, + void (*function)(void *arg), void *arg); + EXPORT int jack_set_process_callback (jack_client_t *client, + JackProcessCallback process_callback, + void *arg); + EXPORT int jack_set_thread_init_callback (jack_client_t *client, + JackThreadInitCallback thread_init_callback, + void *arg); + EXPORT int jack_set_freewheel_callback (jack_client_t *client, + JackFreewheelCallback freewheel_callback, + void *arg); + EXPORT int jack_set_freewheel(jack_client_t* client, int onoff); + EXPORT int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes); + EXPORT int jack_set_buffer_size_callback (jack_client_t *client, + JackBufferSizeCallback bufsize_callback, + void *arg); + EXPORT int jack_set_sample_rate_callback (jack_client_t *client, + JackSampleRateCallback srate_callback, + void *arg); + EXPORT int jack_set_port_registration_callback (jack_client_t *, + JackPortRegistrationCallback + registration_callback, void *arg); + EXPORT int jack_set_graph_order_callback (jack_client_t *, + JackGraphOrderCallback graph_callback, + void *); + EXPORT int jack_set_xrun_callback (jack_client_t *, + JackXRunCallback xrun_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, + const char *port_name, + const char *port_type, + unsigned long flags, + unsigned long buffer_size); + EXPORT int jack_port_unregister (jack_client_t *, jack_port_t *); + EXPORT void * jack_port_get_buffer (jack_port_t *, jack_nframes_t); + EXPORT const char * jack_port_name (const jack_port_t *port); + EXPORT const char * jack_port_short_name (const jack_port_t *port); + EXPORT int jack_port_flags (const jack_port_t *port); + EXPORT const char * jack_port_type (const jack_port_t *port); + EXPORT int jack_port_is_mine (const jack_client_t *, const jack_port_t *port); + EXPORT int jack_port_connected (const jack_port_t *port); + EXPORT int jack_port_connected_to (const jack_port_t *port, + const char *port_name); + EXPORT const char ** jack_port_get_connections (const jack_port_t *port); + EXPORT const char ** jack_port_get_all_connections (const jack_client_t *client, + 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); + EXPORT int jack_port_lock (jack_client_t *, jack_port_t *); + EXPORT int jack_port_unlock (jack_client_t *, jack_port_t *); + 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_latencies (jack_client_t*); + EXPORT int jack_port_set_name (jack_port_t *port, const char *port_name); + EXPORT int jack_port_request_monitor (jack_port_t *port, int onoff); + EXPORT int jack_port_request_monitor_by_name (jack_client_t *client, + const char *port_name, int onoff); + EXPORT int jack_port_ensure_monitor (jack_port_t *port, int onoff); + EXPORT int jack_port_monitoring_input (jack_port_t *port); + EXPORT int jack_connect (jack_client_t *, + const char *source_port, + const char *destination_port); + EXPORT int jack_disconnect (jack_client_t *, + const char *source_port, + const char *destination_port); + EXPORT int jack_port_disconnect (jack_client_t *, jack_port_t *); + EXPORT int jack_port_name_size(void); + EXPORT int jack_port_type_size(void); + EXPORT jack_nframes_t jack_get_sample_rate (jack_client_t *); + EXPORT jack_nframes_t jack_get_buffer_size (jack_client_t *); + EXPORT const char ** jack_get_ports (jack_client_t *, + const char *port_name_pattern, + const char *type_name_pattern, + unsigned long flags); + EXPORT jack_port_t * jack_port_by_name (jack_client_t *, const char *port_name); + EXPORT jack_port_t * jack_port_by_id (jack_client_t *client, + jack_port_id_t port_id); + EXPORT int jack_engine_takeover_timebase (jack_client_t *); + EXPORT jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *); + EXPORT jack_nframes_t jack_frame_time (const jack_client_t *); + EXPORT jack_nframes_t jack_last_frame_time (const jack_client_t *client); + EXPORT float jack_cpu_load (jack_client_t *client); + EXPORT pthread_t jack_client_thread_id (jack_client_t *); + EXPORT void jack_set_error_function (void (*func)(const char *)); + + EXPORT float jack_get_max_delayed_usecs (jack_client_t *client); + EXPORT float jack_get_xrun_delayed_usecs (jack_client_t *client); + EXPORT void jack_reset_max_delayed_usecs (jack_client_t *client); + + EXPORT int jack_release_timebase (jack_client_t *client); + EXPORT int jack_set_sync_callback (jack_client_t *client, + JackSyncCallback sync_callback, + void *arg); + EXPORT int jack_set_sync_timeout (jack_client_t *client, + jack_time_t timeout); + EXPORT int jack_set_timebase_callback (jack_client_t *client, + int conditional, + JackTimebaseCallback timebase_callback, + void *arg); + EXPORT int jack_transport_locate (jack_client_t *client, + jack_nframes_t frame); + EXPORT jack_transport_state_t jack_transport_query (const jack_client_t *client, + jack_position_t *pos); + EXPORT jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client); + EXPORT int jack_transport_reposition (jack_client_t *client, + jack_position_t *pos); + EXPORT void jack_transport_start (jack_client_t *client); + EXPORT void jack_transport_stop (jack_client_t *client); + EXPORT void jack_get_transport_info (jack_client_t *client, + jack_transport_info_t *tinfo); + EXPORT void jack_set_transport_info (jack_client_t *client, + jack_transport_info_t *tinfo); + + EXPORT int jack_acquire_real_time_scheduling (pthread_t thread, int priority); + EXPORT int jack_client_create_thread (jack_client_t* client, + pthread_t *thread, + int priority, + int realtime, // boolean + void *(*start_routine)(void*), + void *arg); + EXPORT int jack_drop_real_time_scheduling (pthread_t thread); + + EXPORT char * jack_get_internal_client_name (jack_client_t *client, + jack_intclient_t intclient); + EXPORT jack_intclient_t jack_internal_client_handle (jack_client_t *client, + const char *client_name, + jack_status_t *status); + EXPORT jack_intclient_t jack_internal_client_load (jack_client_t *client, + const char *client_name, + jack_options_t options, + jack_status_t *status, ...); + EXPORT jack_status_t jack_internal_client_unload (jack_client_t *client, + jack_intclient_t intclient); + +#ifdef __cplusplus +} +#endif + +#ifdef WIN32 +/* missing on Windows : see http://bugs.mysql.com/bug.php?id=15936 */ +inline double rint(double nr) +{ + double f = floor(nr); + double c = ceil(nr); + return (((c -nr) >= (nr - f)) ? f : c); +} +#endif + +static inline bool CheckPort(jack_port_id_t port_index) +{ + return (port_index < PORT_NUM); +} + +static inline void WaitGraphChange() +{ + if (GetGraphManager()->IsPendingChange()) { + JackLog("WaitGraphChange...\n"); + JackSleep(GetEngineControl()->fPeriodUsecs * 2); + } +} + +static void default_jack_error_callback (const char *desc) +{ + fprintf(stderr, "%s\n", desc); +} + +void (*jack_error_callback)(const char *desc) = &default_jack_error_callback; + +EXPORT void jack_set_error_function (void (*func)(const char *)) +{ + jack_error_callback = func; +} + +EXPORT void* jack_port_get_buffer(jack_port_t* port, jack_nframes_t frames) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_get_buffer called with an incorrect port %ld", myport); + return NULL; + } else { + return GetGraphManager()->GetBuffer(myport, frames); + } +} + +EXPORT const char* jack_port_name(const jack_port_t* port) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_name called with an incorrect port %ld", myport); + return NULL; + } else { + return GetGraphManager()->GetPort(myport)->GetName(); + } +} + +EXPORT const char* jack_port_short_name(const jack_port_t* port) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_short_name called with an incorrect port %ld", myport); + return NULL; + } else { + return GetGraphManager()->GetPort(myport)->GetShortName(); + } +} + +EXPORT int jack_port_flags(const jack_port_t* port) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_flags called with an incorrect port %ld", myport); + return -1; + } else { + return GetGraphManager()->GetPort(myport)->Flags(); + } +} + +EXPORT const char* jack_port_type(const jack_port_t* port) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_flags called an incorrect port %ld", myport); + return NULL; + } else { + return GetGraphManager()->GetPort(myport)->Type(); + } +} + +EXPORT int jack_port_connected(const jack_port_t* port) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_connected called with an incorrect port %ld", myport); + return -1; + } else { + WaitGraphChange(); + return GetGraphManager()->GetConnectionsNum(myport); + } +} + +EXPORT int jack_port_connected_to(const jack_port_t* port, const char* portname) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_connected_to called with an incorrect port %ld", myport); + return -1; + } else if (portname == NULL) { + jack_error("jack_port_connected_to called with a NULL port name"); + return -1; + } else { + WaitGraphChange(); + return GetGraphManager()->ConnectedTo(myport, portname); + } +} + +EXPORT int jack_port_tie(jack_port_t* src, jack_port_t* dst) +{ + jack_port_id_t mysrc = (jack_port_id_t)src; + if (!CheckPort(mysrc)) { + jack_error("jack_port_tie called with a NULL src port"); + return -1; + } + jack_port_id_t mydst = (jack_port_id_t)dst; + if (!CheckPort(mydst)) { + jack_error("jack_port_tie called with a NULL dst port"); + return -1; + } + if (GetGraphManager()->GetPort(mysrc)->GetRefNum() != GetGraphManager()->GetPort(mydst)->GetRefNum()) { + jack_error("jack_port_tie called with ports not belonging to the same client"); + return -1; + } + return GetGraphManager()->GetPort(mydst)->Tie(mysrc); +} + +EXPORT int jack_port_untie(jack_port_t* port) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_untie called with an incorrect port %ld", myport); + return -1; + } else { + return GetGraphManager()->GetPort(myport)->UnTie(); + } +} + +EXPORT jack_nframes_t jack_port_get_latency(jack_port_t* port) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_get_latency called with an incorrect port %ld", myport); + return 0; + } else { + WaitGraphChange(); + return GetGraphManager()->GetPort(myport)->GetLatency(); + } +} + +EXPORT void jack_port_set_latency(jack_port_t* port, jack_nframes_t frames) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_set_latency called with an incorrect port %ld", myport); + } else { + GetGraphManager()->GetPort(myport)->SetLatency(frames); + } +} + +EXPORT int jack_recompute_total_latencies(jack_client_t* ext_client) +{ + // The latency computation is done each time jack_port_get_total_latency is called + return 0; +} + +EXPORT int jack_port_set_name(jack_port_t* port, const char* name) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_set_name called with an incorrect port %ld", myport); + return -1; + } else if (name == NULL) { + jack_error("jack_port_set_name called with a NULL port name"); + return -1; + } else { + return GetGraphManager()->GetPort(myport)->SetName(name); + } +} + +EXPORT int jack_port_request_monitor(jack_port_t* port, int onoff) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_request_monitor called with an incorrect port %ld", myport); + return -1; + } else { + return GetGraphManager()->RequestMonitor(myport, onoff); + } +} + +EXPORT int jack_port_request_monitor_by_name(jack_client_t* ext_client, const char* port_name, int onoff) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_request_monitor_by_name called with a NULL client"); + return -1; + } else { + jack_port_id_t myport = GetGraphManager()->GetPort(port_name); + if (!CheckPort(myport)) { + jack_error("jack_port_request_monitor_by_name called with an incorrect port %s", port_name); + return -1; + } else { + return GetGraphManager()->RequestMonitor(myport, onoff); + } + } +} + +EXPORT int jack_port_ensure_monitor(jack_port_t* port, int onoff) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_ensure_monitor called with an incorrect port %ld", myport); + return -1; + } else { + return GetGraphManager()->GetPort(myport)->EnsureMonitor(onoff); + } +} + +EXPORT int jack_port_monitoring_input(jack_port_t* port) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_monitoring_input called with an incorrect port %ld", myport); + return -1; + } else { + return GetGraphManager()->GetPort(myport)->MonitoringInput(); + } +} + +EXPORT int jack_is_realtime(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_is_realtime called with a NULL client"); + return -1; + } else { + return GetEngineControl()->fRealTime; + } +} + +EXPORT void jack_on_shutdown(jack_client_t* ext_client, void (*function)(void* arg), void* arg) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_on_shutdown called with a NULL client"); + } else { + client->OnShutdown(function, arg); + } +} + +EXPORT int jack_set_process_callback(jack_client_t* ext_client, JackProcessCallback callback, void* arg) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_process_callback called with a NULL client"); + return -1; + } else { + return client->SetProcessCallback(callback, arg); + } +} + +EXPORT int jack_set_freewheel_callback(jack_client_t* ext_client, JackFreewheelCallback freewheel_callback, void* arg) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_freewheel_callback called with a NULL client"); + return -1; + } else { + return client->SetFreewheelCallback(freewheel_callback, arg); + } +} + +EXPORT int jack_set_freewheel(jack_client_t* ext_client, int onoff) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_freewheel called with a NULL client"); + return -1; + } else { + return client->SetFreeWheel(onoff); + } +} + +EXPORT int jack_set_buffer_size(jack_client_t* ext_client, jack_nframes_t nframes) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_buffer_size called with a NULL client"); + return -1; + } else { + return client->SetBufferSize(nframes); + } +} + +EXPORT int jack_set_buffer_size_callback(jack_client_t* ext_client, JackBufferSizeCallback bufsize_callback, void* arg) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_buffer_size_callback called with a NULL client"); + return -1; + } else { + return client->SetBufferSizeCallback(bufsize_callback, arg); + } +} + +EXPORT int jack_set_sample_rate_callback(jack_client_t* ext_client, JackSampleRateCallback srate_callback, void* arg) +{ + JackClient* client = (JackClient*)ext_client; + JackLog("jack_set_sample_rate_callback ext_client %x client %x \n", ext_client, client); + if (client == NULL) { + jack_error("jack_set_sample_rate_callback called with a NULL client"); + return -1; + } else { + JackLog("jack_set_sample_rate_callback: deprecated\n"); + return -1; + } +} + +EXPORT int jack_set_port_registration_callback(jack_client_t* ext_client, JackPortRegistrationCallback registration_callback, void* arg) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_port_registration_callback called with a NULL client"); + return -1; + } else { + return client->SetPortRegistrationCallback(registration_callback, arg); + } +} + +EXPORT int jack_set_graph_order_callback(jack_client_t* ext_client, JackGraphOrderCallback graph_callback, void* arg) +{ + JackClient* client = (JackClient*)ext_client; + JackLog("jack_set_graph_order_callback ext_client %x client %x \n", ext_client, client); + if (client == NULL) { + jack_error("jack_set_graph_order_callback called with a NULL client"); + return -1; + } else { + return client->SetGraphOrderCallback(graph_callback, arg); + } +} + +EXPORT int jack_set_xrun_callback(jack_client_t* ext_client, JackXRunCallback callback, void* arg) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_xrun_callback called with a NULL client"); + return -1; + } else { + return client->SetXRunCallback(callback, arg); + } +} + +EXPORT int jack_set_thread_init_callback(jack_client_t* ext_client, JackThreadInitCallback callback, void *arg) +{ + JackClient* client = (JackClient*)ext_client; + JackLog("jack_set_thread_init_callback ext_client %x client %x \n", ext_client, client); + if (client == NULL) { + jack_error("jack_set_thread_init_callback called with a NULL client"); + return -1; + } else { + return client->SetInitCallback(callback, arg); + } +} + +EXPORT int jack_activate(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_activate called with a NULL client"); + return -1; + } else { + return client->Activate(); + } +} + +EXPORT int jack_deactivate(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_deactivate called with a NULL client"); + return -1; + } else { + return client->Deactivate(); + } +} + +EXPORT jack_port_t* jack_port_register(jack_client_t* ext_client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_register called with a NULL client"); + return NULL; + } else if ((port_name == NULL) || (port_type == NULL)) { + jack_error("jack_port_register called with a NULL port name or a NULL port_type"); + return NULL; + } else { + return (jack_port_t *)client->PortRegister(port_name, port_type, flags, buffer_size); + } +} + +EXPORT int jack_port_unregister(jack_client_t* ext_client, jack_port_t* port) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_unregister called with a NULL client"); + return -1; + } + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_unregister called with an incorrect port %ld", myport); + return -1; + } + return client->PortUnRegister(myport); +} + +EXPORT int jack_port_is_mine(const jack_client_t* ext_client, const jack_port_t* port) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_is_mine called with a NULL client"); + return -1; + } + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_is_mine called with an incorrect port %ld", myport); + return -1; + } + return client->PortIsMine(myport); +} + +EXPORT const char** jack_port_get_connections(const jack_port_t* port) +{ + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_get_connections called with an incorrect port %ld", myport); + return NULL; + } else { + WaitGraphChange(); + return GetGraphManager()->GetConnections(myport); + } +} + +// Calling client does not need to "own" the port +EXPORT const char** jack_port_get_all_connections(const jack_client_t* ext_client, const jack_port_t* port) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_get_all_connections called with a NULL client"); + return NULL; + } + + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_get_all_connections called with an incorrect port %ld", myport); + return NULL; + } else { + WaitGraphChange(); + return GetGraphManager()->GetConnections(myport); + } +} + +// Does not use the client parameter +EXPORT int jack_port_lock(jack_client_t* ext_client, jack_port_t* port) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_lock called with a NULL client"); + return -1; + } + + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_lock called with an incorrect port %ld", myport); + return -1; + } else { + return (myport && client->PortIsMine(myport)) ? GetGraphManager()->GetPort(myport)->Lock() : -1; + } +} + +// Does not use the client parameter +EXPORT int jack_port_unlock(jack_client_t* ext_client, jack_port_t* port) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_unlock called with a NULL client"); + return -1; + } + + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_unlock called with an incorrect port %ld", myport); + return -1; + } else { + return (myport && client->PortIsMine(myport)) ? GetGraphManager()->GetPort(myport)->Unlock() : -1; + } +} + +EXPORT jack_nframes_t jack_port_get_total_latency(jack_client_t* ext_client, jack_port_t* port) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_get_total_latency called with a NULL client"); + return 0; + } + + jack_port_id_t myport = (jack_port_id_t)port; + if (!CheckPort(myport)) { + jack_error("jack_port_get_total_latency called with an incorrect port %ld", myport); + return 0; + } else { + // The latency computation is done each time + WaitGraphChange(); + return GetGraphManager()->GetTotalLatency(myport); + } +} + +EXPORT int jack_connect(jack_client_t* ext_client, const char* src, const char* dst) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_connect called with a NULL client"); + return -1; + } else if ((src == NULL) || (dst == NULL)) { + jack_error("jack_connect called with a NULL port name"); + return -1; + } else { + return client->PortConnect(src, dst); + } +} + +EXPORT int jack_disconnect(jack_client_t* ext_client, const char* src, const char* dst) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_disconnect called with a NULL client"); + return -1; + } else if ((src == NULL) || (dst == NULL)) { + jack_error("jack_connect called with a NULL port name"); + return -1; + } else { + return client->PortDisconnect(src, dst); + } +} + +EXPORT int jack_port_connect(jack_client_t* ext_client, jack_port_t* src, jack_port_t* dst) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_connect called with a NULL client"); + return -1; + } + jack_port_id_t mysrc = (jack_port_id_t)src; + if (!CheckPort(mysrc)) { + jack_error("jack_port_connect called with a NULL src port"); + return -1; + } + jack_port_id_t mydst = (jack_port_id_t)dst; + if (!CheckPort(mydst)) { + jack_error("jack_port_connect called with a NULL dst port"); + return -1; + } + return client->PortConnect(mysrc, mydst); +} + +EXPORT int jack_port_disconnect(jack_client_t* ext_client, jack_port_t* src) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_port_disconnect called with a NULL client"); + return -1; + } + jack_port_id_t myport = (jack_port_id_t)src; + if (!CheckPort(myport)) { + jack_error("jack_port_disconnect called with an incorrect port %ld", myport); + return -1; + } + return client->PortDisconnect(myport); +} + +EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_get_sample_rate called with a NULL client"); + return 0; + } else { + return GetEngineControl()->fSampleRate; + } +} + +EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_get_buffer_size called with a NULL client"); + return 0; + } else { + return GetEngineControl()->fBufferSize; + } +} + +EXPORT const char** jack_get_ports(jack_client_t* ext_client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_get_ports called with a NULL client"); + return NULL; + } + return GetGraphManager()->GetPorts(port_name_pattern, type_name_pattern, flags); +} + +EXPORT jack_port_t* jack_port_by_name(jack_client_t* ext_client, const char* portname) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_get_ports called with a NULL client"); + return 0; + } + + if (portname == NULL) { + jack_error("jack_port_by_name called with a NULL port name"); + return NULL; + } else { + int res = GetGraphManager()->GetPort(portname); // returns a port index at least > 1 + return (res == NO_PORT) ? NULL : (jack_port_t*)res; + } +} + +EXPORT jack_port_t* jack_port_by_id(const jack_client_t* ext_client, jack_port_id_t id) +{ + /* jack_port_t* type is actually the port index */ + return (jack_port_t*)id; +} + +EXPORT int jack_engine_takeover_timebase(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_engine_takeover_timebase called with a NULL client"); + return -1; + } else { + jack_error("jack_engine_takeover_timebase : not yet implemented\n"); + return 0; + } +} + +EXPORT jack_nframes_t jack_frames_since_cycle_start(const jack_client_t* ext_client) +{ + JackTimer timer; + GetEngineControl()->fFrameTimer.ReadFrameTime(&timer); + return (jack_nframes_t) floor((((float)GetEngineControl()->fSampleRate) / 1000000.0f) * (GetMicroSeconds() - timer.fCurrentCallback)); +} + +EXPORT jack_nframes_t jack_frame_time(const jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_frame_time called with a NULL client"); + return 0; + } else { + JackTimer timer; + GetEngineControl()->fFrameTimer.ReadFrameTime(&timer); + if (timer.fInitialized) { + return timer.fFrames + + (long) rint(((double) ((jack_time_t)(GetMicroSeconds() - timer.fCurrentWakeup)) / + ((jack_time_t)(timer.fNextWakeUp - timer.fCurrentWakeup))) * GetEngineControl()->fBufferSize); + } else { + return 0; + } + } +} + +EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t* ext_client) +{ + JackTimer timer; + GetEngineControl()->fFrameTimer.ReadFrameTime(&timer); + return timer.fFrames; +} + +EXPORT float jack_cpu_load(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_cpu_load called with a NULL client"); + return 0.0f; + } else { + return GetEngineControl()->fCPULoad; + } +} + +pthread_t EXPORT jack_client_thread_id(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_client_thread_id called with a NULL client"); + return (pthread_t)NULL; + } else { + return client->GetThreadID(); + } +} + +EXPORT char* jack_get_client_name (jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_get_client_name called with a NULL client"); + return NULL; + } else { + return client->GetClientControl()->fName; + } +} + +EXPORT int jack_client_name_size(void) +{ + return JACK_CLIENT_NAME_SIZE; +} + +EXPORT int jack_port_name_size(void) +{ + return JACK_PORT_NAME_SIZE; +} + +// transport.h + +EXPORT int jack_release_timebase(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_release_timebase called with a NULL client"); + return -1; + } else { + return client->ReleaseTimebase(); + } +} + +EXPORT int jack_set_sync_callback(jack_client_t* ext_client, JackSyncCallback sync_callback, void *arg) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_sync_callback called with a NULL client"); + return -1; + } else { + return client->SetSyncCallback(sync_callback, arg); + } +} + +EXPORT int jack_set_sync_timeout(jack_client_t* ext_client, jack_time_t timeout) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_sync_timeout called with a NULL client"); + return -1; + } else { + return client->SetSyncTimeout(timeout); + } +} + +EXPORT int jack_set_timebase_callback(jack_client_t* ext_client, int conditional, JackTimebaseCallback timebase_callback, void* arg) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_set_timebase_callback called with a NULL client"); + return -1; + } else { + return client->SetTimebaseCallback(conditional, timebase_callback, arg); + } +} + +EXPORT int jack_transport_locate(jack_client_t* ext_client, jack_nframes_t frame) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_transport_locate called with a NULL client"); + return -1; + } else { + return client->TransportLocate(frame); + } +} + +EXPORT jack_transport_state_t jack_transport_query(const jack_client_t* ext_client, jack_position_t* pos) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_transport_query called with a NULL client"); + return JackTransportStopped; + } else { + return client->TransportQuery(pos); + } +} + +EXPORT jack_nframes_t jack_get_current_transport_frame(const jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_get_current_transport_frame called with a NULL client"); + return 0; + } else { + return client->GetCurrentTransportFrame(); + } +} + +EXPORT int jack_transport_reposition(jack_client_t* ext_client, jack_position_t* pos) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_transport_reposition called with a NULL client"); + return -1; + } else { + return client->TransportReposition(pos); + } +} + +EXPORT void jack_transport_start(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_transport_start called with a NULL client"); + } else { + client->TransportStart(); + } +} + +EXPORT void jack_transport_stop(jack_client_t* ext_client) +{ + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_transport_stop called with a NULL client"); + } else { + client->TransportStop(); + } +} + +// deprecated + +EXPORT void jack_get_transport_info(jack_client_t* ext_client, jack_transport_info_t* tinfo) +{ + JackLog("jack_get_transport_info : deprecated"); + if (tinfo) + memset(tinfo, 0, sizeof(jack_transport_info_t)); +} + +EXPORT void jack_set_transport_info(jack_client_t* ext_client, jack_transport_info_t* tinfo) +{ + JackLog("jack_set_transport_info : deprecated"); + if (tinfo) + memset(tinfo, 0, sizeof(jack_transport_info_t)); +} + +// statistics.h + +EXPORT float jack_get_max_delayed_usecs(jack_client_t* client) +{ + JackLog("jack_get_max_delayed_usecs: not yet implemented\n"); + return 0.f; +} + +EXPORT float jack_get_xrun_delayed_usecs(jack_client_t* client) +{ + JackLog("jack_get_xrun_delayed_usecs: not yet implemented\n"); + return 0.f; +} + +EXPORT void jack_reset_max_delayed_usecs(jack_client_t* client) +{ + JackLog("jack_reset_max_delayed_usecs: not yet implemented\n"); +} + +// thread.h + +EXPORT int jack_acquire_real_time_scheduling(pthread_t thread, int priority) +{ + JackLog("jack_acquire_real_time_scheduling: not yet implemented\n"); + return -1; +} + +EXPORT int jack_client_create_thread(jack_client_t* client, + pthread_t *thread, + int priority, + int realtime, /* boolean */ + void *(*start_routine)(void*), + void *arg) +{ + JackLog("jack_client_create_thread: not yet implemented\n"); + return -1; +} + +EXPORT int jack_drop_real_time_scheduling(pthread_t thread) +{ + JackLog("jack_drop_real_time_scheduling: not yet implemented\n"); + return -1; +} + +// intclient.h + +EXPORT char* jack_get_internal_client_name(jack_client_t* client, jack_intclient_t intclient) +{ + JackLog("jack_get_internal_client_name: not yet implemented\n"); + return ""; +} + +EXPORT jack_intclient_t jack_internal_client_handle(jack_client_t* client, const char* client_name, jack_status_t* status) +{ + JackLog("jack_internal_client_handle: not yet implemented\n"); + return 0; +} + +EXPORT jack_intclient_t jack_internal_client_load(jack_client_t* client, const char* client_name, jack_options_t options, jack_status_t* status, ...) +{ + JackLog("jack_internal_client_load: not yet implemented\n"); + return 0; +} + +EXPORT jack_status_t jack_internal_client_unload(jack_client_t* client, jack_intclient_t intclient) +{ + JackLog("jack_internal_client_unload: not yet implemented\n"); + return JackFailure; +} diff --git a/common/JackActivationCount.cpp b/common/JackActivationCount.cpp new file mode 100644 index 00000000..6823fef5 --- /dev/null +++ b/common/JackActivationCount.cpp @@ -0,0 +1,43 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackAtomic.h" +#include "JackActivationCount.h" +#include "JackConstants.h" +#include "JackClientControl.h" +#include "JackError.h" + +namespace Jack +{ + +bool JackActivationCount::Signal(JackSynchro* synchro, JackClientControl* control) +{ + if (fValue == 0) { + // Transfer activation to next clients + jack_error("JackActivationCount::Signal value = 0 ref = %ld", control->fRefNum); + return synchro->Signal(); + } else if (DEC_ATOMIC(&fValue) == 1) { + return synchro->Signal(); + } else { + return true; + } +} + +} // end of namespace + diff --git a/common/JackActivationCount.h b/common/JackActivationCount.h new file mode 100644 index 00000000..6c72357c --- /dev/null +++ b/common/JackActivationCount.h @@ -0,0 +1,83 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackActivationCount__ +#define __JackActivationCount__ + +#include "JackSynchro.h" +#include "JackTime.h" + +namespace Jack +{ + +struct JackClientControl; + +/*! +\brief Client activation counter. +*/ + +class JackActivationCount +{ + + private: + + long fValue; + long fCount; + + public: + + JackActivationCount(): fValue(0), fCount(0) + {} + virtual ~JackActivationCount() + {} + + bool Signal(JackSynchro* synchro, JackClientControl* control); + + inline void Reset() + { + fValue = fCount; + } + + inline void SetValue(int val) + { + fCount = val; + } + + inline void IncValue() + { + fCount++; + } + + inline void DecValue() + { + fCount--; + } + + inline int GetValue() const + { + return fValue; + } + +}; + +} // end of namespace + + +#endif + diff --git a/common/JackAtomic.h b/common/JackAtomic.h new file mode 100644 index 00000000..54ac8dc4 --- /dev/null +++ b/common/JackAtomic.h @@ -0,0 +1,197 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackAtomic__ +#define __JackAtomic__ + +typedef unsigned short UInt16; +typedef unsigned long UInt32; +typedef long SInt32; +#ifdef WIN32 + #include <windows.h> + +typedef ULONGLONG UInt64; +#else + +typedef unsigned long long UInt64; +#endif + +#if defined(__APPLE__) + +#if defined(__ppc__) + +static inline int CAS(register UInt32 value, register UInt32 newvalue, register volatile void* addr) +{ + register int result; + asm volatile ( + "# CAS \n" + " lwarx r0, 0, %1 \n" // creates a reservation on addr + " cmpw r0, %2 \n" // test value at addr + " bne- 1f \n" + " sync \n" // synchronize instructions + " stwcx. %3, 0, %1 \n" // if the reservation is not altered + // stores the new value at addr + " bne- 1f \n" + " li %0, 1 \n" + " b 2f \n" + "1: \n" + " li %0, 0 \n" + "2: \n" + : "=r" (result) + : "r" (addr), "r" (value), "r" (newvalue) + : "r0" + ); + return result; +} + +#endif + +#if defined(__i386__) || defined(__x86_64__) + +#ifdef __SMP__ +# define LOCK "lock ; " +#else +# define LOCK "" +#endif + +static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr) +{ + register char ret; + __asm__ __volatile__ ( + "# CAS \n\t" + LOCK "cmpxchg %2, (%1) \n\t" + "sete %0 \n\t" + : "=a" (ret) + : "c" (addr), "d" (newvalue), "a" (value) + ); + return ret; +} + +#endif + +#endif + +#ifdef __linux__ + +#ifdef __PPC__ + +static inline int CAS(register UInt32 value, register UInt32 newvalue, register volatile void* addr) +{ + register int result; + register UInt32 tmp; + asm volatile ( + "# CAS \n" + " lwarx %4, 0, %1 \n" // creates a reservation on addr + " cmpw %4, %2 \n" // test value at addr + " bne- 1f \n" + " sync \n" // synchronize instructions + " stwcx. %3, 0, %1 \n" // if the reservation is not altered + // stores the new value at addr + " bne- 1f \n" + " li %0, 1 \n" + " b 2f \n" + "1: \n" + " li %0, 0 \n" + "2: \n" + : "=r" (result) + : "r" (addr), "r" (value), "r" (newvalue), "r" (tmp) + ); + return result; +} + +#endif + +#if defined(__i386__) || defined(__x86_64__) + +#ifdef __SMP__ +# define LOCK "lock ; " +#else +# define LOCK "" +#endif + +static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr) +{ + register char ret; + __asm__ __volatile__ ( + "# CAS \n\t" + LOCK "cmpxchg %2, (%1) \n\t" + "sete %0 \n\t" + : "=a" (ret) + : "c" (addr), "d" (newvalue), "a" (value) + ); + return ret; +} + +#endif + +#endif + +#ifdef WIN32 + +#ifdef __SMP__ +# define LOCK lock +#else +# define LOCK +#endif + +#define inline __inline + +//---------------------------------------------------------------- +// CAS functions +//---------------------------------------------------------------- +inline char CAS (volatile UInt32 value, UInt32 newvalue, volatile void * addr) +{ + register char c; + __asm { + push ebx + push esi + mov esi, addr + mov eax, value + mov ebx, newvalue + LOCK cmpxchg dword ptr [esi], ebx + sete c + pop esi + pop ebx + } + return c; +} + +#endif + +static inline long INC_ATOMIC(volatile SInt32* val) +{ + SInt32 actual; + do { + actual = *val; + } while (!CAS(actual, actual + 1, val)); + return actual; +} + +static inline long DEC_ATOMIC(volatile SInt32* val) +{ + SInt32 actual; + do { + actual = *val; + } while (!CAS(actual, actual - 1, val)); + return actual; +} + +#endif + + diff --git a/common/JackAtomicArrayState.h b/common/JackAtomicArrayState.h new file mode 100644 index 00000000..7ef69701 --- /dev/null +++ b/common/JackAtomicArrayState.h @@ -0,0 +1,235 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackAtomicArrayState__ +#define __JackAtomicArrayState__ + +#include "JackAtomic.h" +#include "JackError.h" +#include <string.h> // for memcpy + +namespace Jack +{ + +/*! +\brief Counter for CAS +*/ + +struct AtomicArrayCounter +{ + union { + struct { + unsigned char fByteVal[4]; + } + scounter; + UInt32 fLongVal; + }info; + + AtomicArrayCounter& operator=(volatile AtomicArrayCounter& obj) + { + info.fLongVal = obj.info.fLongVal; + return *this; + } +}; + +#define Counter1(e) (e).info.fLongVal +#define GetIndex1(e, state) ((e).info.scounter.fByteVal[state]) +#define SetIndex1(e, state, val) ((e).info.scounter.fByteVal[state] = val) +#define IncIndex1(e, state) ((e).info.scounter.fByteVal[state]++) +#define SwapIndex1(e, state) (((e).info.scounter.fByteVal[0] == state) ? 0 : state) + +/*! +\brief A class to handle serveral states in a lock-free manner + +Requirement: + + - a "current" state + - several possible "pending" state + - an TrySwitchState(int state) operation to atomically switch a "pending" to the "current" state (the pending becomes the current). + + The TrySwitchState operation returns a "current" state (either the same if switch fails or the new one; one can know if the switch has succeeded) + + - a WriteStartState(int state) returns a "pending" state to be written into + - a WriteStartStop(int state) make the written "pending" state become "switchable" + + Different pending states can be written independantly and concurently. + + GetCurrentIndex() *must* return an increasing value to be able to check reading current state coherency + + The fCounter is an array of indexes to access te current and 3 different "pending" states. + + ¥ WriteNextStateStart(int index) must return a valid state to be written into, and must invalidate state "index" ==> cur state switch. + ¥ WriteNextStateStop(int index) makes the "index" state become "switchable" with the current state. + ¥ TrySwitchState(int index) must detect that pending state is a new state, and does the switch + ¥ ReadCurrentState() must return the state + ¥ GetCurrentIndex() must return an index increased each new switch. + ¥ WriteNextStateStart(int index1) and WriteNextStateStart(int index2) can be interleaved + + [switch counter][index state][index state][cur index] + +*/ + +// CHECK livelock + +template <class T> +class JackAtomicArrayState +{ + + protected: + + // fState[0] ==> current + // fState[1] ==> pending + // fState[2] ==> request + + T fState[3]; + volatile AtomicArrayCounter fCounter; + + UInt32 WriteNextStateStartAux(int state, bool* result) + { + AtomicArrayCounter old_val; + AtomicArrayCounter new_val; + UInt32 cur_index; + UInt32 next_index; + bool need_copy; + do { + old_val = fCounter; + new_val = old_val; + *result = GetIndex1(new_val, state); + cur_index = GetIndex1(new_val, 0); + next_index = SwapIndex1(fCounter, state); + need_copy = (GetIndex1(new_val, state) == 0); // Written = false, switch just occured + SetIndex1(new_val, state, 0); // Written = false, invalidate state + } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter)); + if (need_copy) + memcpy(&fState[next_index], &fState[cur_index], sizeof(T)); + return next_index; + } + + void WriteNextStateStopAux(int state) + { + AtomicArrayCounter old_val; + AtomicArrayCounter new_val; + do { + old_val = fCounter; + new_val = old_val; + SetIndex1(new_val, state, 1); // Written = true, state becomes "switchable" + } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter)); + } + + public: + + JackAtomicArrayState() + { + JackLog("JackAtomicArrayState constructor\n"); + Counter1(fCounter) = 0; + } + + ~JackAtomicArrayState() // Not virtual ?? + {} + + /*! + \brief Returns the current state : only valid in the RT reader thread + */ + + T* ReadCurrentState() + { + return &fState[GetIndex1(fCounter, 0)]; + } + + /*! + \brief Returns the current switch counter + */ + + UInt16 GetCurrentIndex() + { + return GetIndex1(fCounter, 3); + } + + /*! + \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one) + */ + + T* TrySwitchState(int state) + { + AtomicArrayCounter old_val; + AtomicArrayCounter new_val; + do { + old_val = fCounter; + new_val = old_val; + if (GetIndex1(new_val, state)) { // If state has been written + SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch + SetIndex1(new_val, state, 0); // Invalidate the state "state" + IncIndex1(new_val, 3); // Inc switch + } + } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter)); + return &fState[GetIndex1(fCounter, 0)]; // Read the counter again + } + + /*! + \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one) + */ + + T* TrySwitchState(int state, bool* result) + { + AtomicArrayCounter old_val; + AtomicArrayCounter new_val; + do { + old_val = fCounter; + new_val = old_val; + if ((*result = GetIndex1(new_val, state))) { // If state has been written + SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch + SetIndex1(new_val, state, 0); // Invalidate the state "state" + IncIndex1(new_val, 3); // Inc switch + } + } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter)); + return &fState[GetIndex1(fCounter, 0)]; // Read the counter again + } + + /*! + \brief Start write operation : setup and returns the next state to update, check for recursive write calls. + */ + + T* WriteNextStateStart(int state) + { + bool tmp; + UInt32 index = WriteNextStateStartAux(state, &tmp); + return &fState[index]; + } + + T* WriteNextStateStart(int state, bool* result) + { + UInt32 index = WriteNextStateStartAux(state, result); + return &fState[index]; + } + + /*! + \brief Stop write operation : make the next state ready to be used by the RT thread + */ + void WriteNextStateStop(int state) + { + WriteNextStateStopAux(state); + } + +}; + +} // end of namespace + + +#endif + diff --git a/common/JackAtomicState.h b/common/JackAtomicState.h new file mode 100644 index 00000000..9e140d3e --- /dev/null +++ b/common/JackAtomicState.h @@ -0,0 +1,240 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackAtomicState__ +#define __JackAtomicState__ + +#include "JackAtomic.h" +#include <string.h> // for memcpy + +namespace Jack +{ + +/*! +\brief Counter for CAS +*/ + +struct AtomicCounter +{ + union { + struct { + UInt16 fShortVal1; // Cur + UInt16 fShortVal2; // Next + } + scounter; + UInt32 fLongVal; + }info; + + AtomicCounter& operator=(volatile AtomicCounter& obj) + { + info.fLongVal = obj.info.fLongVal; + return *this; + } + +}; + + +#define Counter(e) (e).info.fLongVal +#define CurIndex(e) (e).info.scounter.fShortVal1 +#define NextIndex(e) (e).info.scounter.fShortVal2 + +#define CurArrayIndex(e) (CurIndex(e) & 0x0001) +#define NextArrayIndex(e) ((CurIndex(e) + 1) & 0x0001) + +/*! +\brief A class to handle two states (switching from one to the other) in a lock-free manner +*/ + +// CHECK livelock + +template <class T> +class JackAtomicState +{ + + protected: + + T fState[2]; + volatile AtomicCounter fCounter; + SInt32 fCallWriteCounter; + + UInt32 WriteNextStateStartAux() + { + AtomicCounter old_val; + AtomicCounter new_val; + UInt32 cur_index; + UInt32 next_index; + bool need_copy; + do { + old_val = fCounter; + new_val = old_val; + cur_index = CurArrayIndex(new_val); + next_index = NextArrayIndex(new_val); + need_copy = (CurIndex(new_val) == NextIndex(new_val)); + NextIndex(new_val) = CurIndex(new_val); // Invalidate next index + } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter)); + if (need_copy) + memcpy(&fState[next_index], &fState[cur_index], sizeof(T)); + return next_index; + } + + void WriteNextStateStopAux() + { + AtomicCounter old_val; + AtomicCounter new_val; + do { + old_val = fCounter; + new_val = old_val; + NextIndex(new_val)++; // Set next index + } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter)); + } + + public: + + JackAtomicState() + { + Counter(fCounter) = 0; + fCallWriteCounter = 0; + } + + ~JackAtomicState() // Not virtual ?? + {} + + /*! + \brief Returns the current state : only valid in the RT reader thread + */ + T* ReadCurrentState() + { + return &fState[CurArrayIndex(fCounter)]; + } + + /*! + \brief Returns the current state index + */ + UInt16 GetCurrentIndex() + { + return CurIndex(fCounter); + } + + /*! + \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one) + */ + T* TrySwitchState() + { + AtomicCounter old_val; + AtomicCounter new_val; + do { + old_val = fCounter; + new_val = old_val; + CurIndex(new_val) = NextIndex(new_val); // Prepare switch + } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter)); + return &fState[CurArrayIndex(fCounter)]; // Read the counter again + } + + /*! + \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one) + */ + T* TrySwitchState(bool* result) + { + AtomicCounter old_val; + AtomicCounter new_val; + do { + old_val = fCounter; + new_val = old_val; + *result = (CurIndex(new_val) != NextIndex(new_val)); + CurIndex(new_val) = NextIndex(new_val); // Prepare switch + } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter)); + return &fState[CurArrayIndex(fCounter)]; // Read the counter again + } + + /*! + \brief Start write operation : setup and returns the next state to update, check for recursive write calls. + */ + T* WriteNextStateStart() + { + UInt32 next_index = (fCallWriteCounter++ == 0) + ? WriteNextStateStartAux() + : NextArrayIndex(fCounter); // We are inside a wrapping WriteNextStateStart call, NextArrayIndex can be read safely + return &fState[next_index]; + } + + /*! + \brief Stop write operation : make the next state ready to be used by the RT thread + */ + void WriteNextStateStop() + { + if (--fCallWriteCounter == 0) + WriteNextStateStopAux(); + } + + bool IsPendingChange() + { + return CurIndex(fCounter) != NextIndex(fCounter); + } + + /* + // Single writer : write methods get the *next* state to be updated + void TestWriteMethod() + { + T* state = WriteNextStateStart(); + ...... + ...... + WriteNextStateStop(); + } + + // First RT call possibly switch state + void TestReadRTMethod1() + { + T* state = TrySwitchState(); + ...... + ...... + } + + // Other RT methods can safely use the current state during the *same* RT cycle + void TestReadRTMethod2() + { + T* state = ReadCurrentState(); + ...... + ...... + } + + // Non RT read methods : must check state coherency + void TestReadMethod() + { + T* state; + UInt16 cur_index; + UInt16 next_index = GetCurrentIndex(); + do { + cur_index = next_index; + state = ReadCurrentState(); + + ...... + ...... + + next_index = GetCurrentIndex(); + } while (cur_index != next_index); + } + */ +}; + + +} // end of namespace + + +#endif + diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp new file mode 100644 index 00000000..1416a729 --- /dev/null +++ b/common/JackAudioDriver.cpp @@ -0,0 +1,229 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackAudioDriver.h" +#include "JackTime.h" +#include "JackError.h" +#include "JackEngineControl.h" +#include "JackClientControl.h" +#include "JackPort.h" +#include "JackGraphManager.h" +#include "JackEngine.h" +#include <assert.h> + +namespace Jack +{ + + +JackAudioDriver::JackAudioDriver(const char* name, JackEngine* engine, JackSynchro** table) + : JackDriver(name, engine, table), + fCaptureChannels(0), + fPlaybackChannels(0), + fWithMonitorPorts(false) +{} + +JackAudioDriver::~JackAudioDriver() +{} + +int JackAudioDriver::Open(jack_nframes_t nframes, + jack_nframes_t samplerate, + int capturing, + int playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) +{ + fCaptureChannels = inchannels; + fPlaybackChannels = outchannels; + fWithMonitorPorts = monitor; + return JackDriver::Open(nframes, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); +} + +int JackAudioDriver::Attach() +{ + JackPort* port; + jack_port_id_t port_index; + char buf[JACK_PORT_NAME_SIZE]; + unsigned long port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; + int i; + + JackLog("JackAudioDriver::Attach fBufferSize %ld fSampleRate %ld\n", fEngineControl->fBufferSize, fEngineControl->fSampleRate); + + for (i = 0; i < fCaptureChannels; i++) { + snprintf(buf, sizeof(buf) - 1, "%s:%s:out%d", fClientControl->fName, fCaptureDriverName, i + 1); + if ((port_index = fGraphManager->AllocatePort(fClientControl->fRefNum, buf, (JackPortFlags)port_flags)) == NO_PORT) { + jack_error("driver: cannot register port for %s", buf); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetLatency(fEngineControl->fBufferSize + fCaptureLatency); + fCapturePortList[i] = port_index; + JackLog("JackAudioDriver::Attach fCapturePortList[i] %ld \n", port_index); + } + + port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; + + for (i = 0; i < fPlaybackChannels; i++) { + snprintf(buf, sizeof(buf) - 1, "%s:%s:in%d", fClientControl->fName, fPlaybackDriverName, i + 1); + if ((port_index = fGraphManager->AllocatePort(fClientControl->fRefNum, buf, (JackPortFlags)port_flags)) == NO_PORT) { + jack_error("driver: cannot register port for %s", buf); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetLatency(fEngineControl->fBufferSize + fPlaybackLatency); + fPlaybackPortList[i] = port_index; + JackLog("JackAudioDriver::Attach fPlaybackPortList[i] %ld \n", port_index); + + // Monitor ports + if (fWithMonitorPorts) { + JackLog("Create monitor port \n"); + snprintf(buf, sizeof(buf) - 1, "%s:%s:monitor_%u", fClientControl->fName, fPlaybackDriverName, i + 1); + if ((port_index = fGraphManager->AllocatePort(fClientControl->fRefNum, buf, JackPortIsOutput)) == NO_PORT) { + jack_error("Cannot register monitor port for %s", buf); + return -1; + } else { + port = fGraphManager->GetPort(port_index); + port->SetLatency(fEngineControl->fBufferSize); + fMonitorPortList[i] = port_index; + } + } + } + + return 0; +} + +int JackAudioDriver::Detach() +{ + int i; + JackLog("JackAudioDriver::Detach\n"); + + for (i = 0; i < fCaptureChannels; i++) { + fGraphManager->RemovePort(fClientControl->fRefNum, fCapturePortList[i]); + } + + for (i = 0; i < fPlaybackChannels; i++) { + fGraphManager->RemovePort(fClientControl->fRefNum, fPlaybackPortList[i]); + if (fWithMonitorPorts) + fGraphManager->RemovePort(fClientControl->fRefNum, fMonitorPortList[i]); + } + + return 0; +} + +int JackAudioDriver::Write() +{ + for (int i = 0; i < fPlaybackChannels; i++) { + if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) { + float* buffer = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[i], fEngineControl->fBufferSize); + int size = sizeof(float) * fEngineControl->fBufferSize; + // Monitor ports + if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[i]) > 0) + memcpy((jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[i], fEngineControl->fBufferSize), buffer, size); + } + } + return 0; +} + +int JackAudioDriver::Process() +{ + return (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); +} + +/* +The driver ASYNC mode: output buffers computed at the *previous cycle* are used, the server does not +synchronize to the end of client graph execution. +*/ + +int JackAudioDriver::ProcessAsync() +{ + if (Read() < 0) { // Read input buffers for the current cycle + jack_error("ProcessAsync: read error"); + return 0; + } + + if (Write() < 0) { // Write output buffers from the previous cycle + jack_error("ProcessAsync: write error"); + return 0; + } + + if (fIsMaster) { + fEngine->Process(fLastWaitUst); // fLastWaitUst is set in the "low level" layer + fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); + ProcessSlaves(); + } else { + fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); + } + return 0; +} + +/* +The driver SYNC mode: the server does synchronize to the end of client graph execution, +output buffers computed at the *current cycle* are used. +*/ + +int JackAudioDriver::ProcessSync() +{ + if (Read() < 0) { // Read input buffers for the current cycle + jack_error("ProcessSync: read error"); + return 0; + } + + if (fIsMaster) { + fEngine->Process(fLastWaitUst); // fLastWaitUst is set in the "low level" layer + fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); + ProcessSlaves(); + if (fGraphManager->SuspendRefNum(fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0) + jack_error("JackAudioDriver::ProcessSync SuspendRefNum error"); + if (Write() < 0) // Write output buffers for the current cycle + jack_error("Process: write error"); + } else { + fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); + } + return 0; +} + +void JackAudioDriver::NotifyXRun(jack_time_t callback_usecs) +{ + fEngine->NotifyXRun(callback_usecs); +} + +jack_default_audio_sample_t* JackAudioDriver::GetInputBuffer(int port_index) +{ + assert(fCapturePortList[port_index]); + return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[port_index], fEngineControl->fBufferSize); +} + +jack_default_audio_sample_t* JackAudioDriver::GetOutputBuffer(int port_index) +{ + assert(fPlaybackPortList[port_index]); + return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[port_index], fEngineControl->fBufferSize); +} + +jack_default_audio_sample_t* JackAudioDriver::GetMonitorBuffer(int port_index) +{ + assert(fPlaybackPortList[port_index]); + return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_index], fEngineControl->fBufferSize); +} + +} // end of namespace diff --git a/common/JackAudioDriver.h b/common/JackAudioDriver.h new file mode 100644 index 00000000..13de9096 --- /dev/null +++ b/common/JackAudioDriver.h @@ -0,0 +1,88 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackAudioDriver__ +#define __JackAudioDriver__ + +#include "JackDriver.h" + + +namespace Jack +{ + +/*! +\brief The base class for audio drivers: drivers with audio ports. +*/ + +class EXPORT JackAudioDriver : public JackDriver +{ + + protected: + + int fCaptureChannels; + int fPlaybackChannels; + + // static tables since the actual number of ports may be changed by the real driver + // thus dynamic allocation is more difficult to handle + jack_port_id_t fCapturePortList[PORT_NUM]; + jack_port_id_t fPlaybackPortList[PORT_NUM]; + jack_port_id_t fMonitorPortList[PORT_NUM]; + + bool fWithMonitorPorts; + + jack_default_audio_sample_t* GetInputBuffer(int port_index); + jack_default_audio_sample_t* GetOutputBuffer(int port_index); + jack_default_audio_sample_t* GetMonitorBuffer(int port_index); + + private: + + int ProcessAsync(); + int ProcessSync(); + + public: + + JackAudioDriver(const char* name, JackEngine* engine, JackSynchro** table); + virtual ~JackAudioDriver(); + + virtual int Process(); + + virtual int Open(jack_nframes_t nframes, + jack_nframes_t samplerate, + int capturing, + int playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency); + + virtual int Attach(); + virtual int Detach(); + virtual int Write(); + + virtual void NotifyXRun(jack_time_t callback_usecs); // XRun notification sent by the driver + +}; + +} // end of namespace + +#endif diff --git a/common/JackChannel.h b/common/JackChannel.h new file mode 100644 index 00000000..4036aede --- /dev/null +++ b/common/JackChannel.h @@ -0,0 +1,210 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackChannel__ +#define __JackChannel__ + +#include "types.h" +#include "JackError.h" +#include "JackTransportEngine.h" + +namespace Jack +{ + +class JackClientInterface; +class JackClient; +class JackServer; +struct JackEngineControl; +class JackGraphManager; + +/*! +\brief Inter process channel for server/client bidirectionnal communication : request and (receiving) notifications. +*/ + +class JackClientChannelInterface +{ + + public: + + JackClientChannelInterface() + {} + virtual ~JackClientChannelInterface() + {} + + // Open the Server/Client connection + virtual int Open(const char* name, JackClient* obj) + { + return 0; + } + + // Close the Server/Client connection + virtual void Close() + {} + + // Start listening for messages from the server + virtual int Start() + { + return 0; + } + + // Stop listening for messages from the server + virtual void Stop() + {} + + virtual void ClientNew(const char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result) + {} + virtual void ClientNew(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, int* result) + {} + virtual void ClientClose(int refnum, int* result) + {} + + virtual void ClientActivate(int refnum, int* result) + {} + virtual void ClientDeactivate(int refnum, int* result) + {} + + virtual void PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result) + {} + virtual void PortUnRegister(int refnum, jack_port_id_t port_index, int* result) + {} + + virtual void PortConnect(int refnum, const char* src, const char* dst, int* result) + {} + virtual void PortDisconnect(int refnum, const char* src, const char* dst, int* result) + {} + virtual void PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) + {} + virtual void PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) + {} + + virtual void SetBufferSize(jack_nframes_t nframes, int* result) + {} + virtual void SetFreewheel(int onoff, int* result) + {} + + virtual void ReleaseTimebase(int refnum, int* result) + {} + + virtual void SetTimebaseCallback(int refnum, int conditional, int* result) + {} + +}; + +/*! +\brief Inter process channel for server to client notifications. +*/ + +class JackNotifyChannelInterface +{ + + public: + + JackNotifyChannelInterface() + {} + virtual ~JackNotifyChannelInterface() + {} + + // Open the Server/Client connection + virtual int Open(const char* name) + { + return 0; + } + // Close the Server/Client connection + virtual void Close() + {} + + /* + The "sync" parameter allows to choose between "synchronous" and "asynchronous" notification + */ + virtual void ClientNotify(int refnum, const char* name, int notify, int sync, int value, int* result) + {} + + typedef enum { + kAddClient = 0, + kRemoveClient = 1, + kXRunCallback = 2, + kGraphOrderCallback = 3, + kBufferSizeCallback = 4, + kStartFreewheel = 5, + kStopFreewheel = 6, + kPortRegistrationOn = 7, + kPortRegistrationOff = 8, + kZombifyClient = 9, + kDeadClient = 10 + } NotificationType; + +}; + +/*! +\brief Entry point channel for client/server communication. +*/ + +class JackServerChannelInterface +{ + + public: + + JackServerChannelInterface() + {} + virtual ~JackServerChannelInterface() + {} + + // Open the Server/Client connection + virtual int Open(JackServer* server) + { + return 0; + } + // Close the Server/Client connection + virtual void Close() + {} +}; + +/*! +\brief Channel for server RT thread to request server thread communication. +*/ + +class JackServerNotifyChannelInterface +{ + + public: + + JackServerNotifyChannelInterface() + {} + virtual ~JackServerNotifyChannelInterface() + {} + + // Open the Server RT/Server connection + virtual int Open() + { + return 0; + } + // Close the Server RT/Server connection + virtual void Close() + {} + + virtual void ClientNotify(int refnum, int notify, int value) + {} + +}; + + +} // end of namespace + +#endif + diff --git a/common/JackChannelTransaction.h b/common/JackChannelTransaction.h new file mode 100644 index 00000000..d83d4780 --- /dev/null +++ b/common/JackChannelTransaction.h @@ -0,0 +1,48 @@ +/* +Copyright (C) 2004-2006 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. + +*/ + +#ifndef __JackChannelTransaction__ +#define __JackChannelTransaction__ + + +namespace Jack +{ + +/*! +\brief Channel input/output communication. +*/ + +class JackChannelTransaction +{ + + public: + + JackChannelTransaction() + {} + virtual ~JackChannelTransaction() + {} + + virtual int Read(void* data, int len) = 0; + virtual int Write(void* data, int len) = 0; +}; + +} // end of namespace + +#endif + diff --git a/common/JackClient.cpp b/common/JackClient.cpp new file mode 100644 index 00000000..ec1e0468 --- /dev/null +++ b/common/JackClient.cpp @@ -0,0 +1,767 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#include "JackClient.h" +#include "JackGraphManager.h" +#include "JackClientControl.h" +#include "JackEngineControl.h" +#include "JackGlobals.h" +#include "JackChannel.h" +#include "JackTransportEngine.h" +#include <math.h> +#include <string> + +int verbose = 0; + +using namespace std; + +namespace Jack +{ + +JackClient::JackClient() +{} + +JackClient::JackClient(JackSynchro** table) +{ + fThread = JackGlobals::MakeThread(this); + fSynchroTable = table; + fProcess = NULL; + fGraphOrder = NULL; + fXrun = NULL; + fShutdown = NULL; + fInit = NULL; + fBufferSize = NULL; + fFreewheel = NULL; + fPortRegistration = NULL; + fSync = NULL; + fProcessArg = NULL; + fGraphOrderArg = NULL; + fXrunArg = NULL; + fShutdownArg = NULL; + fInitArg = NULL; + fBufferSizeArg = NULL; + fFreewheelArg = NULL; + fPortRegistrationArg = NULL; + fSyncArg = NULL; + fConditionnal = 0; // Temporary?? +} + +JackClient::~JackClient() +{ + delete fThread; +} + +int JackClient::Close() +{ + JackLog("JackClient::Close ref = %ld\n", GetClientControl()->fRefNum); + Deactivate(); + int result = -1; + fChannel->ClientClose(GetClientControl()->fRefNum, &result); + fChannel->Stop(); + fChannel->Close(); + fSynchroTable[GetClientControl()->fRefNum]->Disconnect(); + return result; +} + +bool JackClient::IsActive() +{ + return (GetClientControl()) ? GetClientControl()->fActive : false; +} + +pthread_t JackClient::GetThreadID() +{ + return fThread->GetThreadID(); +} + +/*! +\brief + In ASYNC mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations. + The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations. + Drivers synchro are setup in "flush" mode if server is ASYNC and NOT freewheel. +*/ +void JackClient::SetupDriverSync(bool freewheel) +{ + if (!freewheel && !GetEngineControl()->fSyncMode) { + JackLog("JackClient::SetupDriverSync driver sem in flush mode\n"); + fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(true); + fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(true); + fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(true); + } else { + JackLog("JackClient::SetupDriverSync driver sem in normal mode\n"); + fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(false); + fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(false); + fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(false); + } +} + +/*! +\brief Notification received from the server. +*/ + +int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value) +{ + return 0; +} + +int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, int value) +{ + int res = 0; + + // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient + switch (notify) { + + case JackNotifyChannelInterface::kAddClient: + case JackNotifyChannelInterface::kRemoveClient: + res = ClientNotifyImp(refnum, name, notify, sync, value); + break; + } + + /* + The current semantic is that notifications can only be received when the client has been activated, + although is this implementation, one could imagine calling notifications as soon as the client has be opened. + */ + if (IsActive()) { + + switch (notify) { + + case JackNotifyChannelInterface::kBufferSizeCallback: + JackLog("JackClient::kBufferSizeCallback buffer_size = %ld\n", value); + if (fBufferSize) + res = fBufferSize(value, fBufferSizeArg); + break; + + case JackNotifyChannelInterface::kGraphOrderCallback: + JackLog("JackClient::kGraphOrderCallback\n"); + if (fGraphOrder) + res = fGraphOrder(fGraphOrderArg); + break; + + case JackNotifyChannelInterface::kStartFreewheel: + JackLog("JackClient::kStartFreewheel\n"); + SetupDriverSync(true); + fThread->DropRealTime(); + if (fFreewheel) + fFreewheel(1, fFreewheelArg); + break; + + case JackNotifyChannelInterface::kStopFreewheel: + JackLog("JackClient::kStopFreewheel\n"); + SetupDriverSync(false); + if (fFreewheel) + fFreewheel(0, fFreewheelArg); + fThread->AcquireRealTime(); + break; + + case JackNotifyChannelInterface::kPortRegistrationOn: + JackLog("JackClient::kPortRegistrationOn port_index = %ld\n", value); + if (fPortRegistration) + fPortRegistration(value, 1, fPortRegistrationArg); + break; + + case JackNotifyChannelInterface::kPortRegistrationOff: + JackLog("JackClient::kPortRegistrationOff port_index = %ld \n", value); + if (fPortRegistration) + fPortRegistration(value, 0, fPortRegistrationArg); + break; + + case JackNotifyChannelInterface::kXRunCallback: + JackLog("JackClient::kXRunCallback\n"); + if (fXrun) + res = fXrun(fXrunArg); + break; + + case JackNotifyChannelInterface::kZombifyClient: + res = fThread->Kill(); + JackLog("JackClient::kZombifyClient name = %s ref = %ld \n", name, refnum); + ShutDown(); + break; + } + } + + return res; +} + +/*! +\brief We need to start thread before activating in the server, otherwise the FW driver + connected to the client may not be activated. +*/ +int JackClient::Activate() +{ + JackLog("JackClient::Activate \n"); + if (IsActive()) + return 0; + + if (StartThread() < 0) + return -1; + + int result = -1; + fChannel->ClientActivate(GetClientControl()->fRefNum, &result); + if (result < 0) + return result; + + if (fSync != NULL) /* If a SyncCallback is pending... */ + SetSyncCallback(fSync, fSyncArg); + + if (fTimebase != NULL) /* If a TimebaseCallback is pending... */ + SetTimebaseCallback(fConditionnal, fTimebase, fTimebaseArg); + + GetClientControl()->fActive = true; + return 0; +} + +/*! +\brief Need to stop thread after deactivating in the server. +*/ +int JackClient::Deactivate() +{ + JackLog("JackClient::Deactivate \n"); + if (!IsActive()) + return 0; + + GetClientControl()->fActive = false; + int result = -1; + fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result); + + JackLog("JackClient::Deactivate res = %ld \n", result); + // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate + fThread->Kill(); + return result; +} + +//---------------------- +// RT thread management +//---------------------- + +bool JackClient::CallProcessCallback() +{ + return (fProcess == NULL) ? true : (fProcess(GetEngineControl()->fBufferSize, fProcessArg) == 0); +} + +/*! +\brief Called once when the thread starts. +*/ +bool JackClient::Init() +{ + if (fInit) { + JackLog("JackClient::Init calling client thread init callback\n"); + fInit(fInitArg); + } + return true; +} + +int JackClient::StartThread() +{ + JackLog("JackClient::StartThread : period = %ld computation = %ld constraint = %ld\n", + long(int64_t(GetEngineControl()->fPeriod) / 1000.0f), + long(int64_t(GetEngineControl()->fComputation) / 1000.0f), + long(int64_t(GetEngineControl()->fConstraint) / 1000.0f)); + + // Will do "something" on OSX only... + fThread->SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint); + + if (fThread->Start() < 0) { + jack_error("Start thread error"); + return -1; + } + + if (GetEngineControl()->fRealTime) { + if (fThread->AcquireRealTime(GetEngineControl()->fPriority - 1) < 0) { + jack_error("AcquireRealTime error"); + } + } + + return 0; +} + +/*! +\brief RT thread. +*/ +bool JackClient::Execute() +{ + // Suspend itself: wait on the input synchro + if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) { + jack_error("SuspendRefNum error"); + goto error; + } + + // Process call + if (IsActive()) { + CallSyncCallback(); + bool res = CallProcessCallback(); + CallTimebaseCallback(); + if (!res) + goto end; + } else { + JackLog("Process called for an inactive client\n"); + // Happens if client is still not activated (connected to the FW) + // or still runs while being desactivated by the server + } + + // Resume: signal output clients connected to the running client + if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) { + jack_error("ResumeRefNum error"); + } + + return true; + +end: + JackLog("JackClient::Execute end name = %s\n", GetClientControl()->fName); + + // Continue graph execution for this cycle + if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) { + jack_error("ResumeRefNum error"); + } + + // Hum... not sure about this, the following "close" code is called in the RT thread... + int result; + fThread->DropRealTime(); + fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result); + Close(); // Not sure... + return false; + +error: + jack_error("JackClient::Execute error name = %s", GetClientControl()->fName); + // Hum... not sure about this, the following "close" code is called in the RT thread... + fThread->DropRealTime(); + ShutDown(); + return false; +} + +//----------------- +// Port management +//----------------- + +int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) +{ + // Check port name length + string port_name_str = string(port_name); + if (port_name_str.size() == 0) { + jack_error("port_name is empty."); + return 0; // Means failure here... + } + + string name = string(GetClientControl()->fName) + string(":") + port_name_str; + if (name.size() >= JACK_PORT_NAME_SIZE) { + jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n" + "Please use %lu characters or less.", + GetClientControl()->fName, + port_name, + JACK_PORT_NAME_SIZE - 1); + return 0; // Means failure here... + } + + // Check if port name already exists + if (GetGraphManager()->GetPort(name.c_str()) != NO_PORT) { + jack_error("port_name \"%s\" already exists.", port_name); + return 0; // Means failure here... + } + + JackLog("JackClient::PortRegister ref = %ld name = %s \n", GetClientControl()->fRefNum, name.c_str()); + + int result = -1; + jack_port_id_t port_index = NO_PORT; + fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), flags, buffer_size, &port_index, &result); + JackLog("JackClient::PortRegister port_index = %ld \n", port_index); + + if (result == 0) { + fPortList.push_back(port_index); + return port_index; + } else { + return 0; + } +} + +int JackClient::PortUnRegister(jack_port_id_t port_index) +{ + JackLog("JackClient::PortUnRegister port_index = %ld\n", port_index); + list<jack_port_id_t>::iterator it; + for (it = fPortList.begin(); it != fPortList.end() && *it != port_index; it++) + ; + + if (it != fPortList.end()) { + fPortList.erase(it); + int result = -1; + fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result); + return result; + } else { + jack_error("unregistering a port %ld that is not own by the client", port_index); + return -1; + } +} + +int JackClient::PortConnect(const char* src, const char* dst) +{ + JackLog("JackClient::Connect src = %s dst = %s\n", src, dst); + int result = -1; + fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result); + return result; +} + +int JackClient::PortDisconnect(const char* src, const char* dst) +{ + JackLog("JackClient::Disconnect src = %s dst = %s\n", src, dst); + int result = -1; + fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result); + return result; +} + +int JackClient::PortConnect(jack_port_id_t src, jack_port_id_t dst) +{ + JackLog("JackClient::PortConnect src = %ld dst = %ld\n", src, dst); + int result = -1; + fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result); + return result; +} + +int JackClient::PortDisconnect(jack_port_id_t src) +{ + JackLog("JackClient::PortDisconnect src = %ld\n", src); + int result = -1; + fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result); + return result; +} + +int JackClient::PortIsMine(jack_port_id_t port_index) +{ + JackPort* port = GetGraphManager()->GetPort(port_index); + return GetClientControl()->fRefNum == port->GetRefNum(); +} + +//-------------------- +// Context management +//-------------------- + +int JackClient::SetBufferSize(jack_nframes_t nframes) +{ + int result = -1; + fChannel->SetBufferSize(nframes, &result); + return result; +} + +int JackClient::SetFreeWheel(int onoff) +{ + int result = -1; + fChannel->SetFreewheel(onoff, &result); + return result; +} + +/* +ShutDown is called: +- from the RT thread when Execute method fails +- possibly from a "closed" notification channel +(Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown)) +*/ + +void JackClient::ShutDown() +{ + JackLog("ShutDown\n"); + if (fShutdown) { + GetClientControl()->fActive = false; + fShutdown(fShutdownArg); + fShutdown = NULL; + } +} + +//---------------------- +// Transport management +//---------------------- + +int JackClient::ReleaseTimebase() +{ + int result = -1; + fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result); + if (result == 0) { + fTimebase = NULL; + fTimebaseArg = NULL; + } + return result; +} + +/* 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; + fSyncArg = arg; + return 0; +} + +int JackClient::SetSyncTimeout(jack_time_t timeout) +{ + GetEngineControl()->fTransport.SetSyncTimeout(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); + JackLog("SetTimebaseCallback result = %ld\n", result); + if (result == 0) { + fTimebase = timebase_callback; + fTimebaseArg = arg; + } else { + fTimebase = NULL; + fTimebaseArg = NULL; + } + JackLog("SetTimebaseCallback OK result = %ld\n", result); + return result; + } else { + fTimebase = timebase_callback; + fTimebaseArg = arg; + fConditionnal = conditional; + return 0; + } +} + +// Must be RT safe +int JackClient::RequestNewPos(jack_position_t* pos) +{ + JackTransportEngine& transport = GetEngineControl()->fTransport; + jack_position_t* request = transport.WriteNextStateStart(2); + pos->unique_1 = pos->unique_2 = transport.GenerateUniqueID(); + JackTransportEngine::TransportCopyPosition(pos, request); + JackLog("RequestNewPos pos = %ld\n", pos->frame); + transport.WriteNextStateStop(2); + return 0; +} + +int JackClient::TransportLocate(jack_nframes_t frame) +{ + jack_position_t pos; + pos.frame = frame; + pos.valid = (jack_position_bits_t)0; + JackLog("TransportLocate pos = %ld\n", pos.frame); + return RequestNewPos(&pos); +} + +int JackClient::TransportReposition(jack_position_t* pos) +{ + jack_position_t tmp = *pos; + JackLog("TransportReposition pos = %ld\n", pos->frame); + return (tmp.valid & ~JACK_POSITION_MASK) ? EINVAL : RequestNewPos(&tmp); +} + +jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos) +{ + if (pos) + GetEngineControl()->fTransport.ReadCurrentPos(pos); + return GetEngineControl()->fTransport.GetState(); +} + +jack_nframes_t JackClient::GetCurrentTransportFrame() +{ + jack_position_t pos; + jack_transport_state_t state = TransportQuery(&pos); + + if (state == 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; + } +} + +// Must be RT safe: directly write in the transport shared mem +void JackClient::TransportStart() +{ + GetEngineControl()->fTransport.SetCommand(TransportCommandStart); +} + +// Must be RT safe: directly write in the transport shared mem +void JackClient::TransportStop() +{ + GetEngineControl()->fTransport.SetCommand(TransportCommandStop); +} + +// Never called concurently with the server +// TODO check concurency 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) { + GetClientControl()->fTransportState = JackTransportRolling; + } else if (GetClientControl()->fTransportState == JackTransportStarting) { + if (fSync(transport_state, cur_pos, fSyncArg)) + GetClientControl()->fTransportState = JackTransportRolling; + } + 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; + } +} + +void JackClient::CallTimebaseCallback() +{ + JackTransportEngine& transport = GetEngineControl()->fTransport; + + if (fTimebase != NULL && fTimebaseArg != NULL && GetClientControl()->fRefNum == transport.GetTimebaseMaster()) { + + 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; + } + + transport.WriteNextStateStop(1); + } +} + +//--------------------- +// Callback management +//--------------------- + +void JackClient::OnShutdown(JackShutdownCallback callback, void *arg) +{ + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + } else { + fShutdownArg = arg; + fShutdown = callback; + } +} + +int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg) +{ + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + return -1; + } else { + fProcessArg = arg; + fProcess = callback; + return 0; + } +} + +int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg) +{ + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + return -1; + } else { + fXrunArg = arg; + fXrun = callback; + return 0; + } +} + +int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg) +{ + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + return -1; + } else { + fInitArg = arg; + fInit = callback; + return 0; + } +} + +int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg) +{ + JackLog("SetGraphOrderCallback \n"); + + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + return -1; + } else { + fGraphOrder = callback; + fGraphOrderArg = arg; + return 0; + } +} + +int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg) +{ + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + return -1; + } else { + fBufferSizeArg = arg; + fBufferSize = callback; + return 0; + } +} + +int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg) +{ + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + return -1; + } else { + fFreewheelArg = arg; + fFreewheel = callback; + return 0; + } +} + +int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg) +{ + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + return -1; + } else { + fPortRegistrationArg = arg; + fPortRegistration = callback; + return 0; + } +} + +} // end of namespace + diff --git a/common/JackClient.h b/common/JackClient.h new file mode 100644 index 00000000..b1b5eee0 --- /dev/null +++ b/common/JackClient.h @@ -0,0 +1,162 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackClient__ +#define __JackClient__ + +#include "JackClientInterface.h" +#include "JackThread.h" +#include "JackConstants.h" +#include "JackSynchro.h" +#include "types.h" +#include "transport_types.h" +#include <list> + +namespace Jack +{ + +class JackClientChannelInterface; +class JackGraphManager; +class JackServer; +class JackEngine; +class JackSynchro; +struct JackClientControl; +struct JackEngineControl; +class JackSyncInterface; + +typedef void (*JackShutdownCallback)(void *arg); + +/*! +\brief The base class for clients: share part of the implementation for JackInternalClient and JackLibClient. +*/ + +class JackClient : public JackClientInterface, public JackRunnableInterface +{ + + protected: + + JackProcessCallback fProcess; + JackGraphOrderCallback fGraphOrder; + JackXRunCallback fXrun; + JackShutdownCallback fShutdown; + JackThreadInitCallback fInit; + JackBufferSizeCallback fBufferSize; + JackFreewheelCallback fFreewheel; + JackPortRegistrationCallback fPortRegistration; + JackTimebaseCallback fTimebase; + JackSyncCallback fSync; + void* fProcessArg; + void* fGraphOrderArg; + void* fXrunArg; + void* fShutdownArg; + void* fInitArg; + void* fBufferSizeArg; + void* fFreewheelArg; + void* fPortRegistrationArg; + void* fTimebaseArg; + void* fSyncArg; + int fConditionnal; + + JackThread* fThread; /*! Thread to execute the Process function */ + JackClientChannelInterface* fChannel; + JackSynchro** fSynchroTable; + std::list<jack_port_id_t> fPortList; + + int StartThread(); + void SetupDriverSync(bool freewheel); + bool IsActive(); + + bool CallProcessCallback(); + void CallSyncCallback(); + void CallTimebaseCallback(); + int RequestNewPos(jack_position_t* pos); + + virtual int ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value); + + public: + + JackClient(); + JackClient(JackSynchro** table); + virtual ~JackClient(); + + virtual int Open(const char* name) = 0; + virtual int Close(); + + virtual JackGraphManager* GetGraphManager() const = 0; + virtual JackEngineControl* GetEngineControl() const = 0; + + // Notifications + virtual int ClientNotify(int refnum, const char* name, int notify, int sync, int value); + + virtual int Activate(); + virtual int Deactivate(); + + // Context + virtual int SetBufferSize(jack_nframes_t nframes); + virtual int SetFreeWheel(int onoff); + virtual void ShutDown(); + virtual pthread_t GetThreadID(); + + // Port management + virtual int PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); + virtual int PortUnRegister(jack_port_id_t port); + + virtual int PortConnect(const char* src, const char* dst); + virtual int PortDisconnect(const char* src, const char* dst); + virtual int PortConnect(jack_port_id_t src, jack_port_id_t dst); + virtual int PortDisconnect(jack_port_id_t src); + + int PortIsMine(jack_port_id_t port_index); + + // Transport + virtual int ReleaseTimebase(); + virtual int SetSyncCallback(JackSyncCallback sync_callback, void* arg); + virtual int SetSyncTimeout(jack_time_t timeout); + virtual int SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg); + virtual int TransportLocate(jack_nframes_t frame); + virtual jack_transport_state_t TransportQuery(jack_position_t* pos); + virtual jack_nframes_t GetCurrentTransportFrame(); + virtual int TransportReposition(jack_position_t* pos); + virtual void TransportStart(); + virtual void TransportStop(); + + // Callbacks + virtual void OnShutdown(JackShutdownCallback callback, void *arg); + virtual int SetProcessCallback(JackProcessCallback callback, void* arg); + virtual int SetXRunCallback(JackXRunCallback callback, void* arg); + virtual int SetInitCallback(JackThreadInitCallback callback, void* arg); + virtual int SetGraphOrderCallback(JackGraphOrderCallback callback, void* arg); + virtual int SetBufferSizeCallback(JackBufferSizeCallback callback, void* arg); + virtual int SetFreewheelCallback(JackFreewheelCallback callback, void* arg); + virtual int SetPortRegistrationCallback(JackPortRegistrationCallback callback, void* arg); + + // JackRunnableInterface interface + bool Init(); + bool Execute(); +}; + +// Each "side" server and client will implement this to get the shared graph manager, engine control and inter-process synchro table. +extern JackGraphManager* GetGraphManager(); +extern JackEngineControl* GetEngineControl(); +extern JackSynchro** GetSynchroTable(); + +} // end of namespace + +#endif diff --git a/common/JackClientControl.h b/common/JackClientControl.h new file mode 100644 index 00000000..e9e9ce5a --- /dev/null +++ b/common/JackClientControl.h @@ -0,0 +1,76 @@ +/* +Copyright (C) 2003 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackClientControl__ +#define __JackClientControl__ + +#include "JackShmMem.h" +#include "JackPort.h" +#include "JackSynchro.h" +#include "transport_types.h" + +namespace Jack +{ + +/*! +\brief Client control in shared memory. +*/ + +struct JackClientControl : public JackShmMem +{ + char fName[JACK_CLIENT_NAME_SIZE + 1]; + volatile jack_transport_state_t fTransportState; + int fRefNum; + bool fZombie; + bool fActive; + + JackClientControl(const char* name, int refnum) + { + Init(name, refnum); + } + + JackClientControl(const char* name) + { + Init(name, -1); + } + + JackClientControl() + { + Init("", -1); + } + + virtual ~JackClientControl() + {} + + void Init(const char* name, int refnum) + { + strcpy(fName, name); + fRefNum = refnum; + fTransportState = JackTransportStopped; + fZombie = false; + fActive = false; + } + +}; + +} // end of namespace + + +#endif diff --git a/common/JackClientInterface.h b/common/JackClientInterface.h new file mode 100644 index 00000000..b329794b --- /dev/null +++ b/common/JackClientInterface.h @@ -0,0 +1,56 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackClientInterface__ +#define __JackClientInterface__ + +#include "JackExports.h" + + +namespace Jack +{ + +struct JackClientControl; + +/*! +\brief Client interface. +*/ + +class EXPORT JackClientInterface +{ + + public: + + JackClientInterface() + {} + virtual ~JackClientInterface() + {} + + virtual int Close() = 0; + + virtual int ClientNotify(int refnum, const char* name, int notify, int sync, int value) = 0; + + virtual JackClientControl* GetClientControl() const = 0; +}; + + +} // end of namespace + +#endif diff --git a/common/JackConnectionManager.cpp b/common/JackConnectionManager.cpp new file mode 100644 index 00000000..5a1de3f7 --- /dev/null +++ b/common/JackConnectionManager.cpp @@ -0,0 +1,452 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <iostream> +#include <assert.h> +#include "JackConnectionManager.h" +#include "JackClientControl.h" +#include "JackError.h" + +namespace Jack +{ + +JackConnectionManager::JackConnectionManager() +{ + int i; + JackLog("JackConnectionManager::InitConnections size = %ld \n", sizeof(JackConnectionManager)); + + for (i = 0; i < PORT_NUM; i++) { + fConnection[i].Init(); + } + + fLoopFeedback.Init(); + + JackLog("JackConnectionManager::InitClients\n"); + for (i = 0; i < CLIENT_NUM; i++) { + InitClient(i); + } +} + +JackConnectionManager::~JackConnectionManager() +{} + +//-------------- +// Internal API +//-------------- + +bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const +{ + JackLog("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld\n", ref1, ref2); + + if (ref1 == AUDIO_DRIVER_REFNUM // Driver is reached + || ref2 == AUDIO_DRIVER_REFNUM + || ref1 == FREEWHEEL_DRIVER_REFNUM + || ref2 == FREEWHEEL_DRIVER_REFNUM + || ref1 == LOOPBACK_DRIVER_REFNUM + || ref2 == LOOPBACK_DRIVER_REFNUM) { + return false; + } else if (ref1 == ref2) { // Same refnum + return true; + } else { + jack_int_t output[CLIENT_NUM]; + fConnectionRef.GetOutputTable(ref1, output); + + if (fConnectionRef.IsInsideTable(ref2, output)) { // If ref2 is contained in the outputs of ref1 + return true; + } else { + for (int i = 0; i < CLIENT_NUM && output[i] != EMPTY; i++) { // Otherwise recurse for all ref1 outputs + if (IsLoopPathAux(output[i], ref2)) + return true; // Stop when a path is found + } + return false; + } + } +} + +void JackConnectionManager::InitClient(int refnum) +{ + fInputPort[refnum].Init(); + fOutputPort[refnum].Init(); + fConnectionRef.Init(refnum); + fInputCounter[refnum].SetValue(0); +} + +//-------------- +// External API +//-------------- + +int JackConnectionManager::GetActivation(int refnum) const +{ + return fInputCounter[refnum].GetValue(); +} + +/*! +\brief Connect port_src to port_dst. +*/ +int JackConnectionManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst) +{ + JackLog("JackConnectionManager::Connect port_src = %ld port_dst = %ld\n", port_src, port_dst); + + if (fConnection[port_src].AddItem(port_dst)) { + return 0; + } else { + jack_error("Connection table is full !!"); + return -1; + } +} + +/*! +\brief Disconnect port_src from port_dst. +*/ +int JackConnectionManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst) +{ + JackLog("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld\n", port_src, port_dst); + + if (fConnection[port_src].RemoveItem(port_dst)) { + return 0; + } else { + jack_error("Connection not found !!"); + return -1; + } +} + +/*! +\brief Check if port_src and port_dst are connected. +*/ +bool JackConnectionManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const +{ + return fConnection[port_src].CheckItem(port_dst); +} + +/*! +\brief Get the connection number of a given port. +*/ +jack_int_t JackConnectionManager::Connections(jack_port_id_t port_index) const +{ + return fConnection[port_index].GetItemCount(); +} + +jack_port_id_t JackConnectionManager::GetPort(jack_port_id_t port_index, int connection) const +{ + assert(connection < CONNECTION_NUM); + return (jack_port_id_t)fConnection[port_index].GetItem(connection); +} + +/*! +\brief Get the connection port array. +*/ +const jack_int_t* JackConnectionManager::GetConnections(jack_port_id_t port_index) const +{ + return fConnection[port_index].GetItems(); +} + +//------------------------ +// Client port management +//------------------------ + +/*! +\brief Add an input port to a client. +*/ +int JackConnectionManager::AddInputPort(int refnum, jack_port_id_t port_index) +{ + if (fInputPort[refnum].AddItem(port_index)) { + JackLog("JackConnectionManager::AddInputPort ref = %ld port = %ld\n", refnum, port_index); + return 0; + } else { + jack_error("Maximum number of input ports is reached for application ref = %ld", refnum); + return -1; + } +} + +/*! +\brief Add an output port to a client. +*/ +int JackConnectionManager::AddOutputPort(int refnum, jack_port_id_t port_index) +{ + if (fOutputPort[refnum].AddItem(port_index)) { + JackLog("JackConnectionManager::AddOutputPort ref = %ld port = %ld\n", refnum, port_index); + return 0; + } else { + jack_error("Maximum number of output ports is reached for application ref = %ld", refnum); + return -1; + } +} + +/*! +\brief Remove an input port from a client. +*/ +int JackConnectionManager::RemoveInputPort(int refnum, jack_port_id_t port_index) +{ + JackLog("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld \n", refnum, port_index); + + if (fInputPort[refnum].RemoveItem(port_index)) { + return 0; + } else { + jack_error("Input port index = %ld not found for application ref = %ld", port_index, refnum); + return -1; + } +} + +/*! +\brief Remove an output port from a client. +*/ +int JackConnectionManager::RemoveOutputPort(int refnum, jack_port_id_t port_index) +{ + JackLog("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld \n", refnum, port_index); + + if (fOutputPort[refnum].RemoveItem(port_index)) { + return 0; + } else { + jack_error("Output port index = %ld not found for application ref = %ld", port_index, refnum); + return -1; + } +} + +/*! +\brief Get the input port array of a given refnum. +*/ +const jack_int_t* JackConnectionManager::GetInputPorts(int refnum) +{ + return fInputPort[refnum].GetItems(); +} + +/*! +\brief Get the output port array of a given refnum. +*/ +const jack_int_t* JackConnectionManager::GetOutputPorts(int refnum) +{ + return fOutputPort[refnum].GetItems(); +} + +/*! +\brief Return the first available refnum. +*/ +int JackConnectionManager::AllocateRefNum() +{ + for (int i = 0; i < CLIENT_NUM; i++) { + if (fInputPort[i].IsAvailable()) { + JackLog("JackConnectionManager::AllocateRefNum ref = %ld\n", i); + return i; + } + } + + return -1; +} + +/*! +\brief Release the refnum. +*/ +void JackConnectionManager::ReleaseRefNum(int refnum) +{ + JackLog("JackConnectionManager::ReleaseRefNum ref = %ld\n", refnum); + InitClient(refnum); +} + +/*! +\brief Reset all clients activation. +*/ +void JackConnectionManager::ResetGraph(JackClientTiming* timing) +{ + // Reset activation counter : must be done *before* starting to resume clients + for (int i = 0; i < CLIENT_NUM; i++) { + fInputCounter[i].Reset(); + timing[i].fStatus = NotTriggered; + } +} + +/*! +\brief Wait on the input synchro. +*/ +int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing, long time_out_usec) +{ + int res; + if ((res = table[control->fRefNum]->TimedWait(time_out_usec))) { + timing[control->fRefNum].fStatus = Running; + timing[control->fRefNum].fAwakeAt = GetMicroSeconds(); + } + return (res) ? 0 : -1; +} + +/*! +\brief Signal clients connected to the given client. +*/ +int JackConnectionManager::ResumeRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing) +{ + jack_time_t current_date = GetMicroSeconds(); + const jack_int_t* outputRef = fConnectionRef.GetItems(control->fRefNum); + int res = 0; + + // Update state and timestamp of current client + timing[control->fRefNum].fStatus = Finished; + timing[control->fRefNum].fFinishedAt = current_date; + + for (int i = 0; i < CLIENT_NUM; i++) { + + // Signal connected clients or drivers + if (outputRef[i] > 0) { + + // Update state and timestamp of destination clients + timing[i].fStatus = Triggered; + timing[i].fSignaledAt = current_date; + + if (!fInputCounter[i].Signal(table[i], control)) { + JackLog("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld \n", control->fRefNum, i); + res = -1; + } + } + } + + return res; +} + +/*! +\brief Increment the number of ports between 2 clients, if the 2 clients become connected, then the Activation counter is updated. +*/ +void JackConnectionManager::IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst) +{ + int ref1 = GetOutputRefNum(port_src); + int ref2 = GetInputRefNum(port_dst); + + assert(ref1 >= 0 && ref2 >= 0); + + DirectConnect(ref1, ref2); + JackLog("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld\n", ref1, ref2); +} + +/*! +\brief Decrement the number of ports between 2 clients, if the 2 clients become disconnected, then the Activation counter is updated. +*/ +void JackConnectionManager::DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst) +{ + int ref1 = GetOutputRefNum(port_src); + int ref2 = GetInputRefNum(port_dst); + + assert(ref1 >= 0 && ref2 >= 0); + + DirectDisconnect(ref1, ref2); + JackLog("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld\n", ref1, ref2); +} + +/*! +\brief Directly connect 2 reference numbers. +*/ +void JackConnectionManager::DirectConnect(int ref1, int ref2) +{ + assert(ref1 >= 0 && ref2 >= 0); + + if (fConnectionRef.IncItem(ref1, ref2) == 1) { // First connection between client ref1 and client ref2 + JackLog("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld\n", ref1, ref2); + fInputCounter[ref2].IncValue(); + } +} + +/*! +\brief Directly disconnect 2 reference numbers. +*/ +void JackConnectionManager::DirectDisconnect(int ref1, int ref2) +{ + assert(ref1 >= 0 && ref2 >= 0); + + if (fConnectionRef.DecItem(ref1, ref2) == 0) { // Last connection between client ref1 and client ref2 + JackLog("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld\n", ref1, ref2); + fInputCounter[ref2].DecValue(); + } +} + +/*! +\brief Returns the connections state between 2 refnum. +*/ +bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const +{ + assert(ref1 >= 0 && ref2 >= 0); + return fConnectionRef.GetItemCount(ref1, ref2); +} + +/*! +\brief Get the client refnum of a given input port. +*/ +int JackConnectionManager::GetInputRefNum(jack_port_id_t port_index) const +{ + for (int i = 0; i < CLIENT_NUM; i++) { + if (fInputPort[i].CheckItem(port_index)) + return i; + } + + return -1; +} + +/*! +\brief Get the client refnum of a given ouput port. +*/ +int JackConnectionManager::GetOutputRefNum(jack_port_id_t port_index) const +{ + for (int i = 0; i < CLIENT_NUM; i++) { + if (fOutputPort[i].CheckItem(port_index)) + return i; + } + + return -1; +} + +/*! +\brief Test is a connection path exists between port_src and port_dst. +*/ +bool JackConnectionManager::IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const +{ + return IsLoopPathAux(GetInputRefNum(port_dst), GetOutputRefNum(port_src)); +} + +bool JackConnectionManager::IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const +{ + return (fLoopFeedback.GetConnectionIndex(GetOutputRefNum(port_src), GetInputRefNum(port_dst)) >= 0); +} + +bool JackConnectionManager::IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) +{ + int ref1 = GetOutputRefNum(port_src); + int ref2 = GetInputRefNum(port_dst); + + // Add an activation connection in the other direction + JackLog("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld\n", ref1, ref2); + assert(ref1 >= 0 && ref2 >= 0); + + if (ref1 != ref2) + DirectConnect(ref2, ref1); + + return fLoopFeedback.IncConnection(ref1, ref2); // Add the feedback connection +} + +bool JackConnectionManager::DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) +{ + int ref1 = GetOutputRefNum(port_src); + int ref2 = GetInputRefNum(port_dst); + + // Remove an activation connection in the other direction + JackLog("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld\n", ref1, ref2); + assert(ref1 >= 0 && ref2 >= 0); + + if (ref1 != ref2) + DirectDisconnect(ref2, ref1); + + return fLoopFeedback.DecConnection(ref1, ref2); // Remove the feedback connection +} + +} // end of namespace + + diff --git a/common/JackConnectionManager.h b/common/JackConnectionManager.h new file mode 100644 index 00000000..652cefc5 --- /dev/null +++ b/common/JackConnectionManager.h @@ -0,0 +1,468 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackConnectionManager__ +#define __JackConnectionManager__ + +#include "JackConstants.h" +#include "JackActivationCount.h" +#include <assert.h> + +namespace Jack +{ + +#define NO_PORT 0xFFFE + +#define EMPTY 0xFFFD +#define FREE 0xFFFC + +typedef uint16_t jack_int_t; // Internal type for ports and refnum + +struct JackClientControl; + +typedef enum { + NotTriggered, + Triggered, + Running, + Finished, +} jack_client_state_t; + +/*! +\brief Utility class. +*/ + +template <int SIZE> +class JackFixedArray +{ + + private: + + jack_int_t fTable[SIZE]; + uint32_t fCounter; + + public: + + JackFixedArray() + { + Init(); + } + + virtual ~JackFixedArray() + {} + + void Init() + { + for (int i = 0; i < SIZE; i++) + fTable[i] = EMPTY; + fCounter = 0; + } + + bool AddItem(jack_int_t index) + { + for (int i = 0; i < SIZE; i++) { + if (fTable[i] == EMPTY) { + fTable[i] = index; + fCounter++; + return true; + } + } + return false; + } + + bool RemoveItem(jack_int_t index) + { + for (int i = 0; i < SIZE; i++) { + if (fTable[i] == index) { + fCounter--; + // Shift all indexes + if (i == SIZE - 1) { + fTable[i] = EMPTY; + } else { + int j; + for (j = i; j <= SIZE - 2 && fTable[j] != EMPTY; j++) { + fTable[j] = fTable[j + 1]; + } + fTable[j] = EMPTY; + } + return true; + } + } + return false; + } + + jack_int_t GetItem(jack_int_t index) const + { + return (index < SIZE) ? fTable[index] : EMPTY; + } + + const jack_int_t* GetItems() const + { + return fTable; + } + + bool CheckItem(jack_int_t index) const + { + for (int i = 0; i < SIZE && fTable[i] != EMPTY; i++) { + if (fTable[i] == index) + return true; + } + return false; + } + + uint32_t GetItemCount() const + { + return fCounter; + } + +}; + +/*! +\brief Utility class. +*/ + +template <int SIZE> +class JackFixedArray1 : public JackFixedArray<SIZE> +{ + private: + + bool fUsed; + + public: + + JackFixedArray1() + { + Init(); + } + + virtual ~JackFixedArray1() + {} + + void Init() + { + JackFixedArray<SIZE>::Init(); + fUsed = false; + } + + bool IsAvailable() + { + if (fUsed) { + return false; + } else { + fUsed = true; + return true; + } + } +}; + +/*! +\brief Utility class. +*/ + +template <int SIZE> +class JackFixedMatrix +{ + private: + + jack_int_t fTable[SIZE][SIZE]; + + public: + + JackFixedMatrix() + {} + + virtual ~JackFixedMatrix() + {} + + void Init(jack_int_t index) + { + for (int i = 0; i < SIZE; i++) { + fTable[index][i] = 0; + fTable[i][index] = 0; + } + } + + const jack_int_t* GetItems(jack_int_t index) const + { + return fTable[index]; + } + + jack_int_t IncItem(jack_int_t index1, jack_int_t index2) + { + fTable[index1][index2]++; + return fTable[index1][index2]; + } + + jack_int_t DecItem(jack_int_t index1, jack_int_t index2) + { + fTable[index1][index2]--; + return fTable[index1][index2]; + } + + jack_int_t GetItemCount(jack_int_t index1, jack_int_t index2) const + { + return fTable[index1][index2]; + } + + /*! + \brief Get the output indexes of a given index. + */ + void GetOutputTable(jack_int_t index, jack_int_t* output) const + { + int i, j; + + for (i = 0; i < SIZE; i++) + output[i] = EMPTY; + + for (i = 0, j = 0; i < SIZE; i++) { + if (fTable[index][i] > 0) { + output[j] = i; + j++; + } + } + } + + bool IsInsideTable(jack_int_t index, jack_int_t* output) const + { + for (int i = 0; i < SIZE && output[i] != EMPTY; i++) { + if (output[i] == index) + return true; + } + return false; + } + +}; + +/*! +\brief Utility class. +*/ + +template <int SIZE> +class JackLoopFeedback +{ + private: + + int fTable[SIZE][3]; + + /*! + \brief Add a feedback connection between 2 refnum. + */ + bool AddConnectionAux(int ref1, int ref2) + { + for (int i = 0; i < SIZE; i++) { + if (fTable[i][0] == EMPTY) { + fTable[i][0] = ref1; + fTable[i][1] = ref2; + fTable[i][2] = 1; + JackLog("JackLoopFeedback::AddConnectionAux ref1 = %ld ref2 = %ld\n", ref1, ref2); + return true; + } + } + jack_error("Feedback table is full !!\n"); + return false; + } + + /*! + \brief Remove a feedback connection between 2 refnum. + */ + bool RemoveConnectionAux(int ref1, int ref2) + { + for (int i = 0; i < SIZE; i++) { + if (fTable[i][0] == ref1 && fTable[i][1] == ref2) { + fTable[i][0] = EMPTY; + fTable[i][1] = EMPTY; + fTable[i][2] = 0; + JackLog("JackLoopFeedback::RemoveConnectionAux ref1 = %ld ref2 = %ld\n", ref1, ref2); + return true; + } + } + jack_error("Feedback connection not found\n"); + return false; + } + + int IncConnection(int index) + { + fTable[index][2]++; + return fTable[index][2]; + } + + int DecConnection(int index) + { + fTable[index][2]--; + return fTable[index][2]; + } + + public: + + JackLoopFeedback() + { + Init(); + } + virtual ~JackLoopFeedback() + {} + + void Init() + { + for (int i = 0; i < SIZE; i++) { + fTable[i][0] = EMPTY; + fTable[i][1] = EMPTY; + fTable[i][2] = 0; + } + } + + bool IncConnection(int ref1, int ref2) + { + int index = GetConnectionIndex(ref1, ref2); + + if (index >= 0) { // Feedback connection is already added, increment counter + IncConnection(index); + return true; + } else { + return AddConnectionAux(ref1, ref2); // Add the feedback connection + } + } + + bool DecConnection(int ref1, int ref2) + { + int index = GetConnectionIndex(ref1, ref2); + + if (index >= 0) { + JackLog("JackLoopFeedback::DecConnection ref1 = %ld ref2 = %ld index = %ld\n", ref1, ref2, index); + return (DecConnection(index) == 0) ? RemoveConnectionAux(ref1, ref2) : true; + } else { + return false; + } + } + + /*! + \brief Test if a connection between 2 refnum is a feedback connection. + */ + int GetConnectionIndex(int ref1, int ref2) const + { + for (int i = 0; i < SIZE; i++) { + if (fTable[i][0] == ref1 && fTable[i][1] == ref2) + return i; + } + return -1; + } + +}; + +/*! +\brief For client timing measurements. +*/ + +struct JackClientTiming +{ + jack_time_t fSignaledAt; + jack_time_t fAwakeAt; + jack_time_t fFinishedAt; + jack_client_state_t fStatus; + + JackClientTiming():fSignaledAt(0), fAwakeAt(0), fFinishedAt(0), fStatus(NotTriggered) + {} + ~JackClientTiming() + {} +}; + +/*! +\brief Connection manager. + +<UL> +<LI>The <B>fConnection</B> array contains the list (array line) of connected ports for a given port. +<LI>The <B>fConnectionCount</B> array contains the number of connected ports to a given port. +<LI>The <B>fInputPort</B> array contains the list (array line) of input connected ports for a given client. +<LI>The <B>fOutputPort</B> array contains the list (array line) of ouput connected ports for a given client. +<LI>The <B>fConnectionRef</B> array contains the number of ports connected between two clients. +<LI>The <B>fInputRef</B> array contains the number of input clients connected to a given client. +<LI>The <B>fInputCounter</B> array contains the number of input clients connected to a given for activation purpose. +</UL> +*/ + +class JackConnectionManager +{ + + private: + + JackFixedArray<CONNECTION_NUM> fConnection[PORT_NUM]; /*! Connection matrix: list of connected ports for a given port: needed to compute Mix buffer */ + JackFixedArray1<PORT_NUM_FOR_CLIENT> fInputPort[CLIENT_NUM]; /*! Table of input port per refnum : to find a refnum for a given port */ + JackFixedArray<PORT_NUM_FOR_CLIENT> fOutputPort[CLIENT_NUM]; /*! Table of output port per refnum : to find a refnum for a given port */ + JackFixedMatrix<CLIENT_NUM> fConnectionRef; /*! Table of port connections by (refnum , refnum) */ + JackActivationCount fInputCounter[CLIENT_NUM]; /*! Activation counter per refnum */ + JackLoopFeedback<CONNECTION_NUM> fLoopFeedback; /*! Loop feedback connections */ + + bool IsLoopPathAux(int ref1, int ref2) const; + void InitClient(int refnum); + + public: + + JackConnectionManager(); + virtual ~JackConnectionManager(); + + // Connections management + int Connect(jack_port_id_t port_src, jack_port_id_t port_dst); + int Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst); + bool IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const; + + jack_int_t Connections(jack_port_id_t port_index) const; + jack_port_id_t GetPort(jack_port_id_t port_index, int connection) const; + const jack_int_t* GetConnections(jack_port_id_t port_index) const; + + bool IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst); + bool DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst); + bool IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const; + + bool IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const; + void IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst); + void DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst); + + // Ports management + int AddInputPort(int refnum, jack_port_id_t port_index); + int AddOutputPort(int refnum, jack_port_id_t port_index); + + int RemoveInputPort(int refnum, jack_port_id_t port_index); + int RemoveOutputPort(int refnum, jack_port_id_t port_index); + + const jack_int_t* GetInputPorts(int refnum); + const jack_int_t* GetOutputPorts(int refnum); + + // Client management + int AllocateRefNum(); + void ReleaseRefNum(int refnum); + + int GetInputRefNum(jack_port_id_t port_index) const; + int GetOutputRefNum(jack_port_id_t port_index) const; + + // Connect/Disconnect 2 refnum "directly" + bool IsDirectConnection(int ref1, int ref2) const; + void DirectConnect(int ref1, int ref2); + void DirectDisconnect(int ref1, int ref2); + + int GetActivation(int refnum) const; + + // Graph + void ResetGraph(JackClientTiming* timing); + int ResumeRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing); + int SuspendRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing, long time_out_usec); + +}; + +} // end of namespace + +#endif + diff --git a/common/JackConstants.h b/common/JackConstants.h new file mode 100644 index 00000000..917f692b --- /dev/null +++ b/common/JackConstants.h @@ -0,0 +1,64 @@ +/* + Copyright (C) 2004-2006 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#define PRINTDEBUG + +#define VERSION "0.58" + +#define FORK_SERVER 1 + +#define BUFFER_SIZE_MAX 8192 +#define SAMPLE_RATE 44100 + +#define JACK_PORT_NAME_SIZE 256 +#define JACK_PORT_TYPE_SIZE 32 + +#define JACK_CLIENT_NAME_SIZE 64 + +#define PORT_NUM 512 +#define PORT_NUM_FOR_CLIENT 256 + +#define CONNECTION_NUM 256 + +#define CLIENT_NUM 64 + +#define AUDIO_DRIVER_REFNUM 0 // Audio driver is initialized first, it will get the refnum 0 +#define FREEWHEEL_DRIVER_REFNUM 1 // Freewheel driver is initialized second, it will get the refnum 1 +#define LOOPBACK_DRIVER_REFNUM 2 // Loopback driver is initialized third, it will get the refnum 2 +#define REAL_REFNUM LOOPBACK_DRIVER_REFNUM + 1 // Real clients start at LOOPBACK_DRIVER_REFNUM + 1 + +#define SOCKET_TIME_OUT 5 + +#ifdef WIN32 + #define jack_server_dir "server" + #define jack_client_dir "client" +#else + #define jack_server_dir "/dev/shm" + #define jack_client_dir "/dev/shm" +#endif + +/* +#define jack_server_dir "/mnt/ramfs" +#define jack_client_dir "/mnt/ramfs" +*/ + +#define jack_server_entry "jackdmp_entry" +#define jack_client_entry "jack_client" + +#define ALL_CLIENTS -1 // for notification diff --git a/common/JackDebugClient.cpp b/common/JackDebugClient.cpp new file mode 100644 index 00000000..be066e40 --- /dev/null +++ b/common/JackDebugClient.cpp @@ -0,0 +1,457 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackDebugClient.h" +#include "JackError.h" +#include <iostream> +#include <iomanip> +#include <sstream> +#include <fstream> +#include <string> +#include <time.h> + +using namespace std; + +namespace Jack +{ + +JackDebugClient::JackDebugClient(JackClient * client) +{ + fTotalPortNumber = 1; // The total number of port opened and maybe closed. Historical view. + fOpenPortNumber = 0; // The current number of opened port. + fIsActivated = 0; + fIsDeactivated = 0; + fIsClosed = 0; + fClient = client; +} + +JackDebugClient::~JackDebugClient() +{ + fTotalPortNumber--; // fTotalPortNumber start at 1 + *fStream << endl << endl << "----------------------------------- JackDebugClient summary ------------------------------- " << endl << endl; + *fStream << "Client flags ( 1:yes / 0:no ) :" << endl; + *fStream << setw(5) << "- Client call activated : " << fIsActivated << endl; + *fStream << setw(5) << "- Client call deactivated : " << fIsDeactivated << endl; + *fStream << setw(5) << "- Client call closed : " << fIsClosed << endl; + *fStream << setw(5) << "- Total number of instantiated port : " << fTotalPortNumber << endl; + *fStream << setw(5) << "- Number of port remaining open when exiting client : " << fOpenPortNumber << endl; + if (fOpenPortNumber != 0) + *fStream << "!!! WARNING !!! Some ports have not been unregistrated ! Incorrect exiting !" << endl; + if (fIsDeactivated != fIsActivated) + *fStream << "!!! ERROR !!! Client seem do not perform symetric activation-deactivation ! (not the same number of activate and deactivate)" << endl; + if (fIsClosed == 0) + *fStream << "!!! ERROR !!! Client have not been closed with jack_client_close() !" << endl; + + *fStream << endl << endl << "---------------------------- JackDebugClient detailed port summary ------------------------ " << endl << endl; + //for (int i = 0; i < fTotalPortNumber ; i++) { + for (int i = 1; i <= fTotalPortNumber ; i++) { + *fStream << endl << "Port index (internal debug test value) : " << i << endl; + *fStream << setw(5) << "- Name : " << fPortList[i].name << endl; + *fStream << setw(5) << "- idport : " << fPortList[i].idport << endl; + *fStream << setw(5) << "- IsConnected : " << fPortList[i].IsConnected << endl; + *fStream << setw(5) << "- IsUnregistrated : " << fPortList[i].IsUnregistrated << endl; + if (fPortList[i].IsUnregistrated == 0) + *fStream << "!!! WARNING !!! Port have not been unregistrated ! Incorrect exiting !" << endl; + } + *fStream << "delete object JackDebugClient : end of tracing" << endl; + delete fStream; + delete fClient; +} + +int JackDebugClient::Open(const char* name) +{ + int Tidport; + Tidport = fClient->Open(name); + char provstr[256]; + char buffer[256]; + time_t curtime; + struct tm *loctime; + /* Get the current time. */ + curtime = time (NULL); + /* Convert it to local time representation. */ + loctime = localtime (&curtime); + strftime (buffer, 256, "%I-%M", loctime); + sprintf(provstr, "JackClientDebug-%s-%s.log", name, buffer); + fStream = new ofstream(provstr, ios_base::ate); + if (fStream->is_open()) { + if (Tidport == -1) { + *fStream << "Trying to Open Client with name '" << name << "' with bad result (client not opened)." << Tidport << endl; + } else { + *fStream << "Open Client with name '" << name << "'." << endl; + } + } else { + JackLog("JackDebugClient::Open : cannot open log file\n"); + } + strcpy(fClientName, name); + return Tidport; +} + +int JackDebugClient::Close() +{ + fIsClosed++; + *fStream << "Client '" << fClientName << "' was Closed" << endl; + return fClient->Close(); +} + +pthread_t JackDebugClient::GetThreadID() +{ + return fClient->GetThreadID(); +} + +JackGraphManager* JackDebugClient::GetGraphManager() const +{ + return fClient->GetGraphManager(); +} +JackEngineControl* JackDebugClient::GetEngineControl() const +{ + return fClient->GetEngineControl(); +} +/*! +\brief Notification received from the server. +*/ + +int JackDebugClient::ClientNotify(int refnum, const char* name, int notify, int sync, int value) +{ + return fClient->ClientNotify( refnum, name, notify, sync, value); +} + +int JackDebugClient::Activate() +{ + int Tidport; + Tidport = fClient->Activate(); + fIsActivated++; + if (fIsDeactivated) + *fStream << "Client '" << fClientName << "' call activate a new time (it already call 'activate' previously)." << endl; + *fStream << "Client '" << fClientName << "' Activated" << endl; + if (Tidport != 0) + *fStream << "Client '" << fClientName << "' try to activate but server return " << Tidport << " ." << endl; + return Tidport; +} + +int JackDebugClient::Deactivate() +{ + int Tidport; + Tidport = fClient->Deactivate(); + fIsDeactivated++; + if (fIsActivated == 0) + *fStream << "Client '" << fClientName << "' deactivate while it hasn't been previoulsy activated !" << endl; + *fStream << "Client '" << fClientName << "' Deactivated" << endl; + if (Tidport != 0) + *fStream << "Client '" << fClientName << "' try to deactivate but server return " << Tidport << " ." << endl; + return Tidport; +} + +//----------------- +// Port management +//----------------- + +int JackDebugClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) +{ + int Tidport; + Tidport = fClient->PortRegister(port_name, port_type, flags, buffer_size); + if (Tidport <= 0) { + *fStream << "Client '" << fClientName << "' try port Register ('" << port_name << "') and server return error " << Tidport << " ." << endl; + } else { + if (fTotalPortNumber < MAX_PORT_HISTORY) { + fPortList[fTotalPortNumber].idport = Tidport; + strcpy(fPortList[fTotalPortNumber].name, port_name); + fPortList[fTotalPortNumber].IsConnected = 0; + fPortList[fTotalPortNumber].IsUnregistrated = 0; + } else { + *fStream << "!!! WARNING !!! History is full : no more port history will be recorded." << endl; + } + fTotalPortNumber++; + fOpenPortNumber++; + *fStream << "Client '" << fClientName << "' port Register with portname '" << port_name << " port " << Tidport << "' ." << endl; + } + return Tidport; +} + +int JackDebugClient::PortUnRegister(jack_port_id_t port_index) +{ + int Tidport; + Tidport = fClient->PortUnRegister(port_index); + fOpenPortNumber--; + int i; + for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history + if (fPortList[i].idport == port_index) { // We found the last record + if (fPortList[i].IsUnregistrated != 0) + *fStream << "!!! ERROR !!! : '" << fClientName << "' id deregistering port '" << fPortList[i].name << "' that have already been unregistered !" << endl; + fPortList[i].IsUnregistrated++; + break; + } + } + if (i == 0) // Port is not found + *fStream << "JackClientDebug : PortUnregister : port " << port_index << " was not previously registered !" << endl; + if (Tidport != 0) + *fStream << "Client '" << fClientName << "' try to do PortUnregister and server return " << Tidport << " )." << endl; + *fStream << "Client '" << fClientName << "' unregister port '" << port_index << "'." << endl; + return Tidport; +} + +int JackDebugClient::PortConnect(const char* src, const char* dst) +{ + if (!(fIsActivated)) + *fStream << "!!! ERROR !!! Trying to connect a port ( " << src << " to " << dst << ") while the client has not been activated !" << endl; + int Tidport; + int i; + Tidport = fClient->PortConnect( src, dst); + for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history + if (strcmp(fPortList[i].name, src) == 0) { // We found the last record in sources + if (fPortList[i].IsUnregistrated != 0) + *fStream << "!!! ERROR !!! Connecting port " << src << " previoulsy unregistered !" << endl; + fPortList[i].IsConnected++; + *fStream << "Connecting port " << src << " to " << dst << ". "; + break; + } else if (strcmp(fPortList[i].name, dst) == 0 ) { // We found the record in dest + if (fPortList[i].IsUnregistrated != 0) + *fStream << "!!! ERROR !!! Connecting port " << dst << " previoulsy unregistered !" << endl; + fPortList[i].IsConnected++; + *fStream << "Connecting port " << src << " to " << dst << ". "; + break; + } + } + if (i == 0) // Port is not found + *fStream << "JackClientDebug : PortConnect : port was not found in debug database !" << endl; + if (Tidport != 0) + *fStream << "Client '" << fClientName << "' try to do PortConnect but server return " << Tidport << " ." << endl; + //*fStream << "Client Port Connect done with names" << endl; + return Tidport; +} + +int JackDebugClient::PortDisconnect(const char* src, const char* dst) +{ + if (!(fIsActivated)) + *fStream << "!!! ERROR !!! Trying to disconnect a port ( " << src << " to " << dst << ") while the client has not been activated !" << endl; + int Tidport; + Tidport = fClient->PortDisconnect( src, dst); + int i; + for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history + if (strcmp(fPortList[i].name, src) == 0) { // We found the record in sources + if (fPortList[i].IsUnregistrated != 0) + *fStream << "!!! ERROR !!! : Disconnecting port " << src << " previoulsy unregistered !" << endl; + fPortList[i].IsConnected--; + *fStream << "disconnecting port " << src << ". "; + break; + } else if (strcmp(fPortList[i].name, dst) == 0 ) { // We found the record in dest + if (fPortList[i].IsUnregistrated != 0) + *fStream << "!!! ERROR !!! : Disonnecting port " << dst << " previoulsy unregistered !" << endl; + fPortList[i].IsConnected--; + *fStream << "disconnecting port " << dst << ". "; + break; + } + } + if (i == 0) // Port is not found + *fStream << "JackClientDebug : PortDisConnect : port was not found in debug database !" << endl; + if (Tidport != 0) + *fStream << "Client '" << fClientName << "' try to do PortDisconnect but server return " << Tidport << " ." << endl; + //*fStream << "Client Port Disconnect done." << endl; + return Tidport; +} + +int JackDebugClient::PortConnect(jack_port_id_t src, jack_port_id_t dst) +{ + if (!(fIsActivated)) + *fStream << "!!! ERROR !!! : Trying to connect port " << src << " to " << dst << " while the client has not been activated !" << endl; + int Tidport; + Tidport = fClient->PortConnect(src, dst); + int i; + for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history + if (fPortList[i].idport == src) { // We found the record in sources + if (fPortList[i].IsUnregistrated != 0) + *fStream << "!!! ERROR !!! : Connecting port " << src << " previoulsy unregistered !" << endl; + fPortList[i].IsConnected++; + *fStream << "Connecting port " << src << ". "; + break; + } else if (fPortList[i].idport == dst) { // We found the record in dest + if (fPortList[i].IsUnregistrated != 0) + *fStream << "!!! ERROR !!! : Connecting port " << dst << " previoulsy unregistered !" << endl; + fPortList[i].IsConnected++; + *fStream << "Connecting port " << dst << ". "; + break; + } + } + if (i == 0) // Port is not found + *fStream << "JackClientDebug : PortConnect : port was not found in debug database !" << endl; + if (Tidport == -1) + *fStream << "Client '" << fClientName << "' try to do Portconnect but server return " << Tidport << " ." << endl; + //*fStream << "Client Port Connect with ID done." << endl; + return Tidport; +} + +int JackDebugClient::PortDisconnect(jack_port_id_t src) +{ + if (!(fIsActivated)) + *fStream << "!!! ERROR !!! : Trying to disconnect port " << src << " while that client has not been activated !" << endl; + int Tidport; + Tidport = fClient->PortDisconnect(src); + int i; + for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history + if (fPortList[i].idport == src) { // We found the record in sources + if (fPortList[i].IsUnregistrated != 0) + *fStream << "!!! ERROR !!! : Disconnecting port " << src << " previoulsy unregistered !" << endl; + fPortList[i].IsConnected--; + *fStream << "Disconnecting port " << src << ". " << endl; + break; + } + } + if (i == 0) // Port is not found + *fStream << "JackClientDebug : PortDisconnect : port was not found in debug database !" << endl; + if (Tidport != 0) + *fStream << "Client '" << fClientName << "' try to do PortDisconnect but server return " << Tidport << " ." << endl; + //*fStream << "Client Port Disconnect with ID done." << endl; + return Tidport; +} + +int JackDebugClient::PortIsMine(jack_port_id_t port_index) +{ + return fClient->PortIsMine(port_index); +} + +//-------------------- +// Context management +//-------------------- + +int JackDebugClient::SetBufferSize(jack_nframes_t nframes) +{ + return fClient->SetBufferSize(nframes); +} + +int JackDebugClient::SetFreeWheel(int onoff) +{ + return fClient->SetFreeWheel(onoff); +} + +/* +ShutDown is called: +- from the RT thread when Execute method fails +- possibly from a "closed" notification channel +(Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown)) +*/ + +void JackDebugClient::ShutDown() +{ + fClient->ShutDown(); +} + +//--------------------- +// Transport management +//--------------------- + +int JackDebugClient::ReleaseTimebase() +{ + return fClient->ReleaseTimebase(); +} + +int JackDebugClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg) +{ + return fClient->SetSyncCallback(sync_callback, arg); +} + +int JackDebugClient::SetSyncTimeout(jack_time_t timeout) +{ + return fClient->SetSyncTimeout(timeout); +} + +int JackDebugClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg) +{ + return fClient->SetTimebaseCallback( conditional, timebase_callback, arg); +} + +int JackDebugClient::TransportLocate(jack_nframes_t frame) +{ + return fClient->TransportLocate(frame); +} + +jack_transport_state_t JackDebugClient::TransportQuery(jack_position_t* pos) +{ + return fClient->TransportQuery(pos); +} + +jack_nframes_t JackDebugClient::GetCurrentTransportFrame() +{ + return fClient->GetCurrentTransportFrame(); +} + +int JackDebugClient::TransportReposition(jack_position_t* pos) +{ + return fClient->TransportReposition(pos); +} + +void JackDebugClient::TransportStart() +{ + fClient->TransportStart(); +} + +void JackDebugClient::TransportStop() +{ + fClient->TransportStop(); +} + +//--------------------- +// Callback management +//--------------------- + +void JackDebugClient::OnShutdown(JackShutdownCallback callback, void *arg) +{ + fClient->OnShutdown(callback, arg); +} + +int JackDebugClient::SetProcessCallback(JackProcessCallback callback, void *arg) +{ + return fClient->SetProcessCallback( callback, arg); +} + +int JackDebugClient::SetXRunCallback(JackXRunCallback callback, void *arg) +{ + return fClient->SetXRunCallback(callback, arg); +} + +int JackDebugClient::SetInitCallback(JackThreadInitCallback callback, void *arg) +{ + return fClient->SetInitCallback(callback, arg); +} + +int JackDebugClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg) +{ + return fClient->SetGraphOrderCallback(callback, arg); +} + +int JackDebugClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg) +{ + return fClient->SetBufferSizeCallback(callback, arg); +} + +int JackDebugClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg) +{ + return fClient->SetFreewheelCallback(callback, arg); +} + +int JackDebugClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg) +{ + return fClient->SetPortRegistrationCallback(callback, arg); +} + +JackClientControl* JackDebugClient::GetClientControl() const +{ + return fClient->GetClientControl(); +} + +} // end of namespace + diff --git a/common/JackDebugClient.h b/common/JackDebugClient.h new file mode 100644 index 00000000..9a2c1094 --- /dev/null +++ b/common/JackDebugClient.h @@ -0,0 +1,127 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackDebugClient__ +#define __JackDebugClient__ + +#define MAX_PORT_HISTORY 2048 + +#include "JackClient.h" +#include <list> + +namespace Jack +{ + +/*! +\brief Follow a single port. +*/ + +typedef struct +{ + jack_port_id_t idport; + char name[JACK_PORT_NAME_SIZE]; //portname + int IsConnected; + int IsUnregistrated; +} +PortFollower; + +/*! +\brief A "decorator" debug client to validate API use. +*/ + +class JackDebugClient : public JackClient +{ + private: + + JackClient* fClient; + std::ofstream* fStream; + + protected: + PortFollower fPortList[MAX_PORT_HISTORY]; // Arbitrary value... To be tuned... + int fTotalPortNumber; // The total number of port opened and maybe closed. Historical view. + int fOpenPortNumber; // The current number of opened port. + int fIsActivated; + int fIsDeactivated; + int fIsClosed; + char fClientName[JACK_CLIENT_NAME_SIZE]; + + public: + + JackDebugClient(JackClient *fTheClient); + + virtual ~JackDebugClient(); + + virtual int Open(const char* name); + int Close(); + + virtual JackGraphManager* GetGraphManager() const; + virtual JackEngineControl* GetEngineControl() const; + + // Notifications + int ClientNotify(int refnum, const char* name, int notify, int sync, int value); + + int Activate(); + int Deactivate(); + + // Context + int SetBufferSize(jack_nframes_t nframes); + int SetFreeWheel(int onoff); + void ShutDown(); + pthread_t GetThreadID(); + + // Port management + int PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); + int PortUnRegister(jack_port_id_t port); + + int PortConnect(const char* src, const char* dst); + int PortDisconnect(const char* src, const char* dst); + int PortConnect(jack_port_id_t src, jack_port_id_t dst); + int PortDisconnect(jack_port_id_t src); + + int PortIsMine(jack_port_id_t port_index); + + // Transport + int ReleaseTimebase(); + int SetSyncCallback(JackSyncCallback sync_callback, void* arg); + int SetSyncTimeout(jack_time_t timeout); + int SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg); + int TransportLocate(jack_nframes_t frame); + jack_transport_state_t TransportQuery(jack_position_t* pos); + jack_nframes_t GetCurrentTransportFrame(); + int TransportReposition(jack_position_t* pos); + void TransportStart(); + void TransportStop(); + + // Callbacks + void OnShutdown(JackShutdownCallback callback, void *arg); + int SetProcessCallback(JackProcessCallback callback, void* arg); + int SetXRunCallback(JackXRunCallback callback, void* arg); + int SetInitCallback(JackThreadInitCallback callback, void* arg); + int SetGraphOrderCallback(JackGraphOrderCallback callback, void* arg); + int SetBufferSizeCallback(JackBufferSizeCallback callback, void* arg); + int SetFreewheelCallback(JackFreewheelCallback callback, void* arg); + int SetPortRegistrationCallback(JackPortRegistrationCallback callback, void* arg); + JackClientControl* GetClientControl() const; + +}; + + +} // end of namespace + +#endif diff --git a/common/JackDriver.cpp b/common/JackDriver.cpp new file mode 100644 index 00000000..6e3a67c3 --- /dev/null +++ b/common/JackDriver.cpp @@ -0,0 +1,204 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackDriver.h" +#include "JackTime.h" +#include "JackError.h" +#include "JackPort.h" +#include "JackGraphManager.h" +#include "JackGlobals.h" +#include "JackEngineControl.h" +#include "JackClientControl.h" +#include "JackEngine.h" +#include <math.h> +#include <assert.h> + +using namespace std; + +namespace Jack +{ + +JackDriver::JackDriver(const char* name, JackEngine* engine, JackSynchro** table) +{ + assert(strlen(name) < JACK_CLIENT_NAME_SIZE); + fSynchroTable = table; + fClientControl = new JackClientControl(name); + fEngine = engine; + fGraphManager = NULL; + fLastWaitUst = 0; + fIsMaster = true; +} + +JackDriver::JackDriver() +{ + fSynchroTable = NULL; + fClientControl = NULL; + fEngine = NULL; + fGraphManager = NULL; + fLastWaitUst = 0; + fIsMaster = true; +} + +JackDriver::~JackDriver() +{ + JackLog("~JackDriver\n"); + delete fClientControl; +} + +int JackDriver::Open() +{ + int refnum = -1; + + if (fEngine->ClientCheckName(fClientControl->fName)) { + jack_error("client %s already registered", fClientControl->fName); + return -1; + } + + if (fEngine->ClientInternalNew(fClientControl->fName, &refnum, &fEngineControl, &fGraphManager, this) != 0) { + jack_error("Cannot allocate internal client for audio driver"); + return -1; + } + + fClientControl->fRefNum = refnum; + fClientControl->fActive = true; + fGraphManager->DirectConnect(fClientControl->fRefNum, fClientControl->fRefNum); // Connect driver to itself for sync + + /* + In ASYNC mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations. + The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations. + */ + if (!fEngineControl->fSyncMode) { + fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(true); + fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(true); + fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(true); + } + return 0; +} + +int JackDriver::Open(jack_nframes_t nframes, + jack_nframes_t samplerate, + int capturing, + int playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) +{ + JackLog("JackDriver::Open capture_driver_name = %s\n", capture_driver_name); + JackLog("JackDriver::Open playback_driver_name = %s\n", playback_driver_name); + int refnum = -1; + + if (fEngine->ClientCheckName(fClientControl->fName)) { + jack_error("client %s already registered", fClientControl->fName); + return -1; + } + + if (fEngine->ClientInternalNew(fClientControl->fName, &refnum, &fEngineControl, &fGraphManager, this) != 0) { + jack_error("Cannot allocate internal client for audio driver"); + return -1; + } + + fClientControl->fRefNum = refnum; + fClientControl->fActive = true; + fEngineControl->fBufferSize = nframes; + fEngineControl->fSampleRate = samplerate; + fCaptureLatency = capture_latency; + fPlaybackLatency = playback_latency; + + assert(strlen(capture_driver_name) < JACK_CLIENT_NAME_SIZE); + assert(strlen(playback_driver_name) < JACK_CLIENT_NAME_SIZE); + + strcpy(fCaptureDriverName, capture_driver_name); + strcpy(fPlaybackDriverName, playback_driver_name); + + fEngineControl->fPeriodUsecs = (jack_time_t)floor((((float)nframes) / (float)samplerate) * 1000000.0f); + if (fEngineControl->fTimeOutUsecs == 0) /* usecs; if zero, use 2 period size. */ + fEngineControl->fTimeOutUsecs = (jack_time_t)(2.f * fEngineControl->fPeriodUsecs); + + fGraphManager->DirectConnect(fClientControl->fRefNum, fClientControl->fRefNum); // Connect driver to itself for sync + + /* + In ASYNC mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations. + The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations. + */ + if (!fEngineControl->fSyncMode) { + fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(true); + fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(true); + fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(true); + } + return 0; +} + +int JackDriver::Close() +{ + JackLog("JackDriver::Close\n"); + fGraphManager->DirectDisconnect(fClientControl->fRefNum, fClientControl->fRefNum); // Disconnect driver from itself for sync + fClientControl->fActive = false; + return fEngine->ClientInternalCloseIm(fClientControl->fRefNum); +} + +bool JackDriver::IsRealTime() +{ + return fEngineControl->fRealTime; +} + +JackClientControl* JackDriver::GetClientControl() const +{ + return fClientControl; +} + +void JackDriver::NotifyXRun(jack_time_t callback_usecs) +{ + fEngine->NotifyXRun(callback_usecs); +} + +void JackDriverClient::SetMaster(bool onoff) +{ + fIsMaster = onoff; +} + +bool JackDriverClient::GetMaster() +{ + return fIsMaster; +} + +void JackDriverClient::AddSlave(JackDriverInterface* slave) +{ + fSlaveList.push_back(slave); +} + +void JackDriverClient::RemoveSlave(JackDriverInterface* slave) +{ + fSlaveList.remove(slave); +} + +void JackDriverClient::ProcessSlaves() +{ + list<JackDriverInterface*>::const_iterator it; + for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { + JackDriverInterface* slave = *it; + slave->Process(); + } +} + +} // end of namespace diff --git a/common/JackDriver.h b/common/JackDriver.h new file mode 100644 index 00000000..89427697 --- /dev/null +++ b/common/JackDriver.h @@ -0,0 +1,215 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackDriver__ +#define __JackDriver__ + +#include "types.h" +#include "JackClientInterface.h" +#include "JackConstants.h" +#include <list> + +namespace Jack +{ + +class JackEngine; +class JackGraphManager; +class JackSynchro; +struct JackEngineControl; +struct JackClientControl; + +/*! +\brief The base interface for drivers. +*/ + +class EXPORT JackDriverInterface +{ + + public: + + JackDriverInterface() + {} + virtual ~JackDriverInterface() + {} + + virtual int Open() = 0; + + virtual int Open(jack_nframes_t nframes, + jack_nframes_t samplerate, + int capturing, + int playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) = 0; + + virtual int Attach() = 0; + virtual int Detach() = 0; + + virtual int Read() = 0; + virtual int Write() = 0; + virtual int Start() = 0; + virtual int Stop() = 0; + virtual int SetBufferSize(jack_nframes_t nframes) = 0; + + virtual int Process() = 0; + + virtual void SetMaster(bool onoff) = 0; + virtual bool GetMaster() = 0; + virtual void AddSlave(JackDriverInterface* slave) = 0; + virtual void RemoveSlave(JackDriverInterface* slave) = 0; + virtual void ProcessSlaves() = 0; + + virtual bool IsRealTime() = 0; + + virtual void PrintState() = 0; +}; + +/*! +\brief The base interface for drivers clients. +*/ + +class EXPORT JackDriverClientInterface : public JackDriverInterface, public JackClientInterface +{}; + +/*! +\brief The base class for drivers clients. +*/ + +class EXPORT JackDriverClient : public JackDriverClientInterface +{ + private: + + std::list<JackDriverInterface*> fSlaveList; + + protected: + + bool fIsMaster; + + public: + + virtual void SetMaster(bool onoff); + virtual bool GetMaster(); + virtual void AddSlave(JackDriverInterface* slave); + virtual void RemoveSlave(JackDriverInterface* slave); + virtual void ProcessSlaves(); +}; + +/*! +\brief The base class for drivers. +*/ + +class EXPORT JackDriver : public JackDriverClient +{ + + protected: + + char fCaptureDriverName[JACK_CLIENT_NAME_SIZE]; + char fPlaybackDriverName[JACK_CLIENT_NAME_SIZE]; + jack_nframes_t fCaptureLatency; + jack_nframes_t fPlaybackLatency; + jack_time_t fLastWaitUst; + JackEngine* fEngine; + JackGraphManager* fGraphManager; + JackSynchro** fSynchroTable; + JackEngineControl* fEngineControl; + JackClientControl* fClientControl; + + JackClientControl* GetClientControl() const; + + public: + + JackDriver(const char* name, JackEngine* engine, JackSynchro** table); + JackDriver(); + virtual ~JackDriver(); + + virtual int Open(); + + virtual int Open(jack_nframes_t nframes, + jack_nframes_t samplerate, + int capturing, + int playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency); + + virtual int Close(); + + virtual int Process() + { + return 0; + } + + virtual int Attach() + { + return 0; + } + virtual int Detach() + { + return 0; + } + + virtual int Read() + { + return 0; + } + virtual int Write() + { + return 0; + } + + virtual int Start() + { + return 0; + } + virtual int Stop() + { + return 0; + } + + virtual int SetBufferSize(jack_nframes_t nframes) + { + return 0; + } + + void NotifyXRun(jack_time_t callback_usecs); // XRun notification sent by the driver + + virtual bool IsRealTime(); + + virtual void PrintState() + {} + + int ClientNotify(int refnum, const char* name, int notify, int sync, int value) + { + return 0; + } + +}; + +} // end of namespace + +#endif diff --git a/common/JackDriverLoader.cpp b/common/JackDriverLoader.cpp new file mode 100644 index 00000000..359abe79 --- /dev/null +++ b/common/JackDriverLoader.cpp @@ -0,0 +1,498 @@ +/* +Copyright (C) 2001-2005 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackDriverLoader.h" +#include "JackError.h" +#include <getopt.h> + +#ifdef WIN32 +#define ADDON_DIR "jackmp" // TO IMPROVE +#else +#include <dirent.h> +#define ADDON_DIR "/usr/local/lib/jackmp" // TO IMPROVE +#endif + +static void +jack_print_driver_options (jack_driver_desc_t * desc, FILE *file) +{ + unsigned long i; + char arg_default[JACK_DRIVER_PARAM_STRING_MAX + 1]; + + for (i = 0; i < desc->nparams; i++) { + switch (desc->params[i].type) { + case JackDriverParamInt: + //sprintf (arg_default, "%" PRIi32, desc->params[i].value.i); + sprintf (arg_default, "%" "i", desc->params[i].value.i); + break; + case JackDriverParamUInt: + //sprintf (arg_default, "%" PRIu32, desc->params[i].value.ui); + sprintf (arg_default, "%" "u", desc->params[i].value.ui); + break; + case JackDriverParamChar: + sprintf (arg_default, "%c", desc->params[i].value.c); + break; + case JackDriverParamString: + if (desc->params[i].value.str && + strcmp (desc->params[i].value.str, "") != 0) + sprintf (arg_default, "%s", desc->params[i].value.str); + else + sprintf (arg_default, "none"); + break; + case JackDriverParamBool: + sprintf (arg_default, "%s", desc->params[i].value.i ? "true" : "false"); + break; + } + + fprintf (file, "\t-%c, --%s \t%s (default: %s)\n", + desc->params[i].character, + desc->params[i].name, + desc->params[i].short_desc, + arg_default); + } +} + +static void +jack_print_driver_param_usage (jack_driver_desc_t * desc, unsigned long param, FILE *file) +{ + fprintf (file, "Usage information for the '%s' parameter for driver '%s':\n", + desc->params[param].name, desc->name); + + fprintf (file, "%s\n", desc->params[param].long_desc); +} + +EXPORT int +jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSList ** param_ptr) +{ + struct option * long_options; + char * options, * options_ptr; + unsigned long i; + int opt; + unsigned int param_index; + JSList * params = NULL; + jack_driver_param_t * driver_param; + + if (argc <= 1) { + *param_ptr = NULL; + return 0; + } + + /* check for help */ + if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) { + if (argc > 2) { + for (i = 0; i < desc->nparams; i++) { + if (strcmp (desc->params[i].name, argv[2]) == 0) { + jack_print_driver_param_usage (desc, i, stdout); + return 1; + } + } + + fprintf (stderr, "jackd: unknown option '%s' " + "for driver '%s'\n", argv[2], + desc->name); + } + + printf ("Parameters for driver '%s' (all parameters are optional):\n", desc->name); + jack_print_driver_options (desc, stdout); + return 1; + } + + /* set up the stuff for getopt */ + options = (char*)calloc (desc->nparams * 3 + 1, sizeof (char)); + long_options = (option*)calloc (desc->nparams + 1, sizeof (struct option)); + + options_ptr = options; + for (i = 0; i < desc->nparams; i++) { + sprintf (options_ptr, "%c::", desc->params[i].character); + options_ptr += 3; + + long_options[i].name = desc->params[i].name; + long_options[i].flag = NULL; + long_options[i].val = desc->params[i].character; + long_options[i].has_arg = optional_argument; + } + + /* create the params */ + optind = 0; + opterr = 0; + while ((opt = getopt_long(argc, argv, options, long_options, NULL)) != -1) { + + if (opt == ':' || opt == '?') { + if (opt == ':') { + fprintf (stderr, "Missing option to argument '%c'\n", optopt); + } else { + fprintf (stderr, "Unknownage with option '%c'\n", optopt); + } + + fprintf (stderr, "Options for driver '%s':\n", desc->name); + jack_print_driver_options (desc, stderr); + exit (1); + } + + for (param_index = 0; param_index < desc->nparams; param_index++) { + if (opt == desc->params[param_index].character) { + break; + } + } + + driver_param = (jack_driver_param_t*)calloc (1, sizeof (jack_driver_param_t)); + driver_param->character = desc->params[param_index].character; + + if (!optarg && optind < argc && + strlen(argv[optind]) && + argv[optind][0] != '-') { + optarg = argv[optind]; + } + + if (optarg) { + switch (desc->params[param_index].type) { + case JackDriverParamInt: + driver_param->value.i = atoi (optarg); + break; + case JackDriverParamUInt: + driver_param->value.ui = strtoul (optarg, NULL, 10); + break; + case JackDriverParamChar: + driver_param->value.c = optarg[0]; + break; + case JackDriverParamString: + strncpy (driver_param->value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX); + break; + case JackDriverParamBool: + + /* + if (strcasecmp ("false", optarg) == 0 || + strcasecmp ("off", optarg) == 0 || + strcasecmp ("no", optarg) == 0 || + strcasecmp ("0", optarg) == 0 || + strcasecmp ("(null)", optarg) == 0 ) { + */ + // steph + if (strcmp ("false", optarg) == 0 || + strcmp ("off", optarg) == 0 || + strcmp ("no", optarg) == 0 || + strcmp ("0", optarg) == 0 || + strcmp ("(null)", optarg) == 0 ) { + driver_param->value.i = false; + + } else { + + driver_param->value.i = true; + + } + break; + } + } else { + if (desc->params[param_index].type == JackDriverParamBool) { + driver_param->value.i = true; + } else { + driver_param->value = desc->params[param_index].value; + } + } + + params = jack_slist_append (params, driver_param); + } + + free (options); + free (long_options); + + if (param_ptr) + *param_ptr = params; + + return 0; +} + +EXPORT jack_driver_desc_t * +jack_find_driver_descriptor (JSList * drivers, const char * name) +{ + jack_driver_desc_t * desc = 0; + JSList * node; + + for (node = drivers; node; node = jack_slist_next (node)) { + desc = (jack_driver_desc_t *) node->data; + + if (strcmp (desc->name, name) != 0) { + desc = NULL; + } else { + break; + } + } + + return desc; +} + +jack_driver_desc_t * +jack_drivers_get_descriptor (JSList * drivers, const char * sofile) +{ + jack_driver_desc_t * descriptor, * other_descriptor; + JackDriverDescFunction so_get_descriptor = NULL; + JSList * node; + void * dlhandle; + char * filename; +#ifdef WIN32 + int dlerr; +#else + const char * dlerr; +#endif + + int err; + char* driver_dir; + + if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { + driver_dir = ADDON_DIR; + } + +#ifdef WIN32 + if (strcmp(ADDON_DIR, "") == 0) { + char temp_driver_dir1[512]; + char temp_driver_dir2[512]; + GetCurrentDirectory(512, temp_driver_dir1); + sprintf (temp_driver_dir2, "%s/%s", temp_driver_dir1, ADDON_DIR); + driver_dir = temp_driver_dir2; + } +#endif + + filename = (char *)malloc (strlen (driver_dir) + 1 + strlen (sofile) + 1); + sprintf (filename, "%s/%s", driver_dir, sofile); + + if ((dlhandle = LoadDriverModule (filename)) == NULL) { +#ifdef WIN32 + jack_error ("could not open driver .dll '%s': %ld\n", filename, GetLastError()); +#else + jack_error ("could not open driver .so '%s': %s\n", filename, dlerror ()); +#endif + + free (filename); + return NULL; + } + + so_get_descriptor = (JackDriverDescFunction) + GetProc (dlhandle, "driver_get_descriptor"); + +#ifdef WIN32 + if ((so_get_descriptor == NULL) && (dlerr = GetLastError()) != 0) { + fprintf(stderr, "%ld\n", dlerr); +#else + if ((so_get_descriptor == NULL) && (dlerr = dlerror ()) != NULL) { + fprintf(stderr, "%s\n", dlerr); +#endif + + UnloadDriverModule (dlhandle); + free (filename); + return NULL; + } + + if ((descriptor = so_get_descriptor ()) == NULL) { + jack_error ("driver from '%s' returned NULL descriptor\n", filename); + UnloadDriverModule (dlhandle); + free (filename); + return NULL; + } + +#ifdef WIN32 + if ((err = UnloadDriverModule (dlhandle)) == 0) { + jack_error ("error closing driver .so '%s': %ld\n", filename, GetLastError ()); + } +#else + if ((err = UnloadDriverModule (dlhandle)) != 0) { + jack_error ("error closing driver .so '%s': %s\n", filename, dlerror ()); + } +#endif + + /* check it doesn't exist already */ + for (node = drivers; node; node = jack_slist_next (node)) { + other_descriptor = (jack_driver_desc_t *) node->data; + + if (strcmp (descriptor->name, other_descriptor->name) == 0) { + jack_error ("the drivers in '%s' and '%s' both have the name '%s'; using the first\n", + other_descriptor->file, filename, other_descriptor->name); + /* FIXME: delete the descriptor */ + free (filename); + return NULL; + } + } + + strncpy (descriptor->file, filename, PATH_MAX); + free (filename); + return descriptor; +} + +#ifdef WIN32 + +EXPORT JSList * +jack_drivers_load (JSList * drivers) { + char driver_dir[512]; + char dll_filename[512]; + WIN32_FIND_DATA filedata; + HANDLE file; + const char * ptr = NULL; + JSList * driver_list = NULL; + jack_driver_desc_t * desc; + + GetCurrentDirectory(512, driver_dir); + + sprintf (dll_filename, "%s/%s", ADDON_DIR, "*.dll"); + file = (HANDLE )FindFirstFile(dll_filename, &filedata); + + if (file == INVALID_HANDLE_VALUE) { + printf("error\n"); + return NULL; + } + + do { + ptr = strrchr (filedata.cFileName, '.'); + if (!ptr) { + continue; + } + ptr++; + if (strncmp ("dll", ptr, 3) != 0) { + continue; + } + + desc = jack_drivers_get_descriptor (drivers, filedata.cFileName); + if (desc) { + driver_list = jack_slist_append (driver_list, desc); + } + + } while ((file = (HANDLE )FindNextFile(file, &filedata)) != 0); + + if (!driver_list) { + jack_error ("could not find any drivers in %s!\n", driver_dir); + return NULL; + } + + return driver_list; +} + +#else + +JSList * +jack_drivers_load (JSList * drivers) { + struct dirent * dir_entry; + DIR * dir_stream; + const char * ptr; + int err; + JSList * driver_list = NULL; + jack_driver_desc_t * desc; + char* driver_dir; + + if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { + driver_dir = ADDON_DIR; + } + + /* search through the driver_dir and add get descriptors + from the .so files in it */ + dir_stream = opendir (driver_dir); + if (!dir_stream) { + jack_error ("could not open driver directory %s: %s\n", + driver_dir, strerror (errno)); + return NULL; + } + + while ((dir_entry = readdir(dir_stream))) { + + /* check the filename is of the right format */ + if (strncmp ("jack_", dir_entry->d_name, 5) != 0) { + continue; + } + + ptr = strrchr (dir_entry->d_name, '.'); + if (!ptr) { + continue; + } + ptr++; + if (strncmp ("so", ptr, 2) != 0) { + continue; + } + + desc = jack_drivers_get_descriptor (drivers, dir_entry->d_name); + if (desc) { + driver_list = jack_slist_append (driver_list, desc); + } + } + + err = closedir (dir_stream); + if (err) { + jack_error ("error closing driver directory %s: %s\n", + driver_dir, strerror (errno)); + } + + if (!driver_list) { + jack_error ("could not find any drivers in %s!\n", driver_dir); + return NULL; + } + + return driver_list; +} + +#endif + +jack_driver_info_t * +jack_load_driver (jack_driver_desc_t * driver_desc) { +#ifdef WIN32 + int errstr; +#else + const char * errstr; +#endif + + jack_driver_info_t *info; + + info = (jack_driver_info_t *) calloc (1, sizeof (*info)); + info->handle = LoadDriverModule (driver_desc->file); + + if (info->handle == NULL) { +#ifdef WIN32 + if ((errstr = GetLastError ()) != 0) { + jack_error ("can't load \"%s\": %ld", driver_desc->file, + errstr); +#else + if ((errstr = dlerror ()) != 0) { + jack_error ("can't load \"%s\": %s", driver_desc->file, + errstr); +#endif + + } else { + jack_error ("bizarre error loading driver shared " + "object %s", driver_desc->file); + } + goto fail; + } + + info->initialize = (initialize)GetProc(info->handle, "driver_initialize"); + +#ifdef WIN32 + if ((info->initialize == NULL) && (errstr = GetLastError ()) != 0) { +#else + if ((info->initialize == NULL) && (errstr = dlerror ()) != 0) { +#endif + jack_error ("no initialize function in shared object %s\n", + driver_desc->file); + goto fail; + } + + return info; + +fail: + if (info->handle) { + UnloadDriverModule(info->handle); + } + free (info); + return NULL; +} + diff --git a/common/JackDriverLoader.h b/common/JackDriverLoader.h new file mode 100644 index 00000000..9205d773 --- /dev/null +++ b/common/JackDriverLoader.h @@ -0,0 +1,66 @@ +/* +Copyright (C) 2001-2005 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackDriverLoader__ +#define __JackDriverLoader__ + +#include "jslist.h" +#include "driver_interface.h" +#include "JackDriver.h" + + +#ifdef WIN32 + +#include <windows.h> +#define DRIVER_HANDLE HINSTANCE +#define LoadDriverModule(name) LoadLibrary((name)) +#define UnloadDriverModule(handle) (FreeLibrary(((HMODULE)handle))) +#define GetProc(handle, name) GetProcAddress(((HMODULE)handle),(name)) + +#else + +#include <dlfcn.h> +#define DRIVER_HANDLE void* +#define LoadDriverModule(name) dlopen((name), RTLD_NOW | RTLD_GLOBAL) +#define UnloadDriverModule(handle) dlclose((handle)) +#define GetProc(handle, name) dlsym((handle), (name)) + +#endif + +typedef jack_driver_desc_t * (*JackDriverDescFunction) (); +typedef Jack::JackDriverClientInterface* (*initialize) (Jack::JackEngine*, Jack::JackSynchro**, const JSList *); + +typedef struct _jack_driver_info +{ + Jack::JackDriverClientInterface* (*initialize)(Jack::JackEngine*, Jack::JackSynchro**, const JSList *); + DRIVER_HANDLE handle; +} +jack_driver_info_t; + +EXPORT jack_driver_desc_t * jack_find_driver_descriptor (JSList * drivers, const char * name); + +jack_driver_desc_t * jack_drivers_get_descriptor (JSList * drivers, const char * sofile); + +EXPORT JSList * jack_drivers_load (JSList * drivers); + +jack_driver_info_t * jack_load_driver (jack_driver_desc_t * driver_desc); + +#endif + diff --git a/common/JackDummyDriver.cpp b/common/JackDummyDriver.cpp new file mode 100644 index 00000000..da74cc55 --- /dev/null +++ b/common/JackDummyDriver.cpp @@ -0,0 +1,224 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackDummyDriver.h" +#include "JackEngineControl.h" +#include "JackGraphManager.h" +#include "driver_interface.h" +#include "JackDriverLoader.h" +#include <iostream> +#include <unistd.h> + +namespace Jack +{ + +int JackDummyDriver::Open(jack_nframes_t nframes, + jack_nframes_t samplerate, + int capturing, + int playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) +{ + int res = JackAudioDriver::Open(nframes, + samplerate, + capturing, + playing, + inchannels, + outchannels, + monitor, + capture_driver_name, + playback_driver_name, + capture_latency, + playback_latency); + fEngineControl->fPeriod = 0; + fEngineControl->fComputation = 500 * 1000; + fEngineControl->fConstraint = 500 * 1000; + return res; +} + +int JackDummyDriver::Process() +{ + fLastWaitUst = GetMicroSeconds(); // Take callback date here + JackAudioDriver::Process(); + usleep(std::max(0L, long(fWaitTime - (GetMicroSeconds() - fLastWaitUst)))); + return 0; +} + +int JackDummyDriver::SetBufferSize(jack_nframes_t nframes) +{ + fEngineControl->fBufferSize = nframes; + fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // In microsec + return 0; +} + +void JackDummyDriver::PrintState() +{ + std::cout << "JackDummyDriver state" << std::endl; + + jack_port_id_t port_index; + + std::cout << "Input ports" << std::endl; + + for (int i = 0; i < fPlaybackChannels; i++) { + port_index = fCapturePortList[i]; + JackPort* port = fGraphManager->GetPort(port_index); + std::cout << port->GetName() << std::endl; + if (fGraphManager->GetConnectionsNum(port_index)) {} + } + + std::cout << "Output ports" << std::endl; + + for (int i = 0; i < fCaptureChannels; i++) { + port_index = fPlaybackPortList[i]; + JackPort* port = fGraphManager->GetPort(port_index); + std::cout << port->GetName() << std::endl; + if (fGraphManager->GetConnectionsNum(port_index)) {} + } +} + +} // end of namespace + +#ifdef __cplusplus +extern "C" +{ +#endif + + jack_driver_desc_t * driver_get_descriptor () { + jack_driver_desc_t * desc; + unsigned int i; + + desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t)); + strcpy(desc->name, "dummy"); + desc->nparams = 6; + desc->params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); + + i = 0; + strcpy(desc->params[i].name, "capture"); + desc->params[i].character = 'C'; + desc->params[i].type = JackDriverParamUInt; + desc->params[i].value.ui = 2U; + strcpy(desc->params[i].short_desc, "Number of capture ports"); + strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy(desc->params[i].name, "playback"); + desc->params[i].character = 'P'; + desc->params[i].type = JackDriverParamUInt; + desc->params[1].value.ui = 2U; + strcpy(desc->params[i].short_desc, "Number of playback ports"); + strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy(desc->params[i].name, "rate"); + desc->params[i].character = 'r'; + desc->params[i].type = JackDriverParamUInt; + desc->params[i].value.ui = 48000U; + strcpy(desc->params[i].short_desc, "Sample rate"); + strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy(desc->params[i].name, "monitor"); + desc->params[i].character = 'm'; + desc->params[i].type = JackDriverParamBool; + desc->params[i].value.i = 0; + strcpy(desc->params[i].short_desc, "Provide monitor ports for the output"); + strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy(desc->params[i].name, "period"); + desc->params[i].character = 'p'; + desc->params[i].type = JackDriverParamUInt; + desc->params[i].value.ui = 1024U; + strcpy(desc->params[i].short_desc, "Frames per period"); + strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy(desc->params[i].name, "wait"); + desc->params[i].character = 'w'; + desc->params[i].type = JackDriverParamUInt; + desc->params[i].value.ui = 21333U; + strcpy(desc->params[i].short_desc, + "Number of usecs to wait between engine processes"); + strcpy(desc->params[i].long_desc, desc->params[i].short_desc); + + return desc; + } + + Jack::JackDriverClientInterface* driver_initialize(Jack::JackEngine* engine, Jack::JackSynchro** table, const JSList* params) { + jack_nframes_t sample_rate = 48000; + jack_nframes_t period_size = 1024; + unsigned int capture_ports = 2; + unsigned int playback_ports = 2; + unsigned long wait_time = 0; + const JSList * node; + const jack_driver_param_t * param; + bool monitor = false; + + for (node = params; node; node = jack_slist_next (node)) { + param = (const jack_driver_param_t *) node->data; + + switch (param->character) { + + case 'C': + capture_ports = param->value.ui; + break; + + case 'P': + playback_ports = param->value.ui; + break; + + case 'r': + sample_rate = param->value.ui; + break; + + case 'p': + period_size = param->value.ui; + break; + + case 'w': + wait_time = param->value.ui; + break; + + case 'm': + monitor = param->value.i; + break; + } + } + + if (wait_time == 0) // Not set + wait_time = (unsigned long)((((float)period_size) / ((float)sample_rate)) * 1000000.0f); + + Jack::JackDriverClientInterface* driver = new Jack::JackThreadedDriver(new Jack::JackDummyDriver("dummy_pcm", engine, table, wait_time)); + if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, monitor, "dummy", "dummy", 0, 0) == 0) { + return driver; + } else { + delete driver; + return NULL; + } + } + +#ifdef __cplusplus +} +#endif diff --git a/common/JackDummyDriver.h b/common/JackDummyDriver.h new file mode 100644 index 00000000..ee5436b9 --- /dev/null +++ b/common/JackDummyDriver.h @@ -0,0 +1,68 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackDummyDriver__ +#define __JackDummyDriver__ + +#include "JackAudioDriver.h" +#include "JackThreadedDriver.h" +#include "JackTime.h" + +namespace Jack +{ + +/*! +\brief The dummy driver. +*/ + +class JackDummyDriver : public JackAudioDriver +{ + private: + + long fWaitTime; + + public: + + JackDummyDriver(const char* name, JackEngine* engine, JackSynchro** table, unsigned long wait_time) + : JackAudioDriver(name, engine, table), fWaitTime(wait_time) + {} + virtual ~JackDummyDriver() + {} + + int Open(jack_nframes_t frames_per_cycle, + jack_nframes_t rate, + int capturing, + int playing, + int chan_in, + int chan_out, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency); + + int Process(); + int SetBufferSize(jack_nframes_t nframe); + void PrintState(); +}; + +} // end of namespace + +#endif diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp new file mode 100644 index 00000000..1f2345ec --- /dev/null +++ b/common/JackEngine.cpp @@ -0,0 +1,644 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <iostream> +#include <fstream> +#include <assert.h> + +#include "JackEngine.h" +#include "JackExternalClient.h" +#include "JackEngineControl.h" +#include "JackClientControl.h" +#include "JackEngineTiming.h" +#include "JackGlobals.h" +#include "JackChannel.h" +#include "JackSyncInterface.h" + +namespace Jack +{ + +JackEngine::JackEngine(JackGraphManager* manager, JackSynchro** table, JackEngineControl* control, JackSyncInterface* signal, bool sync, long time_out_ms, bool rt, long priority, bool ve) +{ + fGraphManager = manager; + fSynchroTable = table; + fEngineControl = control; + fEngineControl->fSyncMode = sync; + fEngineControl->fTimeOutUsecs = time_out_ms * 1000; + fEngineControl->fRealTime = rt; + fEngineControl->fPriority = priority; + fEngineControl->fVerbose = ve; + verbose = ve; + fChannel = JackGlobals::MakeServerNotifyChannel(); + fTiming = new JackEngineTiming(fClientTable, fGraphManager, fEngineControl); + fSignal = signal; + for (int i = 0; i < CLIENT_NUM; i++) + fClientTable[i] = 0; + fTiming->ClearTimeMeasures(); + fTiming->ResetRollingUsecs(); +} + +JackEngine::~JackEngine() +{ + delete fChannel; + delete fTiming; +} + +//------------------- +// Client management +//------------------- + +int JackEngine::Open() +{ + JackLog("JackEngine::Open\n"); + + // Open audio thread => request thread communication channel + if (fChannel->Open() < 0) { + jack_error("Cannot connect to server"); + return -1; + } else { + return 0; + } +} + +int JackEngine::Close() +{ + JackLog("JackEngine::Close\n"); + fChannel->Close(); + + // Close (possibly) remaining clients (RT is stopped) + for (int i = 0; i < CLIENT_NUM; i++) { + JackClientInterface* client = fClientTable[i]; + if (client) { + JackLog("JackEngine::Close remaining client %ld\n", i); + ClientCloseAux(i, client, false); + client->Close(); + delete client; + } + } + + return 0; +} + +//------------------ +// Graph management +//------------------ + +void JackEngine::Process(jack_time_t callback_usecs) +{ + // Transport + fEngineControl->fTransport.CycleBegin(fEngineControl->fSampleRate, callback_usecs); + + //JackLog("Process: callback_usecs %lld\n",callback_usecs/1000); + + // Timing + fEngineControl->fFrameTimer.IncFrameTime(fEngineControl->fBufferSize, callback_usecs, fEngineControl->fPeriodUsecs); + fTiming->UpdateTiming(callback_usecs); + + // Graph + if (fGraphManager->IsFinishedGraph()) { + fLastSwitchUsecs = callback_usecs; + if (fGraphManager->RunNextGraph()) // True if the graph actually switched to a new state + fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kGraphOrderCallback, 0); + fSignal->SignalAll(); // Signal for threads waiting for next cycle + } else { + JackLog("Process: graph not finished!\n"); + if (callback_usecs > fLastSwitchUsecs + fEngineControl->fTimeOutUsecs) { + JackLog("Process: switch to next state %ld\n", long(callback_usecs - fLastSwitchUsecs)); + //RemoveZombifiedClients(callback_usecs); TODO + fLastSwitchUsecs = callback_usecs; + if (fGraphManager->RunNextGraph()) + fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kGraphOrderCallback, 0); + fSignal->SignalAll(); // Signal for threads waiting for next cycle + } else { + JackLog("Process: waiting to switch %ld\n", long(callback_usecs - fLastSwitchUsecs)); + if (callback_usecs < fLastSwitchUsecs + 2 * fEngineControl->fPeriodUsecs) // Signal XRun only for the first failling cycle + CheckXRun(callback_usecs); + fGraphManager->RunCurrentGraph(); + } + } + + // Transport + fEngineControl->fTransport.CycleEnd(fClientTable, fEngineControl->fSampleRate, fEngineControl->fBufferSize); +} + +/* +Client that finish *after* the callback date are considered late even if their output buffers may have been +correctly mixed in the time window: callbackUsecs <==> Read <==> Write. +*/ + +void JackEngine::CheckXRun(jack_time_t callback_usecs) // REVOIR les conditions de fin +{ + for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) + { + JackClientInterface* client = fClientTable[i]; + if (client && client->GetClientControl()->fActive) { + JackClientTiming* timing = fGraphManager->GetClientTiming(i); + jack_client_state_t status = timing->fStatus; + jack_time_t finished_date = timing->fFinishedAt; + + if (status != NotTriggered && status != Finished) { + jack_error("JackEngine::XRun: client = %s was not runned: state = %ld", client->GetClientControl()->fName, status); + //fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client + fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0); // Notify all clients + } + if (status == Finished && (long)(finished_date - callback_usecs) > 0) { + jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName); + //fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client + fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0); // Notify all clients + } + } + } +} + +//--------------- +// Zombification +//--------------- + +bool JackEngine::IsZombie(JackClientInterface* client, jack_time_t current_time) +{ + return ((current_time - fGraphManager->GetClientTiming(client->GetClientControl()->fRefNum)->fFinishedAt) > 2 * fEngineControl->fTimeOutUsecs); // A VERIFIER +} + +// TODO : check what happens with looped sub-graph.... +void JackEngine::GetZombifiedClients(bool zombi_clients[CLIENT_NUM], jack_time_t current_time) +{ + for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { + JackClientInterface* client1 = fClientTable[i]; + if (client1 && IsZombie(client1, current_time)) { + JackLog("JackEngine::GetZombifiedClients: %s\n", client1->GetClientControl()->fName); + zombi_clients[i] = true; // Assume client is dead + // If another dead client is connected to the scanned one, then the scanned one is not the first of the dead subgraph + for (int j = REAL_REFNUM; j < CLIENT_NUM; j++) { + JackClientInterface* client2 = fClientTable[j]; + if (client2 && IsZombie(client2, current_time) && fGraphManager->IsDirectConnection(j, i)) { + zombi_clients[i] = false; + break; + } + } + } else { + zombi_clients[i] = false; + } + } +} + +void JackEngine::RemoveZombifiedClients(jack_time_t current_time) +{ + bool zombi_clients[CLIENT_NUM]; + GetZombifiedClients(zombi_clients, current_time); + + for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { + if (zombi_clients[i] && !fClientTable[i]->GetClientControl()->fZombie) { + fClientTable[i]->GetClientControl()->fZombie = true; + JackLog("RemoveZombifiedCients: name = %s\n", fClientTable[i]->GetClientControl()->fName); + fGraphManager->DirectDisconnect(FREEWHEEL_DRIVER_REFNUM, i); + fGraphManager->DirectDisconnect(i, FREEWHEEL_DRIVER_REFNUM); + fGraphManager->DisconnectAllPorts(i); + fChannel->ClientNotify(i, JackNotifyChannelInterface::kZombifyClient, 0); // Signal engine + } + } +} + +void JackEngine::ZombifyClient(int refnum) +{ + NotifyClient(refnum, JackNotifyChannelInterface::kZombifyClient, false, 0); +} + +//--------------- +// Notifications +//--------------- + +void JackEngine::NotifyClient(int refnum, int event, int sync, int value) +{ + JackClientInterface* client = fClientTable[refnum]; + // The client may be notified by the RT thread while closing + if (client && (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, value) < 0)) { + jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client->GetClientControl()->fName, event, value); + } else { + JackLog("JackEngine::NotifyClient: client not available anymore\n"); + } +} + +void JackEngine::NotifyClients(int event, int sync, int value) +{ + for (int i = 0; i < CLIENT_NUM; i++) { + JackClientInterface* client = fClientTable[i]; + if (client && (client->ClientNotify(i, client->GetClientControl()->fName, event, sync, value) < 0)) { + jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client->GetClientControl()->fName, event, value); + } + } +} + +int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum) +{ + // Notify existing clients of the new client and new client of existing clients. + for (int i = 0; i < CLIENT_NUM; i++) { + JackClientInterface* old_client = fClientTable[i]; + if (old_client) { + if (old_client->ClientNotify(refnum, name, JackNotifyChannelInterface::kAddClient, true, 0) < 0) + return -1; + if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, JackNotifyChannelInterface::kAddClient, true, 0) < 0) + return -1; + } + } + + return 0; +} + +void JackEngine::NotifyRemoveClient(const char* name, int refnum) +{ + // Notify existing clients (including the one beeing suppressed) of the removed client + for (int i = 0; i < CLIENT_NUM; i++) { + JackClientInterface* client = fClientTable[i]; + if (client) { + client->ClientNotify(refnum, name, JackNotifyChannelInterface::kRemoveClient, true, 0); + } + } +} + +// Coming from the driver +void JackEngine::NotifyXRun(jack_time_t callback_usecs) +{ + // Use the audio thread => request thread communication channel + fEngineControl->fFrameTimer.ResetFrameTime(fEngineControl->fSampleRate, callback_usecs, fEngineControl->fPeriodUsecs); + fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0); +} + +void JackEngine::NotifyXRun(int refnum) +{ + if (refnum == ALL_CLIENTS) { + NotifyClients(JackNotifyChannelInterface::kXRunCallback, false, 0); + } else { + NotifyClient(refnum, JackNotifyChannelInterface::kXRunCallback, false, 0); + } +} + +void JackEngine::NotifyGraphReorder() +{ + NotifyClients(JackNotifyChannelInterface::kGraphOrderCallback, false, 0); +} + +void JackEngine::NotifyBufferSize(jack_nframes_t nframes) +{ + NotifyClients(JackNotifyChannelInterface::kBufferSizeCallback, true, nframes); +} + +void JackEngine::NotifyFreewheel(bool onoff) +{ + fEngineControl->fRealTime = !onoff; + NotifyClients((onoff ? JackNotifyChannelInterface::kStartFreewheel : JackNotifyChannelInterface::kStopFreewheel), true, 0); +} + +void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff) +{ + NotifyClients((onoff ? JackNotifyChannelInterface::kPortRegistrationOn : JackNotifyChannelInterface::kPortRegistrationOff), false, port_index); +} + +//------------------- +// Client management +//------------------- + +bool JackEngine::ClientCheckName(const char* name) +{ + for (int i = 0; i < CLIENT_NUM; i++) { + JackClientInterface* client = fClientTable[i]; + if (client && (strcmp(client->GetClientControl()->fName, name) == 0)) + return true; + } + + return false; +} + +// Used for external clients +int JackEngine::ClientNew(const char* name, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager) +{ + if (ClientCheckName(name)) { + jack_error("client %s already registered", name); + return -1; + } + + JackExternalClient* client = new JackExternalClient(); + if (ClientExternalNew(name, ref, shared_engine, shared_client, shared_graph_manager, client) < 0) { + delete client; + return -1; + } + + return 0; +} + +// Used for external clients +int JackEngine::ClientExternalNew(const char* name, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager, JackExternalClient* client) +{ + JackLog("JackEngine::ClientNew: name %s \n", name); + int refnum = fGraphManager->AllocateRefNum(); + + if (refnum < 0) { + jack_error("No more refnum available"); + return -1; + } + + if (!fSynchroTable[refnum]->Allocate(name, 0)) { + jack_error("Cannot allocate synchro"); + goto error; + } + + if (client->Open(name, refnum, shared_client) < 0) { + jack_error("Cannot open client"); + goto error; + } + + if (!fSignal->TimedWait(5 * 1000000)) { + // Failure if RT thread is not running (problem with the driver...) + jack_error("Driver is not running"); + goto error; + } + + if (NotifyAddClient(client, name, refnum) < 0) { + jack_error("Cannot notify add client"); + goto error; + } + + fClientTable[refnum] = client; + fTiming->ResetRollingUsecs(); + *shared_engine = fEngineControl->GetShmIndex(); + *shared_graph_manager = fGraphManager->GetShmIndex(); + *ref = refnum; + return 0; + +error: + fGraphManager->ReleaseRefNum(refnum); + ClientCloseAux(refnum, client, false); + client->Close(); + return -1; +} + +// Used for server driver clients +int JackEngine::ClientInternalNew(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client) +{ + JackLog("JackEngine::ClientInternalNew: name %s\n", name); + int refnum = fGraphManager->AllocateRefNum(); + + if (refnum < 0) { + jack_error("No more refnum available"); + return -1; + } + + if (!fSynchroTable[refnum]->Allocate(name, 0)) { + jack_error("Cannot allocate synchro"); + goto error; + } + + if (NotifyAddClient(client, name, refnum) < 0) { + jack_error("Cannot notify add client"); + goto error; + } + + fClientTable[refnum] = client; + fTiming->ResetRollingUsecs(); + *shared_engine = fEngineControl; + *shared_manager = fGraphManager; + *ref = refnum; + return 0; + +error: + fGraphManager->ReleaseRefNum(refnum); + return -1; +} + +// Used for externall clients +int JackEngine::ClientClose(int refnum) +{ + JackClientInterface* client = fClientTable[refnum]; + if (client) { + fEngineControl->fTransport.ResetTimebase(refnum); + int res = ClientCloseAux(refnum, client, true); + client->Close(); + delete client; + return res; + } else { + return -1; + } +} + +// Used for server internal clients +int JackEngine::ClientInternalClose(int refnum) +{ + JackClientInterface* client = fClientTable[refnum]; + return (client) ? ClientCloseAux(refnum, client, true) : -1; +} + +// Used for drivers that close when the RT thread is stopped +int JackEngine::ClientInternalCloseIm(int refnum) +{ + JackClientInterface* client = fClientTable[refnum]; + return (client) ? ClientCloseAux(refnum, client, false) : -1; +} + +int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wait) +{ + JackLog("JackEngine::ClientCloseAux ref = %ld name = %s\n", refnum, client->GetClientControl()->fName); + + // Remove the client from the table + fClientTable[refnum] = NULL; + + // Remove ports + fGraphManager->RemoveAllPorts(refnum); + + // Wait until next cycle to be sure client is not used anymore + if (wait) { + if (!fSignal->TimedWait(fEngineControl->fTimeOutUsecs * 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure + JackLog("JackEngine::ClientCloseAux wait error ref = %ld \n", refnum); + } + } + + // Notify running clients + NotifyRemoveClient(client->GetClientControl()->fName, client->GetClientControl()->fRefNum); + + // Cleanup... + fSynchroTable[refnum]->Destroy(); + fGraphManager->ReleaseRefNum(refnum); + fTiming->ResetRollingUsecs(); + return 0; +} + +int JackEngine::ClientActivate(int refnum) +{ + JackClientInterface* client = fClientTable[refnum]; + assert(fClientTable[refnum]); + + JackLog("JackEngine::ClientActivate ref = %ld name = %s\n", refnum, client->GetClientControl()->fName); + // Wait for graph state change to be effective + if (!fSignal->TimedWait(fEngineControl->fPeriodUsecs * 10)) { + JackLog("JackEngine::ClientActivate wait error ref = %ld name = %s\n", refnum, client->GetClientControl()->fName); + return -1; + } else { + return 0; + } +} + +// May be called without client +int JackEngine::ClientDeactivate(int refnum) +{ + JackClientInterface* client = fClientTable[refnum]; + if (client == NULL) + return -1; + + JackLog("JackEngine::ClientDeactivate ref = %ld name = %s\n", refnum, client->GetClientControl()->fName); + fGraphManager->DisconnectAllPorts(refnum); + // Wait for graph state change to be effective + if (!fSignal->TimedWait(fEngineControl->fPeriodUsecs * 10)) { + JackLog("JackEngine::ClientDeactivate wait error ref = %ld name = %s\n", refnum, client->GetClientControl()->fName); + return -1; + } else { + return 0; + } +} + +//----------------- +// Port management +//----------------- + +int JackEngine::PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index) +{ + JackLog("JackEngine::PortRegister ref = %ld name = %s flags = %d buffer_size = %d\n", refnum, name, flags, buffer_size); + assert(fClientTable[refnum]); + + *port_index = fGraphManager->AllocatePort(refnum, name, (JackPortFlags)flags); + if (*port_index != NO_PORT) { + NotifyPortRegistation(*port_index, true); + return 0; + } else { + return -1; + } +} + +int JackEngine::PortUnRegister(int refnum, jack_port_id_t port_index) +{ + JackLog("JackEngine::PortUnRegister ref = %ld port_index = %ld\n", refnum, port_index); + assert(fClientTable[refnum]); + + if (fGraphManager->RemovePort(refnum, port_index) == 0) { + fGraphManager->ReleasePort(port_index); + NotifyPortRegistation(port_index, false); + return 0; + } else { + return -1; + } +} + +int JackEngine::PortConnect(int refnum, const char* src, const char* dst) +{ + JackLog("JackEngine::PortConnect src = %s dst = %s\n", src, dst); + jack_port_id_t port_src, port_dst; + + return (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0) + ? -1 + : PortConnect(refnum, port_src, port_dst); +} + +int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst) +{ + JackLog("JackEngine::PortDisconnect src = %s dst = %s\n", src, dst); + jack_port_id_t port_src, port_dst; + + return (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0) + ? -1 + : fGraphManager->Disconnect(port_src, port_dst); +} + +int JackEngine::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst) +{ + JackLog("JackEngine::PortConnect src = %d dst = %d\n", src, dst); + JackClientInterface* client; + int ref; + + if (fGraphManager->CheckPorts(src, dst) < 0) + return -1; + + ref = fGraphManager->GetOutputRefNum(src); + assert(ref >= 0); + client = fClientTable[ref]; + assert(client); + if (!client->GetClientControl()->fActive) { + jack_error("Cannot connect ports owned by inactive clients:" + " \"%s\" is not active", client->GetClientControl()->fName); + return -1; + } + + ref = fGraphManager->GetInputRefNum(dst); + assert(ref >= 0); + client = fClientTable[ref]; + assert(client); + if (!client->GetClientControl()->fActive) { + jack_error("Cannot connect ports owned by inactive clients:" + " \"%s\" is not active", client->GetClientControl()->fName); + return -1; + } + + return fGraphManager->Connect(src, dst); +} + +int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst) +{ + JackLog("JackEngine::PortDisconnect src = %d dst = %d\n", src, dst); + + if (dst == ALL_PORTS) { + return (fGraphManager->CheckPort(src) < 0) + ? -1 + : fGraphManager->DisconnectAll(src); + } else { + return (fGraphManager->CheckPorts(src, dst) < 0) + ? -1 + : fGraphManager->Disconnect(src, dst); + } +} + +//---------------------- +// Transport management +//---------------------- + +int JackEngine::ReleaseTimebase(int refnum) +{ + return fEngineControl->fTransport.ResetTimebase(refnum); +} + +int JackEngine::SetTimebaseCallback(int refnum, int conditional) +{ + return fEngineControl->fTransport.SetTimebase(refnum, conditional); +} + +//----------- +// Debugging +//----------- + +void JackEngine::PrintState() +{ + std::cout << "Engine State" << std::endl; + + for (int i = 0; i < CLIENT_NUM; i++) { + JackClientInterface* client = fClientTable[i]; + if (client) + std::cout << "Client : " << client->GetClientControl()->fName << " : " << i << std::endl; + } + + //fGraphManager->PrintState(); + fTiming->PrintState(); +} + +} // end of namespace + diff --git a/common/JackEngine.h b/common/JackEngine.h new file mode 100644 index 00000000..63f2947b --- /dev/null +++ b/common/JackEngine.h @@ -0,0 +1,119 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackEngine__ +#define __JackEngine__ + +#include "JackConstants.h" +#include "JackGraphManager.h" +#include "JackSynchro.h" +#include "JackTransportEngine.h" + +namespace Jack +{ + +class JackClientInterface; +struct JackEngineControl; +class JackServerNotifyChannelInterface; +class JackEngineTiming; +class JackExternalClient; +class JackSyncInterface; + +/*! +\brief Engine description. +*/ + +class JackEngine +{ + private: + + JackGraphManager* fGraphManager; + JackEngineControl* fEngineControl; + JackClientInterface* fClientTable[CLIENT_NUM]; + JackSynchro** fSynchroTable; + JackServerNotifyChannelInterface* fChannel; /*! To communicate between the RT thread and server */ + JackEngineTiming* fTiming; + JackSyncInterface* fSignal; + jack_time_t fLastSwitchUsecs; + + int ClientCloseAux(int refnum, JackClientInterface* client, bool wait); + void CheckXRun(jack_time_t callback_usecs); + int NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum); + void NotifyRemoveClient(const char* name, int refnum); + bool IsZombie(JackClientInterface* client, jack_time_t current_time); + void RemoveZombifiedClients(jack_time_t current_time); + void GetZombifiedClients(bool clients[CLIENT_NUM], jack_time_t current_time); + + public: + + JackEngine(JackGraphManager* manager, JackSynchro** table, JackEngineControl* control, JackSyncInterface* signal, bool sync, long time_out_ms, bool rt, long priority, bool verbose); + virtual ~JackEngine(); + + int Open(); + int Close(); + + // Client management + bool ClientCheckName(const char* name); + int ClientNew(const char* name, int* refnum, int* shared_engine, int* shared_client, int* shared_graph_manager); + int ClientExternalNew(const char* name, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager, JackExternalClient* client); + int ClientInternalNew(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client); + + int ClientClose(int refnum); + int ClientInternalClose(int refnum); + int ClientInternalCloseIm(int refnum); + + int ClientActivate(int refnum); + int ClientDeactivate(int refnum); + + // Port management + int PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index); + int PortUnRegister(int refnum, jack_port_id_t port); + + int PortConnect(int refnum, const char* src, const char* dst); + int PortDisconnect(int refnum, const char* src, const char* dst); + + int PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst); + int PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst); + + // Transport management + int ReleaseTimebase(int refnum); + int SetTimebaseCallback(int refnum, int conditional); + + // Graph + void Process(jack_time_t callback_usecs); + void ZombifyClient(int refnum); + + // Notifications + void NotifyClient(int refnum, int event, int sync, int value); + void NotifyClients(int event, int sync, int value); + void NotifyXRun(jack_time_t callback_usecs); + void NotifyXRun(int refnum); + void NotifyGraphReorder(); + void NotifyBufferSize(jack_nframes_t nframes); + void NotifyFreewheel(bool onoff); + void NotifyPortRegistation(jack_port_id_t port_index, bool onoff); + + void PrintState(); +}; + + +} // end of namespace + +#endif + diff --git a/common/JackEngineControl.h b/common/JackEngineControl.h new file mode 100644 index 00000000..975a888a --- /dev/null +++ b/common/JackEngineControl.h @@ -0,0 +1,58 @@ +/* +Copyright (C) 2003 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackEngineControl__ +#define __JackEngineControl__ + +#include "JackShmMem.h" +#include "JackFrameTimer.h" +#include "JackTransportEngine.h" +#include "types.h" + +namespace Jack +{ + +/*! +\brief Engine control in shared memory. +*/ + +struct JackEngineControl : public JackShmMem +{ + jack_nframes_t fBufferSize; + jack_nframes_t fSampleRate; + bool fRealTime; + int32_t fPriority; + float fCPULoad; + jack_time_t fPeriodUsecs; + jack_time_t fTimeOutUsecs; + UInt64 fPeriod; + UInt64 fComputation; + UInt64 fConstraint; + JackFrameTimer fFrameTimer; + JackTransportEngine fTransport; + bool fSyncMode; + bool fVerbose; +}; + + +} // end of namespace + + +#endif diff --git a/common/JackEngineTiming.cpp b/common/JackEngineTiming.cpp new file mode 100644 index 00000000..521dcbd1 --- /dev/null +++ b/common/JackEngineTiming.cpp @@ -0,0 +1,229 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackEngineTiming.h" +#include "JackClientInterface.h" +#include "JackEngineControl.h" +#include "JackClientControl.h" +#include <math.h> +#include <algorithm> +#include <iostream> +//#include <fstream> +#include <assert.h> + +namespace Jack +{ + +inline jack_time_t MAX(jack_time_t a, jack_time_t b) +{ + return (a < b) ? b : a; +} + +JackEngineTiming::JackEngineTiming(JackClientInterface** table, JackGraphManager* manager, JackEngineControl* control) +{ + fClientTable = table; + fGraphManager = manager; + fEngineControl = control; + fLastTime = 0; + fCurTime = 0; + fProcessTime = 0; + fLastProcessTime = 0; + fSpareUsecs = 0; + fMaxUsecs = 0; + fAudioCycle = 0; +} + +void JackEngineTiming::UpdateTiming(jack_time_t callback_usecs) +{ + GetTimeMeasure(callback_usecs); + CalcCPULoad(); +} + +void JackEngineTiming::CalcCPULoad() +{ + jack_time_t lastCycleEnd = fLastProcessTime; + + for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { + JackClientInterface* client = fClientTable[i]; + JackClientTiming* timing = fGraphManager->GetClientTiming(i); + if (client && client->GetClientControl()->fActive && timing->fStatus == Finished) { + lastCycleEnd = MAX(lastCycleEnd, timing->fFinishedAt); + } + } + + /* store the execution time for later averaging */ + fRollingClientUsecs[fRollingClientUsecsIndex++] = lastCycleEnd - fLastTime; + + if (fRollingClientUsecsIndex >= JACK_ENGINE_ROLLING_COUNT) { + fRollingClientUsecsIndex = 0; + } + + /* every so often, recompute the current maximum use over the + last JACK_ENGINE_ROLLING_COUNT client iterations. + */ + + if (++fRollingClientUsecsCnt % fRollingInterval == 0) { + + jack_time_t maxUsecs = 0; + for (int i = 0; i < JACK_ENGINE_ROLLING_COUNT; i++) { + maxUsecs = MAX(fRollingClientUsecs[i], maxUsecs); + } + + fMaxUsecs = MAX(fMaxUsecs, maxUsecs); + fSpareUsecs = jack_time_t((maxUsecs < fEngineControl->fPeriodUsecs) ? fEngineControl->fPeriodUsecs - maxUsecs : 0); + fEngineControl->fCPULoad + = ((1.0f - (float(fSpareUsecs) / float(fEngineControl->fPeriodUsecs))) * 50.0f + (fEngineControl->fCPULoad * 0.5f)); + } +} + +void JackEngineTiming::ResetRollingUsecs() +{ + memset(fRollingClientUsecs, 0, sizeof(fRollingClientUsecs)); + fRollingClientUsecsIndex = 0; + fRollingClientUsecsCnt = 0; + fSpareUsecs = 0; + fRollingInterval = (int)floor((JACK_ENGINE_ROLLING_INTERVAL * 1000.0f) / fEngineControl->fPeriodUsecs); +} + +void JackEngineTiming::GetTimeMeasure(jack_time_t callbackUsecs) +{ + int pos = (++fAudioCycle) % TIME_POINTS; + + fLastTime = fCurTime; + fCurTime = callbackUsecs; + + fLastProcessTime = fProcessTime; + fProcessTime = GetMicroSeconds(); + + if (fLastTime > 0) { + fMeasure[pos].fEngineTime = fLastTime; + fMeasure[pos].fAudioCycle = fAudioCycle; + + for (int i = 0; i < CLIENT_NUM; i++) { + JackClientInterface* client = fClientTable[i]; + JackClientTiming* timing = fGraphManager->GetClientTiming(i); + if (client && client->GetClientControl()->fActive) { + fMeasure[pos].fClientTable[i].fRefNum = i; + fMeasure[pos].fClientTable[i].fSignaledAt = timing->fSignaledAt; + fMeasure[pos].fClientTable[i].fAwakeAt = timing->fAwakeAt; + fMeasure[pos].fClientTable[i].fFinishedAt = timing->fFinishedAt; + fMeasure[pos].fClientTable[i].fStatus = timing->fStatus; + } + } + } +} + +void JackEngineTiming::PrintState() +{ + jack_time_t prevtime, time; + prevtime = time = fMeasure[0].fEngineTime; + int cycle, prevcycle = fMeasure[0].fAudioCycle; + /* + + std::ofstream f("measure.txt"); + + if (f.is_open()) { + + //std::cout << "---------------------------------------------" << std::endl; + + for (int i = 0; i < CLIENT_NUM; i++) { // client + JackClientInterface* client = fClientTable[i]; + if (client && client->GetClientControl()->fActive) { + // f << "Client : " << i << std::endl; + long maxsignalledat = 0; + long maxawakedat = 0; + long maxfinisheddat = 0; + bool max = false; + prevtime = fMeasure[0].fEngineTime; + prevcycle = fMeasure[0].fAudioCycle; + + // TODO + + for (int j = 0; j < TIME_POINTS; j++) { // measure + time = fMeasure[j].fEngineTime; + cycle = fMeasure[j].fAudioCycle; + + if (fMeasure[j].fClientTable[i].fRefNum > 0) { + + if (fMeasure[j].fClientTable[i].fStatus != Finished) { + f << "error status " << '\t' + << prevtime << '\t' + << time << '\t' + << fMeasure[j + 1].fEngineTime << '\t' + << prevcycle << '\t' + << cycle << '\t' + << fMeasure[j].fClientTable[i].fSignaledAt << '\t' + << fMeasure[j].fClientTable[i].fAwakeAt << '\t' + << fMeasure[j].fClientTable[i].fFinishedAt + << std::endl; + } + + if (long(time - prevtime) > 0) { + + f << long(time - prevtime) << '\t' + << fMeasure[j].fClientTable[i].fSignaledAt - time << '\t' + << fMeasure[j].fClientTable[i].fAwakeAt - time << '\t' + << fMeasure[j].fClientTable[i].fFinishedAt - time << '\t' + << fMeasure[j].fClientTable[i].fStatus + << std::endl; + } else { + f << "error time : " << j << " " << long(time - prevtime) << std::endl; + } + + maxsignalledat = MAX(maxsignalledat, long(fMeasure[j].fClientTable[i].fSignaledAt - time)); + maxawakedat = MAX(maxawakedat, long(fMeasure[j].fClientTable[i].fAwakeAt - time)); + maxfinisheddat = MAX(maxfinisheddat, long(fMeasure[j].fClientTable[i].fFinishedAt - time)); + max = true; + } + prevtime = time; + prevcycle = cycle; + } + + + f << std::endl; + if (max) { + f << "maxsignalledat: " << maxsignalledat + << '\t' << "maxawakedat: " << maxawakedat + << '\t' << "maxfinisheddat: " << maxfinisheddat + << '\t' << std::endl; + } + } + } + + f.close(); + } + */ +} + +void JackEngineTiming::ClearTimeMeasures() +{ + for (int i = 0; i < TIME_POINTS; i++) { + for (int j = 0; j < CLIENT_NUM; j++) { + fMeasure[i].fClientTable[j].fRefNum = 0; + fMeasure[i].fClientTable[j].fSignaledAt = 0; + fMeasure[i].fClientTable[j].fAwakeAt = 0; + fMeasure[i].fClientTable[j].fFinishedAt = 0; + } + } + fLastTime = fCurTime = 0; +} + + +} // end of namespace + diff --git a/common/JackEngineTiming.h b/common/JackEngineTiming.h new file mode 100644 index 00000000..986e548d --- /dev/null +++ b/common/JackEngineTiming.h @@ -0,0 +1,105 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackEngineTiming__ +#define __JackEngineTiming__ + +#include "types.h" +#include "JackGraphManager.h" + +namespace Jack +{ + +#define TIME_POINTS 1000 +#define JACK_ENGINE_ROLLING_COUNT 32 +#define JACK_ENGINE_ROLLING_INTERVAL 1024 + +class JackClientInterface; +struct JackEngineControl; + +/*! +\brief Timing stucture for a client. +*/ + +struct JackTimingMeasureClient +{ + int fRefNum; + jack_time_t fSignaledAt; + jack_time_t fAwakeAt; + jack_time_t fFinishedAt; + jack_client_state_t fStatus; +}; + +/*! +\brief Timing stucture for a table of clients. +*/ + +struct JackTimingMeasure +{ + long fAudioCycle; + jack_time_t fEngineTime; + JackTimingMeasureClient fClientTable[CLIENT_NUM]; +}; + +/*! +\brief Engine timing management. +*/ + +class JackEngineTiming +{ + private: + + JackClientInterface** fClientTable; + JackGraphManager* fGraphManager; + JackEngineControl* fEngineControl; + + JackTimingMeasure fMeasure[TIME_POINTS]; + jack_time_t fLastTime; + jack_time_t fCurTime; + jack_time_t fProcessTime; + jack_time_t fLastProcessTime; + jack_time_t fSpareUsecs; + jack_time_t fMaxUsecs; + uint32_t fAudioCycle; + + jack_time_t fRollingClientUsecs[JACK_ENGINE_ROLLING_COUNT]; + int fRollingClientUsecsCnt; + int fRollingClientUsecsIndex; + int fRollingInterval; + + void CalcCPULoad(); + void GetTimeMeasure(jack_time_t callback_usecs); + + public: + + JackEngineTiming(JackClientInterface** table, JackGraphManager* manager, JackEngineControl* control); + virtual ~JackEngineTiming() + {} + + void UpdateTiming(jack_time_t callback_usecs); + void ResetRollingUsecs(); + + void ClearTimeMeasures(); + void PrintState(); +}; + +} // end of namespace + +#endif + diff --git a/common/JackError.c b/common/JackError.c new file mode 100644 index 00000000..ed463e5f --- /dev/null +++ b/common/JackError.c @@ -0,0 +1,48 @@ +/* + Copyright (C) 2001 Paul Davis + Copyright (C) 2004-2006 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <stdarg.h> +#include <stdio.h> +#include "JackError.h" + +EXPORT void jack_error (const char *fmt, ...) +{ + va_list ap; + char buffer[300]; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + //jack_error_callback(buffer); + fprintf(stderr, "%s\n", buffer); + va_end(ap); +} + +EXPORT void JackLog(char *fmt,...) +{ +#ifdef PRINTDEBUG + if (verbose) { + va_list ap; + va_start(ap, fmt); + fprintf(stderr,"Jack: "); + vfprintf(stderr, fmt, ap); + va_end(ap); + } +#endif +} + diff --git a/common/JackError.h b/common/JackError.h new file mode 100644 index 00000000..42baefb4 --- /dev/null +++ b/common/JackError.h @@ -0,0 +1,48 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +*/ + +#include <string.h> +#include <errno.h> +#include <stdio.h> + +#include "JackConstants.h" +#include "JackExports.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef WIN32 + #define vsnprintf _vsnprintf + #define snprintf _snprintf +#endif + + EXPORT void jack_error(const char *fmt, ...); + + EXPORT void JackLog(char *fmt, ...); + + extern int verbose; + +#ifdef __cplusplus +} +#endif diff --git a/common/JackExports.h b/common/JackExports.h new file mode 100644 index 00000000..3e3edf6d --- /dev/null +++ b/common/JackExports.h @@ -0,0 +1,30 @@ +/* +Copyright (C) 2004-2005 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackExports__ +#define __JackExports__ + +#ifdef WIN32 + #define EXPORT __declspec(dllexport) +#else + #define EXPORT +#endif + +#endif + diff --git a/common/JackExternalClient.cpp b/common/JackExternalClient.cpp new file mode 100644 index 00000000..a637e6c7 --- /dev/null +++ b/common/JackExternalClient.cpp @@ -0,0 +1,83 @@ +/* + Copyright (C) 2001-2003 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackExternalClient.h" +#include "JackClientControl.h" +#include "JackGlobals.h" +#include "JackChannel.h" + +namespace Jack +{ + +JackExternalClient::JackExternalClient(): fClientControl(NULL) +{ + fChannel = JackGlobals::MakeNotifyChannel(); +} + +JackExternalClient::~JackExternalClient() +{ + delete fChannel; +} + +int JackExternalClient::ClientNotify(int refnum, const char* name, int notify, int sync, int value) +{ + int result = -1; + JackLog("JackExternalClient::ClientNotify ref = %ld name = %s notify = %ld\n", refnum, name, notify); + fChannel->ClientNotify(refnum, name, notify, sync, value, &result); + return result; +} + +int JackExternalClient::Open(const char* name, int refnum, int* shared_client) +{ + try { + + if (fChannel->Open(name) < 0) { + jack_error("Cannot connect to client name = %s port\n", name); + return -1; + } + + fClientControl = new JackClientControl(name, refnum); + if (!fClientControl) { + jack_error("Cannot allocate client shared memory segment"); + return -1; + } + + *shared_client = fClientControl->GetShmIndex(); + JackLog("JackExternalClient::Open name = %s index = %ld base = %x\n", name, fClientControl->GetShmIndex(), fClientControl->GetShmAddress()); + return 0; + + } catch (std::exception e) { + return -1; + } +} + +int JackExternalClient::Close() +{ + fChannel->Close(); + delete fClientControl; + return 0; +} + +JackClientControl* JackExternalClient::GetClientControl() const +{ + return fClientControl; +} + +} // end of namespace diff --git a/common/JackExternalClient.h b/common/JackExternalClient.h new file mode 100644 index 00000000..0b895140 --- /dev/null +++ b/common/JackExternalClient.h @@ -0,0 +1,60 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackExternalClient__ +#define __JackExternalClient__ + +#include "JackClientInterface.h" + +namespace Jack +{ + +class JackNotifyChannelInterface; +struct JackClientControl; + +/*! +\brief Server side implementation of library clients. +*/ + +class JackExternalClient : public JackClientInterface +{ + + private: + + JackNotifyChannelInterface* fChannel; /*! Server/client communication channel */ + JackClientControl* fClientControl; /*! Client control in shared memory */ + + public: + + JackExternalClient(); + virtual ~JackExternalClient(); + + int Open(const char* name, int refnum, int* shared_client); + int Close(); + + int ClientNotify(int refnum, const char* name, int notify, int sync, int value); + + JackClientControl* GetClientControl() const; +}; + + +} // end of namespace + +#endif diff --git a/common/JackFifo.cpp b/common/JackFifo.cpp new file mode 100755 index 00000000..3a072ef3 --- /dev/null +++ b/common/JackFifo.cpp @@ -0,0 +1,209 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackFifo.h" +#include "JackError.h" +#include "JackChannel.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <assert.h> + +namespace Jack +{ + +void JackFifo::BuildName(const char* name, char* res) +{ + sprintf(res, "%s/jack_fifo.%s", jack_client_dir, name); +} + +bool JackFifo::Signal() +{ + bool res; + char c = 0; + assert(fFifo >= 0); + + if (fFlush) + return true; + + if ((res = (write(fFifo, &c, sizeof(c)) != sizeof(c)))) { + jack_error("JackFifo::Signal name = %s err = %s", fName, strerror(errno)); + } + return !res; +} + +bool JackFifo::SignalAll() +{ + bool res; + char c = 0; + assert(fFifo >= 0); + + if (fFlush) + return true; + + if ((res = (write(fFifo, &c, sizeof(c)) != sizeof(c)))) { + jack_error("JackFifo::SignalAll name = %s err = %s", fName, strerror(errno)); + } + return !res; +} + +bool JackFifo::Wait() +{ + bool res; + char c; + assert(fFifo >= 0); + + if ((res = (read(fFifo, &c, sizeof(c)) != sizeof(c)))) { + jack_error("JackFifo::Wait name = %s err = %s", fName, strerror(errno)); + } + return !res; +} + +#ifdef __APPLE__ +#warning JackFifo::TimedWait not available : synchronous mode may not work correctly if FIFO are used +bool JackFifo::TimedWait(long usec) +{ + return Wait(); +} +#else +// Does not work on OSX ?? +bool JackFifo::TimedWait(long usec) +{ + assert(fFifo >= 0); + + if ((poll(&fPoll, 1, usec / 1000) < 0) && (errno != EINTR)) { + jack_error("JackFifo::TimedWait name = %s err = %s", fName, strerror(errno)); + return false; + } + + if (fPoll.revents & POLLIN) { + return Wait(); + } else { + jack_error("JackFifo::TimedWait fails name = %s revents %ld ", fName, fPoll.revents); + return false; + } +} +#endif + +// Server side +bool JackFifo::Allocate(const char* name, int value) +{ + struct stat statbuf; + BuildName(name, fName); + + JackLog("JackFifo::Allocate name = %s\n", fName); + + if (stat(fName, &statbuf)) { + if (errno == ENOENT) { + if (mkfifo(fName, 0666) < 0) { + jack_error("Cannot create inter-client FIFO [%s] (%s)\n", name, strerror(errno)); + return false; + } + } else { + jack_error("Cannot check on FIFO %s\n", name); + return false; + } + } else { + if (!S_ISFIFO(statbuf.st_mode)) { + jack_error("FIFO (%s) already exists, but is not a FIFO!\n", name); + return false; + } + } + + if ((fFifo = open(fName, O_RDWR | O_CREAT, 0666)) < 0) { + jack_error("Cannot open fifo [%s] (%s)", name, strerror(errno)); + return false; + } else { + fPoll.fd = fFifo; + fPoll.events = POLLERR | POLLIN | POLLHUP | POLLNVAL; + return true; + } +} + +// Client side +bool JackFifo::ConnectAux(const char* name, int access) +{ + BuildName(name, fName); + JackLog("JackFifo::ConnectAux name = %s\n", fName); + + // Temporary... + if (fFifo >= 0) { + JackLog("Already connected name = %s\n", name); + return true; + } + + if ((fFifo = open(fName, access)) < 0) { + jack_error("Connect: can't connect named fifo name = %s err = %s", fName, strerror(errno)); + return false; + } else { + fPoll.fd = fFifo; + fPoll.events = POLLERR | POLLIN | POLLHUP | POLLNVAL; + return true; + } +} + +bool JackFifo::Connect(const char* name) +{ + return ConnectAux(name, O_RDWR); +} + +bool JackFifo::ConnectOutput(const char* name) +{ + return ConnectAux(name, O_WRONLY | O_NONBLOCK); +} + +bool JackFifo::ConnectInput(const char* name) +{ + return ConnectAux(name, O_RDONLY); +} + +bool JackFifo::Disconnect() +{ + if (fFifo >= 0) { + JackLog("JackFifo::Disconnect %s\n", fName); + if (close(fFifo) != 0) { + jack_error("Disconnect: can't disconnect named fifo name = %s err = %s", fName, strerror(errno)); + return false; + } else { + fFifo = -1; + return true; + } + } else { + return true; + } +} + +// Server side : destroy the fifo +void JackFifo::Destroy() +{ + if (fFifo > 0) { + JackLog("JackFifo::Destroy name = %s\n", fName); + unlink(fName); + if (close(fFifo) != 0) { + jack_error("Destroy: can't destroy fifo name = %s err = %s", fName, strerror(errno)); + } + fFifo = -1; + } else { + jack_error("JackFifo::Destroy fifo < 0"); + } +} + +} // end of namespace + diff --git a/common/JackFifo.h b/common/JackFifo.h new file mode 100755 index 00000000..e91d212a --- /dev/null +++ b/common/JackFifo.h @@ -0,0 +1,73 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackFifo__ +#define __JackFifo__ + +#include "JackSynchro.h" +#include <assert.h> +#include <stdio.h> +#include <poll.h> + +namespace Jack +{ + +/*! +\brief Inter process synchronization using Fifo. +*/ + +class JackFifo : public JackSynchro +{ + + private: + + int fFifo; + pollfd fPoll; + + bool ConnectAux(const char* name, int access); + + protected: + + void BuildName(const char* name, char* res); + + public: + + JackFifo(): JackSynchro(), fFifo( -1) + {} + virtual ~JackFifo() + {} + + bool Signal(); + bool SignalAll(); + bool Wait(); + bool TimedWait(long usec); + + bool Allocate(const char* name, int value); + bool Connect(const char* name); + bool ConnectInput(const char* name); + bool ConnectOutput(const char* name); + bool Disconnect(); + void Destroy(); +}; + +} // end of namespace + + +#endif + diff --git a/common/JackFrameTimer.cpp b/common/JackFrameTimer.cpp new file mode 100644 index 00000000..b0f4db2f --- /dev/null +++ b/common/JackFrameTimer.cpp @@ -0,0 +1,109 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackFrameTimer.h" +#include "JackError.h" +#include <math.h> + +namespace Jack +{ + +JackTimer::JackTimer() +{ + fInitialized = false; + fFrames = 0; + fCurrentWakeup = 0; + fCurrentCallback = 0; + fNextWakeUp = 0; + fFilterCoefficient = 0.01f; + fSecondOrderIntegrator = 0.0f; +} + +void JackFrameTimer::Init() +{ + fFirstWakeUp = true; +} + +void JackFrameTimer::IncFrameTime(jack_nframes_t nframes, jack_time_t callback_usecs, jack_time_t period_usecs) +{ + if (fFirstWakeUp) { + InitFrameTime(callback_usecs, period_usecs); + fFirstWakeUp = false; + } else { + IncFrameTimeAux(nframes, callback_usecs, period_usecs); + } +} + +void JackFrameTimer::InitFrameTime(jack_time_t callback_usecs, jack_time_t period_usecs) +{ + JackTimer* timer = WriteNextStateStart(); + timer->fSecondOrderIntegrator = 0.0f; + timer->fCurrentCallback = callback_usecs; + timer->fNextWakeUp = callback_usecs + period_usecs; + WriteNextStateStop(); + TrySwitchState(); +} + +void JackFrameTimer::ResetFrameTime(jack_nframes_t frames_rate, jack_time_t callback_usecs, jack_time_t period_usecs) +{ + if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle + JackTimer* timer = WriteNextStateStart(); + jack_nframes_t period_size_guess = (jack_nframes_t)(frames_rate * ((timer->fNextWakeUp - timer->fCurrentWakeup) / 1000000.0)); + timer->fFrames += ((callback_usecs - timer->fNextWakeUp) / period_size_guess) * period_size_guess; + timer->fCurrentWakeup = callback_usecs; + timer->fCurrentCallback = callback_usecs; + timer->fNextWakeUp = callback_usecs + period_usecs; + WriteNextStateStop(); + TrySwitchState(); + } +} + +void JackFrameTimer::IncFrameTimeAux(jack_nframes_t nframes, jack_time_t callback_usecs, jack_time_t period_usecs) +{ + JackTimer* timer = WriteNextStateStart(); + float delta = (int64_t)callback_usecs - (int64_t)timer->fNextWakeUp; + timer->fCurrentWakeup = timer->fNextWakeUp; + timer->fCurrentCallback = callback_usecs; + timer->fFrames += nframes; + timer->fSecondOrderIntegrator += 0.5f * timer->fFilterCoefficient * delta; + timer->fNextWakeUp = timer->fCurrentWakeup + period_usecs + (int64_t) floorf((timer->fFilterCoefficient * (delta + timer->fSecondOrderIntegrator))); + timer->fInitialized = true; + WriteNextStateStop(); + TrySwitchState(); +} + +/* + Use the state returned by ReadCurrentState and check that the state was not changed during the read operation. + The operation is lock-free since there is no intermediate state in the write operation that could cause the + read to loop forever. +*/ +void JackFrameTimer::ReadFrameTime(JackTimer* timer) +{ + UInt16 next_index = GetCurrentIndex(); + UInt16 cur_index; + do { + cur_index = next_index; + memcpy(timer, ReadCurrentState(), sizeof(JackTimer)); + next_index = GetCurrentIndex(); + } while (cur_index != next_index); // Until a coherent state has been read +} + +} // end of namespace + diff --git a/common/JackFrameTimer.h b/common/JackFrameTimer.h new file mode 100644 index 00000000..552bc11e --- /dev/null +++ b/common/JackFrameTimer.h @@ -0,0 +1,81 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackFrameTimer__ +#define __JackFrameTimer__ + +#include "JackAtomicState.h" +#include "types.h" + +namespace Jack +{ + +/*! +\brief A structure used for time management. +*/ + +struct JackTimer +{ + jack_nframes_t fFrames; + jack_time_t fCurrentWakeup; + jack_time_t fCurrentCallback; + jack_time_t fNextWakeUp; + float fSecondOrderIntegrator; + bool fInitialized; + + /* not accessed by clients */ + + float fFilterCoefficient; /* set once, never altered */ + + JackTimer(); + ~JackTimer() + {} + +}; + +/*! +\brief A class using the JackAtomicState to manage jack time. +*/ + +class JackFrameTimer : public JackAtomicState<JackTimer> +{ + private: + + bool fFirstWakeUp; + void IncFrameTimeAux(jack_nframes_t nframes, jack_time_t callback_usecs, jack_time_t period_usecs); + + public: + + JackFrameTimer(): fFirstWakeUp(true) + {} + ~JackFrameTimer() + {} + + void Init(); + void InitFrameTime(jack_time_t callback_usecs, jack_time_t period_usecs); + void ResetFrameTime(jack_nframes_t frames_rate, jack_time_t callback_usecs, jack_time_t period_usecs); + void IncFrameTime(jack_nframes_t nframes, jack_time_t callback_usecs, jack_time_t period_usecs); + void ReadFrameTime(JackTimer* timer); +}; + + +} // end of namespace + +#endif diff --git a/common/JackFreewheelDriver.cpp b/common/JackFreewheelDriver.cpp new file mode 100644 index 00000000..2fbe4233 --- /dev/null +++ b/common/JackFreewheelDriver.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2001 Paul Davis + Copyright (C) 2004-2006 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackFreewheelDriver.h" +#include "JackEngineControl.h" +#include "JackEngine.h" + +namespace Jack +{ + +int JackFreewheelDriver::Process() +{ + if (fIsMaster) { + JackLog("JackFreewheelDriver::Process master %lld\n", fEngineControl->fTimeOutUsecs); + fLastWaitUst = GetMicroSeconds(); + fEngine->Process(fLastWaitUst); + fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); // Signal all clients + if (fGraphManager->SuspendRefNum(fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs * 20) < 0) // Wait for all clients to finish + jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); + } else { + fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); // Signal all clients + if (fEngineControl->fSyncMode) { + if (fGraphManager->SuspendRefNum(fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0) + jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error"); + } + } + return 0; +} + +} // end of namespace diff --git a/common/JackFreewheelDriver.h b/common/JackFreewheelDriver.h new file mode 100644 index 00000000..f2f54d5a --- /dev/null +++ b/common/JackFreewheelDriver.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2001 Paul Davis + Copyright (C) 2004-2006 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackFreewheelDriver__ +#define __JackFreewheelDriver__ + +#include "JackDriver.h" + +namespace Jack +{ + +/*! +\brief The FreeWheel driver : run Jack engine at full speed. +*/ + +class JackFreewheelDriver : public JackDriver +{ + + public: + + JackFreewheelDriver(const char* name, JackEngine* engine, JackSynchro** table): JackDriver(name, engine, table) + {} + virtual ~JackFreewheelDriver() + {} + + bool IsRealTime() const + { + return false; + } + + int Process(); +}; + +} // end of namespace + + +#endif diff --git a/common/JackGlobals.cpp b/common/JackGlobals.cpp new file mode 100644 index 00000000..8b72a9fd --- /dev/null +++ b/common/JackGlobals.cpp @@ -0,0 +1,28 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#include "JackGlobals.h" + +namespace Jack +{ + +JackFactoryImpl* JackGlobals::fInstance; + +} // end of namespace diff --git a/common/JackGlobals.h b/common/JackGlobals.h new file mode 100644 index 00000000..af4a8809 --- /dev/null +++ b/common/JackGlobals.h @@ -0,0 +1,282 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackGlobals__ +#define __JackGlobals__ + +#include "JackError.h" + +namespace Jack +{ + +class JackSynchro; +class JackServerNotifyChannelInterface; +class JackClientChannelInterface; +class JackNotifyChannelInterface; +class JackServerChannelInterface; +class JackSyncInterface; +class JackThread; +class JackDriverClientInterface; +class JackRunnableInterface; +class JackEngine; + +/*! +\brief Factory description + +\totdo possibly use in a dynamic way to test different communication/synchro implementations. +*/ + +class JackFactoryImpl +{ + public: + + JackFactoryImpl() + {} + virtual ~JackFactoryImpl() + {} + + virtual JackSynchro* MakeSynchro() = 0; + virtual JackServerNotifyChannelInterface* MakeServerNotifyChannel() = 0; + virtual JackClientChannelInterface* MakeClientChannel() = 0; + virtual JackNotifyChannelInterface* MakeNotifyChannel() = 0; + virtual JackServerChannelInterface* MakeServerChannel() = 0; + virtual JackSyncInterface* MakeInterProcessSync() = 0; + virtual JackThread* MakeThread(JackRunnableInterface* runnable) = 0; +}; + +#ifdef __linux__ + +class JackFactoryLinuxServer : public JackFactoryImpl +{ + public: + + JackFactoryLinuxServer() + {} + virtual ~JackFactoryLinuxServer() + {} + + JackSynchro* MakeSynchro(); + JackServerNotifyChannelInterface* MakeServerNotifyChannel(); + JackClientChannelInterface* MakeClientChannel(); + JackNotifyChannelInterface* MakeNotifyChannel(); + JackServerChannelInterface* MakeServerChannel(); + JackSyncInterface* MakeInterProcessSync(); + JackThread* MakeThread(JackRunnableInterface* runnable); +}; + +class JackFactoryLinuxClient : public JackFactoryImpl +{ + public: + + JackFactoryLinuxClient() + {} + virtual ~JackFactoryLinuxClient() + {} + + JackSynchro* MakeSynchro(); + JackServerNotifyChannelInterface* MakeServerNotifyChannel(); + JackClientChannelInterface* MakeClientChannel(); + JackNotifyChannelInterface* MakeNotifyChannel(); + JackServerChannelInterface* MakeServerChannel(); + JackSyncInterface* MakeInterProcessSync(); + JackThread* MakeThread(JackRunnableInterface* runnable); +}; + +#endif + +#ifdef WIN32 + +class JackFactoryWindowsServer : public JackFactoryImpl +{ + public: + + JackFactoryWindowsServer() + {} + virtual ~JackFactoryWindowsServer() + {} + + JackSynchro* MakeSynchro(); + JackServerNotifyChannelInterface* MakeServerNotifyChannel(); + JackClientChannelInterface* MakeClientChannel(); + JackNotifyChannelInterface* MakeNotifyChannel(); + JackServerChannelInterface* MakeServerChannel(); + JackSyncInterface* MakeInterProcessSync(); + JackThread* MakeThread(JackRunnableInterface* runnable); +}; + +class JackFactoryWindowsClient : public JackFactoryImpl +{ + public: + + JackFactoryWindowsClient() + {} + virtual ~JackFactoryWindowsClient() + {} + + JackSynchro* MakeSynchro(); + JackServerNotifyChannelInterface* MakeServerNotifyChannel(); + JackClientChannelInterface* MakeClientChannel(); + JackNotifyChannelInterface* MakeNotifyChannel(); + JackServerChannelInterface* MakeServerChannel(); + JackSyncInterface* MakeInterProcessSync(); + JackThread* MakeThread(JackRunnableInterface* runnable); +}; + +#endif + +#if defined(__APPLE__) + +class JackFactoryOSXServer : public JackFactoryImpl +{ + public: + + JackFactoryOSXServer() + {} + virtual ~JackFactoryOSXServer() + {} + + JackSynchro* MakeSynchro(); + JackServerNotifyChannelInterface* MakeServerNotifyChannel(); + JackClientChannelInterface* MakeClientChannel(); + JackNotifyChannelInterface* MakeNotifyChannel(); + JackServerChannelInterface* MakeServerChannel(); + JackSyncInterface* MakeInterProcessSync(); + JackThread* MakeThread(JackRunnableInterface* runnable); +}; + +class JackFactoryOSXClient : public JackFactoryImpl +{ + public: + + JackFactoryOSXClient() + {} + virtual ~JackFactoryOSXClient() + {} + + JackSynchro* MakeSynchro(); + JackServerNotifyChannelInterface* MakeServerNotifyChannel(); + JackClientChannelInterface* MakeClientChannel(); + JackNotifyChannelInterface* MakeNotifyChannel(); + JackServerChannelInterface* MakeServerChannel(); + JackSyncInterface* MakeInterProcessSync(); + JackThread* MakeThread(JackRunnableInterface* runnable); +}; + +#endif + +/*! +\brief Factory for OS specific ressources. +*/ + +class JackGlobals +{ + private: + + static JackFactoryImpl* fInstance; + + public: + + JackGlobals() + {} + virtual ~JackGlobals() + {} + + static JackSynchro* MakeSynchro() + { + return fInstance->MakeSynchro(); + } + static JackServerNotifyChannelInterface* MakeServerNotifyChannel() + { + return fInstance->MakeServerNotifyChannel(); + } + static JackClientChannelInterface* MakeClientChannel() + { + return fInstance->MakeClientChannel(); + } + static JackNotifyChannelInterface* MakeNotifyChannel() + { + return fInstance->MakeNotifyChannel(); + } + static JackServerChannelInterface* MakeServerChannel() + { + return fInstance->MakeServerChannel(); + } + static JackSyncInterface* MakeInterProcessSync() + { + return fInstance->MakeInterProcessSync(); + } + static JackThread* MakeThread(JackRunnableInterface* runnable) + { + return fInstance->MakeThread(runnable); + } + + static void InitServer() + { + JackLog("JackGlobals InitServer\n"); + if (!fInstance) { + +#ifdef __APPLE__ + fInstance = new JackFactoryOSXServer(); +#endif + +#ifdef WIN32 + fInstance = new JackFactoryWindowsServer(); +#endif + +#ifdef __linux__ + fInstance = new JackFactoryLinuxServer(); +#endif + + } + } + + static void InitClient() + { + JackLog("JackGlobals InitClient\n"); + if (!fInstance) { + +#ifdef __APPLE__ + fInstance = new JackFactoryOSXClient(); +#endif + +#ifdef WIN32 + fInstance = new JackFactoryWindowsClient(); +#endif + +#ifdef __linux__ + fInstance = new JackFactoryLinuxClient(); +#endif + + } + } + + static void Destroy() + { + JackLog("JackGlobals Destroy\n"); + if (fInstance) { + delete fInstance; + fInstance = NULL; + } + } + +}; + +} // end of namespace + +#endif diff --git a/common/JackGlobalsClient.cpp b/common/JackGlobalsClient.cpp new file mode 100644 index 00000000..4f168917 --- /dev/null +++ b/common/JackGlobalsClient.cpp @@ -0,0 +1,460 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackGlobals.h" + +// OSX +#if defined(__APPLE__) +#include "JackCoreAudioDriver.h" +#include "JackMachServerNotifyChannel.h" +#include "JackMachNotifyChannel.h" +#include "JackMachServerChannel.h" +#include "JackMachClientChannel.h" +#include "JackMachThread.h" +#include "JackMachSemaphore.h" +#include "JackProcessSync.h" +#include "JackSocketServerNotifyChannel.h" +#include "JackSocketNotifyChannel.h" +#include "JackSocketServerChannel.h" +#include "JackSocketClientChannel.h" +#include "JackPosixThread.h" +#include "JackPosixSemaphore.h" +#include "JackFifo.h" +#endif + +// WINDOWS +#ifdef WIN32 +#include "JackWinProcessSync.h" +#include "JackWinNamedPipeClientChannel.h" +#include "JackWinEvent.h" +#include "JackWinThread.h" +#endif + +// LINUX +#ifdef __linux__ +#include "JackAlsaDriver.h" +#include "JackProcessSync.h" +#include "JackSocketServerNotifyChannel.h" +#include "JackSocketNotifyChannel.h" +#include "JackSocketServerChannel.h" +#include "JackSocketClientChannel.h" +#include "JackPosixThread.h" +#include "JackPosixSemaphore.h" +#include "JackFifo.h" +#endif + + +using namespace std; + +namespace Jack +{ + +#ifdef WIN32 + +JackSynchro* JackFactoryWindowsClient::MakeSynchro() +{ + return new JackWinEvent(); +} + +JackServerNotifyChannelInterface* JackFactoryWindowsClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryWindowsClient::MakeClientChannel() +{ + return new JackWinNamedPipeClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryWindowsClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryWindowsClient::MakeServerChannel() +{ + return NULL; +} +// Not used +JackSyncInterface* JackFactoryWindowsClient::MakeInterProcessSync() +{ + return new JackWinProcessSync(); +} + +JackThread* JackFactoryWindowsClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackWinThread(runnable); +} +#endif + +#ifdef __linux__ + +#if defined(SOCKET_RPC_POSIX_SEMA) +JackSynchro* JackFactoryLinuxClient::MakeSynchro() +{ + return new JackPosixSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryLinuxClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryLinuxClient::MakeClientChannel() +{ + return new JackSocketClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryLinuxClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryLinuxClient::MakeServerChannel() +{ + return NULL; +} +// Not used +JackSyncInterface* JackFactoryLinuxClient::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryLinuxClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(SOCKET_RPC_FIFO_SEMA) +JackSynchro* JackFactoryLinuxClient::MakeSynchro() +{ + return new JackFifo(); +} + +JackServerNotifyChannelInterface* JackFactoryLinuxClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryLinuxClient::MakeClientChannel() +{ + return new JackSocketClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryLinuxClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryLinuxClient::MakeServerChannel() +{ + return NULL; +} +// Not used +JackSyncInterface* JackFactoryLinuxClient::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryLinuxClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(SOCKET_RPC_FIFO_SEMA_DUMMY) +JackSynchro* JackFactoryLinuxClient::MakeSynchro() +{ + return new JackFifo(); +} + +JackServerNotifyChannelInterface* JackFactoryLinuxClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryLinuxClient::MakeClientChannel() +{ + return new JackSocketClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryLinuxClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryLinuxClient::MakeServerChannel() +{ + return NULL; +} +// Not used +JackSyncInterface* JackFactoryLinuxClient::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryLinuxClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#endif + +#if defined(__APPLE__) + +#if defined(MACH_RPC_MACH_SEMA) +// Mach RPC + Mach Semaphore +JackSynchro* JackFactoryOSXClient::MakeSynchro() +{ + return new JackMachSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel() +{ + return new JackMachClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel() +{ + return NULL; +} // Not used + +JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(SOCKET_RPC_POSIX_SEMA) +// Socket RPC + Posix Semaphore +JackSynchro* JackFactoryOSXClient::MakeSynchro() +{ + return new JackPosixSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel() +{ + return new JackSocketClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel() +{ + return NULL; +} +// Not used +JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(SOCKET_RPC_FIFO_SEMA) +// Socket RPC + Fifo Semaphore +JackSynchro* JackFactoryOSXClient::MakeSynchro() +{ + return new JackFifo(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel() +{ + return new JackSocketClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel() +{ + return NULL; +} +// Not used +JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} + +JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable, int priority) +{ + return new JackMachThread(runnable, false, priority, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(MACH_RPC_FIFO_SEMA) +// Mach RPC + Fifo Semaphore +JackSynchro* JackFactoryOSXClient::MakeSynchro() +{ + return new JackFifo(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel() +{ + return new JackMachClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel() +{ + return NULL; +} +// Not used +JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(MACH_RPC_POSIX_SEMA) +// Mach RPC + Posix Semaphore +JackSynchro* JackFactoryOSXClient::MakeSynchro() +{ + return new JackPosixSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel() +{ + return new JackMachClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel() +{ + return NULL; +} +// Not used +JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined SOCKET_RPC_MACH_SEMA +// Socket RPC + Mach Semaphore +JackSynchro* JackFactoryOSXClient::MakeSynchro() +{ + return new JackMachSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel() +{ + return NULL; +} +// Not used +JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel() +{ + return new JackSocketClientChannel(); +} + +JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel() +{ + return NULL; +} +// Not used +JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel() +{ + return NULL; +} +// Not used +JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#endif + +} // end of namespace + + diff --git a/common/JackGlobalsServer.cpp b/common/JackGlobalsServer.cpp new file mode 100644 index 00000000..79f9ef64 --- /dev/null +++ b/common/JackGlobalsServer.cpp @@ -0,0 +1,460 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackGlobals.h" + +// OSX +#if defined(__APPLE__) +#include "JackCoreAudioDriver.h" +#include "JackMachServerNotifyChannel.h" +#include "JackMachNotifyChannel.h" +#include "JackMachServerChannel.h" +#include "JackMachClientChannel.h" +#include "JackMachThread.h" +#include "JackMachSemaphore.h" +#include "JackProcessSync.h" + +#include "JackSocketServerNotifyChannel.h" +#include "JackSocketNotifyChannel.h" +#include "JackSocketServerChannel.h" +#include "JackSocketClientChannel.h" +#include "JackPosixThread.h" +#include "JackPosixSemaphore.h" +#include "JackFifo.h" +#endif + +// WINDOWS +#ifdef WIN32 +#include "JackWinProcessSync.h" +#include "JackWinNamedPipeServerNotifyChannel.h" +#include "JackWinNamedPipeNotifyChannel.h" +#include "JackWinNamedPipeServerChannel.h" +#include "JackWinNamedPipeClientChannel.h" +#include "JackWinEvent.h" +#include "JackWinThread.h" +#endif + +// LINUX +#ifdef __linux__ +#include "JackAlsaDriver.h" +#include "JackProcessSync.h" +#include "JackSocketServerNotifyChannel.h" +#include "JackSocketNotifyChannel.h" +#include "JackSocketServerChannel.h" +#include "JackSocketClientChannel.h" +#include "JackPosixThread.h" +#include "JackPosixSemaphore.h" +#include "JackFifo.h" +#endif + +// COMMON +#include "JackDummyDriver.h" +#include "JackAudioDriver.h" + + +using namespace std; + +namespace Jack +{ + +#ifdef WIN32 +JackSynchro* JackFactoryWindowsServer::MakeSynchro() +{ + return new JackWinEvent(); +} + +JackServerNotifyChannelInterface* JackFactoryWindowsServer::MakeServerNotifyChannel() +{ + return new JackWinNamedPipeServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryWindowsServer::MakeClientChannel() +{ + return NULL; +} // Not used + +JackNotifyChannelInterface* JackFactoryWindowsServer::MakeNotifyChannel() +{ + return new JackWinNamedPipeNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryWindowsServer::MakeServerChannel() +{ + return new JackWinNamedPipeServerChannel(); +} + +JackSyncInterface* JackFactoryWindowsServer::MakeInterProcessSync() +{ + return new JackWinProcessSync(); +} + +JackThread* JackFactoryWindowsServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackWinThread(runnable); +} +#endif + +#ifdef __linux__ + +#if defined(SOCKET_RPC_POSIX_SEMA) +JackSynchro* JackFactoryLinuxServer::MakeSynchro() +{ + return new JackPosixSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryLinuxServer::MakeServerNotifyChannel() +{ + return new JackSocketServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryLinuxServer::MakeClientChannel() +{ + return NULL; +} // Not used + +JackNotifyChannelInterface* JackFactoryLinuxServer::MakeNotifyChannel() +{ + return new JackSocketNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryLinuxServer::MakeServerChannel() +{ + return new JackSocketServerChannel(); +} + +JackSyncInterface* JackFactoryLinuxServer::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryLinuxServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(SOCKET_RPC_FIFO_SEMA) +JackSynchro* JackFactoryLinuxServer::MakeSynchro() +{ + return new JackFifo(); +} + +JackServerNotifyChannelInterface* JackFactoryLinuxServer::MakeServerNotifyChannel() +{ + return new JackSocketServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryLinuxServer::MakeClientChannel() +{ + return NULL; +} +// Not used +JackNotifyChannelInterface* JackFactoryLinuxServer::MakeNotifyChannel() +{ + return new JackSocketNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryLinuxServer::MakeServerChannel() +{ + return new JackSocketServerChannel(); +} + +JackSyncInterface* JackFactoryLinuxServer::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryLinuxServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(SOCKET_RPC_FIFO_SEMA_DUMMY) +JackSynchro* JackFactoryLinuxServer::MakeSynchro() +{ + return new JackFifo(); +} + +JackServerNotifyChannelInterface* JackFactoryLinuxServer::MakeServerNotifyChannel() +{ + return new JackSocketServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryLinuxServer::MakeClientChannel() +{ + return NULL; +} +// Not used +JackNotifyChannelInterface* JackFactoryLinuxServer::MakeNotifyChannel() +{ + return new JackSocketNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryLinuxServer::MakeServerChannel() +{ + return new JackSocketServerChannel(); +} + +JackSyncInterface* JackFactoryLinuxServer::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryLinuxServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#endif + +#if defined(__APPLE__) + +#if defined(MACH_RPC_MACH_SEMA) +// Mach RPC + Mach Semaphore +JackSynchro* JackFactoryOSXServer::MakeSynchro() +{ + return new JackMachSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel() +{ + return new JackMachServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel() +{ + return NULL; +} // Not used + +JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel() +{ + return new JackMachNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel() +{ + return new JackMachServerChannel(); +} + +JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(SOCKET_RPC_POSIX_SEMA) +// Socket RPC + Posix Semaphore +JackSynchro* JackFactoryOSXServer::MakeSynchro() +{ + return new JackPosixSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel() +{ + return new JackSocketServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel() +{ + return NULL; +} +// Not used +JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel() +{ + return new JackSocketNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel() +{ + return new JackSocketServerChannel(); +} + +JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(SOCKET_RPC_FIFO_SEMA) +// Socket RPC + Fifo Semaphore +JackSynchro* JackFactoryOSXServer::MakeSynchro() +{ + return new JackFifo(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel() +{ + return new JackSocketServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel() +{ + return NULL; +} +// Not used +JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel() +{ + return new JackSocketNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel() +{ + return new JackSocketServerChannel(); +} + +JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(MACH_RPC_FIFO_SEMA) +// Mach RPC + Fifo Semaphore +JackSynchro* JackFactoryOSXServer::MakeSynchro() +{ + return new JackFifo(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel() +{ + return new JackMachServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel() +{ + return NULL; +} +// Not used +JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel() +{ + return new JackMachNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel() +{ + return new JackMachServerChannel(); +} + +JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(MACH_RPC_POSIX_SEMA) +// Mach RPC + Posix Semaphore +JackSynchro* JackFactoryOSXServer::MakeSynchro() +{ + return new JackPosixSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel() +{ + return new JackMachServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel() +{ + return NULL; +} +// Not used +JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel() +{ + return new JackMachNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel() +{ + return new JackMachServerChannel(); +} + +JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#if defined(SOCKET_RPC_MACH_SEMA) +// Socket RPC + Mac Semaphore +JackSynchro* JackFactoryOSXServer::MakeSynchro() +{ + return new JackMachSemaphore(); +} + +JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel() +{ + return new JackSocketServerNotifyChannel(); +} + +JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel() +{ + return NULL; +} +// Not used +JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel() +{ + return new JackSocketNotifyChannel(); +} + +JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel() +{ + return new JackSocketServerChannel(); +} + +JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync() +{ + return new JackProcessSync(); +} + +JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable) +{ + return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS); +} +#endif + +#endif + +} // end of namespace diff --git a/common/JackGraphManager.cpp b/common/JackGraphManager.cpp new file mode 100644 index 00000000..ee32d8b5 --- /dev/null +++ b/common/JackGraphManager.cpp @@ -0,0 +1,777 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "JackGraphManager.h" +#include "JackConstants.h" +#include <assert.h> +#include <stdlib.h> +#include <algorithm> +#include <regex.h> + +namespace Jack +{ + +static inline jack_nframes_t MAX(jack_nframes_t a, jack_nframes_t b) +{ + return (a < b) ? b : a; +} + +static void AssertPort(jack_port_id_t port_index) +{ + if (port_index >= PORT_NUM) { + JackLog("JackGraphManager::AssertPort port_index = %ld\n", port_index); + assert(port_index < PORT_NUM); + } +} + +static void AssertBufferSize(jack_nframes_t frames) +{ + if (frames > BUFFER_SIZE_MAX) { + JackLog("JackGraphManager::AssertBufferSize frames = %ld\n", frames); + assert(frames <= BUFFER_SIZE_MAX); + } +} + +JackPort* JackGraphManager::GetPort(jack_port_id_t port_index) +{ + AssertPort(port_index); + return &fPortArray[port_index]; +} + +float* JackGraphManager::GetBuffer(jack_port_id_t port_index) +{ + return fPortArray[port_index].GetBuffer(); +} + +// RT, client +int JackGraphManager::GetConnectionsNum(jack_port_id_t port_index) +{ + JackConnectionManager* manager = ReadCurrentState(); + return manager->Connections(port_index); +} + +// Server +int JackGraphManager::AllocateRefNum() +{ + JackConnectionManager* manager = WriteNextStateStart(); + int res = manager->AllocateRefNum(); + WriteNextStateStop(); + return res; +} + +// Server +void JackGraphManager::ReleaseRefNum(int refnum) +{ + JackConnectionManager* manager = WriteNextStateStart(); + manager->ReleaseRefNum(refnum); + WriteNextStateStop(); +} + +// RT +void JackGraphManager::RunCurrentGraph() +{ + JackConnectionManager* manager = ReadCurrentState(); + manager->ResetGraph(fClientTiming); +} + +// RT +bool JackGraphManager::RunNextGraph() +{ + bool res; + JackConnectionManager* manager = TrySwitchState(&res); + manager->ResetGraph(fClientTiming); + return res; +} + +// RT +bool JackGraphManager::IsFinishedGraph() +{ + JackConnectionManager* manager = ReadCurrentState(); + return (manager->GetActivation(FREEWHEEL_DRIVER_REFNUM) == 0); +} + +// RT +int JackGraphManager::ResumeRefNum(JackClientControl* control, JackSynchro** table) +{ + JackConnectionManager* manager = ReadCurrentState(); + return manager->ResumeRefNum(control, table, fClientTiming); +} + +// RT +int JackGraphManager::SuspendRefNum(JackClientControl* control, JackSynchro** table, long usec) +{ + JackConnectionManager* manager = ReadCurrentState(); + return manager->SuspendRefNum(control, table, fClientTiming, usec); +} + +JackClientTiming* JackGraphManager::GetClientTiming(int ref) +{ + return &fClientTiming[ref]; +} + +// Server +void JackGraphManager::DirectConnect(int ref1, int ref2) +{ + JackConnectionManager* manager = WriteNextStateStart(); + manager->DirectConnect(ref1, ref2); + JackLog("JackGraphManager::ConnectRefNum cur_index = %ld ref1 = %ld ref2 = %ld\n", CurIndex(fCounter), ref1, ref2); + WriteNextStateStop(); +} + +// Server +void JackGraphManager::DirectDisconnect(int ref1, int ref2) +{ + JackConnectionManager* manager = WriteNextStateStart(); + manager->DirectDisconnect(ref1, ref2); + JackLog("JackGraphManager::DisconnectRefNum cur_index = %ld ref1 = %ld ref2 = %ld\n", CurIndex(fCounter), ref1, ref2); + WriteNextStateStop(); +} + +// Server +bool JackGraphManager::IsDirectConnection(int ref1, int ref2) +{ + JackConnectionManager* manager = ReadCurrentState(); + return manager->IsDirectConnection(ref1, ref2); +} + +// RT +void* JackGraphManager::GetBuffer(jack_port_id_t port_index, jack_nframes_t frames) +{ + AssertPort(port_index); + AssertBufferSize(frames); + + JackConnectionManager* manager = ReadCurrentState(); + JackPort* port = GetPort(port_index); + + if (!port->IsUsed()) { + // This happens when a port has just been unregistered and is still used by the RT code. + JackLog("JackGraphManager::GetBuffer : port = %ld is released state\n", port_index); + return GetBuffer(0); // port_index 0 is not used + } + + // Output port + if (port->fFlags & JackPortIsOutput) { + return (port->fTied != NO_PORT) ? GetBuffer(port->fTied, frames) : GetBuffer(port_index); + } + + // Input port + jack_int_t len = manager->Connections(port_index); + + if (len == 0) { // No connections: return a zero-filled buffer + float* buffer = GetBuffer(port_index); + memset(buffer, 0, frames * sizeof(float)); // Clear buffer + return buffer; + } else if (len == 1) { // One connection: use zero-copy mode - just pass the buffer of the connected (output) port. + assert(manager->GetPort(port_index, 0) != port_index); // Check recursion + return GetBuffer(manager->GetPort(port_index, 0), frames); + } else { // Multiple connections + const jack_int_t* connections = manager->GetConnections(port_index); + float* mixbuffer = GetBuffer(port_index); + jack_port_id_t src_index; + float* buffer; + + // Copy first buffer + src_index = connections[0]; + AssertPort(src_index); + buffer = (float*)GetBuffer(src_index, frames); + memcpy(mixbuffer, buffer, frames * sizeof(float)); + + // Mix remaining buffers + for (int i = 1; (i < CONNECTION_NUM) && ((src_index = connections[i]) != EMPTY); i++) { + AssertPort(src_index); + buffer = (float*)GetBuffer(src_index, frames); + JackPort::MixBuffer(mixbuffer, buffer, frames); + } + return mixbuffer; + } +} + +int JackGraphManager::RequestMonitor(jack_port_id_t port_index, bool onoff) // Client +{ + AssertPort(port_index); + JackPort* port = GetPort(port_index); + + /** + 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; + */ + + port->RequestMonitor(onoff); + + const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index); + if ((port->fFlags & JackPortIsOutput) == 0) { // ?? Taken from jack, why not (port->fFlags & JackPortIsInput) ? + jack_port_id_t src_index; + for (int i = 0; (i < CONNECTION_NUM) && ((src_index = connections[i]) != EMPTY); i++) { + // XXX much worse things will happen if there is a feedback loop !!! + RequestMonitor(src_index, onoff); + } + } + + return 0; +} + +jack_nframes_t JackGraphManager::GetTotalLatencyAux(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); + jack_nframes_t latency = GetPort(port_index)->GetLatency(); + jack_nframes_t max_latency = 0; + jack_port_id_t dst_index; + + if (hop_count > 8) + return latency; + + for (int i = 0; (i < CONNECTION_NUM) && ((dst_index = connections[i]) != EMPTY); i++) { + if (src_port_index != dst_index) { + AssertPort(dst_index); + JackPort* dst_port = GetPort(dst_index); + jack_nframes_t this_latency = (dst_port->fFlags & JackPortIsTerminal) + ? dst_port->GetLatency() + : GetTotalLatencyAux(dst_index, port_index, manager, hop_count + 1); + max_latency = MAX(max_latency, this_latency); + } + } + + return max_latency + latency; +} + +jack_nframes_t JackGraphManager::GetTotalLatency(jack_port_id_t port_index) +{ + UInt16 cur_index; + UInt16 next_index; + jack_nframes_t total_latency; + AssertPort(port_index); + JackLog("JackGraphManager::GetTotalLatency port_index = %ld\n", port_index); + + do { + cur_index = GetCurrentIndex(); + total_latency = GetTotalLatencyAux(port_index, port_index, ReadCurrentState(), 0); + next_index = GetCurrentIndex(); + } while (cur_index != next_index); // Until a coherent state has been read + + return total_latency; +} + +// Server +jack_port_id_t JackGraphManager::AllocatePortAux(int refnum, const char* port_name, JackPortFlags flags) +{ + jack_port_id_t port_index; + + // Look for first available port Port index start at 1, otherwise a port_index of 0 is "seen" as a NULL port by the external API... + for (port_index = 1; port_index < PORT_NUM; port_index++) { + JackPort* port = GetPort(port_index); + if (!port->IsUsed()) { + JackLog("JackGraphManager::AllocatePortAux port_index = %ld name = %s\n", port_index, port_name); + port->Allocate(refnum, port_name, flags); + break; + } + } + + return (port_index < PORT_NUM) ? port_index : NO_PORT; +} + +// Server +jack_port_id_t JackGraphManager::AllocatePort(int refnum, const char* port_name, JackPortFlags flags) +{ + JackConnectionManager* manager = WriteNextStateStart(); + jack_port_id_t port_index = AllocatePortAux(refnum, port_name, flags); + + if (port_index != NO_PORT) { + int res; + if (flags & JackPortIsOutput) { + res = manager->AddOutputPort(refnum, port_index); + } else { + res = manager->AddInputPort(refnum, port_index); + } + if (res < 0) { + JackPort* port = GetPort(port_index); + assert(port); + port->Release(); + port_index = NO_PORT; + } + } + + WriteNextStateStop(); + return port_index; +} + +// Server +void JackGraphManager::ReleasePort(jack_port_id_t port_index) +{ + JackPort* port = GetPort(port_index); + port->Release(); +} + +// Server +int JackGraphManager::RemovePort(int refnum, jack_port_id_t port_index) +{ + JackConnectionManager* manager = WriteNextStateStart(); + JackPort* port = GetPort(port_index); + int res; + + if (port->fFlags & JackPortIsOutput) { + DisconnectAllOutput(port_index); + res = manager->RemoveOutputPort(refnum, port_index); + } else { + DisconnectAllInput(port_index); + res = manager->RemoveInputPort(refnum, port_index); + } + + WriteNextStateStop(); + return res; +} + +// Server +void JackGraphManager::RemoveAllPorts(int refnum) +{ + JackLog("JackGraphManager::RemoveAllPorts ref = %ld\n", refnum); + JackConnectionManager* manager = WriteNextStateStart(); + jack_port_id_t port_index; + + // Warning : RemovePort shift port to left, thus we always remove the first port until the "input" table is empty + const jack_int_t* input = manager->GetInputPorts(refnum); + while ((port_index = input[0]) != EMPTY) { + RemovePort(refnum, port_index); + ReleasePort(port_index); + } + + // Warning : RemovePort shift port to left, thus we always remove the first port until the "output" table is empty + const jack_int_t* output = manager->GetOutputPorts(refnum); + while ((port_index = output[0]) != EMPTY) { + RemovePort(refnum, port_index); + ReleasePort(port_index); + } + + WriteNextStateStop(); +} + +// Server +void JackGraphManager::DisconnectAllPorts(int refnum) +{ + int i; + JackLog("JackGraphManager::DisconnectAllPorts ref = %ld\n", refnum); + JackConnectionManager* manager = WriteNextStateStart(); + + const jack_int_t* input = manager->GetInputPorts(refnum); + for (i = 0; i < PORT_NUM_FOR_CLIENT && input[i] != EMPTY ; i++) { + DisconnectAllInput(input[i]); + } + + const jack_int_t* output = manager->GetOutputPorts(refnum); + for (i = 0; i < PORT_NUM_FOR_CLIENT && output[i] != EMPTY; i++) { + DisconnectAllOutput(output[i]); + } + + WriteNextStateStop(); +} + +// Server +void JackGraphManager::DisconnectAllInput(jack_port_id_t port_index) +{ + JackLog("JackGraphManager::DisconnectAllInput port_index = %ld \n", port_index); + JackConnectionManager* manager = WriteNextStateStart(); + + for (int i = 0; i < PORT_NUM; i++) { + if (manager->IsConnected(i, port_index)) { + JackLog("JackGraphManager::Disconnect i = %ld port_index = %ld\n", i, port_index); + Disconnect(i, port_index); + } + } + WriteNextStateStop(); +} + +// Server +void JackGraphManager::DisconnectAllOutput(jack_port_id_t port_index) +{ + JackLog("JackGraphManager::DisconnectAllOutput port_index = %ld \n", port_index); + JackConnectionManager* manager = WriteNextStateStart(); + + const jack_int_t* connections = manager->GetConnections(port_index); + while (connections[0] != EMPTY) { + Disconnect(port_index, connections[0]); // Warning : Disconnect shift port to left + } + WriteNextStateStop(); +} + +// Server +int JackGraphManager::DisconnectAll(jack_port_id_t port_index) +{ + AssertPort(port_index); + + JackPort* port = GetPort(port_index); + if (port->fFlags & JackPortIsOutput) { + DisconnectAllOutput(port_index); + } else { + DisconnectAllInput(port_index); + } + return 0; +} + +// Server +int JackGraphManager::GetInputRefNum(jack_port_id_t port_index) +{ + AssertPort(port_index); + JackConnectionManager* manager = WriteNextStateStart(); + int res = manager->GetInputRefNum(port_index); + WriteNextStateStop(); + return res; +} + +// Server +int JackGraphManager::GetOutputRefNum(jack_port_id_t port_index) +{ + AssertPort(port_index); + JackConnectionManager* manager = WriteNextStateStart(); + int res = manager->GetOutputRefNum(port_index); + WriteNextStateStop(); + return res; +} + +int JackGraphManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst) +{ + JackConnectionManager* manager = WriteNextStateStart(); + JackLog("JackGraphManager::Connect port_src = %ld port_dst = %ld\n", port_src, port_dst); + bool in_use_src = GetPort(port_src)->fInUse; + bool in_use_dst = GetPort(port_src)->fInUse; + int res = 0; + + if (!in_use_src || !in_use_dst) { + if (!in_use_src) + jack_error("JackGraphManager::Connect: port_src not %ld used name = %s", port_src, GetPort(port_src)->fName); + if (!in_use_dst) + jack_error("JackGraphManager::Connect: port_dst not %ld used name = %s", port_dst, GetPort(port_dst)->fName); + res = -1; + goto end; + } + if (manager->IsConnected(port_src, port_dst)) { + jack_error("JackGraphManager::Connect already connected port_src = %ld port_dst = %ld", port_src, port_dst); + res = EEXIST; + goto end; + } + + res = manager->Connect(port_src, port_dst); + if (res < 0) { + jack_error("JackGraphManager::Connect failed port_src = %ld port_dst = %ld", port_src, port_dst); + goto end; + } + manager->Connect(port_dst, port_src); + if (res < 0) { + jack_error("JackGraphManager::Connect failed port_src = %ld port_dst = %ld", port_dst, port_src); + goto end; + } + + if (manager->IsLoopPath(port_src, port_dst)) { + JackLog("JackGraphManager::Connect: LOOP detected\n"); + manager->IncFeedbackConnection(port_src, port_dst); + } else { + manager->IncDirectConnection(port_src, port_dst); + } + +end: + WriteNextStateStop(); + if (res < 0) + jack_error("JackGraphManager::Connect failed port_src = %ld port_dst = %ld", port_dst, port_src); + return res; +} + +// Server +int JackGraphManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst) +{ + JackConnectionManager* manager = WriteNextStateStart(); + JackLog("JackGraphManager::Disconnect port_src = %ld port_dst = %ld\n", port_src, port_dst); + bool in_use_src = GetPort(port_src)->fInUse; + bool in_use_dst = GetPort(port_src)->fInUse; + int res = 0; + + if (!in_use_src || !in_use_dst) { + if (!in_use_src) + jack_error("JackGraphManager::Disconnect: port_src not %ld used name = %s", port_src, GetPort(port_src)->fName); + if (!in_use_dst) + jack_error("JackGraphManager::Disconnect: port_src not %ld used name = %s", port_dst, GetPort(port_dst)->fName); + res = -1; + goto end; + } + if (!manager->IsConnected(port_src, port_dst)) { + jack_error("JackGraphManager::Disconnect not connected port_src = %ld port_dst = %ld", port_src, port_dst); + res = -1; + goto end; + } + + manager->Disconnect(port_src, port_dst); + if (res < 0) { + jack_error("JackGraphManager::Disconnect failed port_src = %ld port_dst = %ld", port_src, port_dst); + goto end; + } + manager->Disconnect(port_dst, port_src); + if (res < 0) { + jack_error("JackGraphManager::Disconnect failed port_src = %ld port_dst = %ld", port_dst, port_src); + goto end; + } + + if (manager->IsFeedbackConnection(port_src, port_dst)) { + JackLog("JackGraphManager::Disconnect: FEEDBACK removed\n"); + manager->DecFeedbackConnection(port_src, port_dst); + } else { + manager->DecDirectConnection(port_src, port_dst); + } + +end: + WriteNextStateStop(); + return res; +} + +// Client +int JackGraphManager::ConnectedTo(jack_port_id_t port_src, const char* port_name) +{ + JackLog("JackGraphManager::ConnectedTo port_src = %ld port_name = %s\n", port_src, port_name); + JackConnectionManager* manager = ReadCurrentState(); + jack_port_id_t port_dst; + + if ((port_dst = GetPort(port_name)) == NO_PORT) { + jack_error("Unknown destination port port_name = %s", port_name); + return 0; + } else { + return manager->IsConnected(port_src, port_dst); + } +} + +// Server +int JackGraphManager::CheckPort(jack_port_id_t port_index) +{ + JackPort* port = GetPort(port_index); + + if (port->fLocked) { + jack_error("Port %s is locked against connection changes", port->fName); + return -1; + } else { + return 0; + } +} + +int JackGraphManager::CheckPorts(jack_port_id_t port_src, jack_port_id_t port_dst) +{ + JackPort* src = GetPort(port_src); + JackPort* dst = GetPort(port_dst); + + if ((dst->Flags() & JackPortIsInput) == 0) { + jack_error("Destination port in attempted (dis)connection of %s and %s is not an input port", src->fName, dst->fName); + return -1; + } + + if ((src->Flags() & JackPortIsOutput) == 0) { + jack_error("Source port in attempted (dis)connection of %s and %s is not an output port", src->fName, dst->fName); + return -1; + } + + if (src->fLocked) { + jack_error("Source port %s is locked against connection changes", src->fName); + return -1; + } + + if (dst->fLocked) { + jack_error("Destination port %s is locked against connection changes", dst->fName); + return -1; + } + + return 0; +} + +int JackGraphManager::CheckPorts(const char* src_name, const char* dst_name, jack_port_id_t* port_src, jack_port_id_t* port_dst) +{ + JackLog("JackGraphManager::CheckConnect src_name = %s dst_name = %s\n", src_name, dst_name); + + if ((*port_src = GetPort(src_name)) == NO_PORT) { + jack_error("Unknown source port in attempted (dis)connection src_name [%s] dst_name [%s]", src_name, dst_name); + return -1; + } + + if ((*port_dst = GetPort(dst_name)) == NO_PORT) { + jack_error("Unknown destination port in attempted (dis)connection src_name [%s] dst_name [%s]", src_name, dst_name); + return -1; + } + + return CheckPorts(*port_src, *port_dst); +} + +// Client : port array +jack_port_id_t JackGraphManager::GetPort(const char* name) +{ + for (int i = 0; i < PORT_NUM; i++) { + JackPort* port = GetPort(i); + if (port->IsUsed() && strcmp(port->fName, name) == 0) + return i; + } + return NO_PORT; +} + +/*! +\brief Get the connection port name array. +*/ + +// Client +void JackGraphManager::GetConnectionsAux(JackConnectionManager* manager, const char** res, jack_port_id_t port_index) +{ + const jack_int_t* connections = manager->GetConnections(port_index); + jack_int_t index; + int i; + + for (i = 0; (i < CONNECTION_NUM) && ((index = connections[i]) != EMPTY) ; i++) { + JackPort* port = GetPort(index); + res[i] = port->fName; + } + + res[i] = NULL; +} + +// Client +/* + Use the state returned by ReadCurrentState and check that the state was not changed during the read operation. + The operation is lock-free since there is no intermediate state in the write operation that could cause the + read to loop forever. +*/ +const char** JackGraphManager::GetConnections(jack_port_id_t port_index) +{ + const char** res = (const char**)malloc(sizeof(char*) * (CONNECTION_NUM + 1)); + UInt16 cur_index; + UInt16 next_index; + AssertPort(port_index); + + do { + cur_index = GetCurrentIndex(); + GetConnectionsAux(ReadCurrentState(), res, port_index); + next_index = GetCurrentIndex(); + } while (cur_index != next_index); // Until a coherent state has been read + + return res; +} + +// Client +const char** JackGraphManager::GetPortsAux(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags) +{ + const char** matching_ports; + unsigned long match_cnt = 0; + regex_t port_regex; + regex_t type_regex; + bool matching; + + if (port_name_pattern && port_name_pattern[0]) { + regcomp(&port_regex, port_name_pattern, REG_EXTENDED | REG_NOSUB); + } + if (type_name_pattern && type_name_pattern[0]) { + regcomp(&type_regex, type_name_pattern, REG_EXTENDED | REG_NOSUB); + } + + matching_ports = (const char**)malloc(sizeof(char*) * PORT_NUM); + + for (int i = 0; i < PORT_NUM; i++) { + matching = true; + + JackPort* port = GetPort(i); + + if (port->IsUsed()) { + + if (flags) { + if ((port->fFlags & flags) != flags) { + matching = false; + } + } + + if (matching && port_name_pattern && port_name_pattern[0]) { + if (regexec(&port_regex, port->fName, 0, NULL, 0)) { + matching = false; + } + } + + /* + if (matching && type_name_pattern && type_name_pattern[0]) { + jack_port_type_id_t ptid = psp[i].ptype_id; + if (regexec (&type_regex,engine->port_types[ptid].type_name,0, NULL, 0)) { + matching = false; + } + } + */ + + // TO BE IMPROVED + if (matching && type_name_pattern && type_name_pattern[0]) { + if (regexec(&type_regex, JACK_DEFAULT_AUDIO_TYPE, 0, NULL, 0)) { + matching = false; + } + } + + if (matching) { + matching_ports[match_cnt++] = port->fName; + } + } + } + + matching_ports[match_cnt] = 0; + + if (match_cnt == 0) { + free(matching_ports); + matching_ports = NULL; + } + + return matching_ports; +} + +// Client +/* + Check that the state was not changed during the read operation. + The operation is lock-free since there is no intermediate state in the write operation that could cause the + read to loop forever. +*/ +const char** JackGraphManager::GetPorts(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags) +{ + const char** matching_ports = NULL; + UInt16 cur_index; + UInt16 next_index; + + do { + cur_index = GetCurrentIndex(); + if (matching_ports) + free(matching_ports); + matching_ports = GetPortsAux(port_name_pattern, type_name_pattern, flags); + next_index = GetCurrentIndex(); + } while (cur_index != next_index); // Until a coherent state has been read + + return matching_ports; +} + +// Server +void JackGraphManager::Save(JackConnectionManager* dst) +{ + JackConnectionManager* manager = WriteNextStateStart(); + memcpy(dst, manager, sizeof(JackConnectionManager)); + WriteNextStateStop(); +} + +// Server +void JackGraphManager::Restore(JackConnectionManager* src) +{ + JackConnectionManager* manager = WriteNextStateStart(); + memcpy(manager, src, sizeof(JackConnectionManager)); + WriteNextStateStop(); +} + +} // end of namespace + + diff --git a/common/JackGraphManager.h b/common/JackGraphManager.h new file mode 100644 index 00000000..1c2a1d5d --- /dev/null +++ b/common/JackGraphManager.h @@ -0,0 +1,121 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackGraphManager__ +#define __JackGraphManager__ + +#include "JackShmMem.h" +#include "JackPort.h" +#include "JackConstants.h" +#include "JackConnectionManager.h" +#include "JackAtomicState.h" + +namespace Jack +{ + +/*! +\brief Graph manager: contains the connection manager and the port array. +*/ + +class JackGraphManager : public JackShmMem, public JackAtomicState<JackConnectionManager> +{ + + private: + + JackPort fPortArray[PORT_NUM]; + JackClientTiming fClientTiming[CLIENT_NUM]; + + jack_port_id_t AllocatePortAux(int refnum, const char* port_name, JackPortFlags flags); + void GetConnectionsAux(JackConnectionManager* manager, const char** res, jack_port_id_t port_index); + const char** GetPortsAux(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); + 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 GetTotalLatencyAux(jack_port_id_t port_index, jack_port_id_t src_port_index, JackConnectionManager* manager, int hop_count); + + public: + + JackGraphManager() + {} + virtual ~JackGraphManager() + {} + + // Ports management + jack_port_id_t AllocatePort(int refnum, const char* port_name, JackPortFlags flags); + void ReleasePort(jack_port_id_t port_index); + JackPort* GetPort(jack_port_id_t index); + jack_port_id_t GetPort(const char* name); + jack_nframes_t GetTotalLatency(jack_port_id_t port_index); + int RequestMonitor(jack_port_id_t port_index, bool onoff); + + // Connections management + int Connect(jack_port_id_t src_index, jack_port_id_t dst_index); + int Disconnect(jack_port_id_t src_index, jack_port_id_t dst_index); + int GetConnectionsNum(jack_port_id_t port_index); + + int ConnectedTo(jack_port_id_t port_src, const char* port_name); + const char** GetConnections(jack_port_id_t port_index); + const char** GetPorts(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); + + int CheckPorts(const char* src, const char* dst, jack_port_id_t* src_index, jack_port_id_t* dst_index); + int CheckPorts(jack_port_id_t port_src, jack_port_id_t port_dst); + int CheckPort(jack_port_id_t port_index); + + void DisconnectAllInput(jack_port_id_t port_index); + void DisconnectAllOutput(jack_port_id_t port_index); + int DisconnectAll(jack_port_id_t port_index); + + // Client management + int AllocateRefNum(); + void ReleaseRefNum(int refnum); + + bool IsDirectConnection(int ref1, int ref2); + void DirectConnect(int ref1, int ref2); + void DirectDisconnect(int ref1, int ref2); + + int RemovePort(int refnum, jack_port_id_t port_index); + void RemoveAllPorts(int refnum); + void DisconnectAllPorts(int refnum); + + int GetInputRefNum(jack_port_id_t port_index); + int GetOutputRefNum(jack_port_id_t port_index); + + // Buffer management + void* GetBuffer(jack_port_id_t port_index, jack_nframes_t frames); + + // Activation management + void RunCurrentGraph(); + bool RunNextGraph(); + bool IsFinishedGraph(); + + int ResumeRefNum(JackClientControl* control, JackSynchro** table); + int SuspendRefNum(JackClientControl* control, JackSynchro** table, long usecs); + + JackClientTiming* GetClientTiming(int ref); + + void Save(JackConnectionManager* dst); + void Restore(JackConnectionManager* src); + +}; + + +} // end of namespace + +#endif + diff --git a/common/JackInternalClient.cpp b/common/JackInternalClient.cpp new file mode 100644 index 00000000..d9a0a80b --- /dev/null +++ b/common/JackInternalClient.cpp @@ -0,0 +1,105 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackInternalClient.h" +#include "JackEngine.h" +#include "JackServer.h" +#include "JackGraphManager.h" +#include "JackEngineControl.h" +#include "JackClientControl.h" +#include "JackInternalClientChannel.h" +#include <assert.h> + + +namespace Jack +{ + +JackGraphManager* JackInternalClient::fGraphManager = NULL; +JackEngineControl* JackInternalClient::fEngineControl = NULL; + +// Used for external C API (JackAPI.cpp) +JackGraphManager* GetGraphManager() +{ + return JackServer::fInstance->GetGraphManager(); +} + +JackEngineControl* GetEngineControl() +{ + return JackServer::fInstance->GetEngineControl(); +} + +JackSynchro** GetSynchroTable() +{ + return JackServer::fInstance->GetSynchroTable(); +} + +JackInternalClient::JackInternalClient(JackServer* server, JackSynchro** table): JackClient(table) +{ + fClientControl = new JackClientControl(); + fChannel = new JackInternalClientChannel(server); +} + +JackInternalClient::~JackInternalClient() +{ + delete fClientControl; + delete fChannel; +} + +int JackInternalClient::Open(const char* name) +{ + int result; + JackLog("JackInternalClient::Open name = %s\n", name); + strcpy(fClientControl->fName, name); + + // Require new client + fChannel->ClientNew(name, &fClientControl->fRefNum, &fEngineControl, &fGraphManager, this, &result); + if (result < 0) { + jack_error("Cannot open client name = %s", name); + goto error; + } + + SetupDriverSync(false); + return 0; + +error: + fChannel->Stop(); + fChannel->Close(); + return -1; +} + +JackGraphManager* JackInternalClient::GetGraphManager() const +{ + assert(fGraphManager); + return fGraphManager; +} + +JackEngineControl* JackInternalClient::GetEngineControl() const +{ + assert(fEngineControl); + return fEngineControl; +} + +JackClientControl* JackInternalClient::GetClientControl() const +{ + return fClientControl; +} + +} // end of namespace + diff --git a/common/JackInternalClient.h b/common/JackInternalClient.h new file mode 100644 index 00000000..fc9ed5b4 --- /dev/null +++ b/common/JackInternalClient.h @@ -0,0 +1,60 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackInternalClient__ +#define __JackInternalClient__ + +#include "JackClient.h" + +namespace Jack +{ + +struct JackEngineControl; +class JackClientChannelInterface; + +/*! +\brief Internal clients in the server. +*/ + +class JackInternalClient : public JackClient +{ + + private: + + JackClientControl* fClientControl; /*! Client control */ + + public: + + JackInternalClient(JackServer* server, JackSynchro** table); + virtual ~JackInternalClient(); + + int Open(const char* name); + + JackGraphManager* GetGraphManager() const; + JackEngineControl* GetEngineControl() const; + JackClientControl* GetClientControl() const; + + static JackGraphManager* fGraphManager; /*! Shared memory Port manager */ + static JackEngineControl* fEngineControl; /*! Shared engine cotrol */ +}; + +} // end of namespace + +#endif diff --git a/common/JackInternalClientChannel.h b/common/JackInternalClientChannel.h new file mode 100644 index 00000000..516b0c2f --- /dev/null +++ b/common/JackInternalClientChannel.h @@ -0,0 +1,116 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackInternalClientChannel__ +#define __JackInternalClientChannel__ + +#include "JackChannel.h" + +namespace Jack +{ + +/*! +\brief JackClientChannel for server internal clients. +*/ + +class JackInternalClientChannel : public JackClientChannelInterface +{ + + private: + + JackServer* fServer; + JackEngine* fEngine; + + public: + + JackInternalClientChannel(JackServer* server): fServer(server), fEngine(server->GetEngine()) + {} + virtual ~JackInternalClientChannel() + {} + + void ClientNew(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, int* result) + { + *result = fEngine->ClientInternalNew(name, ref, shared_engine, shared_manager, client); + } + void ClientClose(int refnum, int* result) + { + *result = fEngine->ClientInternalClose(refnum); + } + void ClientActivate(int refnum, int* result) + { + *result = fServer->Activate(refnum); + } + void ClientDeactivate(int refnum, int* result) + { + *result = fServer->Deactivate(refnum); + } + + void PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result) + { + *result = fEngine->PortRegister(refnum, name, flags, buffer_size, port_index); + } + void PortUnRegister(int refnum, jack_port_id_t port_index, int* result) + { + *result = fEngine->PortUnRegister(refnum, port_index); + } + + void PortConnect(int refnum, const char* src, const char* dst, int* result) + { + *result = fEngine->PortConnect(refnum, src, dst); + } + void PortDisconnect(int refnum, const char* src, const char* dst, int* result) + { + *result = fEngine->PortDisconnect(refnum, src, dst); + } + + void PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) + { + *result = fEngine->PortConnect(refnum, src, dst); + } + void PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) + { + *result = fEngine->PortDisconnect(refnum, src, dst); + } + + void SetBufferSize(jack_nframes_t nframes, int* result) + { + *result = fServer->SetBufferSize(nframes); + } + void SetFreewheel(int onoff, int* result) + { + *result = fServer->SetFreewheel(onoff); + } + + // A FINIR + virtual void ReleaseTimebase(int refnum, int* result) + { + *result = fEngine->ReleaseTimebase(refnum); + } + + virtual void SetTimebaseCallback(int refnum, int conditional, int* result) + { + *result = fEngine->SetTimebaseCallback(refnum, conditional); + } + +}; + +} // end of namespace + +#endif + diff --git a/common/JackLibAPI.cpp b/common/JackLibAPI.cpp new file mode 100644 index 00000000..87730d53 --- /dev/null +++ b/common/JackLibAPI.cpp @@ -0,0 +1,133 @@ +/* +Copyright (C) 2001-2003 Paul Davis +Copyright (C) 2004-2006 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 "JackDebugClient.h" +#include "JackLibClient.h" +#include "JackChannel.h" +#include "JackGraphManager.h" +#include "JackLibGlobals.h" +#include "JackGlobals.h" +#include "varargs.h" + +using namespace Jack; + +#ifdef WIN32 + #define EXPORT __declspec(dllexport) +#else + #define EXPORT +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + EXPORT jack_client_t * jack_client_open (const char *client_name, + jack_options_t options, + jack_status_t *status, ...); + EXPORT jack_client_t * jack_client_new (const char *client_name); + EXPORT int jack_client_close (jack_client_t *client); + +#ifdef __cplusplus +} +#endif + +JackLibGlobals* JackLibGlobals::fGlobals = NULL; +long JackLibGlobals::fClientCount = 0; + +static inline bool CheckPort(jack_port_id_t port_index) +{ + return (port_index < PORT_NUM); +} + +EXPORT jack_client_t* jack_client_new(const char* client_name) +{ + int options = JackUseExactName; + if (getenv("JACK_START_SERVER") == NULL) + options |= JackNoStartServer; + + return jack_client_open(client_name, (jack_options_t)options, NULL); +} + +// TO BE IMPLEMENTED PROPERLY +EXPORT jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...) +{ + va_list ap; /* variable argument pointer */ + jack_varargs_t va; /* variable arguments */ + jack_status_t my_status; + + if (status == NULL) /* no status from caller? */ + status = &my_status; /* use local status word */ + *status = (jack_status_t)0; + + /* validate parameters */ + if ((options & ~JackOpenOptions)) { + int my_status1 = *status | (JackFailure | JackInvalidOption); + *status = (jack_status_t)my_status1; + return NULL; + } + + /* parse variable arguments */ + va_start(ap, status); + jack_varargs_parse(options, ap, &va); + va_end(ap); + + JackLog("jack_client_open %s\n", client_name); + if (client_name == NULL) { + jack_error("jack_client_new called with a NULL client_name"); + return NULL; + } + + JackLibGlobals::Init(); // jack library initialisation + +#ifdef __CLIENTDEBUG__ + JackClient* client = new JackDebugClient(new JackLibClient(GetSynchroTable())); // Debug mode +#else + JackClient* client = new JackLibClient(GetSynchroTable()); +#endif + + int res = client->Open(client_name); + if (res < 0) { + delete client; + JackLibGlobals::Destroy(); // jack library destruction + return NULL; + } else { + *status = (jack_status_t)0; + return (jack_client_t*)client; + } + return NULL; +} + +EXPORT int jack_client_close(jack_client_t* ext_client) +{ + JackLog("jack_client_close\n"); + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_client_close called with a NULL client"); + return -1; + } + int res = client->Close(); + delete client; + JackLog("jack_client_close OK\n"); + JackLibGlobals::Destroy(); // jack library destruction + return res; +} + + diff --git a/common/JackLibClient.cpp b/common/JackLibClient.cpp new file mode 100644 index 00000000..45b49e09 --- /dev/null +++ b/common/JackLibClient.cpp @@ -0,0 +1,162 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackLibClient.h" +#include "JackTime.h" +#include "JackLibGlobals.h" +#include "JackGlobals.h" +#include "JackChannel.h" + +namespace Jack +{ + +// Used for external C API (JackAPI.cpp) +JackGraphManager* GetGraphManager() +{ + assert(JackLibGlobals::fGlobals->fGraphManager); + return JackLibGlobals::fGlobals->fGraphManager; +} + +JackEngineControl* GetEngineControl() +{ + assert(JackLibGlobals::fGlobals->fEngineControl); + return JackLibGlobals::fGlobals->fEngineControl; +} + +JackSynchro** GetSynchroTable() +{ + assert(JackLibGlobals::fGlobals); + return JackLibGlobals::fGlobals->fSynchroTable; +} + +//------------------- +// Client management +//------------------- + +JackLibClient::JackLibClient(JackSynchro** table): JackClient(table) +{ + JackLog("JackLibClient::JackLibClient table = %x\n", table); + fChannel = JackGlobals::MakeClientChannel(); +} + +JackLibClient::~JackLibClient() +{ + JackLog("JackLibClient::~JackLibClient\n"); + delete fChannel; +} + +int JackLibClient::Open(const char* name) +{ + int shared_engine, shared_client, shared_ports, result; + JackLog("JackLibClient::Open %s\n", name); + + // Open server/client channel + if (fChannel->Open(name, this) < 0) { + jack_error("Cannot connect to the server"); + goto error; + } + + // Start receiving notifications + if (fChannel->Start() < 0) { + jack_error("Cannot start channel"); + goto error; + } + + // Require new client + fChannel->ClientNew(name, &shared_engine, &shared_client, &shared_ports, &result); + if (result < 0) { + jack_error("Cannot open %s client", name); + goto error; + } + + try { + // Map shared memory segments + JackLibGlobals::fGlobals->fEngineControl = shared_engine; + JackLibGlobals::fGlobals->fGraphManager = shared_ports; + fClientControl = shared_client; + verbose = GetEngineControl()->fVerbose; + } catch (int n) { + jack_error("Map shared memory segments exception %d", n); + goto error; + } + + SetupDriverSync(false); + + // Connect shared synchro : the synchro must be usable in I/O mode when several clients live in the same process + if (!fSynchroTable[fClientControl->fRefNum]->Connect(name)) { + jack_error("Cannot ConnectSemaphore %s client", name); + goto error; + } + + JackLog("JackLibClient::Open: name, refnum %s %ld\n", name, fClientControl->fRefNum); + return 0; + +error: + fChannel->Stop(); + fChannel->Close(); + return -1; +} + +// Notifications received from the server +// TODO this should be done once for all clients in the process, when a shared notification channel +// will be shared by all clients... +int JackLibClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value) +{ + int res = 0; + + // Done all time + switch (notify) { + + case JackNotifyChannelInterface::kAddClient: + JackLog("JackClient::AddClient name = %s, ref = %ld \n", name, refnum); + // the synchro must be usable in I/O mode when several clients live in the same process + res = fSynchroTable[refnum]->Connect(name) ? 0 : -1; + break; + + case JackNotifyChannelInterface::kRemoveClient: + JackLog("JackClient::RemoveClient name = %s, ref = %ld \n", name, refnum); + if (strcmp(GetClientControl()->fName, name) != 0) + res = fSynchroTable[refnum]->Disconnect() ? 0 : -1; + break; + } + + return res; +} + +JackGraphManager* JackLibClient::GetGraphManager() const +{ + assert(JackLibGlobals::fGlobals->fGraphManager); + return JackLibGlobals::fGlobals->fGraphManager; +} + +JackEngineControl* JackLibClient::GetEngineControl() const +{ + assert(JackLibGlobals::fGlobals->fEngineControl); + return JackLibGlobals::fGlobals->fEngineControl; +} + +JackClientControl* JackLibClient::GetClientControl() const +{ + return fClientControl; +} + +} // end of namespace + + + diff --git a/common/JackLibClient.h b/common/JackLibClient.h new file mode 100644 index 00000000..75e30837 --- /dev/null +++ b/common/JackLibClient.h @@ -0,0 +1,60 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackLibClient__ +#define __JackLibClient__ + +#include "JackClient.h" +#include "JackShmMem.h" +#include "JackClientControl.h" +#include "JackEngineControl.h" + +namespace Jack +{ + +/*! +\brief Client on the library side. +*/ + +class JackLibClient : public JackClient +{ + + private: + + JackShmReadWritePtr1<JackClientControl> fClientControl; /*! Shared client control */ + + public: + + JackLibClient(JackSynchro** table); + virtual ~JackLibClient(); + + int Open(const char* name); + + int ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value); + + JackGraphManager* GetGraphManager() const; + JackEngineControl* GetEngineControl() const; + JackClientControl* GetClientControl() const; +}; + + +} // end of namespace + +#endif + diff --git a/common/JackLibGlobals.h b/common/JackLibGlobals.h new file mode 100644 index 00000000..5f857625 --- /dev/null +++ b/common/JackLibGlobals.h @@ -0,0 +1,97 @@ +/* +Copyright (C) 2005 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackLibGlobals__ +#define __JackLibGlobals__ + +#include "JackShmMem.h" +#include "JackEngineControl.h" +#ifdef __APPLE__ +#include "JackMachPort.h" +#include <map> +#endif +#include "JackGlobals.h" +#include "JackTime.h" +#include <assert.h> + +namespace Jack +{ + +class JackClient; + +/*! +\brief Global library static structure: singleton kind of pattern. +*/ + +struct JackLibGlobals +{ + JackShmReadWritePtr<JackGraphManager> fGraphManager; /*! Shared memory Port manager */ + JackShmReadWritePtr<JackEngineControl> fEngineControl; /*! Shared engine control */ // transport engine has to be writable + JackSynchro* fSynchroTable[CLIENT_NUM]; /*! Shared synchro table */ +#ifdef __APPLE__ + std::map<mach_port_t, JackClient*> fClientTable; /*! Client table */ +#endif + + static long fClientCount; + static JackLibGlobals* fGlobals; + + JackLibGlobals() + { + JackLog("JackLibGlobals\n"); + for (int i = 0; i < CLIENT_NUM; i++) + fSynchroTable[i] = JackGlobals::MakeSynchro(); + fGraphManager = -1; + fEngineControl = -1; + } + + virtual ~JackLibGlobals() + { + JackLog("~JackLibGlobals\n"); + for (int i = 0; i < CLIENT_NUM; i++) { + fSynchroTable[i]->Disconnect(); + delete fSynchroTable[i]; + } + } + + static void Init() + { + if (fClientCount++ == 0 && !fGlobals) { + JackLog("JackLibGlobals Init %x\n", fGlobals); + JackGlobals::InitClient(); + InitTime(); + fGlobals = new JackLibGlobals(); + } + } + + static void Destroy() + { + if (--fClientCount == 0 && fGlobals) { + JackLog("JackLibGlobals Destroy %x\n", fGlobals); + delete fGlobals; + fGlobals = NULL; + JackGlobals::Destroy(); + } + } + +}; + +} // end of namespace + +#endif + diff --git a/common/JackLoopbackDriver.cpp b/common/JackLoopbackDriver.cpp new file mode 100644 index 00000000..79550c5e --- /dev/null +++ b/common/JackLoopbackDriver.cpp @@ -0,0 +1,209 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackLoopbackDriver.h" +#include "JackEngineControl.h" +#include "JackGraphManager.h" +#include <iostream> +#include <assert.h> + +namespace Jack +{ + +int JackLoopbackDriver::Open(jack_nframes_t nframes, + jack_nframes_t samplerate, + int capturing, + int playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) +{ + return JackAudioDriver::Open(nframes, + samplerate, + capturing, + playing, + inchannels, + outchannels, + monitor, + capture_driver_name, + playback_driver_name, + capture_latency, + playback_latency); +} + +int JackLoopbackDriver::Process() +{ + assert(fCaptureChannels == fPlaybackChannels); + + // Loopback copy + for (int i = 0; i < fCaptureChannels; i++) { + memcpy(fGraphManager->GetBuffer(fCapturePortList[i], fEngineControl->fBufferSize), + fGraphManager->GetBuffer(fPlaybackPortList[i], fEngineControl->fBufferSize), + sizeof(float) * fEngineControl->fBufferSize); + } + + fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); // Signal all clients + if (fEngineControl->fSyncMode) { + if (fGraphManager->SuspendRefNum(fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0) + jack_error("JackLoopbackDriver::ProcessSync SuspendRefNum error"); + } + return 0; +} + +void JackLoopbackDriver::PrintState() +{ + int i; + jack_port_id_t port_index; + + std::cout << "JackLoopbackDriver state" << std::endl; + std::cout << "Input ports" << std::endl; + + for (i = 0; i < fPlaybackChannels; i++) { + port_index = fCapturePortList[i]; + JackPort* port = fGraphManager->GetPort(port_index); + std::cout << port->GetName() << std::endl; + if (fGraphManager->GetConnectionsNum(port_index)) {} + } + + std::cout << "Output ports" << std::endl; + + for (i = 0; i < fCaptureChannels; i++) { + port_index = fPlaybackPortList[i]; + JackPort* port = fGraphManager->GetPort(port_index); + std::cout << port->GetName() << std::endl; + if (fGraphManager->GetConnectionsNum(port_index)) {} + } +} + +} // end of namespace + +/* +#ifdef __cplusplus +extern "C" { +#endif + +jack_driver_desc_t * driver_get_descriptor () +{ + jack_driver_desc_t * desc; + jack_driver_param_desc_t * params; + unsigned int i; + + desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t)); + strcpy (desc->name, "dummy"); + desc->nparams = 5; + + params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); + + i = 0; + strcpy (params[i].name, "capture"); + params[i].character = 'C'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 2U; + strcpy (params[i].short_desc, "Number of capture ports"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "playback"); + params[i].character = 'P'; + params[i].type = JackDriverParamUInt; + params[1].value.ui = 2U; + strcpy (params[i].short_desc, "Number of playback ports"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "rate"); + params[i].character = 'r'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 48000U; + strcpy (params[i].short_desc, "Sample rate"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "period"); + params[i].character = 'p'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 1024U; + strcpy (params[i].short_desc, "Frames per period"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "wait"); + params[i].character = 'w'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 21333U; + strcpy (params[i].short_desc, + "Number of usecs to wait between engine processes"); + strcpy (params[i].long_desc, params[i].short_desc); + + desc->params = params; + + return desc; +} + +Jack::JackDriverClientInterface* driver_initialize(Jack::JackEngine* engine, Jack::JackSynchro** table, const JSList* params) +{ + jack_nframes_t sample_rate = 48000; + jack_nframes_t period_size = 1024; + unsigned int capture_ports = 2; + unsigned int playback_ports = 2; + const JSList * node; + const jack_driver_param_t * param; + + for (node = params; node; node = jack_slist_next (node)) { + param = (const jack_driver_param_t *) node->data; + + switch (param->character) { + + case 'C': + capture_ports = param->value.ui; + break; + + case 'P': + playback_ports = param->value.ui; + break; + + case 'r': + sample_rate = param->value.ui; + break; + + case 'p': + period_size = param->value.ui; + break; + + } + } + Jack::JackDriverClientInterface* driver = new Jack::JackLoopbackDriver("loopback", engine, table); + if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, "loopback") == 0) { + return driver; + } else { + delete driver; + return NULL; + } +} + +#ifdef __cplusplus +} +#endif +*/ + diff --git a/common/JackLoopbackDriver.h b/common/JackLoopbackDriver.h new file mode 100644 index 00000000..4610822e --- /dev/null +++ b/common/JackLoopbackDriver.h @@ -0,0 +1,63 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackLoopbackDriver__ +#define __JackLoopbackDriver__ + +#include "JackAudioDriver.h" +#include "JackTime.h" + +namespace Jack +{ + +/*! +\brief The loopback driver : to be used to "pipeline" applications connected in sequence. +*/ + +class JackLoopbackDriver : public JackAudioDriver +{ + + public: + + JackLoopbackDriver(const char* name, JackEngine* engine, JackSynchro** table) + : JackAudioDriver(name, engine, table) + {} + virtual ~JackLoopbackDriver() + {} + + int Open(jack_nframes_t frames_per_cycle, + jack_nframes_t rate, + int capturing, + int playing, + int chan_in, + int chan_out, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency); + + int Process(); + void PrintState(); +}; + +} // end of namespace + +#endif diff --git a/common/JackPort.cpp b/common/JackPort.cpp new file mode 100644 index 00000000..c3898e2d --- /dev/null +++ b/common/JackPort.cpp @@ -0,0 +1,229 @@ +/* +Copyright (C) 2001-2003 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackPort.h" +#include "JackError.h" +#include <stdio.h> + +namespace Jack +{ + +JackPort::JackPort() + : fFlags(JackPortIsInput), fRefNum( -1), fLatency(0), fMonitorRequests(0), fInUse(false), fLocked(false), fTied(NO_PORT) +{} + +JackPort::~JackPort() +{} + +void JackPort::Allocate(int refnum, const char* port_name, JackPortFlags flags) +{ + fFlags = flags; + fRefNum = refnum; + strcpy(fName, port_name); + memset(fBuffer, 0, BUFFER_SIZE_MAX * sizeof(float)); + fInUse = true; + fLocked = false; + fLatency = 0; + fTied = NO_PORT; +} + +void JackPort::Release() +{ + fFlags = JackPortIsInput; + fRefNum = -1; + fInUse = false; + fLocked = false; + fLatency = 0; + fTied = NO_PORT; +} + +bool JackPort::IsUsed() const +{ + return fInUse; +} + +float* JackPort::GetBuffer() +{ + return fBuffer; +} + +int JackPort::GetRefNum() const +{ + return fRefNum; +} + +int JackPort::Lock() +{ + fLocked = true; + return 0; +} + +int JackPort::Unlock() +{ + fLocked = false; + return 0; +} + +jack_nframes_t JackPort::GetLatency() const +{ + return fLatency; +} + +void JackPort::SetLatency(jack_nframes_t nframes) +{ + fLatency = nframes; +} + +int JackPort::Tie(jack_port_id_t port_index) +{ + fTied = port_index; + return 0; +} + +int JackPort::UnTie() +{ + fTied = NO_PORT; + return 0; +} + +int JackPort::RequestMonitor(bool onoff) +{ + /** + 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; + */ + + if (onoff) { + fMonitorRequests++; + } else if (fMonitorRequests) { + fMonitorRequests--; + } + + return 0; +} + +int JackPort::EnsureMonitor(bool onoff) +{ + /** + 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; + */ + + if (onoff) { + if (fMonitorRequests == 0) { + fMonitorRequests++; + } + } else { + if (fMonitorRequests > 0) { + fMonitorRequests = 0; + } + } + + return 0; +} + +bool JackPort::MonitoringInput() +{ + return (fMonitorRequests > 0); +} + +const char* JackPort::GetName() const +{ + return fName; +} + +const char* JackPort::GetShortName() const +{ + /* we know there is always a colon, because we put + it there ... + */ + return strchr(fName, ':') + 1; +} + +int JackPort::Flags() const +{ + return fFlags; +} + +const char* JackPort::Type() const +{ + // TO IMPROVE + return "Audio"; +} + +int JackPort::SetName(const char* new_name) +{ + char* colon = strchr(fName, ':'); + int len = sizeof(fName) - ((int) (colon - fName)) - 2; + snprintf(colon + 1, len, "%s", new_name); + return 0; +} + +void JackPort::MixBuffer(float* mixbuffer, float* buffer, jack_nframes_t frames) +{ + jack_nframes_t frames_group = frames / 4; + frames = frames % 4; + + while (frames_group > 0) { + register float mixFloat1 = *mixbuffer; + register float sourceFloat1 = *buffer; + register float mixFloat2 = *(mixbuffer + 1); + register float sourceFloat2 = *(buffer + 1); + register float mixFloat3 = *(mixbuffer + 2); + register float sourceFloat3 = *(buffer + 2); + register float mixFloat4 = *(mixbuffer + 3); + register float sourceFloat4 = *(buffer + 3); + + buffer += 4; + frames_group--; + + mixFloat1 += sourceFloat1; + mixFloat2 += sourceFloat2; + mixFloat3 += sourceFloat3; + mixFloat4 += sourceFloat4; + + *mixbuffer = mixFloat1; + *(mixbuffer + 1) = mixFloat2; + *(mixbuffer + 2) = mixFloat3; + *(mixbuffer + 3) = mixFloat4; + + mixbuffer += 4; + } + + while (frames > 0) { + register float mixFloat1 = *mixbuffer; + register float sourceFloat1 = *buffer; + buffer++; + frames--; + mixFloat1 += sourceFloat1; + *mixbuffer = mixFloat1; + mixbuffer++; + } +} + +} // end of namespace diff --git a/common/JackPort.h b/common/JackPort.h new file mode 100644 index 00000000..84823842 --- /dev/null +++ b/common/JackPort.h @@ -0,0 +1,98 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackPort__ +#define __JackPort__ + +#include "types.h" +#include "JackConstants.h" + +namespace Jack +{ + +#define ALL_PORTS 0xFFFF +#define NO_PORT 0xFFFE + +/*! +\brief Base class for port. +*/ + +class JackPort +{ + + friend class JackGraphManager; + + private: + + enum JackPortFlags fFlags; + char fName[JACK_PORT_NAME_SIZE + 2]; + int fRefNum; + + jack_nframes_t fLatency; + uint8_t fMonitorRequests; + + bool fInUse; + bool fLocked; + jack_port_id_t fTied; // Locally tied source port + + float fBuffer[BUFFER_SIZE_MAX]; + + bool IsUsed() const; + + static void MixBuffer(float* mixbuffer, float* buffer, jack_nframes_t frames); + + public: + + JackPort(); + virtual ~JackPort(); + + void Allocate(int refnum, const char* port_name, JackPortFlags flags); + void Release(); + const char* GetName() const; + const char* GetShortName() const; + int SetName(const char * name); + + int Flags() const; + const char* Type() const; + + int Lock(); + int Unlock(); + + int Tie(jack_port_id_t port_index); + int UnTie(); + + jack_nframes_t GetLatency() const; + void SetLatency(jack_nframes_t latency); + + int RequestMonitor(bool onoff); + int EnsureMonitor(bool onoff); + bool MonitoringInput(); + + float* GetBuffer(); + int GetRefNum() const; + +}; + + +} // end of namespace + + +#endif + diff --git a/common/JackPosixSemaphore.cpp b/common/JackPosixSemaphore.cpp new file mode 100644 index 00000000..18e7a0f5 --- /dev/null +++ b/common/JackPosixSemaphore.cpp @@ -0,0 +1,205 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackPosixSemaphore.h" +#include "JackChannel.h" +#include "JackError.h" +#include <fcntl.h> +#include <sys/time.h> + +namespace Jack +{ + +void JackPosixSemaphore::BuildName(const char* name, char* res) +{ + sprintf(res, "%s/jack_sem.%s", jack_client_dir, name); +} + +bool JackPosixSemaphore::Signal() +{ + int res; + assert(fSemaphore); + + if (fFlush) + return true; + + if ((res = sem_post(fSemaphore)) != 0) { + jack_error("JackPosixSemaphore::Signal name = %s err = %s", fName, strerror(errno)); + } + return (res == 0); +} + +bool JackPosixSemaphore::SignalAll() +{ + int res; + assert(fSemaphore); + + if (fFlush) + return true; + + if ((res = sem_post(fSemaphore)) != 0) { + jack_error("JackPosixSemaphore::SignalAll name = %s err = %s", fName, strerror(errno)); + } + return (res == 0); +} + +/* +bool JackPosixSemaphore::Wait() +{ + int res; + assert(fSemaphore); + if ((res = sem_wait(fSemaphore)) != 0) { + jack_error("JackPosixSemaphore::Wait name = %s err = %s", fName, strerror(errno)); + } + return (res == 0); +} +*/ + +bool JackPosixSemaphore::Wait() +{ + int res; + + while ((res = sem_wait(fSemaphore) < 0)) { + jack_error("JackPosixSemaphore::Wait name = %s err = %s", fName, strerror(errno)); + if (errno != EINTR) + break; + } + return (res == 0); +} + + +/* +#ifdef __linux__ + +bool JackPosixSemaphore::TimedWait(long usec) // unusable semantic !! +{ + int res; + struct timeval now; + timespec time; + assert(fSemaphore); + gettimeofday(&now, 0); + time.tv_sec = now.tv_sec + usec / 1000000; + time.tv_nsec = (now.tv_usec + (usec % 1000000)) * 1000; + + if ((res = sem_timedwait(fSemaphore, &time)) != 0) { + jack_error("JackPosixSemaphore::TimedWait err = %s", strerror(errno)); + JackLog("now %ld %ld \n", now.tv_sec, now.tv_usec); + JackLog("next %ld %ld \n", time.tv_sec, time.tv_nsec/1000); + } + return (res == 0); +} + +#else +#warning "JackPosixSemaphore::TimedWait is not supported: Jack in SYNC mode with JackPosixSemaphore will not run properly !!" + +bool JackPosixSemaphore::TimedWait(long usec) +{ + return Wait(); +} +#endif +*/ + +#warning JackPosixSemaphore::TimedWait not available : synchronous mode may not work correctly if POSIX semaphore are used + +bool JackPosixSemaphore::TimedWait(long usec) +{ + return Wait(); +} + +// Server side : publish the semaphore in the global namespace +bool JackPosixSemaphore::Allocate(const char* name, int value) +{ + BuildName(name, fName); + JackLog("JackPosixSemaphore::Allocate name = %s val = %ld\n", fName, value); + + if ((fSemaphore = sem_open(fName, O_CREAT, 0777, value)) == (sem_t*)SEM_FAILED) { + jack_error("Allocate: can't check in named semaphore name = %s err = %s", fName, strerror(errno)); + return false; + } else { + return true; + } +} + +// Client side : get the published semaphore from server +bool JackPosixSemaphore::ConnectInput(const char* name) +{ + BuildName(name, fName); + JackLog("JackPosixSemaphore::Connect %s\n", fName); + + // Temporary... + if (fSemaphore) { + JackLog("Already connected name = %s\n", name); + return true; + } + + if ((fSemaphore = sem_open(fName, O_CREAT)) == (sem_t*)SEM_FAILED) { + jack_error("Connect: can't connect named semaphore name = %s err = %s", fName, strerror(errno)); + return false; + } else { + int val = 0; + sem_getvalue(fSemaphore, &val); + JackLog("JackPosixSemaphore::Connect sem_getvalue %ld\n", val); + return true; + } +} + +bool JackPosixSemaphore::Connect(const char* name) +{ + return ConnectInput(name); +} + +bool JackPosixSemaphore::ConnectOutput(const char* name) +{ + return ConnectInput(name); +} + +bool JackPosixSemaphore::Disconnect() +{ + JackLog("JackPosixSemaphore::Disconnect %s\n", fName); + + if (fSemaphore) { + if (sem_close(fSemaphore) != 0) { + jack_error("Disconnect: can't disconnect named semaphore name = %s err = %s", fName, strerror(errno)); + return false; + } else { + fSemaphore = NULL; + return true; + } + } else { + return true; + } +} + +// Server side : destroy the semaphore +void JackPosixSemaphore::Destroy() +{ + if (fSemaphore != NULL) { + JackLog("JackPosixSemaphore::Destroy\n"); + sem_unlink(fName); + if (sem_close(fSemaphore) != 0) { + jack_error("Destroy: can't destroy semaphore name = %s err = %s", fName, strerror(errno)); + } + fSemaphore = NULL; + } else { + jack_error("JackPosixSemaphore::Destroy semaphore == NULL"); + } +} + +} // end of namespace + diff --git a/common/JackPosixSemaphore.h b/common/JackPosixSemaphore.h new file mode 100644 index 00000000..dadfcc91 --- /dev/null +++ b/common/JackPosixSemaphore.h @@ -0,0 +1,71 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackPosixSemaphore__ +#define __JackPosixSemaphore__ + +#include "JackSynchro.h" +#include <semaphore.h> +#include <time.h> +#include <stdio.h> +#include <assert.h> + +namespace Jack +{ + +/*! +\brief Inter process synchronization using POSIX semaphore. +*/ + +class JackPosixSemaphore : public JackSynchro +{ + + private: + + sem_t* fSemaphore; + + protected: + + void BuildName(const char* name, char* res); + + public: + + JackPosixSemaphore(): JackSynchro(), fSemaphore(NULL) + {} + virtual ~JackPosixSemaphore() + {} + + bool Signal(); + bool SignalAll(); + bool Wait(); + bool TimedWait(long usec); + + bool Allocate(const char* name, int value); + bool Connect(const char* name); + bool ConnectInput(const char* name); + bool ConnectOutput(const char* name); + bool Disconnect(); + void Destroy(); +}; + +} // end of namespace + + +#endif + diff --git a/common/JackPosixThread.cpp b/common/JackPosixThread.cpp new file mode 100644 index 00000000..32b3b686 --- /dev/null +++ b/common/JackPosixThread.cpp @@ -0,0 +1,209 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackPosixThread.h" +#include "JackError.h" +#include <string.h> // for memset + +namespace Jack +{ + +void* JackPosixThread::ThreadHandler(void* arg) +{ + JackPosixThread* obj = (JackPosixThread*)arg; + JackRunnableInterface* runnable = obj->fRunnable; + int err; + + if ((err = pthread_setcanceltype(obj->fCancellation, NULL)) != 0) { + jack_error("pthread_setcanceltype err = %s", strerror(err)); + } + + // Call Init method + if (!runnable->Init()) { + jack_error("Thread init fails: thread quits"); + return 0; + } + + JackLog("ThreadHandler: start\n"); + + // If Init succeed start the thread loop + bool res = true; + while (obj->fRunning && res) { + res = runnable->Execute(); + //pthread_testcancel(); + } + + JackLog("ThreadHandler: exit\n"); + return 0; +} + +int JackPosixThread::Start() +{ + int res; + fRunning = true; + + if (fRealTime) { + + JackLog("Create RT thread\n"); + + /* Get the client thread to run as an RT-FIFO + scheduled thread of appropriate priority. + */ + pthread_attr_t attributes; + struct sched_param rt_param; + pthread_attr_init(&attributes); + + if ((res = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED))) { + jack_error("Cannot request explicit scheduling for RT thread %d %s", res, strerror(errno)); + return -1; + } + + if ((res = pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE))) { + jack_error("Cannot request joinable thread creation for RT thread %d %s", res, strerror(errno)); + return -1; + } + + if ((res = pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM))) { + jack_error("Cannot set scheduling scope for RT thread %d %s", res, strerror(errno)); + return -1; + } + + //if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_FIFO))) { + + if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_RR))) { + jack_error("Cannot set FIFO scheduling class for RT thread %d %s", res, strerror(errno)); + return -1; + } + + if ((res = pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM))) { + jack_error("Cannot set scheduling scope for RT thread %d %s", res, strerror(errno)); + return -1; + } + + memset(&rt_param, 0, sizeof(rt_param)); + rt_param.sched_priority = fPriority; + + if ((res = pthread_attr_setschedparam(&attributes, &rt_param))) { + jack_error("Cannot set scheduling priority for RT thread %d %s", res, strerror(errno)); + return -1; + } + + if ((res = pthread_create(&fThread, &attributes, ThreadHandler, this))) { + jack_error("Cannot set create thread %d %s", res, strerror(errno)); + return -1; + } + + return 0; + } else { + JackLog("Create non RT thread\n"); + + if ((res = pthread_create(&fThread, 0, ThreadHandler, this))) { + jack_error("Cannot set create thread %d %s", res, strerror(errno)); + return -1; + } + return 0; + } +} + +int JackPosixThread::StartSync() +{ + jack_error("Not implemented yet"); + return -1; +} + +int JackPosixThread::Kill() +{ + if (fThread) { // If thread has been started + JackLog("JackPosixThread::Kill\n"); + void* status; + pthread_cancel(fThread); + pthread_join(fThread, &status); + return 0; + } else { + return -1; + } +} + +int JackPosixThread::Stop() +{ + if (fThread) { // If thread has been started + JackLog("JackPosixThread::Stop\n"); + void* status; + fRunning = false; // Request for the thread to stop + pthread_join(fThread, &status); + return 0; + } else { + return -1; + } +} + +int JackPosixThread::AcquireRealTime() +{ + struct sched_param rtparam; + int res; + + if (!fThread) + return -1; + + memset(&rtparam, 0, sizeof(rtparam)); + rtparam.sched_priority = fPriority; + + //if ((res = pthread_setschedparam(fThread, SCHED_FIFO, &rtparam)) != 0) { + + if ((res = pthread_setschedparam(fThread, SCHED_RR, &rtparam)) != 0) { + jack_error("Cannot use real-time scheduling (FIFO/%d) " + "(%d: %s)", rtparam.sched_priority, res, + strerror(res)); + return -1; + } + return 0; +} + +int JackPosixThread::AcquireRealTime(int priority) +{ + fPriority = priority; + return AcquireRealTime(); +} + +int JackPosixThread::DropRealTime() +{ + struct sched_param rtparam; + int res; + + if (!fThread) + return -1; + + memset(&rtparam, 0, sizeof(rtparam)); + rtparam.sched_priority = 0; + + if ((res = pthread_setschedparam(fThread, SCHED_OTHER, &rtparam)) != 0) { + jack_error("Cannot switch to normal scheduling priority(%s)\n", strerror(errno)); + return -1; + } + return 0; +} + +pthread_t JackPosixThread::GetThreadID() +{ + return fThread; +} + +} // end of namespace + diff --git a/common/JackPosixThread.h b/common/JackPosixThread.h new file mode 100644 index 00000000..34927984 --- /dev/null +++ b/common/JackPosixThread.h @@ -0,0 +1,73 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackPosixThread__ +#define __JackPosixThread__ + +#include "JackThread.h" +#include <pthread.h> + +namespace Jack +{ + +/*! +\brief The POSIX thread base class. +*/ + +class JackPosixThread : public JackThread +{ + + protected: + + pthread_t fThread; + + static void* ThreadHandler(void* arg); + + public: + + JackPosixThread(JackRunnableInterface* runnable, bool real_time, int priority, int cancellation) + : JackThread(runnable, priority, real_time, cancellation), fThread((pthread_t)NULL) + {} + JackPosixThread(JackRunnableInterface* runnable) + : JackThread(runnable, 0, false, PTHREAD_CANCEL_DEFERRED), fThread((pthread_t)NULL) + {} + JackPosixThread(JackRunnableInterface* runnable, int cancellation) + : JackThread(runnable, 0, false, cancellation), fThread((pthread_t)NULL) + {} + + virtual ~JackPosixThread() + {} + + virtual int Start(); + virtual int StartSync(); + virtual int Kill(); + virtual int Stop(); + + virtual int AcquireRealTime(); + virtual int AcquireRealTime(int priority); + virtual int DropRealTime(); + + pthread_t GetThreadID(); +}; + +} // end of namespace + + +#endif diff --git a/common/JackProcessSync.h b/common/JackProcessSync.h new file mode 100644 index 00000000..c8ddd99e --- /dev/null +++ b/common/JackProcessSync.h @@ -0,0 +1,180 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackProcessSync__ +#define __JackProcessSync__ + +#include "JackSyncInterface.h" +#include "JackSynchro.h" +#include <pthread.h> +#include <sys/time.h> +#include <unistd.h> + +namespace Jack +{ + +/*! +\brief A synchronization primitive built using a condition variable. +*/ + +class JackProcessSync : public JackSyncInterface +{ + + private: + + pthread_mutex_t fLock; // Mutex + pthread_cond_t fCond; // Condition variable + + public: + + JackProcessSync(): JackSyncInterface() + { + pthread_mutex_init(&fLock, NULL); + pthread_cond_init(&fCond, NULL); + } + virtual ~JackProcessSync() + { + pthread_mutex_destroy(&fLock); + pthread_cond_destroy(&fCond); + } + + bool Allocate(const char* name) + { + return true; + } + + bool Connect(const char* name) + { + return true; + } + + void Destroy() + {} + + bool TimedWait(long usec) + { + struct timeval T0, T1; + timespec time; + struct timeval now; + int res; + + pthread_mutex_lock(&fLock); + JackLog("JackProcessSync::Wait time out = %ld\n", usec); + gettimeofday(&T0, 0); + + static const UInt64 kNanosPerSec = 1000000000ULL; + static const UInt64 kNanosPerUsec = 1000ULL; + gettimeofday(&now, 0); + UInt64 nextDateNanos = now.tv_sec * kNanosPerSec + (now.tv_usec + usec) * kNanosPerUsec; + time.tv_sec = nextDateNanos / kNanosPerSec; + time.tv_nsec = nextDateNanos % kNanosPerSec; + res = pthread_cond_timedwait(&fCond, &fLock, &time); + if (res != 0) + jack_error("pthread_cond_timedwait error usec = %ld err = %s", usec, strerror(res)); + + gettimeofday(&T1, 0); + pthread_mutex_unlock(&fLock); + JackLog("JackProcessSync::Wait finished delta = %5.1lf\n", + (1e6 * T1.tv_sec - 1e6 * T0.tv_sec + T1.tv_usec - T0.tv_usec)); + return (res == 0); + } + + void Wait() + { + int res; + pthread_mutex_lock(&fLock); + JackLog("JackProcessSync::Wait...\n"); + if ((res = pthread_cond_wait(&fCond, &fLock)) != 0) + jack_error("pthread_cond_wait error err = %s", strerror(errno)); + pthread_mutex_unlock(&fLock); + JackLog("JackProcessSync::Wait finished\n"); + } + + void SignalAll() + { + //pthread_mutex_lock(&fLock); + pthread_cond_broadcast(&fCond); + //pthread_mutex_unlock(&fLock); + } + +}; + +/*! +\brief A synchronization primitive built using an inter-process synchronization object. +*/ + +class JackInterProcessSync : public JackSyncInterface +{ + + private: + + JackSynchro* fSynchro; + + public: + + JackInterProcessSync(JackSynchro* synchro): fSynchro(synchro) + {} + virtual ~JackInterProcessSync() + { + delete fSynchro; + } + + bool Allocate(const char* name) + { + return fSynchro->Allocate(name, 0); + } + + void Destroy() + { + fSynchro->Destroy(); + } + + bool Connect(const char* name) + { + return fSynchro->Connect(name); + } + + bool TimedWait(long usec) + { + struct timeval T0, T1; + JackLog("JackInterProcessSync::Wait...\n"); + gettimeofday(&T0, 0); + bool res = fSynchro->TimedWait(usec); + gettimeofday(&T1, 0); + JackLog("JackInterProcessSync::Wait finished delta = %5.1lf\n", + (1e6 * T1.tv_sec - 1e6 * T0.tv_sec + T1.tv_usec - T0.tv_usec)); + return res; + } + + void Wait() + { + fSynchro->Wait(); + } + + void SignalAll() + { + fSynchro->SignalAll(); + } +}; + + +} // end of namespace + +#endif + diff --git a/common/JackPthreadCond.cpp b/common/JackPthreadCond.cpp new file mode 100644 index 00000000..eb72a21a --- /dev/null +++ b/common/JackPthreadCond.cpp @@ -0,0 +1,207 @@ +/* +Copyright (C) 2004-2005 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackPthreadCond.h" +#include "JackError.h" + +namespace Jack +{ + +JackPthreadCondArray* JackPthreadCondServer::fTable = NULL; +long JackPthreadCondServer::fCount = 0; + +JackShmReadWritePtr1<JackPthreadCondArray> JackPthreadCondClient::fTable; +long JackPthreadCondClient::fCount = 0; + +JackPthreadCondArray::JackPthreadCondArray() +{ + for (int i = 0; i < MAX_ITEM; i++) { + strcpy(fTable[i].fName, ""); + } +} + +void JackPthreadCond::BuildName(const char* name, char* res) +{ + sprintf(res, "%s/jack_sem.%s", jack_client_dir, name); +} + +bool JackPthreadCond::Signal() +{ + //pthread_mutex_lock(&fSynchro->fLock); + //JackLog("JackPthreadCond::Signal...\n"); + pthread_cond_signal(&fSynchro->fCond); + //pthread_mutex_unlock(&fSynchro->fLock); + return true; +} + +bool JackPthreadCond::SignalAll() +{ + pthread_cond_broadcast(&fSynchro->fCond); + return true; +} + +bool JackPthreadCond::Wait() +{ + pthread_mutex_lock(&fSynchro->fLock); + //JackLog("JackPthreadCond::Wait...\n"); + pthread_cond_wait(&fSynchro->fCond, &fSynchro->fLock); + pthread_mutex_unlock(&fSynchro->fLock); + //JackLog("JackProcessSync::Wait finished\n"); + return true; +} + +bool JackPthreadCond::TimedWait(long usec) +{ + timespec time; + struct timeval now; + gettimeofday(&now, 0); + time.tv_sec = now.tv_sec + usec / 1000000; + time.tv_nsec = (now.tv_usec + (usec % 1000000)) * 1000; + pthread_mutex_lock(&fSynchro->fLock); + JackLog("JackProcessSync::Wait...\n"); + pthread_cond_timedwait(&fSynchro->fCond, &fSynchro->fLock, &time); + pthread_mutex_unlock(&fSynchro->fLock); + JackLog("JackProcessSync::Wait finished\n"); + return true; +} + +// Client side : get the published semaphore from server +bool JackPthreadCond::ConnectInput(const char* name) +{ + BuildName(name, fName); + JackLog("JackPthreadCond::Connect %s\n", fName); + + // Temporary... + if (fSynchro) { + JackLog("Already connected name = %s\n", name); + return true; + } + + for (int i = 0; i < MAX_ITEM; i++) { + JackPthreadCondItem* synchro = &(GetTable()->fTable[i]); + if (strcmp(fName, synchro->fName) == 0) { + fSynchro = synchro; + return true; + } + } + + return false; +} + +bool JackPthreadCond::Connect(const char* name) +{ + return ConnectInput(name); +} + +bool JackPthreadCond::ConnectOutput(const char* name) +{ + return ConnectInput(name); +} + +bool JackPthreadCond::Disconnect() +{ + JackLog("JackPthreadCond::Disconnect %s\n", fName); + + if (fSynchro) { + strcpy(fSynchro->fName, ""); + fSynchro = NULL; + return true; + } else { + return false; + } +} + +JackPthreadCondServer::JackPthreadCondServer(): JackPthreadCond() +{ + if (fCount++ == 0 && !fTable) { + fTable = new JackPthreadCondArray(); + } + if (fCount == MAX_ITEM) + throw new std::bad_alloc; +} + +JackPthreadCondServer::~JackPthreadCondServer() +{ + if (--fCount == 0 && fTable) { + delete fTable; + fTable = NULL; + } +} + +bool JackPthreadCondServer::Allocate(const char* name, int value) +{ + BuildName(name, fName); + JackLog("JackPthreadCond::Allocate name = %s val = %ld\n", fName, value); + + pthread_mutexattr_t mutex_attr; + pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); + pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_NORMAL); + + pthread_condattr_t cond_attr; + pthread_condattr_init(&cond_attr); + pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); + + for (int i = 0; i < MAX_ITEM; i++) { + if (strcmp(fTable->fTable[i].fName, "") == 0) { // first empty place + fSynchro = &fTable->fTable[i]; + if (pthread_mutex_init(&fSynchro->fLock, &mutex_attr) != 0) { + jack_error("Allocate: can't check in named semaphore name = %s err = %s", fName, strerror(errno)); + return false; + } + if (pthread_cond_init(&fSynchro->fCond, &cond_attr) != 0) { + jack_error("Allocate: can't check in named semaphore name = %s err = %s", fName, strerror(errno)); + return false; + } + strcpy(fSynchro->fName, fName); + return true; + } + } + + return false; +} + +void JackPthreadCondServer::Destroy() +{ + if (fSynchro != NULL) { + pthread_mutex_destroy(&fSynchro->fLock); + pthread_cond_destroy(&fSynchro->fCond); + strcpy(fSynchro->fName, ""); + fSynchro = NULL; + } else { + jack_error("JackPthreadCond::Destroy semaphore == NULL"); + } +} + +JackPthreadCondClient::JackPthreadCondClient(int shared_index): JackPthreadCond() +{ + if (fCount++ == 0 && !fTable) { + fTable = shared_index; + } + if (fCount == MAX_ITEM) + throw new std::bad_alloc; +} + +JackPthreadCondClient::~JackPthreadCondClient() +{ + if (--fCount == 0 && fTable) + delete fTable; +} + +} // end of namespace + diff --git a/common/JackPthreadCond.h b/common/JackPthreadCond.h new file mode 100644 index 00000000..de1c818b --- /dev/null +++ b/common/JackPthreadCond.h @@ -0,0 +1,133 @@ +/* +Copyright (C) 2004-2005 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackPthreadCond__ +#define __JackPthreadCond__ + +#include "JackSynchro.h" +#include "JackShmMem.h" +#include <pthread.h> +#include <sys/time.h> +#include <time.h> +#include <stdio.h> +#include <assert.h> + +#define MAX_ITEM 8 + +namespace Jack +{ + +struct JackPthreadCondItem +{ + char fName[SYNC_MAX_NAME_SIZE]; + pthread_mutex_t fLock; + pthread_cond_t fCond; +}; + +struct JackPthreadCondArray : public JackShmMem +{ + JackPthreadCondItem fTable[MAX_ITEM]; + + JackPthreadCondArray(); + virtual ~JackPthreadCondArray() + {} +} +; + +/*! +\brief Inter process synchronization using pthread condition variables. +*/ + +class JackPthreadCond : public JackSynchro +{ + + protected: + + JackPthreadCondItem* fSynchro; + void BuildName(const char* name, char* res); + virtual JackPthreadCondArray* GetTable() = 0; + + public: + + JackPthreadCond(): fSynchro(NULL) + {} + virtual ~JackPthreadCond() + {} + + bool Signal(); + bool SignalAll(); + bool Wait(); + bool TimedWait(long usec); + + bool Connect(const char* name); + bool ConnectInput(const char* name); + bool ConnectOutput(const char* name); + bool Disconnect(); + +}; + +class JackPthreadCondServer : public JackPthreadCond +{ + + private: + + static JackPthreadCondArray* fTable; + static long fCount; + + protected: + + JackPthreadCondArray* GetTable() + { + return fTable; + } + + public: + + JackPthreadCondServer(); + virtual ~JackPthreadCondServer(); + + bool Allocate(const char* name, int value); + void Destroy(); +}; + +class JackPthreadCondClient : public JackPthreadCond +{ + + private: + + static JackShmReadWritePtr1<JackPthreadCondArray> fTable; + static long fCount; + + protected: + + JackPthreadCondArray* GetTable() + { + return fTable; + } + + public: + + JackPthreadCondClient(int shared_index); + virtual ~JackPthreadCondClient(); +}; + +} // end of namespace + +#endif + diff --git a/common/JackRequest.h b/common/JackRequest.h new file mode 100644 index 00000000..e096ed8f --- /dev/null +++ b/common/JackRequest.h @@ -0,0 +1,612 @@ + +/* + Copyright (C) 2001 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackRequest__ +#define __JackRequest__ + +#include "JackPort.h" +#include "JackChannelTransaction.h" +#include "JackError.h" +#include <stdio.h> + +namespace Jack +{ + +/*! +\brief Request from client to server. +*/ + +struct JackRequest +{ + +public: + + typedef enum { + kRegisterPort = 1, + kUnRegisterPort = 2, + kConnectPorts = 3, + kDisconnectPorts = 4, + kSetTimeBaseClient = 5, + kActivateClient = 6, + kDeactivateClient = 7, + kDisconnectPort = 8, + kSetClientCapabilities = 9, + kGetPortConnections = 10, + kGetPortNConnections = 11, + + kReleaseTimebase = 12, + kSetTimebaseCallback = 13, + + kSetBufferSize = 20, + kSetFreeWheel = 21, + kClientNew = 22, + kClientClose = 23, + kConnectNamePorts = 24, + kDisconnectNamePorts = 25, + + kNotification = 26 + } RequestType; + + RequestType fType; + + JackRequest() + {} + + JackRequest(RequestType type): fType(type) + {} + + virtual ~JackRequest() + {} + + virtual int Read(JackChannelTransaction* trans) + { + return trans->Read(this, sizeof(JackRequest)); + } + + virtual int Write(JackChannelTransaction* trans) + { + return -1; + } + +}; + +/*! +\brief Result from the server. +*/ + +struct JackResult +{ + + int fResult; + + JackResult(): fResult( -1) + {} + JackResult(int status): fResult(status) + {} + virtual ~JackResult() + {} + + virtual int Read(JackChannelTransaction* trans) + { + return trans->Read(this, sizeof(JackResult)); + } + + virtual int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackResult)); + } +}; + +/*! +\brief NewClient request. +*/ + +struct JackClientNewRequest : public JackRequest +{ + + char fName[JACK_CLIENT_NAME_SIZE + 1]; + + JackClientNewRequest() + {} + JackClientNewRequest(const char* name): JackRequest(kClientNew) + { + snprintf(fName, sizeof(fName), "%s", name); + } + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fName, sizeof(JackClientNewRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackClientNewRequest)); + } +}; + +/*! +\brief NewClient result. +*/ + +struct JackClientNewResult : public JackResult +{ + + int fSharedEngine; + int fSharedClient; + int fSharedPorts; + uint32_t fProtocolVersion; + + JackClientNewResult() + {} + JackClientNewResult(int32_t status, int index1, int index2, int index3) + : JackResult(status), fSharedEngine(index1), fSharedClient(index2), fSharedPorts(index3), fProtocolVersion(0) + {} + + virtual int Read(JackChannelTransaction* trans) + { + return trans->Read(this, sizeof(JackClientNewResult)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackClientNewResult)); + } +}; + +/*! +\brief CloseClient request. +*/ + +struct JackClientCloseRequest : public JackRequest +{ + + int fRefNum; + + JackClientCloseRequest() + {} + JackClientCloseRequest(int refnum): JackRequest(kClientClose), fRefNum(refnum) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackClientCloseRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackClientCloseRequest)); + } +}; + +/*! +\brief Activate request. +*/ + +struct JackActivateRequest : public JackRequest +{ + + int fRefNum; + + JackActivateRequest() + {} + JackActivateRequest(int refnum): JackRequest(kActivateClient), fRefNum(refnum) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackActivateRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackActivateRequest)); + } + +}; + +/*! +\brief Deactivate request. +*/ + +struct JackDeactivateRequest : public JackRequest +{ + + int fRefNum; + + JackDeactivateRequest() + {} + JackDeactivateRequest(int refnum): JackRequest(kDeactivateClient), fRefNum(refnum) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackDeactivateRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackDeactivateRequest)); + } + +}; + +/*! +\brief PortRegister request. +*/ + +struct JackPortRegisterRequest : public JackRequest +{ + + int fRefNum; + char fName[JACK_PORT_NAME_SIZE + 1]; + char fPortType[JACK_PORT_TYPE_SIZE + 1]; + unsigned int fFlags; + unsigned int fBufferSize; + + JackPortRegisterRequest() + {} + JackPortRegisterRequest(int refnum, const char* name, const char* port_type, unsigned int flags, unsigned int buffer_size) + : JackRequest(kRegisterPort), fRefNum(refnum), fFlags(flags), fBufferSize(buffer_size) + { + strcpy(fName, name); + strcpy(fPortType, port_type); + } + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackPortRegisterRequest) - sizeof(JackRequest)) ; + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackPortRegisterRequest)); + } +}; + +/*! +\brief PortRegister result. +*/ + +struct JackPortRegisterResult : public JackResult +{ + + jack_port_id_t fPortIndex; + + JackPortRegisterResult(): fPortIndex(NO_PORT) + {} + + virtual int Read(JackChannelTransaction* trans) + { + return trans->Read(this, sizeof(JackPortRegisterResult)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackPortRegisterResult)); + } +}; + +/*! +\brief PortUnregister request. +*/ + +struct JackPortUnRegisterRequest : public JackRequest +{ + + int fRefNum; + int fPortIndex; + + JackPortUnRegisterRequest() + {} + JackPortUnRegisterRequest(int refnum, int index): JackRequest(kUnRegisterPort), fRefNum(refnum), fPortIndex(index) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackPortUnRegisterRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackPortUnRegisterRequest)); + } +}; + +/*! +\brief PortConnectName request. +*/ + +struct JackPortConnectNameRequest : public JackRequest +{ + + int fRefNum; + char fSrc[JACK_PORT_NAME_SIZE + 1]; + char fDst[JACK_PORT_NAME_SIZE + 1]; + + JackPortConnectNameRequest() + {} + JackPortConnectNameRequest(int refnum, const char* src_name, const char* dst_name): JackRequest(kConnectNamePorts), fRefNum(refnum) + { + strcpy(fSrc, src_name); + strcpy(fDst, dst_name); + } + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackPortConnectNameRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackPortConnectNameRequest)); + } +}; + +/*! +\brief PortDisconnectName request. +*/ + +struct JackPortDisconnectNameRequest : public JackRequest +{ + + int fRefNum; + char fSrc[JACK_PORT_NAME_SIZE + 1]; + char fDst[JACK_PORT_NAME_SIZE + 1]; + + JackPortDisconnectNameRequest() + {} + JackPortDisconnectNameRequest(int refnum, const char* src_name, const char* dst_name): JackRequest(kDisconnectNamePorts), fRefNum(refnum) + { + strcpy(fSrc, src_name); + strcpy(fDst, dst_name); + } + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackPortDisconnectNameRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackPortDisconnectNameRequest)); + } +}; + +/*! +\brief PortConnect request. +*/ + +struct JackPortConnectRequest : public JackRequest +{ + + int fRefNum; + jack_port_id_t fSrc; + jack_port_id_t fDst; + + JackPortConnectRequest() + {} + JackPortConnectRequest(int refnum, jack_port_id_t src, jack_port_id_t dst): JackRequest(kConnectPorts), fRefNum(refnum), fSrc(src), fDst(dst) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackPortConnectRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackPortConnectRequest)); + } +}; + + +/*! +\brief PortDisconnect request. +*/ + +struct JackPortDisconnectRequest : public JackRequest +{ + + int fRefNum; + jack_port_id_t fSrc; + jack_port_id_t fDst; + + JackPortDisconnectRequest() + {} + JackPortDisconnectRequest(int refnum, jack_port_id_t src, jack_port_id_t dst): JackRequest(kDisconnectPorts), fRefNum(refnum), fSrc(src), fDst(dst) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackPortDisconnectRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackPortDisconnectRequest)); + } +}; + +/*! +\brief SetBufferSize request. +*/ + +struct JackSetBufferSizeRequest : public JackRequest +{ + + jack_nframes_t fBufferSize; + + JackSetBufferSizeRequest() + {} + JackSetBufferSizeRequest(jack_nframes_t buffer_size): JackRequest(kSetBufferSize), fBufferSize(buffer_size) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fBufferSize, sizeof(JackSetBufferSizeRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackSetBufferSizeRequest)); + } +}; + +/*! +\brief SetFreeWheel request. +*/ + +struct JackSetFreeWheelRequest : public JackRequest +{ + + int fOnOff; + + JackSetFreeWheelRequest() + {} + JackSetFreeWheelRequest(int onoff): JackRequest(kSetFreeWheel), fOnOff(onoff) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fOnOff, sizeof(JackSetFreeWheelRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackSetFreeWheelRequest)); + } +}; + +/*! +\brief ReleaseTimebase request. +*/ + +struct JackReleaseTimebaseRequest : public JackRequest +{ + + int fRefNum; + + JackReleaseTimebaseRequest() + {} + JackReleaseTimebaseRequest(int refnum): JackRequest(kReleaseTimebase), fRefNum(refnum) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackReleaseTimebaseRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackReleaseTimebaseRequest)); + } + +}; + +/*! +\brief SetTimebaseCallback request. +*/ + +struct JackSetTimebaseCallbackRequest : public JackRequest +{ + + int fRefNum; + int fConditionnal; + + JackSetTimebaseCallbackRequest() + {} + JackSetTimebaseCallbackRequest(int refnum, int conditional): JackRequest(kSetTimebaseCallback), fRefNum(refnum), fConditionnal(conditional) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackSetTimebaseCallbackRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackSetTimebaseCallbackRequest)); + } +}; + +/*! +\brief ClientNotification request. +*/ + +struct JackClientNotificationRequest : public JackRequest +{ + + int fRefNum; + int fNotify; + int fValue; + + JackClientNotificationRequest() + {} + JackClientNotificationRequest(int refnum, int notify, int value) + : JackRequest(kNotification), fRefNum(refnum), fNotify(notify), fValue(value) + {} + + int Read(JackChannelTransaction* trans) + { + return trans->Read(&fRefNum, sizeof(JackClientNotificationRequest) - sizeof(JackRequest)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackClientNotificationRequest)); + } + +}; + +/*! +\brief ClientNotification. +*/ + +struct JackClientNotification +{ + char fName[JACK_CLIENT_NAME_SIZE + 1]; + int fRefNum; + int fNotify; + int fValue; + int fSync; + + JackClientNotification(): fNotify( -1), fValue( -1) + {} + JackClientNotification(const char* name, int refnum, int notify, int sync, int value) + : fRefNum(refnum), fNotify(notify), fValue(value), fSync(sync) + { + snprintf(fName, sizeof(fName), "%s", name); + } + + int Read(JackChannelTransaction* trans) + { + return trans->Read(this, sizeof(JackClientNotification)); + } + + int Write(JackChannelTransaction* trans) + { + return trans->Write(this, sizeof(JackClientNotification)); + } + +}; + +} // end of namespace + +#endif diff --git a/common/JackServer.cpp b/common/JackServer.cpp new file mode 100644 index 00000000..269b41fd --- /dev/null +++ b/common/JackServer.cpp @@ -0,0 +1,339 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackServer.h" +#include "JackTime.h" +#include "JackFreewheelDriver.h" +#include "JackLoopbackDriver.h" +#include "JackThreadedDriver.h" +#include "JackGlobals.h" +#include "JackEngine.h" +#include "JackAudioDriver.h" +#include "JackChannel.h" +#include "JackClientControl.h" +#include "JackEngineControl.h" +#include "JackSyncInterface.h" +#include "JackGraphManager.h" + +#ifdef __APPLE_ +#include <CoreFoundation/CFNotificationCenter.h> +#endif + +namespace Jack +{ + +JackServer* JackServer::fInstance = NULL; + +JackServer::JackServer(bool sync, long timeout, bool rt, long priority, long loopback, bool verbose) +{ + JackGlobals::InitServer(); + for (int i = 0; i < CLIENT_NUM; i++) + fSynchroTable[i] = JackGlobals::MakeSynchro(); + fGraphManager = new JackGraphManager(); + fEngineControl = new JackEngineControl(); + fSignal = JackGlobals::MakeInterProcessSync(); + fEngine = new JackEngine(fGraphManager, fSynchroTable, fEngineControl, fSignal, sync, timeout, rt, priority, verbose); + fFreewheelDriver = new JackThreadedDriver(new JackFreewheelDriver("freewheel", fEngine, fSynchroTable)); + fLoopbackDriver = new JackLoopbackDriver("loopback", fEngine, fSynchroTable); + fChannel = JackGlobals::MakeServerChannel(); + fState = new JackConnectionManager(); + fFreewheel = false; + fSyncMode = sync; + fLoopback = loopback; + fDriverInfo = NULL; + fAudioDriver = NULL; + fInstance = this; // Unique instance +} + +JackServer::~JackServer() +{ + for (int i = 0; i < CLIENT_NUM; i++) + delete fSynchroTable[i]; + delete fGraphManager; + delete fAudioDriver; + delete fFreewheelDriver; + delete fLoopbackDriver; + delete fEngine; + delete fChannel; + delete fEngineControl; + delete fSignal; + delete fState; + if (fDriverInfo) { + UnloadDriverModule(fDriverInfo->handle); + free(fDriverInfo); + } + JackGlobals::Destroy(); +} + +// TODO : better handling of intermediate failing cases... + +int JackServer::Open(jack_driver_desc_t* driver_desc, JSList* driver_params) +{ + if (fChannel->Open(this) < 0) { + jack_error("Server channel open error"); + return -1; + } + + if (fEngine->Open() != 0) { + jack_error("Cannot open engine"); + return -1; + } + + if ((fDriverInfo = jack_load_driver(driver_desc)) == NULL) { + return -1; + } + + if ((fAudioDriver = fDriverInfo->initialize(fEngine, fSynchroTable, driver_params)) == NULL) { + jack_error("Cannot initialize driver"); + return -1; + } + + if (fFreewheelDriver->Open() != 0) { // before engine open + jack_error("Cannot open driver"); + return -1; + } + + // Before engine open + if (fLoopbackDriver->Open(fEngineControl->fBufferSize, fEngineControl->fSampleRate, 1, 1, fLoopback, fLoopback, false, "loopback", "loopback", 0, 0) != 0) { + jack_error("Cannot open driver"); + return -1; + } + + if (fAudioDriver->Attach() != 0) { + jack_error("Cannot attach audio driver"); + return -1; + } + + if (fLoopback > 0 && fLoopbackDriver->Attach() != 0) { + jack_error("Cannot attach loopback driver"); + return -1; + } + + fFreewheelDriver->SetMaster(false); + fAudioDriver->SetMaster(true); + if (fLoopback > 0) + fAudioDriver->AddSlave(fLoopbackDriver); + fAudioDriver->AddSlave(fFreewheelDriver); // After ??? + InitTime(); + +#ifdef __APPLE__ + // Send notification to be used in the Jack Router + CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), + CFSTR("com.grame.jackserver.start"), + CFSTR("com.grame.jackserver"), + NULL, + true); +#endif + + return 0; +} + +int JackServer::Close() +{ + JackLog("JackServer::Close\n"); + fChannel->Close(); + fSignal->Destroy(); // A REVOIR + fAudioDriver->Detach(); + if (fLoopback > 0) + fLoopbackDriver->Detach(); + fAudioDriver->Close(); + fFreewheelDriver->Close(); + fLoopbackDriver->Close(); + fEngine->Close(); + +#ifdef __APPLE__ + // Send notification to be used in the Jack Router + CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), + CFSTR("com.grame.jackserver.stop"), + CFSTR("com.grame.jackserver"), + NULL, + true); +#endif + + return 0; +} + +int JackServer::Start() +{ + JackLog("JackServer::Start\n"); + fEngineControl->fFrameTimer.Init(); + return fAudioDriver->Start(); +} + +int JackServer::Stop() +{ + JackLog("JackServer::Stop\n"); + return fAudioDriver->Stop(); +} + +int JackServer::Activate(int refnum) +{ + fGraphManager->DirectConnect(fFreewheelDriver->GetClientControl()->fRefNum, refnum); + fGraphManager->DirectConnect(refnum, fFreewheelDriver->GetClientControl()->fRefNum); + return fEngine->ClientActivate(refnum); +} + +// Disconnection from the FW must be done in last otherwise an intermediate "unconnected" +// (thus unactivated) state may happen where the client is still checked for its end. +int JackServer::Deactivate(int refnum) +{ + int res = fEngine->ClientDeactivate(refnum); + + // Disconnect only when needed + if (fGraphManager->IsDirectConnection(fFreewheelDriver->GetClientControl()->fRefNum, refnum)) { + fGraphManager->DirectDisconnect(fFreewheelDriver->GetClientControl()->fRefNum, refnum); + } else { + JackLog("JackServer::Deactivate: client = %ld was not activated \n", refnum); + } + + // Disconnect only when needed + if (fGraphManager->IsDirectConnection(refnum, fFreewheelDriver->GetClientControl()->fRefNum)) { + fGraphManager->DirectDisconnect(refnum, fFreewheelDriver->GetClientControl()->fRefNum); + } else { + JackLog("JackServer::Deactivate: client = %ld was not activated \n", refnum); + } + + return res; +} + +int JackServer::SetBufferSize(jack_nframes_t nframes) +{ + JackLog("JackServer::SetBufferSize nframes = %ld\n", nframes); + + if (nframes > BUFFER_SIZE_MAX) + return -1; + + if (fAudioDriver->Stop() != 0) { + jack_error("Cannot stop audio driver"); + return -1; + } + + if (fAudioDriver->SetBufferSize(nframes) != 0) { + jack_error("Cannot SetBufferSize for audio driver"); + return -1; + } + + if (fFreewheelDriver->SetBufferSize(nframes) != 0) { + jack_error("Cannot SetBufferSize for freewheel driver"); + return -1; + } + + fEngine->NotifyBufferSize(nframes); + + fEngineControl->fFrameTimer.Init(); + return fAudioDriver->Start(); +} + +/* +Freewheel mode is implemented by switching from the (audio + freewheel) driver to the freewheel driver only: + + - "global" connection state is saved + - all audio driver ports are deconnected, thus there is no more dependancies with the audio driver + - the freewheel driver will be synchronized with the end of graph execution : all clients are connected to the freewheel driver + - the freewheel driver becomes the "master" + +Normal mode is restored with the connections state valid before freewheel mode was done. Thus one consider that +no graph state change can be done during freewheel mode. +*/ + +int JackServer::SetFreewheel(bool onoff) +{ + JackLog("JackServer::SetFreewheel state = %ld\n", onoff); + + if (fFreewheel) { + if (onoff) { + return -1; + } else { + fFreewheel = false; + fFreewheelDriver->Stop(); + fGraphManager->Restore(fState); // Restore previous connection state + fEngine->NotifyFreewheel(onoff); + fFreewheelDriver->SetMaster(false); + fEngineControl->fFrameTimer.Init(); + return fAudioDriver->Start(); + } + } else { + if (onoff) { + fFreewheel = true; + fAudioDriver->Stop(); + fGraphManager->Save(fState); // Save connection state + fGraphManager->DisconnectAllPorts(fAudioDriver->GetClientControl()->fRefNum); + fEngine->NotifyFreewheel(onoff); + fFreewheelDriver->SetMaster(true); + return fFreewheelDriver->Start(); + } else { + return -1; + } + } +} + +// Coming from the RT thread or server channel +void JackServer::Notify(int refnum, int notify, int value) +{ + switch (notify) { + + case JackNotifyChannelInterface::kGraphOrderCallback: + fEngine->NotifyGraphReorder(); + break; + + case JackNotifyChannelInterface::kXRunCallback: + fEngine->NotifyXRun(refnum); + break; + + case JackNotifyChannelInterface::kZombifyClient: + fEngine->ZombifyClient(refnum); + break; + + case JackNotifyChannelInterface::kDeadClient: + JackLog("JackServer: kDeadClient ref = %ld\n", refnum); + Deactivate(refnum); + fEngine->ClientClose(refnum); + break; + } +} + +JackEngine* JackServer::GetEngine() +{ + return fEngine; +} + +JackSynchro** JackServer::GetSynchroTable() +{ + return fSynchroTable; +} + +JackEngineControl* JackServer::GetEngineControl() +{ + return fEngineControl; +} + +JackGraphManager* JackServer::GetGraphManager() +{ + return fGraphManager; +} + +void JackServer::PrintState() +{ + fAudioDriver->PrintState(); + fEngine->PrintState(); +} + +} // end of namespace + diff --git a/common/JackServer.h b/common/JackServer.h new file mode 100644 index 00000000..8766844b --- /dev/null +++ b/common/JackServer.h @@ -0,0 +1,96 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackServer__ +#define __JackServer__ + +#include "JackExports.h" +#include "driver_interface.h" +#include "JackDriverLoader.h" +#include "jslist.h" + +namespace Jack +{ + +class JackGraphManager; +class JackConnectionManager; +class JackDriverClientInterface; +class JackServerChannelInterface; +class JackSyncInterface; +struct JackEngineControl; +class JackEngine; + +/*! +\brief The Jack server. +*/ + +class EXPORT JackServer +{ + + private: + + jack_driver_info_t* fDriverInfo; + JackDriverClientInterface* fAudioDriver; + JackDriverClientInterface* fFreewheelDriver; + JackDriverClientInterface* fLoopbackDriver; + JackEngine* fEngine; + JackEngineControl* fEngineControl; + JackGraphManager* fGraphManager; + JackServerChannelInterface* fChannel; + JackConnectionManager* fState; + JackSynchro* fSynchroTable[CLIENT_NUM]; + JackSyncInterface* fSignal; + bool fSyncMode; + bool fFreewheel; + long fLoopback; + + public: + + JackServer(bool sync, long timeout, bool rt, long priority, long loopback, bool verbose); + virtual ~JackServer(); + + int Open(jack_driver_desc_t* driver_desc, JSList* driver_params); + + int Close(); + + int Start(); + int Stop(); + + int Activate(int refnum); + int Deactivate(int refnum); + + int SetBufferSize(jack_nframes_t nframes); + int SetFreewheel(bool onoff); + void Notify(int refnum, int notify, int value); + + JackEngine* GetEngine(); + JackEngineControl* GetEngineControl(); + JackSynchro** GetSynchroTable(); + JackGraphManager* GetGraphManager(); + + void PrintState(); + + static JackServer* fInstance; // Unique instance +}; + +} // end of namespace + + +#endif diff --git a/common/JackServerAPI.cpp b/common/JackServerAPI.cpp new file mode 100644 index 00000000..0edbfc42 --- /dev/null +++ b/common/JackServerAPI.cpp @@ -0,0 +1,174 @@ +/* +Copyright (C) 2001-2003 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackInternalClient.h" +#include "JackGraphManager.h" +#include "JackServer.h" +#include "JackDebugClient.h" +#include "JackServerGlobals.h" +#include "JackError.h" +#include "varargs.h" + +/* +TODO: + +- implement the "jack_client_new", "jack_client_open", "jack_client_close" API so that we can provide a libjackdmp shared library +to be used by clients for direct access. + +- automatic launch of the jack server with the first client open, automatic close when the last client exit. Use of a jackd.rsc configuration file. + +*/ + +#ifdef WIN32 + #define EXPORT __declspec(dllexport) +#else + #define EXPORT +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + EXPORT jack_client_t* my_jack_internal_client_new(const char* client_name); + EXPORT void my_jack_internal_client_close(jack_client_t* ext_client); + + EXPORT jack_client_t * jack_client_open (const char *client_name, + jack_options_t options, + jack_status_t *status, ...); + EXPORT jack_client_t * jack_client_new (const char *client_name); + EXPORT int jack_client_close (jack_client_t *client); + +#ifdef __cplusplus +} +#endif + +using namespace Jack; + +EXPORT jack_client_t* my_jack_internal_client_new(const char* client_name) +{ + JackLog("jack_internal_client_new %s", client_name); + if (client_name == NULL) { + jack_error("jack_internal_client_new called with a NULL client_name"); + return NULL; + } +#ifdef __CLIENTDEBUG__ + JackClient* client = new JackDebugClient(new JackInternalClient(JackServer::fInstance, GetSynchroTable())); // Debug mode +#else + JackClient* client = new JackInternalClient(JackServer::fInstance, GetSynchroTable()); // To improve... +#endif + + int res = client->Open(client_name); + if (res < 0) { + delete client; + return NULL; + } else { + return (jack_client_t*)client; + } +} + +EXPORT void my_jack_internal_client_close(jack_client_t* ext_client) +{ + JackLog("jack_internal_client_close"); + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_internal_client_close called with a NULL client"); + } else { + int res = client->Deactivate(); + JackLog("jack_internal_client_close Deactivate %ld", res); + res = client->Close(); + delete client; + JackLog("jack_internal_client_close OK"); + } +} + +EXPORT jack_client_t* jack_client_new(const char* client_name) +{ + int options = JackUseExactName; + if (getenv("JACK_START_SERVER") == NULL) + options |= JackNoStartServer; + + return jack_client_open(client_name, (jack_options_t)options, NULL); +} + +// TO BE IMPLEMENTED PROPERLY +EXPORT jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...) +{ + va_list ap; /* variable argument pointer */ + jack_varargs_t va; /* variable arguments */ + jack_status_t my_status; + + if (status == NULL) /* no status from caller? */ + status = &my_status; /* use local status word */ + *status = (jack_status_t)0; + + /* validate parameters */ + if ((options & ~JackOpenOptions)) { + int my_status1 = *status | (JackFailure | JackInvalidOption); + *status = (jack_status_t)my_status1; + return NULL; + } + + /* parse variable arguments */ + va_start(ap, status); + jack_varargs_parse(options, ap, &va); + va_end(ap); + + JackLog("jack_client_open %s\n", client_name); + if (client_name == NULL) { + jack_error("jack_client_new called with a NULL client_name"); + return NULL; + } + + JackServerGlobals::Init(); // jack server initialisation + +#ifdef __CLIENTDEBUG__ + JackClient* client = new JackDebugClient(new JackInternalClient(JackServer::fInstance, GetSynchroTable())); // Debug mode +#else + JackClient* client = new JackInternalClient(JackServer::fInstance, GetSynchroTable()); // To improve... +#endif + + int res = client->Open(client_name); + if (res < 0) { + delete client; + JackServerGlobals::Destroy(); // jack server destruction + return NULL; + } else { + *status = (jack_status_t)0; + return (jack_client_t*)client; + } + return NULL; +} + +EXPORT int jack_client_close(jack_client_t* ext_client) +{ + JackLog("jack_client_close\n"); + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_client_close called with a NULL client"); + return -1; + } + int res = client->Close(); + delete client; + JackLog("jack_client_close OK\n"); + JackServerGlobals::Destroy(); // jack library destruction + return res; +} + diff --git a/common/JackServerGlobals.cpp b/common/JackServerGlobals.cpp new file mode 100644 index 00000000..96b21e83 --- /dev/null +++ b/common/JackServerGlobals.cpp @@ -0,0 +1,389 @@ +/* +Copyright (C) 2005 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackServerGlobals.h" +#include "JackError.h" +#include "shm.h" +#include <getopt.h> + +#ifndef WIN32 + #include <dirent.h> +#endif + +#define DEFAULT_TMP_DIR "/tmp" +char* jack_tmpdir = DEFAULT_TMP_DIR; +static char* server_name = "default"; +static int realtime = 0; +static int client_timeout = 0; /* msecs; if zero, use period size. */ +static int realtime_priority = 10; +static int verbose_aux = 0; +static int do_mlock = 1; +static unsigned int port_max = 128; +static int loopback = 0; +static int do_unlock = 0; +static int temporary = 0; + +namespace Jack +{ + +JackServerGlobals* JackServerGlobals::fGlobals = NULL; +long JackServerGlobals::fClientCount = 0; +JackServer* JackServerGlobals::fServer = NULL; + +#ifndef WIN32 + +static char* jack_default_server_name(void) +{ + char *server_name; + if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL) + server_name = "default"; + return server_name; +} + +/* returns the name of the per-user subdirectory of jack_tmpdir */ +static char* jack_user_dir(void) +{ + static char user_dir[PATH_MAX] = ""; + + /* format the path name on the first call */ + if (user_dir[0] == '\0') { + snprintf (user_dir, sizeof (user_dir), "%s/jack-%d", + jack_tmpdir, getuid ()); + } + + return user_dir; +} + +/* returns the name of the per-server subdirectory of jack_user_dir() */ + +static char* get_jack_server_dir(const char* toto) +{ + static char server_dir[PATH_MAX] = ""; + + // format the path name on the first call + if (server_dir[0] == '\0') { + snprintf (server_dir, sizeof (server_dir), "%s/%s", + jack_user_dir (), server_name); + } + + return server_dir; +} + +static void +jack_cleanup_files (const char *server_name) +{ + DIR *dir; + struct dirent *dirent; + char *dir_name = get_jack_server_dir (server_name); + + /* On termination, we remove all files that jackd creates so + * subsequent attempts to start jackd will not believe that an + * instance is already running. If the server crashes or is + * terminated with SIGKILL, this is not possible. So, cleanup + * is also attempted when jackd starts. + * + * There are several tricky issues. First, the previous JACK + * server may have run for a different user ID, so its files + * may be inaccessible. This is handled by using a separate + * JACK_TMP_DIR subdirectory for each user. Second, there may + * be other servers running with different names. Each gets + * its own subdirectory within the per-user directory. The + * current process has already registered as `server_name', so + * we know there is no other server actively using that name. + */ + + /* nothing to do if the server directory does not exist */ + if ((dir = opendir (dir_name)) == NULL) { + return ; + } + + /* unlink all the files in this directory, they are mine */ + while ((dirent = readdir (dir)) != NULL) { + + char fullpath[PATH_MAX]; + + if ((strcmp (dirent->d_name, ".") == 0) + || (strcmp (dirent->d_name, "..") == 0)) { + continue; + } + + snprintf (fullpath, sizeof (fullpath), "%s/%s", + dir_name, dirent->d_name); + + if (unlink (fullpath)) { + jack_error ("cannot unlink `%s' (%s)", fullpath, + strerror (errno)); + } + } + + closedir (dir); + + /* now, delete the per-server subdirectory, itself */ + if (rmdir (dir_name)) { + jack_error ("cannot remove `%s' (%s)", dir_name, + strerror (errno)); + } + + /* finally, delete the per-user subdirectory, if empty */ + if (rmdir (jack_user_dir ())) { + if (errno != ENOTEMPTY) { + jack_error ("cannot remove `%s' (%s)", + jack_user_dir (), strerror (errno)); + } + } +} + +#endif + +int JackServerGlobals::JackStart(jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int time_out_ms, int rt, int priority, int loopback, int verbose) +{ + JackLog("Jackdmp: sync = %ld timeout = %ld rt = %ld priority = %ld verbose = %ld \n", sync, time_out_ms, rt, priority, verbose); + fServer = new JackServer(sync, time_out_ms, rt, priority, loopback, verbose); + int res = fServer->Open(driver_desc, driver_params); + return (res < 0) ? res : fServer->Start(); +} + +int JackServerGlobals::JackStop() +{ + fServer->Stop(); + fServer->Close(); + JackLog("Jackdmp: server close\n"); + delete fServer; + JackLog("Jackdmp: delete server\n"); + return 0; +} + +int JackServerGlobals::JackDelete() +{ + delete fServer; + JackLog("Jackdmp: delete server\n"); + return 0; +} + +// Temporary : to test + +JackServerGlobals::JackServerGlobals() +{ + jack_driver_desc_t* driver_desc; + const char *options = "-ad:P:uvshVRL:STFl:t:mn:p:"; + struct option long_options[] = { + { "driver", 1, 0, 'd' + }, + { "verbose", 0, 0, 'v' }, + { "help", 0, 0, 'h' }, + { "port-max", 1, 0, 'p' }, + { "no-mlock", 0, 0, 'm' }, + { "name", 0, 0, 'n' }, + { "unlock", 0, 0, 'u' }, + { "realtime", 0, 0, 'R' }, + { "loopback", 0, 0, 'L' }, + { "realtime-priority", 1, 0, 'P' }, + { "timeout", 1, 0, 't' }, + { "temporary", 0, 0, 'T' }, + { "version", 0, 0, 'V' }, + { "silent", 0, 0, 's' }, + { "sync", 0, 0, 'S' }, + { 0, 0, 0, 0 } + }; + int opt = 0; + int option_index = 0; + int seen_driver = 0; + char *driver_name = NULL; + char **driver_args = NULL; + JSList* driver_params; + int driver_nargs = 1; + JSList* drivers = NULL; + char* server_name = NULL; + int show_version = 0; + int sync = 0; + int rc, i; + + int argc = 8; + //char* argv[] = {"jackdmp", "-R", "-v", "-d", "coreaudio", "-p", "512"}; + char* argv[] = {"jackdmp", "-R", "-S", "-v", "-d", "portaudio", "-p", "512"}; + + for (i = 0; i < argc; i++) { + printf("arg %i %s\n", i, argv[i]); + } + + opterr = 0; + while (!seen_driver && + (opt = getopt_long(argc, argv, options, + long_options, &option_index)) != EOF) { + switch (opt) { + + case 'd': + seen_driver = 1; + driver_name = optarg; + break; + + case 'v': + verbose_aux = 1; + break; + + case 's': + // jack_set_error_function(silent_jack_error_callback); + break; + + case 'S': + sync = 1; + break; + + case 'n': + server_name = optarg; + break; + + case 'm': + do_mlock = 0; + break; + + case 'p': + port_max = (unsigned int)atol(optarg); + break; + + case 'P': + realtime_priority = atoi(optarg); + break; + + case 'R': + realtime = 1; + break; + + case 'L': + loopback = atoi(optarg); + break; + + case 'T': + temporary = 1; + break; + + case 't': + client_timeout = atoi(optarg); + break; + + case 'u': + do_unlock = 1; + break; + + case 'V': + show_version = 1; + break; + + default: + fprintf(stderr, "unknown option character %c\n", + optopt); + /*fallthru*/ + case 'h': + //usage(stdout); + return ; + } + } + + drivers = jack_drivers_load (drivers); + if (!drivers) { + fprintf (stderr, "jackdmp: no drivers found; exiting\n"); + exit (1); + } + + driver_desc = jack_find_driver_descriptor (drivers, driver_name); + if (!driver_desc) { + fprintf (stderr, "jackdmp: unknown driver '%s'\n", driver_name); + exit (1); + } + + if (optind < argc) { + driver_nargs = 1 + argc - optind; + } else { + driver_nargs = 1; + } + + if (driver_nargs == 0) { + fprintf (stderr, "No driver specified ... hmm. JACK won't do" + " anything when run like this.\n"); + return ; + } + + driver_args = (char **) malloc (sizeof (char *) * driver_nargs); + driver_args[0] = driver_name; + + for (i = 1; i < driver_nargs; i++) { + driver_args[i] = argv[optind++]; + } + + if (jack_parse_driver_params (driver_desc, driver_nargs, + driver_args, &driver_params)) { + return ; + } + +#ifndef WIN32 + if (server_name == NULL) + server_name = jack_default_server_name (); +#endif + + rc = jack_register_server (server_name); + + /* clean up shared memory and files from any previous + * instance of this server name */ + jack_cleanup_shm(); +#ifndef WIN32 + jack_cleanup_files(server_name); +#endif + + if (!realtime && client_timeout == 0) + client_timeout = 500; /* 0.5 sec; usable when non realtime. */ + + int res = JackStart(driver_desc, driver_params, sync, client_timeout, realtime, realtime_priority, loopback, verbose_aux); + if (res < 0) { + jack_error("Cannot start server... exit"); + JackDelete(); + return ; + } +} + +JackServerGlobals::~JackServerGlobals() +{ + JackLog("~JackServerGlobals\n"); + JackStop(); + jack_cleanup_shm(); +#ifndef WIN32 + jack_cleanup_files(server_name); +#endif + jack_unregister_server(server_name); +} + +void JackServerGlobals::Init() +{ + if (fClientCount++ == 0 && !fGlobals) { + JackLog("JackServerGlobals Init %x\n", fGlobals); + fGlobals = new JackServerGlobals(); + } +} + +void JackServerGlobals::Destroy() +{ + if (--fClientCount == 0 && fGlobals) { + JackLog("JackServerGlobals Destroy %x\n", fGlobals); + delete fGlobals; + fGlobals = NULL; + } +} + +} // end of namespace + + diff --git a/common/JackServerGlobals.h b/common/JackServerGlobals.h new file mode 100644 index 00000000..d043ecbe --- /dev/null +++ b/common/JackServerGlobals.h @@ -0,0 +1,58 @@ +/* +Copyright (C) 2005 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackServerGlobals__ +#define __JackServerGlobals__ + +#include "driver_interface.h" +#include "driver_parse.h" +#include "JackDriverLoader.h" +#include "JackServer.h" + +#include <assert.h> + +namespace Jack +{ + +class JackClient; + +/*! +\brief Global server static structure: singleton kind of pattern. +*/ + +struct JackServerGlobals +{ + static long fClientCount; + static JackServerGlobals* fGlobals; + static JackServer* fServer; + + JackServerGlobals(); + virtual ~JackServerGlobals(); + + static void Init(); + static void Destroy(); + static int JackStart(jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int time_out_ms, int rt, int priority, int loopback, int verbose); + static int JackStop(); + static int JackDelete(); +}; + +} // end of namespace + +#endif + diff --git a/common/JackShmMem.cpp b/common/JackShmMem.cpp new file mode 100644 index 00000000..d9b93b24 --- /dev/null +++ b/common/JackShmMem.cpp @@ -0,0 +1,92 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackShmMem.h" +#include "JackError.h" +#include <stdio.h> + +namespace Jack +{ + +unsigned long JackShmMem::fSegmentNum = 0; +unsigned long JackShmMem::fSegmentCount = 0; + +jack_shm_info_t JackShmMem::gInfo; + +void* JackShmMem::operator new(size_t size) +{ + jack_shm_info_t info; + JackShmMem* obj; + char name[64]; + + snprintf(name, sizeof(name), "/jack_shared%ld", JackShmMem::fSegmentNum++); + + if (JackShmMem::fSegmentCount++ == 0) { + JackLog("jack_initialize_shm\n"); + if (jack_initialize_shm_server() < 0) { + jack_error("cannot initialize shm", strerror(errno)); + goto error; + } + } + + if (jack_shmalloc(name, size, &info)) { + jack_error("cannot create shared memory segment of size = %d", size, strerror(errno)); + goto error; + } + + if (jack_attach_shm(&info)) { + jack_error("cannot attach shared memory segment name = %s err = %s", name, strerror(errno)); + jack_destroy_shm(&info); + goto error; + } + + obj = (JackShmMem*)jack_shm_addr(&info); + // It is unsafe to set object fields directly (may be overwritten during object initialization), + // so use an intermediate global data + gInfo.index = info.index; + gInfo.attached_at = info.attached_at; + JackLog("JackShmMem::new index = %ld attached = %x size = %ld \n", info.index, info.attached_at, size); + return obj; + +error: + jack_error("JackShmMem::new bad alloc", size); + throw new std::bad_alloc; +} + +void JackShmMem::operator delete(void* p, size_t size) +{ + jack_shm_info_t info; + JackShmMem* obj = (JackShmMem*)p; + info.index = obj->fInfo.index; + info.attached_at = obj->fInfo.attached_at; + + JackLog("JackShmMem::delete size = %ld index = %ld\n", size, info.index); + + jack_release_shm(&info); + jack_destroy_shm(&info); + + if (--JackShmMem::fSegmentCount == 0) { + JackLog("jack_cleanup_shm\n"); + jack_cleanup_shm(); + } +} + +} // end of namespace + diff --git a/common/JackShmMem.h b/common/JackShmMem.h new file mode 100644 index 00000000..cb9b5d6b --- /dev/null +++ b/common/JackShmMem.h @@ -0,0 +1,308 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackShmMem__ +#define __JackShmMem__ + +#include "shm.h" +#include <new> // GCC 4.0 +#include "JackError.h" +#include <errno.h> + +namespace Jack +{ + +/*! +\brief The base class for shared memory management. + +A class which objects need to be allocated in shared memory derives from this class. +*/ + +class JackShmMem +{ + + private: + + jack_shm_info_t fInfo; + static unsigned long fSegmentNum; + static unsigned long fSegmentCount; + static jack_shm_info_t gInfo; + + public: + + void* operator new(size_t size); + void operator delete(void* p, size_t size); + + JackShmMem() + { + fInfo.index = gInfo.index; + fInfo.attached_at = gInfo.attached_at; + } + + virtual ~JackShmMem() + {} + + int GetShmIndex() + { + return fInfo.index; + } + + char* GetShmAddress() + { + return (char*)fInfo.attached_at; + } + +}; + +/*! +\brief Pointer on shared memory segment in the client side. +*/ + +template <class T> +class JackShmReadWritePtr +{ + + private: + + jack_shm_info_t fInfo; + + void Init(int index) + { + if (fInfo.index < 0 && index >= 0) { + JackLog("JackShmReadWritePtr::Init %ld %ld\n", index, fInfo.index); + if (jack_initialize_shm_client() < 0) + throw - 1; + fInfo.index = index; + if (jack_attach_shm(&fInfo)) { + //jack_error("cannot attach shared memory segment", strerror(errno)); + throw - 2; + } + } + } + + public: + + JackShmReadWritePtr() + { + fInfo.index = -1; + fInfo.attached_at = NULL; + } + + JackShmReadWritePtr(int index) + { + Init(index); + } + + virtual ~JackShmReadWritePtr() + { + if (fInfo.index >= 0) { + JackLog("JackShmReadWritePtr::~JackShmReadWritePtr %ld\n", fInfo.index); + jack_release_shm(&fInfo); + fInfo.index = -1; + } + } + + T* operator->() const + { + return (T*)fInfo.attached_at; + } + + operator T*() const + { + return (T*)fInfo.attached_at; + } + + JackShmReadWritePtr& operator=(int index) + { + Init(index); + return *this; + } + + int GetShmIndex() + { + return fInfo.index; + } + + T* GetShmAddress() + { + return (T*)fInfo.attached_at; + } +}; + +/*! +\brief Pointer on shared memory segment in the client side: destroy the segment (used client control) +*/ + +template <class T> +class JackShmReadWritePtr1 +{ + + private: + + jack_shm_info_t fInfo; + + void Init(int index) + { + if (fInfo.index < 0 && index >= 0) { + JackLog("JackShmReadWritePtr1::Init %ld %ld\n", index, fInfo.index); + if (jack_initialize_shm_client() < 0) + throw - 1; + fInfo.index = index; + if (jack_attach_shm(&fInfo)) { + //jack_error("cannot attach shared memory segment", strerror(errno)); + throw - 2; + } + /* + nobody else needs to access this shared memory any more, so + destroy it. because we have our own attachment to it, it won't + vanish till we exit (and release it). + */ + jack_destroy_shm(&fInfo); + } + } + + public: + + JackShmReadWritePtr1() + { + fInfo.index = -1; + fInfo.attached_at = NULL; + } + + JackShmReadWritePtr1(int index) + { + Init(index); + } + + virtual ~JackShmReadWritePtr1() + { + if (fInfo.index >= 0) { + JackLog("JackShmReadWritePtr1::~JackShmReadWritePtr1 %ld\n", fInfo.index); + jack_release_shm(&fInfo); + fInfo.index = -1; + } + } + + T* operator->() const + { + return (T*)fInfo.attached_at; + } + + operator T*() const + { + return (T*)fInfo.attached_at; + } + + JackShmReadWritePtr1& operator=(int index) + { + Init(index); + return *this; + } + + int GetShmIndex() + { + return fInfo.index; + } + + T* GetShmAddress() + { + return (T*)fInfo.attached_at; + } +}; + +/*! +\brief Pointer on shared memory segment in the client side. +*/ + +template <class T> +class JackShmReadPtr +{ + + private: + + jack_shm_info_t fInfo; + + void Init(int index) + { + if (fInfo.index < 0 && index >= 0) { + JackLog("JackShmPtrRead::Init %ld %ld\n", index, fInfo.index); + if (jack_initialize_shm_client() < 0) + throw - 1; + fInfo.index = index; + if (jack_attach_shm_read(&fInfo)) { + //jack_error("cannot attach shared memory segment", strerror(errno)); + throw - 2; + } + } + } + + public: + + JackShmReadPtr() + { + fInfo.index = -1; + fInfo.attached_at = NULL; + } + + JackShmReadPtr(int index) + { + Init(index); + } + + virtual ~JackShmReadPtr() + { + if (fInfo.index >= 0) { + JackLog("JackShmPtrRead::~JackShmPtrRead %ld\n", fInfo.index); + jack_release_shm(&fInfo); + fInfo.index = -1; + } + } + + T* operator->() const + { + return (T*)fInfo.attached_at; + } + + operator T*() const + { + return (T*)fInfo.attached_at; + } + + JackShmReadPtr& operator=(int index) + { + Init(index); + return *this; + } + + int GetShmIndex() + { + return fInfo.index; + } + + T* GetShmAddress() + { + return (T*)fInfo.attached_at; + } + +}; + +} // end of namespace + +#endif diff --git a/common/JackSocket.cpp b/common/JackSocket.cpp new file mode 100644 index 00000000..c0500081 --- /dev/null +++ b/common/JackSocket.cpp @@ -0,0 +1,312 @@ +/* +Copyright (C) 2004-2006 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 "JackSocket.h" +#include "JackError.h" +#include <string.h> + +namespace Jack +{ + +JackClientSocket::JackClientSocket(int socket): fSocket(socket) +{} + +void JackClientSocket::SetReadTimeOut(long sec) +{ + struct timeval timout; + timout.tv_sec = sec; + timout.tv_usec = 0; + if (setsockopt(fSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timout, sizeof(timeval)) < 0) { + JackLog("setsockopt SO_RCVTIMEO fd = %ld err = (%s)\n", fSocket, strerror(errno)); + } +} + +void JackClientSocket::SetWriteTimeOut(long sec) +{ + struct timeval timout; + timout.tv_sec = sec ; + timout.tv_usec = 0; + if (setsockopt(fSocket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timout, sizeof(timeval)) < 0) { + JackLog("setsockopt SO_SNDTIMEO fd = %ld err = (%s)\n", fSocket, strerror(errno)); + } +} + +int JackClientSocket::Connect(const char* dir, const char* name, int which) // A revoir : utilisation de "which" +{ + struct sockaddr_un addr; + + if ((fSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + jack_error("Cannot create socket (%s)", strerror(errno)); + return -1; + } + + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%s/jack_%s", dir, name); + + JackLog("Connect: addr.sun_path %s\n", addr.sun_path); + + if (connect(fSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + jack_error("Cannot connect to server socket (%s)", strerror(errno)); + close(fSocket); + return -1; + } + +#ifdef __APPLE__ + int on = 1 ; + if (setsockopt(fSocket, SOL_SOCKET, SO_NOSIGPIPE, (const char*)&on, sizeof(on)) < 0) { + JackLog("setsockopt SO_NOSIGPIPE fd = %ld err = %s\n", fSocket, strerror(errno)); + } +#endif + + return 0; +} + +int JackClientSocket::Connect(const char* dir, int which) +{ + struct sockaddr_un addr; + + if ((fSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + jack_error("Cannot create socket (%s)", strerror(errno)); + return -1; + } + + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%s/jack_%d", dir, which); + + JackLog("Connect: addr.sun_path %s\n", addr.sun_path); + + if (connect(fSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + jack_error("Cannot connect to server socket (%s)", strerror(errno)); + close(fSocket); + return -1; + } + +#ifdef __APPLE__ + int on = 1 ; + if (setsockopt(fSocket, SOL_SOCKET, SO_NOSIGPIPE, (const char*)&on, sizeof(on)) < 0) { + JackLog("setsockopt SO_NOSIGPIPE fd = %ld err = %s\n", fSocket, strerror(errno)); + } +#endif + + return 0; +} + +int JackClientSocket::Close() +{ + JackLog("JackClientSocket::Close\n"); + //shutdown(fSocket, SHUT_RDWR); + if (fSocket > 0) { + close(fSocket); + fSocket = -1; + return 0; + } else { + return -1; + } +} + +int JackClientSocket::Read(void* data, int len) +{ + int len1; + JackLog("JackClientSocket::Read len = %ld\n", len); + + if ((len1 = read(fSocket, data, len)) != len) { + jack_error("Cannot read socket %d %d (%s)", len, len1, strerror(errno)); + if (errno == EWOULDBLOCK) { + JackLog("JackClientSocket::Read time out\n"); + return 0; + } else { + return -1; + } + } else { + return 0; + } +} + +int JackClientSocket::Write(void* data, int len) +{ + if (write(fSocket, data, len) != len) { + jack_error("Cannot write socket fd %ld (%s)", fSocket, strerror(errno)); + return -1; + } else { + return 0; + } +} + +/* +void +jack_cleanup_files () +{ + DIR *dir; + struct dirent *dirent; + + // its important that we remove all files that jackd creates + // because otherwise subsequent attempts to start jackd will + // believe that an instance is already running. + + + if ((dir = opendir (jack_server_dir)) == NULL) { + fprintf (stderr, "jack(%d): cannot open jack FIFO directory " + "(%s)\n", getpid(), strerror (errno)); + return; + } + + while ((dirent = readdir (dir)) != NULL) { + if (strncmp (dirent->d_name, "jack-", 5) == 0 || + strncmp (dirent->d_name, "jack_", 5) == 0) { + char fullpath[PATH_MAX+1]; + snprintf (fullpath, sizeof (fullpath), "%s/%s", + jack_server_dir, dirent->d_name); + unlink (fullpath); + } + } + + closedir (dir); +} +*/ + +int JackServerSocket::Bind(const char* dir, const char* name, int which) // A revoir : utilisation de "which" +{ + struct sockaddr_un addr; + + if ((fSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + jack_error("Cannot create server socket (%s)", strerror(errno)); + return -1; + } + + addr.sun_family = AF_UNIX; + + // TO CORRECT: always reuse the same name for now... + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%s/jack_%s", dir, name); + snprintf(fName, sizeof(addr.sun_path) - 1, "%s/jack_%s", dir, name); + /* + if (access(addr.sun_path, F_OK) == 0) { + goto error; + } + */ + + JackLog("Bind: addr.sun_path %s\n", addr.sun_path); + unlink(fName); // Security... + + JackLog("Bind: addr.sun_path %s\n", addr.sun_path); + unlink(fName); // Security... + + if (bind(fSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + jack_error("Cannot bind server to socket (%s)", strerror(errno)); + goto error; + } + + if (listen(fSocket, 1) < 0) { + jack_error("Cannot enable listen on server socket (%s)", strerror(errno)); + goto error; + } + + return 0; + +error: + unlink(fName); + close(fSocket); + return -1; +} + +int JackServerSocket::Bind(const char* dir, int which) // A revoir : utilisation de "which" +{ + struct sockaddr_un addr; + + if ((fSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + jack_error ("Cannot create server socket (%s)", strerror(errno)); + return -1; + } + + addr.sun_family = AF_UNIX; + + /* + for (int i = 0; i < 999; i++) { + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1,"%s/jack_%d", dir, i); + snprintf(fName, sizeof(addr.sun_path) - 1,"%s/jack_%d", dir, i); + if (access(addr.sun_path, F_OK) != 0) { + break; + } + } + */ + + // TO CORRECT: always reuse the same name for now... + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%s/jack_%d", dir, which); + snprintf(fName, sizeof(addr.sun_path) - 1, "%s/jack_%d", dir, which); + /* + if (access(addr.sun_path, F_OK) == 0) { + goto error; + } + */ + + JackLog("Bind: addr.sun_path %s\n", addr.sun_path); + unlink(fName); // Security... + + if (bind(fSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + jack_error("Cannot bind server to socket (%s)", strerror(errno)); + goto error; + } + + if (listen(fSocket, 1) < 0) { + jack_error("Cannot enable listen on server socket (%s)", strerror(errno)); + goto error; + } + + return 0; + +error: + unlink(fName); + close(fSocket); + return -1; +} + +JackClientSocket* JackServerSocket::Accept() +{ + struct sockaddr_un client_addr; + socklen_t client_addrlen; + + memset(&client_addr, 0, sizeof(client_addr)); + client_addrlen = sizeof(client_addr); + + int fd = accept(fSocket, (struct sockaddr*) & client_addr, &client_addrlen); + if (fd < 0) { + jack_error("Cannot accept new connection (%s)", strerror(errno)); + return 0; + } else { + return new JackClientSocket(fd); + } +} + +int JackServerSocket::Close() +{ + JackLog("JackServerSocket::Close %s\n", fName); + //shutdown(fSocket, SHUT_RDWR); + if (fSocket > 0) { + //shutdown(fSocket, SHUT_RDWR); + close(fSocket); + unlink(fName); + fSocket = -1; + return 0; + } else { + return -1; + } +} + +} // end of namespace + + diff --git a/common/JackSocket.h b/common/JackSocket.h new file mode 100644 index 00000000..ad1b3640 --- /dev/null +++ b/common/JackSocket.h @@ -0,0 +1,104 @@ +/* +Copyright (C) 2004-2006 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. + +*/ + +#ifndef __JackSocket__ +#define __JackSocket__ + +#include "JackChannelTransaction.h" + +#include <sys/un.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +namespace Jack +{ + +/*! +\brief Client socket. +*/ + +class JackClientSocket : public JackChannelTransaction +{ + + private: + + int fSocket; + + public: + + JackClientSocket(): fSocket( -1) + {} + JackClientSocket(int socket); + virtual ~JackClientSocket() + {} + + int Connect(const char* dir, int which); + int Connect(const char* dir, const char* name, int which); + int Close(); + int Read(void* data, int len); + int Write(void* data, int len); + int GetFd() + { + return fSocket; + } + void SetReadTimeOut(long sec); + void SetWriteTimeOut(long sec); +}; + +/*! +\brief Server socket. +*/ + +class JackServerSocket +{ + + private: + + int fSocket; + char fName[256]; + + public: + + JackServerSocket(): fSocket( -1) + {} + virtual ~JackServerSocket() + {} + + int Bind(const char* dir, int which); + int Bind(const char* dir, const char* name, int which); + JackClientSocket* Accept(); + int Close(); + int GetFd() + { + return fSocket; + } +}; + +} // end of namespace + +#endif + diff --git a/common/JackSocketClientChannel.cpp b/common/JackSocketClientChannel.cpp new file mode 100644 index 00000000..05c77c73 --- /dev/null +++ b/common/JackSocketClientChannel.cpp @@ -0,0 +1,264 @@ +/* +Copyright (C) 2004-2006 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 "JackSocketClientChannel.h" +#include "JackRequest.h" +#include "JackClient.h" +#include "JackGlobals.h" + +namespace Jack +{ + +JackSocketClientChannel::JackSocketClientChannel() +{ + fThread = JackGlobals::MakeThread(this); + fNotificationSocket = NULL; + fClient = NULL; +} + +JackSocketClientChannel::~JackSocketClientChannel() +{ + delete fThread; + delete fNotificationSocket; +} + +int JackSocketClientChannel::Open(const char* name, JackClient* obj) +{ + JackLog("JackSocketClientChannel::Open name = %s\n", name); + + if (fRequestSocket.Connect(jack_server_dir, 0) < 0) { + jack_error("Cannot connect to server socket"); + goto error; + } + + if (fNotificationListenSocket.Bind(jack_client_dir, name, 0) < 0) { + jack_error("Cannot bind socket"); + goto error; + } + + fClient = obj; + return 0; + +error: + fRequestSocket.Close(); + fNotificationListenSocket.Close(); + return -1; +} + +void JackSocketClientChannel::Close() +{ + fRequestSocket.Close(); + fNotificationListenSocket.Close(); + if (fNotificationSocket) + fNotificationSocket->Close(); +} + +int JackSocketClientChannel::Start() +{ + JackLog("JackSocketClientChannel::Start\n"); + if (fThread->Start() != 0) { + jack_error("Cannot start Jack client listener"); + return -1; + } else { + return 0; + } +} + +void JackSocketClientChannel::Stop() +{ + JackLog("JackSocketClientChannel::Stop\n"); + fThread->Kill(); +} + +void JackSocketClientChannel::ServerSyncCall(JackRequest* req, JackResult* res, int* result) +{ + if (req->Write(&fRequestSocket) < 0) { + jack_error("Could not write request type = %ld", req->fType); + *result = -1; + return ; + } + + if (res->Read(&fRequestSocket) < 0) { + jack_error("Could not read result type = %ld", req->fType); + *result = -1; + return ; + } + + *result = res->fResult; +} + +void JackSocketClientChannel::ServerAsyncCall(JackRequest* req, JackResult* res, int* result) +{ + if (req->Write(&fRequestSocket) < 0) { + jack_error("Could not write request type = %ld", req->fType); + *result = -1; + } else { + *result = 0; + } +} + +void JackSocketClientChannel::ClientNew(const char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result) +{ + JackClientNewRequest req(name); + JackClientNewResult res; + ServerSyncCall(&req, &res, result); + *shared_engine = res.fSharedEngine; + *shared_client = res.fSharedClient; + *shared_ports = res.fSharedPorts; +} + +void JackSocketClientChannel::ClientClose(int refnum, int* result) +{ + JackClientCloseRequest req(refnum); + JackResult res; + ServerAsyncCall(&req, &res, result); +} + +void JackSocketClientChannel::ClientActivate(int refnum, int* result) +{ + JackActivateRequest req(refnum); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::ClientDeactivate(int refnum, int* result) +{ + JackDeactivateRequest req(refnum); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result) +{ + JackPortRegisterRequest req(refnum, name, "audio", flags, buffer_size); + JackPortRegisterResult res; + ServerSyncCall(&req, &res, result); + *port_index = res.fPortIndex; +} + +void JackSocketClientChannel::PortUnRegister(int refnum, jack_port_id_t port_index, int* result) +{ + JackPortUnRegisterRequest req(refnum, port_index); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::PortConnect(int refnum, const char* src, const char* dst, int* result) +{ + JackPortConnectNameRequest req(refnum, src, dst); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::PortDisconnect(int refnum, const char* src, const char* dst, int* result) +{ + JackPortDisconnectNameRequest req(refnum, src, dst); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) +{ + JackPortConnectRequest req(refnum, src, dst); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) +{ + JackPortDisconnectRequest req(refnum, src, dst); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::SetBufferSize(jack_nframes_t nframes, int* result) +{ + JackSetBufferSizeRequest req(nframes); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::SetFreewheel(int onoff, int* result) +{ + JackSetFreeWheelRequest req(onoff); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::ReleaseTimebase(int refnum, int* result) +{ + JackReleaseTimebaseRequest req(refnum); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +void JackSocketClientChannel::SetTimebaseCallback(int refnum, int conditional, int* result) +{ + JackSetTimebaseCallbackRequest req(refnum, conditional); + JackResult res; + ServerSyncCall(&req, &res, result); +} + +bool JackSocketClientChannel::Init() +{ + JackLog("JackSocketClientChannel::Init \n"); + fNotificationSocket = fNotificationListenSocket.Accept(); + // No more needed + fNotificationListenSocket.Close(); + + if (!fNotificationSocket) { + jack_error("JackSocketClientChannel: cannot establish notication socket"); + return false; + } else { + return true; + } +} + +bool JackSocketClientChannel::Execute() +{ + JackClientNotification event; + JackResult res; + + //fClient->Init(); // To be checked + + if (event.Read(fNotificationSocket) < 0) { + fNotificationSocket->Close(); + jack_error("JackSocketClientChannel read fail"); + goto error; + } + + res.fResult = fClient->ClientNotify(event.fRefNum, event.fName, event.fNotify, event.fSync, event.fValue); + + if (event.fSync) { + if (res.Write(fNotificationSocket) < 0) { + fNotificationSocket->Close(); + jack_error("JackSocketClientChannel write fail"); + goto error; + } + } + return true; + +error: + fClient->ShutDown(); + return false; +} + +} // end of namespace + + diff --git a/common/JackSocketClientChannel.h b/common/JackSocketClientChannel.h new file mode 100644 index 00000000..14953141 --- /dev/null +++ b/common/JackSocketClientChannel.h @@ -0,0 +1,89 @@ +/* +Copyright (C) 2004-2006 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. + +*/ + +#ifndef __JackSocketClientChannel__ +#define __JackSocketClientChannel__ + +#include "JackChannel.h" +#include "JackSocket.h" +#include "JackThread.h" +#include "JackRequest.h" + +namespace Jack +{ + +/*! +\brief JackClientChannel using sockets. +*/ + +class JackSocketClientChannel : public JackClientChannelInterface, public JackRunnableInterface +{ + + private: + + JackClientSocket fRequestSocket; // Socket to communicate with the server + JackServerSocket fNotificationListenSocket; // Socket listener for server notification + JackClientSocket* fNotificationSocket; // Socket for server notification + JackThread* fThread; // Thread to execute the event loop + JackClient* fClient; + + void ServerSyncCall(JackRequest* req, JackResult* res, int* result); + void ServerAsyncCall(JackRequest* req, JackResult* res, int* result); + + public: + + JackSocketClientChannel(); + virtual ~JackSocketClientChannel(); + + int Open(const char* name, JackClient* obj); + void Close(); + + int Start(); + void Stop(); + + void ClientNew(const char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result); + void ClientClose(int refnum, int* result); + + void ClientActivate(int refnum, int* result); + void ClientDeactivate(int refnum, int* result); + + void PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result); + void PortUnRegister(int refnum, jack_port_id_t port_index, int* result); + + void PortConnect(int refnum, const char* src, const char* dst, int* result); + void PortDisconnect(int refnum, const char* src, const char* dst, int* result); + + void PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result); + void PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result); + + void SetBufferSize(jack_nframes_t nframes, int* result); + void SetFreewheel(int onoff, int* result); + + void ReleaseTimebase(int refnum, int* result); + void SetTimebaseCallback(int refnum, int conditional, int* result); + + // JackRunnableInterface interface + bool Init(); + bool Execute(); +}; + +} // end of namespace + +#endif + diff --git a/common/JackSocketNotifyChannel.cpp b/common/JackSocketNotifyChannel.cpp new file mode 100644 index 00000000..c0c1a3fd --- /dev/null +++ b/common/JackSocketNotifyChannel.cpp @@ -0,0 +1,79 @@ +/* +Copyright (C) 2004-2006 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 "JackRequest.h" +#include "JackSocketNotifyChannel.h" +#include "JackError.h" +#include "JackConstants.h" + +namespace Jack +{ + +// Server to client +int JackSocketNotifyChannel::Open(const char* name) +{ + JackLog("JackSocketNotifyChannel::Open name = %s\n", name); + + // Connect to client listen socket + if (fNotifySocket.Connect(jack_client_dir, name, 0) < 0) { + jack_error("Cannot connect client socket"); + return -1; + } + // Use a time out for notifications + fNotifySocket.SetReadTimeOut(SOCKET_TIME_OUT); + return 0; +} + +void JackSocketNotifyChannel::Close() +{ + JackLog("JackSocketNotifyChannel::Close\n"); + fNotifySocket.Close(); +} + +void JackSocketNotifyChannel::ClientNotify(int refnum, const char* name, int notify, int sync, int value, int* result) +{ + JackClientNotification event(name, refnum, notify, sync, value); + JackResult res; + + // Send notification + if (event.Write(&fNotifySocket) < 0) { + jack_error("Could not write notification"); + fNotifySocket.Close(); + *result = -1; + return ; + } + + // Read the result in "synchronous" mode only + if (sync) { + // Get result : use a time out + if (res.Read(&fNotifySocket) < 0) { + jack_error("Could not read result"); + fNotifySocket.Close(); + *result = -1; + } else { + *result = res.fResult; + } + } else { + *result = 0; + } +} + +} // end of namespace + + diff --git a/common/JackSocketNotifyChannel.h b/common/JackSocketNotifyChannel.h new file mode 100644 index 00000000..a43d76b9 --- /dev/null +++ b/common/JackSocketNotifyChannel.h @@ -0,0 +1,56 @@ +/* +Copyright (C) 2004-2006 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. + +*/ + +#ifndef __JackSocketNotifyChannel__ +#define __JackSocketNotifyChannel__ + +#include "JackChannel.h" +#include "JackSocket.h" + +namespace Jack +{ + +/*! +\brief JackNotifyChannel using sockets. +*/ + +class JackSocketNotifyChannel : public JackNotifyChannelInterface +{ + + private: + + JackClientSocket fNotifySocket; // Socket to communicate with the server : from server to client + + public: + + JackSocketNotifyChannel() + {} + virtual ~JackSocketNotifyChannel() + {} + + int Open(const char* name); // Open the Server/Client connection + void Close(); // Close the Server/Client connection + + void ClientNotify(int refnum, const char* name, int notify, int sync, int value, int* result); +}; + +} // end of namespace + +#endif + diff --git a/common/JackSocketServerChannel.cpp b/common/JackSocketServerChannel.cpp new file mode 100644 index 00000000..5ec70edb --- /dev/null +++ b/common/JackSocketServerChannel.cpp @@ -0,0 +1,375 @@ +/* +Copyright (C) 2004-2006 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 "JackSocketServerChannel.h" +#include "JackRequest.h" +#include "JackServer.h" +#include "JackEngine.h" +#include "JackGlobals.h" +#include "JackClient.h" +#include <assert.h> + +using namespace std; + +namespace Jack +{ + +JackSocketServerChannel::JackSocketServerChannel() +{ + fThread = JackGlobals::MakeThread(this); + fPollTable = NULL; + fRebuild = true; +} + +JackSocketServerChannel::~JackSocketServerChannel() +{ + delete fThread; + delete[] fPollTable; +} + +int JackSocketServerChannel::Open(JackServer* server) +{ + JackLog("JackSocketServerChannel::Open \n"); + fServer = server; + + // Prepare request socket + if (fRequestListenSocket.Bind(jack_server_dir, 0) < 0) { + JackLog("JackSocketServerChannel::Open : cannot create result listen socket\n"); + return -1; + } + + // Prepare for poll + BuildPoolTable(); + + // Start listening + if (fThread->Start() != 0) { + jack_error("Cannot start Jack server listener"); + goto error; + } + + return 0; + +error: + fRequestListenSocket.Close(); + return -1; +} + +void JackSocketServerChannel::Close() +{ + fThread->Kill(); + fRequestListenSocket.Close(); +} + +void JackSocketServerChannel::CreateClient() +{ + JackLog("JackSocketServerChannel::CreateClient socket\n"); + JackClientSocket* socket = fRequestListenSocket.Accept(); + if (socket) { + fSocketTable[socket->GetFd()] = make_pair( -1, socket); + fRebuild = true; + } else { + jack_error("Client socket cannot be created"); + } +} + +void JackSocketServerChannel::AddClient(int fd, char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result) +{ + JackLog("JackSocketServerChannel::AddClient\n"); + int refnum = -1; + *result = fServer->GetEngine()->ClientNew(name, &refnum, shared_engine, shared_client, shared_ports); + if (*result == 0) { + fSocketTable[fd].first = refnum; + fRebuild = true; + } else { + jack_error("Cannot create new client"); + } +} + +void JackSocketServerChannel::RemoveClient(int fd, int refnum) +{ + pair<int, JackClientSocket*> elem = fSocketTable[fd]; + JackClientSocket* socket = elem.second; + assert(socket); + JackLog("JackSocketServerChannel::RemoveClient ref = %d\n", refnum); + fSocketTable.erase(fd); + socket->Close(); + delete socket; + fRebuild = true; +} + +void JackSocketServerChannel::KillClient(int fd) +{ + pair<int, JackClientSocket*> elem = fSocketTable[fd]; + JackClientSocket* socket = elem.second; + int refnum = elem.first; + + assert(socket); + JackLog("JackSocketServerChannel::KillClient ref = %d\n", refnum); + + if (refnum == -1) { // Should never happen... correspond to a client that started the socket but never opened... + jack_error("Client not opened"); + } else { + fServer->Notify(refnum, JackNotifyChannelInterface::kDeadClient, 0); + } + + fSocketTable.erase(fd); + socket->Close(); + delete socket; + fRebuild = true; +} + +int JackSocketServerChannel::HandleRequest(int fd) +{ + pair<int, JackClientSocket*> elem = fSocketTable[fd]; + JackClientSocket* socket = elem.second; + assert(socket); + + // Read header + JackRequest header; + if (header.Read(socket) < 0) { + jack_error("HandleRequest: cannot read header"); + return -1; + } + + // Read data + switch (header.fType) { + + case JackRequest::kClientNew: { + JackLog("JackRequest::ClientNew\n"); + JackClientNewRequest req; + JackClientNewResult res; + req.Read(socket); + AddClient(fd, req.fName, &res.fSharedEngine, &res.fSharedClient, &res.fSharedPorts, &res.fResult); + res.Write(socket); + break; + } + + case JackRequest::kClientClose: { + JackLog("JackRequest::ClientClose\n"); + JackClientCloseRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->GetEngine()->ClientClose(req.fRefNum); + res.Write(socket); + RemoveClient(fd, req.fRefNum); + break; + } + + case JackRequest::kActivateClient: { + JackActivateRequest req; + JackResult res; + JackLog("JackRequest::ActivateClient\n"); + req.Read(socket); + res.fResult = fServer->Activate(req.fRefNum); + res.Write(socket); + break; + } + + case JackRequest::kDeactivateClient: { + JackLog("JackRequest::DeactivateClient\n"); + JackDeactivateRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->Deactivate(req.fRefNum); + res.Write(socket); + break; + } + + case JackRequest::kRegisterPort: { + JackLog("JackRequest::RegisterPort\n"); + JackPortRegisterRequest req; + JackPortRegisterResult res; + req.Read(socket); + res.fResult = fServer->GetEngine()->PortRegister(req.fRefNum, req.fName, req.fFlags, req.fBufferSize, &res.fPortIndex); + res.Write(socket); + break; + } + + case JackRequest::kUnRegisterPort: { + JackLog("JackRequest::UnRegisterPort\n"); + JackPortUnRegisterRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->GetEngine()->PortUnRegister(req.fRefNum, req.fPortIndex); + res.Write(socket); + break; + } + + case JackRequest::kConnectNamePorts: { + JackLog("JackRequest::ConnectPorts\n"); + JackPortConnectNameRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->GetEngine()->PortConnect(req.fRefNum, req.fSrc, req.fDst); + res.Write(socket); + break; + } + + case JackRequest::kDisconnectNamePorts: { + JackLog("JackRequest::DisconnectPorts\n"); + JackPortDisconnectNameRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->GetEngine()->PortDisconnect(req.fRefNum, req.fSrc, req.fDst); + res.Write(socket); + break; + } + + case JackRequest::kConnectPorts: { + JackLog("JackRequest::ConnectPorts\n"); + JackPortConnectRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->GetEngine()->PortConnect(req.fRefNum, req.fSrc, req.fDst); + res.Write(socket); + break; + } + + case JackRequest::kDisconnectPorts: { + JackLog("JackRequest::DisconnectPorts\n"); + JackPortDisconnectRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->GetEngine()->PortDisconnect(req.fRefNum, req.fSrc, req.fDst); + res.Write(socket); + break; + } + + case JackRequest::kSetBufferSize: { + JackLog("JackRequest::SetBufferSize\n"); + JackSetBufferSizeRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->SetBufferSize(req.fBufferSize); + res.Write(socket); + break; + } + + case JackRequest::kSetFreeWheel: { + JackLog("JackRequest::SetFreeWheel\n"); + JackSetFreeWheelRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->SetFreewheel(req.fOnOff); + res.Write(socket); + break; + } + + case JackRequest::kReleaseTimebase: { + JackLog("JackRequest::kReleaseTimebase\n"); + JackReleaseTimebaseRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->GetEngine()->ReleaseTimebase(req.fRefNum); + res.Write(socket); + break; + } + + case JackRequest::kSetTimebaseCallback: { + JackLog("JackRequest::kSetTimebaseCallback\n"); + JackSetTimebaseCallbackRequest req; + JackResult res; + req.Read(socket); + res.fResult = fServer->GetEngine()->SetTimebaseCallback(req.fRefNum, req.fConditionnal); + res.Write(socket); + break; + } + + case JackRequest::kNotification: { + JackLog("JackRequest::Notification\n"); + JackClientNotificationRequest req; + req.Read(socket); + fServer->Notify(req.fRefNum, req.fNotify, req.fValue); + break; + } + + default: + JackLog("Unknown request %ld\n", header.fType); + break; + } + + return 0; +} + +void JackSocketServerChannel::BuildPoolTable() +{ + if (fRebuild) { + fRebuild = false; + delete[] fPollTable; + fPollTable = new pollfd[fSocketTable.size() + 1]; + + JackLog("JackSocketServerChannel::BuildPoolTable size = %d\n", fSocketTable.size() + 1); + + // First fd is the server request socket + fPollTable[0].fd = fRequestListenSocket.GetFd(); + fPollTable[0].events = POLLIN | POLLERR; + + // Next fd for clients + map<int, pair<int, JackClientSocket*> >::iterator it; + int i; + + for (i = 1, it = fSocketTable.begin(); it != fSocketTable.end(); it++, i++) { + JackLog("fSocketTable i = %ld fd = %ld\n", i, it->first); + fPollTable[i].fd = it->first; + fPollTable[i].events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; + } + } +} + +bool JackSocketServerChannel::Execute() +{ + // Global poll + if ((poll(fPollTable, fSocketTable.size() + 1, 10000) < 0) && (errno != EINTR)) { + jack_error("Engine poll failed err = %s request thread quits...", strerror(errno)); + return false; + } else { + + // Poll all clients + for (unsigned int i = 1; i < fSocketTable.size() + 1; i++) { + int fd = fPollTable[i].fd; + JackLog("fPollTable i = %ld fd = %ld\n", i, fd); + if (fPollTable[i].revents & ~POLLIN) { + jack_error("Poll client error err = %s", strerror(errno)); + KillClient(fd); + } else if (fPollTable[i].revents & POLLIN) { + if (HandleRequest(fd) < 0) { + jack_error("Could not handle external client request"); + //RemoveClient(fd); TO CHECK + } + } + } + + // Check the server request socket */ + if (fPollTable[0].revents & POLLERR) { + jack_error("Error on server request socket err = %s", strerror(errno)); + //return false; TO CHECK + } + + if (fPollTable[0].revents & POLLIN) { + CreateClient(); + } + } + + BuildPoolTable(); + return true; +} + +} // end of namespace + + diff --git a/common/JackSocketServerChannel.h b/common/JackSocketServerChannel.h new file mode 100644 index 00000000..0a00ae3c --- /dev/null +++ b/common/JackSocketServerChannel.h @@ -0,0 +1,70 @@ +/* +Copyright (C) 2004-2006 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. + +*/ + +#ifndef __JackSocketServerChannel__ +#define __JackSocketServerChannel__ + +#include "JackChannel.h" +#include "JackSocket.h" +#include "JackThread.h" +#include <poll.h> +#include <map> + +namespace Jack +{ + +/*! +\brief JackServerChannel using sockets. +*/ + +class JackSocketServerChannel : public JackServerChannelInterface, public JackRunnableInterface +{ + + private: + + JackServerSocket fRequestListenSocket; // Socket to create request socket for the client + JackThread* fThread; // Thread to execute the event loop + JackServer* fServer; + pollfd* fPollTable; + bool fRebuild; + std::map<int, std::pair<int, JackClientSocket*> > fSocketTable; + + int HandleRequest(int fd); + void CreateClient(); + void AddClient(int fd, char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result); + void RemoveClient(int fd, int refnum); + void KillClient(int fd); + void BuildPoolTable(); + + public: + + JackSocketServerChannel(); + virtual ~JackSocketServerChannel(); + + int Open(JackServer* server); // Open the Server/Client connection + void Close(); // Close the Server/Client connection + + // JackRunnableInterface interface + bool Execute(); +}; + +} // end of namespace + +#endif + diff --git a/common/JackSocketServerNotifyChannel.cpp b/common/JackSocketServerNotifyChannel.cpp new file mode 100644 index 00000000..3ca024a1 --- /dev/null +++ b/common/JackSocketServerNotifyChannel.cpp @@ -0,0 +1,59 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackSocketServerNotifyChannel.h" +#include "JackError.h" +#include "JackRequest.h" +#include "JackConstants.h" + +namespace Jack +{ + +int JackSocketServerNotifyChannel::Open() +{ + if (fRequestSocket.Connect(jack_server_dir, 0) < 0) { + jack_error("Cannot connect to server socket"); + return -1; + } else { + return 0; + } +} + +void JackSocketServerNotifyChannel::Close() +{ + fRequestSocket.Close(); +} + +/* +The requirement is that the Notification from RT thread can be delivered... not sure using a socket is adequate here... +Can the write operation block? +A non blocking write operation shoud be used : check if write can succeed, and ignore the notification otherwise +(since its mainly used for XRun, ignoring a notification is OK, successive XRun will come...) +*/ +void JackSocketServerNotifyChannel::ClientNotify(int refnum, int notify, int value) +{ + JackClientNotificationRequest req(refnum, notify, value); + if (req.Write(&fRequestSocket) < 0) { + jack_error("Could not write request type = %ld", req.fType); + } +} + +} // end of namespace + + diff --git a/common/JackSocketServerNotifyChannel.h b/common/JackSocketServerNotifyChannel.h new file mode 100644 index 00000000..f4ce0532 --- /dev/null +++ b/common/JackSocketServerNotifyChannel.h @@ -0,0 +1,55 @@ +/* +Copyright (C) 2004-2006 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. + +*/ + +#ifndef __JackSocketServerNotifyChannel__ +#define __JackSocketServerNotifyChannel__ + +#include "JackChannel.h" +#include "JackSocket.h" + +namespace Jack +{ + +/*! +\brief JackServerNotifyChannel using sockets. +*/ + +class JackSocketServerNotifyChannel : public JackServerNotifyChannelInterface +{ + private: + + JackClientSocket fRequestSocket; + + public: + + JackSocketServerNotifyChannel() + {} + virtual ~JackSocketServerNotifyChannel() + {} + + int Open(); + void Close(); + + void ClientNotify(int refnum, int notify, int value); +}; + +} // end of namespace + +#endif + diff --git a/common/JackSyncInterface.h b/common/JackSyncInterface.h new file mode 100644 index 00000000..a2f3cf9f --- /dev/null +++ b/common/JackSyncInterface.h @@ -0,0 +1,51 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackSyncInterface__ +#define __JackSyncInterface__ + +namespace Jack +{ + +/*! +\brief A synchronization primitive interface. +*/ + +class JackSyncInterface +{ + + public: + + JackSyncInterface() + {} + virtual ~JackSyncInterface() + {} + + virtual bool Allocate(const char* name) = 0; + virtual bool Connect(const char* name) = 0; + virtual bool TimedWait(long usec) = 0; + virtual void Wait() = 0; + virtual void SignalAll() = 0; + virtual void Destroy() = 0; +}; + +} // end of namespace + +#endif + diff --git a/common/JackSynchro.h b/common/JackSynchro.h new file mode 100644 index 00000000..d067ecad --- /dev/null +++ b/common/JackSynchro.h @@ -0,0 +1,102 @@ +/* +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackSynchro__ +#define __JackSynchro__ + +#include "JackError.h" + +#define SYNC_MAX_NAME_SIZE 256 + +namespace Jack +{ + +/*! +\brief An inter process synchronization primitive. +*/ + +class JackSynchro +{ + + protected: + + char fName[SYNC_MAX_NAME_SIZE]; + bool fFlush; // If true, signal are "flushed" : used for drivers that do no consume the signal + + virtual void BuildName(const char* name, char* res) + {} + + public: + + JackSynchro(): fFlush(false) + {} + virtual ~JackSynchro() + {} + + virtual bool Signal() + { + return true; + } + virtual bool SignalAll() + { + return true; + } + virtual bool Wait() + { + return true; + } + virtual bool TimedWait(long usec) + { + return true; + } + virtual bool Allocate(const char* name, int value) + { + return true; + } + virtual bool Connect(const char* name) + { + return true; + } + virtual bool ConnectInput(const char* name) + { + return true; + } + virtual bool ConnectOutput(const char* name) + { + return true; + } + virtual bool Disconnect() + { + return true; + } + virtual void Destroy() + {} + + void SetFlush(bool mode) + { + fFlush = mode; + } + +}; + + +} // end of namespace + +#endif + diff --git a/common/JackThread.h b/common/JackThread.h new file mode 100644 index 00000000..1a7aad00 --- /dev/null +++ b/common/JackThread.h @@ -0,0 +1,97 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackThread__ +#define __JackThread__ + +#ifdef WIN32 + #include <windows.h> +typedef HANDLE pthread_t; +typedef ULONGLONG UInt64; +#else + #include <pthread.h> +typedef unsigned long long UInt64; +#endif + +namespace Jack +{ + +/*! +\brief The base class for runnable objects, that have an <B> Init </B> and <B> Execute </B> method to be called in a thread. +*/ + +class JackRunnableInterface +{ + + public: + + JackRunnableInterface() + {} + virtual ~JackRunnableInterface() + {} + + virtual bool Init() /*! Called once when the thread is started */ + { + return true; + } + virtual bool Execute() = 0; /*! Must be implemented by subclasses */ +}; + +/*! +\brief The thread base class. +*/ + +class JackThread +{ + + protected: + + JackRunnableInterface* fRunnable; + int fPriority; + bool fRealTime; + volatile bool fRunning; + int fCancellation; + + public: + + JackThread(JackRunnableInterface* runnable, int priority, bool real_time, int cancellation): + fRunnable(runnable), fPriority(priority), fRealTime(real_time), fRunning(false), fCancellation(cancellation) + {} + virtual ~JackThread() + {} + + virtual int Start() = 0; + virtual int StartSync() = 0; + virtual int Kill() = 0; + virtual int Stop() = 0; + + virtual int AcquireRealTime() = 0; + virtual int AcquireRealTime(int priority) = 0; + virtual int DropRealTime() = 0; + + virtual void SetParams(UInt64 period, UInt64 computation, UInt64 constraint) // Empty implementation, will only make sense on OSX... + {} + + virtual pthread_t GetThreadID() = 0; +}; + +} // end of namespace + +#endif diff --git a/common/JackThreadedDriver.cpp b/common/JackThreadedDriver.cpp new file mode 100644 index 00000000..9a7e89fc --- /dev/null +++ b/common/JackThreadedDriver.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2001 Paul Davis + Copyright (C) 2004-2006 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackThreadedDriver.h" +#include "JackError.h" +#include "JackGlobals.h" +#include "JackClient.h" +#include "JackEngineControl.h" + +namespace Jack +{ + +JackThreadedDriver::JackThreadedDriver(JackDriverClient* driver) +{ + fThread = JackGlobals::MakeThread(this); + fDriver = driver; +} + +JackThreadedDriver::~JackThreadedDriver() +{ + delete fThread; + delete fDriver; +} + +int JackThreadedDriver::Start() +{ + JackLog("JackThreadedDriver::Start\n"); + int res; + + if ((res = fDriver->Start()) < 0) { + jack_error("Cannot start driver"); + return res; + } + if ((res = fThread->Start()) < 0) { + jack_error("Cannot start thread"); + return res; + } + + if (fDriver->IsRealTime()) { + JackLog("JackThreadedDriver::Start IsRealTime\n"); + // Will do "something" on OSX only... + // fThread->SetParams(fDriver->fEngineControl->fPeriod, fDriver->fEngineControl->fComputation, fDriver->fEngineControl->fConstraint); + if (fThread->AcquireRealTime(GetEngineControl()->fPriority) < 0) { + jack_error("AcquireRealTime error"); + } + } + + return 0; +} + +int JackThreadedDriver::Stop() +{ + JackLog("JackThreadedDriver::Stop\n"); + int res; + + if ((res = fThread->Stop()) < 0) { // Stop when the thread cycle is finished + jack_error("Cannot stop thread"); + return res; + } + if ((res = fDriver->Stop()) < 0) { + jack_error("Cannot stop driver"); + return res; + } + return 0; +} + +bool JackThreadedDriver::Execute() +{ + return (Process() == 0); +} + +} // end of namespace diff --git a/common/JackThreadedDriver.h b/common/JackThreadedDriver.h new file mode 100644 index 00000000..66546c83 --- /dev/null +++ b/common/JackThreadedDriver.h @@ -0,0 +1,154 @@ +/* + Copyright (C) 2001 Paul Davis + Copyright (C) 2004-2006 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackThreadedDriver__ +#define __JackThreadedDriver__ + +#include "JackDriver.h" +#include "JackThread.h" + +namespace Jack +{ + +/*! +\brief The base class for threaded drivers. Threaded drivers are used with blocking devices. +*/ + +class JackThreadedDriver : public JackDriverClientInterface, public JackRunnableInterface +{ + + private: + + JackThread* fThread; + JackDriverClient* fDriver; + + public: + + JackThreadedDriver(JackDriverClient* driver); + virtual ~JackThreadedDriver(); + + virtual int Open() + { + return fDriver->Open(); + } + + virtual int Open(jack_nframes_t nframes, + jack_nframes_t samplerate, + int capturing, + int playing, + int inchannels, + int outchannels, + bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) + { + return fDriver->Open(nframes, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); + } + + virtual int Close() + { + return fDriver->Close(); + } + + virtual int Process() + { + return fDriver->Process(); + } + + virtual int Attach() + { + return fDriver->Attach(); + } + virtual int Detach() + { + return fDriver->Detach(); + } + + virtual int Read() + { + return fDriver->Read(); + } + virtual int Write() + { + return fDriver->Write(); + } + + virtual int Start(); + virtual int Stop(); + + virtual int SetBufferSize(jack_nframes_t nframes) + { + return fDriver->SetBufferSize(nframes); + } + + virtual void SetMaster(bool onoff) + { + fDriver->SetMaster(onoff); + } + virtual bool GetMaster() + { + return fDriver->GetMaster(); + } + + virtual void AddSlave(JackDriverInterface* slave) + { + fDriver->AddSlave(slave); + } + virtual void RemoveSlave(JackDriverInterface* slave) + { + fDriver->RemoveSlave(slave); + } + virtual void ProcessSlaves() + { + fDriver->ProcessSlaves(); + } + + virtual void PrintState() + { + fDriver->PrintState(); + } + + virtual int ClientNotify(int refnum, const char* name, int notify, int sync, int value) + { + return fDriver->ClientNotify(refnum, name, notify, sync, value); + } + + virtual JackClientControl* GetClientControl() const + { + return fDriver->GetClientControl(); + } + + virtual bool IsRealTime() + { + return fDriver->IsRealTime(); + } + + // JackRunnableInterface interface + + virtual bool Execute(); + +}; + +} // end of namespace + + +#endif diff --git a/common/JackTime.c b/common/JackTime.c new file mode 100644 index 00000000..dbbf6063 --- /dev/null +++ b/common/JackTime.c @@ -0,0 +1,121 @@ +/* + Copyright (C) 2001-2003 Paul Davis + Copyright (C) 2004-2006 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackTime.h" +#include "JackError.h" + +#ifdef __APPLE__ + +double __jack_time_ratio; + +/* This should only be called ONCE per process. */ +void InitTime() +{ + JackLog("InitTime\n"); + mach_timebase_info_data_t info; + mach_timebase_info(&info); + __jack_time_ratio = ((float)info.numer / info.denom) / 1000; +} + +#endif + +#ifdef WIN32 + +EXPORT LARGE_INTEGER _jack_freq; + +void InitTime() +{ + QueryPerformanceFrequency(&_jack_freq); + JackLog("InitTime freq = %ld %ld\n", _jack_freq.HighPart, _jack_freq.LowPart); + _jack_freq.QuadPart = _jack_freq.QuadPart / 1000000; // by usec +} + +jack_time_t GetMicroSeconds(void) +{ + LARGE_INTEGER t1; + QueryPerformanceCounter (&t1); + return (jack_time_t)(((double)t1.QuadPart)/((double)_jack_freq.QuadPart)); +} + +// TODO +#endif + +#ifdef linux + +#ifdef GETCYCLE_TIME + +#include <stdio.h> +jack_time_t GetMhz(void) +{ + FILE *f = fopen("/proc/cpuinfo", "r"); + if (f == 0) + { + perror("can't open /proc/cpuinfo\n"); + exit(1); + } + + for ( ; ; ) + { + jack_time_t mhz; + int ret; + char buf[1000]; + + if (fgets(buf, sizeof(buf), f) == NULL) { + jack_error ("FATAL: cannot locate cpu MHz in " + "/proc/cpuinfo\n"); + exit(1); + } + +#if defined(__powerpc__) + ret = sscanf(buf, "clock\t: %" SCNu64 "MHz", &mhz); +#elif defined( __i386__ ) || defined (__hppa__) || defined (__ia64__) || \ + defined(__x86_64__) + ret = sscanf(buf, "cpu MHz : %" SCNu64, &mhz); +#elif defined( __sparc__ ) + ret = sscanf(buf, "Cpu0Bogo : %" SCNu64, &mhz); +#elif defined( __mc68000__ ) + ret = sscanf(buf, "Clocking: %" SCNu64, &mhz); +#elif defined( __s390__ ) + ret = sscanf(buf, "bogomips per cpu: %" SCNu64, &mhz); +#else /* MIPS, ARM, alpha */ + ret = sscanf(buf, "BogoMIPS : %" SCNu64, &mhz); +#endif + if (ret == 1) + { + fclose(f); + return (jack_time_t)mhz; + } + } +} + +jack_time_t __jack_cpu_mhz; + +void InitTime() +{ + __jack_cpu_mhz = GetMhz(); +} + +#else +void InitTime() +{} + +#endif + +#endif diff --git a/common/JackTime.h b/common/JackTime.h new file mode 100644 index 00000000..22c1379a --- /dev/null +++ b/common/JackTime.h @@ -0,0 +1,111 @@ +/* +Copyright (C) 2001-2003 Paul Davis +Copyright (C) 2004-2006 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. + +*/ + +#ifndef __JackTime__ +#define __JackTime__ + +#include "types.h" +#include "JackExports.h" +#include <stdio.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if defined(__APPLE__) + +#include <mach/mach_time.h> + #include <unistd.h> + + extern double __jack_time_ratio; + + static inline jack_time_t GetMicroSeconds(void) { + return (jack_time_t) (mach_absolute_time () * __jack_time_ratio); + } + + /* This should only be called ONCE per process. */ + extern void InitTime(); + + static inline void JackSleep(long usec) { + usleep(usec); + } + +#endif + +#ifdef WIN32 + + extern EXPORT LARGE_INTEGER _jack_freq; + + /* + static jack_time_t GetMicroSeconds(void) { + LARGE_INTEGER t1; + QueryPerformanceCounter (&t1); + return (jack_time_t)(((double)t1.QuadPart)/((double)_jack_freq.QuadPart)); + } + */ + + extern EXPORT jack_time_t GetMicroSeconds(void) ; + + extern void InitTime(); + + static void JackSleep(long usec) { + Sleep(usec / 1000); + } + +#endif + +#ifdef linux + +#include <unistd.h> + + static inline void JackSleep(long usec) { + usleep(usec); + } + +#ifdef GETCYCLE_TIME + #include "cycles.h" + extern jack_time_t __jack_cpu_mhz; + extern jack_time_t GetMhz(); + extern void InitTime(); + static inline jack_time_t GetMicroSeconds (void) { + return get_cycles() / __jack_cpu_mhz; + } +#else + #include <time.h> + extern void InitTime(); + static inline jack_time_t GetMicroSeconds (void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (jack_time_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + } +#endif + +#endif + +#ifdef __cplusplus +} +#endif + + +#endif + + + diff --git a/common/JackTransportEngine.cpp b/common/JackTransportEngine.cpp new file mode 100644 index 00000000..af484846 --- /dev/null +++ b/common/JackTransportEngine.cpp @@ -0,0 +1,259 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackTransportEngine.h" +#include "JackClientControl.h" +#include "JackError.h" +#include "JackTime.h" +#include <assert.h> +#include <stdlib.h> + +using namespace std; + +namespace Jack +{ + +JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t>() +{ + fTransportState = JackTransportStopped; + fTransportCmd = fPreviousCmd = TransportCommandStop; + fSyncTimeout = 2000000; /* 2 second default */ + fSyncTimeLeft = 0; + fTimeBaseMaster = -1; + fWriteCounter = 0; + fPendingPos = 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; + JackLog("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld\n", (long)fSyncTimeout, (long)fSyncTimeLeft); +} + +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; + } +} + +int JackTransportEngine::SetTimebase(int refnum, bool conditionnal) +{ + if (conditionnal && fTimeBaseMaster > 0) { + if (refnum != fTimeBaseMaster) { + JackLog("conditional timebase for ref = %ld failed: %ld is already the master\n", refnum, fTimeBaseMaster); + return EBUSY; + } else { + JackLog("ref = %ld was already timebase master\n", refnum); + return 0; + } + } else { + fTimeBaseMaster = refnum; + JackLog("new timebase master: ref = %ld\n", refnum); + return 0; + } +} + +bool JackTransportEngine::CheckOneSynching(JackClientInterface** table) +{ + for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { + JackClientInterface* client = table[i]; + if (client && client->GetClientControl()->fTransportState == JackTransportSynching) { + JackLog("CheckOneSynching\n"); + return true; + } + } + return false; +} + +bool JackTransportEngine::CheckAllRolling(JackClientInterface** table) +{ + for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { + JackClientInterface* client = table[i]; + if (client && client->GetClientControl()->fTransportState != JackTransportRolling) { + JackLog("CheckAllRolling refnum = %ld is not rolling\n", i); + return false; + } + } + JackLog("CheckAllRolling\n"); + return true; +} + +void JackTransportEngine::MakeAllStarting(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; + JackLog("MakeAllStarting refnum = %ld \n", i); + } + } + JackLog("MakeAllStarting\n"); +} + +void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time) // really needed?? (woule be done in CycleEnd...) +{ + jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state + pending->usecs = time; + pending->frame_rate = frame_rate; + WriteNextStateStop(1); +} + +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; + JackLog("transport command: %s\n", (cmd == TransportCommandStart ? "START" : "STOP")); + } else { + cmd = TransportCommandNone; + } + + /* state transition switch */ + switch (fTransportState) { + + case JackTransportSynching: + if (cmd == TransportCommandStart) { + fTransportState = JackTransportStarting; + MakeAllStarting(table); + SyncTimeout(frame_rate, buffer_size); + JackLog("transport locate ==> starting....\n"); + } else if (fPendingPos) { + fTransportState = JackTransportSynching; + JackLog("transport locate ==> locate....\n"); + } else { + fTransportState = JackTransportStopped; + JackLog("transport locate ==> stopped....\n"); + } + break; + + case JackTransportStopped: + // Set a JackTransportStarting for the current cycle, if all clients are ready (now slow_sync) ==> JackTransportRolling next state + if (cmd == TransportCommandStart) { + fTransportState = JackTransportStarting; + MakeAllStarting(table); + SyncTimeout(frame_rate, buffer_size); + JackLog("transport stopped ==> starting....\n"); + } else if (fPendingPos || CheckOneSynching(table)) { + fTransportState = JackTransportSynching; + JackLog("transport stopped ==> locate....\n"); + } + break; + + case JackTransportStarting: + JackLog("transport starting fSyncTimeLeft %ld\n", fSyncTimeLeft); + + if (cmd == TransportCommandStop) { + fTransportState = JackTransportStopped; + JackLog("transport starting ==> stopped\n"); + } else if (fPendingPos) { + fTransportState = JackTransportStarting; + MakeAllStarting(table); + SyncTimeout(frame_rate, buffer_size); + } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { + fTransportState = JackTransportRolling; + JackLog("transport starting ==> rolling.... fSyncTimeLeft %ld\n", fSyncTimeLeft); + } + break; + + case JackTransportRolling: + if (cmd == TransportCommandStop) { + fTransportState = JackTransportStopped; + JackLog("transport rolling ==> stopped\n"); + } else if (fPendingPos || CheckOneSynching(table)) { + fTransportState = JackTransportStarting; + MakeAllStarting(table); + SyncTimeout(frame_rate, buffer_size); + JackLog("transport rolling ==> starting....\n"); + } + 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) { + JackLog("New pos = %ld\n", request->frame); + jack_position_t* pending = WriteNextStateStart(1); + TransportCopyPosition(request, pending); + WriteNextStateStop(1); + } +} + +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::TransportCopyPosition(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 diff --git a/common/JackTransportEngine.h b/common/JackTransportEngine.h new file mode 100644 index 00000000..0936b94f --- /dev/null +++ b/common/JackTransportEngine.h @@ -0,0 +1,137 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackTransportEngine__ +#define __JackTransportEngine__ + +#include "transport_types.h" +#include "JackClientInterface.h" +#include "JackConstants.h" +#include "JackAtomicArrayState.h" + +namespace Jack +{ + +typedef enum { + TransportCommandNone = 0, + TransportCommandStart = 1, + TransportCommandStop = 2, +} transport_command_t; + +/*! +\brief The client transport structure. + +We have: + + - a "current" position + - a "pending" position prepared by the server at each cycle + - a "request" position wanted by a client + + At the beginning of a cycle the server needs to select a new current position. When a request and a pending position are available, + the resquest takes precedence on the pending one. The server atomically switches to the new position. + The current position can be read by clients. + + We use a JackAtomicArrayState pattern that allows to manage several "next" states independantly. +*/ + +class JackTransportEngine : public JackAtomicArrayState<jack_position_t> +{ + + private: + + jack_transport_state_t fTransportState; + volatile transport_command_t fTransportCmd; + transport_command_t fPreviousCmd; /* previous transport_cmd */ + jack_time_t fSyncTimeout; + int fSyncTimeLeft; + int fTimeBaseMaster; + bool fPendingPos; + volatile SInt32 fWriteCounter; + + bool CheckOneSynching(JackClientInterface** table); + bool CheckAllRolling(JackClientInterface** table); + void MakeAllStarting(JackClientInterface** table); + void SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size); + + public: + + JackTransportEngine(); + + ~JackTransportEngine() + {} + + void SetCommand(transport_command_t state) + { + fTransportCmd = state; + } + + jack_transport_state_t GetState() const + { + return fTransportState; + } + + int GetTimebaseMaster() const + { + return fTimeBaseMaster; + } + + /* + \brief + */ + int ResetTimebase(int refnum); + + /* + \brief + */ + int SetTimebase(int refnum, bool conditionnal); + + /* + \brief + */ + void CycleBegin(jack_nframes_t frame_rate, jack_time_t time); + + /* + \brief + */ + void CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size); + + /* + \brief + */ + void SetSyncTimeout(jack_time_t timeout) + { + fSyncTimeout = timeout; + } + + void ReadCurrentPos(jack_position_t* pos); + + jack_unique_t GenerateUniqueID() + { + return (jack_unique_t)INC_ATOMIC(&fWriteCounter); + } + + static void TransportCopyPosition(jack_position_t* from, jack_position_t* to); + +}; + + +} // end of namespace + +#endif diff --git a/common/JackTypes.h b/common/JackTypes.h new file mode 100644 index 00000000..96c4c41f --- /dev/null +++ b/common/JackTypes.h @@ -0,0 +1,36 @@ +/* + Copyright (C) 2001 Paul Davis + + 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. + + $Id: JackTypes.h,v 1.2.2.1 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __JackTypes__ +#define __JackTypes__ + +namespace Jack +{ + +typedef enum { + NotTriggered, + Triggered, + Running, + Finished, +} jack_client_state_t; + +} + +#endif diff --git a/common/Jackdmp.cpp b/common/Jackdmp.cpp new file mode 100644 index 00000000..747e880a --- /dev/null +++ b/common/Jackdmp.cpp @@ -0,0 +1,569 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2006 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <iostream> +#include <assert.h> +#include <signal.h> +#include <pwd.h> +#include <sys/types.h> +#include <dirent.h> +#include <getopt.h> + +#include "JackServer.h" +#include "JackConstants.h" +#include "driver_interface.h" +#include "driver_parse.h" +#include "JackDriverLoader.h" +#include "jslist.h" +#include "JackError.h" +#include "shm.h" +#include "jack.h" + +using namespace Jack; + +static JackServer* fServer; +static char* server_name = NULL; +static int realtime_priority = 10; +static int do_mlock = 1; +static unsigned int port_max = 128; +static int realtime = 0; +static int loopback = 0; +static int temporary = 0; +static int client_timeout = 0; /* msecs; if zero, use period size. */ +static int do_unlock = 0; +static JSList* drivers = NULL; + +static sigset_t signals; + +#define DEFAULT_TMP_DIR "/tmp" +char* jack_tmpdir = DEFAULT_TMP_DIR; + +static void silent_jack_error_callback (const char *desc) +{} + +static void copyright(FILE* file) +{ + fprintf (file, "jackdmp " VERSION "\n" + "Copyright 2001-2005 Paul Davis and others.\n" + "Copyright 2004-2006 Grame.\n" + "jackdmp comes with ABSOLUTELY NO WARRANTY\n" + "This is free software, and you are welcome to redistribute it\n" + "under certain conditions; see the file COPYING for details\n"); +} + +static void usage (FILE* file) +{ + copyright (file); + fprintf (file, "\n" + "usage: jackdmp [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n" + " [ --name OR -n server-name ]\n" + // " [ --no-mlock OR -m ]\n" + // " [ --unlock OR -u ]\n" + " [ --timeout OR -t client-timeout-in-msecs ]\n" + " [ --loopback OR -L loopback-port-number ]\n" + // " [ --port-max OR -p maximum-number-of-ports]\n" + " [ --verbose OR -v ]\n" + " [ --silent OR -s ]\n" + " [ --sync OR -S ]\n" + " [ --version OR -V ]\n" + " -d driver [ ... driver args ... ]\n" + " where driver can be `alsa', `coreaudio' or `dummy'\n" + " jackdmp -d driver --help\n" + " to display options for each driver\n\n"); +} + + +static void DoNothingHandler(int sig) +{ + /* this is used by the child (active) process, but it never + gets called unless we are already shutting down after + another signal. + */ + char buf[64]; + snprintf(buf, sizeof(buf), "received signal %d during shutdown (ignored)\n", sig); + write(1, buf, strlen(buf)); +} + +static int JackStart(jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int time_out_ms, int rt, int priority, int loopback, int verbose) +{ + JackLog("Jackdmp: sync = %ld timeout = %ld rt = %ld priority = %ld verbose = %ld \n", sync, time_out_ms, rt, priority, verbose); + fServer = new JackServer(sync, time_out_ms, rt, priority, loopback, verbose); + int res = fServer->Open(driver_desc, driver_params); + return (res < 0) ? res : fServer->Start(); +} + +static int JackStop() +{ + fServer->Stop(); + fServer->Close(); + JackLog("Jackdmp: server close\n"); + delete fServer; + JackLog("Jackdmp: delete server\n"); + return 0; +} + +static int JackDelete() +{ + delete fServer; + JackLog("Jackdmp: delete server\n"); + return 0; +} + +static void FilterSIGPIPE() +{ + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGPIPE); + //sigprocmask(SIG_BLOCK, &set, 0); + pthread_sigmask(SIG_BLOCK, &set, 0); +} + +static char* jack_default_server_name(void) +{ + char *server_name; + if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL) + server_name = "default"; + return server_name; +} + +/* returns the name of the per-user subdirectory of jack_tmpdir */ +static char* jack_user_dir(void) +{ + static char user_dir[PATH_MAX] = ""; + + /* format the path name on the first call */ + if (user_dir[0] == '\0') { + snprintf (user_dir, sizeof (user_dir), "%s/jack-%d", + jack_tmpdir, getuid ()); + } + + return user_dir; +} + +/* returns the name of the per-server subdirectory of jack_user_dir() */ + +static char* get_jack_server_dir(const char* toto) +{ + static char server_dir[PATH_MAX] = ""; + + // format the path name on the first call + if (server_dir[0] == '\0') { + snprintf (server_dir, sizeof (server_dir), "%s/%s", + jack_user_dir (), server_name); + } + + return server_dir; +} + +static void +jack_cleanup_files (const char *server_name) +{ + DIR *dir; + struct dirent *dirent; + char *dir_name = get_jack_server_dir (server_name); + + /* On termination, we remove all files that jackd creates so + * subsequent attempts to start jackd will not believe that an + * instance is already running. If the server crashes or is + * terminated with SIGKILL, this is not possible. So, cleanup + * is also attempted when jackd starts. + * + * There are several tricky issues. First, the previous JACK + * server may have run for a different user ID, so its files + * may be inaccessible. This is handled by using a separate + * JACK_TMP_DIR subdirectory for each user. Second, there may + * be other servers running with different names. Each gets + * its own subdirectory within the per-user directory. The + * current process has already registered as `server_name', so + * we know there is no other server actively using that name. + */ + + /* nothing to do if the server directory does not exist */ + if ((dir = opendir (dir_name)) == NULL) { + return ; + } + + /* unlink all the files in this directory, they are mine */ + while ((dirent = readdir (dir)) != NULL) { + + char fullpath[PATH_MAX]; + + if ((strcmp (dirent->d_name, ".") == 0) + || (strcmp (dirent->d_name, "..") == 0)) { + continue; + } + + snprintf (fullpath, sizeof (fullpath), "%s/%s", + dir_name, dirent->d_name); + + if (unlink (fullpath)) { + jack_error ("cannot unlink `%s' (%s)", fullpath, + strerror (errno)); + } + } + + closedir (dir); + + /* now, delete the per-server subdirectory, itself */ + if (rmdir (dir_name)) { + jack_error ("cannot remove `%s' (%s)", dir_name, + strerror (errno)); + } + + /* finally, delete the per-user subdirectory, if empty */ + if (rmdir (jack_user_dir ())) { + if (errno != ENOTEMPTY) { + jack_error ("cannot remove `%s' (%s)", + jack_user_dir (), strerror (errno)); + } + } +} + +#ifdef FORK_SERVER + +int main(int argc, char* argv[]) +{ + int sig; + sigset_t allsignals; + struct sigaction action; + int waiting; + + jack_driver_desc_t* driver_desc; + const char *options = "-ad:P:uvshVRL:STFl:t:mn:p:"; + struct option long_options[] = { + { "driver", 1, 0, 'd' + }, + { "verbose", 0, 0, 'v' }, + { "help", 0, 0, 'h' }, + { "port-max", 1, 0, 'p' }, + { "no-mlock", 0, 0, 'm' }, + { "name", 0, 0, 'n' }, + { "unlock", 0, 0, 'u' }, + { "realtime", 0, 0, 'R' }, + { "loopback", 0, 0, 'L' }, + { "realtime-priority", 1, 0, 'P' }, + { "timeout", 1, 0, 't' }, + { "temporary", 0, 0, 'T' }, + { "version", 0, 0, 'V' }, + { "silent", 0, 0, 's' }, + { "sync", 0, 0, 'S' }, + { 0, 0, 0, 0 } + }; + int opt = 0; + int option_index = 0; + int seen_driver = 0; + char *driver_name = NULL; + char **driver_args = NULL; + JSList* driver_params; + int driver_nargs = 1; + int show_version = 0; + int sync = 0; + int rc, i; + + opterr = 0; + while (!seen_driver && + (opt = getopt_long(argc, argv, options, + long_options, &option_index)) != EOF) { + switch (opt) { + + case 'd': + seen_driver = 1; + driver_name = optarg; + break; + + case 'v': + verbose = 1; + break; + + case 's': + jack_set_error_function(silent_jack_error_callback); + break; + + case 'S': + sync = 1; + break; + + case 'n': + server_name = optarg; + break; + + case 'm': + do_mlock = 0; + break; + + case 'p': + port_max = (unsigned int)atol(optarg); + break; + + case 'P': + realtime_priority = atoi(optarg); + break; + + case 'R': + realtime = 1; + break; + + case 'L': + loopback = atoi(optarg); + break; + + case 'T': + temporary = 1; + break; + + case 't': + client_timeout = atoi(optarg); + break; + + case 'u': + do_unlock = 1; + break; + + case 'V': + show_version = 1; + break; + + default: + fprintf(stderr, "unknown option character %c\n", + optopt); + /*fallthru*/ + case 'h': + usage(stdout); + return -1; + } + } + + /* + if (show_version) { + printf ( "jackd version " VERSION + " tmpdir " DEFAULT_TMP_DIR + " protocol " PROTOCOL_VERSION + "\n"); + return -1; + } + */ + + if (!seen_driver) { + usage (stderr); + exit (1); + } + + drivers = jack_drivers_load (drivers); + if (!drivers) { + fprintf (stderr, "jackdmp: no drivers found; exiting\n"); + exit (1); + } + + driver_desc = jack_find_driver_descriptor (drivers, driver_name); + if (!driver_desc) { + fprintf (stderr, "jackdmp: unknown driver '%s'\n", driver_name); + exit (1); + } + + if (optind < argc) { + driver_nargs = 1 + argc - optind; + } else { + driver_nargs = 1; + } + + if (driver_nargs == 0) { + fprintf (stderr, "No driver specified ... hmm. JACK won't do" + " anything when run like this.\n"); + return -1; + } + + driver_args = (char **) malloc (sizeof (char *) * driver_nargs); + driver_args[0] = driver_name; + + for (i = 1; i < driver_nargs; i++) { + driver_args[i] = argv[optind++]; + } + + if (jack_parse_driver_params (driver_desc, driver_nargs, + driver_args, &driver_params)) { + exit (0); + } + + if (server_name == NULL) + server_name = jack_default_server_name (); + + copyright (stdout); + + rc = jack_register_server (server_name); + switch (rc) { + case EEXIST: + fprintf (stderr, "`%s' server already active\n", server_name); + exit (1); + case ENOSPC: + fprintf (stderr, "too many servers already active\n"); + exit (2); + case ENOMEM: + fprintf (stderr, "no access to shm registry\n"); + exit (3); + default: + if (verbose) + fprintf (stderr, "server `%s' registered\n", + server_name); + } + + /* clean up shared memory and files from any previous + * instance of this server name */ + jack_cleanup_shm(); + jack_cleanup_files(server_name); + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + sigemptyset(&signals); + sigaddset(&signals, SIGHUP); + sigaddset(&signals, SIGINT); + sigaddset(&signals, SIGQUIT); + sigaddset(&signals, SIGPIPE); + sigaddset(&signals, SIGTERM); + sigaddset(&signals, SIGUSR1); + sigaddset(&signals, SIGUSR2); + + // all child threads will inherit this mask unless they + // explicitly reset it + + FilterSIGPIPE(); + pthread_sigmask(SIG_BLOCK, &signals, 0); + + if (!realtime && client_timeout == 0) + client_timeout = 500; /* 0.5 sec; usable when non realtime. */ + + int res = JackStart(driver_desc, driver_params, sync, client_timeout, realtime, realtime_priority, loopback, verbose); + if (res < 0) { + jack_error("Cannot start server... exit"); + JackDelete(); + return 0; + } + + /* + For testing purpose... + InternalMetro* client1 = new InternalMetro(1200, 0.4, 20, 80, "metro1"); + InternalMetro* client2 = new InternalMetro(600, 0.4, 20, 150, "metro2"); + InternalMetro* client3 = new InternalMetro(1000, 0.4, 20, 110, "metro3"); + InternalMetro* client4 = new InternalMetro(1200, 0.4, 20, 80, "metro4"); + InternalMetro* client5 = new InternalMetro(1500, 0.4, 20, 60, "metro5"); + InternalMetro* client6 = new InternalMetro(1200, 0.4, 20, 84, "metro6"); + InternalMetro* client7 = new InternalMetro(600, 0.4, 20, 160, "metro7"); + InternalMetro* client8 = new InternalMetro(1000, 0.4, 20, 113, "metro8"); + InternalMetro* client9 = new InternalMetro(1200, 0.4, 20, 84, "metro9"); + InternalMetro* client10 = new InternalMetro(1500, 0.4, 20, 70, "metro10"); + */ + + // install a do-nothing handler because otherwise pthreads + // behaviour is undefined when we enter sigwait. + + sigfillset(&allsignals); + action.sa_handler = DoNothingHandler; + action.sa_mask = allsignals; + action.sa_flags = SA_RESTART | SA_RESETHAND; + + for (i = 1; i < NSIG; i++) { + if (sigismember(&signals, i)) { + sigaction(i, &action, 0); + } + } + + waiting = TRUE; + + while (waiting) { + sigwait(&signals, &sig); + + fprintf(stderr, "jack main caught signal %d\n", sig); + + switch (sig) { + case SIGUSR1: + //jack_dump_configuration(engine, 1); + break; + case SIGUSR2: + // driver exit + waiting = FALSE; + break; + default: + waiting = FALSE; + break; + } + } + + if (sig != SIGSEGV) { + // unblock signals so we can see them during shutdown. + // this will help prod developers not to lose sight of + // bugs that cause segfaults etc. during shutdown. + sigprocmask(SIG_UNBLOCK, &signals, 0); + } + + JackStop(); + + jack_cleanup_shm(); + jack_cleanup_files(server_name); + jack_unregister_server(server_name); + + return 1; +} + +#else + +int main(int argc, char* argv[]) +{ + char c; + long sample_sate = lopt(argv, "-r", 44100); + long buffer_size = lopt(argv, "-p", 512); + long chan_in = lopt(argv, "-i", 2); + long chan_out = lopt(argv, "-o", 2); + long audiodevice = lopt(argv, "-I", -1); + long sync = lopt(argv, "-s", 0); + long timeout = lopt(argv, "-t", 100 * 1000); + const char* name = flag(argv, "-n", "Built-in Audio"); + long rt = lopt(argv, "-R", 0); + verbose = lopt(argv, "-v", 0); + + copyright(stdout); + usage(stdout); + + FilterSIGPIPE(); + + printf("jackdmp: sample_sate = %ld buffer_size = %ld chan_in = %ld chan_out = %ld name = %s sync-mode = %ld\n", + sample_sate, buffer_size, chan_in, chan_out, name, sync); + assert(buffer_size <= BUFFER_SIZE_MAX); + + int res = JackStart(sample_sate, buffer_size, chan_in, chan_out, name, audiodevice, sync, timeout, rt); + if (res < 0) { + jack_error("Cannot start server... exit\n"); + JackDelete(); + return 0; + } + + while (((c = getchar()) != 'q')) { + + switch (c) { + + case 's': + fServer->PrintState(); + break; + } + } + + JackStop(); + return 0; +} + +#endif diff --git a/common/cycles.h b/common/cycles.h new file mode 100644 index 00000000..96dcbb4d --- /dev/null +++ b/common/cycles.h @@ -0,0 +1,94 @@ +/* + Copyright (C) 2001 Paul Davis + Code derived from various headers from the Linux kernel + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: cycles.h,v 1.4.2.1 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __jack_cycles_h__ +#define __jack_cycles_h__ + +/* + * Standard way to access the cycle counter on i586+ CPUs. + * Currently only used on SMP. + * + * If you really have a SMP machine with i486 chips or older, + * compile for that, and this will just always return zero. + * That's ok, it just means that the nicer scheduling heuristics + * won't work for you. + * + * We only use the low 32 bits, and we'd simply better make sure + * that we reschedule before that wraps. Scheduling at least every + * four billion cycles just basically sounds like a good idea, + * regardless of how fast the machine is. + */ + +#ifdef __linux__ + +#ifdef __PPC__ + +/* PowerPC */ + +#define CPU_FTR_601 0x00000100 + +typedef unsigned long cycles_t; + +/* For the "cycle" counter we use the timebase lower half. */ + +extern cycles_t cacheflush_time; + +static inline cycles_t get_cycles(void) +{ + cycles_t ret = 0; + + __asm__ __volatile__( + "98: mftb %0\n" + "99:\n" + ".section __ftr_fixup,\"a\"\n" + " .long %1\n" + " .long 0\n" + " .long 98b\n" + " .long 99b\n" + ".previous" + : "=r" (ret) : "i" (CPU_FTR_601)); + return ret; +} + +#endif + +#ifdef __i386__ + +typedef unsigned long long cycles_t; + +extern cycles_t cacheflush_time; + +#define rdtscll(val) \ + __asm__ __volatile__("rdtsc" : "=A" (val)) + +static inline cycles_t get_cycles (void) +{ + unsigned long long ret; + + rdtscll(ret); + return ret; +} + +#endif + +#endif + +#endif /* __jack_cycles_h__ */ diff --git a/common/driver_interface.h b/common/driver_interface.h new file mode 100644 index 00000000..13a642d6 --- /dev/null +++ b/common/driver_interface.h @@ -0,0 +1,97 @@ +/* + Copyright (C) 2003 Bob Ham <rah@bash.sh> + + 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. + +*/ + +#ifndef __jack_driver_interface_h__ +#define __jack_driver_interface_h__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <limits.h> + +#ifdef WIN32 +#include "types.h" +#define PATH_MAX 1024 +#else +#include <inttypes.h> +#endif + + +#define JACK_DRIVER_NAME_MAX 15 +#define JACK_DRIVER_PARAM_NAME_MAX 15 +#define JACK_DRIVER_PARAM_STRING_MAX 63 + + /** Driver parameter types */ + typedef enum + { + JackDriverParamInt = 1, + JackDriverParamUInt, + JackDriverParamChar, + JackDriverParamString, + JackDriverParamBool + } jack_driver_param_type_t; + + /** Driver parameter value */ + typedef union + { + uint32_t ui; + int32_t i; + char c; + char str[JACK_DRIVER_PARAM_STRING_MAX + 1]; + } jack_driver_param_value_t; + + + /** A driver parameter descriptor */ + typedef struct { + char name[JACK_DRIVER_NAME_MAX + 1]; /**< The parameter's name */ + char character; /**< The parameter's character (for getopt, etc) */ + jack_driver_param_type_t type; /**< The parameter's type */ + jack_driver_param_value_t value; /**< The parameter's (default) value */ + char short_desc[64]; /**< A short (~30 chars) description for the user */ + char long_desc[1024]; /**< A longer description for the user */ + } + jack_driver_param_desc_t; + + /** A driver parameter */ + typedef struct { + char character; + jack_driver_param_value_t value; + } + jack_driver_param_t; + + + /** A struct for describing a jack driver */ + typedef struct { + char name[JACK_DRIVER_NAME_MAX + 1]; /**< The driver's canonical name */ + char file[PATH_MAX + 1]; /**< The filename of the driver's shared object file */ + uint32_t nparams; /**< The number of parameters the driver has */ + jack_driver_param_desc_t * params; /**< An array of parameter descriptors */ + } + jack_driver_desc_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_driver_interface_h__ */ + + diff --git a/common/driver_parse.h b/common/driver_parse.h new file mode 100644 index 00000000..67a20109 --- /dev/null +++ b/common/driver_parse.h @@ -0,0 +1,33 @@ +/* +Copyright (C) 2003 Bob Ham <rah@bash.sh + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __jack_driver_parse_h__ +#define __jack_driver_parse_h__ + +#include "jslist.h" +#include "driver_interface.h" +#include "JackExports.h" + +EXPORT int +jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSList ** param_ptr); + + +#endif /* __jack_driver_parse_h__ */ + + diff --git a/common/intclient.h b/common/intclient.h new file mode 100644 index 00000000..8e87f6d8 --- /dev/null +++ b/common/intclient.h @@ -0,0 +1,132 @@ +/* +* Copyright (C) 2004 Jack O'Quin +* +* 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. +* +* $Id: intclient.h,v 1.2.2.1 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __jack_intclient_h__ +#define __jack_intclient_h__ + +#ifdef __cplusplus +extern "C" +{ +#endif + + //#include <jack/types.h> +#include "types.h" + + /** + * Get an internal client's name. This is useful when @ref + * JackUseExactName was not specified on jack_internal_client_load() + * and @ref JackNameNotUnique status was returned. In that case, the + * actual name will differ from the @a client_name requested. + * + * @param client requesting JACK client's handle. + * + * @param intclient handle returned from jack_internal_client_load() + * or jack_internal_client_handle(). + * + * @return NULL if unsuccessful, otherwise pointer to the internal + * client name obtained from the heap via malloc(). The caller should + * free() this storage when no longer needed. + */ + char *jack_get_internal_client_name (jack_client_t *client, + jack_intclient_t intclient); + + /** + * Return the @ref jack_intclient_t handle for an internal client + * running in the JACK server. + * + * @param client requesting JACK client's handle. + * + * @param client_name for the internal client of no more than + * jack_client_name_size() characters. The name scope is local to the + * current server. + * + * @param status (if non-NULL) an address for JACK to return + * information from this operation. This status word is formed by + * OR-ing together the relevant @ref JackStatus bits. + * + * @return Opaque internal client handle if successful. If 0, the + * internal client was not found, and @a *status includes the @ref + * JackNoSuchClient and @ref JackFailure bits. + */ + jack_intclient_t jack_internal_client_handle (jack_client_t *client, + const char *client_name, + jack_status_t *status); + + /** + * Load an internal client into the JACK server. + * + * Internal clients run inside the JACK server process. They can use + * most of the same functions as external clients. Each internal + * client is built as a shared object module, which must declare + * jack_initialize() and jack_finish() entry points called at load and + * unload times. See @ref inprocess.c for an example. + * + * @param client loading JACK client's handle. + * + * @param client_name of at most jack_client_name_size() characters + * for the internal client to load. The name scope is local to the + * current server. + * + * @param options formed by OR-ing together @ref JackOptions bits. + * Only the @ref JackLoadOptions bits are valid. + * + * @param status (if non-NULL) an address for JACK to return + * information from the load operation. This status word is formed by + * OR-ing together the relevant @ref JackStatus bits. + * + * <b>Optional parameters:</b> depending on corresponding [@a options + * bits] additional parameters may follow @a status (in this order). + * + * @arg [@ref JackLoadName] <em>(char *) load_name</em> is the shared + * object file from which to load the new internal client (otherwise + * use the @a client_name). + * + * @arg [@ref JackLoadInit] <em>(char *) load_init</em> an arbitary + * string passed to the internal client's jack_initialize() routine + * (otherwise NULL), of no more than @ref JACK_LOAD_INIT_LIMIT bytes. + * + * @return Opaque internal client handle if successful. If this is 0, + * the load operation failed, the internal client was not loaded, and + * @a *status includes the @ref JackFailure bit. + */ + jack_intclient_t jack_internal_client_load (jack_client_t *client, + const char *client_name, + jack_options_t options, + jack_status_t *status, ...); + /** + * Unload an internal client from a JACK server. This calls the + * intclient's jack_finish() entry point then removes it. See @ref + * inprocess.c for an example. + * + * @param client unloading JACK client's handle. + * + * @param intclient handle returned from jack_internal_client_load() or + * jack_internal_client_handle(). + * + * @return 0 if successful, otherwise @ref JackStatus bits. + */ + jack_status_t jack_internal_client_unload (jack_client_t *client, + jack_intclient_t intclient); + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_intclient_h__ */ diff --git a/common/jack.h b/common/jack.h new file mode 100644 index 00000000..1fb97c8e --- /dev/null +++ b/common/jack.h @@ -0,0 +1,805 @@ +/* + Copyright (C) 2001 Paul Davis + Copyright (C) 2004 Jack O'Quin + + 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. + + $Id: jack.h,v 1.5.2.6 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __jack_h__ +#define __jack_h__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef WIN32 + #include <windows.h> + typedef HANDLE pthread_t; +#else + #include <pthread.h> +#endif + + /* + #include <jack/types.h> + #include <jack/transport.h> + */ + +#include "types.h" +#include "transport.h" + + /** + * Note: More documentation can be found in jack/types.h. + */ + + /** + * Open an external client session with a JACK server. This interface + * is more complex but more powerful than jack_client_new(). With it, + * clients may choose which of several servers to connect, and control + * whether and how to start the server automatically, if it was not + * already running. There is also an option for JACK to generate a + * unique client name, when necessary. + * + * @param client_name of at most jack_client_name_size() characters. + * The name scope is local to each server. Unless forbidden by the + * @ref JackUseExactName option, the server will modify this name to + * create a unique variant, if needed. + * + * @param options formed by OR-ing together @ref JackOptions bits. + * Only the @ref JackOpenOptions bits are allowed. + * + * @param status (if non-NULL) an address for JACK to return + * information from the open operation. This status word is formed by + * OR-ing together the relevant @ref JackStatus bits. + * + * + * <b>Optional parameters:</b> depending on corresponding [@a options + * bits] additional parameters may follow @a status (in this order). + * + * @arg [@ref JackServerName] <em>(char *) server_name</em> selects + * from among several possible concurrent server instances. Server + * names are unique to each user. If unspecified, use "default" + * unless \$JACK_DEFAULT_SERVER is defined in the process environment. + * + * @return Opaque client handle if successful. If this is NULL, the + * open operation failed, @a *status includes @ref JackFailure and the + * caller is not a JACK client. + */ + jack_client_t * jack_client_open (const char *client_name, + jack_options_t options, + jack_status_t *status, ...); + + /** + * Attempt to become an external client of the Jack server. + * + * JACK is evolving a mechanism for automatically starting the server + * when needed. As a transition, jack_client_new() only does this + * when \$JACK_START_SERVER is defined in the environment of the + * calling process. In the future this will become normal behavior. + * For full control of this feature, use jack_client_open(), instead. + * In either case, defining \$JACK_NO_START_SERVER disables this + * feature. + * + * @param client_name of at most jack_client_name_size() characters. + * If this name is already in use, the request fails. + * + * @return Opaque client handle if successful, otherwise NULL. + * + * @note Failure generally means that the JACK server is not running. + * If there was some other problem, it will be reported via the @ref + * jack_error_callback mechanism. Use jack_client_open() and check + * the @a status parameter for more detailed information. + */ + jack_client_t * jack_client_new (const char *client_name); + + /** + * Disconnects an external client from a JACK server. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_client_close (jack_client_t *client); + + /** + * @return the maximum number of characters in a JACK client name + * including the final NULL character. This value is a constant. + */ + int jack_client_name_size (void); + + /** + * @return pointer to actual client name. This is useful when @ref + * JackUseExactName is not specified on open and @ref + * JackNameNotUnique status was returned. In that case, the actual + * name will differ from the @a client_name requested. + */ + char * jack_get_client_name (jack_client_t *client); + + /** + * Load an internal client into the Jack server. + * + * Internal clients run inside the JACK server process. They can use + * most of the same functions as external clients. Each internal + * client must declare jack_initialize() and jack_finish() entry + * points, called at load and unload times. See inprocess.c for an + * example of how to write an internal client. + * + * @deprecated Please use jack_internal_client_load(). + * + * @param client_name of at most jack_client_name_size() characters. + * + * @param load_name of a shared object file containing the code for + * the new client. + * + * @param load_init an arbitary string passed to the jack_initialize() + * routine of the new client (may be NULL). + * + * @return 0 if successful. + */ + int jack_internal_client_new (const char *client_name, + const char *load_name, + const char *load_init); + + jack_client_t* my_jack_internal_client_new(const char* client_name); + + /** + * Remove an internal client from a JACK server. + * + * @deprecated Please use jack_internal_client_load(). + */ + void jack_internal_client_close (const char *client_name); + + void my_jack_internal_client_close (jack_client_t* client); + + /** + * @param client pointer to JACK client structure. + * + * Check if the JACK subsystem is running with -R (--realtime). + * + * @return 1 if JACK is running realtime, 0 otherwise + */ + int jack_is_realtime (jack_client_t *client); + + /** + * @param client pointer to JACK client structure. + * @param function The jack_shutdown function pointer. + * @param arg The arguments for the jack_shutdown function. + * + * Register a function (and argument) to be called if and when the + * JACK server shuts down the client thread. The function must + * be written as if it were an asynchonrous POSIX signal + * handler --- use only async-safe functions, and remember that it + * is executed from another thread. A typical function might + * set a flag or write to a pipe so that the rest of the + * application knows that the JACK client thread has shut + * down. + * + * NOTE: clients do not need to call this. It exists only + * to help more complex clients understand what is going + * on. It should be called before jack_client_activate(). + */ + void jack_on_shutdown (jack_client_t *client, + void (*function)(void *arg), void *arg); + + /** + * Tell the Jack server to call @a process_callback whenever there is + * work be done, passing @a arg as the second argument. + * + * The code in the supplied function must be suitable for real-time + * execution. That means that it cannot call functions that might + * block for a long time. This includes malloc, free, printf, + * pthread_mutex_lock, sleep, wait, poll, select, pthread_join, + * pthread_cond_wait, etc, etc. See + * http://jackit.sourceforge.net/docs/design/design.html#SECTION00411000000000000000 + * for more information. + * + * @return 0 on success, otherwise a non-zero error code, causing JACK + * to remove that client from the process() graph. + */ + int jack_set_process_callback (jack_client_t *client, + JackProcessCallback process_callback, + void *arg); + + /** + * Tell JACK to call @a thread_init_callback once just after + * the creation of the thread in which all other callbacks + * will be handled. + * + * The code in the supplied function does not need to be + * suitable for real-time execution. + * + * @return 0 on success, otherwise a non-zero error code, causing JACK + * to remove that client from the process() graph. + */ + int jack_set_thread_init_callback (jack_client_t *client, + JackThreadInitCallback thread_init_callback, + void *arg); + + /** + * Tell the Jack server to call @a freewheel_callback + * whenever we enter or leave "freewheel" mode, passing @a + * arg as the second argument. The first argument to the + * callback will be non-zero if JACK is entering freewheel + * mode, and zero otherwise. + * + * @return 0 on success, otherwise a non-zero error code. + */ + int jack_set_freewheel_callback (jack_client_t *client, + JackFreewheelCallback freewheel_callback, + void *arg); + + /** + * Start/Stop JACK's "freewheel" mode. + * + * When in "freewheel" mode, JACK no longer waits for + * any external event to begin the start of the next process + * cycle. + * + * As a result, freewheel mode causes "faster than realtime" + * execution of a JACK graph. If possessed, real-time + * scheduling is dropped when entering freewheel mode, and + * if appropriate it is reacquired when stopping. + * + * IMPORTANT: on systems using capabilities to provide real-time + * scheduling (i.e. Linux kernel 2.4), if onoff is zero, this function + * must be called from the thread that originally called jack_activate(). + * This restriction does not apply to other systems (e.g. Linux kernel 2.6 + * or OS X). + * + * @param client pointer to JACK client structure + * @param onoff if non-zero, freewheel mode starts. Otherwise + * freewheel mode ends. + * + * @return 0 on success, otherwise a non-zero error code. + */ + int jack_set_freewheel(jack_client_t* client, int onoff); + + /** + * Change the buffer size passed to the @a process_callback. + * + * This operation stops the JACK engine process cycle, then calls all + * registered @a bufsize_callback functions before restarting the + * process cycle. This will cause a gap in the audio flow, so it + * should only be done at appropriate stopping points. + * + * @see jack_set_buffer_size_callback() + * + * @param client pointer to JACK client structure. + * @param nframes new buffer size. Must be a power of two. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes); + + /** + * Tell JACK to call @a bufsize_callback whenever the size of the the + * buffer that will be passed to the @a process_callback is about to + * change. Clients that depend on knowing the buffer size must supply + * a @a bufsize_callback before activating themselves. + * + * @param client pointer to JACK client structure. + * @param bufsize_callback function to call when the buffer size changes. + * @param arg argument for @a bufsize_callback. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_set_buffer_size_callback (jack_client_t *client, + JackBufferSizeCallback bufsize_callback, + void *arg); + + /** + * Tell the Jack server to call @a srate_callback whenever the system + * sample rate changes. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_set_sample_rate_callback (jack_client_t *client, + JackSampleRateCallback srate_callback, + void *arg); + + /** + * Tell the JACK server to call @a registration_callback whenever a + * port is registered or unregistered, passing @a arg as a parameter. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_set_port_registration_callback (jack_client_t *, + JackPortRegistrationCallback + registration_callback, void *arg); + + /** + * Tell the JACK server to call @a graph_callback whenever the + * processing graph is reordered, passing @a arg as a parameter. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_set_graph_order_callback (jack_client_t *, + JackGraphOrderCallback graph_callback, + void *); + + /** + * Tell the JACK server to call @a xrun_callback whenever there is a + * xrun, passing @a arg as a parameter. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_set_xrun_callback (jack_client_t *, + JackXRunCallback xrun_callback, void *arg); + + /** + * Tell the Jack server that the program is ready to start processing + * audio. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_activate (jack_client_t *client); + + /** + * Tell the Jack server to remove this @a client from the process + * graph. Also, disconnect all ports belonging to it, since inactive + * clients have no port connections. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_deactivate (jack_client_t *client); + + /** + * Create a new port for the client. This is an object used for moving + * data of any type in or out of the client. Ports may be connected + * in various ways. + * + * Each port has a short name. The port's full name contains the name + * of the client concatenated with a colon (:) followed by its short + * name. The jack_port_name_size() is the maximum length of this full + * name. Exceeding that will cause the port registration to fail and + * return NULL. + * + * All ports have a type, which may be any non-NULL and non-zero + * length string, passed as an argument. Some port types are built + * into the JACK API, currently only JACK_DEFAULT_AUDIO_TYPE. + * + * @param client pointer to JACK client structure. + * @param port_name non-empty short name for the new port (not + * including the leading @a "client_name:"). + * @param port_type port type name. If longer than + * jack_port_type_size(), only that many characters are significant. + * @param flags @ref JackPortFlags bit mask. + * @param buffer_size must be non-zero if this is not a built-in @a + * port_type. Otherwise, it is ignored. + * + * @return jack_port_t pointer on success, otherwise NULL. + */ + jack_port_t * jack_port_register (jack_client_t *client, + const char *port_name, + const char *port_type, + unsigned long flags, + unsigned long buffer_size); + + /** + * Remove the port from the client, disconnecting any existing + * connections. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_port_unregister (jack_client_t *, jack_port_t *); + + /** + * This returns a pointer to the memory area associated with the + * specified port. For an output port, it will be a memory area + * that can be written to; for an input port, it will be an area + * containing the data from the port's connection(s), or + * zero-filled. if there are multiple inbound connections, the data + * will be mixed appropriately. + * + * FOR OUTPUT PORTS ONLY + * --------------------- + * You may cache the value returned, but only between calls to + * your "blocksize" callback. For this reason alone, you should + * either never cache the return value or ensure you have + * a "blocksize" callback and be sure to invalidate the cached + * address from there. + */ + void * jack_port_get_buffer (jack_port_t *, jack_nframes_t); + + /** + * @return the full name of the jack_port_t (including the @a + * "client_name:" prefix). + * + * @see jack_port_name_size(). + */ + const char * jack_port_name (const jack_port_t *port); + + /** + * @return the short name of the jack_port_t (not including the @a + * "client_name:" prefix). + * + * @see jack_port_name_size(). + */ + const char * jack_port_short_name (const jack_port_t *port); + + /** + * @return the @ref JackPortFlags of the jack_port_t. + */ + int jack_port_flags (const jack_port_t *port); + + /** + * @return the @a port type, at most jack_port_type_size() characters + * including a final NULL. + */ + const char * jack_port_type (const jack_port_t *port); + + /** + * @return TRUE if the jack_port_t belongs to the jack_client_t. + */ + int jack_port_is_mine (const jack_client_t *, const jack_port_t *port); + + /** + * @return number of connections to or from @a port. + * + * @pre The calling client must own @a port. + */ + int jack_port_connected (const jack_port_t *port); + + /** + * @return TRUE if the locally-owned @a port is @b directly connected + * to the @a port_name. + * + * @see jack_port_name_size() + */ + int jack_port_connected_to (const jack_port_t *port, + const char *port_name); + + /** + * @return a null-terminated array of full port names to which the @a + * port is connected. If none, returns NULL. + * + * The caller is responsible for calling free(3) on any non-NULL + * returned value. + * + * @param port locally owned jack_port_t pointer. + * + * @see jack_port_name_size(), jack_port_get_all_connections() + */ + const char ** jack_port_get_connections (const jack_port_t *port); + + /** + * @return a null-terminated array of full port names to which the @a + * port is connected. If none, returns NULL. + * + * The caller is responsible for calling free(3) on any non-NULL + * returned value. + * + * This differs from jack_port_get_connections() in two important + * respects: + * + * 1) You may not call this function from code that is + * executed in response to a JACK event. For example, + * you cannot use it in a GraphReordered handler. + * + * 2) You need not be the owner of the port to get information + * about its connections. + * + * @see jack_port_name_size() + */ + const char ** jack_port_get_all_connections (const jack_client_t *client, + const jack_port_t *port); + + /** + * A client may call this on a pair of its own ports to + * semi-permanently wire them together. This means that + * a client that wants to direct-wire an input port to + * an output port can call this and then no longer + * have to worry about moving data between them. Any data + * arriving at the input port will appear automatically + * at the output port. + * + * The 'destination' port must be an output port. The 'source' + * port must be an input port. Both ports must belong to + * the same client. You cannot use this to tie ports between + * clients. That is what a connection is for. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_port_tie (jack_port_t *src, jack_port_t *dst); + + /** + * This undoes the effect of jack_port_tie(). The port + * should be same as the 'destination' port passed to + * jack_port_tie(). + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_port_untie (jack_port_t *port); + + /** + * A client may call this function to prevent other objects + * from changing the connection status of a port. The port + * must be owned by the calling client. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_port_lock (jack_client_t *, jack_port_t *); + + /** + * This allows other objects to change the connection status of a port. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_port_unlock (jack_client_t *, jack_port_t *); + + /** + * @return the time (in frames) between data being available or + * delivered at/to a port, and the time at which it arrived at or is + * delivered to the "other side" of the port. E.g. for a physical + * audio output port, this is the time between writing to the port and + * when the signal will leave the connector. For a physical audio + * input port, this is the time between the sound arriving at the + * connector and the corresponding frames being readable from the + * port. + */ + jack_nframes_t jack_port_get_latency (jack_port_t *port); + + /** + * The maximum of the sum of the latencies in every + * connection path that can be drawn between the port and other + * ports with the @ref JackPortIsTerminal flag set. + */ + jack_nframes_t jack_port_get_total_latency (jack_client_t *, + jack_port_t *port); + + /** + * The port latency is zero by default. Clients that control + * physical hardware with non-zero latency should call this + * to set the latency to its correct value. Note that the value + * should include any systemic latency present "outside" the + * physical hardware controlled by the client. For example, + * for a client controlling a digital audio interface connected + * to an external digital converter, the latency setting should + * include both buffering by the audio interface *and* the converter. + */ + void jack_port_set_latency (jack_port_t *, jack_nframes_t); + + /** + * + */ + int jack_recompute_total_latencies (jack_client_t*); + + /** + * Modify a port's short name. May be called at any time. If the + * resulting full name (including the @a "client_name:" prefix) is + * longer than jack_port_name_size(), it will be truncated. + * + * @return 0 on success, otherwise a non-zero error code. + */ + int jack_port_set_name (jack_port_t *port, const char *port_name); + + /** + * If @ref JackPortCanMonitor is set for this @a port, turn input + * monitoring on or off. Otherwise, do nothing. + */ + int jack_port_request_monitor (jack_port_t *port, int onoff); + + /** + * If @ref JackPortCanMonitor is set for this @a port_name, turn input + * monitoring on or off. Otherwise, do nothing. + * + * @return 0 on success, otherwise a non-zero error code. + * + * @see jack_port_name_size() + */ + int jack_port_request_monitor_by_name (jack_client_t *client, + const char *port_name, int onoff); + + /** + * If @ref JackPortCanMonitor is set for a port, this function turns + * on input monitoring if it was off, and turns it off if only one + * request has been made to turn it on. Otherwise it does nothing. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_port_ensure_monitor (jack_port_t *port, int onoff); + + /** + * @return TRUE if input monitoring has been requested for @a port. + */ + int jack_port_monitoring_input (jack_port_t *port); + + /** + * Establish a connection between two ports. + * + * When a connection exists, data written to the source port will + * be available to be read at the destination port. + * + * @pre The port types must be identical. + * + * @pre The @ref JackPortFlags of the @a source_port must include @ref + * JackPortIsOutput. + * + * @pre The @ref JackPortFlags of the @a destination_port must include + * @ref JackPortIsInput. + * + * @return 0 on success, EEXIST if the connection is already made, + * otherwise a non-zero error code + */ + int jack_connect (jack_client_t *, + const char *source_port, + const char *destination_port); + + /** + * Remove a connection between two ports. + * + * @pre The port types must be identical. + * + * @pre The @ref JackPortFlags of the @a source_port must include @ref + * JackPortIsOutput. + * + * @pre The @ref JackPortFlags of the @a destination_port must include + * @ref JackPortIsInput. + * + * @return 0 on success, otherwise a non-zero error code + */ + int jack_disconnect (jack_client_t *, + const char *source_port, + const char *destination_port); + + /** + * Perform the same function as jack_disconnect() using port handles + * rather than names. This avoids the name lookup inherent in the + * name-based version. + * + * Clients connecting their own ports are likely to use this function, + * while generic connection clients (e.g. patchbays) would use + * jack_disconnect(). + */ + int jack_port_disconnect (jack_client_t *, jack_port_t *); + + /** + * @return the maximum number of characters in a full JACK port name + * including the final NULL character. This value is a constant. + * + * A port's full name contains the owning client name concatenated + * with a colon (:) followed by its short name and a NULL + * character. + */ + int jack_port_name_size(void); + + /** + * @return the maximum number of characters in a JACK port type name + * including the final NULL character. This value is a constant. + */ + int jack_port_type_size(void); + + /** + * @return the sample rate of the jack system, as set by the user when + * jackd was started. + */ + jack_nframes_t jack_get_sample_rate (jack_client_t *); + + /** + * @return the current maximum size that will ever be passed to the @a + * process_callback. It should only be used *before* the client has + * been activated. This size may change, clients that depend on it + * must register a @a bufsize_callback so they will be notified if it + * does. + * + * @see jack_set_buffer_size_callback() + */ + jack_nframes_t jack_get_buffer_size (jack_client_t *); + + /** + * @param port_name_pattern A regular expression used to select + * ports by name. If NULL or of zero length, no selection based + * on name will be carried out. + * @param type_name_pattern A regular expression used to select + * ports by type. If NULL or of zero length, no selection based + * on type will be carried out. + * @param flags A value used to select ports by their flags. + * If zero, no selection based on flags will be carried out. + * + * @return a NULL-terminated array of ports that match the specified + * arguments. The caller is responsible for calling free(3) any + * non-NULL returned value. + * + * @see jack_port_name_size(), jack_port_type_size() + */ + const char ** jack_get_ports (jack_client_t *, + const char *port_name_pattern, + const char *type_name_pattern, + unsigned long flags); + + /** + * @return address of the jack_port_t named @a port_name. + * + * @see jack_port_name_size() + */ + jack_port_t * jack_port_by_name (jack_client_t *, const char *port_name); + + /** + * @return address of the jack_port_t of a @a port_id. + */ + jack_port_t * jack_port_by_id (jack_client_t *client, + jack_port_id_t port_id); + + /** + * Old-style interface to become the timebase for the entire JACK + * subsystem. + * + * @deprecated This function still exists for compatibility with the + * earlier transport interface, but it does nothing. Instead, see + * transport.h and use jack_set_timebase_callback(). + * + * @return ENOSYS, function not implemented. + */ + int jack_engine_takeover_timebase (jack_client_t *); + + /** + * @return the time in frames that has passed since the JACK server + * began the current process cycle. + */ + jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *); + + /** + * @return an estimate of the current time in frames. This is a + * running counter, no significance should be attached to its value, + * but it can be compared to a previously returned value. + */ + jack_nframes_t jack_frame_time (const jack_client_t *); + + /** + * @return the frame_time after the last processing of the graph + * this is only to be used from the process callback. + * + * This function can be used to put timestamps generated by + * jack_frame_time() in correlation to the current process cycle. + */ + jack_nframes_t jack_last_frame_time (const jack_client_t *client); + + /** + * @return the current CPU load estimated by JACK. This is a running + * average of the time it takes to execute a full process cycle for + * all clients as a percentage of the real time available per cycle + * determined by the buffer size and sample rate. + */ + float jack_cpu_load (jack_client_t *client); + + /** + * @return the pthread ID of the thread running the JACK client side + * code. + */ + pthread_t jack_client_thread_id (jack_client_t *); + + /** + * Display JACK error message. + * + * Set via jack_set_error_function(), otherwise a JACK-provided + * default will print @a msg (plus a newline) to stderr. + * + * @param msg error message text (no newline at end). + */ + extern void (*jack_error_callback)(const char *msg); + + /** + * Set the @ref jack_error_callback for error message display. + * + * The JACK library provides two built-in callbacks for this purpose: + * default_jack_error_callback() and silent_jack_error_callback(). + */ + void jack_set_error_function (void (*func)(const char *)); + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_h__ */ diff --git a/common/jslist.h b/common/jslist.h new file mode 100644 index 00000000..45e8c266 --- /dev/null +++ b/common/jslist.h @@ -0,0 +1,287 @@ +/* + Based on gslist.c from glib-1.2.9 (LGPL). + + Adaption to JACK, Copyright (C) 2002 Kai Vehmanen. + - replaced use of gtypes with normal ANSI C types + - glib's memory allocation routines replaced with + malloc/free calls + + 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. + +*/ + +#ifndef __jack_jslist_h__ +#define __jack_jslist_h__ + +#include <stdlib.h> + +#ifdef WIN32 +#define __inline__ inline +#endif + +typedef struct _JSList JSList; + +typedef int (*JCompareFunc) (void* a, void* b); +struct _JSList +{ + void *data; + JSList *next; +}; + +static __inline__ +JSList* +jack_slist_alloc (void) +{ + JSList *new_list; + + new_list = (JSList*)malloc(sizeof(JSList)); + new_list->data = NULL; + new_list->next = NULL; + + return new_list; +} + +static __inline__ +JSList* +jack_slist_prepend (JSList* list, void* data) +{ + JSList *new_list; + + new_list = (JSList*)malloc(sizeof(JSList)); + new_list->data = data; + new_list->next = list; + + return new_list; +} + +#define jack_slist_next(slist) ((slist) ? (((JSList *)(slist))->next) : NULL) +static __inline__ +JSList* +jack_slist_last (JSList *list) +{ + if (list) { + while (list->next) + list = list->next; + } + + return list; +} + +static __inline__ +JSList* +jack_slist_remove_link (JSList *list, + JSList *link) +{ + JSList *tmp; + JSList *prev; + + prev = NULL; + tmp = list; + + while (tmp) { + if (tmp == link) { + if (prev) + prev->next = tmp->next; + if (list == tmp) + list = list->next; + + tmp->next = NULL; + break; + } + + prev = tmp; + tmp = tmp->next; + } + + return list; +} + +static __inline__ +void +jack_slist_free (JSList *list) +{ + while (list) { + JSList *next = list->next; + free(list); + list = next; + } +} + +static __inline__ +void +jack_slist_free_1 (JSList *list) +{ + if (list) { + free(list); + } +} + +static __inline__ +JSList* +jack_slist_remove (JSList *list, + void *data) +{ + JSList *tmp; + JSList *prev; + + prev = NULL; + tmp = list; + + while (tmp) { + if (tmp->data == data) { + if (prev) + prev->next = tmp->next; + if (list == tmp) + list = list->next; + + tmp->next = NULL; + jack_slist_free (tmp); + + break; + } + + prev = tmp; + tmp = tmp->next; + } + + return list; +} + +static __inline__ +unsigned int +jack_slist_length (JSList *list) +{ + unsigned int length; + + length = 0; + while (list) { + length++; + list = list->next; + } + + return length; +} + +static __inline__ +JSList* +jack_slist_find (JSList *list, + void *data) +{ + while (list) { + if (list->data == data) + break; + list = list->next; + } + + return list; +} + +static __inline__ +JSList* +jack_slist_copy (JSList *list) +{ + JSList *new_list = NULL; + + if (list) { + JSList *last; + + new_list = jack_slist_alloc (); + new_list->data = list->data; + last = new_list; + list = list->next; + while (list) { + last->next = jack_slist_alloc (); + last = last->next; + last->data = list->data; + list = list->next; + } + } + + return new_list; +} + +static __inline__ +JSList* +jack_slist_append (JSList *list, + void *data) +{ + JSList *new_list; + JSList *last; + + new_list = jack_slist_alloc (); + new_list->data = data; + + if (list) { + last = jack_slist_last (list); + last->next = new_list; + + return list; + } else + return new_list; +} + +static __inline__ +JSList* +jack_slist_sort_merge (JSList *l1, + JSList *l2, + JCompareFunc compare_func) +{ + JSList list, *l; + + l = &list; + + while (l1 && l2) { + if (compare_func(l1->data, l2->data) < 0) { + l = l->next = l1; + l1 = l1->next; + } else { + l = l->next = l2; + l2 = l2->next; + } + } + l->next = l1 ? l1 : l2; + + return list.next; +} + +static __inline__ +JSList* +jack_slist_sort (JSList *list, + JCompareFunc compare_func) +{ + JSList *l1, *l2; + + if (!list) + return NULL; + if (!list->next) + return list; + + l1 = list; + l2 = list->next; + + while ((l2 = l2->next) != NULL) { + if ((l2 = l2->next) == NULL) + break; + l1 = l1->next; + } + l2 = l1->next; + l1->next = NULL; + + return jack_slist_sort_merge (jack_slist_sort (list, compare_func), + jack_slist_sort (l2, compare_func), + compare_func); +} + +#endif /* __jack_jslist_h__ */ diff --git a/common/patest_sine.c b/common/patest_sine.c new file mode 100644 index 00000000..80a9c499 --- /dev/null +++ b/common/patest_sine.c @@ -0,0 +1,151 @@ +/** @file patest_sine.c + @brief Play a sine wave for several seconds. + @author Ross Bencina <rossb@audiomulch.com> + @author Phil Burk <philburk@softsynth.com> +*/ +/* + * $Id: patest_sine.c,v 1.1.2.1 2006/06/06 19:37:56 letz Exp $ + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include <stdio.h> +#include <math.h> +#include "portaudio.h" + +#define NUM_SECONDS (5) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (64) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + + (void) timeInfo; /* Prevent unused variable warnings. */ + (void) statusFlags; + (void) inputBuffer; + + for( i=0; i<framesPerBuffer; i++ ) + { + *out++ = data->sine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + + return paContinue; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PaStreamParameters outputParameters; + PaStream *stream; + PaError err; + paTestData data; + int i; + + + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); + + /* initialise sinusoidal wavetable */ + for( i=0; i<TABLE_SIZE; i++ ) + { + data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ); + } + data.left_phase = data.right_phase = 0; + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + outputParameters.channelCount = 2; /* stereo output */ + outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ + outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + err = Pa_OpenStream( + &stream, + NULL, /* no input */ + &outputParameters, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Play for %d seconds.\n", NUM_SECONDS ); + Pa_Sleep( NUM_SECONDS * 1000 ); + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + + Pa_Terminate(); + printf("Test finished.\n"); + + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/common/ringbuffer.c b/common/ringbuffer.c new file mode 100644 index 00000000..75170411 --- /dev/null +++ b/common/ringbuffer.c @@ -0,0 +1,359 @@ +/* + Copyright (C) 2000 Paul Davis + Copyright (C) 2003 Rohan Drape + + 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. + + ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code. + This is safe for the case of one read thread and one write thread. +*/ + +#include <stdlib.h> +#include <string.h> +#ifdef USE_MLOCK +#include <sys/mman.h> +#endif /* USE_MLOCK */ +#include "ringbuffer.h" + +/* Create a new ringbuffer to hold at least `sz' bytes of data. The + actual buffer size is rounded up to the next power of two. */ + +jack_ringbuffer_t * +jack_ringbuffer_create (size_t sz) +{ + int power_of_two; + jack_ringbuffer_t *rb; + + rb = malloc (sizeof (jack_ringbuffer_t)); + + for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); + + rb->size = 1 << power_of_two; + rb->size_mask = rb->size; + rb->size_mask -= 1; + rb->write_ptr = 0; + rb->read_ptr = 0; + rb->buf = malloc (rb->size); + memset(rb->buf, 0, rb->size); + rb->mlocked = 0; + + return rb; +} + +/* Free all data associated with the ringbuffer `rb'. */ + +void +jack_ringbuffer_free (jack_ringbuffer_t * rb) +{ +#ifdef USE_MLOCK + if (rb->mlocked) { + munlock (rb->buf, rb->size); + } +#endif /* USE_MLOCK */ + free (rb->buf); +} + +/* Lock the data block of `rb' using the system call 'mlock'. */ + +int +jack_ringbuffer_mlock (jack_ringbuffer_t * rb) +{ +#ifdef USE_MLOCK + if (mlock (rb->buf, rb->size)) { + return -1; + } +#endif /* USE_MLOCK */ + rb->mlocked = 1; + return 0; +} + +/* Reset the read and write pointers to zero. This is not thread + safe. */ + +void +jack_ringbuffer_reset (jack_ringbuffer_t * rb) +{ + rb->read_ptr = 0; + rb->write_ptr = 0; +} + +/* Return the number of bytes available for reading. This is the + number of bytes in front of the read pointer and behind the write + pointer. */ + +size_t +jack_ringbuffer_read_space (const jack_ringbuffer_t * rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) { + return w - r; + } else { + return (w - r + rb->size) & rb->size_mask; + } +} + +/* Return the number of bytes available for writing. This is the + number of bytes in front of the write pointer and behind the read + pointer. */ + +size_t +jack_ringbuffer_write_space (const jack_ringbuffer_t * rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) { + return ((r - w + rb->size) & rb->size_mask) - 1; + } else if (w < r) { + return (r - w) - 1; + } else { + return rb->size - 1; + } +} + +/* The copying data reader. Copy at most `cnt' bytes from `rb' to + `dest'. Returns the actual number of bytes copied. */ + +size_t +jack_ringbuffer_read (jack_ringbuffer_t * rb, char *dest, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + + if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) { + return 0; + } + + to_read = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->read_ptr + to_read; + + if (cnt2 > rb->size) { + n1 = rb->size - rb->read_ptr; + n2 = cnt2 & rb->size_mask; + } else { + n1 = to_read; + n2 = 0; + } + + memcpy (dest, &(rb->buf[rb->read_ptr]), n1); + rb->read_ptr += n1; + rb->read_ptr &= rb->size_mask; + + if (n2) { + memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); + rb->read_ptr += n2; + rb->read_ptr &= rb->size_mask; + } + + return to_read; +} + +/* The copying data reader w/o read pointer advance. Copy at most + `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes +copied. */ + +size_t +jack_ringbuffer_peek (jack_ringbuffer_t * rb, char *dest, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + size_t tmp_read_ptr; + + tmp_read_ptr = rb->read_ptr; + + if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) { + return 0; + } + + to_read = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = tmp_read_ptr + to_read; + + if (cnt2 > rb->size) { + n1 = rb->size - tmp_read_ptr; + n2 = cnt2 & rb->size_mask; + } else { + n1 = to_read; + n2 = 0; + } + + memcpy (dest, &(rb->buf[tmp_read_ptr]), n1); + tmp_read_ptr += n1; + tmp_read_ptr &= rb->size_mask; + + if (n2) { + memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2); + tmp_read_ptr += n2; + tmp_read_ptr &= rb->size_mask; + } + + return to_read; +} + + +/* The copying data writer. Copy at most `cnt' bytes to `rb' from + `src'. Returns the actual number of bytes copied. */ + +size_t +jack_ringbuffer_write (jack_ringbuffer_t * rb, const char *src, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_write; + size_t n1, n2; + + if ((free_cnt = jack_ringbuffer_write_space (rb)) == 0) { + return 0; + } + + to_write = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->write_ptr + to_write; + + if (cnt2 > rb->size) { + n1 = rb->size - rb->write_ptr; + n2 = cnt2 & rb->size_mask; + } else { + n1 = to_write; + n2 = 0; + } + + memcpy (&(rb->buf[rb->write_ptr]), src, n1); + rb->write_ptr += n1; + rb->write_ptr &= rb->size_mask; + + if (n2) { + memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); + rb->write_ptr += n2; + rb->write_ptr &= rb->size_mask; + } + + return to_write; +} + +/* Advance the read pointer `cnt' places. */ + +void +jack_ringbuffer_read_advance (jack_ringbuffer_t * rb, size_t cnt) +{ + rb->read_ptr += cnt; + rb->read_ptr &= rb->size_mask; +} + +/* Advance the write pointer `cnt' places. */ + +void +jack_ringbuffer_write_advance (jack_ringbuffer_t * rb, size_t cnt) +{ + rb->write_ptr += cnt; + rb->write_ptr &= rb->size_mask; +} + +/* The non-copying data reader. `vec' is an array of two places. Set + the values at `vec' to hold the current readable data at `rb'. If + the readable data is in one segment the second segment has zero + length. */ + +void +jack_ringbuffer_get_read_vector (const jack_ringbuffer_t * rb, + jack_ringbuffer_data_t * vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) { + free_cnt = w - r; + } else { + free_cnt = (w - r + rb->size) & rb->size_mask; + } + + cnt2 = r + free_cnt; + + if (cnt2 > rb->size) { + + /* Two part vector: the rest of the buffer after the current write + ptr, plus some from the start of the buffer. */ + + vec[0].buf = &(rb->buf[r]); + vec[0].len = rb->size - r; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + + } else { + + /* Single part vector: just the rest of the buffer */ + + vec[0].buf = &(rb->buf[r]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} + +/* The non-copying data writer. `vec' is an array of two places. Set + the values at `vec' to hold the current writeable data at `rb'. If + the writeable data is in one segment the second segment has zero + length. */ + +void +jack_ringbuffer_get_write_vector (const jack_ringbuffer_t * rb, + jack_ringbuffer_data_t * vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) { + free_cnt = ((r - w + rb->size) & rb->size_mask) - 1; + } else if (w < r) { + free_cnt = (r - w) - 1; + } else { + free_cnt = rb->size - 1; + } + + cnt2 = w + free_cnt; + + if (cnt2 > rb->size) { + + /* Two part vector: the rest of the buffer after the current write + ptr, plus some from the start of the buffer. */ + + vec[0].buf = &(rb->buf[w]); + vec[0].len = rb->size - w; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } else { + vec[0].buf = &(rb->buf[w]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} diff --git a/common/ringbuffer.h b/common/ringbuffer.h new file mode 100644 index 00000000..78bf6536 --- /dev/null +++ b/common/ringbuffer.h @@ -0,0 +1,235 @@ +/* + Copyright (C) 2000 Paul Davis + Copyright (C) 2003 Rohan Drape + + 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. + + $Id: ringbuffer.h,v 1.2.2.1 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef _RINGBUFFER_H +#define _RINGBUFFER_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <sys/types.h> + + /** @file ringbuffer.h + * + * A set of library functions to make lock-free ringbuffers available + * to JACK clients. The `capture_client.c' (in the example_clients + * directory) is a fully functioning user of this API. + * + * The key attribute of a ringbuffer is that it can be safely accessed + * by two threads simultaneously -- one reading from the buffer and + * the other writing to it -- without using any synchronization or + * mutual exclusion primitives. For this to work correctly, there can + * only be a single reader and a single writer thread. Their + * identities cannot be interchanged. + */ + + typedef struct { + char *buf; + size_t len; + } + jack_ringbuffer_data_t ; + + typedef struct { + char *buf; + volatile size_t write_ptr; + volatile size_t read_ptr; + size_t size; + size_t size_mask; + int mlocked; + } + jack_ringbuffer_t ; + + /** + * Allocates a ringbuffer data structure of a specified size. The + * caller must arrange for a call to jack_ringbuffer_free() to release + * the memory associated with the ringbuffer. + * + * @param sz the ringbuffer size in bytes. + * + * @return a pointer to a new jack_ringbuffer_t, if successful; NULL + * otherwise. + */ + jack_ringbuffer_t *jack_ringbuffer_create(size_t sz); + + /** + * Frees the ringbuffer data structure allocated by an earlier call to + * jack_ringbuffer_create(). + * + * @param rb a pointer to the ringbuffer structure. + */ + void jack_ringbuffer_free(jack_ringbuffer_t *rb); + + /** + * Fill a data structure with a description of the current readable + * data held in the ringbuffer. This description is returned in a two + * element array of jack_ringbuffer_data_t. Two elements are needed + * because the data to be read may be split across the end of the + * ringbuffer. + * + * The first element will always contain a valid @a len field, which + * may be zero or greater. If the @a len field is non-zero, then data + * can be read in a contiguous fashion using the address given in the + * corresponding @a buf field. + * + * If the second element has a non-zero @a len field, then a second + * contiguous stretch of data can be read from the address given in + * its corresponding @a buf field. + * + * @param rb a pointer to the ringbuffer structure. + * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t. + * + */ + void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb, + jack_ringbuffer_data_t *vec); + + /** + * Fill a data structure with a description of the current writable + * space in the ringbuffer. The description is returned in a two + * element array of jack_ringbuffer_data_t. Two elements are needed + * because the space available for writing may be split across the end + * of the ringbuffer. + * + * The first element will always contain a valid @a len field, which + * may be zero or greater. If the @a len field is non-zero, then data + * can be written in a contiguous fashion using the address given in + * the corresponding @a buf field. + * + * If the second element has a non-zero @a len field, then a second + * contiguous stretch of data can be written to the address given in + * the corresponding @a buf field. + * + * @param rb a pointer to the ringbuffer structure. + * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t. + */ + void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb, + jack_ringbuffer_data_t *vec); + + /** + * Read data from the ringbuffer. + * + * @param rb a pointer to the ringbuffer structure. + * @param dest a pointer to a buffer where data read from the + * ringbuffer will go. + * @param cnt the number of bytes to read. + * + * @return the number of bytes read, which may range from 0 to cnt. + */ + size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt); + + /** + * Read data from the ringbuffer. Opposed to jack_ringbuffer_read() + * this function does not move the read pointer. Thus it's + * a convenient way to inspect data in the ringbuffer in a + * continous fashion. The price is that the data is copied + * into a user provided buffer. For "raw" non-copy inspection + * of the data in the ringbuffer use jack_ringbuffer_get_read_vector(). + * + * @param rb a pointer to the ringbuffer structure. + * @param dest a pointer to a buffer where data read from the + * ringbuffer will go. + * @param cnt the number of bytes to read. + * + * @return the number of bytes read, which may range from 0 to cnt. + */ + size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt); + + /** + * Advance the read pointer. + * + * After data have been read from the ringbuffer using the pointers + * returned by jack_ringbuffer_get_read_vector(), use this function to + * advance the buffer pointers, making that space available for future + * write operations. + * + * @param rb a pointer to the ringbuffer structure. + * @param cnt the number of bytes read. + */ + void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt); + + /** + * Return the number of bytes available for reading. + * + * @param rb a pointer to the ringbuffer structure. + * + * @return the number of bytes available to read. + */ + size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb); + + /** + * Lock a ringbuffer data block into memory. + * + * Uses the mlock() system call. This is not a realtime operation. + * + * @param rb a pointer to the ringbuffer structure. + */ + int jack_ringbuffer_mlock(jack_ringbuffer_t *rb); + + /** + * Reset the read and write pointers, making an empty buffer. + * + * This is not thread safe. + * + * @param rb a pointer to the ringbuffer structure. + */ + void jack_ringbuffer_reset(jack_ringbuffer_t *rb); + + /** + * Write data into the ringbuffer. + * + * @param rb a pointer to the ringbuffer structure. + * @param src a pointer to the data to be written to the ringbuffer. + * @param cnt the number of bytes to write. + * + * @return the number of bytes write, which may range from 0 to cnt + */ + size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src, + size_t cnt); + + /** + * Advance the write pointer. + * + * After data have been written the ringbuffer using the pointers + * returned by jack_ringbuffer_get_write_vector(), use this function + * to advance the buffer pointer, making the data available for future + * read operations. + * + * @param rb a pointer to the ringbuffer structure. + * @param cnt the number of bytes written. + */ + void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt); + + /** + * Return the number of bytes available for writing. + * + * @param rb a pointer to the ringbuffer structure. + * + * @return the amount of free space (in bytes) available for writing. + */ + size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/common/shm.c b/common/shm.c new file mode 100644 index 00000000..1c0ad939 --- /dev/null +++ b/common/shm.c @@ -0,0 +1,1182 @@ +/* This module provides a set of abstract shared memory interfaces + * with support using both System V and POSIX shared memory + * implementations. The code is divided into three sections: + * + * - common (interface-independent) code + * - POSIX implementation + * - System V implementation + * + * The implementation used is determined by whether USE_POSIX_SHM was + * set in the ./configure step. + */ + +/* + * Copyright (C) 2003 Paul Davis + * Copyright (C) 2004 Jack O'Quin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: shm.c,v 1.8.2.16 2006/08/28 10:10:16 letz Exp $ + */ + +#ifdef WIN32 +#include <process.h> +#else + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <limits.h> +#include <errno.h> +#include <dirent.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/shm.h> +#include <sys/sem.h> +#include <stdlib.h> + +#endif + +#include "shm.h" +#include "JackError.h" + +#ifdef USE_POSIX_SHM +static jack_shmtype_t jack_shmtype = shm_POSIX; +#elif WIN32 +static jack_shmtype_t jack_shmtype = shm_WIN32; +#else +static jack_shmtype_t jack_shmtype = shm_SYSV; +#endif + +/* interface-dependent forward declarations */ +static int jack_access_registry (jack_shm_info_t *ri); +static int jack_create_registry (jack_shm_info_t *ri); +static void jack_remove_shm (jack_shm_id_t *id); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * common interface-independent section + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* The JACK SHM registry is a chunk of memory for keeping track of the + * shared memory used by each active JACK server. This allows the + * server to clean up shared memory when it exits. To avoid memory + * leakage due to kill -9, crashes or debugger-driven exits, this + * cleanup is also done when a new instance of that server starts. + */ + +/* per-process global data for the SHM interfaces */ +static jack_shm_id_t registry_id; /* SHM id for the registry */ + +#ifdef WIN32 +static jack_shm_info_t registry_info = {/* SHM info for the registry */ + JACK_SHM_NULL_INDEX, + NULL +}; +#else +static jack_shm_info_t registry_info = { /* SHM info for the registry */ + .index = JACK_SHM_NULL_INDEX, + .attached_at = MAP_FAILED +}; + +#endif + +/* pointers to registry header and array */ +static jack_shm_header_t *jack_shm_header = NULL; +static jack_shm_registry_t *jack_shm_registry = NULL; +static char jack_shm_server_prefix[JACK_SERVER_NAME_SIZE] = ""; + +/* jack_shm_lock_registry() serializes updates to the shared memory + * segment JACK uses to keep track of the SHM segements allocated to + * all its processes, including multiple servers. + * + * This is not a high-contention lock, but it does need to work across + * multiple processes. High transaction rates and realtime safety are + * not required. Any solution needs to at least be portable to POSIX + * and POSIX-like systems. + * + * We must be particularly careful to ensure that the lock be released + * if the owning process terminates abnormally. Otherwise, a segfault + * or kill -9 at the wrong moment could prevent JACK from ever running + * again on that machine until after a reboot. + */ + +#define JACK_SEMAPHORE_KEY 0x282929 +#ifndef USE_POSIX_SHM +#define JACK_SHM_REGISTRY_KEY JACK_SEMAPHORE_KEY +#endif + +static int semid = -1; + +#ifdef WIN32 + +// steph TODO +static void +semaphore_init () {} + +static void +semaphore_add (int value) {} + +#else +/* all semaphore errors are fatal -- issue message, but do not return */ +static void +semaphore_error (char *msg) +{ + jack_error ("Fatal JACK semaphore error: %s (%s)", + msg, strerror (errno)); + abort (); +} + +static void +semaphore_init () +{ + key_t semkey = JACK_SEMAPHORE_KEY; + struct sembuf sbuf; + int create_flags = IPC_CREAT | IPC_EXCL + | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + + /* Get semaphore ID associated with this key. */ + if ((semid = semget(semkey, 0, 0)) == -1) { + + /* Semaphore does not exist - Create. */ + if ((semid = semget(semkey, 1, create_flags)) != -1) { + + /* Initialize the semaphore, allow one owner. */ + sbuf.sem_num = 0; + sbuf.sem_op = 1; + sbuf.sem_flg = 0; + if (semop(semid, &sbuf, 1) == -1) { + semaphore_error ("semop"); + } + + } else if (errno == EEXIST) { + if ((semid = semget(semkey, 0, 0)) == -1) { + semaphore_error ("semget"); + } + + } else { + semaphore_error ("semget creation"); + } + } +} + +static inline void +semaphore_add (int value) +{ + struct sembuf sbuf; + + sbuf.sem_num = 0; + sbuf.sem_op = value; + sbuf.sem_flg = SEM_UNDO; + if (semop(semid, &sbuf, 1) == -1) { + semaphore_error ("semop"); + } +} + +#endif + +static void +jack_shm_lock_registry (void) +{ + if (semid == -1) + semaphore_init (); + + semaphore_add (-1); +} + +static void +jack_shm_unlock_registry (void) +{ + semaphore_add (1); +} + +static void +jack_shm_init_registry () +{ + /* registry must be locked */ + int i; + + memset (jack_shm_header, 0, JACK_SHM_REGISTRY_SIZE); + + jack_shm_header->magic = JACK_SHM_MAGIC; + //jack_shm_header->protocol = jack_protocol_version; // steph + jack_shm_header->type = jack_shmtype; + jack_shm_header->size = JACK_SHM_REGISTRY_SIZE; + jack_shm_header->hdr_len = sizeof (jack_shm_header_t); + jack_shm_header->entry_len = sizeof (jack_shm_registry_t); + + for (i = 0; i < MAX_SHM_ID; ++i) { + jack_shm_registry[i].index = i; + } +} + +static int +jack_shm_validate_registry () +{ + /* registry must be locked */ + + if ((jack_shm_header->magic == JACK_SHM_MAGIC) + //&& (jack_shm_header->protocol == jack_protocol_version) // steph + && (jack_shm_header->type == jack_shmtype) + && (jack_shm_header->size == JACK_SHM_REGISTRY_SIZE) + && (jack_shm_header->hdr_len == sizeof (jack_shm_header_t)) + && (jack_shm_header->entry_len == sizeof (jack_shm_registry_t))) { + + return 0; /* registry OK */ + } + + return -1; +} + +/* set a unique per-user, per-server shm prefix string + * + * According to the POSIX standard: + * + * "The name argument conforms to the construction rules for a + * pathname. If name begins with the slash character, then processes + * calling shm_open() with the same value of name refer to the same + * shared memory object, as long as that name has not been + * removed. If name does not begin with the slash character, the + * effect is implementation-defined. The interpretation of slash + * characters other than the leading slash character in name is + * implementation-defined." + * + * Since the Linux implementation does not allow slashes *within* the + * name, in the interest of portability we use colons instead. + */ +static void +jack_set_server_prefix (const char *server_name) +{ +#ifdef WIN32 + snprintf (jack_shm_server_prefix, sizeof (jack_shm_server_prefix), + "jack-%d:%s:", _getpid (), server_name); // steph TO CHECK +#else + snprintf (jack_shm_server_prefix, sizeof (jack_shm_server_prefix), + "/jack-%d:%s:", getuid (), server_name); +#endif + +} + +/* gain server addressability to shared memory registration segment + * + * returns: 0 if successful + */ +static int +jack_server_initialize_shm (void) +{ + int rc; + + if (jack_shm_header) + return 0; /* already initialized */ + + jack_shm_lock_registry (); + + rc = jack_access_registry (®istry_info); + + switch (rc) { + case ENOENT: /* registry does not exist */ + rc = jack_create_registry (®istry_info); + break; + case 0: /* existing registry */ + if (jack_shm_validate_registry () == 0) + break; + /* else it was invalid, so fall through */ + case EINVAL: /* bad registry */ + /* Apparently, this registry was created by an older + * JACK version. Delete it so we can try again. */ + jack_release_shm (®istry_info); + jack_remove_shm (®istry_id); + if ((rc = jack_create_registry (®istry_info)) != 0) { + jack_error ("incompatible shm registry (%s)", + strerror (errno)); +#ifndef USE_POSIX_SHM + jack_error ("to delete, use `ipcrm -M 0x%0.8x'", + JACK_SHM_REGISTRY_KEY); +#endif + } + break; + default: /* failure return code */ + break; + } + + jack_shm_unlock_registry (); + return rc; +} + +/* gain client addressability to shared memory registration segment + * + * NOTE: this function is no longer used for server initialization, + * instead it calls jack_register_server(). + * + * returns: 0 if successful + */ +int +jack_initialize_shm (const char *server_name) +{ + int rc; + + if (jack_shm_header) + return 0; /* already initialized */ + + jack_set_server_prefix (server_name); + + jack_shm_lock_registry (); + if ((rc = jack_access_registry (®istry_info)) == 0) { + if ((rc = jack_shm_validate_registry ()) != 0) { + jack_error ("Incompatible shm registry, " + "are jackd and libjack in sync?"); + } + } + jack_shm_unlock_registry (); + + return rc; +} + + +// steph + +int jack_initialize_shm_server() +{ + return jack_initialize_shm("default"); +} + +int jack_initialize_shm_client() +{ + return jack_initialize_shm("default"); +} + +char* jack_shm_addr (jack_shm_info_t* si) +{ + return (char*)si->attached_at; +} + +void +jack_destroy_shm (jack_shm_info_t* si) +{ + /* must NOT have the registry locked */ + if (si->index == JACK_SHM_NULL_INDEX) + return; /* segment not allocated */ + + jack_remove_shm (&jack_shm_registry[si->index].id); + jack_release_shm_info (si->index); +} + +jack_shm_registry_t * +jack_get_free_shm_info () +{ + /* registry must be locked */ + jack_shm_registry_t* si = NULL; + int i; + + for (i = 0; i < MAX_SHM_ID; ++i) { + if (jack_shm_registry[i].size == 0) { + break; + } + } + + if (i < MAX_SHM_ID) { + si = &jack_shm_registry[i]; + } + + return si; +} + +static void +jack_release_shm_entry (jack_shm_registry_index_t index) +{ + /* the registry must be locked */ + jack_shm_registry[index].size = 0; + jack_shm_registry[index].allocator = 0; + memset (&jack_shm_registry[index].id, 0, + sizeof (jack_shm_registry[index].id)); +} + +void +jack_release_shm_info (jack_shm_registry_index_t index) +{ +#ifdef WIN32 + int my_pid = _getpid(); +#else + pid_t my_pid = getpid(); +#endif + + /* must NOT have the registry locked */ + if (jack_shm_registry[index].allocator == my_pid) { + jack_shm_lock_registry (); + jack_release_shm_entry (index); + jack_shm_unlock_registry (); + } +} + +/* Claim server_name for this process. + * + * returns 0 if successful + * EEXIST if server_name was already active for this user + * ENOSPC if server registration limit reached + * ENOMEM if unable to access shared memory registry + */ +EXPORT int +jack_register_server (const char *server_name) +{ + int i; + +#ifdef WIN32 + int my_pid = _getpid(); +#else + pid_t my_pid = getpid(); +#endif + + jack_set_server_prefix (server_name); + + if (jack_server_initialize_shm ()) + return ENOMEM; + + jack_shm_lock_registry (); + + /* See if server_name already registered. Since server names + * are per-user, we register the unique server prefix string. + */ + for (i = 0; i < MAX_SERVERS; i++) { + + if (strncmp (jack_shm_header->server[i].name, + jack_shm_server_prefix, + JACK_SERVER_NAME_SIZE) != 0) + continue; /* no match */ + + if (jack_shm_header->server[i].pid == my_pid) + return 0; /* it's me */ + + /* see if server still exists */ + #ifndef WIN32 // steph TO CHECK + if (kill (jack_shm_header->server[i].pid, 0) == 0) { + return EEXIST; /* other server running */ + } + #endif + + /* it's gone, reclaim this entry */ + memset (&jack_shm_header->server[i], 0, + sizeof (jack_shm_server_t)); + } + + /* find a free entry */ + for (i = 0; i < MAX_SERVERS; i++) { + if (jack_shm_header->server[i].pid == 0) + break; + } + + if (i >= MAX_SERVERS) + return ENOSPC; /* out of space */ + + /* claim it */ + jack_shm_header->server[i].pid = my_pid; + strncpy (jack_shm_header->server[i].name, + jack_shm_server_prefix, + JACK_SERVER_NAME_SIZE); + + jack_shm_unlock_registry (); + return 0; +} + +/* release server_name registration */ +EXPORT void +jack_unregister_server (const char *server_name /* unused */) +{ + int i; + +#ifdef WIN32 + int my_pid = _getpid(); +#else + pid_t my_pid = getpid(); +#endif + + jack_shm_lock_registry (); + + for (i = 0; i < MAX_SERVERS; i++) { + if (jack_shm_header->server[i].pid == my_pid) { + memset (&jack_shm_header->server[i], 0, + sizeof (jack_shm_server_t)); + } + } + + jack_shm_unlock_registry (); +} + +/* called for server startup and termination */ +EXPORT int +jack_cleanup_shm () +{ + int i; + int destroy; + jack_shm_info_t copy; + +#ifdef WIN32 + int my_pid = _getpid(); +#else + pid_t my_pid = getpid(); +#endif + + jack_shm_lock_registry (); + + for (i = 0; i < MAX_SHM_ID; i++) { + jack_shm_registry_t* r; + + r = &jack_shm_registry[i]; + memcpy (©, r, sizeof (jack_shm_info_t)); + destroy = FALSE; + + /* ignore unused entries */ + if (r->allocator == 0) + continue; + + /* is this my shm segment? */ + if (r->allocator == my_pid) { + + /* allocated by this process, so unattach + and destroy. */ + jack_release_shm (©); + destroy = TRUE; + + } else { + + /* see if allocator still exists */ + #ifdef WIN32 // steph + printf("TODO: kill API not available !!\n"); + #else + if (kill (r->allocator, 0)) { + if (errno == ESRCH) { + /* allocator no longer exists, + * so destroy */ + destroy = TRUE; + } + } + #endif + } + + if (destroy) { + + int index = copy.index; + + if ((index >= 0) && (index < MAX_SHM_ID)) { + jack_remove_shm (&jack_shm_registry[index].id); + jack_release_shm_entry (index); + } + r->size = 0; + r->allocator = 0; + } + } + + jack_shm_unlock_registry (); + return TRUE; +} + +/* resize a shared memory segment + * + * There is no way to resize a System V shm segment. Resizing is + * possible with POSIX shm, but not with the non-conformant Mac OS X + * implementation. Since POSIX shm is mainly used on that platform, + * it's simpler to treat them both the same. + * + * So, we always resize by deleting and reallocating. This is + * tricky, because the old segment will not disappear until + * all the clients have released it. We only do what we can + * from here. + * + * This is not done under a single lock. I don't even want to think + * about all the things that could possibly go wrong if multple + * processes tried to resize the same segment concurrently. That + * probably doesn't happen. + */ +int +jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size) +{ + jack_shm_id_t id; + + /* The underlying type of `id' differs for SYSV and POSIX */ + memcpy (&id, &jack_shm_registry[si->index].id, sizeof (id)); + + jack_release_shm (si); + jack_destroy_shm (si); + + if (jack_shmalloc ((char *) id, size, si)) { + return -1; + } + + return jack_attach_shm (si); +} + +#ifdef USE_POSIX_SHM + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * POSIX interface-dependent functions + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* gain addressability to existing SHM registry segment + * + * sets up global registry pointers, if successful + * + * returns: 0 if existing registry accessed successfully + * ENOENT if registry does not exist + * EINVAL if registry exists, but has the wrong size + */ +static int +jack_access_registry (jack_shm_info_t *ri) +{ + /* registry must be locked */ + int shm_fd; + + strncpy (registry_id, "/jack-shm-registry", sizeof (registry_id)); + + /* try to open an existing segment */ + if ((shm_fd = shm_open (registry_id, O_RDWR, 0666)) < 0) { + int rc = errno; + if (errno != ENOENT) { + jack_error ("Cannot open existing shm registry segment" + " (%s)", strerror (errno)); + } + close (shm_fd); + return rc; + } + + if ((ri->attached_at = mmap (0, JACK_SHM_REGISTRY_SIZE, + PROT_READ|PROT_WRITE, + MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { + jack_error ("Cannot mmap shm registry segment (%s)", + strerror (errno)); + close (shm_fd); + return EINVAL; + } + + /* set up global pointers */ + ri->index = JACK_SHM_REGISTRY_INDEX; + jack_shm_header = ri->attached_at; + jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); + + close (shm_fd); // steph + return 0; +} + +/* create a new SHM registry segment + * + * sets up global registry pointers, if successful + * + * returns: 0 if registry created successfully + * nonzero error code if unable to allocate a new registry + */ +static int +jack_create_registry (jack_shm_info_t *ri) +{ + /* registry must be locked */ + int shm_fd; + + strncpy (registry_id, "/jack-shm-registry", sizeof (registry_id)); + + if ((shm_fd = shm_open (registry_id, O_RDWR|O_CREAT, 0666)) < 0) { + int rc = errno; + jack_error ("Cannot create shm registry segment (%s)", + strerror (errno)); + return rc; + } + + /* Set the desired segment size. NOTE: the non-conformant Mac + * OS X POSIX shm only allows ftruncate() on segment creation. + */ + if (ftruncate (shm_fd, JACK_SHM_REGISTRY_SIZE) < 0) { + int rc = errno; + jack_error ("Cannot set registry size (%s)", strerror (errno)); + jack_remove_shm (®istry_id); + close (shm_fd); + return rc; + } + + if ((ri->attached_at = mmap (0, JACK_SHM_REGISTRY_SIZE, + PROT_READ|PROT_WRITE, + MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { + jack_error ("Cannot mmap shm registry segment (%s)", + strerror (errno)); + jack_remove_shm (®istry_id); + close (shm_fd); + return EINVAL; + } + + /* set up global pointers */ + ri->index = JACK_SHM_REGISTRY_INDEX; + jack_shm_header = ri->attached_at; + jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); + + /* initialize registry contents */ + jack_shm_init_registry (); + close (shm_fd); // steph + return 0; +} + +static void +jack_remove_shm (jack_shm_id_t *id) +{ + /* registry may or may not be locked */ + shm_unlink ((char *) id); +} + +void +jack_release_shm (jack_shm_info_t* si) +{ + /* registry may or may not be locked */ + if (si->attached_at != MAP_FAILED) { + munmap (si->attached_at, jack_shm_registry[si->index].size); + } +} + +/* allocate a POSIX shared memory segment */ +int +jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si) +{ + jack_shm_registry_t* registry; + int shm_fd; + int rc = -1; + char name[SHM_NAME_MAX+1]; + + jack_shm_lock_registry (); + + if ((registry = jack_get_free_shm_info ()) == NULL) { + jack_error ("shm registry full"); + goto unlock; + } + + /* On Mac OS X, the maximum length of a shared memory segment + * name is SHM_NAME_MAX (instead of NAME_MAX or PATH_MAX as + * defined by the standard). Unfortunately, Apple sets this + * value so small (about 31 bytes) that it is useless for + * actual names. So, we construct a short name from the + * registry index for uniqueness and ignore the shm_name + * parameter. Bah! + */ + snprintf (name, sizeof (name), "/jack-%d", registry->index); + + if (strlen (name) >= sizeof (registry->id)) { + jack_error ("shm segment name too long %s", name); + goto unlock; + } + + if ((shm_fd = shm_open (name, O_RDWR|O_CREAT, 0666)) < 0) { + jack_error ("Cannot create shm segment %s (%s)", + name, strerror (errno)); + goto unlock; + } + + if (ftruncate (shm_fd, size) < 0) { + jack_error ("Cannot set size of engine shm " + "registry 0 (%s)", + strerror (errno)); + close (shm_fd); + goto unlock; + } + + close (shm_fd); + registry->size = size; + strncpy (registry->id, name, sizeof (registry->id)); + registry->allocator = getpid(); + si->index = registry->index; + si->attached_at = MAP_FAILED; /* not attached */ + rc = 0; /* success */ + + unlock: + jack_shm_unlock_registry (); + return rc; +} + +int +jack_attach_shm (jack_shm_info_t* si) +{ + int shm_fd; + jack_shm_registry_t *registry = &jack_shm_registry[si->index]; + + if ((shm_fd = shm_open (registry->id, + O_RDWR, 0666)) < 0) { + jack_error ("Cannot open shm segment %s (%s)", registry->id, + strerror (errno)); + return -1; + } + + if ((si->attached_at = mmap (0, registry->size, PROT_READ|PROT_WRITE, + MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { + jack_error ("Cannot mmap shm segment %s (%s)", + registry->id, + strerror (errno)); + close (shm_fd); + return -1; + } + + close (shm_fd); + return 0; +} + +int +jack_attach_shm_read (jack_shm_info_t* si) +{ + int shm_fd; + jack_shm_registry_t *registry = &jack_shm_registry[si->index]; + + if ((shm_fd = shm_open (registry->id, + O_RDONLY, 0666)) < 0) { + jack_error ("Cannot open shm segment %s (%s)", registry->id, + strerror (errno)); + return -1; + } + + if ((si->attached_at = mmap (0, registry->size, PROT_READ, + MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { + jack_error ("Cannot mmap shm segment %s (%s)", + registry->id, + strerror (errno)); + close (shm_fd); + return -1; + } + + close (shm_fd); + return 0; +} + +#elif WIN32 + +static int +jack_access_registry (jack_shm_info_t *ri) +{ + /* registry must be locked */ + HANDLE shm_fd; + LPSECURITY_ATTRIBUTES sec = 0; + + strncpy (registry_id, "jack-shm-registry", sizeof (registry_id)); + + /* try to open an existing segment */ + + if ((shm_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, registry_id)) == NULL) { + int rc = GetLastError(); + if (rc != ERROR_FILE_NOT_FOUND) { + jack_error ("Cannot open existing shm registry segment (%ld)", rc); + } + return rc; + } + + if ((ri->attached_at = MapViewOfFile (shm_fd, FILE_MAP_ALL_ACCESS, 0, 0, JACK_SHM_REGISTRY_SIZE)) == NULL) { + jack_error ("Cannot mmap shm registry segment (%ld)", GetLastError()); + jack_remove_shm (®istry_id); + CloseHandle (shm_fd); + return EINVAL; + } + + /* set up global pointers */ + ri->index = JACK_SHM_REGISTRY_INDEX; + jack_shm_header = ri->attached_at; + jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); + + //CloseHandle(shm_fd); // TO CHECK + return 0; +} + +static int +jack_create_registry (jack_shm_info_t *ri) +{ + /* registry must be locked */ + HANDLE shm_fd; + + strncpy (registry_id, "jack-shm-registry", sizeof (registry_id)); + + if ((shm_fd = CreateFileMapping(INVALID_HANDLE_VALUE, + 0, PAGE_READWRITE, + 0, JACK_SHM_REGISTRY_SIZE, + registry_id)) == NULL || (shm_fd == INVALID_HANDLE_VALUE)) { + int rc = GetLastError(); + jack_error ("Cannot create shm registry segment (%ld)", rc); + return rc; + } + + if ((ri->attached_at = MapViewOfFile (shm_fd, FILE_MAP_ALL_ACCESS, 0, 0, JACK_SHM_REGISTRY_SIZE)) == NULL) { + jack_error ("Cannot mmap shm registry segment (%ld)", GetLastError()); + jack_remove_shm (®istry_id); + CloseHandle (shm_fd); + return EINVAL; + } + + /* set up global pointers */ + ri->index = JACK_SHM_REGISTRY_INDEX; + jack_shm_header = ri->attached_at; + jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); + + /* initialize registry contents */ + jack_shm_init_registry (); + + //CloseHandle(shm_fd); // TO CHECK + return 0; +} + +static void +jack_remove_shm (jack_shm_id_t *id) +{ + /* nothing to do */ +} + +void +jack_release_shm (jack_shm_info_t* si) +{ + /* registry may or may not be locked */ + if (si->attached_at != NULL) { + UnmapViewOfFile (si->attached_at); + } +} + +int +jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si) +{ + jack_shm_registry_t* registry; + HANDLE shm_fd; + int rc = -1; + char name[SHM_NAME_MAX+1]; + + jack_shm_lock_registry (); + + if ((registry = jack_get_free_shm_info ()) == NULL) { + jack_error ("shm registry full"); + goto unlock; + } + + snprintf (name, sizeof (name), "jack-%d", registry->index); + + if (strlen (name) >= sizeof (registry->id)) { + jack_error ("shm segment name too long %s", name); + goto unlock; + } + + if ((shm_fd = CreateFileMapping(INVALID_HANDLE_VALUE, + 0, PAGE_READWRITE, + 0, size, + name)) == NULL || (shm_fd == INVALID_HANDLE_VALUE)) { + int rc = GetLastError(); + jack_error ("Cannot create shm segment (%ld)",rc); + goto unlock; + } + + //CloseHandle (shm_fd); // TO CHECK + + registry->size = size; + strncpy (registry->id, name, sizeof (registry->id)); + registry->allocator = _getpid(); + si->index = registry->index; + si->attached_at = NULL; /* not attached */ + rc = 0; /* success */ + + unlock: + jack_shm_unlock_registry (); + return rc; +} + +int +jack_attach_shm (jack_shm_info_t* si) +{ + HANDLE shm_fd; + jack_shm_registry_t *registry = &jack_shm_registry[si->index]; + + if ((shm_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, registry->id)) == NULL) { + int rc = GetLastError(); + jack_error ("Cannot open shm segment (%ld)",rc); + return -1; + } + + if ((si->attached_at = MapViewOfFile (shm_fd, FILE_MAP_ALL_ACCESS, 0, 0, registry->size)) == NULL) { + jack_error ("Cannot mmap shm segment (%ld)", GetLastError()); + jack_remove_shm (®istry_id); + CloseHandle (shm_fd); + return -1; + } + + //CloseHandle (shm_fd); // TO CHECK + return 0; +} + +int +jack_attach_shm_read (jack_shm_info_t* si) +{ + HANDLE shm_fd; + jack_shm_registry_t *registry = &jack_shm_registry[si->index]; + + if ((shm_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, registry->id)) == NULL) { + int rc = GetLastError(); + jack_error ("Cannot open shm segment (%ld)",rc); + return -1; + } + + if ((si->attached_at = MapViewOfFile (shm_fd, FILE_MAP_READ, 0, 0, registry->size)) == NULL) { + jack_error ("Cannot mmap shm segment (%ld)", GetLastError()); + jack_remove_shm (®istry_id); + CloseHandle (shm_fd); + return -1; + } + + //CloseHandle (shm_fd); // TO CHECK + return 0; +} + +#else + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * System V interface-dependent functions + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* gain addressability to existing SHM registry segment + * + * sets up global registry pointers, if successful + * + * returns: 0 if existing registry accessed successfully + * ENOENT if registry does not exist + * EINVAL if registry exists, but has the wrong size + * other nonzero error code if unable to access registry + */ +static int +jack_access_registry (jack_shm_info_t *ri) +{ + /* registry must be locked */ + + /* try without IPC_CREAT to get existing segment */ + if ((registry_id = shmget (JACK_SHM_REGISTRY_KEY, + JACK_SHM_REGISTRY_SIZE, 0666)) < 0) { + + switch (errno) { + + case ENOENT: /* segment does not exist */ + return ENOENT; + + case EINVAL: /* segment exists, but too small */ + /* attempt minimum size access */ + registry_id = shmget (JACK_SHM_REGISTRY_KEY, 1, 0666); + return EINVAL; + + default: /* or other error */ + jack_error ("unable to access shm registry (%s)", + strerror (errno)); + return errno; + } + } + + if ((ri->attached_at = shmat (registry_id, 0, 0)) < 0) { + jack_error ("Cannot attach shm registry segment (%s)", + strerror (errno)); + return EINVAL; + } + + /* set up global pointers */ + ri->index = JACK_SHM_REGISTRY_INDEX; + jack_shm_header = ri->attached_at; + jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); + return 0; +} + +/* create a new SHM registry segment + * + * sets up global registry pointers, if successful + * + * returns: 0 if registry created successfully + * nonzero error code if unable to allocate a new registry + */ +static int +jack_create_registry (jack_shm_info_t *ri) +{ + /* registry must be locked */ + if ((registry_id = shmget (JACK_SHM_REGISTRY_KEY, + JACK_SHM_REGISTRY_SIZE, + 0666|IPC_CREAT)) < 0) { + jack_error ("Cannot create shm registry segment (%s)", + strerror (errno)); + return errno; + } + + if ((ri->attached_at = shmat (registry_id, 0, 0)) < 0) { + jack_error ("Cannot attach shm registry segment (%s)", + strerror (errno)); + return EINVAL; + } + + /* set up global pointers */ + ri->index = JACK_SHM_REGISTRY_INDEX; + jack_shm_header = ri->attached_at; + jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); + + /* initialize registry contents */ + jack_shm_init_registry (); + return 0; +} + +static void +jack_remove_shm (jack_shm_id_t *id) +{ + /* registry may or may not be locked */ + shmctl (*id, IPC_RMID, NULL); +} + +void +jack_release_shm (jack_shm_info_t* si) +{ + /* registry may or may not be locked */ + if (si->attached_at != MAP_FAILED) { + shmdt (si->attached_at); + } +} + +int +jack_shmalloc (const char* name_not_used, jack_shmsize_t size, + jack_shm_info_t* si) +{ + int shmflags; + int shmid; + int rc = -1; + jack_shm_registry_t* registry; + + jack_shm_lock_registry (); + + if ((registry = jack_get_free_shm_info ())) { + + shmflags = 0666 | IPC_CREAT | IPC_EXCL; + + if ((shmid = shmget (IPC_PRIVATE, size, shmflags)) >= 0) { + + registry->size = size; + registry->id = shmid; + registry->allocator = getpid(); + si->index = registry->index; + si->attached_at = MAP_FAILED; /* not attached */ + rc = 0; + + } else { + jack_error ("Cannot create shm segment %s (%s)", + name_not_used, strerror (errno)); + } + } + + jack_shm_unlock_registry (); + return rc; +} + +int +jack_attach_shm (jack_shm_info_t* si) +{ + if ((si->attached_at = shmat (jack_shm_registry[si->index].id, 0, 0)) < 0) { + jack_error ("Cannot attach shm segment (%s)", + strerror (errno)); + jack_release_shm_info (si->index); + return -1; + } + return 0; +} + +#endif /* !USE_POSIX_SHM */ diff --git a/common/shm.h b/common/shm.h new file mode 100644 index 00000000..2ef29fc4 --- /dev/null +++ b/common/shm.h @@ -0,0 +1,156 @@ +#ifndef __jack_shm_h__ +#define __jack_shm_h__ + +#include <limits.h> +#include <sys/types.h> +#include "types.h" +#include "JackExports.h" + + +#define TRUE 1 +#define FALSE 0 + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MAX_SERVERS 8 /* maximum concurrent servers */ +#define MAX_SHM_ID 256 /* generally about 16 per server */ +#define JACK_SERVER_NAME_SIZE 256 /* maximum length of server name */ +#define JACK_SHM_MAGIC 0x4a41434b /* shm magic number: "JACK" */ +#define JACK_SHM_NULL_INDEX -1 /* NULL SHM index */ +#define JACK_SHM_REGISTRY_INDEX -2 /* pseudo SHM index for registry */ + + + /* On Mac OS X, SHM_NAME_MAX is the maximum length of a shared memory + * segment name (instead of NAME_MAX or PATH_MAX as defined by the + * standard). + */ +#ifdef USE_POSIX_SHM + +#ifndef SHM_NAME_MAX +#define SHM_NAME_MAX NAME_MAX +#endif + typedef char shm_name_t[SHM_NAME_MAX]; + typedef shm_name_t jack_shm_id_t; + +#elif WIN32 // steph TO CHECK +#define NAME_MAX 255 +#ifndef SHM_NAME_MAX +#define SHM_NAME_MAX NAME_MAX +#endif + typedef char shm_name_t[SHM_NAME_MAX]; + typedef shm_name_t jack_shm_id_t; + +#else + /* System V SHM */ + typedef int jack_shm_id_t; +#endif /* SHM type */ + + /* shared memory type */ + typedef enum { + shm_POSIX = 1, /* POSIX shared memory */ + shm_SYSV = 2, /* System V shared memory */ + shm_WIN32 = 3 /* Windows 32 shared memory */ + } jack_shmtype_t; + + typedef int16_t jack_shm_registry_index_t; + + /** + * A structure holding information about shared memory allocated by + * JACK. this persists across invocations of JACK, and can be used by + * multiple JACK servers. It contains no pointers and is valid across + * address spaces. + * + * The registry consists of two parts: a header including an array of + * server names, followed by an array of segment registry entries. + */ + typedef struct _jack_shm_server { +#ifdef WIN32 + int pid; /* process ID */ +#else + + pid_t pid; /* process ID */ +#endif + + char name[JACK_SERVER_NAME_SIZE]; + } + jack_shm_server_t; + + typedef struct _jack_shm_header { + uint32_t magic; /* magic number */ + uint16_t protocol; /* JACK protocol version */ + jack_shmtype_t type; /* shm type */ + jack_shmsize_t size; /* total registry segment size */ + jack_shmsize_t hdr_len; /* size of header */ + jack_shmsize_t entry_len; /* size of registry entry */ + jack_shm_server_t server[MAX_SERVERS]; /* current server array */ + } + jack_shm_header_t; + + typedef struct _jack_shm_registry { + jack_shm_registry_index_t index; /* offset into the registry */ + +#ifdef WIN32 + + int allocator; /* PID that created shm segment */ +#else + + pid_t allocator; /* PID that created shm segment */ +#endif + + jack_shmsize_t size; /* for POSIX unattach */ + jack_shm_id_t id; /* API specific, see above */ + } + jack_shm_registry_t; + +#define JACK_SHM_REGISTRY_SIZE (sizeof (jack_shm_header_t) \ + + sizeof (jack_shm_registry_t) * MAX_SHM_ID) + + /** + * a structure holding information about shared memory + * allocated by JACK. this version is valid only + * for a given address space. It contains a pointer + * indicating where the shared memory has been + * attached to the address space. + */ + typedef struct _jack_shm_info { + jack_shm_registry_index_t index; /* offset into the registry */ + void *attached_at; /* address where attached */ + } + jack_shm_info_t; + + /* utility functions used only within JACK */ + + extern void jack_shm_copy_from_registry (jack_shm_info_t*, + jack_shm_registry_index_t); + extern void jack_shm_copy_to_registry (jack_shm_info_t*, + jack_shm_registry_index_t*); + extern void jack_release_shm_info (jack_shm_registry_index_t); + + extern char* jack_shm_addr (jack_shm_info_t* si); // steph + + /* here beginneth the API */ + + EXPORT extern int jack_register_server (const char *server_name); + EXPORT extern void jack_unregister_server (const char *server_name); + + extern int jack_initialize_shm (const char *server_name); + extern int jack_initialize_shm_server (void); // steph + extern int jack_initialize_shm_client (void); // steph + EXPORT extern int jack_cleanup_shm (void); + + extern int jack_shmalloc (const char *shm_name, jack_shmsize_t size, + jack_shm_info_t* result); + extern void jack_release_shm (jack_shm_info_t*); + extern void jack_destroy_shm (jack_shm_info_t*); + extern int jack_attach_shm (jack_shm_info_t*); + extern int jack_attach_shm_read (jack_shm_info_t*); // steph + extern int jack_resize_shm (jack_shm_info_t*, jack_shmsize_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_shm_h__ */ diff --git a/common/statistics.h b/common/statistics.h new file mode 100644 index 00000000..23ea4b66 --- /dev/null +++ b/common/statistics.h @@ -0,0 +1,59 @@ +/* +* Copyright (C) 2004 Rui Nuno Capela, Lee Revell +* +* 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. +* +* $Id: statistics.h,v 1.2.2.3 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __statistics_h__ +#define __statistics_h__ + +#ifdef __cplusplus +extern "C" +{ +#endif + + //#include <jack/types.h> +#include "types.h" + + /** + * @return the maximum delay reported by the backend since + * startup or reset. When compared to the period size in usecs, this + * can be used to estimate the ideal period size for a given setup. + */ + float jack_get_max_delayed_usecs (jack_client_t *client); + + /** + * @return the delay in microseconds due to the most recent XRUN + * occurrence. This probably only makes sense when called from a @ref + * JackXRunCallback defined using jack_set_xrun_callback(). + */ + float jack_get_xrun_delayed_usecs (jack_client_t *client); + + /** + * Reset the maximum delay counter. This would be useful + * to estimate the effect that a change to the configuration of a running + * system (e.g. toggling kernel preemption) has on the delay + * experienced by JACK, without having to restart the JACK engine. + */ + void jack_reset_max_delayed_usecs (jack_client_t *client); + +#ifdef __cplusplus +} +#endif + +#endif /* __statistics_h__ */ diff --git a/common/thread.h b/common/thread.h new file mode 100644 index 00000000..a7ddd4a7 --- /dev/null +++ b/common/thread.h @@ -0,0 +1,92 @@ +/* + Copyright (C) 2004 Paul Davis + + 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. + + $Id: thread.h,v 1.1.2.2 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __jack_thread_h__ +#define __jack_thread_h__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef WIN32 + #include <windows.h> + typedef HANDLE pthread_t; +#else + #include <pthread.h> +#endif + + + /** @file thread.h + * + * Library functions to standardize thread creation for JACK and its + * clients. These interfaces hide some system variations in the + * handling of realtime scheduling and associated privileges. + */ + + /** + * Attempt to enable realtime scheduling for a thread. On some + * systems that may require special privileges. + * + * @param thread POSIX thread ID. + * @param priority requested thread priority. + * + * @returns 0, if successful; EPERM, if the calling process lacks + * required realtime privileges; otherwise some other error number. + */ + int jack_acquire_real_time_scheduling (pthread_t thread, int priority); + + /** + * Create a thread for JACK or one of its clients. The thread is + * created executing @a start_routine with @a arg as its sole + * argument. + * + * @param client the JACK client for whom the thread is being created. May be + * NULL if the client is being created within the JACK server. + * @param thread place to return POSIX thread ID. + * @param priority thread priority, if realtime. + * @param realtime true for the thread to use realtime scheduling. On + * some systems that may require special privileges. + * @param start_routine function the thread calls when it starts. + * @param arg parameter passed to the @a start_routine. + * + * @returns 0, if successful; otherwise some error number. + */ + int jack_client_create_thread (jack_client_t* client, + pthread_t *thread, + int priority, + int realtime, /* boolean */ + void *(*start_routine)(void*), + void *arg); + + /** + * Drop realtime scheduling for a thread. + * + * @param thread POSIX thread ID. + * + * @returns 0, if successful; otherwise an error number. + */ + int jack_drop_real_time_scheduling (pthread_t thread); + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_thread_h__ */ diff --git a/common/transport.h b/common/transport.h new file mode 100644 index 00000000..877f8a68 --- /dev/null +++ b/common/transport.h @@ -0,0 +1,430 @@ +/* + Copyright (C) 2002 Paul Davis + Copyright (C) 2003 Jack O'Quin + + 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. + + $Id: transport.h,v 1.8.2.5 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __jack_transport_h__ +#define __jack_transport_h__ + +#ifdef __cplusplus +extern "C" +{ +#endif + + //#include <jack/types.h> +#include "types.h" + + /** + * Transport states. + */ + typedef enum { + + /* the order matters for binary compatibility */ + JackTransportStopped = 0, /**< Transport halted */ + JackTransportRolling = 1, /**< Transport playing */ + JackTransportLooping = 2, /**< For OLD_TRANSPORT, now ignored */ + JackTransportStarting = 3, /**< Waiting for sync ready */ + JackTransportSynching = 4 /**< temporary*/ + + } jack_transport_state_t; + + typedef uint64_t jack_unique_t; /**< Unique ID (opaque) */ + + /** + * Optional struct jack_position_t fields. + */ + typedef enum { + + JackPositionBBT = 0x10, /**< Bar, Beat, Tick */ + JackPositionTimecode = 0x20 /**< External timecode */ + + } 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_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; + + 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) */ + + /* 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]; + + /* When (unique_1 == unique_2) the contents are consistent. */ + jack_unique_t unique_2; /**< unique ID */ + + } + jack_position_t; + + /** + * Called by the timebase master to release itself from that + * responsibility. + * + * If the timebase master releases the timebase or leaves the JACK + * graph for any reason, the JACK engine takes over at the start of + * the next process cycle. The transport state does not change. If + * rolling, it continues to play, with frame numbers as the only + * available position information. + * + * @see jack_set_timebase_callback + * + * @param client the JACK client structure. + * + * @return 0 on success, otherwise a non-zero error code. + */ + + int jack_release_timebase (jack_client_t *client); + + /** + * Prototype for the @a sync_callback defined by slow-sync clients. + * When the client is active, this callback is invoked just before + * process() in the same thread. This occurs once after registration, + * then subsequently whenever some client requests a new position, or + * the transport enters the ::JackTransportStarting state. This + * realtime function must not wait. + * + * The transport @a state will be: + * + * - ::JackTransportStopped when a new position is requested; + * - ::JackTransportStarting when the transport is waiting to start; + * - ::JackTransportRolling when the timeout has expired, and the + * position is now a moving target. + * + * @param state current transport state. + * @param pos new transport position. + * @param arg the argument supplied by jack_set_sync_callback(). + * + * @return TRUE (non-zero) when ready to roll. + */ + typedef int (*JackSyncCallback)(jack_transport_state_t state, + jack_position_t *pos, + void *arg); + + /** + * Register (or unregister) as a slow-sync client, one that cannot + * respond immediately to transport position changes. + * + * The @a sync_callback will be invoked at the first available + * opportunity after its registration is complete. If the client is + * currently active this will be the following process cycle, + * otherwise it will be the first cycle after calling jack_activate(). + * After that, it runs according to the ::JackSyncCallback rules. + * Clients that don't set a @a sync_callback are assumed to be ready + * immediately any time the transport wants to start. + * + * @param client the JACK client structure. + * @param sync_callback is a realtime function that returns TRUE when + * the client is ready. Setting @a sync_callback to NULL declares that + * this client no longer requires slow-sync processing. + * @param arg an argument for the @a sync_callback function. + * + * @return 0 on success, otherwise a non-zero error code. + */ + + int jack_set_sync_callback (jack_client_t *client, + JackSyncCallback sync_callback, + void *arg); + /** + * Set the timeout value for slow-sync clients. + * + * This timeout prevents unresponsive slow-sync clients from + * completely halting the transport mechanism. The default is two + * seconds. When the timeout expires, the transport starts rolling, + * even if some slow-sync clients are still unready. The @a + * sync_callbacks of these clients continue being invoked, giving them + * a chance to catch up. + * + * @see jack_set_sync_callback + * + * @param client the JACK client structure. + * @param timeout is delay (in microseconds) before the timeout expires. + * + * @return 0 on success, otherwise a non-zero error code. + */ + + int jack_set_sync_timeout (jack_client_t *client, + jack_time_t timeout); + /** + * Prototype for the @a timebase_callback used to provide extended + * position information. Its output affects all of the following + * process cycle. This realtime function must not wait. + * + * This function is called immediately after process() in the same + * thread whenever the transport is rolling, or when any client has + * requested a new position in the previous cycle. The first cycle + * after jack_set_timebase_callback() is also treated as a new + * position, or the first cycle after jack_activate() if the client + * had been inactive. + * + * The timebase master may not use its @a pos argument to set @a + * pos->frame. To change position, use jack_transport_reposition() or + * jack_transport_locate(). These functions are realtime-safe, the @a + * timebase_callback can call them directly. + * + * @param state current transport state. + * @param nframes number of frames in current period. + * @param pos address of the position structure for the next cycle; @a + * pos->frame will be its frame number. If @a new_pos is FALSE, this + * structure contains extended position information from the current + * cycle. If TRUE, it contains whatever was set by the requester. + * The @a timebase_callback's task is to update the extended + * information here. + * @param new_pos TRUE (non-zero) for a newly requested @a pos, or for + * the first cycle after the @a timebase_callback is defined. + * @param arg the argument supplied by jack_set_timebase_callback(). + */ + typedef void (*JackTimebaseCallback)(jack_transport_state_t state, + jack_nframes_t nframes, + jack_position_t *pos, + int new_pos, + void *arg); + + /** + * Register as timebase master for the JACK subsystem. + * + * The timebase master registers a callback that updates extended + * position information such as beats or timecode whenever necessary. + * Without this extended information, there is no need for this + * function. + * + * There is never more than one master at a time. When a new client + * takes over, the former @a timebase_callback is no longer called. + * Taking over the timebase may be done conditionally, so it fails if + * there was a master already. + * + * @param client the JACK client structure. + * @param conditional non-zero for a conditional request. + * @param timebase_callback is a realtime function that returns + * position information. + * @param arg an argument for the @a timebase_callback function. + * + * @return + * - 0 on success; + * - EBUSY if a conditional request fails because there was already a + * timebase master; + * - other non-zero error code. + */ + + int jack_set_timebase_callback (jack_client_t *client, + int conditional, + JackTimebaseCallback timebase_callback, + void *arg); + /** + * Reposition the transport to a new frame number. + * + * May be called at any time by any client. The new position takes + * effect in two process cycles. If there are slow-sync clients and + * the transport is already rolling, it will enter the + * ::JackTransportStarting state and begin invoking their @a + * sync_callbacks until ready. This function is realtime-safe. + * + * @see jack_transport_reposition, jack_set_sync_callback + * + * @param client the JACK client structure. + * @param frame frame number of new transport position. + * + * @return 0 if valid request, non-zero otherwise. + */ + + int jack_transport_locate (jack_client_t *client, + jack_nframes_t frame); + /** + * Query the current transport state and position. + * + * This function is realtime-safe, and can be called from any thread. + * If called from the process thread, @a pos corresponds to the first + * frame of the current cycle and the state returned is valid for the + * entire cycle. + * + * @param client the JACK client structure. + * @param pos pointer to structure for returning current transport + * position; @a pos->valid will show which fields contain valid data. + * If @a pos is NULL, do not return position information. + * + * @return Current transport state. + */ + + jack_transport_state_t jack_transport_query (const jack_client_t *client, + jack_position_t *pos); + /** + * Return an estimate of the current transport frame, + * including any time elapsed since the last transport + * positional update. + * + * @param client the JACK client structure + */ + + jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client); + /** + * Request a new transport position. + * + * May be called at any time by any client. The new position takes + * effect in two process cycles. If there are slow-sync clients and + * the transport is already rolling, it will enter the + * ::JackTransportStarting state and begin invoking their @a + * sync_callbacks until ready. This function is realtime-safe. + * + * @see jack_transport_locate, jack_set_sync_callback + * + * @param client the JACK client structure. + * @param pos requested new transport position. + * + * @return 0 if valid request, EINVAL if position structure rejected. + */ + + int jack_transport_reposition (jack_client_t *client, + jack_position_t *pos); + /** + * Start the JACK transport rolling. + * + * Any client can make this request at any time. It takes effect no + * sooner than the next process cycle, perhaps later if there are + * slow-sync clients. This function is realtime-safe. + * + * @see jack_set_sync_callback + * + * @param client the JACK client structure. + */ + + void jack_transport_start (jack_client_t *client); + /** + * Stop the JACK transport. + * + * Any client can make this request at any time. It takes effect on + * the next process cycle. This function is realtime-safe. + * + * @param client the JACK client structure. + */ + + void jack_transport_stop (jack_client_t *client); + + /********************************************************************* + * The following interfaces are DEPRECATED. They are only provided + * for compatibility with the earlier JACK transport implementation. + *********************************************************************/ + + /** + * Optional struct jack_transport_info_t fields. + * + * @see jack_position_bits_t. + */ + typedef enum { + + JackTransportState = 0x1, /**< Transport state */ + JackTransportPosition = 0x2, /**< Frame number */ + JackTransportLoop = 0x4, /**< Loop boundaries (ignored) */ + JackTransportSMPTE = 0x8, /**< SMPTE (ignored) */ + JackTransportBBT = 0x10 /**< Bar, Beat, Tick */ + + } jack_transport_bits_t; + + /** + * Deprecated struct for transport position information. + * + * @deprecated This is for compatibility with the earlier transport + * interface. Use the jack_position_t struct, instead. + */ + typedef struct { + + /* these two cannot be set from clients: the server sets them */ + + jack_nframes_t frame_rate; /**< current frame rate (per second) */ + jack_time_t usecs; /**< monotonic, free-rolling */ + + jack_transport_bits_t valid; /**< which fields are legal to read */ + jack_transport_state_t transport_state; + jack_nframes_t frame; + jack_nframes_t loop_start; + jack_nframes_t loop_end; + + long smpte_offset; /**< SMPTE offset (from frame 0) */ + float smpte_frame_rate; /**< 29.97, 30, 24 etc. */ + + int bar; + int beat; + int tick; + double bar_start_tick; + + float beats_per_bar; + float beat_type; + double ticks_per_beat; + double beats_per_minute; + + } + jack_transport_info_t; + + /** + * Gets the current transport info structure (deprecated). + * + * @param client the JACK client structure. + * @param tinfo current transport info structure. The "valid" field + * describes which fields contain valid data. + * + * @deprecated This is for compatibility with the earlier transport + * interface. Use jack_transport_query(), instead. + * + * @pre Must be called from the process thread. + */ + + void jack_get_transport_info (jack_client_t *client, + jack_transport_info_t *tinfo); + /** + * Set the transport info structure (deprecated). + * + * @deprecated This function still exists for compatibility with the + * earlier transport interface, but it does nothing. Instead, define + * a ::JackTimebaseCallback. + */ + + void jack_set_transport_info (jack_client_t *client, + jack_transport_info_t *tinfo); + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_transport_h__ */ diff --git a/common/transport_types.h b/common/transport_types.h new file mode 100644 index 00000000..74aa94ec --- /dev/null +++ b/common/transport_types.h @@ -0,0 +1,223 @@ +/* + Copyright (C) 2002 Paul Davis + Copyright (C) 2003 Jack O'Quin + + 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. + + $Id: transport_types.h,v 1.1.2.2 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __jack_transport_aux_h__ +#define __jack_transport_aux_h__ + +#ifdef __cplusplus +extern "C" +{ +#endif + + //#include <jack/types.h> +#include "types.h" + + /** + * Transport states. + */ + typedef enum { + + /* the order matters for binary compatibility */ + JackTransportStopped = 0, /**< Transport halted */ + JackTransportRolling = 1, /**< Transport playing */ + JackTransportLooping = 2, /**< For OLD_TRANSPORT, now ignored */ + JackTransportStarting = 3, /**< Waiting for sync ready */ + JackTransportSynching = 4 /**< temporary*/ + + } jack_transport_state_t; + + typedef uint64_t jack_unique_t; /**< Unique ID (opaque) */ + + /** + * Optional struct jack_position_t fields. + */ + typedef enum { + + JackPositionBBT = 0x10, /**< Bar, Beat, Tick */ + JackPositionTimecode = 0x20 /**< External timecode */ + + } 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_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; + + 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) */ + + /* 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]; + + /* When (unique_1 == unique_2) the contents are consistent. */ + jack_unique_t unique_2; /**< unique ID */ + + } + jack_position_t; + + /** + * Prototype for the @a sync_callback defined by slow-sync clients. + * When the client is active, this callback is invoked just before + * process() in the same thread. This occurs once after registration, + * then subsequently whenever some client requests a new position, or + * the transport enters the ::JackTransportStarting state. This + * realtime function must not wait. + * + * The transport @a state will be: + * + * - ::JackTransportStopped when a new position is requested; + * - ::JackTransportStarting when the transport is waiting to start; + * - ::JackTransportRolling when the timeout has expired, and the + * position is now a moving target. + * + * @param state current transport state. + * @param pos new transport position. + * @param arg the argument supplied by jack_set_sync_callback(). + * + * @return TRUE (non-zero) when ready to roll. + */ + typedef int (*JackSyncCallback)(jack_transport_state_t state, + jack_position_t *pos, + void *arg); + + + /** + * Prototype for the @a timebase_callback used to provide extended + * position information. Its output affects all of the following + * process cycle. This realtime function must not wait. + * + * This function is called immediately after process() in the same + * thread whenever the transport is rolling, or when any client has + * requested a new position in the previous cycle. The first cycle + * after jack_set_timebase_callback() is also treated as a new + * position, or the first cycle after jack_activate() if the client + * had been inactive. + * + * The timebase master may not use its @a pos argument to set @a + * pos->frame. To change position, use jack_transport_reposition() or + * jack_transport_locate(). These functions are realtime-safe, the @a + * timebase_callback can call them directly. + * + * @param state current transport state. + * @param nframes number of frames in current period. + * @param pos address of the position structure for the next cycle; @a + * pos->frame will be its frame number. If @a new_pos is FALSE, this + * structure contains extended position information from the current + * cycle. If TRUE, it contains whatever was set by the requester. + * The @a timebase_callback's task is to update the extended + * information here. + * @param new_pos TRUE (non-zero) for a newly requested @a pos, or for + * the first cycle after the @a timebase_callback is defined. + * @param arg the argument supplied by jack_set_timebase_callback(). + */ + typedef void (*JackTimebaseCallback)(jack_transport_state_t state, + jack_nframes_t nframes, + jack_position_t *pos, + int new_pos, + void *arg); + + /********************************************************************* + * The following interfaces are DEPRECATED. They are only provided + * for compatibility with the earlier JACK transport implementation. + *********************************************************************/ + + /** + * Optional struct jack_transport_info_t fields. + * + * @see jack_position_bits_t. + */ + typedef enum { + + JackTransportState = 0x1, /**< Transport state */ + JackTransportPosition = 0x2, /**< Frame number */ + JackTransportLoop = 0x4, /**< Loop boundaries (ignored) */ + JackTransportSMPTE = 0x8, /**< SMPTE (ignored) */ + JackTransportBBT = 0x10 /**< Bar, Beat, Tick */ + + } jack_transport_bits_t; + + /** + * Deprecated struct for transport position information. + * + * @deprecated This is for compatibility with the earlier transport + * interface. Use the jack_position_t struct, instead. + */ + typedef struct { + + /* these two cannot be set from clients: the server sets them */ + + jack_nframes_t frame_rate; /**< current frame rate (per second) */ + jack_time_t usecs; /**< monotonic, free-rolling */ + + jack_transport_bits_t valid; /**< which fields are legal to read */ + jack_transport_state_t transport_state; + jack_nframes_t frame; + jack_nframes_t loop_start; + jack_nframes_t loop_end; + + long smpte_offset; /**< SMPTE offset (from frame 0) */ + float smpte_frame_rate; /**< 29.97, 30, 24 etc. */ + + int bar; + int beat; + int tick; + double bar_start_tick; + + float beats_per_bar; + float beat_type; + double ticks_per_beat; + double beats_per_minute; + + } + jack_transport_info_t; + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_transport_aux_h__ */ diff --git a/common/types.h b/common/types.h new file mode 100644 index 00000000..ec294348 --- /dev/null +++ b/common/types.h @@ -0,0 +1,395 @@ +/* + Copyright (C) 2001 Paul Davis + Copyright (C) 2004 Jack O'Quin + + 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. + + $Id: types.h,v 1.4.2.8 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __jack_types_h__ +#define __jack_types_h__ + +#ifdef WIN32 + #include <windows.h> +typedef char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef long int32_t; +typedef unsigned long uint32_t; +typedef LONGLONG int64_t; +typedef ULONGLONG uint64_t; +#else + #include <inttypes.h> +#endif + +typedef int32_t jack_shmsize_t; + +/** + * Type used to represent sample frame counts. + */ +typedef uint32_t jack_nframes_t; + +/** + * Maximum value that can be stored in jack_nframes_t + */ +#define JACK_MAX_FRAMES (4294967295U) /* This should be UINT32_MAX, but C++ has a problem with that. */ + +/** + * Type used to represent the value of free running + * monotonic clock with units of microseconds. + */ + +#ifdef WIN32 +typedef int64_t jack_time_t; +//typedef double jack_time_t; +//typedef uint64_t jack_time_t; + +#else +typedef uint64_t jack_time_t; +#endif + +/** + * Maximum size of @a load_init string passed to an internal client + * jack_initialize() function via jack_internal_client_load(). + */ +#define JACK_LOAD_INIT_LIMIT 1024 + +/** + * jack_intclient_t is an opaque type representing a loaded internal + * client. You may only access it using the API provided in @ref + * intclient.h "<jack/intclient.h>". + */ +typedef uint64_t jack_intclient_t; + +/** + * jack_port_t is an opaque type. You may only access it using the + * API provided. + */ +typedef struct _jack_port jack_port_t; + +/** + * jack_client_t is an opaque type. You may only access it using the + * API provided. + */ +typedef struct _jack_client jack_client_t; + +/** + * Ports have unique ids. A port registration callback is the only + * place you ever need to know their value. + */ +#if defined(__x86_64__) +typedef uint64_t jack_port_id_t; +#else +typedef uint32_t jack_port_id_t; +#endif + +/** + * Prototype for the client supplied function that is called + * by the engine anytime there is work to be done. + * + * @pre nframes == jack_get_buffer_size() + * @pre nframes == pow(2,x) + * + * @param nframes number of frames to process + * @param arg pointer to a client supplied structure + * + * @return zero on success, non-zero on error + */ +typedef int (*JackProcessCallback)(jack_nframes_t nframes, void *arg); + +/** + * Prototype for the client supplied function that is called + * once after the creation of the thread in which other + * callbacks will be made. Special thread characteristics + * can be set from this callback, for example. This is a + * highly specialized callback and most clients will not + * and should not use it. + * + * @param arg pointer to a client supplied structure + * + * @return void + */ +typedef void (*JackThreadInitCallback)(void *arg); + +/** + * Prototype for the client supplied function that is called + * whenever the processing graph is reordered. + * + * @param arg pointer to a client supplied structure + * + * @return zero on success, non-zero on error + */ +typedef int (*JackGraphOrderCallback)(void *arg); + +/** + * Prototype for the client-supplied function that is called whenever + * an xrun has occured. + * + * @see jack_get_xrun_delayed_usecs() + * + * @param arg pointer to a client supplied structure + * + * @return zero on success, non-zero on error + */ +typedef int (*JackXRunCallback)(void *arg); + +/** + * Prototype for the @a bufsize_callback that is invoked whenever the + * JACK engine buffer size changes. Although this function is called + * in the JACK process thread, the normal process cycle is suspended + * during its operation, causing a gap in the audio flow. So, the @a + * bufsize_callback can allocate storage, touch memory not previously + * referenced, and perform other operations that are not realtime + * safe. + * + * @param nframes buffer size + * @param arg pointer supplied by jack_set_buffer_size_callback(). + * + * @return zero on success, non-zero on error + */ +typedef int (*JackBufferSizeCallback)(jack_nframes_t nframes, void *arg); + +/** + * Prototype for the client supplied function that is called + * when the engine sample rate changes. + * + * @param nframes new engine sample rate + * @param arg pointer to a client supplied structure + * + * @return zero on success, non-zero on error + */ +typedef int (*JackSampleRateCallback)(jack_nframes_t nframes, void *arg); + +/** + * Prototype for the client supplied function that is called + * whenever a port is registered or unregistered. + * + * @param arg pointer to a client supplied structure + */ +typedef void (*JackPortRegistrationCallback)(jack_port_id_t port, int, void *arg); + +/** + * Prototype for the client supplied function that is called + * whenever jackd starts or stops freewheeling. + * + * @param starting non-zero if we start starting to freewheel, zero otherwise + * @param arg pointer to a client supplied structure + */ +typedef void (*JackFreewheelCallback)(int starting, void *arg); + +/** + * Used for the type argument of jack_port_register() for default + * audio ports. + */ +#define JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" + +/** + * For convenience, use this typedef if you want to be able to change + * between float and double. You may want to typedef sample_t to + * jack_default_audio_sample_t in your application. + */ +typedef float jack_default_audio_sample_t; + +/** + * A port has a set of flags that are formed by AND-ing together the + * desired values from the list below. The flags "JackPortIsInput" and + * "JackPortIsOutput" are mutually exclusive and it is an error to use + * them both. + */ +enum JackPortFlags { + + /** + * if JackPortIsInput is set, then the port can receive + * data. + */ + JackPortIsInput = 0x1, + + /** + * if JackPortIsOutput is set, then data can be read from + * the port. + */ + JackPortIsOutput = 0x2, + + /** + * if JackPortIsPhysical is set, then the port corresponds + * to some kind of physical I/O connector. + */ + JackPortIsPhysical = 0x4, + + /** + * if JackPortCanMonitor is set, then a call to + * jack_port_request_monitor() makes sense. + * + * Precisely what this means is dependent on the client. A typical + * result of it being called with TRUE as the second argument is + * that data that would be available from an output port (with + * JackPortIsPhysical set) is sent to a physical output connector + * as well, so that it can be heard/seen/whatever. + * + * Clients that do not control physical interfaces + * should never create ports with this bit set. + */ + JackPortCanMonitor = 0x8, + + /** + * JackPortIsTerminal means: + * + * for an input port: the data received by the port + * will not be passed on or made + * available at any other port + * + * for an output port: the data available at the port + * does not originate from any other port + * + * Audio synthesizers, I/O hardware interface clients, HDR + * systems are examples of clients that would set this flag for + * their ports. + */ + JackPortIsTerminal = 0x10 +}; + +/** + * @ref jack_options_t bits + */ +enum JackOptions { + + /** + * Null value to use when no option bits are needed. + */ + JackNullOption = 0x00, + + /** + * Do not automatically start the JACK server when it is not + * already running. This option is always selected if + * \$JACK_NO_START_SERVER is defined in the calling process + * environment. + */ + JackNoStartServer = 0x01, + + /** + * Use the exact client name requested. Otherwise, JACK + * automatically generates a unique one, if needed. + */ + JackUseExactName = 0x02, + + /** + * Open with optional <em>(char *) server_name</em> parameter. + */ + JackServerName = 0x04, + + /** + * Load internal client from optional <em>(char *) + * load_name</em>. Otherwise use the @a client_name. + */ + JackLoadName = 0x08, + + /** + * Pass optional <em>(char *) load_init</em> string to the + * jack_initialize() entry point of an internal client. + */ + JackLoadInit = 0x10 +}; + +/** Valid options for opening an external client. */ +#define JackOpenOptions (JackServerName|JackNoStartServer|JackUseExactName) + +/** Valid options for loading an internal client. */ +#define JackLoadOptions (JackLoadInit|JackLoadName|JackUseExactName) + +/** + * Options for several JACK operations, formed by OR-ing together the + * relevant @ref JackOptions bits. + */ +typedef enum JackOptions jack_options_t; + +/** + * @ref jack_status_t bits + */ +enum JackStatus { + + /** + * Overall operation failed. + */ + JackFailure = 0x01, + + /** + * The operation contained an invalid or unsupported option. + */ + JackInvalidOption = 0x02, + + /** + * The desired client name was not unique. With the @ref + * JackUseExactName option this situation is fatal. Otherwise, + * the name was modified by appending a dash and a two-digit + * number in the range "-01" to "-99". The + * jack_get_client_name() function will return the exact string + * that was used. If the specified @a client_name plus these + * extra characters would be too long, the open fails instead. + */ + JackNameNotUnique = 0x04, + + /** + * The JACK server was started as a result of this operation. + * Otherwise, it was running already. In either case the caller + * is now connected to jackd, so there is no race condition. + * When the server shuts down, the client will find out. + */ + JackServerStarted = 0x08, + + /** + * Unable to connect to the JACK server. + */ + JackServerFailed = 0x10, + + /** + * Communication error with the JACK server. + */ + JackServerError = 0x20, + + /** + * Requested client does not exist. + */ + JackNoSuchClient = 0x40, + + /** + * Unable to load internal client + */ + JackLoadFailure = 0x80, + + /** + * Unable to initialize client + */ + JackInitFailure = 0x100, + + /** + * Unable to access shared memory + */ + JackShmFailure = 0x200, + + /** + * Client's protocol version does not match + */ + JackVersionError = 0x400 +}; + +/** + * Status word returned from several JACK operations, formed by + * OR-ing together the relevant @ref JackStatus bits. + */ +typedef enum JackStatus jack_status_t; + +#endif /* __jack_types_h__ */ diff --git a/common/varargs.h b/common/varargs.h new file mode 100644 index 00000000..238cb3c1 --- /dev/null +++ b/common/varargs.h @@ -0,0 +1,71 @@ +/* +* Copyright (C) 2004 Jack O'Quin +* +* 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. +* +* $Id: varargs.h,v 1.3 2005/11/23 11:24:00 letz Exp $ +*/ + +#ifndef __jack_varargs_h__ +#define __jack_varargs_h__ + +#include <stdlib.h> +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* variable argument structure */ + typedef struct { + char *server_name; /* server name */ + char *load_name; /* load module name */ + char *load_init; /* initialization string */ + } + jack_varargs_t; + + static char* jack_default_server_name (void) { + char *server_name; + if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL) + server_name = "default"; + return server_name; + } + + static inline void jack_varargs_init (jack_varargs_t *va) { + memset (va, 0, sizeof(jack_varargs_t)); + va->server_name = jack_default_server_name(); + } + + static inline void jack_varargs_parse (jack_options_t options, va_list ap, jack_varargs_t *va) { + // initialize default settings + jack_varargs_init (va); + + if ((options & JackServerName)) { + char *sn = va_arg(ap, char *); + if (sn) + va->server_name = sn; + } + if ((options & JackLoadName)) + va->load_name = va_arg(ap, char *); + if ((options & JackLoadInit)) + va->load_init = va_arg(ap, char *); + } + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_varargs_h__ */ |