git-svn-id: svn+ssh://jackaudio.org/trunk/jack@511 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -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 | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -21,6 +21,7 @@ | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <string.h> | |||
| #include <jack/engine.h> | |||
| #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; | |||
| @@ -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 */ | |||
| @@ -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; | |||
| @@ -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); | |||
| @@ -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 *); | |||
| @@ -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 <jack/types.h> */ | |||
| #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__ */ | |||
| @@ -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 | |||
| */ | |||
| @@ -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; | |||
| } | |||
| @@ -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 */ | |||
| @@ -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 | |||
| @@ -1,3 +1,12 @@ | |||
| 2003-09-18 Jack O'Quin <joq@io.com> | |||
| * 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 <joq@io.com> | |||
| * <jack/types.h> typedefs are now defined using the C99 standard | |||
| @@ -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) | |||
| @@ -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; | |||
| } | |||
| @@ -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]; | |||
| @@ -19,14 +19,11 @@ | |||
| */ | |||
| #include <stdlib.h> | |||
| #include <config.h> | |||
| #include <jack/pool.h> | |||
| void * | |||
| jack_pool_alloc (size_t bytes) | |||
| { | |||
| /* XXX need RT-pool based allocator here */ | |||
| @@ -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 = "", | |||
| } | |||
| }; | |||