diff options
-rw-r--r-- | Makefile.am | 11 | ||||
-rw-r--r-- | alsa_driver.c | 115 | ||||
-rw-r--r-- | client.c | 86 | ||||
-rw-r--r-- | configure.in | 10 | ||||
-rw-r--r-- | driver.c | 2 | ||||
-rw-r--r-- | engine.c | 182 | ||||
-rw-r--r-- | jack/alsa_driver.h | 4 | ||||
-rw-r--r-- | jack/driver.h | 4 | ||||
-rw-r--r-- | jack/engine.h | 11 | ||||
-rw-r--r-- | jack/internal.h | 7 | ||||
-rw-r--r-- | jack/jack.h | 74 | ||||
-rw-r--r-- | jack/types.h | 1 | ||||
-rw-r--r-- | jackd.c | 76 | ||||
-rw-r--r-- | simple_client.c | 4 |
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? */ @@ -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) - @@ -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; } @@ -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*); @@ -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))) { |