Most audio/video players do a stop/start cycle whenever the play position is changed, track is changed, etc. Currently, stopping drains the play buffer, which by default is very large (to workaround very long kernel non-preemptive code-paths). This makes player controls sluggish.
This diff adds a new sio_flush() function to workaround the jumbo buffer sizes: it stops playback immediately, discarding buffered data. Basically it's the same as sio_stop() but doesn't wait. The plan is to make players use it. In the network protocol, sio_flush() is implemented by adding a flag to the message corresponding to sio_stop(). Old sndiod servers ignore it and just work with new libraries. New sndiod servers see that the flag is not set by old libraries and properly drain the play buffer. Tested with mplayer, mpv and audacious, if we go this way other ports will follow. ok? Index: include/sndio.h =================================================================== RCS file: /cvs/src/include/sndio.h,v retrieving revision 1.13 diff -u -p -r1.13 sndio.h --- include/sndio.h 28 Jun 2020 05:21:38 -0000 1.13 +++ include/sndio.h 23 Mar 2022 20:23:29 -0000 @@ -164,6 +164,7 @@ size_t sio_write(struct sio_hdl *, const size_t sio_read(struct sio_hdl *, void *, size_t); int sio_start(struct sio_hdl *); int sio_stop(struct sio_hdl *); +int sio_flush(struct sio_hdl *); int sio_nfds(struct sio_hdl *); int sio_pollfd(struct sio_hdl *, struct pollfd *, int); int sio_revents(struct sio_hdl *, struct pollfd *); Index: lib/libsndio/Symbols.map =================================================================== RCS file: /cvs/src/lib/libsndio/Symbols.map,v retrieving revision 1.2 diff -u -p -r1.2 Symbols.map --- lib/libsndio/Symbols.map 26 Feb 2020 13:53:58 -0000 1.2 +++ lib/libsndio/Symbols.map 23 Mar 2022 20:23:29 -0000 @@ -11,6 +11,7 @@ sio_read; sio_start; sio_stop; + sio_flush; sio_nfds; sio_pollfd; sio_revents; Index: lib/libsndio/amsg.h =================================================================== RCS file: /cvs/src/lib/libsndio/amsg.h,v retrieving revision 1.14 diff -u -p -r1.14 amsg.h --- lib/libsndio/amsg.h 1 Nov 2021 14:43:24 -0000 1.14 +++ lib/libsndio/amsg.h 23 Mar 2022 20:23:29 -0000 @@ -96,6 +96,9 @@ struct amsg { #define AMSG_DATAMAX 0x1000 uint32_t size; } data; + struct amsg_stop { + uint8_t drain; + } stop; struct amsg_ts { int32_t delta; } ts; Index: lib/libsndio/shlib_version =================================================================== RCS file: /cvs/src/lib/libsndio/shlib_version,v retrieving revision 1.12 diff -u -p -r1.12 shlib_version --- lib/libsndio/shlib_version 26 Feb 2020 13:53:58 -0000 1.12 +++ lib/libsndio/shlib_version 23 Mar 2022 20:23:29 -0000 @@ -1,2 +1,2 @@ major=7 -minor=1 +minor=2 Index: lib/libsndio/sio.c =================================================================== RCS file: /cvs/src/lib/libsndio/sio.c,v retrieving revision 1.26 diff -u -p -r1.26 sio.c --- lib/libsndio/sio.c 1 Nov 2021 14:43:24 -0000 1.26 +++ lib/libsndio/sio.c 23 Mar 2022 20:23:29 -0000 @@ -129,6 +129,8 @@ sio_start(struct sio_hdl *hdl) int sio_stop(struct sio_hdl *hdl) { + if (hdl->ops->stop == NULL) + return sio_flush(hdl); if (hdl->eof) { DPRINTF("sio_stop: eof\n"); return 0; @@ -139,6 +141,28 @@ sio_stop(struct sio_hdl *hdl) return 0; } if (!hdl->ops->stop(hdl)) + return 0; +#ifdef DEBUG + DPRINTFN(2, "libsndio: polls: %llu, samples = %llu\n", + hdl->pollcnt, hdl->cpos); +#endif + hdl->started = 0; + return 1; +} + +int +sio_flush(struct sio_hdl *hdl) +{ + if (hdl->eof) { + DPRINTF("sio_flush: eof\n"); + return 0; + } + if (!hdl->started) { + DPRINTF("sio_flush: not started\n"); + hdl->eof = 1; + return 0; + } + if (!hdl->ops->flush(hdl)) return 0; #ifdef DEBUG DPRINTFN(2, "libsndio: polls: %llu, samples = %llu\n", Index: lib/libsndio/sio_aucat.c =================================================================== RCS file: /cvs/src/lib/libsndio/sio_aucat.c,v retrieving revision 1.20 diff -u -p -r1.20 sio_aucat.c --- lib/libsndio/sio_aucat.c 9 Jan 2016 08:27:24 -0000 1.20 +++ lib/libsndio/sio_aucat.c 23 Mar 2022 20:23:29 -0000 @@ -49,6 +49,7 @@ struct sio_aucat_hdl { static void sio_aucat_close(struct sio_hdl *); static int sio_aucat_start(struct sio_hdl *); static int sio_aucat_stop(struct sio_hdl *); +static int sio_aucat_flush(struct sio_hdl *); static int sio_aucat_setpar(struct sio_hdl *, struct sio_par *); static int sio_aucat_getpar(struct sio_hdl *, struct sio_par *); static int sio_aucat_getcap(struct sio_hdl *, struct sio_cap *); @@ -69,6 +70,7 @@ static struct sio_ops sio_aucat_ops = { sio_aucat_read, sio_aucat_start, sio_aucat_stop, + sio_aucat_flush, sio_aucat_nfds, sio_aucat_pollfd, sio_aucat_revents, @@ -207,7 +209,7 @@ sio_aucat_start(struct sio_hdl *sh) } static int -sio_aucat_stop(struct sio_hdl *sh) +sio_aucat_drain(struct sio_hdl *sh, int drain) { #define ZERO_MAX 0x400 static unsigned char zero[ZERO_MAX]; @@ -240,6 +242,7 @@ sio_aucat_stop(struct sio_hdl *sh) */ AMSG_INIT(&hdl->aucat.wmsg); hdl->aucat.wmsg.cmd = htonl(AMSG_STOP); + hdl->aucat.wmsg.u.stop.drain = drain; hdl->aucat.wtodo = sizeof(struct amsg); if (!_aucat_wmsg(&hdl->aucat, &hdl->sio.eof)) return 0; @@ -260,6 +263,18 @@ sio_aucat_stop(struct sio_hdl *sh) } } return 1; +} + +static int +sio_aucat_stop(struct sio_hdl *sh) +{ + return sio_aucat_drain(sh, 1); +} + +static int +sio_aucat_flush(struct sio_hdl *sh) +{ + return sio_aucat_drain(sh, 0); } static int Index: lib/libsndio/sio_open.3 =================================================================== RCS file: /cvs/src/lib/libsndio/sio_open.3,v retrieving revision 1.52 diff -u -p -r1.52 sio_open.3 --- lib/libsndio/sio_open.3 29 Nov 2020 08:02:42 -0000 1.52 +++ lib/libsndio/sio_open.3 23 Mar 2022 20:23:29 -0000 @@ -25,6 +25,7 @@ .Nm sio_getcap , .Nm sio_start , .Nm sio_stop , +.Nm sio_flush , .Nm sio_read , .Nm sio_write , .Nm sio_onmove , @@ -53,6 +54,8 @@ .Fn sio_start "struct sio_hdl *hdl" .Ft "int" .Fn sio_stop "struct sio_hdl *hdl" +.Ft "int" +.Fn sio_flush "struct sio_hdl *hdl" .Ft "size_t" .Fn sio_read "struct sio_hdl *hdl" "void *addr" "size_t nbytes" .Ft "size_t" @@ -257,6 +260,8 @@ Parameters cannot be changed after .Fn sio_start has been called, .Fn sio_stop +or +.Fn sio_flush must be called before parameters can be changed. .Pp If the device is exposed by the @@ -410,6 +415,14 @@ If samples to play are queued but playba then playback is forced immediately; playback will actually stop once the buffer is drained. In no case are samples in the play buffer discarded. +.Pp +The +.Fn sio_flush +function stops playback and recording immediately, +possibly discarding play buffer contents, and puts the audio subsystem +in the same state as before +.Fn sio_start +is called. .Ss Playing and recording When record mode is selected, the .Fn sio_read @@ -748,6 +761,7 @@ The .Fn sio_getcap , .Fn sio_start , .Fn sio_stop , +.Fn sio_flush , and .Fn sio_setvol functions return 1 on success and 0 on failure. @@ -828,7 +842,8 @@ The .Fn sio_getpar , .Fn sio_getcap , .Fn sio_start , +.Fn sio_stop , and -.Fn sio_stop +.Fn sio_flush functions may block for a very short period of time, thus they should be avoided in code sections where blocking is not desirable. Index: lib/libsndio/sio_priv.h =================================================================== RCS file: /cvs/src/lib/libsndio/sio_priv.h,v retrieving revision 1.9 diff -u -p -r1.9 sio_priv.h --- lib/libsndio/sio_priv.h 16 Jan 2015 16:48:52 -0000 1.9 +++ lib/libsndio/sio_priv.h 23 Mar 2022 20:23:29 -0000 @@ -58,6 +58,7 @@ struct sio_ops { size_t (*read)(struct sio_hdl *, void *, size_t); int (*start)(struct sio_hdl *); int (*stop)(struct sio_hdl *); + int (*flush)(struct sio_hdl *); int (*nfds)(struct sio_hdl *); int (*pollfd)(struct sio_hdl *, struct pollfd *, int); int (*revents)(struct sio_hdl *, struct pollfd *); Index: lib/libsndio/sio_sun.c =================================================================== RCS file: /cvs/src/lib/libsndio/sio_sun.c,v retrieving revision 1.28 diff -u -p -r1.28 sio_sun.c --- lib/libsndio/sio_sun.c 28 Jun 2019 13:32:42 -0000 1.28 +++ lib/libsndio/sio_sun.c 23 Mar 2022 20:23:29 -0000 @@ -48,7 +48,7 @@ struct sio_sun_hdl { static void sio_sun_close(struct sio_hdl *); static int sio_sun_start(struct sio_hdl *); -static int sio_sun_stop(struct sio_hdl *); +static int sio_sun_flush(struct sio_hdl *); static int sio_sun_setpar(struct sio_hdl *, struct sio_par *); static int sio_sun_getpar(struct sio_hdl *, struct sio_par *); static int sio_sun_getcap(struct sio_hdl *, struct sio_cap *); @@ -66,7 +66,8 @@ static struct sio_ops sio_sun_ops = { sio_sun_write, sio_sun_read, sio_sun_start, - sio_sun_stop, + NULL, + sio_sun_flush, sio_sun_nfds, sio_sun_pollfd, sio_sun_revents, @@ -395,7 +396,7 @@ sio_sun_start(struct sio_hdl *sh) } static int -sio_sun_stop(struct sio_hdl *sh) +sio_sun_flush(struct sio_hdl *sh) { struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; Index: usr.bin/sndiod/siofile.c =================================================================== RCS file: /cvs/src/usr.bin/sndiod/siofile.c,v retrieving revision 1.25 diff -u -p -r1.25 siofile.c --- usr.bin/sndiod/siofile.c 1 Nov 2021 14:43:25 -0000 1.25 +++ usr.bin/sndiod/siofile.c 23 Mar 2022 20:23:30 -0000 @@ -317,7 +317,7 @@ dev_sio_start(struct dev *d) void dev_sio_stop(struct dev *d) { - if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) { + if (!sio_eof(d->sio.hdl) && !sio_flush(d->sio.hdl)) { if (log_level >= 1) { dev_log(d); log_puts(": failed to stop device\n"); Index: usr.bin/sndiod/sock.c =================================================================== RCS file: /cvs/src/usr.bin/sndiod/sock.c,v retrieving revision 1.45 diff -u -p -r1.45 sock.c --- usr.bin/sndiod/sock.c 1 Nov 2021 14:43:25 -0000 1.45 +++ usr.bin/sndiod/sock.c 23 Mar 2022 20:23:30 -0000 @@ -1186,7 +1186,7 @@ sock_execmsg(struct sock *f) f->ralign = s->round * s->mix.bpf; } } - slot_stop(s, 1); + slot_stop(s, AMSG_ISSET(m->u.stop.drain) ? m->u.stop.drain : 1); break; case AMSG_SETPAR: #ifdef DEBUG