diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | config/Makefile.am | 1 | ||||
-rw-r--r-- | config/os/generic/time.h | 6 | ||||
-rw-r--r-- | config/os/gnu-linux/Makefile.am | 1 | ||||
-rw-r--r-- | config/os/gnu-linux/time.h | 2 | ||||
-rw-r--r-- | config/os/macosx/Makefile.am | 1 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | drivers/alsa/alsa_driver.c | 4 | ||||
-rw-r--r-- | jack/jack.h | 37 | ||||
-rw-r--r-- | libjack/client.c | 546 | ||||
-rw-r--r-- | libjack/pool.c | 2 | ||||
-rw-r--r-- | libjack/port.c | 2 |
12 files changed, 329 insertions, 277 deletions
diff --git a/Makefile.am b/Makefile.am index b96b0bf..39e207d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,7 +13,7 @@ dist-check-doxygen: @false endif -SUBDIRS = jack libjack jackd drivers example-clients $(DOC_DIR) +SUBDIRS = jack libjack jackd drivers example-clients config $(DOC_DIR) DIST_SUBDIRS = config jack libjack jackd drivers example-clients doc pkgconfigdir = $(libdir)/pkgconfig diff --git a/config/Makefile.am b/config/Makefile.am index 6da81ac..d608932 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -2,6 +2,7 @@ # subdirectories. DIST_SUBDIRS = cpu os sysdeps + EXTRA_DIST = depcomp MAINTAINERCLEANFILES = Makefile.in config.guess config.sub \ install-sh ltmain.sh missing mkinstalldirs diff --git a/config/os/generic/time.h b/config/os/generic/time.h index 7118ed8..7ebbc2f 100644 --- a/config/os/generic/time.h +++ b/config/os/generic/time.h @@ -22,9 +22,11 @@ #ifndef __jack_time_h__ #define __jack_time_h__ -#include <jack/internal.h> +#include <jack/types.h> -inline jack_time_t +extern jack_time_t jack_get_microseconds_from_system (void); + +static inline jack_time_t jack_get_microseconds (void) { return jack_get_microseconds_from_system (); } diff --git a/config/os/gnu-linux/Makefile.am b/config/os/gnu-linux/Makefile.am index ae10130..56c5dca 100644 --- a/config/os/gnu-linux/Makefile.am +++ b/config/os/gnu-linux/Makefile.am @@ -1,2 +1,3 @@ MAINTAINERCLEANFILES = Makefile.in noinst_HEADERS = time.c time.h + diff --git a/config/os/gnu-linux/time.h b/config/os/gnu-linux/time.h index e97601c..9d308db 100644 --- a/config/os/gnu-linux/time.h +++ b/config/os/gnu-linux/time.h @@ -24,7 +24,7 @@ #ifndef __jack_time_h__ #define __jack_time_h__ -#include <jack/internal.h> +#include <jack/types.h> extern jack_time_t (*_jack_get_microseconds)(void); diff --git a/config/os/macosx/Makefile.am b/config/os/macosx/Makefile.am index b49bd77..6231480 100644 --- a/config/os/macosx/Makefile.am +++ b/config/os/macosx/Makefile.am @@ -1,4 +1,5 @@ MAINTAINERCLEANFILES = Makefile.in noinst_HEADERS = getopt.h ipc.h JACK_LOCATION.h mach_port.h \ poll.h pThreadUtilities.h time.c time.h + EXTRA_DIST = jack.xcode/project.pbxproj diff --git a/configure.ac b/configure.ac index 41c7a0d..e69b130 100644 --- a/configure.ac +++ b/configure.ac @@ -554,7 +554,7 @@ fi JACK_DEFAULT_DRIVER=\"dummy\" AC_ARG_ENABLE(portaudio, [ --disable-portaudio ignore PortAudio driver ], - TRY_PORTAUDIO=$enableval , TRY_PORTAUDIO=yes ) + TRY_PORTAUDIO=$enableval , TRY_PORTAUDIO=no ) HAVE_PA="false" if test "x$TRY_PORTAUDIO" = "xyes" then diff --git a/drivers/alsa/alsa_driver.c b/drivers/alsa/alsa_driver.c index 415545a..91eed3f 100644 --- a/drivers/alsa/alsa_driver.c +++ b/drivers/alsa/alsa_driver.c @@ -2220,8 +2220,8 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->capture_and_playback_not_synced = FALSE; if (driver->capture_handle && driver->playback_handle) { - if (snd_pcm_link (driver->capture_handle, - driver->playback_handle) != 0) { + if (snd_pcm_link (driver->playback_handle, + driver->capture_handle) != 0) { driver->capture_and_playback_not_synced = TRUE; } } diff --git a/jack/jack.h b/jack/jack.h index 8024c53..dc61ade 100644 --- a/jack/jack.h +++ b/jack/jack.h @@ -198,6 +198,43 @@ int jack_set_process_callback (jack_client_t *client, void *arg); /** + * Block until this JACK client should process data. + * + * This is an alternative API for clients whose internal + * architecture doesn't suit a callback model. They should + * instead contain a core loop that looks something like + * + * \code + * jack_nframes_t nframes; + * + * // wait for the first time we should do something + * + * nframes = jack_thread_wait (client, 0); + * + * while (TRUE) { + * nframes = jack_thread_wait (client, do_some_processing (nframes)); + * } + * \endcode + * + * The function do_some_processing() should return zero if + * the client should keep interacting with JACK, and non-zero + * if it is finished. Note that passing a non-zero status + * will terminate the calling thread. Therefore, this loop should + * run in its own thread which should probably have + * been created with @function jack_client_create_thread(). + * + * Clients using this call should probably not call + * @function jack_set_process_callback although it is not + * an error for them to do so. + * + * @param client - pointer to a JACK client structure + * @param status - if non-zero, calling thread should exit + * + * @return the number of frames of data to process + */ +jack_nframes_t jack_thread_wait (jack_client_t*, int status); + +/** * Tell JACK to call @a thread_init_callback once just after * the creation of the thread in which all other callbacks * will be handled. diff --git a/libjack/client.c b/libjack/client.c index d1c4c2d..1146f7f 100644 --- a/libjack/client.c +++ b/libjack/client.c @@ -62,10 +62,6 @@ #include <sysdeps/pThreadUtilities.h> #endif -#ifdef WITH_TIMESTAMPS -#include <jack/timestamps.h> -#endif /* WITH_TIMESTAMPS */ - static pthread_mutex_t client_lock; static pthread_cond_t client_ready; #ifdef ARCH_X86 @@ -1263,89 +1259,185 @@ jack_stop_freewheel (jack_client_t* client) } } -static void * -jack_client_thread (void *arg) +static void +jack_client_thread_suicide (jack_client_t* client) +{ + if (client->on_shutdown) { + jack_error ("zombified - calling shutdown handler"); + client->on_shutdown (client->on_shutdown_arg); + } else { + jack_error ("jack_client_thread zombified - exiting from JACK"); + jack_client_close (client); + /* Need a fix : possibly make client crash if + * zombified without shutdown handler + */ + } + + pthread_exit (0); + /*NOTREACHED*/ +} + +static int +jack_client_process_events (jack_client_t* client) { - jack_client_t *client = (jack_client_t *) arg; - jack_client_control_t *control = client->control; jack_event_t event; char status = 0; - int err = 0; -#ifndef JACK_USE_MACH_THREADS - char c = 0; -#endif + jack_client_control_t *control = client->control; - pthread_mutex_lock (&client_lock); - client->thread_ok = TRUE; - client->thread_id = pthread_self(); - pthread_cond_signal (&client_ready); - pthread_mutex_unlock (&client_lock); + if (client->pollfd[EVENT_POLL_INDEX].revents & POLLIN) { + + DEBUG ("client receives an event, " + "now reading on event fd"); + + /* server has sent us an event. process the + * event and reply */ + + if (read (client->event_fd, &event, sizeof (event)) + != sizeof (event)) { + jack_error ("cannot read server event (%s)", + strerror (errno)); + return -1; + } + + status = 0; + + switch (event.type) { + case PortRegistered: + if (control->port_register) { + control->port_register + (event.x.port_id, TRUE, + control->port_register_arg); + } + break; + + case PortUnregistered: + if (control->port_register) { + control->port_register + (event.x.port_id, FALSE, + control->port_register_arg); + } + break; + + case GraphReordered: + status = jack_handle_reorder (client, &event); + break; + + case PortConnected: + case PortDisconnected: + status = jack_client_handle_port_connection + (client, &event); + break; + + case BufferSizeChange: + jack_client_invalidate_port_buffers (client); + if (control->bufsize) { + status = control->bufsize + (control->nframes, + control->bufsize_arg); + } + break; + + case SampleRateChange: + if (control->srate) { + status = control->srate + (control->nframes, + control->srate_arg); + } + break; + + case XRun: + if (control->xrun) { + status = control->xrun + (control->xrun_arg); + } + break; + + case AttachPortSegment: + jack_attach_port_segment (client, event.y.ptid); + break; + + case StartFreewheel: + jack_start_freewheel (client); + break; + + case StopFreewheel: + jack_stop_freewheel (client); + break; + } + + DEBUG ("client has dealt with the event, writing " + "response on event fd"); + + if (write (client->event_fd, &status, sizeof (status)) + != sizeof (status)) { + jack_error ("cannot send event response to " + "engine (%s)", strerror (errno)); + return -1; + } + } - client->control->pid = getpid(); - client->control->pgrp = getpgrp(); + return 0; +} + +jack_nframes_t +jack_thread_wait (jack_client_t* client, int status) +{ + /* this function waits for either event notifications from the server + (all platforms) or process notifications from the server (with + non-threaded backends like ALSA and OSS). it will process the + event notifications, and return whenever a process notification + arrives. - DEBUG ("client thread is now running"); + if passed a non-zero status code, it will terminate the thread + in which it is called. + */ - if (client->control->thread_init) { - DEBUG ("calling client thread init callback"); - client->control->thread_init (client->control->thread_init_arg); + jack_client_control_t *control = client->control; + + if (status || client->engine->engine_ok == 0) { + return 0; } - while (err == 0) { + DEBUG ("client polling on %s", client->pollmax == 2 ? + "event_fd and graph_wait_fd..." : + "event_fd only"); - if (client->engine->engine_ok == 0) { - if (client->on_shutdown) - client->on_shutdown (client->on_shutdown_arg); - else - jack_error ("engine unexpectedly shutdown; " - "thread exiting\n"); - pthread_exit (0); - } - - DEBUG ("client polling on %s", client->pollmax == 2 ? - "event_fd and graph_wait_fd..." : - "event_fd only"); - + while (1) { if (poll (client->pollfd, client->pollmax, 1000) < 0) { if (errno == EINTR) { continue; } jack_error ("poll failed in client (%s)", strerror (errno)); - status = -1; - break; + return 0; } - + + pthread_testcancel(); + +#ifndef JACK_USE_MACH_THREADS + /* get an accurate timestamp on waking from poll for a * process() cycle. */ - - #ifndef JACK_USE_MACH_THREADS + if (client->graph_wait_fd >= 0 && client->pollfd[WAIT_POLL_INDEX].revents & POLLIN) { control->awake_at = jack_get_microseconds(); } - + DEBUG ("pfd[EVENT].revents = 0x%x pfd[WAIT].revents = 0x%x", client->pollfd[EVENT_POLL_INDEX].revents, client->pollfd[WAIT_POLL_INDEX].revents); - #endif - - pthread_testcancel(); - #ifndef JACK_USE_MACH_THREADS if (client->graph_wait_fd >= 0 && (client->pollfd[WAIT_POLL_INDEX].revents & ~POLLIN)) { - DEBUG ("\n\n\n\n\n\n\n\nWAITFD ERROR," - " ZOMBIE\n\n\n\n\n"); - /* our upstream "wait" connection closed, which either means that an intermediate client exited, or jackd exited, or jackd zombified us. - + we can discover the zombification via client->control->dead, but the other two possibilities are @@ -1356,250 +1448,171 @@ jack_client_thread (void *arg) */ if (client->upstream_is_jackd) { - DEBUG ("WE DIE\n"); - goto zombie; + DEBUG ("WE DIE\n"); + return 0; } else { - DEBUG ("WE PUNT\n"); + DEBUG ("WE PUNT\n"); /* don't poll on the wait fd * again until we get a * GraphReordered event. */ - + client->graph_wait_fd = -1; client->pollmax = 1; } } - #endif - - if (client->control->dead) { - goto zombie; - } - - if (client->pollfd[EVENT_POLL_INDEX].revents & ~POLLIN) { - /* jackd shutdown */ - goto zombie; - } - - if (client->pollfd[EVENT_POLL_INDEX].revents & POLLIN) { - - DEBUG ("client receives an event, " - "now reading on event fd"); - - /* server has sent us an event. process the - * event and reply */ - - if (read (client->event_fd, &event, sizeof (event)) - != sizeof (event)) { - jack_error ("cannot read server event (%s)", - strerror (errno)); - err++; - break; - } - - status = 0; - - switch (event.type) { - case PortRegistered: - if (control->port_register) { - control->port_register - (event.x.port_id, TRUE, - control->port_register_arg); - } - break; - - case PortUnregistered: - if (control->port_register) { - control->port_register - (event.x.port_id, FALSE, - control->port_register_arg); - } - break; - - case GraphReordered: - status = jack_handle_reorder (client, &event); - break; - - case PortConnected: - case PortDisconnected: - status = jack_client_handle_port_connection - (client, &event); - break; - - case BufferSizeChange: - jack_client_invalidate_port_buffers (client); - if (control->bufsize) { - status = control->bufsize - (control->nframes, - control->bufsize_arg); - } - break; - - case SampleRateChange: - if (control->srate) { - status = control->srate - (control->nframes, - control->srate_arg); - } - break; - - case XRun: - if (control->xrun) { - status = control->xrun - (control->xrun_arg); - } - break; - - case AttachPortSegment: - jack_attach_port_segment (client, event.y.ptid); - break; - - case StartFreewheel: - jack_start_freewheel (client); - break; - - case StopFreewheel: - jack_stop_freewheel (client); - break; - } - - DEBUG ("client has dealt with the event, writing " - "response on event fd"); - - if (write (client->event_fd, &status, sizeof (status)) - != sizeof (status)) { - jack_error ("cannot send event response to " - "engine (%s)", strerror (errno)); - err++; - break; - } +#endif + + if (jack_client_process_events (client)) { + DEBUG ("event processing failed\n"); + return 0; } + if ((client->pollfd[WAIT_POLL_INDEX].revents & POLLIN)) { + DEBUG ("time to run process()\n"); + break; + } + } -#ifndef JACK_USE_MACH_THREADS - if (client->pollfd[WAIT_POLL_INDEX].revents & POLLIN) { - -#ifdef WITH_TIMESTAMPS - jack_reset_timestamps (); -#endif - - DEBUG ("client %d signalled at %" PRIu64 - ", awake for process at %" PRIu64 - " (delay = %" PRIu64 - " usecs) (wakeup on graph_wait_fd==%d)", - getpid(), - control->signalled_at, - control->awake_at, - control->awake_at - control->signalled_at, - client->pollfd[WAIT_POLL_INDEX].fd); - - control->state = Running; - - /* begin preemption checking */ - CHECK_PREEMPTION (client->engine, TRUE); - - if (control->sync_cb) - jack_call_sync_client (client); - - if (control->process) { - if (control->process (control->nframes, - control->process_arg) - == 0) { - control->state = Finished; - } - } else { - control->state = Finished; - } + if (client->control->dead || client->pollfd[EVENT_POLL_INDEX].revents & ~POLLIN) { + DEBUG ("client appears dead or event pollfd has error status\n"); + return 0; + } - if (control->timebase_cb) - jack_call_timebase_master (client); + DEBUG("thread wait returns %u\n", control->nframes); + return control->nframes; +} - /* end preemption checking */ - CHECK_PREEMPTION (client->engine, FALSE); +static int +jack_wake_next_client (jack_client_t* client) +{ + char c = 0; - control->finished_at = jack_get_microseconds(); + if (write (client->graph_next_fd, &c, sizeof (c)) + != sizeof (c)) { + jack_error ("cannot continue execution of the " + "processing graph (%s)", + strerror(errno)); + return -1; + } + + DEBUG ("client sent message to next stage by %" PRIu64 + ", client reading on graph_wait_fd==%d", + jack_get_microseconds(), client->graph_wait_fd); + + DEBUG("reading cleanup byte from pipe %d\n", client->graph_wait_fd); + + if ((read (client->graph_wait_fd, &c, sizeof (c)) + != sizeof (c))) { + jack_error ("cannot complete execution of the " + "processing graph (%s)", + strerror(errno)); + return -1; + } + + return 0; +} -#ifdef WITH_TIMESTAMPS - jack_timestamp ("finished"); -#endif - -#ifndef JACK_USE_MACH_THREADS - /* pass the execution token along */ - - DEBUG ("client finished processing at %" PRIu64 - " (elapsed = %" PRIu64 - " usecs), writing on graph_next_fd==%d", - control->finished_at, - control->finished_at - control->awake_at, - client->graph_next_fd); - - 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; - } +static int +jack_client_run_process_callback (jack_client_t* client) +{ + jack_client_control_t *control = client->control; - DEBUG ("client sent message to next stage by %" PRIu64 - ", client reading on graph_wait_fd==%d", - jack_get_microseconds(), client->graph_wait_fd); +#ifdef JACK_USE_MACH_THREADS + /* this is done in a dedicated thread on OS X */ + return control->nframes; #endif -#ifdef WITH_TIMESTAMPS - jack_timestamp ("read pending byte from wait"); -#endif - DEBUG("reading cleanup byte from pipe\n"); + DEBUG ("client %d signalled at %" PRIu64 + ", awake for process at %" PRIu64 + " (delay = %" PRIu64 + " usecs) (wakeup on graph_wait_fd==%d)", + getpid(), + control->signalled_at, + control->awake_at, + control->awake_at - control->signalled_at, + client->pollfd[WAIT_POLL_INDEX].fd); + + control->state = Running; + + /* begin preemption checking */ + CHECK_PREEMPTION (client->engine, TRUE); + + if (control->sync_cb) + jack_call_sync_client (client); + + if (control->process) { + if (control->process (control->nframes, + control->process_arg) + == 0) { + control->state = Finished; + } + } else { + control->state = Finished; + } + + if (control->timebase_cb) { + jack_call_timebase_master (client); + } + + /* end preemption checking */ + CHECK_PREEMPTION (client->engine, FALSE); + + control->finished_at = jack_get_microseconds(); + + DEBUG ("client finished processing at %" PRIu64 + " (elapsed = %" PRIu64 + " usecs), writing on graph_next_fd==%d", + control->finished_at, + control->finished_at - control->awake_at, + client->graph_next_fd); + + /* wake the next client in the chain (could be the server), + and check if we were killed during the process + cycle. + */ + + if (jack_wake_next_client (client) || client->control->dead) { + DEBUG("client cannot wake next, or is dead\n"); + return -1; + } -#ifndef JACK_USE_MACH_THREADS - if ((read (client->graph_wait_fd, &c, sizeof (c)) - != sizeof (c))) { - DEBUG ("WARNING: READ FAILED!"); -#if 0 - jack_error ("cannot complete execution of the " - "processing graph (%s)", - strerror(errno)); - err++; - break; -#endif - } -#endif + DEBUG("process cycle fully complete\n"); + + return 0; +} - /* check if we were killed during the process - * cycle (or whatever) */ - if (client->control->dead) { - goto zombie; - } +static void * +jack_client_thread (void *arg) +{ + jack_client_t *client = (jack_client_t *) arg; + jack_client_control_t *control = client->control; - DEBUG("process cycle fully complete\n"); + pthread_mutex_lock (&client_lock); + client->thread_ok = TRUE; + client->thread_id = pthread_self(); + pthread_cond_signal (&client_ready); + pthread_mutex_unlock (&client_lock); -#ifdef WITH_TIMESTAMPS - jack_timestamp ("read done"); - jack_dump_timestamps (stdout); -#endif + client->control->pid = getpid(); + client->control->pgrp = getpgrp(); - } -#endif + DEBUG ("client thread is now running"); + if (client->control->thread_init) { + DEBUG ("calling client thread init callback"); + client->control->thread_init (client->control->thread_init_arg); } - - return (void *) ((intptr_t)err); - zombie: - if (client->on_shutdown) { - jack_error ("zombified - calling shutdown handler"); - client->on_shutdown (client->on_shutdown_arg); - } else { - jack_error ("jack_client_thread zombified - exiting from JACK"); - jack_client_close (client); - /* Need a fix : possibly make client crash if - * zombified without shutdown handler - */ + /* wait for first wakeup from server */ + + if (jack_thread_wait (client, 0) == control->nframes) { + while (jack_thread_wait (client, jack_client_run_process_callback (client)) == control->nframes); } - pthread_exit (0); + jack_client_thread_suicide (client); /*NOTREACHED*/ - return 0; + return (void *) 0; } #ifdef JACK_USE_MACH_THREADS @@ -1654,9 +1667,6 @@ jack_client_process_thread (void *arg) control->finished_at = jack_get_microseconds(); -#ifdef WITH_TIMESTAMPS - jack_timestamp ("finished"); -#endif DEBUG ("client finished processing at %Lu (elapsed = %f usecs)", control->finished_at, ((float)(control->finished_at - control->awake_at))); diff --git a/libjack/pool.c b/libjack/pool.c index 1c094d1..43b3adc 100644 --- a/libjack/pool.c +++ b/libjack/pool.c @@ -33,7 +33,7 @@ jack_pool_alloc (size_t bytes) { #ifdef HAVE_POSIX_MEMALIGN void* m; - int err = posix_memalign (&m, 16, bytes); + int err = posix_memalign (&m, 64, bytes); return (!err) ? m : 0; #else return malloc (bytes); diff --git a/libjack/port.c b/libjack/port.c index ee910d4..f8f323d 100644 --- a/libjack/port.c +++ b/libjack/port.c @@ -396,7 +396,7 @@ jack_port_new (const jack_client_t *client, jack_port_id_t port_id, port->client_segment_base = (void **) &client->port_segment[ptid].attached_at; - + return port; } |