diff options
-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 | ||||
-rw-r--r-- | regress/lib/libsndio/vol/Makefile | 6 | ||||
-rw-r--r-- | regress/lib/libsndio/vol/vol.c | 157 |
10 files changed, 477 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; } + diff --git a/regress/lib/libsndio/vol/Makefile b/regress/lib/libsndio/vol/Makefile new file mode 100644 index 00000000000..7e1f32bec17 --- /dev/null +++ b/regress/lib/libsndio/vol/Makefile @@ -0,0 +1,6 @@ +# $OpenBSD: Makefile,v 1.1 2008/11/11 19:39:35 ratchov Exp $ +PROG= vol +LDADD= -lsndio +REGRESS_SKIP= + +.include <bsd.regress.mk> diff --git a/regress/lib/libsndio/vol/vol.c b/regress/lib/libsndio/vol/vol.c new file mode 100644 index 00000000000..2ee08e35c7a --- /dev/null +++ b/regress/lib/libsndio/vol/vol.c @@ -0,0 +1,157 @@ +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <poll.h> +#include <termios.h> +#include <sndio.h> + +#define BUFSZ 0x100 +unsigned char buf[BUFSZ]; +struct sio_par par; +unsigned vol = 0xdeadbeef; + +void +usage(void) { + fprintf(stderr, "usage: vol [-r rate] [-c nchan] [-e enc]\n"); +} + +void +onvol(void *addr, unsigned val) +{ + vol = val; + fprintf(stderr, "volume set to %u\n", vol); +} + +int +main(int argc, char **argv) { + int ch; + int tty; + struct sio_hdl *hdl; + struct termios tio; + struct pollfd pfd[2]; + char cmd; + ssize_t n, len; + + /* + * defaults parameters + */ + sio_initpar(&par); + par.sig = 1; + par.bits = 16; + par.pchan = 2; + par.rate = 44100; + + if (isatty(STDIN_FILENO)) { + fprintf(stderr, "stdin can't be a tty\n"); + exit(1); + } + tty = open("/dev/tty", O_RDWR); + if (tty < 0) { + perror("/dev/tty"); + exit(1); + } + if (tcgetattr(tty, &tio) < 0) { + perror("tcsetattr"); + exit(1); + } + tio.c_lflag &= ~ICANON; + tio.c_lflag &= ~ECHO; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(tty, TCSAFLUSH, &tio) < 0) { + perror("tcsetattr"); + exit(1); + } + + while ((ch = getopt(argc, argv, "r:c:e:b:x:")) != -1) { + switch(ch) { + case 'r': + if (sscanf(optarg, "%u", &par.rate) != 1) { + fprintf(stderr, "%s: bad rate\n", optarg); + exit(1); + } + break; + case 'c': + if (sscanf(optarg, "%u", &par.pchan) != 1) { + fprintf(stderr, "%s: bad channels\n", optarg); + exit(1); + } + break; + case 'e': + if (!sio_strtoenc(&par, optarg)) { + fprintf(stderr, "%s: bad encoding\n", optarg); + exit(1); + } + break; + default: + usage(); + exit(1); + break; + } + } + + hdl = sio_open(NULL, SIO_PLAY, 0); + if (hdl == NULL) { + fprintf(stderr, "sio_open() failed\n"); + exit(1); + } + if (!sio_setpar(hdl, &par)) { + fprintf(stderr, "sio_setpar() failed\n"); + exit(1); + } + sio_onvol(hdl, onvol, NULL); + fprintf(stderr, "use ``+'' and ``-'' to adjust the volume\n"); + if (!sio_start(hdl)) { + fprintf(stderr, "sio_start() failed\n"); + exit(1); + } + for (;;) { + pfd[0].fd = tty; + pfd[0].events = POLLIN; + sio_pollfd(hdl, &pfd[1], POLLOUT); + if (poll(pfd, 2, INFTIM) < 0) { + perror("poll"); + exit(1); + } + if (pfd[0].revents & POLLIN) { + if (read(tty, &cmd, 1) < 0) { + perror("read(tty)"); + exit(1); + } + switch (cmd) { + case '+': + if (vol < SIO_MAXVOL) { + vol++; + sio_setvol(hdl, vol); + } + break; + case '-': + if (vol > 0) { + vol--; + sio_setvol(hdl, vol); + } + break; + } + } + if (sio_revents(hdl, &pfd[1]) & POLLOUT) { + len = read(STDIN_FILENO, buf, BUFSZ); + if (len < 0) { + perror("stdin"); + exit(1); + } + if (len == 0) + break; + n = sio_write(hdl, buf, len); + if (n == 0) { + fprintf(stderr, "sio_write: failed\n"); + exit(1); + } + } + } + sio_close(hdl); + return 0; +} |