diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2008-11-11 19:39:36 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2008-11-11 19:39:36 +0000 |
commit | 66ce1beacebfce86cedb28308199ef5dbebb775c (patch) | |
tree | c5cd534c23219bbfd3653e19d8fea8a0499c1fc6 /lib/libsndio | |
parent | 3bc33a8c8c92b0092ffe1bb775de2cc4fc8ec6d0 (diff) |
expose a volume knob in the sndio API: add sio_setvol(3) and
sio_onvol(3) functions. The audio(4) backend tries to use the
inputs.dac, outputs.dac, outputs.output and outputs.master
controls (in this order). Add a sample file in
regress/lib/libsndio/vol/vol.c
Diffstat (limited to 'lib/libsndio')
-rw-r--r-- | lib/libsndio/Makefile | 4 | ||||
-rw-r--r-- | lib/libsndio/aucat.c | 132 | ||||
-rw-r--r-- | lib/libsndio/shlib_version | 2 | ||||
-rw-r--r-- | lib/libsndio/sio_open.3 | 41 | ||||
-rw-r--r-- | lib/libsndio/sndio.c | 44 | ||||
-rw-r--r-- | lib/libsndio/sndio.h | 11 | ||||
-rw-r--r-- | lib/libsndio/sndio_priv.h | 11 | ||||
-rw-r--r-- | lib/libsndio/sun.c | 128 |
8 files changed, 314 insertions, 59 deletions
diff --git a/lib/libsndio/Makefile b/lib/libsndio/Makefile index 6c0943fa968..e4eee6a8c61 100644 --- a/lib/libsndio/Makefile +++ b/lib/libsndio/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.1 2008/10/27 00:26:33 ratchov Exp $ +# $OpenBSD: Makefile,v 1.2 2008/11/11 19:39:35 ratchov Exp $ LIB= sndio MAN= sio_open.3 @@ -20,6 +20,8 @@ MLINKS = \ sio_open.3 sio_pollfd.3 \ sio_open.3 sio_revents.3 \ sio_open.3 sio_eof.3 \ + sio_open.3 sio_setvol.3 \ + sio_open.3 sio_onvol.3 \ sio_open.3 sio_initpar.3 includes: diff --git a/lib/libsndio/aucat.c b/lib/libsndio/aucat.c index 83689a16e37..14d6fc393ed 100644 --- a/lib/libsndio/aucat.c +++ b/lib/libsndio/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.1 2008/10/27 00:26:33 ratchov Exp $ */ +/* $OpenBSD: aucat.c,v 1.2 2008/11/11 19:39:35 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -40,6 +40,7 @@ struct aucat_hdl { unsigned rbpf, wbpf; /* read and write bytes-per-frame */ int maxwrite; /* latency constraint */ int events; /* events the user requested */ + unsigned curvol, reqvol; /* current and requested volume */ }; void aucat_close(struct sio_hdl *); @@ -52,6 +53,8 @@ size_t aucat_read(struct sio_hdl *, void *, size_t); size_t aucat_write(struct sio_hdl *, void *, size_t); int aucat_pollfd(struct sio_hdl *, struct pollfd *, int); int aucat_revents(struct sio_hdl *, struct pollfd *); +int aucat_setvol(struct sio_hdl *, unsigned); +void aucat_getvol(struct sio_hdl *); struct sio_ops aucat_ops = { aucat_close, @@ -63,7 +66,9 @@ struct sio_ops aucat_ops = { aucat_start, aucat_stop, aucat_pollfd, - aucat_revents + aucat_revents, + aucat_setvol, + aucat_getvol }; struct sio_hdl * @@ -101,6 +106,8 @@ sio_open_aucat(char *path, unsigned mode, int nbio) hdl->rtodo = 0xdeadbeef; hdl->wstate = STATE_IDLE; hdl->wtodo = 0xdeadbeef; + hdl->curvol = SIO_MAXVOL; + hdl->reqvol = SIO_MAXVOL; return (struct sio_hdl *)hdl; } @@ -434,15 +441,19 @@ aucat_read(struct sio_hdl *sh, void *buf, size_t len) return n; } -size_t -aucat_write(struct sio_hdl *sh, void *buf, size_t len) +int +aucat_buildmsg(struct aucat_hdl *hdl, size_t len) { - struct aucat_hdl *hdl = (struct aucat_hdl *)sh; unsigned sz; - ssize_t n; - switch (hdl->wstate) { - case STATE_IDLE: + if (hdl->curvol != hdl->reqvol) { + hdl->wstate = STATE_MSG; + hdl->wtodo = sizeof(struct amsg); + hdl->wmsg.cmd = AMSG_SETVOL; + hdl->wmsg.u.vol.ctl = hdl->reqvol; + hdl->curvol = hdl->reqvol; + return 1; + } else if (len > 0) { sz = (len < AMSG_DATAMAX) ? len : AMSG_DATAMAX; sz -= sz % hdl->wbpf; if (sz == 0) @@ -451,44 +462,63 @@ aucat_write(struct sio_hdl *sh, void *buf, size_t len) hdl->wtodo = sizeof(struct amsg); hdl->wmsg.cmd = AMSG_DATA; hdl->wmsg.u.data.size = sz; - /* PASSTHROUGH */ - case STATE_MSG: - if (!aucat_wmsg(hdl)) - return 0; - hdl->wstate = STATE_DATA; - hdl->wtodo = hdl->wmsg.u.data.size; - /* PASSTHROUGH */ - case STATE_DATA: - if (hdl->maxwrite <= 0) - return 0; - if (len > hdl->maxwrite) - len = hdl->maxwrite; - if (len > hdl->wtodo) - len = hdl->wtodo; - if (len == 0) { - fprintf(stderr, "aucat_write: len == 0\n"); + return 1; + } + return 0; +} + +size_t +aucat_write(struct sio_hdl *sh, void *buf, size_t len) +{ + struct aucat_hdl *hdl = (struct aucat_hdl *)sh; + ssize_t n; + + while (hdl->wstate != STATE_DATA) { + switch (hdl->wstate) { + case STATE_IDLE: + if (!aucat_buildmsg(hdl, len)) + return 0; + /* PASSTHROUGH */ + case STATE_MSG: + if (!aucat_wmsg(hdl)) + return 0; + if (hdl->wmsg.cmd == AMSG_DATA) { + hdl->wstate = STATE_DATA; + hdl->wtodo = hdl->wmsg.u.data.size; + } else + hdl->wstate = STATE_IDLE; + break; + default: + fprintf(stderr, "aucat_read: bad state\n"); abort(); } - while ((n = write(hdl->fd, buf, len)) < 0) { - if (errno == EINTR) - continue; - if (errno != EAGAIN) { - hdl->sa.eof = 1; - perror("aucat_read: read"); - } - return 0; - } - hdl->maxwrite -= n; - hdl->wtodo -= n; - if (hdl->wtodo == 0) { - hdl->wstate = STATE_IDLE; - hdl->wtodo = 0xdeadbeef; - } - return n; - default: - fprintf(stderr, "aucat_read: bad state\n"); + } + if (hdl->maxwrite <= 0) + return 0; + if (len > hdl->maxwrite) + len = hdl->maxwrite; + if (len > hdl->wtodo) + len = hdl->wtodo; + if (len == 0) { + fprintf(stderr, "aucat_write: len == 0\n"); abort(); } + while ((n = write(hdl->fd, buf, len)) < 0) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + hdl->sa.eof = 1; + perror("aucat_read: read"); + } + return 0; + } + hdl->maxwrite -= n; + hdl->wtodo -= n; + if (hdl->wtodo == 0) { + hdl->wstate = STATE_IDLE; + hdl->wtodo = 0xdeadbeef; + } + return n; } int @@ -526,3 +556,21 @@ aucat_revents(struct sio_hdl *sh, struct pollfd *pfd) } return revents & hdl->events; } + +int +aucat_setvol(struct sio_hdl *sh, unsigned vol) +{ + struct aucat_hdl *hdl = (struct aucat_hdl *)sh; + + hdl->reqvol = vol; + return 1; +} + +void +aucat_getvol(struct sio_hdl *sh) +{ + struct aucat_hdl *hdl = (struct aucat_hdl *)sh; + + sio_onvol_cb(&hdl->sa, hdl->reqvol); + return; +} diff --git a/lib/libsndio/shlib_version b/lib/libsndio/shlib_version index 012c14171d3..3f0196ebf4a 100644 --- a/lib/libsndio/shlib_version +++ b/lib/libsndio/shlib_version @@ -1,2 +1,2 @@ major=3 -minor=0 +minor=1 diff --git a/lib/libsndio/sio_open.3 b/lib/libsndio/sio_open.3 index 4a2eca2c59e..883d74ed07c 100644 --- a/lib/libsndio/sio_open.3 +++ b/lib/libsndio/sio_open.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sio_open.3,v 1.4 2008/11/09 19:49:10 naddy Exp $ +.\" $OpenBSD: sio_open.3,v 1.5 2008/11/11 19:39:35 ratchov Exp $ .\" .\" Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: November 9 2008 $ +.Dd $Mdocdate: November 11 2008 $ .Dt SIO_OPEN 3 .Os .Sh NAME @@ -32,6 +32,8 @@ .Nm sio_pollfd , .Nm sio_revents , .Nm sio_eof , +.Nm sio_setvol , +.Nm sio_onvol , .Nm sio_initpar .Nd interface to bidirectional audio streams .Sh SYNOPSIS @@ -64,6 +66,10 @@ .Fn "sio_revents" "struct sio_hdl *hdl" "struct pollfd *pfd" .Ft "int" .Fn "sio_eof" "struct sio_hdl *hdl" +.Ft "int" +.Fn "sio_setvol" "struct sio_hdl *hdl" "unsigned vol" +.Ft "void" +.Fn "sio_onvol" "struct sio_hdl *hdl" "void (*cb)(void *arg, unsigned vol)" "void *arg" .Ft "void" .Fn "sio_initpar" "struct sio_par *par" .\"Fd #define SIO_BPS(bits) @@ -624,6 +630,37 @@ This mode is mostly useful for testing; portable applications shouldn't depend on it, since it's not available on other systems. .El +.Ss Controlling the volume +The +.Fn sio_setvol +function can be used to set the playback attenuation. +The +.Va vol +parameter takes a value between 0 (maximum attenuation) +and +.Va SIO_MAXVOL +(no attenuation). +This parameter gives the weight the audio subsystem will +give to this stream. +It is not meant to control hardware parameters like the +speakers gain, the +.Xr mixer +interface should be used for that purpose instead. +.Pp +An application can use the +.Fn sio_onvol +function to register a call-back function that +will be called each time the volume is changed, including +when +.Fn sio_setvol +is used. +The call-back is always invoked when +.Fn sio_onvol +function is called in order to provide the initial volume. +The application can safely assume that once +.Fn sio_onvol +returns, the call-back was already invoked and thus +the current volume is available. .Ss Error handling Errors related to the audio subsystem (like hardware errors, dropped connections) and diff --git a/lib/libsndio/sndio.c b/lib/libsndio/sndio.c index 6089320b403..afc9b36013c 100644 --- a/lib/libsndio/sndio.c +++ b/lib/libsndio/sndio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sndio.c,v 1.5 2008/11/09 15:32:50 ratchov Exp $ */ +/* $OpenBSD: sndio.c,v 1.6 2008/11/11 19:39:35 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -188,7 +188,8 @@ sio_create(struct sio_hdl *hdl, struct sio_ops *ops, unsigned mode, int nbio) hdl->nbio = nbio; hdl->started = 0; hdl->eof = 0; - hdl->cb_pos = 0; + hdl->move_cb = NULL; + hdl->vol_cb = NULL; } void @@ -487,8 +488,8 @@ sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr) hdl->eof = 1; return; } - hdl->cb_pos = cb; - hdl->cb_addr = addr; + hdl->move_cb = cb; + hdl->move_addr = addr; } void @@ -515,6 +516,37 @@ sio_onmove_cb(struct sio_hdl *hdl, int delta) hdl->realpos < 0 ? playpos : playpos - hdl->realpos); } #endif - if (hdl->cb_pos) - hdl->cb_pos(hdl->cb_addr, delta); + if (hdl->move_cb) + hdl->move_cb(hdl->move_addr, delta); +} + +int +sio_setvol(struct sio_hdl *hdl, unsigned ctl) +{ + if (hdl->eof) + return 0; + if (!hdl->ops->setvol(hdl, ctl)) + return 0; + hdl->ops->getvol(hdl); + return 1; +} + +void +sio_onvol(struct sio_hdl *hdl, void (*cb)(void *, unsigned), void *addr) +{ + if (hdl->started) { + fprintf(stderr, "sio_onmove: already started\n"); + hdl->eof = 1; + return; + } + hdl->vol_cb = cb; + hdl->vol_addr = addr; + hdl->ops->getvol(hdl); +} + +void +sio_onvol_cb(struct sio_hdl *hdl, unsigned ctl) +{ + if (hdl->vol_cb) + hdl->vol_cb(hdl->vol_addr, ctl); } diff --git a/lib/libsndio/sndio.h b/lib/libsndio/sndio.h index 9ddd12f414d..ec0afaa468d 100644 --- a/lib/libsndio/sndio.h +++ b/lib/libsndio/sndio.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sndio.h,v 1.2 2008/10/28 23:00:08 jsg Exp $ */ +/* $OpenBSD: sndio.h,v 1.3 2008/11/11 19:39:35 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -112,6 +112,11 @@ struct sio_cap { */ #define SIO_AUCAT_PATH "/tmp/aucat.sock" +/* + * maximum value of volume, eg. for sio_setvol() + */ +#define SIO_MAXVOL 127 + int sio_strtoenc(struct sio_par *, char *); int sio_enctostr(struct sio_par *, char *); void sio_initpar(struct sio_par *); @@ -135,5 +140,7 @@ int sio_nfds(struct sio_hdl *); int sio_pollfd(struct sio_hdl *, struct pollfd *, int); int sio_revents(struct sio_hdl *, struct pollfd *); int sio_eof(struct sio_hdl *); +int sio_setvol(struct sio_hdl *, unsigned); +void sio_onvol(struct sio_hdl *, void (*)(void *, unsigned), void *); -#endif /* !defined(LIBSIO_H) */ +#endif /* !defined(SNDIO_H) */ diff --git a/lib/libsndio/sndio_priv.h b/lib/libsndio/sndio_priv.h index 080adf2c72c..44284cf61e1 100644 --- a/lib/libsndio/sndio_priv.h +++ b/lib/libsndio/sndio_priv.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sndio_priv.h,v 1.1 2008/10/27 00:26:33 ratchov Exp $ */ +/* $OpenBSD: sndio_priv.h,v 1.2 2008/11/11 19:39:35 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -25,8 +25,10 @@ */ struct sio_hdl { struct sio_ops *ops; - void (*cb_pos)(void *, int); /* call-back for realpos changes */ - void *cb_addr; /* user priv. data */ + void (*move_cb)(void *, int); /* call-back for realpos changes */ + void *move_addr; /* user priv. data for move_cb */ + void (*vol_cb)(void *, unsigned); /* call-back for volume changes */ + void *vol_addr; /* user priv. data for vol_cb */ unsigned mode; /* SIO_PLAY | SIO_REC */ int started; /* true if started */ int nbio; /* true if non-blocking io */ @@ -56,10 +58,13 @@ struct sio_ops { int (*stop)(struct sio_hdl *); int (*pollfd)(struct sio_hdl *, struct pollfd *, int); int (*revents)(struct sio_hdl *, struct pollfd *); + int (*setvol)(struct sio_hdl *, unsigned); + void (*getvol)(struct sio_hdl *); }; void sio_create(struct sio_hdl *, struct sio_ops *, unsigned, int); void sio_destroy(struct sio_hdl *); void sio_onmove_cb(struct sio_hdl *, int); +void sio_onvol_cb(struct sio_hdl *, unsigned); #endif /* !defined(LIBSIO_PRIV_H) */ diff --git a/lib/libsndio/sun.c b/lib/libsndio/sun.c index a0e2384cca0..7755d46c4ed 100644 --- a/lib/libsndio/sun.c +++ b/lib/libsndio/sun.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sun.c,v 1.4 2008/11/07 21:01:15 ratchov Exp $ */ +/* $OpenBSD: sun.c,v 1.5 2008/11/11 19:39:35 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -29,6 +29,8 @@ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/audioio.h> +#include <sys/stat.h> +#include <limits.h> #include <errno.h> #include <fcntl.h> #include <poll.h> @@ -48,6 +50,9 @@ struct sun_hdl { unsigned ierr, oerr; /* frames the hw dropped */ int offset; /* frames play is ahead of record */ int idelta, odelta; /* position reported to client */ + int mix_fd, mix_index; /* /dev/mixerN stuff */ + int voltodo; /* 1 if vol initialization pending */ + unsigned curvol; }; void sun_close(struct sio_hdl *); @@ -60,6 +65,8 @@ size_t sun_read(struct sio_hdl *, void *, size_t); size_t sun_write(struct sio_hdl *, void *, size_t); int sun_pollfd(struct sio_hdl *, struct pollfd *, int); int sun_revents(struct sio_hdl *, struct pollfd *); +int sun_setvol(struct sio_hdl *, unsigned); +void sun_getvol(struct sio_hdl *); struct sio_ops sun_ops = { sun_close, @@ -71,7 +78,22 @@ struct sio_ops sun_ops = { sun_start, sun_stop, sun_pollfd, - sun_revents + sun_revents, + sun_setvol, + sun_getvol +}; + +/* + * prefered controls for the volume knob, in reverse order of preference + */ +struct sun_pref { + char *cls, *dev; +} sun_vols[] = { + { AudioCoutputs, AudioNmaster }, + { AudioCoutputs, AudioNoutput }, + { AudioCoutputs, AudioNdac }, + { AudioCinputs, AudioNdac }, + { NULL, NULL} }; /* @@ -318,6 +340,106 @@ sun_getcap(struct sio_hdl *sh, struct sio_cap *cap) #undef NRATES } +/* + * initialize volume knob + */ +void +sun_initvol(struct sun_hdl *hdl) +{ + int i, fd, index = -1, last_pref = -1; + struct sun_pref *p; + struct stat sb; + struct mixer_devinfo mi, cl; + struct mixer_ctrl m; + char path[PATH_MAX]; + + if (fstat(hdl->fd, &sb) < 0) + return; + if (!S_ISCHR(sb.st_mode)) + return; + snprintf(path, PATH_MAX, "/dev/mixer%d", sb.st_rdev & 0xf); + fd = open(path, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: couldn't open mixer\n", path); + return; + } + + for (mi.index = 0; ; mi.index++) { + if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) + break; + if (mi.type == AUDIO_MIXER_CLASS || mi.prev != -1) + continue; + cl.index = mi.mixer_class; + if (ioctl(fd, AUDIO_MIXER_DEVINFO, &cl) < 0) + continue; + /* + * find prefered input gain and output gain + */ + for (i = 0, p = sun_vols; p->cls != NULL; i++, p++) { + if (strcmp(p->cls, cl.label.name) != 0 || + strcmp(p->dev, mi.label.name) != 0) + continue; + if (last_pref < i) { + index = mi.index; + last_pref = i; + } + break; + } + } + hdl->mix_fd = fd; + hdl->mix_index = index; + if (index >= 0) { + m.dev = index; + m.type = AUDIO_MIXER_VALUE; + m.un.value.num_channels = 1; + if (ioctl(hdl->mix_fd, AUDIO_MIXER_READ, &m) < 0) { + fprintf(stderr, "sun_getvol: %d: failed to get volume\n", m.dev); + hdl->sa.eof = 1; + return; + } + hdl->curvol = m.un.value.level[0] / 2; + } else + hdl->curvol = SIO_MAXVOL; + return; +} + +void +sun_getvol(struct sio_hdl *sh) +{ + struct sun_hdl *hdl = (struct sun_hdl *)sh; + + if (hdl->voltodo) { + sun_initvol(hdl); + hdl->voltodo = 0; + } + sio_onvol_cb(&hdl->sa, hdl->curvol); +} + +int +sun_setvol(struct sio_hdl *sh, unsigned vol) +{ + struct sun_hdl *hdl = (struct sun_hdl *)sh; + struct mixer_ctrl m; + + if (hdl->voltodo) { + sun_initvol(hdl); + hdl->voltodo = 0; + } + if (hdl->mix_fd == -1 || hdl->mix_index == -1) + return 0; + m.dev = hdl->mix_index; + m.type = AUDIO_MIXER_VALUE; + m.un.value.num_channels = 1; + m.un.value.level[0] = 2 * vol; + if (ioctl(hdl->mix_fd, AUDIO_MIXER_WRITE, &m) < 0) { + fprintf(stderr, "sun_setvol: failed to set volume\n"); + hdl->sa.eof = 1; + return 0; + } + hdl->curvol = vol; + return 1; +} + struct sio_hdl * sio_open_sun(char *path, unsigned mode, int nbio) { @@ -348,6 +470,7 @@ sio_open_sun(char *path, unsigned mode, int nbio) goto bad_free; } hdl->fd = fd; + hdl->voltodo = 1; /* * If both play and record are requested then @@ -816,3 +939,4 @@ sun_revents(struct sio_hdl *sh, struct pollfd *pfd) revents |= POLLOUT; return revents; } + |