From 8d8b669c660e99297cd008cb948faf293899771d Mon Sep 17 00:00:00 2001 From: pbd Date: Mon, 28 Jan 2002 03:51:28 +0000 Subject: [PATCH] many changes related to integration of ardour git-svn-id: svn+ssh://jackaudio.org/trunk/jack@89 0c269be4-1314-0410-8aa9-9f06e86f4224 --- Makefile.am | 7 +- TODO | 9 +- alsa_driver.c | 183 +++++++++----------------- capture_client.c | 2 +- client.c | 319 +++++++++++++++++++++++++++++++++++---------- driver.c | 73 +---------- engine.c | 263 +++++++++++++------------------------ fltk_client.cc | 5 +- jack.pc.in | 2 +- jack/alsa_driver.h | 14 +- jack/driver.h | 19 +-- jack/internal.h | 31 +++-- jack/jack.h | 93 +++++++++++-- jack/port.h | 9 +- jack/types.h | 5 +- jackd.c | 15 ++- monitor_client.c | 8 +- pool.c | 6 + simple_client.c | 4 +- 19 files changed, 579 insertions(+), 488 deletions(-) diff --git a/Makefile.am b/Makefile.am index 2912189..fabd5a2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,7 +8,8 @@ SUBDIRS = jack EXTRA_PROGRAMS = jack_fltk_client jackrec -bin_PROGRAMS = jackd jack_simple_client jack_monitor_client @XTRA@ +bin_PROGRAMS = jackd jack_simple_client jack_monitor_client \ + jack_impulse_grabber @XTRA@ AM_CFLAGS = $(JACK_CFLAGS) -DADDON_DIR=\"$(ADDON_DIR)\" @GLIB_CFLAGS@ @@ -34,6 +35,10 @@ jackrec_SOURCES = capture_client.c jackrec_LDFLAGS = -lsndfile -ldl -lpthread jackrec_LDADD = libjack.la +jack_impulse_grabber_SOURCES = impulse_grabber.c +jack_impulse_grabber_LDFLAGS = -ldl -lpthread -lm +jack_impulse_grabber_LDADD = libjack.la + lib_LTLIBRARIES = libjack.la jack_alsa.la libjack_la_SOURCES = client.c pool.c driver.c diff --git a/TODO b/TODO index fccfac3..e7ddb6e 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,10 @@ TODO -fix graph sorting to use same approach as in ARDOUR::Session for - routes +* silence ports when they are disconnected from for the last time +* use a VSTTimeInfo-like structure for time info +* call time client before all others +* support frames_since_interrupt() +* fix monitor system to be exactly like audioengine's figure out how to have pools of buffers for ports by type figure out how to use the same buffer over and over when possible @@ -16,5 +19,3 @@ TO THINK ABOUT: multiple port buffer shm segments (i.e. dynamically increase the total number of ports in the system) -wingo (apwingo@eos.ncsu.edu) adds: - - add helper functions to access _jack_port_shared without requiring glib diff --git a/alsa_driver.c b/alsa_driver.c index 2d3d75d..9986e5f 100644 --- a/alsa_driver.c +++ b/alsa_driver.c @@ -24,9 +24,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -38,12 +38,6 @@ static int config_max_level = 0; static int config_min_level = 0; -static unsigned long long current_cycles () { - unsigned long long now; - rdtscll (now); - return now; -} - static void alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) @@ -62,11 +56,6 @@ alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) free (driver->silent); driver->silent = 0; } - - if (driver->input_monitor_requests) { - free (driver->input_monitor_requests); - driver->input_monitor_requests = 0; - } } static int @@ -118,7 +107,7 @@ alsa_driver_generic_hardware (alsa_driver_t *driver) } static int -alsa_driver_hw_specific (alsa_driver_t *driver) +alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring) { int err; @@ -135,13 +124,12 @@ alsa_driver_hw_specific (alsa_driver_t *driver) if (driver->hw->capabilities & Cap_HardwareMonitoring) { driver->has_hw_monitoring = TRUE; + /* XXX need to ensure that this is really FALSE or TRUE or whatever*/ + driver->hw_monitoring = hw_monitoring; } else { driver->has_hw_monitoring = FALSE; + driver->hw_monitoring = FALSE; } - - /* XXX need to ensure that this is really FALSE */ - - driver->hw_monitoring = FALSE; if (driver->hw->capabilities & Cap_ClockLockReporting) { driver->has_clock_sync_reporting = TRUE; @@ -460,9 +448,6 @@ alsa_driver_set_parameters (alsa_driver_t *driver, nframes_t frames_per_cycle, n driver->silent[chn] = 0; } - driver->input_monitor_requests = (unsigned long *) malloc (sizeof (unsigned long) * driver->max_nchannels); - memset (driver->input_monitor_requests, 0, sizeof (unsigned long) * driver->max_nchannels); - driver->clock_sync_data = (ClockSyncStatus *) malloc (sizeof (ClockSyncStatus) * driver->capture_nchannels > driver->playback_nchannels ? driver->capture_nchannels : driver->playback_nchannels); @@ -607,8 +592,10 @@ alsa_driver_audio_start (alsa_driver_t *driver) driver->playback_nfds = snd_pcm_poll_descriptors_count (driver->playback_handle); driver->capture_nfds = snd_pcm_poll_descriptors_count (driver->capture_handle); + if (driver->pfd) + free (driver->pfd); driver->pfd = (struct pollfd *) malloc (sizeof (struct pollfd) * - (driver->playback_nfds + driver->capture_nfds)); + (driver->playback_nfds + driver->capture_nfds + 1)); return 0; } @@ -675,7 +662,7 @@ alsa_driver_silence_untouched_channels (alsa_driver_t *driver, nframes_t nframes for (chn = 0; chn < driver->playback_nchannels; chn++) { if ((driver->channels_not_done & (1<silent[chn] < driver->buffer_frames) { - alsa_driver_silence_on_channel (driver, chn, nframes); + alsa_driver_silence_on_channel_no_mark (driver, chn, nframes); driver->silent[chn] += nframes; } } @@ -709,7 +696,6 @@ alsa_driver_wait (alsa_driver_t *driver) int need_capture = 1; int need_playback = 1; int i; - unsigned long long before; again: @@ -731,6 +717,11 @@ alsa_driver_wait (alsa_driver_t *driver) ci = nfds; nfds += driver->capture_nfds; } + + if (need_capture != need_playback) { + fprintf (stderr, "poll needs capture: %s playback: %s\n", need_capture ? "yes":"no", + need_playback ? "yes":"no"); + } /* ALSA doesn't set POLLERR in some versions of 0.9.X */ @@ -738,10 +729,6 @@ alsa_driver_wait (alsa_driver_t *driver) driver->pfd[nfds].events |= POLLERR; } -// printf ("c? %d p? %d poll on %d fds\n", need_capture, need_playback, nfds); - - before = current_cycles(); - if (poll (driver->pfd, nfds, 1000) < 0) { if (errno == EINTR) { printf ("poll interrupt\n"); @@ -756,13 +743,13 @@ alsa_driver_wait (alsa_driver_t *driver) jack_error ("ALSA::Device: poll call failed (%s)", strerror (errno)); return -1; } - - driver->time_at_interrupt = current_cycles(); -// printf ("time in poll: %f usecs since last = %f usecs\n", -// ((float) (driver->time_at_interrupt - before)/450.0f), -// ((float) (driver->time_at_interrupt - last_time)/450.0f)); -// last_time = driver->time_at_interrupt; - + + if (driver->engine) { + struct timeval tv; + gettimeofday (&tv, NULL); + driver->engine->control->time.microseconds = tv.tv_sec * 1000000 + tv.tv_usec; + } + p_timed_out = 0; if (need_playback) { @@ -827,11 +814,7 @@ alsa_driver_wait (alsa_driver_t *driver) } if (xrun_detected) { - if (alsa_driver_xrun_recovery (driver)) { - return -1; - } else { - return 0; - } + return alsa_driver_xrun_recovery (driver); } avail = capture_avail < playback_avail ? capture_avail : playback_avail; @@ -854,11 +837,8 @@ alsa_driver_wait (alsa_driver_t *driver) return -1; } - contiguous = capture_avail < playback_avail ? capture_avail : playback_avail; -// printf ("\tcontiguous = %lu\n", contiguous); - /* XXX possible race condition here with silence_pending */ /* XXX this design is wrong. cf. ardour/audioengine *** FIX ME *** */ @@ -871,21 +851,15 @@ alsa_driver_wait (alsa_driver_t *driver) } driver->silence_pending = 0; } - + driver->channels_not_done = driver->channel_done_bits; - - if ((driver->hw->input_monitor_mask != driver->input_monitor_mask) && - driver->hw_monitoring && !driver->all_monitor_in) { - driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask); - } - + /* XXX race condition on engine ptr */ if (driver->engine && driver->engine->process (driver->engine, contiguous)) { jack_error ("alsa_pcm: engine processing error - stopping."); return -1; } - /* now move data from ports to channels */ for (chn = 0, node = driver->playback_ports; node; node = g_slist_next (node), chn++) { @@ -904,7 +878,16 @@ alsa_driver_wait (alsa_driver_t *driver) /* Now handle input monitoring */ + driver->input_monitor_mask = 0; + + for (chn = 0, node = driver->capture_ports; node; node = g_slist_next (node), chn++) { + if (((jack_port_t *) node->data)->shared->monitor_requests) { + driver->input_monitor_mask |= (1<hw_monitoring) { + if (driver->all_monitor_in) { for (chn = 0; chn < driver->playback_nchannels; chn++) { alsa_driver_copy_channel (driver, chn, chn, contiguous); @@ -916,20 +899,26 @@ alsa_driver_wait (alsa_driver_t *driver) } } } + + } else { + + if ((driver->hw->input_monitor_mask != driver->input_monitor_mask) && !driver->all_monitor_in) { + driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask); + } } if (driver->channels_not_done) { alsa_driver_silence_untouched_channels (driver, contiguous); } - + snd_pcm_mmap_commit (driver->capture_handle, capture_offset, contiguous); snd_pcm_mmap_commit (driver->playback_handle, playback_offset, contiguous); - + avail -= contiguous; } -// end = current_cycles(); -// printf ("entire cycle took %f usecs\n", ((float)(end - driver->time_at_interrupt))/450.0f); +// rdtscl (now); +// fprintf (stderr, "engine cycle took %.6f usecs\n", (((float) (now - start))/450.0f)); return 0; } @@ -957,18 +946,6 @@ alsa_driver_process (nframes_t nframes, void *arg) return 0; } -static void -alsa_driver_port_monitor_handler (jack_port_id_t port_id, int onoff, void *arg) -{ - alsa_driver_t *driver = (alsa_driver_t *) arg; - jack_port_shared_t *port; - int channel; - - port = &driver->engine->control->ports[port_id]; - sscanf (port->name, "%*s%*s%*s%d", &channel); - driver->request_monitor_input ((jack_driver_t *) driver, channel, onoff); -} - static void alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine) @@ -990,7 +967,6 @@ alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine) } jack_set_process_callback (driver->client, alsa_driver_process, driver); - jack_set_port_monitor_callback (driver->client, alsa_driver_port_monitor_handler, driver); for (chn = 0; chn < driver->capture_nchannels; chn++) { snprintf (buf, sizeof(buf) - 1, "in_%lu", chn+1); @@ -1001,6 +977,12 @@ alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine) jack_error ("ALSA: cannot register port for %s", buf); break; } + + /* XXX fix this so that it can handle: systemic (external) latency + */ + + jack_port_set_latency (port, driver->frames_per_cycle * driver->nfragments); + driver->capture_ports = g_slist_append (driver->capture_ports, port); } @@ -1013,6 +995,12 @@ alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine) jack_error ("ALSA: cannot register port for %s", buf); break; } + + /* XXX fix this so that it can handle: systemic (external) latency + */ + + jack_port_set_latency (port, driver->frames_per_cycle * driver->nfragments); + driver->playback_ports = g_slist_append (driver->playback_ports, port); } @@ -1061,45 +1049,6 @@ alsa_driver_mark_channel_silent (alsa_driver_t *driver, unsigned long chn) driver->silence_pending |= (1<= driver->max_nchannels) { - return; - } - - changed = FALSE; - - if (yn) { - if (++driver->input_monitor_requests[chn] == 1) { - if (!(driver->input_monitor_mask & (1<input_monitor_mask |= (1<input_monitor_requests[chn] && --driver->input_monitor_requests[chn] == 0) { - if (driver->input_monitor_mask & (1<input_monitor_mask &= ~(1<hw_monitoring && !yn) { - alsa_driver_mark_channel_silent (driver, chn); - } - - /* Tell anyone who cares about the state of input monitoring */ - - jack_driver_input_monitor_notify ((jack_driver_t *) driver, chn, yn); - } -} - static void alsa_driver_request_all_monitor_input (alsa_driver_t *driver, int yn) @@ -1133,12 +1082,6 @@ alsa_driver_set_hw_monitoring (alsa_driver_t *driver, int yn) } } -static nframes_t -alsa_driver_frames_since_cycle_start (alsa_driver_t *driver) -{ - return (nframes_t) ((driver->frame_rate / 1000000.0) * ((float) (current_cycles() - driver->time_at_interrupt))); -} - static ClockSyncStatus alsa_driver_clock_sync_status (channel_t chn) @@ -1199,13 +1142,16 @@ alsa_driver_delete (alsa_driver_t *driver) static jack_driver_t * alsa_driver_new (char *name, char *alsa_device, nframes_t frames_per_cycle, - nframes_t rate) + nframes_t rate, + int hw_monitoring) { int err; alsa_driver_t *driver; - printf ("creating alsa driver ... %s|%lu|%lu\n", alsa_device, frames_per_cycle, rate); + printf ("creating alsa driver ... %s|%lu|%lu|%s\n", + alsa_device, frames_per_cycle, rate, + hw_monitoring ? "hwmon":"swmon"); driver = (alsa_driver_t *) calloc (1, sizeof (alsa_driver_t)); @@ -1220,9 +1166,7 @@ alsa_driver_new (char *name, char *alsa_device, driver->set_hw_monitoring = (JackDriverSetHwMonitoringFunction) alsa_driver_set_hw_monitoring ; driver->reset_parameters = (JackDriverResetParametersFunction) alsa_driver_reset_parameters; driver->mark_channel_silent = (JackDriverMarkChannelSilentFunction) alsa_driver_mark_channel_silent; - driver->request_monitor_input = (JackDriverRequestMonitorInputFunction) alsa_driver_request_monitor_input; driver->request_all_monitor_input = (JackDriverRequestAllMonitorInputFunction) alsa_driver_request_all_monitor_input; - driver->frames_since_cycle_start = (JackDriverFramesSinceCycleStartFunction) alsa_driver_frames_since_cycle_start; driver->clock_sync_status = (JackDriverClockSyncStatusFunction) alsa_driver_clock_sync_status; driver->change_sample_clock = (JackDriverChangeSampleClockFunction) alsa_driver_change_sample_clock; @@ -1238,7 +1182,6 @@ alsa_driver_new (char *name, char *alsa_device, driver->capture_addr = 0; driver->silence_pending = 0; driver->silent = 0; - driver->input_monitor_requests = 0; driver->all_monitor_in = FALSE; driver->clock_mode = ClockMaster; /* XXX is it? */ @@ -1310,7 +1253,7 @@ alsa_driver_new (char *name, char *alsa_device, driver->capture_and_playback_not_synced = FALSE; } - alsa_driver_hw_specific (driver); + alsa_driver_hw_specific (driver, hw_monitoring); return (jack_driver_t *) driver; } @@ -1323,12 +1266,14 @@ driver_initialize (va_list ap) nframes_t srate; nframes_t frames_per_interrupt; char *pcm_name; + int hw_monitoring; pcm_name = va_arg (ap, char *); frames_per_interrupt = va_arg (ap, nframes_t); srate = va_arg (ap, nframes_t); + hw_monitoring = va_arg (ap, int); - return alsa_driver_new ("alsa_pcm", pcm_name, frames_per_interrupt, srate); + return alsa_driver_new ("alsa_pcm", pcm_name, frames_per_interrupt, srate, hw_monitoring); } void diff --git a/capture_client.c b/capture_client.c index 3e5a875..c159d06 100644 --- a/capture_client.c +++ b/capture_client.c @@ -283,7 +283,7 @@ setup_ports (int sources, char *source_names[], thread_info_t *info) } for (i = 0; i < nports; i++) { - if (jack_port_connect (info->client, source_names[i], jack_port_name (ports[i]))) { + if (jack_connect (info->client, source_names[i], jack_port_name (ports[i]))) { fprintf (stderr, "cannot connect input port %s to %s\n", jack_port_name (ports[i]), source_names[i]); jack_client_close (info->client); exit (1); diff --git a/client.c b/client.c index 22fbeb6..ba2311b 100644 --- a/client.c +++ b/client.c @@ -26,10 +26,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include @@ -51,6 +54,13 @@ static pthread_mutex_t client_lock; static pthread_cond_t client_ready; static void *jack_zero_filled_buffer = 0; +static void jack_audio_port_mixdown (jack_port_t *port, nframes_t nframes); + +jack_port_type_info_t builtin_port_types[] = { + { JACK_DEFAULT_AUDIO_TYPE, jack_audio_port_mixdown, 1 }, + { "", NULL } +}; + struct _jack_client { jack_control_t *engine; @@ -131,23 +141,41 @@ jack_port_by_id (jack_client_t *client, jack_port_id_t id) return NULL; } -static jack_port_id_t -jack_port_id_by_name (jack_client_t *client, const char *port_name) +static jack_port_shared_t * +jack_shared_port_by_name (jack_client_t *client, const char *port_name) { - jack_port_id_t id, limit; + unsigned long i, limit; jack_port_shared_t *port; limit = client->engine->port_max; port = &client->engine->ports[0]; - for (id = 0; id < limit; id++) { - if (port[id].in_use && strcmp (port[id].name, port_name) == 0) { - return port[id].id; + for (i = 0; i < limit; i++) { + if (port[i].in_use && strcmp (port[i].name, port_name) == 0) { + return &port[i]; } } - return NoPort; + return NULL; +} + +jack_port_t * +jack_port_by_name (jack_client_t *client, const char *port_name) +{ + unsigned long i, limit; + jack_port_shared_t *port; + + limit = client->engine->port_max; + port = &client->engine->ports[0]; + + for (i = 0; i < limit; i++) { + if (port[i].in_use && strcmp (port[i].name, port_name) == 0) { + return jack_port_new (client, port[i].id, client->engine); + } + } + + return NULL; } static void @@ -167,8 +195,11 @@ jack_client_invalidate_port_buffers (jack_client_t *client) port = (jack_port_t *) node->data; if (port->shared->flags & JackPortIsInput) { - /* XXX release buffer */ - port->client_segment_base = NULL; + if (port->client_segment_base != 0 && port->shared->offset == 0) { + jack_pool_release ((void *) port->shared->offset); + port->client_segment_base = 0; + port->shared->offset = 0; + } } } } @@ -196,7 +227,6 @@ jack_client_handle_port_connection (jack_client_t *client, jack_event_t *event) other = (jack_port_t *) node->data; if (other->shared->id == event->y.other_id) { - printf ("%s DIS-connecting and %s\n", control_port->shared->name, other->shared->name); control_port->connections = g_slist_remove_link (control_port->connections, node); g_slist_free_1 (node); free (other); @@ -541,19 +571,6 @@ jack_client_thread (void *arg) case NewPortBufferSegment: break; - - case PortMonitor: - if (control->port_monitor) { - control->port_monitor (event.x.port_id, TRUE, control->port_monitor_arg); - } - break; - - case PortUnMonitor: - if (control->port_monitor) { - control->port_monitor (event.x.port_id, FALSE, control->port_monitor_arg); - } - break; - } if (write (client->event_fd, &status, sizeof (status)) != sizeof (status)) { @@ -632,6 +649,11 @@ jack_start_thread (jack_client_t *client) jack_error ("Cannot set scheduling priority for RT thread (%s)", strerror (errno)); return -1; } + + if (mlockall (MCL_CURRENT|MCL_FUTURE)) { + jack_error ("cannot lock down all memory (%s)", strerror (errno)); + return -1; + } } if (pthread_create (&client->thread, attributes, jack_client_thread, client)) { @@ -882,7 +904,7 @@ unsigned long jack_get_buffer_size (jack_client_t *client) unsigned long jack_get_sample_rate (jack_client_t *client) { - return client->engine->sample_rate; + return client->engine->time.frame_rate; } static jack_port_t * @@ -898,7 +920,7 @@ jack_port_new (jack_client_t *client, jack_port_id_t port_id, jack_control_t *co port = (jack_port_t *) malloc (sizeof (jack_port_t)); - port->client_segment_base = NULL; + port->client_segment_base = 0; port->shared = shared; port->connections = 0; port->tied = NULL; @@ -933,16 +955,8 @@ jack_port_register (jack_client_t *client, { jack_request_t req; jack_port_t *port = 0; - - /* before we get started, check a few basics */ - - if (flags & JackPortCanMonitor) { - if (client->control->port_monitor == NULL) { - jack_error ("you cannot register ports with PortCanMonitor " - "without a port monitor callback"); - return NULL; - } - } + jack_port_type_info_t *type_info; + int n; req.type = RegisterPort; @@ -971,6 +985,32 @@ jack_port_register (jack_client_t *client, port = jack_port_new (client, req.x.port_info.port_id, client->engine); + type_info = NULL; + + for (n = 0; builtin_port_types[n].type_name[0]; n++) { + + if (strcmp (req.x.port_info.type, builtin_port_types[n].type_name) == 0) { + type_info = &builtin_port_types[n]; + break; + } + } + + if (type_info == NULL) { + + /* not a builtin type, so allocate a new type_info structure, + and fill it appropriately. + */ + + type_info = (jack_port_type_info_t *) malloc (sizeof (jack_port_type_info_t)); + + snprintf ((char *) type_info->type_name, sizeof (type_info->type_name), req.x.port_info.type); + + type_info->mixdown = NULL; /* we have no idea how to mix this */ + type_info->buffer_scale_factor = -1; /* use specified port buffer size */ + } + + memcpy (&port->shared->type_info, type_info, sizeof (jack_port_type_info_t)); + client->ports = g_slist_prepend (client->ports, port); return port; @@ -1000,7 +1040,7 @@ jack_port_unregister (jack_client_t *client, jack_port_t *port) } int -jack_port_connect (jack_client_t *client, const char *source_port, const char *destination_port) +jack_connect (jack_client_t *client, const char *source_port, const char *destination_port) { jack_request_t req; @@ -1026,8 +1066,7 @@ jack_port_connect (jack_client_t *client, const char *source_port, const char *d } int -jack_port_disconnect (jack_client_t *client, const char *source_port, const char *destination_port) - +jack_disconnect (jack_client_t *client, const char *source_port, const char *destination_port) { jack_request_t req; @@ -1086,6 +1125,18 @@ jack_set_error_function (void (*func) (const char *, ...)) jack_error = func; } +nframes_t +jack_port_get_latency (jack_port_t *port) +{ + return port->shared->latency; +} + +void +jack_port_set_latency (jack_port_t *port, nframes_t nframes) +{ + port->shared->latency = nframes; +} + void * jack_port_get_buffer (jack_port_t *port, nframes_t nframes) @@ -1128,8 +1179,7 @@ jack_port_get_buffer (jack_port_t *port, nframes_t nframes) during the connection process. */ - if (port->client_segment_base == NULL) { - port->client_segment_base = 0; + if (port->client_segment_base == 0 && port->shared->offset == 0) { port->shared->offset = (size_t) jack_pool_alloc (port->shared->type_info.buffer_scale_factor * sizeof (sample_t) * nframes); } @@ -1139,7 +1189,7 @@ jack_port_get_buffer (jack_port_t *port, nframes_t nframes) } int -jack_port_tie (jack_port_t *dst, jack_port_t *src) +jack_port_tie (jack_port_t *src, jack_port_t *dst) { if (dst->shared->client_id != src->shared->client_id) { @@ -1209,7 +1259,7 @@ jack_set_sample_rate_callback (jack_client_t *client, JackSampleRateCallback cal /* Now invoke it */ - callback (client->engine->sample_rate, arg); + callback (client->engine->time.frame_rate, arg); return 0; } @@ -1226,18 +1276,6 @@ jack_set_port_registration_callback(jack_client_t *client, JackPortRegistrationC return 0; } -int -jack_set_port_monitor_callback (jack_client_t *client, JackPortMonitorCallback callback, void *arg) - -{ - if (client->control->active) { - return -1; - } - client->control->port_monitor_arg = arg; - client->control->port_monitor = callback; - return 0; -} - int jack_get_process_start_fd (jack_client_t *client) { @@ -1257,34 +1295,103 @@ jack_get_process_done_fd (jack_client_t *client) } int -jack_port_request_monitor (jack_client_t *client, const char *port_name, int onoff) +jack_port_request_monitor_by_name (jack_client_t *client, const char *port_name, int onoff) { - jack_request_t req; - int n; + jack_port_shared_t *port; - req.type = (onoff ? RequestPortMonitor : RequestPortUnMonitor); - req.x.port_info.port_id = jack_port_id_by_name (client, port_name); + if ((port = jack_shared_port_by_name (client, port_name)) != NULL) { + if (onoff) { + port->monitor_requests++; + } else if (port->monitor_requests) { + port->monitor_requests--; + } + return 0; + } + return -1; +} - if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) { - jack_error ("cannot send request to jack server (%s)", strerror (errno)); - return -1; +int +jack_port_request_monitor (jack_port_t *port, int onoff) + +{ + if (onoff) { + port->shared->monitor_requests++; + } else if (port->shared->monitor_requests) { + port->shared->monitor_requests--; } + return 0; +} - if ((n = read (client->request_fd, &req, sizeof (req))) != sizeof (req)) { - jack_error ("cannot read response from jack server (%s)", strerror (errno)); - return -1; +int +jack_ensure_port_monitor_input (jack_port_t *port, int yn) +{ + if (yn) { + if (port->shared->monitor_requests == 0) { + port->shared->monitor_requests++; + } + } else { + if (port->shared->monitor_requests == 1) { + port->shared->monitor_requests--; + } } - return req.status; + return 0; } - + +int +jack_port_monitoring_input (jack_port_t *port) +{ + return port->shared->monitor_requests > 0; +} + const char * jack_port_name (const jack_port_t *port) { return port->shared->name; } +const char * +jack_port_short_name (const jack_port_t *port) +{ + /* we know there is always a colon, because we put + it there ... + */ + + return strchr (port->shared->name, ':') + 1; +} + +int +jack_port_flags (const jack_port_t *port) +{ + return port->shared->flags; +} + +const char * +jack_port_type (const jack_port_t *port) +{ + return port->shared->type_info.type_name; +} + +int +jack_port_equal (const jack_port_t *a, const jack_port_t *b) +{ + return a->shared == b->shared; +} + +int +jack_port_set_name (jack_port_t *port, const char *new_name) +{ + char *colon; + int len; + + colon = strchr (port->shared->name, ':'); + len = sizeof (port->shared->name) - ((int) (colon - port->shared->name)) - 2; + snprintf (colon+1, len, "%s", new_name); + + return 0; +} + void jack_on_shutdown (jack_client_t *client, void (*function)(void *arg), void *arg) { @@ -1309,10 +1416,10 @@ jack_get_ports (jack_client_t *client, engine = client->engine; - if (port_name_pattern) { + if (port_name_pattern && port_name_pattern[0]) { regcomp (&port_regex, port_name_pattern, REG_EXTENDED|REG_NOSUB); } - if (type_name_pattern) { + if (type_name_pattern && type_name_pattern[0]) { regcomp (&type_regex, type_name_pattern, REG_EXTENDED|REG_NOSUB); } @@ -1334,13 +1441,13 @@ jack_get_ports (jack_client_t *client, } } - if (matching && port_name_pattern) { + if (matching && port_name_pattern && port_name_pattern[0]) { if (regexec (&port_regex, psp[i].name, 0, NULL, 0)) { matching = 0; } } - if (matching && type_name_pattern) { + if (matching && type_name_pattern && type_name_pattern[0]) { if (regexec (&type_regex, psp[i].type_info.type_name, 0, NULL, 0)) { matching = 0; } @@ -1353,5 +1460,75 @@ jack_get_ports (jack_client_t *client, matching_ports[match_cnt] = 0; + if (match_cnt == 0) { + free (matching_ports); + matching_ports = 0; + } + return matching_ports; } + +nframes_t +jack_frames_since_cycle_start (jack_client_t *client) +{ + struct timeval now; + float usecs; + + gettimeofday (&now, NULL); + usecs = ((now.tv_sec * 1000000) + now.tv_usec) - client->engine->time.microseconds; + + return (nframes_t) floor ((((float) client->engine->time.frame_rate) / 1000000.0f) * usecs); +} + +int +jack_port_lock (jack_client_t *client, jack_port_t *port) +{ + if (port) { + port->shared->locked = 1; + return 0; + } + return -1; +} + +int +jack_port_unlock (jack_client_t *client, jack_port_t *port) +{ + if (port) { + port->shared->locked = 0; + return 0; + } + return -1; +} + +static void +jack_audio_port_mixdown (jack_port_t *port, nframes_t nframes) +{ + GSList *node; + jack_port_t *input; + nframes_t n; + sample_t *buffer; + sample_t *dst, *src; + + /* by the time we've called this, we've already established + the existence of more than 1 connection to this input port. + */ + + node = port->connections; + input = (jack_port_t *) node->data; + buffer = jack_port_buffer (port); + + memcpy (buffer, jack_port_buffer (input), sizeof (sample_t) * nframes); + + for (node = g_slist_next (node); node; node = g_slist_next (node)) { + + input = (jack_port_t *) node->data; + + n = nframes; + dst = buffer; + src = jack_port_buffer (input); + + while (n--) { + *dst++ += *src++; + } + } +} diff --git a/driver.c b/driver.c index 7733bd9..cc9fd67 100644 --- a/driver.c +++ b/driver.c @@ -29,7 +29,6 @@ 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 int dummy_wait (jack_driver_t *drv) { return 0; } -static nframes_t dummy_frames_since_cycle_start (jack_driver_t *drv) { return 0; } static ClockSyncStatus dummy_clock_sync_status (jack_driver_t *drv, channel_t chn) { return ClockMaster; } static int dummy_audio_stop (jack_driver_t *drv) { return 0; } static int dummy_audio_start (jack_driver_t *drv) { return 0;; } @@ -37,15 +36,8 @@ static void dummy_set_hw_monitoring (jack_driver_t *drv, int yn) { return; } static int dummy_change_sample_clock (jack_driver_t *drv, SampleClockMode mode) { return 0; } static int dummy_reset_parameters (jack_driver_t *drv, nframes_t frames_per_cycle, nframes_t rate) { return 0; } static void dummy_mark_channel_silent (jack_driver_t *drv, unsigned long chn) { return; } -static void dummy_request_monitor_input (jack_driver_t *drv, unsigned long chn, int yn) { return ; } static void dummy_request_all_monitor_input (jack_driver_t *drv, int yn) { return; } -int -jack_driver_monitoring_input (jack_driver_t *driver, channel_t chn) -{ - return chn != NoChannel && (driver->all_monitor_in || (driver->input_monitor_mask & (1<attach = dummy_attach; driver->detach = dummy_detach; driver->wait = dummy_wait; - driver->frames_since_cycle_start = dummy_frames_since_cycle_start; driver->clock_sync_status = dummy_clock_sync_status; driver->audio_stop = dummy_audio_stop; driver->audio_start = dummy_audio_start; @@ -65,9 +56,7 @@ jack_driver_init (jack_driver_t *driver) driver->change_sample_clock = dummy_change_sample_clock; driver->reset_parameters = dummy_reset_parameters; driver->mark_channel_silent = dummy_mark_channel_silent; - driver->request_monitor_input = dummy_request_monitor_input; driver->request_all_monitor_input = dummy_request_all_monitor_input; - driver->monitoring_input = jack_driver_monitoring_input; driver->engine = 0; pthread_mutex_init (&driver->clock_sync_lock, 0); @@ -96,8 +85,8 @@ jack_driver_release (jack_driver_t *driver) int jack_driver_listen_for_clock_sync_status (jack_driver_t *driver, - ClockSyncListenerFunction func, - void *arg) + ClockSyncListenerFunction func, + void *arg) { ClockSyncListener *csl; @@ -132,71 +121,19 @@ jack_driver_stop_listening_to_clock_sync_status (jack_driver_t *driver, int whic return ret; } -int -jack_driver_listen_for_input_monitor_status (jack_driver_t *driver, - InputMonitorListenerFunction func, - void *arg) -{ - InputMonitorListener *iml; - - iml = (InputMonitorListener *) malloc (sizeof (InputMonitorListener)); - iml->function = func; - iml->arg = arg; - iml->id = driver->next_input_monitor_listener_id++; - - pthread_mutex_lock (&driver->input_monitor_lock); - driver->input_monitor_listeners = g_slist_prepend (driver->input_monitor_listeners, iml); - pthread_mutex_unlock (&driver->input_monitor_lock); - return iml->id; -} - -int -jack_driver_stop_listening_to_input_monitor_status (jack_driver_t *driver, int which) - -{ - GSList *node; - int ret = -1; - - pthread_mutex_lock (&driver->input_monitor_lock); - for (node = driver->input_monitor_listeners; node; node = g_slist_next (node)) { - if (((InputMonitorListener *) node->data)->id == which) { - driver->input_monitor_listeners = g_slist_remove_link (driver->input_monitor_listeners, node); - free (node->data); - g_slist_free_1 (node); - ret = 0; - break; - } - } - pthread_mutex_unlock (&driver->input_monitor_lock); - return ret; -} - void jack_driver_clock_sync_notify (jack_driver_t *driver, channel_t chn, ClockSyncStatus status) { GSList *node; - pthread_mutex_lock (&driver->input_monitor_lock); - for (node = driver->input_monitor_listeners; node; node = g_slist_next (node)) { + pthread_mutex_lock (&driver->clock_sync_lock); + for (node = driver->clock_sync_listeners; node; node = g_slist_next (node)) { ClockSyncListener *csl = (ClockSyncListener *) node->data; csl->function (chn, status, csl->arg); } - pthread_mutex_unlock (&driver->input_monitor_lock); - -} - -void jack_driver_input_monitor_notify (jack_driver_t *driver, channel_t chn, int status) -{ - GSList *node; + pthread_mutex_unlock (&driver->clock_sync_lock); - pthread_mutex_lock (&driver->input_monitor_lock); - for (node = driver->input_monitor_listeners; node; node = g_slist_next (node)) { - InputMonitorListener *iml = (InputMonitorListener *) node->data; - iml->function (chn, status, iml->arg); - } - pthread_mutex_unlock (&driver->input_monitor_lock); } - jack_driver_t * jack_driver_load (const char *path_to_so, ...) diff --git a/engine.c b/engine.c index a18b995..7783e85 100644 --- a/engine.c +++ b/engine.c @@ -95,16 +95,11 @@ static void jack_port_registration_notify (jack_engine_t *, jack_port_id_t, int) static int jack_send_connection_notification (jack_engine_t *, jack_client_id_t, jack_port_id_t, jack_port_id_t, int); static int jack_deliver_event (jack_engine_t *, jack_client_internal_t *, jack_event_t *); -static void jack_audio_port_mixdown (jack_port_t *port, nframes_t nframes); +static int jack_get_total_latency (jack_engine_t *engine, const char *portname, nframes_t *latency); static int *jack_shm_registry; static int jack_shm_id_cnt; -jack_port_type_info_t builtin_port_types[] = { - { JACK_DEFAULT_AUDIO_TYPE, jack_audio_port_mixdown, 1 }, - { 0, NULL } -}; - static inline int jack_client_is_inprocess (jack_client_internal_t *client) { @@ -398,7 +393,7 @@ static int jack_set_sample_rate (jack_engine_t *engine, nframes_t nframes) { - engine->control->sample_rate = nframes; + engine->control->time.frame_rate = nframes; return 0; } @@ -412,9 +407,6 @@ jack_process (jack_engine_t *engine, nframes_t nframes) struct pollfd pollfd[1]; char c; - unsigned long then, now; -// rdtscl (then); - if (pthread_mutex_trylock (&engine->graph_lock) != 0) { return 0; } @@ -426,7 +418,7 @@ jack_process (jack_engine_t *engine, nframes_t nframes) } if (engine->timebase_client) { - engine->control->frame_time = engine->timebase_client->control->frame_time; + engine->control->time.frame = engine->timebase_client->control->frame_time; } for (node = engine->clients; err == 0 && node; ) { @@ -472,18 +464,14 @@ jack_process (jack_engine_t *engine, nframes_t nframes) pollfd[0].fd = client->subgraph_wait_fd; pollfd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; - rdtscl (then); if (poll (pollfd, 1, engine->driver->period_interval) < 0) { jack_error ("engine cannot poll for graph completion (%s)", strerror (errno)); err++; break; } - rdtscl (now); if (pollfd[0].revents == 0) { - jack_error ("subgraph starting at %s timed out (state = %d) (time = %f usecs)", - client->control->name, client->control->state, - ((float)(now - then))/450.0f); + jack_error ("subgraph starting at %s timed out (state = %d)", client->control->name, client->control->state); err++; break; } else if (pollfd[0].revents & ~POLLIN) { @@ -514,8 +502,6 @@ jack_process (jack_engine_t *engine, nframes_t nframes) jack_cleanup_clients (engine); } -// rdtscl (now); -// printf ("engine cycle time: %.6f usecs\n", ((float) (now - then)) / 450.00f); return 0; } @@ -574,7 +560,7 @@ handle_new_client (jack_engine_t *engine, int client_fd) { GSList *node; - jack_client_internal_t *client; + jack_client_internal_t *client = NULL; jack_client_connect_request_t req; jack_client_connect_result_t res; @@ -587,6 +573,7 @@ handle_new_client (jack_engine_t *engine, int client_fd) for (node = engine->clients; node; node = g_slist_next (node)) { client = (jack_client_internal_t *) node->data; + if (strncmp(req.name, (char*)client->control->name, sizeof(req.name)) == 0) { jack_error ("cannot create new client; %s already exists", client->control->name); @@ -596,7 +583,7 @@ handle_new_client (jack_engine_t *engine, int client_fd) if (res.status == 0) { - if ((client = jack_client_internal_new (engine, client_fd, &req)) == 0) { + if ((client = jack_client_internal_new (engine, client_fd, &req)) == NULL) { jack_error ("cannot create new client object"); return -1; } @@ -617,8 +604,12 @@ handle_new_client (jack_engine_t *engine, int client_fd) } } + if (client == NULL) { + return -1; + } + if (write (client->request_fd, &res, sizeof (res)) != sizeof (res)) { - jack_error ("cannot write connection response to client"); + jack_error ("cannot write connection response to client"); jack_client_delete (engine, client); return -1; } @@ -693,23 +684,6 @@ jack_client_drop (jack_engine_t *engine, jack_client_id_t id) return 0; } -#if 0 -static int -jack_client_has_connections (jack_client_internal_t *client) - -{ - GSList *node; - - for (node = client->ports; node; node = g_slist_next (node)) { - if (((jack_port_internal_t *) node->data)->connections) { - return TRUE; - } - } - - return FALSE; -} -#endif - static int jack_client_activate (jack_engine_t *engine, jack_client_id_t id) @@ -803,7 +777,7 @@ jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id, int to_wait) if (client == engine->timebase_client) { engine->timebase_client = 0; - engine->control->frame_time = 0; + engine->control->time.frame = 0; } for (portnode = client->ports; portnode; portnode = g_slist_next (portnode)) { @@ -829,7 +803,7 @@ jack_set_timebase (jack_engine_t *engine, jack_client_id_t client) pthread_mutex_lock (&engine->graph_lock); if ((engine->timebase_client = jack_client_internal_by_id (engine, client)) != 0) { - engine->control->frame_time = engine->timebase_client->control->frame_time; + engine->control->time.frame = engine->timebase_client->control->frame_time; ret = 0; } pthread_mutex_unlock (&engine->graph_lock); @@ -863,41 +837,6 @@ handle_client_jack_error (jack_engine_t *engine, int fd) return 0; } -static int -jack_client_port_monitor (jack_engine_t *engine, jack_port_id_t port_id, int onoff) - -{ - jack_port_shared_t *port; - jack_client_internal_t *client = NULL; - jack_event_t event; - - if (port_id < 0 || port_id >= engine->port_max) { - jack_error ("illegal port ID in port monitor request"); - return -1; - } - - port = &engine->control->ports[port_id]; - - if (!(port->flags & JackPortCanMonitor)) { - jack_error ("port monitor request made on a port (%s) that doesn't support monitoring", - port->name); - return -1; - } - - pthread_mutex_lock (&engine->graph_lock); - if ((client = jack_client_internal_by_id (engine, port->client_id)) == NULL) { - jack_error ("unknown client owns port %d!!", port_id); - pthread_mutex_unlock (&engine->graph_lock); - return -1; - } - pthread_mutex_unlock (&engine->graph_lock); - - event.type = (onoff ? PortMonitor : PortUnMonitor); - event.x.port_id = port_id; - - return jack_deliver_event (engine, client, &event); -} - static int handle_client_io (jack_engine_t *engine, int fd) @@ -965,12 +904,14 @@ handle_client_io (jack_engine_t *engine, int fd) req.status = jack_set_timebase (engine, req.x.client_id); break; - case RequestPortMonitor: - req.status = jack_client_port_monitor (engine, req.x.port_info.port_id, TRUE); + case GetPortTotalLatency: + req.status = jack_get_total_latency (engine, req.x.port_info.name, &req.x.nframes); break; - - case RequestPortUnMonitor: - req.status = jack_client_port_monitor (engine, req.x.port_info.port_id, FALSE); + + default: + /* some requests are handled entirely on the client side, + by adjusting the shared memory area(s) + */ break; } @@ -1133,16 +1074,6 @@ jack_engine_new (int realtime, int rtpriority) engine->fifo[i] = -1; } - /* Build a linked list of known port types. We use a list so that - we can easily manage other data types without messing with - reallocation of arrays, etc. - */ - - engine->port_types = NULL; - for (i = 0; builtin_port_types[i].type_name; i++) { - engine->port_types = g_slist_append (engine->port_types, &builtin_port_types[i]); - } - engine->external_client_cnt = 0; srandom (time ((time_t *) 0)); @@ -1195,9 +1126,9 @@ jack_engine_new (int realtime, int rtpriority) engine->control->real_time = realtime; engine->control->client_priority = engine->rtpriority - 1; - engine->control->sample_rate = 0; engine->control->buffer_size = 0; - engine->control->frame_time = 0; + engine->control->time.frame_rate = 0; + engine->control->time.frame = 0; sprintf (engine->fifo_prefix, "%s/ack_fifo_%d", jack_temp_dir, getpid()); @@ -1392,8 +1323,6 @@ jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_req client->control->srate_arg = NULL; client->control->port_register = NULL; client->control->port_register_arg = NULL; - client->control->port_monitor = NULL; - client->control->port_monitor_arg = NULL; if (req->type == ClientDynamic) { if (jack_load_client (engine, client, req->object_path)) { @@ -1439,7 +1368,7 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) if (client == engine->timebase_client) { engine->timebase_client = 0; - engine->control->frame_time = 0; + engine->control->time.frame = 0; } jack_client_disconnect (engine, client); @@ -1563,18 +1492,6 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client, jack_ } break; - case PortMonitor: - if (client->control->port_monitor) { - client->control->port_monitor (event->x.port_id, TRUE, client->control->port_monitor_arg); - } - break; - - case PortUnMonitor: - if (client->control->port_monitor) { - client->control->port_monitor (event->x.port_id, FALSE, client->control->port_monitor_arg); - } - break; - default: /* internal clients don't need to know */ break; @@ -1901,6 +1818,16 @@ jack_port_do_connect (jack_engine_t *engine, return -1; } + if (srcport->shared->locked) { + jack_error ("source port is locked against connection changes"); + return -1; + } + + if (dstport->shared->locked) { + jack_error ("destination port is locked against connection changes"); + return -1; + } + if (strcmp (srcport->shared->type_info.type_name, dstport->shared->type_info.type_name) != 0) { jack_error ("ports used in attemped connection are not of the same data type"); @@ -1964,6 +1891,16 @@ jack_port_disconnect_internal (jack_engine_t *engine, src_id = srcport->shared->id; dst_id = dstport->shared->id; + /* this is a bit harsh, but it basically says that if we actually + do a disconnect, and its the last one, then make sure that + any input monitoring is turned off on the srcport. this isn't + ideal for all situations, but it works better for most of them. + */ + + if (srcport->connections == NULL) { + srcport->shared->monitor_requests = 0; + } + jack_send_connection_notification (engine, srcport->shared->client_id, src_id, dst_id, FALSE); jack_send_connection_notification (engine, dstport->shared->client_id, dst_id, src_id, FALSE); @@ -2010,6 +1947,51 @@ jack_port_do_disconnect (jack_engine_t *engine, return ret; } +static int +jack_port_get_total_latency (jack_engine_t *engine, jack_port_internal_t *port, nframes_t *latency) +{ + GSList *node; + + /* call tree should hold engine->graph_lock. */ + + (*latency) = port->shared->latency; + + for (node = port->connections; node; node = g_slist_next (node)) { + + nframes_t this_latency; + jack_connection_internal_t *connection; + + connection = (jack_connection_internal_t *) node->data; + + /* if we're a destination in the connection, recurse on the source to + get its total latency + */ + + if (connection->destination == port) { + + jack_port_get_total_latency (engine, connection->source, &this_latency); + + if (this_latency > *latency) { + (*latency) = this_latency; + } + } + } + + return 0; +} + +static int +jack_get_total_latency (jack_engine_t *engine, const char *portname, nframes_t *latency) +{ + jack_port_internal_t *port; + + if ((port = jack_get_port_by_name (engine, portname)) == NULL) { + return -1; + } + + return jack_port_get_total_latency (engine, port, latency); +} + static int jack_get_fifo_fd (jack_engine_t *engine, int which_fifo) @@ -2139,12 +2121,10 @@ int jack_port_do_register (jack_engine_t *engine, jack_request_t *req) { - GSList *node; jack_port_id_t port_id; jack_port_shared_t *shared; jack_port_internal_t *port; jack_client_internal_t *client; - jack_port_type_info_t *type_info; pthread_mutex_lock (&engine->graph_lock); if ((client = jack_client_internal_by_id (engine, req->x.port_info.client_id)) == 0) { @@ -2164,42 +2144,16 @@ jack_port_do_register (jack_engine_t *engine, jack_request_t *req) shared->client_id = req->x.port_info.client_id; shared->flags = req->x.port_info.flags; - shared->locked = 0; shared->buffer_size = req->x.port_info.buffer_size; + shared->latency = 0; + shared->monitor_requests = 0; + shared->locked = 0; port = &engine->internal_ports[port_id]; port->shared = shared; port->connections = 0; - type_info = NULL; - - for (node = engine->port_types; node; node = g_slist_next (node)) { - - if (strcmp (req->x.port_info.type, ((jack_port_type_info_t *) node->data)->type_name) == 0) { - type_info = (jack_port_type_info_t *) node->data; - break; - } - } - - if (type_info == NULL) { - - /* not a builtin type, so allocate a new type_info structure, - and fill it appropriately. - */ - - type_info = (jack_port_type_info_t *) malloc (sizeof (jack_port_type_info_t)); - - type_info->type_name = strdup (req->x.port_info.type); - type_info->mixdown = NULL; /* we have no idea how to mix this */ - type_info->buffer_scale_factor = -1; /* use specified port buffer size */ - - engine->port_types = g_slist_prepend (engine->port_types, type_info); - } - - - memcpy (&port->shared->type_info, type_info, sizeof (jack_port_type_info_t)); - if (jack_port_assign_buffer (engine, port)) { jack_error ("cannot assign buffer for port"); return -1; @@ -2288,6 +2242,7 @@ jack_port_assign_buffer (jack_engine_t *engine, jack_port_internal_t *port) port->shared->shm_key = -1; if (port->shared->flags & JackPortIsInput) { + port->shared->offset = 0; return 0; } @@ -2372,35 +2327,3 @@ jack_send_connection_notification (jack_engine_t *engine, jack_client_id_t clien return 0; } -static void -jack_audio_port_mixdown (jack_port_t *port, nframes_t nframes) -{ - GSList *node; - jack_port_t *input; - nframes_t n; - sample_t *buffer; - sample_t *dst, *src; - - /* by the time we've called this, we've already established - the existence of more than 1 connection to this input port. - */ - - node = port->connections; - input = (jack_port_t *) node->data; - buffer = jack_port_buffer (port); - - memcpy (buffer, jack_port_buffer (input), sizeof (sample_t) * nframes); - - for (node = g_slist_next (node); node; node = g_slist_next (node)) { - - input = (jack_port_t *) node->data; - - n = nframes; - dst = buffer; - src = jack_port_buffer (input); - - while (n--) { - *dst++ += *src++; - } - } -} diff --git a/fltk_client.cc b/fltk_client.cc index da68be3..9d62182 100644 --- a/fltk_client.cc +++ b/fltk_client.cc @@ -1,4 +1,5 @@ #include +#include #include #include @@ -81,11 +82,11 @@ main (int argc, char *argv[]) printf ("client activated\n"); - if (jack_port_connect (client, "alsa_pcm:in_1", jack_port_name (input_port))) { + if (jack_connect (client, "alsa_pcm:in_1", jack_port_name (input_port))) { fprintf (stderr, "cannot connect input ports\n"); } - if (jack_port_connect (client, jack_port_name (output_port), "alsa_pcm:out_1")) { + if (jack_connect (client, jack_port_name (output_port), "alsa_pcm:out_1")) { fprintf (stderr, "cannot connect output ports\n"); } diff --git a/jack.pc.in b/jack.pc.in index f3a624e..0ddc6a0 100644 --- a/jack.pc.in +++ b/jack.pc.in @@ -7,5 +7,5 @@ Name: jack Description: the Jack Audio Connection Kit: a low-latency synchronous callback-based media server Requires: glib >= 1.0.0 Version: @JACK_VERSION@ -Libs: -L${libdir} -ljack +Libs: -L${libdir} -ljack -lglib -lpthread -ldl Cflags: -I${includedir} diff --git a/jack/alsa_driver.h b/jack/alsa_driver.h index 8348669..e7dcc1e 100644 --- a/jack/alsa_driver.h +++ b/jack/alsa_driver.h @@ -49,7 +49,6 @@ typedef struct { char **capture_addr; const snd_pcm_channel_area_t *capture_areas; const snd_pcm_channel_area_t *playback_areas; - unsigned long long time_at_interrupt; struct pollfd *pfd; unsigned int playback_nfds; unsigned int capture_nfds; @@ -78,7 +77,6 @@ typedef struct { snd_ctl_t *ctl_handle; snd_pcm_t *playback_handle; snd_pcm_t *capture_handle; - unsigned long *input_monitor_requests; snd_pcm_hw_params_t *playback_hw_params; snd_pcm_sw_params_t *playback_sw_params; snd_pcm_hw_params_t *capture_hw_params; @@ -116,6 +114,18 @@ static __inline__ void alsa_driver_silence_on_channel (alsa_driver_t *driver, ch alsa_driver_mark_channel_done (driver,chn); } +static __inline__ void alsa_driver_silence_on_channel_no_mark (alsa_driver_t *driver, channel_t chn, nframes_t nframes) { + if (driver->interleaved) { + memset_interleave + (driver->playback_addr[chn], + 0, nframes * driver->sample_bytes, + driver->interleave_unit, + driver->playback_interleave_skip); + } else { + memset (driver->playback_addr[chn], 0, nframes * driver->sample_bytes); + } +} + static __inline__ void alsa_driver_read_from_channel (alsa_driver_t *driver, channel_t channel, sample_t *buf, nframes_t nsamples, diff --git a/jack/driver.h b/jack/driver.h index d69cd86..7f8d410 100644 --- a/jack/driver.h +++ b/jack/driver.h @@ -24,6 +24,7 @@ #include #include #include +#include typedef void (*ClockSyncListenerFunction)(channel_t,ClockSyncStatus,void*); @@ -33,14 +34,6 @@ typedef struct { void *arg; } ClockSyncListener; -typedef void (*InputMonitorListenerFunction)(channel_t,int,void*); - -typedef struct { - unsigned long id; - InputMonitorListenerFunction function; - void *arg; -} InputMonitorListener; - struct _jack_engine; struct _jack_driver; @@ -55,9 +48,7 @@ typedef void (*JackDriverSetHwMonitoringFunction) (struct _jack_drive typedef int (*JackDriverChangeSampleClockFunction) (struct _jack_driver *, SampleClockMode mode); typedef int (*JackDriverResetParametersFunction) (struct _jack_driver *, nframes_t frames_per_cycle, nframes_t rate); typedef void (*JackDriverMarkChannelSilentFunction) (struct _jack_driver *, unsigned long chn); -typedef void (*JackDriverRequestMonitorInputFunction) (struct _jack_driver *, unsigned long chn, int yn); typedef void (*JackDriverRequestAllMonitorInputFunction) (struct _jack_driver *, int yn); -typedef int (*JackDriverMonitoringInputFunction)(struct _jack_driver *,channel_t chn); #define JACK_DRIVER_DECL \ nframes_t frame_rate; \ @@ -91,7 +82,6 @@ typedef int (*JackDriverMonitoringInputFunction)(struct _jack_driver could/should be provided by a different kind of object. \ */ \ \ - JackDriverFramesSinceCycleStartFunction frames_since_cycle_start; \ JackDriverClockSyncStatusFunction clock_sync_status; \ JackDriverAudioStopFunction audio_stop; \ JackDriverAudioStartFunction audio_start; \ @@ -99,9 +89,7 @@ typedef int (*JackDriverMonitoringInputFunction)(struct _jack_driver JackDriverChangeSampleClockFunction change_sample_clock; \ JackDriverResetParametersFunction reset_parameters; \ JackDriverMarkChannelSilentFunction mark_channel_silent; \ - JackDriverRequestMonitorInputFunction request_monitor_input; \ JackDriverRequestAllMonitorInputFunction request_all_monitor_input; \ - JackDriverMonitoringInputFunction monitoring_input; typedef struct _jack_driver { @@ -118,11 +106,8 @@ void jack_driver_unload (jack_driver_t *); int jack_driver_listen_for_clock_sync_status (jack_driver_t *, ClockSyncListenerFunction, void *arg); int jack_driver_stop_listen_for_clock_sync_status (jack_driver_t *, int); -int jack_driver_listen_for_input_monitor_status (jack_driver_t *, InputMonitorListenerFunction, void *arg); -int jack_driver_stop_listen_for_input_monitor_status (jack_driver_t *, int); - void jack_driver_clock_sync_notify (jack_driver_t *, channel_t chn, ClockSyncStatus); -void jack_driver_input_monitor_notify (jack_driver_t *, channel_t chn, int); +void jack_driver_input_monitor_notify (jack_driver_t *, jack_port_shared_t *, int); #endif /* __jack_driver_h__ */ diff --git a/jack/internal.h b/jack/internal.h index 49f3a05..ffe814f 100644 --- a/jack/internal.h +++ b/jack/internal.h @@ -48,11 +48,30 @@ typedef struct { char *address; } jack_port_segment_info_t; +typedef struct _time_info +{ + nframes_t frame; + nframes_t frame_rate; + unsigned long microseconds; + + double ppqPos; // 1 ppq + double tempo; // in bpm + double barStartPos; // last bar start, in 1 ppq + double cycleStartPos; // 1 ppq + double cycleEndPos; // 1 ppq + + float timeSigNumerator; // time signature + float timeSigDenominator; + long smpteOffset; + long smpteFrameRate; // 0:24, 1:25, 2:29.97, 3:30, 4:29.97 df, 5:30 df + long samplesToNextClock; // midi clock resolution (24 ppq), can be negative + long flags; // see below +} time_info; + typedef struct { - nframes_t frame_time; + time_info time; pid_t engine_pid; - unsigned long sample_rate; unsigned long buffer_size; char real_time; int client_priority; @@ -70,8 +89,6 @@ typedef enum { GraphReordered, PortRegistered, PortUnregistered, - PortMonitor, - PortUnMonitor, } AudioEngineEventType; typedef struct { @@ -118,8 +135,6 @@ typedef volatile struct { void *srate_arg; JackPortRegistrationCallback port_register; void *port_register_arg; - JackPortMonitorCallback port_monitor; - void *port_monitor_arg; /* for engine use only */ @@ -178,8 +193,7 @@ typedef enum { DropClient = 6, ActivateClient = 7, DeactivateClient = 8, - RequestPortMonitor = 9, - RequestPortUnMonitor = 10 + GetPortTotalLatency = 9 } AudioEngineRequestType; typedef struct { @@ -199,6 +213,7 @@ typedef struct { char destination_port[JACK_PORT_NAME_SIZE+1]; } connect; jack_client_id_t client_id; + nframes_t nframes; } x; int status; } jack_request_t; diff --git a/jack/jack.h b/jack/jack.h index 8937c01..1a95296 100644 --- a/jack/jack.h +++ b/jack/jack.h @@ -51,10 +51,6 @@ int jack_set_process_callback (jack_client_t *, JackProcessCallback, void *arg); int jack_set_buffer_size_callback (jack_client_t *, JackBufferSizeCallback, void *arg); int jack_set_sample_rate_callback (jack_client_t *, JackSampleRateCallback, void *arg); int jack_set_port_registration_callback (jack_client_t *, JackPortRegistrationCallback, void *); -int jack_set_port_monitor_callback (jack_client_t *, JackPortMonitorCallback, void *); - -int jack_get_process_start_fd (jack_client_t *); -int jack_get_process_done_fd (jack_client_t *); int jack_activate (jack_client_t *client); int jack_deactivate (jack_client_t *client); @@ -123,9 +119,16 @@ jack_port_register (jack_client_t *, int jack_port_unregister (jack_client_t *, jack_port_t *); -/* a port is an opaque type, and its name is not inferable */ +/* a port is an opaque type. these help with a few things */ const char * jack_port_name (const jack_port_t *port); +const char * jack_port_short_name (const jack_port_t *port); +int jack_port_flags (const jack_port_t *port); +const char * jack_port_type (const jack_port_t *port); +int jack_port_connected (const jack_port_t *port); +int jack_port_equal (const jack_port_t *a, const jack_port_t *b); + +int jack_port_set_name (jack_port_t *port, const char *name); /* This returns a pointer to the memory area associated with the specified port. It can only be called from within the client's @@ -149,13 +152,27 @@ void *jack_port_get_buffer (jack_port_t *, nframes_t); the flags of the destination port must include PortIsInput. */ -int jack_port_connect (jack_client_t *, - const char *source_port, - const char *destination_port); +int jack_connect (jack_client_t *, + const char *source_port, + const char *destination_port); + +int jack_disconnect (jack_client_t *, + const char *source_port, + const char *destination_port); + +/* these two functions perform the exact same function + as the jack_connect() and jack_disconnect(), but they + use port handles rather than names, which avoids + the name lookup inherent in the name-based versions. + + it is envisaged that clients (dis)connecting their own + ports will use these two, wherease generic connection + clients (e.g. patchbays) will use the name-based + versions +*/ -int jack_port_disconnect (jack_client_t *, - const char *source_port, - const char *destination_port); +int jack_port_connect (jack_client_t *, jack_port_t *src, jack_port_t *dst); +int jack_port_disconnect (jack_client_t *, jack_port_t *); /* A client may call this on a pair of its own ports to semi-permanently wire them together. This means that @@ -171,7 +188,7 @@ int jack_port_disconnect (jack_client_t *, clients. Thats what a connection is for. */ -int jack_port_tie (jack_port_t *dst, jack_port_t *src); +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 @@ -181,19 +198,60 @@ int jack_port_tie (jack_port_t *dst, jack_port_t *src); int jack_port_untie (jack_port_t *port); /* a client may call this function to prevent other objects - from changing the connection status of the named port. + from changing the connection status of a port. the port + must be owned by the calling client. */ int jack_port_lock (jack_client_t *, jack_port_t *); int jack_port_unlock (jack_client_t *, jack_port_t *); +/* returns 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 audio will be audible. + + for a physical audio input port, this is the time between the sound + being audible and the corresponding frames being readable from the + port. +*/ + +nframes_t jack_port_get_latency (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 *, nframes_t); /* if JackPortCanMonitor is set for a port, then this function will turn on/off input monitoring for the port. if JackPortCanMonitor is not set, then this function will do nothing. */ -int jack_port_request_monitor (jack_client_t *, const char *port_name, int onoff); +int jack_port_request_monitor (jack_port_t *port, int onoff); +int jack_port_request_monitor_by_name (jack_client_t *client, const char *port_name, int onoff); + +/* if JackPortCanMonitor is set for a port, then this function will + turn on input monitoring if it was off, and will turn it off it + only one request has been made to turn it on. if JackPortCanMonitor + is not set, then this function will do nothing. +*/ + +int jack_port_ensure_monitor (jack_port_t *port, int onoff); + +/* returns a true or false value depending on whether or not + input monitoring has been requested for `port'. +*/ + +int jack_port_monitoring_input (jack_port_t *port); /* this returns the sample rate of the jack */ @@ -226,6 +284,8 @@ char * const * jack_get_ports (jack_client_t *, const char *type_name_pattern, unsigned long flags); +jack_port_t *jack_port_by_name (jack_client_t *, const char *portname); + /* If a client is told to become the timebase for the entire system, it calls this function. If it returns zero, then the client has the responsibility to call jack_update_time() at the end @@ -237,6 +297,11 @@ char * const * jack_get_ports (jack_client_t *, int jack_engine_takeover_timebase (jack_client_t *); void jack_update_time (jack_client_t *, nframes_t); +/* this estimates the time that has passed since the + start of the current cycle. +*/ + +nframes_t jack_frames_since_cycle_start (jack_client_t *); #ifdef __cplusplus } diff --git a/jack/port.h b/jack/port.h index 25984a3..0537e2b 100644 --- a/jack/port.h +++ b/jack/port.h @@ -25,7 +25,7 @@ #include typedef struct _jack_port_type_info { - const char *type_name; /* what do you think ? */ + const char type_name[32]; /* what do you think ? */ void (*mixdown)(jack_port_t *, nframes_t); /* function to mixdown multiple inputs to a buffer. can be NULL, indicating that multiple input connections @@ -61,9 +61,12 @@ typedef struct _jack_port_shared { char name[JACK_CLIENT_NAME_SIZE+JACK_PORT_NAME_SIZE+2]; jack_port_type_info_t type_info; jack_client_id_t client_id; + nframes_t latency; + unsigned char monitor_requests; + + char in_use : 1; + char locked : 1; - char in_use : 1; - char locked : 1; } jack_port_shared_t; /* This is the data structure allocated by the client diff --git a/jack/types.h b/jack/types.h index 4a26760..0a56cd5 100644 --- a/jack/types.h +++ b/jack/types.h @@ -21,6 +21,8 @@ #ifndef __jack_engine_types_h__ #define __jack_engine_types_h__ +#include + typedef float sample_t; typedef unsigned long nframes_t; typedef long jack_port_id_t; @@ -28,11 +30,12 @@ typedef unsigned long jack_client_id_t; typedef float gain_t; typedef long channel_t; +static const nframes_t max_frames = ULONG_MAX; + typedef int (*JackProcessCallback)(nframes_t, void *); typedef int (*JackBufferSizeCallback)(nframes_t, void *); typedef int (*JackSampleRateCallback)(nframes_t, void *); typedef void (*JackPortRegistrationCallback)(jack_port_id_t,int,void*); -typedef void (*JackPortMonitorCallback)(jack_port_id_t,int,void*); #define NoChannel -1 #define NoPort -1 diff --git a/jackd.c b/jackd.c index 5fe9644..bd7d88c 100644 --- a/jackd.c +++ b/jackd.c @@ -40,6 +40,7 @@ static nframes_t srate = 48000; static int realtime = 0; static int realtime_priority = 10; static int with_fork = 1; +static int hw_monitoring = 0; static void signal_handler (int sig) @@ -123,7 +124,11 @@ jack_engine_waiter_thread (void *arg) return 0; } - if ((driver = jack_driver_load (ADDON_DIR "/jack_alsa.so", alsa_pcm_name, frames_per_interrupt, srate)) == 0) { + if ((driver = jack_driver_load (ADDON_DIR "/jack_alsa.so", + alsa_pcm_name, + frames_per_interrupt, + srate, + hw_monitoring)) == 0) { fprintf (stderr, "cannot load ALSA driver module\n"); kill (signal_pid, SIGTERM); return 0; @@ -203,6 +208,7 @@ static void usage () [ --srate OR -r sample-rate ] [ --frames-per-interrupt OR -p frames_per_interrupt ] [ --realtime OR -R [ --realtime-priority OR -P priority ] ] + [ --hw-monitor OR -h ] [ --spoon OR -F ] (don't fork) "); } @@ -211,7 +217,7 @@ int main (int argc, char *argv[]) { - const char *options = "hd:r:p:RP:FD:"; + const char *options = "hd:r:p:RP:FD:H"; struct option long_options[] = { { "tmpdir", 1, 0, 'D' }, @@ -221,6 +227,7 @@ main (int argc, char *argv[]) { "help", 0, 0, 'h' }, { "realtime", 0, 0, 'R' }, { "realtime-priority", 1, 0, 'P' }, + { "hw-monitor", 0, 0, 'H' }, { "spoon", 0, 0, 'F' }, { 0, 0, 0, 0 } }; @@ -258,6 +265,10 @@ main (int argc, char *argv[]) realtime = 1; break; + case 'H': + hw_monitoring = 1; + break; + case 'h': default: fprintf (stderr, "unknown option character %c\n", opt); diff --git a/monitor_client.c b/monitor_client.c index dd82f93..c2464c4 100644 --- a/monitor_client.c +++ b/monitor_client.c @@ -17,9 +17,13 @@ main (int argc, char *argv[]) return 1; } - jack_port_request_monitor (client, "alsa_pcm:in_1", TRUE); + if (jack_port_request_monitor_by_name (client, "alsa_pcm:in_1", TRUE)) { + fprintf (stderr, "could not enable monitoring for in_1\n"); + } sleep (10); - jack_port_request_monitor (client, "alsa_pcm:in_1", FALSE); + if (jack_port_request_monitor_by_name (client, "alsa_pcm:in_1", FALSE)) { + fprintf (stderr, "could not disable monitoring for in_1\n"); + } jack_client_close (client); exit (0); } diff --git a/pool.c b/pool.c index ae3ca02..cb02ecb 100644 --- a/pool.c +++ b/pool.c @@ -29,3 +29,9 @@ jack_pool_alloc (size_t bytes) return malloc (bytes); } + +void +jack_pool_release (void *ptr) +{ + free (ptr); +} diff --git a/simple_client.c b/simple_client.c index ad794ee..d116f2c 100644 --- a/simple_client.c +++ b/simple_client.c @@ -109,11 +109,11 @@ main (int argc, char *argv[]) the client is activated (this may change in the future). */ - if (jack_port_connect (client, "alsa_pcm:in_1", jack_port_name (input_port))) { + if (jack_connect (client, "alsa_pcm:in_1", jack_port_name (input_port))) { fprintf (stderr, "cannot connect input ports\n"); } - if (jack_port_connect (client, jack_port_name (output_port), "alsa_pcm:out_1")) { + if (jack_connect (client, jack_port_name (output_port), "alsa_pcm:out_1")) { fprintf (stderr, "cannot connect output ports\n"); }