mixer(8): Implement hot-swapping
Introduce a -V option, which can be used alongside -d (default unit change), in order to hot-swap devices (i.e switch to them on the fly without needing to restart the track), in case virtual_oss(8) exists and is running. Sponsored by: The FreeBSD Foundation MFC after: 2 days Reviewed by: dev_submerge.ch Differential Revision: https://reviews.freebsd.org/D46253
This commit is contained in:
+71
-4
@@ -19,7 +19,7 @@
|
||||
.\" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
.\" THE SOFTWARE.
|
||||
.\"
|
||||
.Dd February 8, 2024
|
||||
.Dd August 14, 2024
|
||||
.Dt MIXER 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -28,7 +28,7 @@
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl f Ar device
|
||||
.Op Fl d Ar pcmN | N
|
||||
.Op Fl d Ar pcmN | N Op Fl V Ar voss_device:mode
|
||||
.Op Fl os
|
||||
.Op Ar dev Ns Op Cm \&. Ns Ar control Ns Op Cm \&= Ns Ar value
|
||||
.Ar ...
|
||||
@@ -43,7 +43,7 @@ The
|
||||
utility is used to set and display soundcard mixer device controls.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width "-d pcmN | N"
|
||||
.Bl -tag -width "-V voss_device:mode"
|
||||
.It Fl a
|
||||
Print the values for all mixer devices available in the system
|
||||
.Pq see Sx FILES .
|
||||
@@ -54,6 +54,30 @@ where N is the unit number (e.g for pcm0, the unit number is 0).
|
||||
See
|
||||
.Sx EXAMPLES
|
||||
on how to list all available audio devices in the system.
|
||||
.Pp
|
||||
There is also the possibility of hot-swapping to the new default device if
|
||||
.Xr virtual_oss 8
|
||||
exists in the system and is running, in which case the
|
||||
.Fl V
|
||||
option needs to be specified as well.
|
||||
.Pp
|
||||
Hot-swapping generally cannot happen with plain
|
||||
.Xr sound 4 ,
|
||||
so the user has to restart the track in order to get sound coming out of the
|
||||
new default device.
|
||||
This is because applications usually open a device at the start of the track
|
||||
and do not check for default device changes, in order to open the new device
|
||||
mid-track.
|
||||
.Xr virtual_oss 8 ,
|
||||
on the other hand, can do hot-swapping, because it creates a virtual device for
|
||||
applications to open, and then does all the necessary routing and conversions
|
||||
to the appropriate device(s).
|
||||
.Pp
|
||||
Note that hot-swapping will work only for applications that are using
|
||||
.Xr virtual_oss 8
|
||||
devices, and not plain
|
||||
.Xr sound 4
|
||||
ones.
|
||||
.It Fl f Ar device
|
||||
Open
|
||||
.Ar device
|
||||
@@ -66,6 +90,33 @@ Print mixer values in a format suitable for use inside scripts.
|
||||
The mixer's header (name, audio card name, ...) will not be printed.
|
||||
.It Fl s
|
||||
Print only the recording source(s) of the mixer device.
|
||||
.It Fl V Ar voss_device:mode
|
||||
Specify a
|
||||
.Xr virtual_oss 8
|
||||
control device, as well as a mode (see below), in order to hot-swap devices.
|
||||
This option is meant to only be used in combination with the
|
||||
.Fl d
|
||||
option.
|
||||
.Pp
|
||||
The available modes are as follows:
|
||||
.Bl -column play
|
||||
.It Sy Mode Ta Sy Action
|
||||
.It all Ta Playback and recording
|
||||
.It play Ta Playback
|
||||
.It rec Ta Recording
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Pa mode
|
||||
part is needed, so that
|
||||
.Nm
|
||||
will not accidentally hot-swap both the recording and playback device in
|
||||
.Xr virtual_oss 8 ,
|
||||
if only one direction is to be hot-swapped.
|
||||
.Pp
|
||||
See
|
||||
.Sx EXAMPLES
|
||||
on how to use this option.
|
||||
.El
|
||||
.Pp
|
||||
The list of mixer devices that may be modified are:
|
||||
@@ -273,10 +324,26 @@ $ mixer -f /dev/mixer0 -o > info
|
||||
\&...
|
||||
$ mixer -f /dev/mixer0 `cat info`
|
||||
.Ed
|
||||
.Pp
|
||||
Suppose
|
||||
.Xr virtual_oss 8
|
||||
is running with
|
||||
.Pa /dev/vdsp.ctl
|
||||
as its control device, and
|
||||
.Pa pcm0
|
||||
as the playback device.
|
||||
Change the default device to
|
||||
.Pa pcm1 ,
|
||||
and hot-swap to it for both recording and playback in
|
||||
.Xr virtual_oss 8 :
|
||||
.Bd -literal -offset indent
|
||||
$ mixer -d pcm1 -V /dev/vdsp.ctl:all
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr mixer 3 ,
|
||||
.Xr sound 4 ,
|
||||
.Xr sysctl 8
|
||||
.Xr sysctl 8 ,
|
||||
.Xr virtual_oss 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
|
||||
+74
-7
@@ -20,6 +20,9 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <mixer.h>
|
||||
@@ -40,7 +43,7 @@ static void printall(struct mixer *, int);
|
||||
static void printminfo(struct mixer *, int);
|
||||
static void printdev(struct mixer *, int);
|
||||
static void printrecsrc(struct mixer *, int); /* XXX: change name */
|
||||
static int set_dunit(struct mixer *, int);
|
||||
static int set_dunit(struct mixer *, int, char *);
|
||||
/* Control handlers */
|
||||
static int mod_volume(struct mix_dev *, void *);
|
||||
static int mod_mute(struct mix_dev *, void *);
|
||||
@@ -54,13 +57,13 @@ main(int argc, char *argv[])
|
||||
{
|
||||
struct mixer *m;
|
||||
mix_ctl_t *cp;
|
||||
char *name = NULL, buf[NAME_MAX];
|
||||
char *name = NULL, buf[NAME_MAX], *vctl = NULL;
|
||||
char *p, *q, *devstr, *ctlstr, *valstr = NULL;
|
||||
int dunit, i, n, pall = 1, shorthand;
|
||||
int aflag = 0, dflag = 0, oflag = 0, sflag = 0;
|
||||
int ch;
|
||||
|
||||
while ((ch = getopt(argc, argv, "ad:f:hos")) != -1) {
|
||||
while ((ch = getopt(argc, argv, "ad:f:hosV:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'a':
|
||||
aflag = 1;
|
||||
@@ -83,6 +86,9 @@ main(int argc, char *argv[])
|
||||
case 's':
|
||||
sflag = 1;
|
||||
break;
|
||||
case 'V':
|
||||
vctl = optarg;
|
||||
break;
|
||||
case 'h': /* FALLTHROUGH */
|
||||
case '?':
|
||||
default:
|
||||
@@ -119,7 +125,7 @@ main(int argc, char *argv[])
|
||||
initctls(m);
|
||||
|
||||
if (dflag) {
|
||||
if (set_dunit(m, dunit) < 0)
|
||||
if (set_dunit(m, dunit, vctl) < 0)
|
||||
goto parse;
|
||||
else {
|
||||
/*
|
||||
@@ -209,7 +215,8 @@ main(int argc, char *argv[])
|
||||
static void __dead2
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: %1$s [-f device] [-d pcmN | N] [-os] [dev[.control[=value]]] ...\n"
|
||||
fprintf(stderr, "usage: %1$s [-f device] [-d pcmN | N "
|
||||
"[-V voss_device:mode]] [-os] [dev[.control[=value]]] ...\n"
|
||||
" %1$s [-os] -a\n"
|
||||
" %1$s -h\n", getprogname());
|
||||
exit(1);
|
||||
@@ -322,9 +329,32 @@ printrecsrc(struct mixer *m, int oflag)
|
||||
}
|
||||
|
||||
static int
|
||||
set_dunit(struct mixer *m, int dunit)
|
||||
set_dunit(struct mixer *m, int dunit, char *vctl)
|
||||
{
|
||||
int n;
|
||||
const char *opt;
|
||||
char *dev, *mode;
|
||||
char buf[32];
|
||||
size_t size;
|
||||
int n, rc;
|
||||
|
||||
/*
|
||||
* Issue warning in case of hw.snd.basename_clone being unset. Omit the
|
||||
* check and warning if the -V flag is used, since the user is most
|
||||
* likely to be aware of this, and the warning might be confusing.
|
||||
*/
|
||||
if (vctl == NULL) {
|
||||
size = sizeof(int);
|
||||
if (sysctlbyname("hw.snd.basename_clone", &n, &size,
|
||||
NULL, 0) < 0) {
|
||||
warn("hw.snd.basename_clone failed");
|
||||
return (-1);
|
||||
}
|
||||
if (n == 0) {
|
||||
warnx("warning: hw.snd.basename_clone not set. "
|
||||
"/dev/dsp is managed externally and does not "
|
||||
"change with the default unit change here.");
|
||||
}
|
||||
}
|
||||
|
||||
if ((n = mixer_get_dunit()) < 0) {
|
||||
warn("cannot get default unit");
|
||||
@@ -336,6 +366,43 @@ set_dunit(struct mixer *m, int dunit)
|
||||
}
|
||||
printf("default_unit: %d -> %d\n", n, dunit);
|
||||
|
||||
/* Hot-swap in case virtual_oss exists and is running. */
|
||||
if (vctl != NULL) {
|
||||
dev = strsep(&vctl, ":");
|
||||
mode = vctl;
|
||||
if (dev == NULL || mode == NULL) {
|
||||
warnx("voss_device:mode tuple incomplete");
|
||||
return (-1);
|
||||
}
|
||||
if (strcmp(mode, "all") == 0)
|
||||
opt = "-f";
|
||||
else if (strcmp(mode, "play") == 0)
|
||||
opt = "-P";
|
||||
else if (strcmp(mode, "rec") == 0)
|
||||
opt = "-R";
|
||||
else {
|
||||
warnx("please use one of the following modes: "
|
||||
"all, play, rec");
|
||||
return (-1);
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "/dev/dsp%d", dunit);
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
warn("fork");
|
||||
break;
|
||||
case 0:
|
||||
rc = execl("/usr/local/sbin/virtual_oss_cmd",
|
||||
"virtual_oss_cmd", dev, opt, buf, NULL);
|
||||
if (rc < 0)
|
||||
warn("virtual_oss_cmd");
|
||||
_exit(0);
|
||||
default:
|
||||
if (wait(NULL) < 0)
|
||||
warn("wait");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user