git-svn-id: svn+ssh://jackaudio.org/trunk/jack@145 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -54,6 +54,11 @@ alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) | |||
| free (driver->silent); | |||
| driver->silent = 0; | |||
| } | |||
| if (driver->dither_state) { | |||
| free (driver->dither_state); | |||
| driver->dither_state = 0; | |||
| } | |||
| } | |||
| static int | |||
| @@ -150,12 +155,27 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) | |||
| driver->channel_copy = memcpy_fake; | |||
| } | |||
| if (driver->dither) { | |||
| switch (driver->dither) { | |||
| case Rectangular: | |||
| printf("Rectangular dithering at 16 bits\n"); | |||
| driver->write_via_copy = sample_move_dither_rect_d16_sS; | |||
| } else { | |||
| break; | |||
| case Triangular: | |||
| printf("Triangular dithering at 16 bits\n"); | |||
| driver->write_via_copy = sample_move_dither_tri_d16_sS; | |||
| break; | |||
| case Shaped: | |||
| printf("Noise-shaped dithering at 16 bits\n"); | |||
| driver->write_via_copy = sample_move_dither_shaped_d16_sS; | |||
| break; | |||
| default: | |||
| driver->write_via_copy = sample_move_d16_sS; | |||
| break; | |||
| } | |||
| driver->read_via_copy = sample_move_dS_s16; | |||
| break; | |||
| @@ -463,6 +483,8 @@ alsa_driver_set_parameters (alsa_driver_t *driver, nframes_t frames_per_cycle, n | |||
| for (chn = 0; chn < driver->playback_nchannels; chn++) { | |||
| driver->channel_done_bits |= (1<<chn); | |||
| } | |||
| driver->dither_state = (dither_state_t *) calloc ( driver->playback_nchannels, sizeof (dither_state_t)); | |||
| } | |||
| if (driver->capture_handle) { | |||
| @@ -995,14 +1017,17 @@ alsa_driver_process (alsa_driver_t *driver, nframes_t nframes) | |||
| /* now move data from ports to channels */ | |||
| for (chn = 0, node = driver->playback_ports; node; node = g_slist_next (node), chn++) { | |||
| sample_t *buf; | |||
| jack_port_t *port = (jack_port_t *) node->data; | |||
| if (!jack_port_connected (port)) { | |||
| continue; | |||
| } | |||
| buf = jack_port_get_buffer (port, contiguous); | |||
| alsa_driver_write_to_channel (driver, chn, jack_port_get_buffer (port, contiguous), contiguous); | |||
| alsa_driver_write_to_channel (driver, chn, buf, contiguous); | |||
| } | |||
| } | |||
| @@ -1256,7 +1281,7 @@ alsa_driver_new (char *name, char *alsa_device, | |||
| int hw_monitoring, | |||
| int capturing, | |||
| int playing, | |||
| int dither) | |||
| DitherAlgorithm dither) | |||
| { | |||
| int err; | |||
| @@ -1456,6 +1481,7 @@ alsa PCM driver args: | |||
| -D (duplex, default: yes) | |||
| -C (capture, default: duplex) | |||
| -P (playback, default: duplex) | |||
| -z[r|t|s|-] (dither, rect|tri|shaped|off, default: off) | |||
| "); | |||
| } | |||
| @@ -1469,7 +1495,7 @@ driver_initialize (int argc, char **argv) | |||
| int hw_monitoring = FALSE; | |||
| int capture = FALSE; | |||
| int playback = FALSE; | |||
| int dither = FALSE; | |||
| DitherAlgorithm dither = None; | |||
| int i; | |||
| /* grrrr ... getopt() cannot be called in more than one "loop" | |||
| @@ -1518,7 +1544,24 @@ driver_initialize (int argc, char **argv) | |||
| break; | |||
| case 'z': | |||
| dither = TRUE; | |||
| switch (argv[i][2]) { | |||
| case '-': | |||
| dither = None; | |||
| break; | |||
| case 'r': | |||
| dither = Rectangular; | |||
| break; | |||
| case 's': | |||
| dither = Shaped; | |||
| break; | |||
| case 't': | |||
| default: | |||
| dither = Triangular; | |||
| break; | |||
| } | |||
| break; | |||
| default: | |||
| @@ -1203,6 +1203,9 @@ jack_port_get_buffer (jack_port_t *port, nframes_t nframes) | |||
| the buffer of the connected (output) port. | |||
| */ | |||
| printf ("get buffer for port %s is at offset %d\n", port->shared->name, | |||
| ((jack_port_t *) node->data)->shared->offset); | |||
| return jack_port_buffer (((jack_port_t *) node->data)); | |||
| } | |||
| @@ -1597,7 +1600,7 @@ jack_audio_port_mixdown (jack_port_t *port, nframes_t nframes) | |||
| node = port->connections; | |||
| input = (jack_port_t *) node->data; | |||
| buffer = jack_port_buffer (port); | |||
| memcpy (buffer, jack_port_buffer (input), sizeof (sample_t) * nframes); | |||
| for (node = g_slist_next (node); node; node = g_slist_next (node)) { | |||
| @@ -4,7 +4,7 @@ AC_INIT(client.c) | |||
| AC_CONFIG_AUX_DIR(.) | |||
| JACK_MAJOR_VERSION=0 | |||
| JACK_MINOR_VERSION=19 | |||
| JACK_MINOR_VERSION=20 | |||
| JACK_MICRO_VERSION=0 | |||
| BETA= | |||
| @@ -31,7 +31,7 @@ JACK_OPT_CFLAGS="-D_REENTRANT -O6 -Wall -fomit-frame-pointer -ffast-math -fstren | |||
| AC_ARG_ENABLE(optimize, | |||
| [ --enable-optimize ask the compiler for its best optimizations.], | |||
| [ if test "x$enable_optimize" != "xno" ; then JACK_CFLAGS="$JACK_OPT_CFLAGS" ; fi ]) | |||
| [ if test "x$enable_optimize" != "xno" ; then CFLAGS="$JACK_OPT_CFLAGS" ; fi ]) | |||
| AC_SUBST(JACK_CFLAGS) | |||
| @@ -520,48 +520,6 @@ jack_process (jack_engine_t *engine, nframes_t nframes) | |||
| return engine->process_errors > 0; | |||
| } | |||
| static void * | |||
| jack_cleanup_clients (void *arg) | |||
| { | |||
| jack_engine_t *engine = (jack_engine_t *) arg; | |||
| jack_client_internal_t *client; | |||
| GSList *node, *tmp; | |||
| int need_sort = FALSE; | |||
| jack_lock_graph (engine); | |||
| /* remove all dead clients */ | |||
| for (node = engine->clients; node; ) { | |||
| tmp = g_slist_next (node); | |||
| client = (jack_client_internal_t *) node->data; | |||
| if (client->error) { | |||
| if (engine->verbose) { | |||
| fprintf (stderr, "removing failed client %s (errors: %d)\n", | |||
| client->control->name, client->error); | |||
| } | |||
| jack_remove_client (engine, (jack_client_internal_t *) node->data); | |||
| need_sort = TRUE; | |||
| } | |||
| node = tmp; | |||
| } | |||
| if (need_sort) { | |||
| jack_sort_graph (engine); | |||
| } | |||
| jack_unlock_graph (engine); | |||
| pthread_mutex_unlock (&engine->cleanup_lock); | |||
| return NULL; | |||
| } | |||
| static int | |||
| jack_engine_post_process (jack_engine_t *engine) | |||
| { | |||
| @@ -579,10 +537,6 @@ jack_engine_post_process (jack_engine_t *engine) | |||
| client = (jack_client_internal_t *) node->data; | |||
| ctl = client->control; | |||
| if (engine->verbose) { | |||
| fprintf (stderr, "client %s state = %s\n", ctl->name, client_state_names[ctl->state]); | |||
| } | |||
| if (ctl->timed_out || (ctl->state > NotTriggered && ctl->state != Finished)) { | |||
| client->error++; | |||
| } | |||
| @@ -592,20 +546,40 @@ jack_engine_post_process (jack_engine_t *engine) | |||
| } | |||
| } | |||
| jack_unlock_graph (engine); | |||
| if (need_remove) { | |||
| /* only one thread is allowed to run cleanup at a time. if | |||
| one is already underway, don't bother starting another. | |||
| */ | |||
| GSList *tmp; | |||
| int need_sort = FALSE; | |||
| /* remove all dead clients */ | |||
| if (pthread_mutex_trylock (&engine->cleanup_lock) == 0) { | |||
| pthread_t cleanup_thread; | |||
| pthread_create (&cleanup_thread, NULL, jack_cleanup_clients, engine); | |||
| for (node = engine->clients; node; ) { | |||
| tmp = g_slist_next (node); | |||
| client = (jack_client_internal_t *) node->data; | |||
| if (client->error) { | |||
| if (engine->verbose) { | |||
| fprintf (stderr, "removing failed client %s state = %s\n", | |||
| client->control->name, client_state_names[client->control->state]); | |||
| } | |||
| jack_remove_client (engine, (jack_client_internal_t *) node->data); | |||
| need_sort = TRUE; | |||
| } | |||
| node = tmp; | |||
| } | |||
| if (need_sort) { | |||
| jack_sort_graph (engine); | |||
| } | |||
| } | |||
| jack_unlock_graph (engine); | |||
| return 0; | |||
| } | |||
| @@ -1152,7 +1126,6 @@ jack_engine_new (int realtime, int rtpriority, int verbose) | |||
| engine->asio_mode = FALSE; | |||
| pthread_mutex_init (&engine->client_lock, 0); | |||
| pthread_mutex_init (&engine->cleanup_lock, 0); | |||
| pthread_mutex_init (&engine->buffer_lock, 0); | |||
| pthread_mutex_init (&engine->port_lock, 0); | |||
| @@ -2563,6 +2536,13 @@ jack_port_assign_buffer (jack_engine_t *engine, jack_port_internal_t *port) | |||
| break; | |||
| } | |||
| } | |||
| if (engine->verbose) { | |||
| fprintf (stderr, "port %s assigned buffer in shm key 0x%x at offset %d\n", | |||
| port->shared->name, | |||
| port->shared->shm_key, | |||
| port->shared->offset); | |||
| } | |||
| if (port->shared->shm_key >= 0) { | |||
| engine->port_buffer_freelist = g_slist_remove (engine->port_buffer_freelist, bi); | |||
| @@ -34,7 +34,8 @@ typedef void (*ReadCopyFunction) (sample_t *dst, char *src, | |||
| unsigned long src_skip_bytes); | |||
| typedef void (*WriteCopyFunction) (char *dst, sample_t *src, | |||
| unsigned long src_bytes, | |||
| unsigned long dst_skip_bytes); | |||
| unsigned long dst_skip_bytes, | |||
| dither_state_t *state); | |||
| typedef void (*CopyCopyFunction) (char *dst, char *src, | |||
| unsigned long src_bytes, | |||
| unsigned long dst_skip_bytes, | |||
| @@ -102,7 +103,8 @@ typedef struct { | |||
| WriteCopyFunction write_via_copy; | |||
| CopyCopyFunction channel_copy; | |||
| int dither; | |||
| int dither; | |||
| dither_state_t *dither_state; | |||
| SampleClockMode clock_mode; | |||
| GSList *clock_sync_listeners; | |||
| @@ -161,7 +163,8 @@ static __inline__ void alsa_driver_write_to_channel (alsa_driver_t *driver, | |||
| driver->write_via_copy (driver->playback_addr[channel], | |||
| buf, | |||
| nsamples, | |||
| driver->playback_interleave_skip); | |||
| driver->playback_interleave_skip, | |||
| driver->dither_state+channel); | |||
| alsa_driver_mark_channel_done (driver, channel); | |||
| } | |||
| @@ -38,7 +38,6 @@ struct _jack_engine { | |||
| void (*process_unlock)(struct _jack_engine *); | |||
| int (*post_process)(struct _jack_engine *); | |||
| pthread_mutex_t client_lock; | |||
| pthread_mutex_t cleanup_lock; | |||
| pthread_mutex_t buffer_lock; | |||
| pthread_mutex_t port_lock; | |||
| int process_errors; | |||
| @@ -23,17 +23,18 @@ | |||
| #include <jack/types.h> | |||
| void sample_move_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip); | |||
| void sample_move_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip); | |||
| void sample_move_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
| void sample_move_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
| void sample_move_dither_rect_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip); | |||
| void sample_move_dither_rect_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
| void sample_move_dither_tri_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
| void sample_move_dither_shaped_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
| void sample_move_dS_s32u24 (sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); | |||
| void sample_move_dS_s16 (sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); | |||
| void sample_merge_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip); | |||
| void sample_merge_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip); | |||
| void sample_merge_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
| void sample_merge_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
| static __inline__ void | |||
| sample_merge (sample_t *dst, sample_t *src, unsigned long cnt) | |||
| @@ -70,4 +70,21 @@ typedef enum { | |||
| NoSync = 0x8 | |||
| } ClockSyncStatus; | |||
| typedef enum { | |||
| None, | |||
| Rectangular, | |||
| Triangular, | |||
| Shaped | |||
| } DitherAlgorithm; | |||
| #define DITHER_BUF_SIZE 8 | |||
| #define DITHER_BUF_MASK 7 | |||
| typedef struct { | |||
| unsigned int depth; | |||
| float rm1; | |||
| unsigned int idx; | |||
| float e[DITHER_BUF_SIZE]; | |||
| } dither_state_t; | |||
| #endif /* __jack_engine_types_h__ */ | |||
| @@ -41,7 +41,7 @@ inline int f_round(float f) { | |||
| floating-point => int conversion the compiler provides. | |||
| */ | |||
| void sample_move_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip) | |||
| void sample_move_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) | |||
| { | |||
| /* ALERT: signed sign-extension portability !!! */ | |||
| @@ -64,7 +64,7 @@ void sample_move_dS_s32u24 (sample_t *dst, char *src, unsigned long nsamples, un | |||
| } | |||
| } | |||
| void sample_move_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip) | |||
| void sample_move_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) | |||
| { | |||
| sample_t val; | |||
| @@ -89,7 +89,7 @@ void sample_move_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsi | |||
| } | |||
| } | |||
| void sample_move_dither_rect_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip) | |||
| void sample_move_dither_rect_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) | |||
| { | |||
| sample_t val; | |||
| @@ -112,6 +112,82 @@ void sample_move_dither_rect_d16_sS (char *dst, sample_t *src, unsigned long ns | |||
| } | |||
| } | |||
| void sample_move_dither_tri_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) | |||
| { | |||
| sample_t x; | |||
| float r; | |||
| float rm1 = state->rm1; | |||
| int y; | |||
| while (nsamples--) { | |||
| x = *src * (float)SAMPLE_MAX_16BIT; | |||
| r = 2.0f * (float)rand() / (float)RAND_MAX - 1.0f; | |||
| x += r - rm1; | |||
| rm1 = r; | |||
| /* swh: This could be some inline asm on x86 */ | |||
| y = f_round(x); | |||
| if (y > SHRT_MAX) { | |||
| *((short *)dst) = SHRT_MAX; | |||
| } else if (y < SHRT_MIN) { | |||
| *((short *)dst) = SHRT_MIN; | |||
| } else { | |||
| *((short *) dst) = (short)y; | |||
| } | |||
| dst += dst_skip; | |||
| src++; | |||
| } | |||
| state->rm1 = rm1; | |||
| } | |||
| void sample_move_dither_shaped_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) | |||
| { | |||
| sample_t x; | |||
| sample_t xe; /* the innput sample - filtered error */ | |||
| sample_t xp; /* x' */ | |||
| float r; | |||
| float rm1 = state->rm1; | |||
| unsigned int idx = state->idx; | |||
| int y; | |||
| while (nsamples--) { | |||
| x = *src * (float)SAMPLE_MAX_16BIT; | |||
| r = 2.0f * (float)rand() / (float)RAND_MAX - 1.0f; | |||
| /* Filter the error with Lipshitz's minimally audible FIR: | |||
| [2.033 -2.165 1.959 -1.590 0.6149] */ | |||
| xe = x | |||
| - state->e[idx] * 2.033f | |||
| + state->e[(idx - 1) & DITHER_BUF_MASK] * 2.165f | |||
| - state->e[(idx - 2) & DITHER_BUF_MASK] * 1.959f | |||
| + state->e[(idx - 3) & DITHER_BUF_MASK] * 1.590f | |||
| - state->e[(idx - 4) & DITHER_BUF_MASK] * 0.6149f; | |||
| xp = xe + r - rm1; | |||
| rm1 = r; | |||
| /* This could be some inline asm on x86 */ | |||
| y = f_round(xp); | |||
| /* Intrinsic z^-1 delay */ | |||
| idx = (idx + 1) & DITHER_BUF_MASK; | |||
| state->e[idx] = y - xe; | |||
| if (y > SHRT_MAX) { | |||
| *((short *)dst) = SHRT_MAX; | |||
| } else if (y < SHRT_MIN) { | |||
| *((short *)dst) = SHRT_MIN; | |||
| } else { | |||
| *((short *) dst) = (short)y; | |||
| } | |||
| dst += dst_skip; | |||
| src++; | |||
| } | |||
| state->rm1 = rm1; | |||
| state->idx = idx; | |||
| } | |||
| void sample_move_dS_s16 (sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) | |||
| { | |||
| @@ -123,7 +199,7 @@ void sample_move_dS_s16 (sample_t *dst, char *src, unsigned long nsamples, unsig | |||
| } | |||
| } | |||
| void sample_merge_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip) | |||
| void sample_merge_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) | |||
| { | |||
| short val; | |||
| @@ -144,7 +220,7 @@ void sample_merge_d16_sS (char *dst, sample_t *src, unsigned long nsamples, uns | |||
| } | |||
| } | |||
| void sample_merge_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip) | |||
| void sample_merge_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) | |||
| { | |||
| /* ALERT: signed sign-extension portability !!! */ | |||