diff --git a/configure.in b/configure.in index 6c5d199..0736fdf 100644 --- a/configure.in +++ b/configure.in @@ -13,7 +13,7 @@ dnl micro version = incremented when implementation-only dnl changes are made dnl --- JACK_MAJOR_VERSION=0 -JACK_MINOR_VERSION=82 +JACK_MINOR_VERSION=83 JACK_MICRO_VERSION=0 dnl --- @@ -24,7 +24,7 @@ dnl made to the way libjack communicates with jackd dnl that would break applications linked with an older dnl version of libjack. dnl --- -JACK_PROTOCOL_VERSION=9 +JACK_PROTOCOL_VERSION=10 dnl --- dnl HOWTO: updating the libjack interface version diff --git a/drivers/alsa/alsa_driver.c b/drivers/alsa/alsa_driver.c index 5b34380..4704285 100644 --- a/drivers/alsa/alsa_driver.c +++ b/drivers/alsa/alsa_driver.c @@ -661,9 +661,11 @@ alsa_driver_set_parameters (alsa_driver_t *driver, return 0; } -#if 0 -static int /* UNUSED */ -alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t frames_per_cycle, jack_nframes_t user_nperiods, jack_nframes_t rate) +static int +alsa_driver_reset_parameters (alsa_driver_t *driver, + jack_nframes_t frames_per_cycle, + jack_nframes_t user_nperiods, + jack_nframes_t rate) { /* XXX unregister old ports ? */ alsa_driver_release_channel_dependent_memory (driver); @@ -671,7 +673,6 @@ alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t frames_per_c frames_per_cycle, user_nperiods, rate); } -#endif static int alsa_driver_get_channel_addresses (alsa_driver_t *driver, @@ -996,9 +997,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay } #ifdef DEBUG_WAKEUP - fprintf (stderr, "%Lu: checked %d fds, %Lu usecs since poll"" entered\n", - poll_ret, - nfds, + fprintf (stderr, "%" PRIu64 ": checked %d fds, %" PRIu64 + " usecs since poll entered\n", poll_ret, nfds, poll_ret - poll_enter); #endif @@ -1031,7 +1031,9 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay if (driver->pfd[i].revents == 0) { p_timed_out++; #ifdef DEBUG_WAKEUP - fprintf (stderr, "%Lu playback stream timed out\n", poll_ret); + fprintf (stderr, "%" PRIu64 + " playback stream timed out\n", + poll_ret); #endif } } @@ -1039,7 +1041,9 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay if (p_timed_out == 0) { need_playback = 0; #ifdef DEBUG_WAKEUP - fprintf (stderr, "%Lu playback stream ready\n", poll_ret); + fprintf (stderr, "%" PRIu64 + " playback stream ready\n", + poll_ret); #endif } } @@ -1055,7 +1059,9 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay if (driver->pfd[i].revents == 0) { c_timed_out++; #ifdef DEBUG_WAKEUP - fprintf (stderr, "%Lu capture stream timed out\n", poll_ret); + fprintf (stderr, "%" PRIu64 + " capture stream timed out\n", + poll_ret); #endif } } @@ -1063,14 +1069,18 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay if (c_timed_out == 0) { need_capture = 0; #ifdef DEBUG_WAKEUP - fprintf (stderr, "%Lu capture stream ready\n", poll_ret); + fprintf (stderr, "%" PRIu64 + " capture stream ready\n", + poll_ret); #endif } } 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, polled for %Lu usecs", poll_ret - poll_enter); + jack_error ("ALSA: poll time out, polled for %" PRIu64 + " usecs", + poll_ret - poll_enter); *status = -5; return 0; } @@ -1112,7 +1122,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay avail = capture_avail < playback_avail ? capture_avail : playback_avail; #ifdef DEBUG_WAKEUP - fprintf (stderr, "wakup complete, avail = %lu, pavail = %lu cavail = %lu\n", + fprintf (stderr, "wakeup complete, avail = %lu, pavail = %lu " + "cavail = %lu\n", avail, playback_avail, capture_avail); #endif @@ -1183,6 +1194,14 @@ alsa_driver_null_cycle (alsa_driver_t* driver, jack_nframes_t nframes) return 0; } +static int +alsa_driver_bufsize (alsa_driver_t* driver, jack_nframes_t nframes) +{ + return alsa_driver_reset_parameters (driver, nframes, + driver->user_nperiods, + driver->frame_rate); +} + static int alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) { @@ -1557,6 +1576,7 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->write = (JackDriverReadFunction) alsa_driver_write; driver->null_cycle = (JackDriverNullCycleFunction) alsa_driver_null_cycle; + driver->bufsize = (JackDriverBufSizeFunction) alsa_driver_bufsize; driver->start = (JackDriverStartFunction) alsa_driver_audio_start; driver->stop = (JackDriverStopFunction) alsa_driver_audio_stop; diff --git a/drivers/dummy/dummy_driver.c b/drivers/dummy/dummy_driver.c index 18c53e4..ea92e0b 100644 --- a/drivers/dummy/dummy_driver.c +++ b/drivers/dummy/dummy_driver.c @@ -80,6 +80,21 @@ dummy_driver_null_cycle (dummy_driver_t* driver, jack_nframes_t nframes) return 0; } +static int +dummy_driver_bufsize (dummy_driver_t* driver, jack_nframes_t nframes) +{ + /* these are arbitrary size restrictions */ + if ((nframes < 4) || (nframes > 65536)) + return EINVAL; + + driver->period_size = nframes; + driver->period_usecs = driver->wait_time = + (jack_time_t) floor ((((float) nframes) / driver->sample_rate) + * 1000000.0f); + driver->engine->set_buffer_size (driver->engine, nframes); + return 0; +} + static int dummy_driver_read (dummy_driver_t *driver, jack_nframes_t nframes) { @@ -199,6 +214,7 @@ dummy_driver_new (jack_client_t * client, driver->read = (JackDriverReadFunction) dummy_driver_read; driver->write = (JackDriverReadFunction) dummy_driver_write; driver->null_cycle = (JackDriverNullCycleFunction) dummy_driver_null_cycle; + driver->bufsize = (JackDriverBufSizeFunction) dummy_driver_bufsize; driver->start = (JackDriverStartFunction) dummy_driver_audio_start; driver->stop = (JackDriverStopFunction) dummy_driver_audio_stop; diff --git a/drivers/portaudio/portaudio_driver.c b/drivers/portaudio/portaudio_driver.c index adebdff..c2f6aab 100644 --- a/drivers/portaudio/portaudio_driver.c +++ b/drivers/portaudio/portaudio_driver.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include "portaudio_driver.h" @@ -142,6 +143,12 @@ portaudio_driver_null_cycle (portaudio_driver_t* driver, jack_nframes_t nframes) return 0; } +static int +portaudio_driver_bufsize (portaudio_driver_t* driver, jack_nframes_t nframes) +{ + return ENOSYS; /* function not implemented */ +} + static int portaudio_driver_read (portaudio_driver_t *driver, jack_nframes_t nframes) { @@ -254,6 +261,7 @@ portaudio_driver_new (char *name, driver->read = (JackDriverReadFunction) portaudio_driver_read; driver->write = (JackDriverReadFunction) portaudio_driver_write; driver->null_cycle = (JackDriverNullCycleFunction) portaudio_driver_null_cycle; + driver->bufsize = (JackDriverBufSizeFunction) portaudio_driver_bufsize; driver->start = (JackDriverStartFunction) portaudio_driver_audio_start; driver->stop = (JackDriverStopFunction) portaudio_driver_audio_stop; diff --git a/jack/driver.h b/jack/driver.h index 6b0353b..74df68e 100644 --- a/jack/driver.h +++ b/jack/driver.h @@ -61,6 +61,8 @@ typedef int (*JackDriverStartFunction)(struct _jack_driver *); typedef jack_nframes_t (*JackDriverWaitFunction)(struct _jack_driver *, int fd, int *status, float *delayed_usecs); +typedef int (*JackDriverBufSizeFunction)(struct _jack_driver *, + jack_nframes_t nframes); /* Call sequence summary: @@ -80,6 +82,12 @@ typedef jack_nframes_t (*JackDriverWaitFunction)(struct _jack_driver *, Note that stop/start may be called multiple times in the event of an error return from the `wait' function. + + If a client requests a new buffer size... + + 1) engine stops driver + 2) engine->bufsize() + 3) engine starts driver again */ @@ -186,6 +194,11 @@ typedef struct _jack_driver { JackDriverStartFunction start; + The engine will call this to let the driver know that some client + has requested a new buffer size. The stop function will be called + first, and the start function afterwards. + + JackDriverBufSizeFunction bufsize; */ /* define the fields here... */ @@ -201,7 +214,8 @@ typedef struct _jack_driver { JackDriverWriteFunction write; \ JackDriverNullCycleFunction null_cycle; \ JackDriverStopFunction stop; \ - JackDriverStartFunction start; + JackDriverStartFunction start; \ + JackDriverBufSizeFunction bufsize; JACK_DRIVER_DECL /* expand the macro */ diff --git a/jack/engine.h b/jack/engine.h index 0db6295..72bfeac 100644 --- a/jack/engine.h +++ b/jack/engine.h @@ -31,6 +31,29 @@ struct _jack_driver; struct _jack_client_internal; struct _jack_port_internal; +/* Structures is allocated by the engine in local memory to keep track + * of port buffers and connections. */ +typedef struct { + const char *shm_name; + jack_shmsize_t offset; +} jack_port_buffer_info_t; + +typedef struct _jack_port_internal { + struct _jack_port_shared *shared; + JSList *connections; + jack_port_buffer_info_t *buffer_info; +} jack_port_internal_t; + +/* The engine's internal port type structure. */ +typedef struct _jack_port_type_internal { + pthread_mutex_t buffer_lock; /* only lock within server */ + JSList *buffer_freelist; /* list of free buffers */ + void *buffer_info; /* jack_buffer_info_t array */ + void *seg_addr; /* buffer segment address */ +} jack_port_type_internal_t; + + +/* The main engine structure in local memory. */ struct _jack_engine { jack_control_t *control; struct _jack_driver *driver; @@ -45,20 +68,27 @@ struct _jack_engine { /* "private" sections starts here */ - pthread_mutex_t client_lock; + /* engine serialization -- use precedence for deadlock avoidance */ pthread_mutex_t port_lock; - pthread_mutex_t request_lock; + pthread_mutex_t request_lock; /* precedes driver_lock */ + pthread_mutex_t driver_lock; /* precedes client_lock */ + pthread_mutex_t client_lock; int process_errors; int period_msecs; - int client_timeout_msecs; /* Time to wait for clients in msecs. Used when jackd is - * run in non-ASIO mode and without realtime priority enabled. - */ + int client_timeout_msecs; /* Time to wait for clients in + * msecs. Used when jackd is run in + * non-ASIO mode and without realtime + * priority enabled. */ + + /* internal port type info, indexed by the port type_id */ + jack_port_type_internal_t port_type[JACK_MAX_PORT_TYPES]; + unsigned int port_max; shm_name_t control_shm_name; size_t control_size; - shm_name_t port_segment_name; /* XXX fix me */ - size_t port_segment_size; /* XXX fix me */ - void *port_segment_address; /* XXX fix me */ + shm_name_t port_segment_name; /* XXX fix me */ + size_t port_segment_size; /* XXX fix me */ + void *port_segment_address; /* XXX fix me */ pthread_t main_thread; pthread_t server_thread; diff --git a/jack/internal.h b/jack/internal.h index 1cd8d3d..a71cc3e 100644 --- a/jack/internal.h +++ b/jack/internal.h @@ -68,11 +68,6 @@ typedef struct _jack_request jack_request_t; typedef void * dlhandle; -typedef struct { - const char *shm_name; - size_t offset; /* JOQ: 32/64 problem? */ -} jack_port_buffer_info_t; - typedef enum { TransportCommandNone = 0, TransportCommandStart = 1, @@ -86,13 +81,6 @@ typedef struct { volatile jack_time_t guard2; } jack_frame_timer_t; -/* the relatively low value of this constant - * reflects the fact that JACK currently only - * knows about *1* port type. (March 2003) - */ - -#define JACK_MAX_PORT_TYPES 4 - /* JACK engine shared memory data structure. */ typedef struct { @@ -115,7 +103,7 @@ typedef struct { int32_t internal; jack_nframes_t frames_at_cycle_start; pid_t engine_pid; - uint32_t buffer_size; + jack_nframes_t buffer_size; int8_t real_time; int32_t client_priority; int32_t has_capabilities; @@ -131,7 +119,7 @@ typedef struct { typedef enum { BufferSizeChange, SampleRateChange, - NewPortType, + AttachPortSegment, PortConnected, PortDisconnected, GraphReordered, @@ -150,11 +138,11 @@ typedef struct { } x; union { uint32_t n; + jack_port_type_id_t ptid; jack_port_id_t other_id; - void* addr; /* JOQ: 32/64 problem? */ } y; union { - size_t size; /* JOQ: 32/64 problem? */ + jack_shmsize_t size; } z; } jack_event_t; @@ -187,6 +175,7 @@ typedef volatile struct { volatile int8_t sync_poll : 1; /* w: engine and client, r: engine */ volatile int8_t sync_new : 1; /* w: engine and client, r: engine */ volatile pid_t pid; /* w: client r: engine; client pid */ + volatile pid_t pgrp; /* w: client r: engine; client pgrp */ volatile uint64_t signalled_at; volatile uint64_t awake_at; volatile uint64_t finished_at; @@ -242,23 +231,20 @@ typedef struct { shm_name_t client_shm_name; shm_name_t control_shm_name; - int8_t fifo_prefix[PATH_MAX+1]; + char fifo_prefix[PATH_MAX+1]; int32_t realtime; int32_t realtime_priority; /* these two are valid only if the connect request - was for type == ClientDriver. - */ + was for type == ClientDriver. */ + jack_client_control_t *client_control; /* JOQ: 64/32 problem */ + jack_control_t *engine_control; /* JOQ: 64/32 problem */ - jack_client_control_t *client_control; - jack_control_t *engine_control; - size_t control_size; /* JOQ: 32/64 problem? */ + jack_shmsize_t control_size; /* when we write this response, we deliver n_port_types - of jack_port_type_info_t after it. - */ - + of jack_port_type_info_t after it. */ uint32_t n_port_types; #if defined(__APPLE__) && defined(__POWERPC__) @@ -292,6 +278,7 @@ typedef enum { SetSyncClient = 13, ResetSyncClient = 14, SetSyncTimeout = 15, + SetBufferSize = 16, } RequestType; struct _jack_request { @@ -301,9 +288,9 @@ struct _jack_request { struct { char name[JACK_PORT_NAME_SIZE+1]; char type[JACK_PORT_TYPE_SIZE+1]; - uint32_t flags; - uint32_t buffer_size; - jack_port_id_t port_id; + uint32_t flags; + jack_shmsize_t buffer_size; + jack_port_id_t port_id; jack_client_id_t client_id; } port_info; struct { @@ -359,9 +346,9 @@ extern void jack_cleanup_files (); extern int jack_client_handle_port_connection (jack_client_t *client, jack_event_t *event); -extern void jack_client_handle_new_port_type (jack_client_t *client, - shm_name_t, size_t, void* addr); - +extern void jack_client_set_port_segment (jack_client_t *client, shm_name_t, + jack_port_type_id_t ptid, + jack_shmsize_t, void *addr); extern jack_client_t *jack_driver_client_new (jack_engine_t *, const char *client_name); jack_client_t *jack_client_alloc_internal (jack_client_control_t*, @@ -374,6 +361,8 @@ extern char *jack_server_dir; extern void jack_error (const char *fmt, ...); +extern jack_port_functions_t jack_builtin_audio_functions; + extern jack_port_type_info_t jack_builtin_port_types[]; extern void jack_client_invalidate_port_buffers (jack_client_t *client); diff --git a/jack/jack.h b/jack/jack.h index 9218245..212aa10 100644 --- a/jack/jack.h +++ b/jack/jack.h @@ -87,44 +87,71 @@ int jack_is_realtime (jack_client_t *client); * * NOTE: clients do not need to call this. It exists only * to help more complex clients understand what is going - * on. If called, it should be called before jack_client_activate(). + * on. It should be called before jack_client_activate(). */ void jack_on_shutdown (jack_client_t *client, void (*function)(void *arg), void *arg); /** - * Tell the Jack server to call 'process_callback' whenever there is work - * be done, passing 'arg' as the second argument. + * Tell the Jack server to call @a process_callback whenever there is + * work be done, passing @a arg as the second argument. * * The code in the supplied function must be suitable for real-time - * execution. That means that it cannot call functions that might block for - * a long time.  This includes malloc, free, printf, pthread_mutex_lock, - * sleep, wait, poll, select, pthread_join, pthread_cond_wait, etc, etc.  - * See + * execution. That means that it cannot call functions that might + * block for a long time.  This includes malloc, free, printf, + * pthread_mutex_lock, sleep, wait, poll, select, pthread_join, + * pthread_cond_wait, etc, etc.  See * http://jackit.sourceforge.net/docs/design/design.html#SECTION00411000000000000000 * for more information. - * * * @return 0 on success, otherwise a non-zero error code, causing JACK * to remove that client from the process() graph. */ -int jack_set_process_callback (jack_client_t *, JackProcessCallback process_callback, void *arg); +int jack_set_process_callback (jack_client_t *client, + JackProcessCallback process_callback, + void *arg); /** - * Tell the Jack server to call 'bufsize_callback' whenever the size of the - * the buffer that will be passed to the process callback changes, - * passing 'arg' as the second argument. + * Change the buffer size passed to the @a process_callback. + * + * This operation stops the JACK engine process cycle, then calls all + * registered @a bufsize_callback functions before restarting the + * process cycle. This will cause a gap in the audio flow, so it + * should only be done at appropriate stopping points. + * + * @see jack_set_buffer_size_callback() + * + * @param client pointer to JACK client structure. + * @param nframes new buffer size. Must be a power of two. * * @return 0 on success, otherwise a non-zero error code */ -int jack_set_buffer_size_callback (jack_client_t *, JackBufferSizeCallback bufsize_callback, void *arg); +int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes); /** - * Tell the Jack server to call 'srate_callback' whenever the sample rate of - * the system changes. + * Tell JACK to call @a bufsize_callback whenever the size of the the + * buffer that will be passed to the @a process_callback is about to + * change. Clients that depend on knowing the buffer size must supply + * a @a bufsize_callback before activating themselves. + * + * @param client pointer to JACK client structure. + * @param bufsize_callback function to call when the buffer size changes. + * @param arg argument for @a bufsize_callback. * * @return 0 on success, otherwise a non-zero error code */ -int jack_set_sample_rate_callback (jack_client_t *, JackSampleRateCallback srate_callback, void *arg); +int jack_set_buffer_size_callback (jack_client_t *client, + JackBufferSizeCallback bufsize_callback, + void *arg); + +/** + * Tell the Jack server to call @a srate_callback whenever the system + * sample rate changes. + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_set_sample_rate_callback (jack_client_t *client, + JackSampleRateCallback srate_callback, + void *arg); /** * Tell the Jack server to call 'registration_callback' whenever a port is registered @@ -185,16 +212,14 @@ int jack_deactivate (jack_client_t *client); * character. * * A port has a type, which may be any non-NULL and non-zero length - * string, and is passed as the second argument. For types that are - * not built into the jack API (currently just - * JACK_DEFAULT_AUDIO_TYPE) the client MUST supply a non-zero size - * for the buffer as for 'buffer_size' . For builtin types, - * 'buffer_size' is ignored. + * string, and is passed as the second argument. Some port types are + * built into the JACK API (currently only JACK_DEFAULT_AUDIO_TYPE). + * For other types, the client must supply a non-zero @a buffer_size. + * For builtin types, @a buffer_size is ignored. * - * The 'flags' argument is formed from a bitmask of JackPortFlags values. + * The @a flags argument is formed from a bitmask of JackPortFlags values. * * @return a valid jack_port_t* on success, NULL otherwise. - * returns NULL. */ jack_port_t *jack_port_register (jack_client_t *, const char *port_name, @@ -478,11 +503,13 @@ int jack_port_disconnect (jack_client_t *, jack_port_t *); jack_nframes_t jack_get_sample_rate (jack_client_t *); /** - * This returns the current maximum size that will - * ever be passed to the "process" callback. It should only - * be used *before* the client has been activated. After activation, - * the client will be notified of buffer size changes if it - * registers a buffer_size callback. + * This returns the current maximum size that will ever be passed to + * the @a process_callback. It should only be used *before* the + * client has been activated. This size may change, clients that + * depend on it must register a @a bufsize_callback so they will be + * notified if it does. + * + * @see jack_set_buffer_size_callback() */ jack_nframes_t jack_get_buffer_size (jack_client_t *); diff --git a/jack/port.h b/jack/port.h index ba91a32..23dae16 100644 --- a/jack/port.h +++ b/jack/port.h @@ -28,36 +28,44 @@ #define JACK_PORT_NAME_SIZE 32 #define JACK_PORT_TYPE_SIZE 32 +/* The relatively low value of this constant reflects the fact that + * JACK currently only knows about *1* port type. (March 2003) + */ +#define JACK_MAX_PORT_TYPES 4 +#define JACK_AUDIO_PORT_TYPE 0 + /* these should probably go somewhere else, but not in */ #define JACK_CLIENT_NAME_SIZE 32 typedef uint32_t jack_client_id_t; +/* JACK shared memory segments are limited to MAX_INT32, they can be + * shared between 32-bit and 64-bit clients. */ +#define JACK_SHM_MAX (MAX_INT32) +typedef int32_t jack_shmsize_t; +typedef int32_t jack_port_type_id_t; + typedef struct { - shm_name_t shm_name; - char *address; - size_t size; + shm_name_t shm_name; + char *address; /* JOQ: no longer set globally */ + jack_shmsize_t size; } jack_port_segment_info_t; +/* Port type structure. Has several uses: + * + * (1) One for each port type is part of the engine's jack_control_t + * shared memory structure. + * + * (2) One for each port type is appended to the engine's + * jack_client_connect_result_t response. + * + * (3) The client reads these into its local memory, and uses them to + * attach the corresponding shared memory segments. + */ typedef struct _jack_port_type_info { - uint32_t type_id; + jack_port_type_id_t type_id; const char type_name[JACK_PORT_TYPE_SIZE]; - /* Function to mixdown multiple inputs to a buffer. Can be NULL, - * indicating that multiple input connections are not legal for - * this data type. */ - void (*mixdown)(jack_port_t *, jack_nframes_t); - - /* Function to compute a peak value for a buffer. Can be NULL, - * indicating that the computation has no meaning. The return - * value is normalized to a [0..1] range. */ - double (*peak)(jack_port_t *, jack_nframes_t); - - /* Function to compute a power value for a buffer. Can be NULL, - * indicating that the computation has no meaning. The return - * value is normalized to a [0..1] range. */ - double (*power)(jack_port_t *, jack_nframes_t); - /* If == 1, then a buffer to handle nframes worth of data has * sizeof(jack_default_audio_sample_t) * nframes bytes. If * anything other than 1, the buffer allocated for input mixing @@ -68,12 +76,9 @@ typedef struct _jack_port_type_info { int32_t buffer_scale_factor; /* ignored unless buffer_scale_factor is < 0. see above */ - size_t buffer_size; + jack_shmsize_t buffer_size; - /* these are all run-time information, controlled by the server */ jack_port_segment_info_t shm_info; - pthread_mutex_t buffer_lock; - JSList *buffer_freelist; } jack_port_type_info_t; @@ -84,10 +89,11 @@ typedef struct _jack_port_shared { jack_port_type_info_t type_info; /* location of buffer as an offset from the start of the port's * type-specific shared memory region. */ - size_t offset; + jack_shmsize_t offset; /* index into engine port array for this port */ jack_port_id_t id; - uint32_t flags; + int8_t has_mixdown; /* port has a mixdown function */ + enum JackPortFlags flags; char name[JACK_CLIENT_NAME_SIZE+JACK_PORT_NAME_SIZE+2]; jack_client_id_t client_id; /* who owns me */ @@ -97,17 +103,24 @@ typedef struct _jack_port_shared { char in_use : 1; char locked : 1; - struct _jack_port *tied; } jack_port_shared_t; -/* This is the data structure allocated by the client in local - * memory. The `shared' pointer points to the corresponding structure - * in shared memory. - */ +typedef struct _jack_port_functions { + + /* Function to mixdown multiple inputs to a buffer. Can be NULL, + * indicating that multiple input connections are not legal for + * this data type. */ + void (*mixdown)(jack_port_t *, jack_nframes_t); + +} jack_port_functions_t; + +/* This port structure is allocated by the client in local memory. */ struct _jack_port { char *client_segment_base; - struct _jack_port_shared *shared; + struct _jack_port_shared *shared; /* corresponding shm struct */ + struct _jack_port *tied; /* locally tied source port */ + jack_port_functions_t fptr; /* local port functions */ pthread_mutex_t connection_lock; JSList *connections; }; @@ -117,12 +130,5 @@ struct _jack_port { #define jack_port_buffer(p) \ ((void *) ((p)->client_segment_base + (p)->shared->offset)) -/* This is the structure allocated by the engine in local memory. */ -typedef struct _jack_port_internal { - struct _jack_port_shared *shared; - JSList *connections; - void *buffer_info; -} jack_port_internal_t; - #endif /* __jack_port_h__ */ diff --git a/jack/types.h b/jack/types.h index 1dbb1b9..18dee99 100644 --- a/jack/types.h +++ b/jack/types.h @@ -103,13 +103,16 @@ typedef int (*JackGraphOrderCallback)(void *arg); typedef int (*JackXRunCallback)(void *arg); /** - * Prototype for the client supplied function that is called - * when the engine buffersize changes. - * - * Note! Use of this callback function is deprecated! + * Prototype for the @a bufsize_callback that is invoked whenever the + * JACK engine buffer size changes. Although this function is called + * in the JACK process thread, the normal process cycle is suspended + * during its operation, causing a gap in the audio flow. So, the @a + * bufsize_callback can allocate storage, touch memory not previously + * referenced, and perform other operations that are not realtime + * safe. * - * @param nframes new engine buffer size - * @param arg pointer to a client supplied structure + * @param nframes buffer size + * @param arg pointer supplied by jack_set_buffer_size_callback(). * * @return zero on success, non-zero on error */ diff --git a/jackd/engine.c b/jackd/engine.c index 9a877bb..871d35d 100644 --- a/jackd/engine.c +++ b/jackd/engine.c @@ -142,6 +142,12 @@ jack_client_is_internal (jack_client_internal_t *client) (client->control->type == ClientDriver); } +static inline int +jack_rolling_interval (jack_time_t period_usecs) +{ + return floor ((JACK_ENGINE_ROLLING_INTERVAL * 1000.0f) / period_usecs); +} + static inline void jack_engine_reset_rolling_usecs (jack_engine_t *engine) { @@ -151,9 +157,8 @@ jack_engine_reset_rolling_usecs (jack_engine_t *engine) engine->rolling_client_usecs_cnt = 0; if (engine->driver) { - engine->rolling_interval = (int) - floor (JACK_ENGINE_ROLLING_INTERVAL * 1000.0f - / engine->driver->period_usecs); + engine->rolling_interval = + jack_rolling_interval (engine->driver->period_usecs); } else { engine->rolling_interval = JACK_ENGINE_ROLLING_INTERVAL; } @@ -164,16 +169,18 @@ jack_engine_reset_rolling_usecs (jack_engine_t *engine) static inline jack_port_type_info_t * jack_global_port_type_info (jack_engine_t *engine, jack_port_internal_t *port) { - /* this returns a pointer to the engine's private port type - information, rather than the copy that the port uses. we - need it for buffer allocation, because we need the mutex - that protects the free buffer list for this port type, and - that requires accessing the single copy owned by the - engine. - */ + /* Returns a pointer to the port type information in the + engine's shared control structure. */ return &engine->control->port_types[port->shared->type_info.type_id]; } +static inline jack_port_type_internal_t * +jack_local_port_type_info (jack_engine_t *engine, jack_port_internal_t *port) +{ + /* Points to the engine's private port type struct. */ + return &engine->port_type[port->shared->type_info.type_id]; +} + static int make_sockets (int fd[2]) { @@ -291,19 +298,21 @@ jack_cleanup_files () closedir (dir); } -static int +static void jack_resize_port_segment (jack_engine_t *engine, jack_port_type_info_t *port_type, - jack_nframes_t buffer_size, unsigned long nports) { jack_event_t event; - char *addr; - size_t offset; - size_t one_buffer; - size_t size; + void *addr; + jack_shmsize_t offset; /* shared memory offset */ + size_t one_buffer; /* size of one buffer */ + size_t size; /* segment size */ int shmid; int perm; + jack_port_buffer_info_t *bi; + jack_port_type_id_t ptid = port_type->type_id; + jack_port_type_internal_t *pti = &engine->port_type[ptid]; if (port_type->buffer_scale_factor < 0) { one_buffer = port_type->buffer_size; @@ -324,6 +333,7 @@ jack_resize_port_segment (jack_engine_t *engine, if (port_type->shm_info.size == 0) { + /* no segment allocated, yet */ snprintf (port_type->shm_info.shm_name, sizeof(port_type->shm_info.shm_name), "/jck-[%s]", port_type->type_name); @@ -335,14 +345,32 @@ jack_resize_port_segment (jack_engine_t *engine, " bytes, shm_name = %s (%s)", size, port_type->shm_info.shm_name, strerror (errno)); - return -1; + return; } jack_register_shm (port_type->shm_info.shm_name, addr, shmid); - port_type->shm_info.address = addr; + + /* Allocate an array of buffer info structures for all + * the buffers in the segment. Chain them to the free + * list in memory address order, offset zero must come + * first. */ + pthread_mutex_lock (&pti->buffer_lock); + offset = 0; + bi = pti->buffer_info = (jack_port_buffer_info_t *) + malloc (nports * sizeof (jack_port_buffer_info_t)); + while (offset < size) { + bi->shm_name = port_type->shm_info.shm_name; + bi->offset = offset; + pti->buffer_freelist = + jack_slist_append (pti->buffer_freelist, bi); + offset += one_buffer; + ++bi; + } + pthread_mutex_unlock (&pti->buffer_lock); } else { + /* resize existing buffer segment */ if ((addr = jack_resize_shm (port_type->shm_info.shm_name, size, perm, 0666, PROT_READ|PROT_WRITE)) @@ -351,84 +379,132 @@ jack_resize_port_segment (jack_engine_t *engine, " shm_name = %s (%s)", size, port_type->shm_info.shm_name, strerror (errno)); - return -1; + return; } - } - - port_type->shm_info.size = size; - port_type->shm_info.address = addr; - - pthread_mutex_lock (&port_type->buffer_lock); - - offset = 0; - - while (offset < port_type->shm_info.size) { - jack_port_buffer_info_t *bi; - - bi = (jack_port_buffer_info_t *) - malloc (sizeof (jack_port_buffer_info_t)); - bi->shm_name = port_type->shm_info.shm_name; - bi->offset = offset; - - /* we append because we want the list to be in - * memory-address order */ - port_type->buffer_freelist = - jack_slist_append (port_type->buffer_freelist, bi); - offset += one_buffer; + /* recompute the buffer offsets */ + pthread_mutex_lock (&pti->buffer_lock); + offset = 0; + bi = pti->buffer_info; + while (offset < size) { + bi->offset = offset; + offset += one_buffer; + ++bi; + } + pthread_mutex_unlock (&pti->buffer_lock); } - pthread_mutex_unlock (&port_type->buffer_lock); - - /* tell everybody about it */ + port_type->shm_info.size = size; + engine->port_type[ptid].seg_addr = addr; - event.type = NewPortType; + /* Tell everybody about this segment. */ + event.type = AttachPortSegment; strcpy (event.x.shm_name, port_type->shm_info.shm_name); - event.y.addr = addr; - + event.y.ptid = ptid; + event.z.size = size; /* JOQ: why wasn't this set before? */ jack_deliver_event_to_all (engine, &event); - - return 0; } +/* The driver invokes this callback both initially and whenever its + * buffer size changes. */ static int -jack_set_buffer_size (jack_engine_t *engine, jack_nframes_t nframes) +jack_driver_buffer_size (jack_engine_t *engine, jack_nframes_t nframes) { int i; jack_event_t event; + jack_port_type_internal_t *pti; + JSList *node; + + VERBOSE (engine, "new buffer size %" PRIu32 "\n", nframes); engine->control->buffer_size = nframes; for (i = 0; i < engine->control->n_port_types; ++i) { jack_resize_port_segment (engine, &engine->control->port_types[i], - nframes, engine->control->port_max); + engine->control->port_max); } - /* convert the first chunk of the audio port buffer segment - * into a zero-filled area */ + /* allocate the first buffer of the audio port segment for a + * zero-filled area */ + pti = &engine->port_type[0]; if (engine->silent_buffer == NULL) { - jack_port_type_info_t *port_type = - &engine->control->port_types[0]; - engine->silent_buffer = (jack_port_buffer_info_t *) - port_type->buffer_freelist->data; - port_type->buffer_freelist = - jack_slist_remove_link (port_type->buffer_freelist, - port_type->buffer_freelist); + pti->buffer_freelist->data; + pti->buffer_freelist = + jack_slist_remove_link (pti->buffer_freelist, + pti->buffer_freelist); + } - memset (port_type->shm_info.address + - engine->silent_buffer->offset, 0, - sizeof (jack_default_audio_sample_t) * nframes); + /* always zero `nframes' samples, it could have changed */ + memset (engine->port_type[0].seg_addr + engine->silent_buffer->offset, + 0, sizeof (jack_default_audio_sample_t) * nframes); + + /* update shared client copy of nframes */ + jack_lock_graph (engine); + for (node = engine->clients; node; node = jack_slist_next (node)) { + jack_client_internal_t *client = node->data; + client->control->nframes = nframes; } + jack_unlock_graph (engine); event.type = BufferSizeChange; jack_deliver_event_to_all (engine, &event); - return 0; } +/* handle client SetBufferSize request */ +int +jack_set_buffer_size_request (jack_engine_t *engine, jack_nframes_t nframes) +{ + /* precondition: caller holds the request_lock */ + int rc; + jack_driver_t* driver = engine->driver; + + if (driver == NULL) + return ENXIO; /* no such device */ + + if (!jack_power_of_two(nframes)) { + jack_error("buffer size %" PRIu32 " not a power of 2", + nframes); + return EINVAL; + } + + /* this halts the jack_main_thread() loop while we reset the + * driver's parameters */ + pthread_mutex_lock (&engine->driver_lock); + + if (driver->stop (driver)) { + jack_error ("cannot stop driver to set buffer size"); + pthread_mutex_unlock (&engine->driver_lock); + return EIO; + } + +#if USE_POSIX_SHM + rc = driver->bufsize(driver, nframes); + if (rc == 0) + engine->rolling_interval = + jack_rolling_interval (driver->period_usecs); + else + jack_error("driver does not support %" PRIu32 + "-frame buffers", nframes); +#else + /* jack_resize_shm() not implemented for SysV shm */ + rc = ENOSYS; /* function not implemented */ +#endif + + if (driver->start (driver)) { + jack_error ("cannot restart driver after setting buffer size"); + pthread_mutex_unlock (&engine->driver_lock); + exit(1); + } + + pthread_mutex_unlock (&engine->driver_lock); + + return rc; +} + static JSList * jack_process_internal(jack_engine_t *engine, JSList *node, @@ -494,6 +570,7 @@ jack_process_external(jack_engine_t *engine, JSList *node) static JSList * jack_process_external(jack_engine_t *engine, JSList *node) { + /* precondition: caller has driver_lock */ int status; char c; float delayed_usecs; @@ -632,14 +709,15 @@ jack_process_external(jack_engine_t *engine, JSList *node) static int jack_engine_process (jack_engine_t *engine, jack_nframes_t nframes) { + /* precondition: caller has graph_lock and driver_lock */ jack_client_internal_t *client; - jack_client_control_t *ctl; JSList *node; engine->process_errors = 0; for (node = engine->clients; node; node = jack_slist_next (node)) { - ctl = ((jack_client_internal_t *) node->data)->control; + jack_client_control_t *ctl = + ((jack_client_internal_t *) node->data)->control; ctl->state = NotTriggered; ctl->nframes = nframes; ctl->timed_out = 0; @@ -878,6 +956,7 @@ jack_load_client (jack_engine_t *engine, jack_client_internal_t *client, static void jack_client_unload (jack_client_internal_t *client) { + /* precondition: caller has driver_lock */ if (client->handle) { if (client->finish) { client->finish (client->control->process_arg); @@ -973,19 +1052,19 @@ setup_client (jack_engine_t *engine, int client_fd, /* call its initialization function */ if (client->control->type == ClientInternal) { - unsigned long i; /* tell it about current port types and their * shared memory information */ + jack_port_type_id_t i; for (i = 0; i < engine->control->n_port_types; ++i) { - jack_client_handle_new_port_type ( + jack_port_type_info_t *port_type = + &engine->control->port_types[i]; + jack_client_set_port_segment ( client->control->private_client, - engine->control->port_types[i] - .shm_info.shm_name, - engine->control->port_types[i] - .shm_info.size, - engine->control->port_types[i] - .shm_info.address); + port_type->shm_info.shm_name, + i, + port_type->shm_info.size, + engine->port_type[i].seg_addr); } if (client->initialize (client->control->private_client, @@ -1022,6 +1101,7 @@ setup_client (jack_engine_t *engine, int client_fd, static jack_driver_info_t * jack_load_driver (jack_engine_t *engine, char *so_name) { + /* precondition: caller has driver_lock */ const char *errstr; char path_to_so[PATH_MAX+1]; jack_driver_info_t *info; @@ -1082,6 +1162,7 @@ jack_load_driver (jack_engine_t *engine, char *so_name) void jack_driver_unload (jack_driver_t *driver) { + /* precondition: caller has driver_lock */ driver->finish (driver); dlclose (driver->handle); } @@ -1095,15 +1176,17 @@ jack_engine_load_driver (jack_engine_t *engine, int argc, char *argv[]) jack_driver_t *driver; jack_driver_info_t *info; + pthread_mutex_lock (&engine->driver_lock); + if ((info = jack_load_driver (engine, argv[0])) == NULL) { - return -1; + goto unlock; } req.type = ClientDriver; snprintf (req.name, sizeof (req.name), "%s", info->client_name); if ((client = setup_client (engine, -1, &req, &res)) == NULL) { - return -1; + goto unlock; } if ((driver = info->initialize (client->control->private_client, @@ -1117,10 +1200,15 @@ jack_engine_load_driver (jack_engine_t *engine, int argc, char *argv[]) if (jack_use_driver (engine, driver)) { jack_driver_unload (driver); jack_client_delete (engine, client); - return -1; + goto unlock; } + pthread_mutex_unlock (&engine->driver_lock); return 0; + + unlock: + pthread_mutex_unlock (&engine->driver_lock); + return -1; } static int @@ -1629,6 +1717,10 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd) } break; + case SetBufferSize: + req->status = jack_set_buffer_size_request (engine, + req->x.nframes); + default: /* some requests are handled entirely on the client * side, by adjusting the shared memory area(s) */ @@ -1859,7 +1951,7 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) engine->driver = 0; engine->set_sample_rate = jack_set_sample_rate; - engine->set_buffer_size = jack_set_buffer_size; + engine->set_buffer_size = jack_driver_buffer_size; engine->run_cycle = jack_run_cycle; engine->transport_cycle_start = jack_transport_cycle_start; engine->client_timeout_msecs = client_timeout; @@ -1876,6 +1968,7 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) pthread_mutex_init (&engine->client_lock, 0); pthread_mutex_init (&engine->port_lock, 0); pthread_mutex_init (&engine->request_lock, 0); + pthread_mutex_init (&engine->driver_lock, 0); engine->clients = 0; @@ -1931,13 +2024,12 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) memcpy (&engine->control->port_types[i], &jack_builtin_port_types[i], sizeof (jack_port_type_info_t)); - + /* set offset into port_types array */ engine->control->port_types[i].type_id = i; /* be sure to initialize mutex correctly */ - pthread_mutex_init (&engine->control->port_types[i].buffer_lock, - NULL); + pthread_mutex_init (&engine->port_type[i].buffer_lock, NULL); /* indicate no shared memory allocation for this port type */ engine->control->port_types[i].shm_info.size = 0; @@ -2064,8 +2156,10 @@ cancel_cleanup (int status, void *arg) { jack_engine_t *engine = (jack_engine_t *) arg; engine->control->engine_ok = 0; - engine->driver->stop (engine->driver); - engine->driver->finish (engine->driver); + if (pthread_mutex_trylock (&engine->driver_lock) == 0) { + engine->driver->stop (engine->driver); + engine->driver->finish (engine->driver); + } } #else #ifdef HAVE_ATEXIT @@ -2076,8 +2170,10 @@ cancel_cleanup (void) { jack_engine_t *engine = global_engine; - engine->driver->stop (engine->driver); - engine->driver->finish (engine->driver); + if (pthread_mutex_trylock (&engine->driver_lock) == 0) { + engine->driver->stop (engine->driver); + engine->driver->finish (engine->driver); + } } #else #error "Don't know how to make an exit handler" @@ -2088,11 +2184,17 @@ static void * watchdog_thread (void *arg) { jack_engine_t *engine = (jack_engine_t *) arg; - int watchdog_priority = (engine->rtpriority) > 89 ? 99 : engine->rtpriority + 10; + int watchdog_priority = engine->rtpriority + 10; + int max_priority = sched_get_priority_max(SCHED_FIFO); + + if ((max_priority != -1) && + (max_priority < watchdog_priority)) + watchdog_priority = max_priority; pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); if (jack_become_real_time (pthread_self(), watchdog_priority)) { + VERBOSE(engine, "no realtime watchdog thread\n"); return 0; } @@ -2101,11 +2203,17 @@ watchdog_thread (void *arg) while (1) { usleep (5000000); if (engine->watchdog_check == 0) { + jack_error ("jackd watchdog: timeout - killing jackd"); - /* kill the process group of the current client */ - kill (-engine->current_client->control->pid, SIGKILL); - /* kill our process group */ - kill (-getpgrp(), SIGKILL); + + /* Kill the current client's process group. */ + if (engine->current_client) { + kill (-engine->current_client-> + control->pgrp, SIGKILL); + } + + /* kill our process group, trying to get a dump */ + kill (-getpgrp(), SIGABRT); /*NOTREACHED*/ exit (1); } @@ -2161,16 +2269,15 @@ jack_inc_frame_time (jack_engine_t *engine, jack_nframes_t amount) } static int -jack_run_cycle (jack_engine_t *engine, jack_nframes_t nframes, - float delayed_usecs) +jack_run_one_cycle (jack_engine_t *engine, jack_nframes_t nframes, + float delayed_usecs) { + /* precondition: caller has driver lock */ int restart = 0; jack_driver_t* driver = engine->driver; int ret = -1; static int consecutive_excessive_delays = 0; - engine->watchdog_check = 1; - #define WORK_SCALE 1.0f if (engine->control->real_time && @@ -2216,6 +2323,8 @@ jack_run_cycle (jack_engine_t *engine, jack_nframes_t nframes, goto unlock; } + engine->watchdog_check = 1; + if (jack_engine_process (engine, nframes)) { driver->stop (driver); restart = 1; @@ -2241,6 +2350,32 @@ jack_run_cycle (jack_engine_t *engine, jack_nframes_t nframes, return ret; } +static int +jack_run_cycle (jack_engine_t *engine, jack_nframes_t nframes, + float delayed_usecs) +{ + jack_nframes_t left; + jack_nframes_t b_size = engine->control->buffer_size; + + if (engine->verbose) { + if (nframes != b_size) { + fprintf(stderr, + "late driver wakeup: nframes to process = %" + PRIu32 ".\n", nframes); + } + } + + /* run as many cycles as it takes to consume nframes */ + for (left = nframes; left >= b_size; left -= b_size) { + if (jack_run_one_cycle (engine, b_size, delayed_usecs)) { + jack_error ("cycle execution failure, exiting"); + return EIO; + } + } + + return 0; +} + static void * jack_main_thread (void *arg) { @@ -2267,47 +2402,45 @@ jack_main_thread (void *arg) engine->watchdog_check = 1; while (1) { - - if ((nframes = driver->wait (driver, -1, &wait_status, - &delayed_usecs)) == 0) { + + pthread_mutex_lock (&engine->driver_lock); + + nframes = driver->wait (driver, -1, &wait_status, + &delayed_usecs); + + if (nframes == 0) { + /* the driver detected an xrun and restarted */ jack_engine_notify_clients_about_delay (engine); - continue; - } - if (wait_status == 0) { - jack_nframes_t left, b_size = - engine->control->buffer_size; - if (engine->verbose) { - if (nframes != b_size) { - fprintf(stderr, "late driver wakeup: " - "nframes to process = %lu.\n", - (unsigned long int)nframes); - } - } - for(left = nframes; left >= b_size; left -= b_size) { - if (jack_run_cycle (engine, b_size, - delayed_usecs)) { - jack_error ("cycle execution failure," - " exiting"); - break; - } + } else if (wait_status == 0) { + + if (jack_run_cycle (engine, nframes, + delayed_usecs) != 0) { + break; } + } else if (wait_status < 0) { + break; + } else { /* driver restarted, just continue */ } + + pthread_mutex_unlock (&engine->driver_lock); } + pthread_mutex_unlock (&engine->driver_lock); pthread_exit (0); - - return 0; + /*NOTREACHED*/ + return 0; } int jack_run (jack_engine_t *engine) { + int rc; #ifdef HAVE_ON_EXIT on_exit (cancel_cleanup, engine); @@ -2325,7 +2458,10 @@ jack_run (jack_engine_t *engine) return -1; } - if (engine->driver->start (engine->driver)) { + pthread_mutex_lock (&engine->driver_lock); + rc = engine->driver->start (engine->driver); + pthread_mutex_unlock (&engine->driver_lock); + if (rc != 0) { jack_error ("cannot start driver"); return -1; } @@ -2382,8 +2518,8 @@ jack_engine_delete (jack_engine_t *engine) if (engine) { #if defined(__APPLE__) && defined(__POWERPC__) - /* the jack_run_cycle function is directly called from - * the CoreAudo audio callback */ + /* the jack_run_cycle() function is directly called + * from the CoreAudo audio callback */ return 0; #else return pthread_cancel (engine->main_thread); @@ -2591,6 +2727,7 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) static void jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client) { + /* precondition: caller has driver_lock */ if (jack_client_is_internal (client)) { jack_client_unload (client); free ((char *) client->control); @@ -2695,9 +2832,9 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client, case SampleRateChange: if (client->control->srate) { - client->control->srate ( - event->x.n, - client->control->bufsize_arg); + client->control->srate + (event->x.n, + client->control->srate_arg); } break; @@ -2715,11 +2852,15 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client, } break; - case NewPortType: - jack_client_handle_new_port_type ( - client->control->private_client, - event->x.shm_name, event->z.size, - event->y.addr); + case AttachPortSegment: + + /* Internal clients don't need to attach, but + * they still need to set the port_segment. */ + jack_client_set_port_segment + (client->control->private_client, + event->x.shm_name, event->y.ptid, + event->z.size, + engine->port_type[event->y.ptid].seg_addr); break; default: @@ -3384,7 +3525,7 @@ jack_port_do_connect (jack_engine_t *engine, jack_lock_graph (engine); if (dstport->connections && - dstport->shared->type_info.mixdown == NULL) { + !dstport->shared->has_mixdown) { jack_error ("cannot make multiple connections to a port of" " type [%s]", dstport->shared->type_info.type_name); @@ -3633,6 +3774,7 @@ jack_clear_fifos (jack_engine_t *engine) static int jack_use_driver (jack_engine_t *engine, jack_driver_t *driver) { + /* precondition: caller has driver_lock */ if (engine->driver) { engine->driver->detach (engine->driver, engine); engine->driver = 0; @@ -3642,9 +3784,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->rolling_interval = + jack_rolling_interval (driver->period_usecs); } engine->driver = driver; @@ -3685,8 +3826,8 @@ jack_port_release (jack_engine_t *engine, jack_port_internal_t *port) port->shared->in_use = 0; if (port->buffer_info) { - jack_port_type_info_t *info = - jack_global_port_type_info (engine, port); + jack_port_type_internal_t *info = + jack_local_port_type_info (engine, port); pthread_mutex_lock (&info->buffer_lock); info->buffer_freelist = jack_slist_prepend (info->buffer_freelist, @@ -3967,8 +4108,8 @@ jack_port_registration_notify (jack_engine_t *engine, int jack_port_assign_buffer (jack_engine_t *engine, jack_port_internal_t *port) { - jack_port_type_info_t *port_type = - jack_global_port_type_info (engine, port); + jack_port_type_internal_t *pti = + jack_local_port_type_info (engine, port); jack_port_buffer_info_t *bi; if (port->shared->flags & JackPortIsInput) { @@ -3976,23 +4117,24 @@ jack_port_assign_buffer (jack_engine_t *engine, jack_port_internal_t *port) return 0; } - pthread_mutex_lock (&port_type->buffer_lock); + pthread_mutex_lock (&pti->buffer_lock); - if (port_type->buffer_freelist == NULL) { + if (pti->buffer_freelist == NULL) { + jack_port_type_info_t *port_type = + jack_global_port_type_info (engine, port); jack_error ("all %s port buffers in use!", port_type->type_name); - pthread_mutex_unlock (&port_type->buffer_lock); + pthread_mutex_unlock (&pti->buffer_lock); return -1; } - bi = (jack_port_buffer_info_t *) port_type->buffer_freelist->data; - port_type->buffer_freelist = - jack_slist_remove (port_type->buffer_freelist, bi); + bi = (jack_port_buffer_info_t *) pti->buffer_freelist->data; + pti->buffer_freelist = jack_slist_remove (pti->buffer_freelist, bi); port->shared->offset = bi->offset; port->buffer_info = bi; - pthread_mutex_unlock (&port_type->buffer_lock); + pthread_mutex_unlock (&pti->buffer_lock); return 0; } diff --git a/jackd/jackd.c b/jackd/jackd.c index cb9584e..55683ad 100644 --- a/jackd/jackd.c +++ b/jackd/jackd.c @@ -179,15 +179,21 @@ jack_main (int argc, char **argv) sigaddset(&signals, SIGHUP); sigaddset(&signals, SIGINT); sigaddset(&signals, SIGQUIT); + sigaddset(&signals, SIGPIPE); + sigaddset(&signals, SIGTERM); + sigaddset(&signals, SIGUSR1); + +#if 0 + /* POSIX defines these as "synchronous" signals, which must be + * delivered to the offending thread. I think it's a bad idea + * to block them. (JOQ) */ sigaddset(&signals, SIGILL); sigaddset(&signals, SIGTRAP); sigaddset(&signals, SIGABRT); sigaddset(&signals, SIGIOT); sigaddset(&signals, SIGFPE); - sigaddset(&signals, SIGPIPE); - sigaddset(&signals, SIGTERM); - sigaddset(&signals, SIGUSR1); sigaddset(&signals, SIGSEGV); +#endif /* all child threads will inherit this mask unless they * explicitly reset it */ diff --git a/jackd/transengine.c b/jackd/transengine.c index e7c06fc..5d19d64 100644 --- a/jackd/transengine.c +++ b/jackd/transengine.c @@ -50,7 +50,9 @@ jack_sync_poll_new (jack_engine_t *engine, jack_client_internal_t *client) engine->control->transport_state = JackTransportStarting; VERBOSE (engine, "force transport state to Starting\n"); } - VERBOSE (engine, "polling sync client %" PRIu32 "\n", client->control->id); + + VERBOSE (engine, "polling sync client %" PRIu32 "\n", + client->control->id); } /* stop polling a specific slow-sync client diff --git a/libjack/ChangeLog b/libjack/ChangeLog index af0f4c3..f442e78 100644 --- a/libjack/ChangeLog +++ b/libjack/ChangeLog @@ -1,3 +1,12 @@ +2003-09-18 Jack O'Quin + + * new function jack_set_buffer_size(). + + Compatibility issues: programs that rely on the buffer size but do + not register a callback will fail. Cached output buffer addresses + are not valid after the buffer size changes. These rules existed + before, but were never enforced. + 2003-08-26 Jack O'Quin * typedefs are now defined using the C99 standard diff --git a/libjack/client.c b/libjack/client.c index 4aeb727..aa73c50 100644 --- a/libjack/client.c +++ b/libjack/client.c @@ -75,7 +75,7 @@ jack_set_server_dir (const char *path) static pthread_mutex_t client_lock; static pthread_cond_t client_ready; -void *jack_zero_filled_buffer = 0; +void *jack_zero_filled_buffer = NULL; #define event_fd pollfd[0].fd #define graph_wait_fd pollfd[1].fd @@ -138,16 +138,15 @@ jack_client_t * jack_client_alloc () { jack_client_t *client; + jack_port_type_id_t ptid; client = (jack_client_t *) malloc (sizeof (jack_client_t)); client->pollfd = (struct pollfd *) malloc (sizeof (struct pollfd) * 2); client->pollmax = 2; - client->request_fd = -1; client->event_fd = -1; client->graph_wait_fd = -1; client->graph_next_fd = -1; - client->port_segments = NULL; client->ports = NULL; client->engine = NULL; client->control = 0; @@ -155,6 +154,13 @@ jack_client_alloc () client->first_active = TRUE; client->on_shutdown = NULL; + client->n_port_types = 0; + for (ptid = 0; ptid < JACK_MAX_PORT_TYPES; ++ptid) { + client->port_segment[ptid].shm_name[0] = '\0'; + client->port_segment[ptid].address = NULL; + client->port_segment[ptid].size = 0; + } + return client; } @@ -455,6 +461,30 @@ jack_request_client (ClientType type, const char* client_name, const char* so_na return -1; } +void +jack_attach_port_segment (jack_client_t *client, shm_name_t shm_name, + jack_port_type_id_t ptid, jack_shmsize_t size) +{ + int shmid; + void *addr; + + /* Lookup, attach and register the port/buffer segments in use + * right now. */ + if (client->control->type != ClientExternal) { + jack_error("Only external clients need attach port segments"); + abort(); + } + + if ((addr = jack_get_shm (shm_name, size, O_RDWR, 0, + (PROT_READ|PROT_WRITE), + &shmid)) == MAP_FAILED) { + jack_error ("cannot attach port segment shared memory" + " (%s)", strerror (errno)); + } + + jack_client_set_port_segment (client, shm_name, ptid, size, addr); +} + jack_client_t * jack_client_new (const char *client_name) { @@ -463,9 +493,9 @@ jack_client_new (const char *client_name) jack_client_connect_result_t res; jack_client_t *client; void *addr; - int i; int shmid; jack_port_type_info_t* type_info; + jack_port_type_id_t ptid; /* external clients need this initialized; internal clients will use the setup in the server's address space. @@ -530,10 +560,11 @@ jack_client_new (const char *client_name) goto fail; } - for (i = 0; i < res.n_port_types; ++i) { - jack_client_handle_new_port_type ( - client, type_info[i].shm_info.shm_name, - type_info[i].shm_info.size, 0); + client->n_port_types = res.n_port_types; + for (ptid = 0; ptid < res.n_port_types; ++ptid) { + jack_attach_port_segment (client, + type_info[ptid].shm_info.shm_name, + ptid, type_info[ptid].shm_info.size); } free (type_info); @@ -620,44 +651,22 @@ jack_internal_client_close (const char *client_name) } void -jack_client_handle_new_port_type (jack_client_t *client, shm_name_t shm_name, - size_t size, void* addr) +jack_client_set_port_segment (jack_client_t *client, shm_name_t shm_name, + jack_port_type_id_t ptid, jack_shmsize_t size, + void *addr) { - jack_port_segment_info_t *si; - int shmid; - - /* Lookup, attach and register the port/buffer segments in use - * right now. */ - if (client->control->type == ClientExternal) { - - if ((addr = jack_get_shm(shm_name, size, O_RDWR, 0, - (PROT_READ|PROT_WRITE), - &shmid)) == MAP_FAILED) { - jack_error ("cannot attached port segment shared memory" - " (%s)", strerror (errno)); - return; - } - - } else { - - /* client is in same address space as server, so just - * use `addr' directly */ - } - - si = (jack_port_segment_info_t *) - malloc (sizeof (jack_port_segment_info_t)); - strcpy (si->shm_name, shm_name); - si->address = addr; - si->size = size; - - /* the first chunk of the first port segment is always set by - * the engine to be a conveniently-sized, zero-filled lump of - * memory. */ - if (client->port_segments == NULL) { - jack_zero_filled_buffer = si->address; + client->port_segment[ptid].address = addr; + client->port_segment[ptid].size = size; + strncpy (client->port_segment[ptid].shm_name, + shm_name, sizeof (shm_name_t)); + + /* The first chunk of the audio port segment will be set by + * the engine to be a zero-filled buffer. This hasn't been + * done yet, but it will happen before the process cycle + * (re)starts. */ + if (ptid == JACK_AUDIO_PORT_TYPE) { + jack_zero_filled_buffer = client->port_segment[ptid].address; } - - client->port_segments = jack_slist_prepend (client->port_segments, si); } static void * @@ -680,6 +689,7 @@ jack_client_thread (void *arg) pthread_mutex_unlock (&client_lock); client->control->pid = getpid(); + client->control->pgrp = getpgrp(); DEBUG ("client thread is now running"); @@ -790,10 +800,11 @@ jack_client_thread (void *arg) } break; - case NewPortType: - jack_client_handle_new_port_type ( - client, event.x.shm_name, - event.z.size, event.y.addr); + case AttachPortSegment: + jack_attach_port_segment (client, + event.x.shm_name, + event.y.ptid, + event.z.size); break; } @@ -1275,6 +1286,7 @@ jack_client_close (jack_client_t *client) { JSList *node; void *status; + jack_port_type_id_t ptid; if (client->control->active) { jack_deactivate (client); @@ -1295,14 +1307,12 @@ jack_client_close (jack_client_t *client) munmap ((char *) client->engine, sizeof (jack_control_t)); - for (node = client->port_segments; node; - node = jack_slist_next (node)) { - jack_port_segment_info_t *si = - (jack_port_segment_info_t *) node->data; - munmap ((char *) si->address, si->size); - free (node->data); + for (ptid = 0; ptid < client->n_port_types; ++ptid) { + if (client->port_segment[ptid].size) { + munmap (client->port_segment[ptid].address, + client->port_segment[ptid].size); + } } - jack_slist_free (client->port_segments); if (client->graph_wait_fd) { close (client->graph_wait_fd); @@ -1337,6 +1347,17 @@ jack_get_buffer_size (jack_client_t *client) return client->engine->buffer_size; } +int +jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) +{ + jack_request_t req; + + req.type = SetBufferSize; + req.x.nframes = nframes; + + return jack_client_deliver_request (client, &req); +} + int jack_connect (jack_client_t *client, const char *source_port, const char *destination_port) diff --git a/libjack/driver.c b/libjack/driver.c index 7d975ee..cb5ea04 100644 --- a/libjack/driver.c +++ b/libjack/driver.c @@ -43,6 +43,8 @@ static int dummy_write (jack_driver_t *drv, static int dummy_read (jack_driver_t *drv, jack_nframes_t nframes) { return 0; } static int dummy_null_cycle (jack_driver_t *drv, jack_nframes_t nframes) { return 0; } +static int dummy_bufsize (jack_driver_t *drv, + jack_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; } @@ -57,6 +59,7 @@ jack_driver_init (jack_driver_t *driver) driver->write = dummy_write; driver->read = dummy_read; driver->null_cycle = dummy_null_cycle; + driver->bufsize = dummy_bufsize; driver->start = dummy_start; driver->stop = dummy_stop; } diff --git a/libjack/local.h b/libjack/local.h index ae73e3e..509f54b 100644 --- a/libjack/local.h +++ b/libjack/local.h @@ -10,7 +10,8 @@ struct _jack_client { int pollmax; int graph_next_fd; int request_fd; - JSList *port_segments; + jack_port_type_id_t n_port_types; + jack_port_segment_info_t port_segment[JACK_MAX_PORT_TYPES]; JSList *ports; pthread_t thread; char fifo_prefix[PATH_MAX+1]; diff --git a/libjack/pool.c b/libjack/pool.c index 58125fe..34e110c 100644 --- a/libjack/pool.c +++ b/libjack/pool.c @@ -19,14 +19,11 @@ */ #include - #include - #include void * jack_pool_alloc (size_t bytes) - { /* XXX need RT-pool based allocator here */ diff --git a/libjack/port.c b/libjack/port.c index 9cc6a0b..f95d022 100644 --- a/libjack/port.c +++ b/libjack/port.c @@ -34,81 +34,58 @@ #include "local.h" -static void jack_audio_port_mixdown (jack_port_t *port, - jack_nframes_t nframes); +static void jack_audio_port_mixdown (jack_port_t *port, + jack_nframes_t nframes); + +/* These function pointers are local to each address space. For + * internal clients they reside within jackd; for external clients in + * the application process. */ +jack_port_functions_t jack_builtin_audio_functions = { + .mixdown = jack_audio_port_mixdown, +}; + +jack_port_functions_t jack_builtin_NULL_functions = { + .mixdown = NULL, +}; + +/* Only the Audio port type is currently built in. */ +jack_port_type_info_t jack_builtin_port_types[] = { + { .type_name = JACK_DEFAULT_AUDIO_TYPE, + .buffer_scale_factor = 1, + }, + { .type_name = "", } +}; jack_port_t * jack_port_new (const jack_client_t *client, jack_port_id_t port_id, jack_control_t *control) { - jack_port_t *port; - jack_port_shared_t *shared; - jack_port_segment_info_t *si; - JSList *node; - - shared = &control->ports[port_id]; - - port = (jack_port_t *) malloc (sizeof (jack_port_t)); + jack_port_shared_t *shared = &control->ports[port_id]; + jack_port_type_id_t ptid = shared->type_info.type_id; + jack_port_t *port = (jack_port_t *) malloc (sizeof (jack_port_t)); port->client_segment_base = 0; port->shared = shared; pthread_mutex_init (&port->connection_lock, NULL); port->connections = 0; - port->shared->tied = NULL; + port->tied = NULL; - /* reset function pointers to be within client address space */ - - switch (client->control->type) { - case ClientExternal: - - if (client->control->id == port->shared->client_id) { + if (client->control->id == port->shared->client_id) { - /* its our port, and therefore we need to make - sure that the function pointers in the - shared memory object that refer to - functions called within the client's - address space point to the location of the - correct functions within the - client. without this, they point to those - same functions in the server's address - space, and that's a recipe for disaster. - */ - - port->shared->type_info.mixdown = - jack_builtin_port_types[ - port->shared->type_info.type_id - ].mixdown; - port->shared->type_info.peak = - jack_builtin_port_types[ - port->shared->type_info.type_id].peak; - port->shared->type_info.power = - jack_builtin_port_types[ - port->shared->type_info.type_id].power; - } - break; + /* It's our port, so initialize the pointers to port + * functions within this address space. These builtin + * definitions can be overridden by the client. */ - default: - break; - } + if (port->shared->type_info.type_id == JACK_AUDIO_PORT_TYPE) { - /* now find the shared memory segment that contains the buffer - * for this port */ - for (node = client->port_segments; node; - node = jack_slist_next (node)) { - - si = (jack_port_segment_info_t *) node->data; + port->fptr = jack_builtin_audio_functions; + port->shared->has_mixdown = TRUE; - if (strcmp (si->shm_name, - port->shared->type_info.shm_info.shm_name) == 0) { - break; - } - } + } else { /* no other builtin functions */ - if (node == NULL) { - jack_error ("cannot find port memory segment [%s] for" - " new port\n", - port->shared->type_info.shm_info.shm_name); - return NULL; + port->fptr = jack_builtin_NULL_functions; + port->shared->has_mixdown = FALSE; + } } /* set up a base address so that port->offset can be used to @@ -117,7 +94,7 @@ jack_port_new (const jack_client_t *client, jack_port_id_t port_id, port->offset can change if the buffer size or port counts are changed. */ - port->client_segment_base = si->address; + port->client_segment_base = client->port_segment[ptid].address; return port; } @@ -387,31 +364,24 @@ jack_port_get_buffer (jack_port_t *port, jack_nframes_t nframes) { JSList *node, *next; - /* Output port. The buffer was assigned by the engine + /* Output port. The buffer was assigned by the engine when the port was registered. */ - if (port->shared->flags & JackPortIsOutput) { - if (port->shared->tied) { - return jack_port_get_buffer (port->shared->tied, - nframes); + if (port->tied) { + return jack_port_get_buffer (port->tied, nframes); } return jack_port_buffer (port); } - /* Input port. + /* Input port. Since this can only be called from the + process() callback, and since no connections can be + made/broken during this phase (enforced by the jack + server), there is no need to take the connection lock here */ - - /* since this can only be called from the process() callback, - and since no connections can be made/broken during this - phase (enforced by the jack server), there is no need - to take the connection lock here - */ - if ((node = port->connections) == NULL) { /* no connections; return a zero-filled buffer */ - return jack_zero_filled_buffer; } @@ -420,20 +390,19 @@ jack_port_get_buffer (jack_port_t *port, jack_nframes_t nframes) /* one connection: use zero-copy mode - just pass the buffer of the connected (output) port. */ - return jack_port_get_buffer (((jack_port_t *) node->data), nframes); } - /* multiple connections. use a local buffer and mixdown - the incoming data to that buffer. we have already - established the existence of a mixdown function - during the connection process. + /* Multiple connections. Use a local buffer and mixdown the + incoming data to that buffer. we have already established + the existence of a mixdown function during the connection + process. - no port can have an offset of 0 - that offset refers - to the zero-filled area at the start of a shared port - segment area. so, use the offset to store the location - of a locally allocated buffer, and reset the client_segment_base + No port can have an offset of 0, that offset refers to the + zero-filled area at the start of a shared port segment + area. So, use the offset to store the location of a + locally allocated buffer, and reset the client_segment_base so that the jack_port_buffer() computation works correctly. */ @@ -445,7 +414,7 @@ jack_port_get_buffer (jack_port_t *port, jack_nframes_t nframes) * nframes); port->client_segment_base = 0; } - port->shared->type_info.mixdown (port, nframes); + port->fptr.mixdown (port, nframes); return (jack_default_audio_sample_t *) port->shared->offset; } @@ -463,7 +432,7 @@ jack_port_tie (jack_port_t *src, jack_port_t *dst) return -1; } - dst->shared->tied = src; + dst->tied = src; return 0; } @@ -471,11 +440,11 @@ int jack_port_untie (jack_port_t *port) { - if (port->shared->tied == NULL) { + if (port->tied == NULL) { jack_error ("port \"%s\" is not tied", port->shared->name); return -1; } - port->shared->tied = NULL; + port->tied = NULL; return 0; } @@ -625,21 +594,6 @@ static inline float f_max(float x, float a) return (x); } -static double -jack_audio_port_peak (jack_port_t *port, jack_nframes_t nframes) -{ - jack_nframes_t n; - jack_default_audio_sample_t *buf = (jack_default_audio_sample_t *) - jack_port_get_buffer (port, nframes); - float max = 0; - - for (n = 0; n < nframes; ++n) { - max = f_max (buf[n], max); - } - - return max; -} - static void jack_audio_port_mixdown (jack_port_t *port, jack_nframes_t nframes) { @@ -680,15 +634,3 @@ jack_audio_port_mixdown (jack_port_t *port, jack_nframes_t nframes) } } } - -jack_port_type_info_t jack_builtin_port_types[] = { - { .type_name = JACK_DEFAULT_AUDIO_TYPE, - .mixdown = jack_audio_port_mixdown, - .peak = jack_audio_port_peak, - .power = NULL, - .buffer_scale_factor = 1, - }, - { .type_name = "", - } -}; -