summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am11
-rw-r--r--alsa_driver.c115
-rw-r--r--client.c86
-rw-r--r--configure.in10
-rw-r--r--driver.c2
-rw-r--r--engine.c182
-rw-r--r--jack/alsa_driver.h4
-rw-r--r--jack/driver.h4
-rw-r--r--jack/engine.h11
-rw-r--r--jack/internal.h7
-rw-r--r--jack/jack.h74
-rw-r--r--jack/types.h1
-rw-r--r--jackd.c76
-rw-r--r--simple_client.c4
14 files changed, 479 insertions, 108 deletions
diff --git a/Makefile.am b/Makefile.am
index ecd56e8..cb1115e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,11 +6,12 @@ MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure config.h.in \
if HAVE_DOXYGEN
SUBDIRS = . jack doc
+DIST_SUBDIRS = jack
else
SUBDIRS = . jack
+DIST_SUBDIRS = jack
endif
-DIST_SUBDIRS = jack doc
if HAVE_FLTK
FLTK_CLIENT = jack_fltk_client
@@ -25,7 +26,7 @@ JACKREC =
endif
bin_PROGRAMS = jackd jack_simple_client jack_monitor_client \
- jack_impulse_grabber jack_connect jack_disconnect $(FLTK_CLIENT) $(JACKREC)
+ jack_impulse_grabber jack_cache_killer jack_connect jack_disconnect $(FLTK_CLIENT) $(JACKREC)
AM_CFLAGS = $(JACK_CFLAGS) -DADDON_DIR=\"$(ADDON_DIR)\" $(GLIB_CFLAGS)
@@ -51,6 +52,10 @@ jack_monitor_client_SOURCES = monitor_client.c
jack_monitor_client_LDFLAGS = -ldl -lpthread
jack_monitor_client_LDADD = libjack.la
+jack_cache_killer_SOURCES = cache_killer.c
+jack_cache_killer_LDFLAGS = -ldl -lpthread
+jack_cache_killer_LDADD = libjack.la
+
if HAVE_FLTK
jack_fltk_client_SOURCES = fltk_client.cc
jack_fltk_client_LDFLAGS = -L/usr/X11R6/lib -lfltk -lX11 -lXext -ldl -lpthread
@@ -76,7 +81,7 @@ libjack_la_SOURCES = client.c pool.c driver.c
libjack_la_LDFLAGS = $(GLIB_LIBS) -lm
jack_alsa_la_LDFLAGS = -module
-jack_alsa_la_SOURCES = alsa_driver.c hammerfall.c generic_hw.c memops.c
+jack_alsa_la_SOURCES = alsa_driver.c hammerfall.c generic_hw.c memops.c
jack_alsa_la_LIBADD = -lasound $(GLIB_LIBS) -lm
pkgconfigdir = $(libdir)/pkgconfig
diff --git a/alsa_driver.c b/alsa_driver.c
index c2026b2..45e416d 100644
--- a/alsa_driver.c
+++ b/alsa_driver.c
@@ -27,7 +27,7 @@
#include <glib.h>
#include <stdarg.h>
#include <getopt.h>
-#include <asm/msr.h>
+#include <asm/timex.h>
#include <jack/alsa_driver.h>
#include <jack/types.h>
@@ -35,6 +35,12 @@
#include <jack/engine.h>
#include <jack/hammerfall.h>
#include <jack/generic.h>
+#include <jack/util.h>
+
+extern void store_work_time (int);
+extern void store_wait_time (int);
+extern void show_wait_times ();
+extern void show_work_times ();
static void
alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver)
@@ -236,7 +242,7 @@ alsa_driver_configure_stream (alsa_driver_t *driver,
if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) < 0) {
if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
jack_error ("ALSA: mmap-based access is not possible for the %s "
- "stream of this audio interface", stream_name);
+ "stream of this audio interface", stream_name);
return -1;
}
}
@@ -244,8 +250,8 @@ alsa_driver_configure_stream (alsa_driver_t *driver,
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S32_LE)) < 0) {
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
jack_error ("Sorry. The audio interface \"%s\""
- "doesn't support either of the two hardware sample formats that ardour can use.",
- driver->alsa_name);
+ "doesn't support either of the two hardware sample formats that ardour can use.",
+ driver->alsa_name);
return -1;
}
}
@@ -444,7 +450,6 @@ alsa_driver_set_parameters (alsa_driver_t *driver, nframes_t frames_per_cycle, n
driver->buffer_frames = driver->frames_per_cycle * driver->nfragments;
driver->sample_bytes = snd_pcm_format_physical_width (driver->sample_format) / 8;
- driver->bytes_per_cycle = driver->sample_bytes * driver->frames_per_cycle;
switch (driver->sample_format) {
case SND_PCM_FORMAT_S32_LE:
@@ -515,9 +520,8 @@ alsa_driver_set_parameters (alsa_driver_t *driver, nframes_t frames_per_cycle, n
driver->clock_sync_data = (ClockSyncStatus *) malloc (sizeof (ClockSyncStatus) *
driver->capture_nchannels > driver->playback_nchannels ?
driver->capture_nchannels : driver->playback_nchannels);
-
- driver->period_interval = (unsigned long) floor ((((float) driver->frames_per_cycle) / driver->frame_rate) * 1000.0);
+ driver->period_usecs = (((float) driver->frames_per_cycle) / driver->frame_rate) * 1000000.0f;
if (driver->engine) {
driver->engine->set_buffer_size (driver->engine, driver->frames_per_cycle);
@@ -584,6 +588,9 @@ alsa_driver_audio_start (alsa_driver_t *driver)
snd_pcm_uframes_t poffset, pavail;
channel_t chn;
+ driver->poll_last = 0;
+ driver->poll_next = 0;
+
if (driver->playback_handle) {
if ((err = snd_pcm_prepare (driver->playback_handle)) < 0) {
jack_error ("ALSA-HW: prepare error for playback on \"%s\" (%s)", driver->alsa_name, snd_strerror(err));
@@ -723,14 +730,13 @@ alsa_driver_xrun_recovery (alsa_driver_t *driver)
gettimeofday(&now, 0);
snd_pcm_status_get_trigger_tstamp(status, &tstamp);
timersub(&now, &tstamp, &diff);
- fprintf(stderr, "alsa_pcm: xrun of at least %.3f msecs\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
+ fprintf(stderr, "\n\n**** alsa_pcm: xrun of at least %.3f msecs\n\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
}
if (alsa_driver_audio_stop (driver) || alsa_driver_audio_start (driver)) {
return -1;
}
-
return 0;
}
@@ -761,7 +767,7 @@ alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, ClockSy
static int under_gdb = FALSE;
static nframes_t
-alsa_driver_wait (alsa_driver_t *driver, int fd, int *status)
+alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delayed_usecs)
{
snd_pcm_sframes_t avail = 0;
snd_pcm_sframes_t capture_avail = 0;
@@ -770,10 +776,14 @@ alsa_driver_wait (alsa_driver_t *driver, int fd, int *status)
int need_capture;
int need_playback;
int i;
+ unsigned long long poll_enter, poll_ret;
*status = -1;
+ *delayed_usecs = 0;
+
need_capture = driver->capture_handle ? 1 : 0;
- if (fd >= 0) {
+
+ if (extra_fd >= 0) {
need_playback = 0;
} else {
need_playback = driver->playback_handle ? 1 : 0;
@@ -806,13 +816,15 @@ alsa_driver_wait (alsa_driver_t *driver, int fd, int *status)
driver->pfd[nfds].events |= POLLERR;
}
- if (fd >= 0) {
- driver->pfd[nfds].fd = fd;
+ if (extra_fd >= 0) {
+ driver->pfd[nfds].fd = extra_fd;
driver->pfd[nfds].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
nfds++;
}
- if (poll (driver->pfd, nfds, 1000) < 0) {
+ poll_enter = get_cycles ();
+
+ if (poll (driver->pfd, nfds, (int) floor (driver->period_usecs / 1000.0f)) < 0) {
if (errno == EINTR) {
printf ("poll interrupt\n");
// this happens mostly when run
@@ -820,26 +832,48 @@ alsa_driver_wait (alsa_driver_t *driver, int fd, int *status)
if (under_gdb) {
goto again;
}
+ *status = -2;
return 0;
}
- jack_error ("ALSA::Device: poll call failed (%s)", strerror (errno));
+ jack_error ("ALSA: poll call failed (%s)", strerror (errno));
+ *status = -3;
return 0;
}
+ poll_ret = get_cycles ();
+
+ if (extra_fd < 0) {
+ if (driver->poll_next && poll_ret > driver->poll_next) {
+ *delayed_usecs = (float) (poll_ret - driver->poll_next) / driver->cpu_mhz;
+ }
+ driver->poll_last = poll_ret;
+ driver->poll_next = poll_ret + (unsigned long long) floor ((driver->period_usecs * driver->cpu_mhz));
+ }
+
/* check to see if it was the extra FD that caused us to return from poll
*/
- if (fd >= 0) {
+ if (extra_fd >= 0) {
+
if (driver->pfd[nfds-1].revents == 0) {
/* we timed out on the extra fd */
+
+ *status = -4;
+#if 0
+ printf ("checked %d fds, at %Lu %.9f usecs since poll entered\n",
+ nfds,
+ poll_ret,
+ (float) (poll_ret - poll_enter) / driver->cpu_mhz);
+#endif
return -1;
- } else {
- /* if POLLIN was the only bit set, we're OK */
- *status = 0;
- return (driver->pfd[nfds-1].revents == POLLIN) ? 0 : -1;
- }
+ }
+
+ /* if POLLIN was the only bit set, we're OK */
+
+ *status = 0;
+ return (driver->pfd[nfds-1].revents == POLLIN) ? 0 : -1;
}
if (driver->engine) {
@@ -887,6 +921,7 @@ alsa_driver_wait (alsa_driver_t *driver, int fd, int *status)
if ((p_timed_out && (p_timed_out == driver->playback_nfds)) &&
(c_timed_out && (c_timed_out == driver->capture_nfds))){
jack_error ("ALSA: poll time out");
+ *status = -5;
return 0;
}
@@ -979,7 +1014,7 @@ alsa_driver_process (alsa_driver_t *driver, nframes_t nframes)
&capture_offset, 0) < 0) {
return -1;
}
-
+
contiguous = capture_avail;
}
@@ -993,8 +1028,8 @@ alsa_driver_process (alsa_driver_t *driver, nframes_t nframes)
(snd_pcm_uframes_t *) 0,
(snd_pcm_uframes_t *) &playback_avail,
0, &playback_offset) < 0) {
- return -1;
- }
+ return -1;
+ }
contiguous = playback_avail;
}
@@ -1019,10 +1054,13 @@ alsa_driver_process (alsa_driver_t *driver, nframes_t nframes)
alsa_driver_read_from_channel (driver, chn, jack_port_get_buffer (port, nframes), nframes);
}
-
snd_pcm_mmap_commit (driver->capture_handle, capture_offset, contiguous);
}
+ if (contiguous != driver->frames_per_cycle) {
+ printf ("wierd contig size %lu\n", contiguous);
+ }
+
if ((ret = engine->process (engine, contiguous)) != 0) {
engine->process_unlock (engine);
alsa_driver_audio_stop (driver);
@@ -1032,7 +1070,6 @@ alsa_driver_process (alsa_driver_t *driver, nframes_t nframes)
return ret;
}
-
if (driver->playback_handle) {
/* now move data from ports to channels */
@@ -1091,7 +1128,6 @@ alsa_driver_process (alsa_driver_t *driver, nframes_t nframes)
if (driver->channels_not_done) {
alsa_driver_silence_untouched_channels (driver, contiguous);
}
-
snd_pcm_mmap_commit (driver->playback_handle, playback_offset, contiguous);
}
@@ -1109,6 +1145,7 @@ alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine)
char buf[32];
channel_t chn;
jack_port_t *port;
+ int port_flags;
driver->engine = engine;
@@ -1122,12 +1159,17 @@ alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine)
return;
}
+ port_flags = JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal;
+
+ if (driver->has_hw_monitoring) {
+ port_flags |= JackPortCanMonitor;
+ }
+
for (chn = 0; chn < driver->capture_nchannels; chn++) {
+
snprintf (buf, sizeof(buf) - 1, "in_%lu", chn+1);
- port = jack_port_register (driver->client, buf,
- JACK_DEFAULT_AUDIO_TYPE,
- JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal|JackPortCanMonitor, 0);
- if (port == 0) {
+
+ if ((port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) {
jack_error ("ALSA: cannot register port for %s", buf);
break;
}
@@ -1139,13 +1181,13 @@ alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine)
driver->capture_ports = g_slist_append (driver->capture_ports, port);
}
+
+ port_flags = JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal;
for (chn = 0; chn < driver->playback_nchannels; chn++) {
snprintf (buf, sizeof(buf) - 1, "out_%lu", chn+1);
- port = jack_port_register (driver->client, buf,
- JACK_DEFAULT_AUDIO_TYPE,
- JackPortIsInput|JackPortIsTerminal|JackPortIsPhysical, 0);
- if (port == 0) {
+
+ if ((port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) {
jack_error ("ALSA: cannot register port for %s", buf);
break;
}
@@ -1158,8 +1200,6 @@ alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine)
driver->playback_ports = g_slist_append (driver->playback_ports, port);
}
- printf ("ALSA: ports registered, starting driver\n");
-
jack_activate (driver->client);
}
@@ -1336,6 +1376,7 @@ alsa_driver_new (char *name, char *alsa_device,
driver->capture_addr = 0;
driver->silent = 0;
driver->all_monitor_in = FALSE;
+ driver->cpu_mhz = jack_get_mhz();
driver->clock_mode = ClockMaster; /* XXX is it? */
driver->input_monitor_mask = 0; /* XXX is it? */
diff --git a/client.c b/client.c
index 9bafc54..6569837 100644
--- a/client.c
+++ b/client.c
@@ -247,14 +247,14 @@ jack_handle_reorder (jack_client_t *client, jack_event_t *event)
sprintf (path, "%s-%lu", client->fifo_prefix, event->x.n);
- if ((client->graph_wait_fd = open (path, O_RDONLY)) <= 0) {
+ if ((client->graph_wait_fd = open (path, O_RDONLY|O_NONBLOCK)) <= 0) {
jack_error ("cannot open specified fifo [%s] for reading (%s)", path, strerror (errno));
return -1;
}
sprintf (path, "%s-%lu", client->fifo_prefix, event->x.n+1);
- if ((client->graph_next_fd = open (path, O_WRONLY)) < 0) {
+ if ((client->graph_next_fd = open (path, O_WRONLY|O_NONBLOCK)) < 0) {
jack_error ("cannot open specified fifo [%s] for writing (%s)", path, strerror (errno));
return -1;
}
@@ -515,7 +515,7 @@ jack_client_thread (void *arg)
status = -1;
break;
}
-
+
if (client->pollfd[0].revents & ~POLLIN) {
jack_error ("engine has shut down socket; thread exiting");
if (client->on_shutdown) {
@@ -572,6 +572,12 @@ jack_client_thread (void *arg)
}
break;
+ case XRun:
+ if (control->xrun) {
+ status = control->xrun (control->xrun_arg);
+ }
+ break;
+
case NewPortBufferSegment:
break;
}
@@ -585,15 +591,7 @@ jack_client_thread (void *arg)
if (client->pollfd[1].revents & POLLIN) {
- /* the previous stage of the graph has told us to
- process().
- */
-
- if (read (client->graph_wait_fd, &c, sizeof (c)) != sizeof (c)) {
- jack_error ("cannot clean up byte from inter-client pipe (%s)", strerror (errno));
- err++;
- break;
- }
+ rdtscll (control->signalled_at);
control->state = Running;
@@ -604,14 +602,23 @@ jack_client_thread (void *arg)
} else {
control->state = Finished;
}
+
+ rdtscll (control->finished_at);
- /* this may fail. if it does, the engine will discover
- it due a cycle timeout, which is about
- the best we can do without a lot of mostly wasted
- effort.
- */
+ /* pass the execution token along */
+
+ if (write (client->graph_next_fd, &c, sizeof (c)) != sizeof (c)) {
+ jack_error ("cannot continue execution of the processing graph (%s)", strerror(errno));
+ err++;
+ break;
+ }
+
+ if ((read (client->graph_wait_fd, &c, sizeof (c)) != sizeof (c))) {
+ jack_error ("cannot complete execution of the processing graph (%s)", strerror(errno));
+ err++;
+ break;
+ }
- write (client->graph_next_fd, &c, 1);
}
}
@@ -672,6 +679,17 @@ jack_activate (jack_client_t *client)
{
jack_request_t req;
+#define BIG_ENOUGH_STACK 1048576
+
+ char buf[BIG_ENOUGH_STACK];
+ int i;
+
+ for (i = 0; i < BIG_ENOUGH_STACK; i++) {
+ buf[i] = (char) (i & 0xff);
+ }
+
+#undef BIG_ENOUGH_STACK
+
if (client->control->type == ClientOutOfProcess && client->first_active) {
pthread_mutex_init (&client_lock, NULL);
@@ -1740,3 +1758,35 @@ jack_port_get_total_latency (jack_client_t *client, jack_port_t *port)
{
return port->shared->total_latency;
}
+
+int
+jack_get_mhz (void)
+{
+ FILE *f = fopen("/proc/cpuinfo", "r");
+ if (f == 0)
+ {
+ perror("can't open /proc/cpuinfo\n");
+ exit(1);
+ }
+
+ for ( ; ; )
+ {
+ int mhz;
+ int ret;
+ char buf[1000];
+
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ {
+ fprintf(stderr, "cannot locate cpu MHz in /proc/cpuinfo\n");
+ exit(1);
+ }
+
+ ret = sscanf(buf, "cpu MHz : %d", &mhz);
+
+ if (ret == 1)
+ {
+ fclose(f);
+ return mhz;
+ }
+ }
+}
diff --git a/configure.in b/configure.in
index 2310ced..973c56b 100644
--- a/configure.in
+++ b/configure.in
@@ -4,8 +4,8 @@ AC_INIT(client.c)
AC_CONFIG_AUX_DIR(.)
JACK_MAJOR_VERSION=0
-JACK_MINOR_VERSION=24
-JACK_MICRO_VERSION=3
+JACK_MINOR_VERSION=25
+JACK_MICRO_VERSION=0
BETA=
@@ -21,7 +21,7 @@ AC_SUBST(JACK_SO_VERSION)
AC_SUBST(JACK_VERSION)
AC_SUBST(JACK_RELEASE)
-AM_INIT_AUTOMAKE(jack,${JACK_VERSION})
+AM_INIT_AUTOMAKE(jack-audio-connection-kit,${JACK_VERSION})
AC_PROG_CC
AC_PROG_CXX
@@ -51,7 +51,8 @@ AM_PATH_GLIB(1.0.0,
AC_CHECK_LIB(asound,snd_pcm_drop,
[:],
- [AC_MSG_ERROR([*** JACK currently requires ALSA (0.9.X) which you don't appear to have])]
+ [AC_MSG_ERROR([*** JACK currently requires ALSA (0.9.X) which you don't appear to have])],
+ [-lm]
)
AC_LANG_SAVE
@@ -90,4 +91,3 @@ AM_CONDITIONAL(HAVE_FLTK, $HAVE_FLTK)
AM_CONDITIONAL(HAVE_SNDFILE, $HAVE_SNDFILE)
AM_CONDITIONAL(HAVE_DOXYGEN, $HAVE_DOXYGEN)
AC_OUTPUT(Makefile jack.pc jack/Makefile doc/Makefile doc/reference.doxygen)
-
diff --git a/driver.c b/driver.c
index 7a50260..214c0d3 100644
--- a/driver.c
+++ b/driver.c
@@ -30,7 +30,7 @@
static int dummy_attach (jack_driver_t *drv, jack_engine_t *eng) { return 0; }
static int dummy_detach (jack_driver_t *drv, jack_engine_t *eng) { return 0; }
-static nframes_t dummy_wait (jack_driver_t *drv, int fd, int *status) { *status = 0; return 0; }
+static nframes_t dummy_wait (jack_driver_t *drv, int fd, int *status, float *delayed_usecs) { *status = 0; *delayed_usecs = 0; return 0; }
static int dummy_process (jack_driver_t *drv, nframes_t nframes) { return 0; }
static int dummy_stop (jack_driver_t *drv) { return 0; }
static int dummy_start (jack_driver_t *drv) { return 0; }
diff --git a/engine.c b/engine.c
index ad54fd8..788e284 100644
--- a/engine.c
+++ b/engine.c
@@ -18,6 +18,7 @@
$Id$
*/
+#include <math.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/poll.h>
@@ -35,12 +36,12 @@
#include <string.h>
#include <limits.h>
#include <sys/mman.h>
-
-#include <asm/msr.h>
+#include <asm/timex.h>
#include <jack/internal.h>
#include <jack/engine.h>
#include <jack/driver.h>
+#include <jack/util.h>
#define MAX_SHM_ID 256 /* likely use is more like 16 */
@@ -126,6 +127,22 @@ jack_lock_graph (jack_engine_t *engine)
}
static inline void
+jack_engine_reset_rolling_usecs (jack_engine_t *engine)
+{
+ memset (engine->rolling_client_usecs, 0, sizeof (engine->rolling_client_usecs));
+ engine->rolling_client_usecs_index = 0;
+ engine->rolling_client_usecs_cnt = 0;
+
+ if (engine->driver) {
+ engine->rolling_interval = (int) floor (JACK_ENGINE_ROLLING_INTERVAL * 1000.0f / engine->driver->period_usecs);
+ } else {
+ engine->rolling_interval = JACK_ENGINE_ROLLING_INTERVAL; // whatever
+ }
+
+ engine->spare_usecs = 0;
+}
+
+static inline void
jack_unlock_graph (jack_engine_t *engine)
{
pthread_mutex_unlock (&engine->client_lock);
@@ -406,6 +423,9 @@ jack_process (jack_engine_t *engine, nframes_t nframes)
GSList *node;
char c;
int status;
+ float delayed_usecs;
+ unsigned long long now, then;
+ unsigned long long clients_start, clients_end;
engine->process_errors = 0;
@@ -419,6 +439,8 @@ jack_process (jack_engine_t *engine, nframes_t nframes)
engine->control->time.frame = engine->timebase_client->control->frame_time;
}
+ clients_start = get_cycles();
+
for (node = engine->clients; engine->process_errors == 0 && node; ) {
client = (jack_client_internal_t *) node->data;
@@ -429,6 +451,7 @@ jack_process (jack_engine_t *engine, nframes_t nframes)
}
ctl = client->control;
+ ctl->timed_out;
if (jack_client_is_inprocess (client)) {
@@ -438,6 +461,8 @@ jack_process (jack_engine_t *engine, nframes_t nframes)
ctl->state = Running;
+ /* XXX how to time out an in-process client? */
+
if (ctl->process (nframes, ctl->process_arg) == 0) {
ctl->state = Finished;
} else {
@@ -457,7 +482,8 @@ jack_process (jack_engine_t *engine, nframes_t nframes)
/* out of process subgraph */
ctl->state = Triggered; // a race exists if we do this after the write(2)
- ctl->timed_out = 0;
+ ctl->signalled_at = 0;
+ ctl->finished_at = 0;
if (write (client->subgraph_start_fd, &c, sizeof (c)) != sizeof (c)) {
jack_error ("cannot initiate graph processing (%s)", strerror (errno));
@@ -465,22 +491,24 @@ jack_process (jack_engine_t *engine, nframes_t nframes)
break;
}
+ then = get_cycles ();
+
if (engine->asio_mode) {
- engine->driver->wait (engine->driver, client->subgraph_wait_fd, &status);
+ engine->driver->wait (engine->driver, client->subgraph_wait_fd, &status, &delayed_usecs);
} else {
struct pollfd pfd[1];
pfd[0].fd = client->subgraph_wait_fd;
pfd[0].events = POLLERR|POLLIN|POLLHUP|POLLNVAL;
- if (poll (pfd, 1, engine->driver->period_interval) < 0) {
+ if (poll (pfd, 1, engine->driver->period_usecs) < 0) {
jack_error ("poll on subgraph processing failed (%s)", strerror (errno));
status = -1;
}
if (pfd[0].revents & ~POLLIN) {
jack_error ("subgraph starting at %s lost client", client->control->name);
- status = -1;
+ status = -2;
}
if (pfd[0].revents & POLLIN) {
@@ -493,18 +521,30 @@ jack_process (jack_engine_t *engine, nframes_t nframes)
}
}
+ now = get_cycles();
+
if (status != 0) {
- client->control->timed_out = 1;
+ if (engine->verbose) {
+ fprintf (stderr, "at %Lu client waiting on %d took %.9f usecs, status = %d sig = %Lu fin = %Lu dur=%.6f\n",
+ now,
+ client->subgraph_wait_fd,
+ (float) (now - then) / engine->cpu_mhz,
+ status,
+ ctl->signalled_at,
+ ctl->finished_at,
+ ((float) (ctl->finished_at - ctl->signalled_at)) / engine->cpu_mhz);
+ }
+ ctl->timed_out++;
engine->process_errors++;
break;
} else {
if (read (client->subgraph_wait_fd, &c, sizeof (c)) != sizeof (c)) {
- jack_error ("cannot clean up byte from graph wait fd (%s)", strerror (errno));
- engine->process_errors++;
+ jack_error ("pp: cannot clean up byte from graph wait fd (%s)", strerror (errno));
+ client->error++;
break;
}
}
-
+
/* Move to next in-process client (or end of client list) */
while (node) {
@@ -517,6 +557,42 @@ jack_process (jack_engine_t *engine, nframes_t nframes)
}
}
+ if (!engine->process_errors) {
+ clients_end = get_cycles();
+
+ /* store the execution time for this this part of the graph */
+
+ engine->rolling_client_usecs[engine->rolling_client_usecs_index++] = (float) (clients_end - clients_start) / engine->cpu_mhz;
+
+ if (engine->rolling_client_usecs_index >= JACK_ENGINE_ROLLING_COUNT) {
+ engine->rolling_client_usecs_index = 0;
+ }
+
+ /* every so often, recompute the current average use over the
+ last JACK_ENGINE_ROLLING_COUNT client iterations.
+ */
+
+ if (++engine->rolling_client_usecs_cnt % engine->rolling_interval == 0) {
+ float average_usecs = 0;
+ int i;
+
+ for (i = 0; i < JACK_ENGINE_ROLLING_COUNT; i++) {
+ average_usecs += engine->rolling_client_usecs[i];
+ }
+
+ average_usecs /= i;
+ if (average_usecs < engine->driver->period_usecs) {
+ engine->spare_usecs = engine->driver->period_usecs - average_usecs;
+ } else {
+ engine->spare_usecs = 0;
+ }
+
+ if (engine->verbose) {
+ fprintf (stderr, "average usecs: %.3f, spare = %.3f\n", average_usecs, engine->spare_usecs);
+ }
+ }
+ }
+
return engine->process_errors > 0;
}
@@ -537,8 +613,8 @@ jack_engine_post_process (jack_engine_t *engine)
client = (jack_client_internal_t *) node->data;
ctl = client->control;
- if (ctl->timed_out || (ctl->state > NotTriggered && ctl->state != Finished)) {
- client->error++;
+ if (ctl->timed_out || (ctl->state > NotTriggered && ctl->state != Finished)) {
+ client->error = TRUE;
}
if (client->error) {
@@ -576,6 +652,9 @@ jack_engine_post_process (jack_engine_t *engine)
if (need_sort) {
jack_sort_graph (engine);
}
+
+ jack_engine_reset_rolling_usecs (engine);
+
}
jack_unlock_graph (engine);
@@ -704,6 +783,8 @@ handle_new_client (jack_engine_t *engine, int client_fd)
engine->clients = g_slist_prepend (engine->clients, client);
+ jack_engine_reset_rolling_usecs (engine);
+
if (client->control->type != ClientDynamic) {
if (engine->pfd_max >= engine->pfd_size) {
engine->pfd = (struct pollfd *) realloc (engine->pfd, sizeof (struct pollfd) * engine->pfd_size + 16);
@@ -1124,6 +1205,9 @@ jack_engine_new (int realtime, int rtpriority, int verbose)
engine->silent_buffer = 0;
engine->verbose = verbose;
engine->asio_mode = FALSE;
+ engine->cpu_mhz = jack_get_mhz();
+
+ jack_engine_reset_rolling_usecs (engine);
pthread_mutex_init (&engine->client_lock, 0);
pthread_mutex_init (&engine->buffer_lock, 0);
@@ -1277,12 +1361,28 @@ jack_start_watchdog (jack_engine_t *engine)
return 0;
}
+static void
+jack_engine_notify_clients_about_delay (jack_engine_t *engine)
+{
+ GSList *node;
+ jack_event_t event;
+
+ event.type = XRun;
+
+ jack_lock_graph (engine);
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+ jack_deliver_event (engine, (jack_client_internal_t *) node->data, &event);
+ }
+ jack_unlock_graph (engine);
+}
+
static void *
jack_main_thread (void *arg)
{
jack_engine_t *engine = (jack_engine_t *) arg;
jack_driver_t *driver = engine->driver;
+ int consecutive_excessive_delays;
if (engine->control->real_time) {
@@ -1303,13 +1403,46 @@ jack_main_thread (void *arg)
pthread_exit (0);
}
+ consecutive_excessive_delays = 0;
+
while (1) {
int status;
nframes_t nframes;
+ float delayed_usecs;
engine->watchdog_check = 1;
- nframes = driver->wait (driver, -1, &status);
+ nframes = driver->wait (driver, -1, &status, &delayed_usecs);
+
+#define WORK_SCALE 1.0f
+
+ if (engine->spare_usecs && ((WORK_SCALE * engine->spare_usecs) <= delayed_usecs)) {
+
+ printf ("delay of %.3f usecs exceeds estimated spare time of %.3f; restart ...\n",
+ delayed_usecs, WORK_SCALE * engine->spare_usecs);
+
+ if (++consecutive_excessive_delays > 10) {
+ jack_error ("too many consecutive interrupt delays ... engine stopping");
+ break;
+ }
+
+ if (driver->stop (driver)) {
+ jack_error ("cannot stop current driver");
+ break;
+ }
+
+ jack_engine_notify_clients_about_delay (engine);
+
+ if (driver->start (driver)) {
+ jack_error ("cannot restart current driver after delay");
+ break;
+ }
+
+ continue;
+
+ } else {
+ consecutive_excessive_delays = 0;
+ }
if (status != 0) {
jack_error ("driver wait function failed, exiting");
@@ -1444,6 +1577,8 @@ jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_req
client->control->timed_out = 0;
client->control->id = engine->next_client_id++;
strcpy ((char *) client->control->name, req->name);
+ client->subgraph_start_fd = -1;
+ client->subgraph_wait_fd = -1;
client->control->process = NULL;
client->control->process_arg = NULL;
@@ -1491,10 +1626,11 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
GSList *node;
int i;
+
if (engine->verbose) {
fprintf (stderr, "adios senor %s\n", client->control->name);
}
-
+
/* caller must hold the client_lock */
/* this stops jack_deliver_event() from doing anything */
@@ -1629,6 +1765,12 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client, jack_
}
break;
+ case XRun:
+ if (client->control->xrun) {
+ client->control->xrun (client->control->xrun_arg);
+ }
+ break;
+
default:
/* internal clients don't need to know */
break;
@@ -1747,7 +1889,7 @@ jack_rechain_graph (jack_engine_t *engine)
fprintf(stderr, "client %s: in subgraph after %s, execution_order=%lu.\n",
client->control->name, subgraph_client->control->name, n);
}
-
+ subgraph_client->subgraph_wait_fd = -1;
}
/* make sure fifo for 'n + 1' exists
@@ -2049,17 +2191,19 @@ void jack_dump_configuration(jack_engine_t *engine, int take_lock)
if (take_lock) {
jack_lock_graph (engine);
}
-
+
for (n = 0, clientnode = engine->clients; clientnode; clientnode = g_slist_next (clientnode)) {
client = (jack_client_internal_t *) clientnode->data;
ctl = client->control;
- fprintf (stderr, "client #%d: %s (type: %d, process? %s, fed by %d clients)\n",
+ fprintf (stderr, "client #%d: %s (type: %d, process? %s, fed by %d clients) start=%d wait=%d\n",
++n,
ctl->name,
ctl->type,
ctl->process ? "yes" : "no",
- g_slist_length(client->fed_by));
+ g_slist_length(client->fed_by),
+ client->subgraph_start_fd,
+ client->subgraph_wait_fd);
for(m = 0, portnode = client->ports; portnode; portnode = g_slist_next (portnode)) {
port = (jack_port_internal_t *) portnode->data;
@@ -2361,6 +2505,8 @@ jack_use_driver (jack_engine_t *engine, jack_driver_t *driver)
if (driver->attach (driver, engine)) {
return -1;
}
+
+ engine->rolling_interval = (int) floor ((JACK_ENGINE_ROLLING_INTERVAL * 1000.0f) / driver->period_usecs);
}
engine->driver = driver;
diff --git a/jack/alsa_driver.h b/jack/alsa_driver.h
index bd0b903..d70ce44 100644
--- a/jack/alsa_driver.h
+++ b/jack/alsa_driver.h
@@ -45,6 +45,8 @@ typedef struct {
JACK_DRIVER_DECL
+ unsigned long long poll_last;
+ unsigned long long poll_next;
char **playback_addr;
char **capture_addr;
const snd_pcm_channel_area_t *capture_areas;
@@ -63,6 +65,7 @@ typedef struct {
nframes_t frame_rate;
nframes_t frames_per_cycle;
+ float cpu_mhz;
nframes_t capture_frame_latency;
nframes_t playback_frame_latency;
@@ -70,7 +73,6 @@ typedef struct {
char *alsa_name;
char *alsa_driver;
snd_pcm_uframes_t buffer_frames;
- unsigned long bytes_per_cycle; /* per channel */
unsigned long channels_not_done;
unsigned long channel_done_bits;
snd_pcm_format_t sample_format;
diff --git a/jack/driver.h b/jack/driver.h
index 5493fab..1dd6baf 100644
--- a/jack/driver.h
+++ b/jack/driver.h
@@ -39,13 +39,13 @@ struct _jack_driver;
typedef int (*JackDriverAttachFunction)(struct _jack_driver *, struct _jack_engine *);
typedef int (*JackDriverDetachFunction)(struct _jack_driver *, struct _jack_engine *);
-typedef nframes_t (*JackDriverWaitFunction)(struct _jack_driver *, int fd, int *status);
+typedef nframes_t (*JackDriverWaitFunction)(struct _jack_driver *, int fd, int *status, float *delayed_usecs);
typedef int (*JackDriverProcessFunction)(struct _jack_driver *, nframes_t);
typedef int (*JackDriverStopFunction)(struct _jack_driver *);
typedef int (*JackDriverStartFunction)(struct _jack_driver *);
#define JACK_DRIVER_DECL \
- nframes_t period_interval; \
+ nframes_t period_usecs; \
void *handle; \
void (*finish)(struct _jack_driver *);\
JackDriverAttachFunction attach; \
diff --git a/jack/engine.h b/jack/engine.h
index 7093263..3c6b20a 100644
--- a/jack/engine.h
+++ b/jack/engine.h
@@ -81,6 +81,17 @@ struct _jack_engine {
char asio_mode;
int reordered;
int watchdog_check;
+ float cpu_mhz;
+
+#define JACK_ENGINE_ROLLING_COUNT 32
+#define JACK_ENGINE_ROLLING_INTERVAL 1024
+
+ float rolling_client_usecs[JACK_ENGINE_ROLLING_COUNT];
+ int rolling_client_usecs_cnt;
+ int rolling_client_usecs_index;
+ int rolling_interval;
+ float spare_usecs;
+ float usecs_per_cycle;
};
/* public functions */
diff --git a/jack/internal.h b/jack/internal.h
index 2a0b7dd..85a2c4c 100644
--- a/jack/internal.h
+++ b/jack/internal.h
@@ -94,6 +94,7 @@ typedef enum {
GraphReordered,
PortRegistered,
PortUnregistered,
+ XRun,
} AudioEngineEventType;
typedef struct {
@@ -133,6 +134,8 @@ typedef volatile struct {
volatile char active : 1; /* w: engine r: engine and client */
volatile char dead : 1; /* r/w: engine */
volatile char timed_out : 1; /* r/w: engine */
+ volatile unsigned long long signalled_at;
+ volatile unsigned long long finished_at;
/* callbacks */
@@ -146,6 +149,8 @@ typedef volatile struct {
void *port_register_arg;
JackGraphOrderCallback graph_order;
void *graph_order_arg;
+ JackXRunCallback xrun;
+ void *xrun_arg;
/* for engine use only */
@@ -237,6 +242,8 @@ jack_client_t *jack_driver_become_client (const char *client_name);
extern char *jack_temp_dir;
+extern int jack_get_mhz (void);
+
#endif /* __jack_internal_h__ */
diff --git a/jack/jack.h b/jack/jack.h
index b7c8207..2c36edf 100644
--- a/jack/jack.h
+++ b/jack/jack.h
@@ -62,26 +62,41 @@ int jack_client_close (jack_client_t *client);
void jack_on_shutdown (jack_client_t *client, void (*function)(void *arg), void *arg);
/**
- * Tell the Jack serve to call 'process()' whenever there is work
- * be done.
+ * Tell the Jack serve to call 'process_callback' whenever there is work
+ * be done, passing 'arg' as the second argument.
*/
-int jack_set_process_callback (jack_client_t *, JackProcessCallback, void *arg);
+int jack_set_process_callback (jack_client_t *, JackProcessCallback process_callback, void *arg);
/**
- * Tell the Jack server to call 'bufsize()' whenever the
- * maximum number of frames that will be passed to 'process()'
- * changes.
+ * Tell the Jack server to call 'bufsize_callback' whenever the
+ * maximum number of frames that could be passed to 'process()'
+ * changes. 'arg' will be supplied as a second argument.
*/
-int jack_set_buffer_size_callback (jack_client_t *, JackBufferSizeCallback, void *arg);
+int jack_set_buffer_size_callback (jack_client_t *, JackBufferSizeCallback bufsize_callback, void *arg);
/**
- * Tell the Jack server to call 'srate()' whenver the sample rate of
+ * Tell the Jack server to call 'srate_callback' whenver the sample rate of
* the system changes.
*/
-int jack_set_sample_rate_callback (jack_client_t *, JackSampleRateCallback, void *arg);
+int jack_set_sample_rate_callback (jack_client_t *, JackSampleRateCallback srate_callback, void *arg);
-int jack_set_port_registration_callback (jack_client_t *, JackPortRegistrationCallback, void *);
-int jack_set_graph_order_callback (jack_client_t *, JackGraphOrderCallback, void *);
+/**
+ * Tell the Jack server to call 'registration_callback' whenver the a Port is registered
+ * or unregistered, passing 'arg' as a second argument.
+ */
+int jack_set_port_registration_callback (jack_client_t *, JackPortRegistrationCallback registration_callback, void *arg);
+
+/**
+ * Tell the Jack server to call 'registration_callback' whenever the processing
+ * graph is reordered, passing 'arg' as an argument.
+ */
+int jack_set_graph_order_callback (jack_client_t *, JackGraphOrderCallback graph_callback, void *);
+
+/**
+ * Tell the Jack serve to call 'xrun_callback' whenever there is a xrun, passing
+ * 'arg' as an argument.
+ */
+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
@@ -92,24 +107,42 @@ int jack_activate (jack_client_t *client);
/**
* Tells the Jack server that the program should be removed from the
- * processing graph.
+ * processing graph. As a side effect, this will disconnect any
+ * and all ports belonging to the client, since inactive clients
+ * are not allowed to be connected to any other ports.
*/
int jack_deactivate (jack_client_t *client);
-
/**
- A port has a set of flags, enumerated below and passed as the third
- argument in the form of a bitmask created by AND-ing together the
- desired flags. The flags "IsInput" and "IsOutput" are mutually
- exclusive and it is an error to use them both.
+ 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,
- JackPortIsPhysical = 0x4, /* refers to a physical connection */
+
+ /**
+ * 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
@@ -143,11 +176,10 @@ enum JackPortFlags {
*/
JackPortIsTerminal = 0x10
-
};
/**
- * Used for the flag argument of jack_port_register().
+ * Used for the type argument of jack_port_register().
*/
#define JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
@@ -170,6 +202,8 @@ enum JackPortFlags {
JACK_DEFAULT_AUDIO_TYPE) the client MUST supply a non-zero size
for the buffer as the fourth argument. For builtin types, the
fourth argument is ignored.
+
+ The flags argument is formed from a bitmask of JackPortFlags values.
*/
jack_port_t *
diff --git a/jack/types.h b/jack/types.h
index b66295d..f91e15c 100644
--- a/jack/types.h
+++ b/jack/types.h
@@ -34,6 +34,7 @@ static const nframes_t max_frames = ULONG_MAX;
typedef int (*JackProcessCallback)(nframes_t, void *);
typedef int (*JackGraphOrderCallback)(void *);
+typedef int (*JackXRunCallback)(void *);
typedef int (*JackBufferSizeCallback)(nframes_t, void *);
typedef int (*JackSampleRateCallback)(nframes_t, void *);
typedef void (*JackPortRegistrationCallback)(jack_port_id_t,int,void*);
diff --git a/jackd.c b/jackd.c
index 530e129..cd717af 100644
--- a/jackd.c
+++ b/jackd.c
@@ -46,6 +46,76 @@ typedef struct {
char **argv;
} waiter_arg_t;
+#define ILOWER 0
+#define IRANGE 3000
+int wait_times[IRANGE];
+int unders = 0;
+int overs = 0;
+int max_over = 0;
+int min_under = INT_MAX;
+
+#define WRANGE 3000
+int work_times[WRANGE];
+int work_overs = 0;
+int work_max = 0;
+
+void
+store_work_time (int howlong)
+{
+ if (howlong < WRANGE) {
+ work_times[howlong]++;
+ } else {
+ work_overs++;
+ }
+
+ if (work_max < howlong) {
+ work_max = howlong;
+ }
+}
+
+void
+show_work_times ()
+{
+ int i;
+ for (i = 0; i < WRANGE; ++i) {
+ printf ("%d %d\n", i, work_times[i]);
+ }
+ printf ("work overs = %d\nmax = %d\n", work_overs, work_max);
+}
+
+void
+store_wait_time (int interval)
+{
+ if (interval < ILOWER) {
+ unders++;
+ } else if (interval >= ILOWER + IRANGE) {
+ overs++;
+ } else {
+ wait_times[interval-ILOWER]++;
+ }
+
+ if (interval > max_over) {
+ max_over = interval;
+ }
+
+ if (interval < min_under) {
+ min_under = interval;
+ }
+}
+
+void
+show_wait_times ()
+{
+ int i;
+
+ for (i = 0; i < IRANGE; i++) {
+ printf ("%d %d\n", i+ILOWER, wait_times[i]);
+ }
+
+ printf ("unders: %d\novers: %d\n", unders, overs);
+ printf ("max: %d\nmin: %d\n", max_over, min_under);
+}
+
static void
signal_handler (int sig)
{
@@ -205,6 +275,8 @@ jack_main (int argc, char **argv)
while(1) {
err = sigwait (&signals, &sig);
+
+ printf ("jack main caught signal %d\n", sig);
if (sig == SIGUSR1) {
jack_dump_configuration(engine, 1);
@@ -213,7 +285,7 @@ jack_main (int argc, char **argv)
break;
}
}
-
+
pthread_cancel (waiter_thread);
jack_engine_delete (engine);
@@ -237,7 +309,7 @@ int
main (int argc, char *argv[])
{
- const char *options = "ad:D:P:vhRF";
+ const char *options = "ad:D:P:vhRFl:";
struct option long_options[] =
{
{ "asio", 0, 0, 'a' },
diff --git a/simple_client.c b/simple_client.c
index 07e14c4..0ce1b0c 100644
--- a/simple_client.c
+++ b/simple_client.c
@@ -108,7 +108,9 @@ main (int argc, char *argv[])
}
/* connect the ports. Note: you can't do this before
- the client is activated (this may change in the future).
+ the client is activated, because we can't allow
+ connections to be made to clients that aren't
+ running.
*/
if (jack_connect (client, "alsa_pcm:in_1", jack_port_name (input_port))) {