From af6aa1c03bad837c366dcb603b6d922a07c52814 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 15 May 2016 21:03:38 +0100 Subject: OpenBSD compilable : - md5sum is not available. - adding sndio support from Jacob Meuser . - various patches from ajacoutot@, jasper@ and sthen@ --- configure.ac | 26 +- drivers/Makefile.am | 10 +- drivers/dummy/dummy_driver.c | 5 +- drivers/sndio/Makefile.am | 13 + drivers/sndio/sndio_driver.c | 997 +++++++++++++++++++++++++++++++++++++++++++ drivers/sndio/sndio_driver.h | 74 ++++ jackd/Makefile.am | 4 + jackd/controlapi.c | 2 +- jackd/engine.c | 2 +- jackd/jackd.1.in | 41 +- jackd/jackd.c | 2 +- 11 files changed, 1167 insertions(+), 9 deletions(-) create mode 100644 drivers/sndio/Makefile.am create mode 100644 drivers/sndio/sndio_driver.c create mode 100644 drivers/sndio/sndio_driver.h diff --git a/configure.ac b/configure.ac index 49cf862..6150970 100644 --- a/configure.ac +++ b/configure.ac @@ -165,15 +165,18 @@ CFLAGS="$ORIGINAL_CFLAGS" AC_MSG_CHECKING([platform dependencies]) HOST_DEFAULT_TMP_DIR=/dev/shm +USE_MD5SUM=1 case "${host_os}" in freebsd*) # current FreeBSD header files conflict with the OSS driver's # barrier code, this may be fixed in 5.3, stay tuned. USE_BARRIER="no" + USE_MD5SUM=0 ;; openbsd*) # pthread_barrier* not implemented USE_BARRIER="no" + USE_MD5SUM=0 # need small realtime stack JACK_THREAD_STACK_TOUCH=10000 ;; @@ -189,6 +192,9 @@ case "${host_os}" in esac AC_SUBST(OS_LDFLAGS) +AC_SUBST(USE_MD5SUM) +AC_DEFINE_UNQUOTED(USE_MD5SUM,"$USE_MD5SUM",[Using md5sum command line if available]) +AM_CONDITIONAL(USE_MD5SUM, $USE_MD5SUM) # # We need to establish suitable defaults for a 64-bit OS @@ -228,7 +234,7 @@ if test "x$JACK_USE_MACH_THREADS" != "x"; then fi # headers -AC_CHECK_HEADERS(string.h strings.h alloca.h db.h, [], +AC_CHECK_HEADERS(string.h strings.h db.h, [], AC_MSG_ERROR([*** a required header file is missing])) AC_CHECK_HEADERS(getopt.h, [], [ @@ -783,7 +789,7 @@ AC_SUBST(NETJACK_CFLAGS) # Check which backend drivers can be built. The last one successfully # configured becomes the default JACK driver; so the order of -# precedence is: alsa, sun, oss, coreaudio, portaudio, dummy. +# precedence is: alsa, sun, oss, coreaudio, portaudio, sndio, dummy. JACK_DEFAULT_DRIVER=\"dummy\" @@ -843,6 +849,20 @@ then fi AM_CONDITIONAL(HAVE_SUN, $HAVE_SUN) +AC_ARG_ENABLE(sndio, AC_HELP_STRING([--disable-sndio],[ignore sndio driver ]), + TRY_SNDIO=$enableval , TRY_SNDIO=yes ) +HAVE_SNDIO="false" +if test "x$TRY_SNDIO" = "xyes" +then + # check for sndio audio API + AC_CHECK_HEADER([sndio.h], + [HAVE_SNDIO="true" + JACK_DEFAULT_DRIVER=\"sndio\"]) + SNDIO_LIBS="-lsndio" + AC_SUBST([SNDIO_LIBS]) +fi +AM_CONDITIONAL(HAVE_SNDIO, $HAVE_SNDIO) + AC_ARG_ENABLE(freebob, AC_HELP_STRING([--disable-freebob],[ignore FreeBob driver ]), TRY_FREEBOB=$enableval , TRY_FREEBOB=yes ) HAVE_FREEBOB="false" @@ -975,6 +995,7 @@ drivers/alsa_midi/Makefile drivers/dummy/Makefile drivers/oss/Makefile drivers/sun/Makefile +drivers/sndio/Makefile drivers/portaudio/Makefile drivers/coreaudio/Makefile drivers/freebob/Makefile @@ -1006,6 +1027,7 @@ echo \| Build with old FireWire \(FreeBob\) support............. : $HAVE_FREEBOB echo \| Build with new FireWire \(FFADO\) support............... : $HAVE_FIREWIRE echo \| Build with OSS support................................ : $HAVE_OSS echo \| Build with Sun audio support.......................... : $HAVE_SUN +echo \| Build with Sndio audio support........................ : $HAVE_SNDIO echo \| Build with CoreAudio support.......................... : $HAVE_COREAUDIO echo \| Build with PortAudio support.......................... : $HAVE_PA echo \| Build with Celt support............................... : $HAVE_CELT diff --git a/drivers/Makefile.am b/drivers/Makefile.am index b159b19..4554857 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -48,5 +48,11 @@ else FIREWIRE_DIR = endif -SUBDIRS = $(ALSA_MIDI_DIR) $(ALSA_DIR) dummy $(OSS_DIR) $(SUN_DIR) $(PA_DIR) $(CA_DIR) $(FREEBOB_DIR) $(FIREWIRE_DIR) netjack -DIST_SUBDIRS = alsa alsa_midi dummy oss sun portaudio coreaudio freebob firewire netjack +if HAVE_SNDIO +SNDIO_DIR = sndio +else +SNDIO_DIR = +endif + +SUBDIRS = $(ALSA_MIDI_DIR) $(ALSA_DIR) dummy $(OSS_DIR) $(SUN_DIR) $(PA_DIR) $(CA_DIR) $(FREEBOB_DIR) $(FIREWIRE_DIR) ${SNDIO_DIR} netjack +DIST_SUBDIRS = alsa alsa_midi dummy oss sun portaudio coreaudio freebob firewire netjack sndio diff --git a/drivers/dummy/dummy_driver.c b/drivers/dummy/dummy_driver.c index d414cb1..42092c7 100644 --- a/drivers/dummy/dummy_driver.c +++ b/drivers/dummy/dummy_driver.c @@ -111,6 +111,7 @@ dummy_driver_wait (dummy_driver_t *driver, int extra_fd, int *status, { jack_nframes_t nframes = driver->period_size; struct timespec now; + struct timespec ts; *status = 0; /* this driver doesn't work so well if we report a delay */ @@ -136,7 +137,9 @@ dummy_driver_wait (dummy_driver_t *driver, int extra_fd, int *status, } driver->next_wakeup = add_ts (driver->next_wakeup, driver->wait_time); } else { - if (clock_nanosleep (CLOCK_REALTIME, TIMER_ABSTIME, &driver->next_wakeup, NULL)) { + ts.tv_sec = 0; + ts.tv_nsec = ts_to_nsec(driver->next_wakeup) - ts_to_nsec(now); + if (nanosleep (&ts, NULL)) { jack_error ("error while sleeping"); *status = -1; } else { diff --git a/drivers/sndio/Makefile.am b/drivers/sndio/Makefile.am new file mode 100644 index 0000000..1e22bef --- /dev/null +++ b/drivers/sndio/Makefile.am @@ -0,0 +1,13 @@ +MAINTAINCLEANFILES = Makefile.in + +AM_CFLAGS = $(JACK_CFLAGS) + +plugindir = $(ADDON_DIR) + +plugin_LTLIBRARIES = jack_sndio.la + +jack_sndio_la_LDFLAGS = -module -avoid-version +jack_sndio_la_LIBADD = $(SNDIO_LIBS) +jack_sndio_la_SOURCES = sndio_driver.c sndio_driver.h + +noinst_HEADERS = sndio_driver.h diff --git a/drivers/sndio/sndio_driver.c b/drivers/sndio/sndio_driver.c new file mode 100644 index 0000000..49ab79f --- /dev/null +++ b/drivers/sndio/sndio_driver.c @@ -0,0 +1,997 @@ +/* + * Copyright (c) 2009 Jacob Meuser + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#ifndef _REENTRANT +#define _REENTRANT +#endif +#ifndef _THREAD_SAFE +#define _THREAD_SAFE +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sndio_driver.h" + + +#define SNDIO_DRIVER_N_PARAMS 10 +const static jack_driver_param_desc_t sndio_params[SNDIO_DRIVER_N_PARAMS] = { + { "rate", + 'r', + JackDriverParamUInt, + { .ui = SNDIO_DRIVER_DEF_FS }, + NULL, + "sample rate", + "sample rate" + }, + { "period", + 'p', + JackDriverParamUInt, + { .ui = SNDIO_DRIVER_DEF_BLKSIZE }, + NULL, + "period size", + "period size" + }, + { "nperiods", + 'n', + JackDriverParamUInt, + { .ui = SNDIO_DRIVER_DEF_NPERIODS }, + NULL, + "number of periods in buffer", + "number of periods in buffer" + }, + { "wordlength", + 'w', + JackDriverParamInt, + { .i = SNDIO_DRIVER_DEF_BITS }, + NULL, + "word length", + "word length" + }, + { "inchannels", + 'i', + JackDriverParamUInt, + { .ui = SNDIO_DRIVER_DEF_INS }, + NULL, + "capture channels", + "capture channels" + }, + { "outchannels", + 'o', + JackDriverParamUInt, + { .ui = SNDIO_DRIVER_DEF_OUTS }, + NULL, + "playback channels", + "playback channels" + }, + { "device", + 'd', + JackDriverParamString, + { }, + NULL, + "device", + "device" + }, + { "ignorehwbuf", + 'b', + JackDriverParamBool, + { }, + NULL, + "ignore hardware period size", + "ignore hardware period size" + }, + { "input latency", + 'I', + JackDriverParamUInt, + { .ui = 0 }, + NULL, + "system capture latency", + "system capture latency" + }, + { "output latency", + 'O', + JackDriverParamUInt, + { .ui = 0 }, + NULL, + "system playback latency", + "system playback latency" + } +}; + + +/* internal functions */ + + +static void +set_period_size (sndio_driver_t *driver, jack_nframes_t new_period_size) +{ + driver->period_size = new_period_size; + + driver->period_usecs = + ((double)driver->period_size / + (double)driver->sample_rate) * 1e6; + driver->last_wait_ust = 0; + driver->poll_timeout = (int)(driver->period_usecs / 666); +} + + +static void +sndio_driver_write_silence (sndio_driver_t *driver, jack_nframes_t nframes) +{ + size_t localsize, io_res, nbytes, offset; + void *localbuf; + + localsize = nframes * driver->sample_bytes * driver->playback_channels; + localbuf = malloc(localsize); + if (localbuf == NULL) + { + jack_error("sndio_driver: malloc() failed: %s@%i", + __FILE__, __LINE__); + return; + } + memset(localbuf, 0, localsize); + + offset = 0; + nbytes = localsize; + while (nbytes > 0) + { + io_res = sio_write(driver->hdl, localbuf + offset, nbytes); + if (io_res == 0) + { + jack_error("sndio_driver: sio_write() failed: " + "count=%d/%d: %s@%i", io_res, localsize, + __FILE__, __LINE__); + } + offset += io_res; + nbytes -= io_res; + } + free(localbuf); +} + + +static void +sndio_driver_read_silence (sndio_driver_t *driver, jack_nframes_t nframes) +{ + size_t localsize, io_res, nbytes, offset; + void *localbuf; + + localsize = nframes * driver->sample_bytes * driver->capture_channels; + localbuf = malloc(localsize); + if (localbuf == NULL) + { + jack_error("sndio_driver: malloc() failed: %s@%i", + __FILE__, __LINE__); + return; + } + + offset = 0; + nbytes = localsize; + while (nbytes > 0) { + io_res = sio_read(driver->hdl, localbuf + offset, nbytes); + if (io_res == 0) { + jack_error("sndio_driver: sio_read() failed: " + "count=%d/%d: %s@%i", io_res, nbytes, + __FILE__, __LINE__); + break; + } + offset +=- io_res; + nbytes -= io_res; + } + free(localbuf); +} + + +static int +sndio_driver_start (sndio_driver_t *driver) +{ + if (!sio_start(driver->hdl)) + jack_error("sio_start failed: %s@%i", + __FILE__, __LINE__); + + /* prime playback buffers */ + if (driver->playback_channels > 0) + sndio_driver_write_silence(driver, driver->pprime); + + return 0; +} + + +static int +sndio_driver_set_parameters (sndio_driver_t *driver) +{ + struct sio_par par; + unsigned int period_size = 0; + unsigned int nperiods; + int mode = 0; + + if (driver->capture_channels > 0) + mode |= SIO_REC; + + if (driver->playback_channels > 0) + mode |= SIO_PLAY; + + driver->hdl = sio_open(driver->dev, mode, 0); + if (driver->hdl == NULL) + { + jack_error("sndio_driver: failed to open device " + "%s: %s@%i", (driver->dev == NULL) ? + "default" : driver->dev, __FILE__, __LINE__); + return -1; + } + + if (driver->bits != 16 && driver->bits != 24 && driver->bits != 32) + { + jack_error("sndio_driver: invalid sample bits"); + return -1; + } + + sio_initpar(&par); + par.sig = 1; + par.bits = driver->bits; + par.pchan = driver->playback_channels; + par.rchan = driver->capture_channels; + par.rate = driver->sample_rate; + par.appbufsz = driver->period_size * driver->nperiods; + par.round = driver->period_size; + par.xrun = SIO_SYNC; + + if (!sio_setpar(driver->hdl, &par)) + { + jack_error("sndio_driver: failed to set parameters: %s@%i", + __FILE__, __LINE__); + return -1; + } + + if (!sio_getpar(driver->hdl, &par)) + { + jack_error("sndio_driver: sio_getpar() failed: %s@%i", + __FILE__, __LINE__); + return -1; + } + + if (par.sig != 1 || par.bits != driver->bits || + par.pchan != driver->playback_channels || + par.rchan != driver->capture_channels || + par.rate != driver->sample_rate) + { + jack_error("sndio_driver: setting parameters failed: %s@%i", + __FILE__, __LINE__); + return -1; + } + + period_size = par.round; + nperiods = par.appbufsz / par.round; + driver->sample_bytes = par.bps; + driver->pprime = par.bufsz; + + if (period_size != 0 && !driver->ignorehwbuf && + (period_size != driver->period_size || + nperiods != driver->nperiods)) + { + printf("sndio_driver: buffer update: " + "period_size=%u, nperiods=%u\n", period_size, nperiods); + + driver->nperiods = nperiods; + set_period_size(driver, period_size); + + if (driver->engine) + driver->engine->set_buffer_size(driver->engine, + driver->period_size); + } + + driver->capbufsize = 0; + driver->capbuf = NULL; + if (driver->capture_channels != 0) + { + driver->capbufsize = driver->period_size * + driver->capture_channels * driver->sample_bytes; + driver->capbuf = malloc(driver->capbufsize); + if (driver->capbuf == NULL) + { + jack_error("sndio_driver: malloc() failed: %s@%i", + __FILE__, __LINE__); + return -1; + } + memset(driver->capbuf, 0, driver->capbufsize); + } + + driver->playbufsize = 0; + driver->playbuf = NULL; + if (driver->playback_channels > 0) + { + driver->playbufsize = driver->period_size * + driver->playback_channels * driver->sample_bytes; + driver->playbuf = malloc(driver->playbufsize); + if (driver->playbuf == NULL) + { + jack_error("sndio_driver: malloc() failed: %s@%i", + __FILE__, __LINE__); + return -1; + } + memset(driver->playbuf, 0, driver->playbufsize); + } + + printf("sndio_driver: capbuf %zd B, playbuf %zd B\n", + driver->capbufsize, driver->playbufsize); + + return 0; +} + + +static int +sndio_driver_stop (sndio_driver_t *driver) +{ + if (driver->hdl != NULL) + sio_stop(driver->hdl); + + return 0; +} + + +static jack_nframes_t +sndio_driver_wait (sndio_driver_t *driver, int *status, float *iodelay) +{ + struct pollfd pfd; + nfds_t snfds, nfds; + jack_time_t poll_ret; + int need_capture, need_playback; + int events, revents; + + *status = 0; + *iodelay = 0; + + need_capture = need_playback = 0; + + if (driver->capture_channels > 0) + need_capture = 1; + + if (driver->playback_channels > 0) + need_playback = 1; + + if (jack_get_microseconds() > driver->poll_next) + { + /* late. don't count as wakeup delay. */ + driver->poll_next = 0; + } + + snfds = sio_nfds(driver->hdl); + + while (need_capture || need_playback) + { + events = 0; + if (need_capture) + events |= POLLIN; + + if (need_playback) + events |= POLLOUT; + + if (snfds != sio_pollfd(driver->hdl, &pfd, events)) { + jack_error("sndio_driver: sio_pollfd failed: %s@%i", + __FILE__, __LINE__); + *status = -1; + return 0; + } + nfds = poll(&pfd, snfds, 1000); + if (nfds == -1) + { + jack_error("sndio_driver: poll() error: %s: %s@%i", + strerror(errno), __FILE__, __LINE__); + *status = -1; + return 0; + } + else if (nfds == 0) + { + jack_error("sndio_driver: poll() time out: %s@%i", + __FILE__, __LINE__); + *status = -1; + return 0; + } + revents = sio_revents(driver->hdl, &pfd); + if (revents & (POLLERR | POLLHUP | POLLNVAL)) + { + jack_error("sndio_driver: poll() error: %s@%i", + __FILE__, __LINE__); + *status = -1; + return 0; + } + + if (revents & POLLIN) + need_capture = 0; + + if (revents & POLLOUT) + need_playback = 0; + + if (sio_eof(driver->hdl)) + { + jack_error("sndio_driver: sio_eof(): %s@%i", + __FILE__, __LINE__); + *status = -1; + return 0; + } + } + poll_ret = jack_get_microseconds(); + + if (driver->poll_next && poll_ret > driver->poll_next) + *iodelay = poll_ret - driver->poll_next; + + driver->poll_next = poll_ret + driver->period_usecs; + driver->engine->transport_cycle_start(driver->engine, poll_ret); + + driver->last_wait_ust = poll_ret; + + return driver->period_size; +} + + +static inline int +sndio_driver_run_cycle (sndio_driver_t *driver) +{ + jack_nframes_t nframes; + int wait_status; + float iodelay; + + nframes = sndio_driver_wait(driver, &wait_status, &iodelay); + + if (wait_status < 0) + return -1; + + return driver->engine->run_cycle(driver->engine, nframes, iodelay); +} + + +static void +copy_and_convert_in (jack_sample_t *dst, void *src, + size_t nframes, int channel, int chcount, int bits) +{ + int srcidx, dstidx; + signed short *s16src = (signed short *)src; + signed int *s32src = (signed int *)src; + jack_sample_t scale; + + srcidx = channel; + switch (bits) + { + case 16: + scale = 1.0f / 0x7fff; + for (dstidx = 0; dstidx < nframes; dstidx++) + { + dst[dstidx] = (jack_sample_t) + s16src[srcidx] * scale; + srcidx += chcount; + } + break; + case 24: + case 32: + scale = 1.0f / 0x7fffffff; + for (dstidx = 0; dstidx < nframes; dstidx++) + { + dst[dstidx] = (jack_sample_t) + s32src[srcidx] * scale; + srcidx += chcount; + } + break; + } +} + + +static void +copy_and_convert_out (void *dst, jack_sample_t *src, + size_t nframes, int channel, int chcount, int bits) +{ + int srcidx; + int dstidx; + signed short *s16dst = (signed short *)dst; + signed int *s32dst = (signed int *)dst; + jack_sample_t scale; + + dstidx = channel; + switch (bits) + { + case 16: + scale = 0x7fff; + for (srcidx = 0; srcidx < nframes; srcidx++) + { + s16dst[dstidx] = (signed short) + (src[srcidx] >= 0.0f) ? + (src[srcidx] * scale + 0.5f) : + (src[srcidx] * scale - 0.5f); + dstidx += chcount; + } + break; + case 24: + case 32: + scale = 0x7fffffff; + for (srcidx = 0; srcidx < nframes; srcidx++) + { + s32dst[dstidx] = (signed int) + (src[srcidx] >= 0.0f) ? + (src[srcidx] * scale + 0.5f) : + (src[srcidx] * scale - 0.5f); + dstidx += chcount; + } + break; + } +} + + +/* jack driver interface */ + +static int +sndio_driver_attach (sndio_driver_t *driver) +{ + int port_flags; + int channel; + char channel_name[64]; + jack_port_t *port; + jack_latency_range_t range; + + driver->engine->set_buffer_size(driver->engine, driver->period_size); + driver->engine->set_sample_rate(driver->engine, driver->sample_rate); + + port_flags = JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal; + + for (channel = 0; channel < driver->capture_channels; channel++) + { + snprintf(channel_name, sizeof(channel_name), + "capture_%u", channel + 1); + port = jack_port_register(driver->client, channel_name, + JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); + if (port == NULL) + { + jack_error("sndio_driver: cannot register port for %s: " + "%s@%i", channel_name, __FILE__, __LINE__); + break; + } + range.min = range.max = driver->period_size + + driver->sys_cap_latency; + jack_port_set_latency_range(port, JackCaptureLatency, &range); + driver->capture_ports = + jack_slist_append(driver->capture_ports, port); + } + + port_flags = JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal; + for (channel = 0; channel < driver->playback_channels; channel++) + { + snprintf(channel_name, sizeof(channel_name), + "playback_%u", channel + 1); + port = jack_port_register(driver->client, channel_name, + JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); + if (port == NULL) + { + jack_error("sndio_driver: cannot register port for " + "%s: %s@%i", channel_name, __FILE__, __LINE__); + break; + } + range.min = range.max = driver->period_size + + driver->sys_play_latency; + jack_port_set_latency_range(port, JackPlaybackLatency, &range); + driver->playback_ports = + jack_slist_append(driver->playback_ports, port); + } + + return jack_activate(driver->client); +} + + +static int +sndio_driver_detach (sndio_driver_t *driver) +{ + JSList *node; + + if (driver->engine == NULL) + return 0; + + node = driver->capture_ports; + while (node != NULL) + { + jack_port_unregister(driver->client, + ((jack_port_t *) node->data)); + node = jack_slist_next(node); + } + if (driver->capture_ports != NULL) + { + jack_slist_free(driver->capture_ports); + driver->capture_ports = NULL; + } + + node = driver->playback_ports; + while (node != NULL) + { + jack_port_unregister(driver->client, + ((jack_port_t *) node->data)); + node = jack_slist_next(node); + } + if (driver->playback_ports != NULL) + { + jack_slist_free(driver->playback_ports); + driver->playback_ports = NULL; + } + + return 0; +} + + +static int +sndio_driver_read (sndio_driver_t *driver, jack_nframes_t nframes) +{ + jack_nframes_t nbytes, offset; + int channel; + size_t io_res; + jack_sample_t *portbuf; + JSList *node; + jack_port_t *port; + + if (driver->engine->freewheeling || driver->capture_channels == 0) + return 0; + + if (nframes > driver->period_size) + { + jack_error("sndio_driver: read failed: nframes > period_size: " + "(%u/%u): %s@%i", nframes, driver->period_size, + __FILE__, __LINE__); + return -1; + } + + node = driver->capture_ports; + channel = 0; + while (node != NULL) + { + port = (jack_port_t *)node->data; + + if (jack_port_connected(port)) + { + portbuf = jack_port_get_buffer(port, nframes); + copy_and_convert_in(portbuf, driver->capbuf, + nframes, channel, + driver->capture_channels, + driver->bits); + } + + node = jack_slist_next(node); + channel++; + } + + io_res = offset = 0; + nbytes = nframes * driver->capture_channels * driver->sample_bytes; + while (nbytes > 0) + { + io_res = sio_read(driver->hdl, driver->capbuf + offset, nbytes); + if (io_res == 0) + { + jack_error("sndio_driver: sio_read() failed: %s@%i", + __FILE__, __LINE__); + break; + } + offset += io_res; + nbytes -= io_res; + } + return 0; +} + + +static int +sndio_driver_write (sndio_driver_t *driver, jack_nframes_t nframes) +{ + jack_nframes_t nbytes; + int channel; + size_t io_res, offset; + jack_sample_t *portbuf; + JSList *node; + jack_port_t *port; + + if (driver->engine->freewheeling || driver->playback_channels == 0) + return 0; + + if (nframes > driver->period_size) + { + jack_error("sndio_driver: write failed: nframes > period_size " + "(%u/%u): %s@%i", nframes, driver->period_size, + __FILE__, __LINE__); + return -1; + } + + node = driver->playback_ports; + channel = 0; + while (node != NULL) + { + port = (jack_port_t *)node->data; + + if (jack_port_connected(port)) + { + portbuf = jack_port_get_buffer(port, nframes); + copy_and_convert_out(driver->playbuf, portbuf, + nframes, channel, + driver->playback_channels, + driver->bits); + } + + node = jack_slist_next(node); + channel++; + } + + io_res = offset = 0; + nbytes = nframes * driver->playback_channels * driver->sample_bytes; + while (nbytes > 0) + { + io_res = sio_write(driver->hdl, driver->playbuf + offset, nbytes); + if (io_res == 0) + { + jack_error("sndio_driver: sio_write() failed: %s@%i", + __FILE__, __LINE__); + break; + } + offset += io_res; + nbytes -= io_res; + } + memset(driver->playbuf, 0, driver->playbufsize); + return 0; +} + + +static int +sndio_driver_null_cycle (sndio_driver_t *driver, jack_nframes_t nframes) +{ + if (nframes > driver->period_size) + { + jack_error("sndio_driver: null cycle failed: " + "nframes > period_size (%u/%u): %s@%i", nframes, + driver->period_size, __FILE__, __LINE__); + return -1; + } + + printf("sndio_driver: running null cycle\n"); + + if (driver->playback_channels > 0) + sndio_driver_write_silence (driver, nframes); + + if (driver->capture_channels > 0) + sndio_driver_read_silence (driver, nframes); + + return 0; +} + + +static int +sndio_driver_bufsize (sndio_driver_t *driver, jack_nframes_t nframes) +{ + return sndio_driver_set_parameters(driver); +} + + +static void +sndio_driver_delete (sndio_driver_t *driver) +{ + if (driver->hdl != NULL) + { + sio_close(driver->hdl); + driver->hdl = NULL; + } + + if (driver->capbuf != NULL) + { + free(driver->capbuf); + driver->capbuf = NULL; + } + if (driver->playbuf != NULL) + { + free(driver->playbuf); + driver->playbuf = NULL; + } + + if (driver->dev != NULL) + { + free(driver->dev); + driver->dev = NULL; + } + + jack_driver_nt_finish((jack_driver_nt_t *) driver); + + if (driver != NULL) + { + free(driver); + driver = NULL; + } +} + + +void +driver_finish (jack_driver_t *driver) +{ + sndio_driver_delete((sndio_driver_t *)driver); +} + + +static jack_driver_t * +sndio_driver_new (char *dev, jack_client_t *client, + jack_nframes_t sample_rate, jack_nframes_t period_size, + jack_nframes_t nperiods, int bits, + int capture_channels, int playback_channels, + jack_nframes_t cap_latency, jack_nframes_t play_latency, + int ignorehwbuf) +{ + sndio_driver_t *driver; + + driver = (sndio_driver_t *)calloc(1, sizeof(sndio_driver_t)); + if (driver == NULL) + { + jack_error("sndio_driver: malloc() failed: %s: %s@%i", + strerror(errno), __FILE__, __LINE__); + return NULL; + } + driver->engine = NULL; + jack_driver_nt_init((jack_driver_nt_t *)driver); + + driver->nt_attach = (JackDriverNTAttachFunction)sndio_driver_attach; + driver->nt_detach = (JackDriverNTDetachFunction)sndio_driver_detach; + driver->read = (JackDriverReadFunction)sndio_driver_read; + driver->write = (JackDriverWriteFunction)sndio_driver_write; + driver->null_cycle = (JackDriverNullCycleFunction)sndio_driver_null_cycle; + driver->nt_bufsize = (JackDriverNTBufSizeFunction)sndio_driver_bufsize; + driver->nt_start = (JackDriverNTStartFunction)sndio_driver_start; + driver->nt_stop = (JackDriverNTStopFunction)sndio_driver_stop; + driver->nt_run_cycle = (JackDriverNTRunCycleFunction)sndio_driver_run_cycle; + + if (dev != NULL) + driver->dev = strdup(dev); + else + driver->dev = NULL; + + driver->ignorehwbuf = ignorehwbuf; + + driver->sample_rate = sample_rate; + driver->period_size = period_size; + driver->orig_period_size = period_size; + driver->nperiods = nperiods; + driver->bits = bits; + driver->capture_channels = capture_channels; + driver->playback_channels = playback_channels; + driver->sys_cap_latency = cap_latency; + driver->sys_play_latency = play_latency; + + set_period_size(driver, period_size); + + driver->hdl = NULL; + driver->capbuf = driver->playbuf = NULL; + driver->capture_ports = driver->playback_ports = NULL; + + driver->poll_next = 0; + + if (sndio_driver_set_parameters(driver) < 0) + { + free(driver); + return NULL; + } + + driver->client = client; + + return (jack_driver_t *)driver; +} + + +/* jack driver published interface */ + + +const char driver_client_name[] = "sndio"; + + +jack_driver_desc_t * +driver_get_descriptor () +{ + jack_driver_desc_t *desc; + jack_driver_param_desc_t *params; + + desc = (jack_driver_desc_t *)calloc(1, sizeof(jack_driver_desc_t)); + if (desc == NULL) + { + jack_error("sndio_driver: calloc() failed: %s: %s@%i", + strerror(errno), __FILE__, __LINE__); + return NULL; + } + strlcpy(desc->name, driver_client_name, sizeof(desc->name)); + desc->nparams = SNDIO_DRIVER_N_PARAMS; + + params = calloc(desc->nparams, sizeof(jack_driver_param_desc_t)); + if (params == NULL) + { + jack_error("sndio_driver: calloc() failed: %s: %s@%i", + strerror(errno), __FILE__, __LINE__); + return NULL; + } + memcpy(params, sndio_params, + desc->nparams * sizeof(jack_driver_param_desc_t)); + desc->params = params; + + return desc; +} + + +jack_driver_t * +driver_initialize (jack_client_t *client, JSList * params) +{ + int bits = SNDIO_DRIVER_DEF_BITS; + jack_nframes_t sample_rate = SNDIO_DRIVER_DEF_FS; + jack_nframes_t period_size = SNDIO_DRIVER_DEF_BLKSIZE; + jack_nframes_t cap_latency = 0; + jack_nframes_t play_latency = 0; + unsigned int nperiods = SNDIO_DRIVER_DEF_NPERIODS; + unsigned int capture_channels = SNDIO_DRIVER_DEF_INS; + unsigned int playback_channels = SNDIO_DRIVER_DEF_OUTS; + const JSList *pnode; + const jack_driver_param_t *param; + char *dev = NULL; + int ignorehwbuf = 0; + + pnode = params; + while (pnode != NULL) + { + param = (const jack_driver_param_t *)pnode->data; + + switch (param->character) + { + case 'r': + sample_rate = param->value.ui; + break; + case 'p': + period_size = param->value.ui; + break; + case 'n': + nperiods = param->value.ui; + break; + case 'w': + bits = param->value.i; + break; + case 'i': + capture_channels = param->value.ui; + break; + case 'o': + playback_channels = param->value.ui; + break; + case 'd': + dev = strdup(param->value.str); + break; + case 'b': + ignorehwbuf = 1; + break; + case 'I': + cap_latency = param->value.ui; + break; + case 'O': + play_latency = param->value.ui; + break; + } + pnode = jack_slist_next(pnode); + } + + return sndio_driver_new(dev, client, sample_rate, period_size, + nperiods, bits, capture_channels, playback_channels, + cap_latency, play_latency, ignorehwbuf); +} diff --git a/drivers/sndio/sndio_driver.h b/drivers/sndio/sndio_driver.h new file mode 100644 index 0000000..03df4f1 --- /dev/null +++ b/drivers/sndio/sndio_driver.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2009 Jacob Meuser + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __JACK_SNDIO_DRIVER_H__ +#define __JACK_SNDIO_DRIVER_H__ + +#include +#include +#include + +#include +#include +#include +#include + +#define SNDIO_DRIVER_DEF_DEV "default" +#define SNDIO_DRIVER_DEF_FS 44100 +#define SNDIO_DRIVER_DEF_BLKSIZE 1024 +#define SNDIO_DRIVER_DEF_NPERIODS 2 +#define SNDIO_DRIVER_DEF_BITS 16 +#define SNDIO_DRIVER_DEF_INS 2 +#define SNDIO_DRIVER_DEF_OUTS 2 + +typedef jack_default_audio_sample_t jack_sample_t; + +typedef struct _sndio_driver +{ + JACK_DRIVER_NT_DECL + + jack_nframes_t sample_rate; + jack_nframes_t period_size; + jack_nframes_t orig_period_size; + unsigned int nperiods; + int bits; + unsigned int capture_channels; + unsigned int playback_channels; + jack_nframes_t sys_cap_latency; + jack_nframes_t sys_play_latency; + int ignorehwbuf; + + struct sio_hdl *hdl; + char *dev; + + void *capbuf; + size_t capbufsize; + void *playbuf; + size_t playbufsize; + JSList *capture_ports; + JSList *playback_ports; + + int sample_bytes; + size_t pprime; + + int poll_timeout; + jack_time_t poll_next; + + jack_client_t *client; + +} sndio_driver_t; + +#endif diff --git a/jackd/Makefile.am b/jackd/Makefile.am index eb03ca3..b284f9a 100644 --- a/jackd/Makefile.am +++ b/jackd/Makefile.am @@ -37,7 +37,11 @@ jack_md5.h: jackd if STRIPPED_JACKD strip -R .note -R .comment .libs/jackd endif +if USE_MD5SUM echo "#define JACKD_MD5_SUM \"`md5sum .libs/jackd | awk '{print $$1}'`\"" > jack_md5.h +else + echo "#define JACKD_MD5_SUM \"`md5 -q .libs/jackd | awk '{print $$1}'`\"" > jack_md5.h +endif jackstart_SOURCES = jackstart.c md5.c jackstart_LDFLAGS = -lcap diff --git a/jackd/controlapi.c b/jackd/controlapi.c index 90edc28..a117e20 100644 --- a/jackd/controlapi.c +++ b/jackd/controlapi.c @@ -843,7 +843,7 @@ get_realtime_priority_constraint () return constraint_ptr; #else - return NULL + return NULL; #endif } diff --git a/jackd/engine.c b/jackd/engine.c index c6b2e60..1ca6584 100644 --- a/jackd/engine.c +++ b/jackd/engine.c @@ -1429,7 +1429,7 @@ handle_external_client_request (jack_engine_t *engine, int fd) if ((r = read (client->request_fd, &req, sizeof(req))) < (ssize_t)sizeof(req)) { if (r == 0) { -#ifdef JACK_USE_MACH_THREADS +#if defined(JACK_USE_MACH_THREADS) || defined(__OpenBSD__) /* poll is implemented using select (see the macosx/fakepoll code). When the socket is closed diff --git a/jackd/jackd.1.in b/jackd/jackd.1.in index 1bc7c9c..6b86210 100644 --- a/jackd/jackd.1.in +++ b/jackd/jackd.1.in @@ -28,7 +28,7 @@ For the latest JACK information, please consult the web site, .br Select the audio interface backend. The current list of supported backends is: \fBalsa\fR, \fBcoreaudio\fR, \fBdummy\fR, \fBfreebob\fR, -\fBoss\fR \fBsun\fR and \fBportaudio\fR. They are not all available +\fBoss\fR \fBsun\fR \fBportaudio\fR and \fB sndio. They are not all available on all platforms. All \fIbackend\-parameters\fR are optional. .TP \fB\-h, \-\-help\fR @@ -523,6 +523,43 @@ Driver name (default: none) .TP \fB\-z \-\-dither\fR Dithering mode (default: none) +.SS SNDIO BACKEND PARAMETERS +.TP +\fB\-r, \-\-rate \fIint\fR +Specify the sample rate. The default is 44100. +.TP +\fB\-p, \-\-period \fIint\fR +Specify the number of frames between JACK \fBprocess()\fR calls. This +value must be a power of 2, and the default is 1024. If you need low +latency, set \fB\-p\fR as low as you can go without seeing xruns. A larger +period size yields higher latency, but makes xruns less likely. The JACK +capture latency in seconds is \fB\-\-period\fR divided by \fB\-\-rate\fR. +Note that this value is mostly advisory when aucat server is running +and may be updated internally. +.TP +\fB\-n, \-\-nperiods \fIint\fR +Specify the number of periods in the hardware buffer. The default is +2. The period size (\fB\-p\fR) times \fB\-\-nperiods\fR times four +(assuming 2 channels 16-bit samples) is the JACK buffer size in bytes. +The JACK output latency in seconds is \fB\-\-nperiods\fR times +\fB\-\-period\fR divided by \fB\-\-rate\fR. +Note that this value is mostly advisory when aucat server is running +and may be updated internally. +.TP +\fB\-w, \-\-wordlength \fIint\fR +Specify the sample size in bits. The default is 16. +.TP +\fB\-i, \-\-inchannels \fIint\fR +Specify how many channels to capture (default: 2) +.TP +\fB\-o, \-\-outchannels \fIint\fR +Specify number of playback channels (default: 2) +.TP +\fB\-d, \-\-device \fIdevice_file\fR +Specify device for capture and playback (default: NULL) +.TP +\fB\-b, \-\-ignorehwbuf \fIboolean\fR +Specify, whether to ignore hardware period size (default: false) .SH "EXAMPLES" .PP Print usage message for the parameters specific to each backend. @@ -544,6 +581,8 @@ Print usage message for the parameters specific to each backend. \fBjackd \-d sun \-\-help\fR .br \fBjackd \-d portaudio \-\-help\fR +.br +\fBjackd \-d sndio \-\-help\fR .PP Run the JACK daemon with realtime priority using the first ALSA hardware card defined in \fB/etc/modules.conf\fR. diff --git a/jackd/jackd.c b/jackd/jackd.c index 0faae93..ff7242a 100644 --- a/jackd/jackd.c +++ b/jackd/jackd.c @@ -537,7 +537,7 @@ static void usage (FILE *file) #ifdef __APPLE__ " Available backends may include: coreaudio, dummy, net, portaudio.\n\n" #else - " Available backends may include: alsa, dummy, freebob, firewire, net, oss, sun, or portaudio.\n\n" + " Available backends may include: alsa, dummy, freebob, firewire, net, oss, sun, portaudio or sndio.\n\n" #endif " jackd -d backend --help\n" " to display options for each backend\n\n"); -- cgit v1.2.1