summaryrefslogtreecommitdiff
path: root/lib/libsndio
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2008-11-11 19:39:36 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2008-11-11 19:39:36 +0000
commit66ce1beacebfce86cedb28308199ef5dbebb775c (patch)
treec5cd534c23219bbfd3653e19d8fea8a0499c1fc6 /lib/libsndio
parent3bc33a8c8c92b0092ffe1bb775de2cc4fc8ec6d0 (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/Makefile4
-rw-r--r--lib/libsndio/aucat.c132
-rw-r--r--lib/libsndio/shlib_version2
-rw-r--r--lib/libsndio/sio_open.341
-rw-r--r--lib/libsndio/sndio.c44
-rw-r--r--lib/libsndio/sndio.h11
-rw-r--r--lib/libsndio/sndio_priv.h11
-rw-r--r--lib/libsndio/sun.c128
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;
}
+