mixer

FreeBSD OSS mixer library implementation and a complete rewrite of mixer(8)
git clone git://git.margiolis.net/mixer.git
Log | Files | Refs | README

commit 79523985cb60e5f3301e9cbfd9745c633587c11b
parent 59b41ffceb9c625d13f70729ce4960d69cc511d2
Author: Christos Margiolis <christos@margiolis.net>
Date:   Wed, 30 Jun 2021 01:08:59 +0300

mixer_prog: added handling through controls sysctl-style

Diffstat:
Mmixer_lib/mixer.3 | 2+-
Mmixer_lib/mixer.h | 2+-
Mmixer_prog/mixer_prog.8 | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mmixer_prog/mixer_prog.c | 292+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Arc.d/mixer | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc.d/mixer.old | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 511 insertions(+), 175 deletions(-)

diff --git a/mixer_lib/mixer.3 b/mixer_lib/mixer.3 @@ -20,7 +20,7 @@ .\" THE SOFTWARE. .\" -.Dd June 28, 2021 +.Dd June 30, 2021 .Dt mixer 3 .Os .Sh NAME diff --git a/mixer_lib/mixer.h b/mixer_lib/mixer.h @@ -69,7 +69,7 @@ struct mix_dev { }; struct mixer { - TAILQ_HEAD(head, mix_dev) devs; /* XXX: use LIST? */ + TAILQ_HEAD(head, mix_dev) devs; struct mix_dev *dev; oss_mixerinfo mi; oss_card_info ci; diff --git a/mixer_prog/mixer_prog.8 b/mixer_prog/mixer_prog.8 @@ -20,87 +20,124 @@ .\" THE SOFTWARE. .\" -.Dd June 28, 2021 +.Dd June 30, 2021 .Dt mixer 8 .Os .Sh NAME .Nm mixer -.Nd manipulate soundcard mixer values +.Nd manipulate soundcard mixer controls .Sh SYNOPSIS .Nm -.Op Fl f device -.Op Fl d unit -.Op Fl o -.Oo -.Ar dev -.Sm off -.Oo -.Op Cm + | - -.Ar lvol -.Op : Oo Cm + | - Oc Ar rvol -.Oc -.Oc -.Sm on -.Ar ... -.Nm -.Op Fl f device -.Op Fl d unit -.Op Fl o -.Fl s -.Ar ... -.Nm -.Op Fl f device -.Op Fl d unit -.Op Fl o -.Sm off -.Bro -.Cm ^ | + | - | = -.Brc -.Cm rec -.Sm on -.Ar rdev ... +.Op Fl f Ar device +.Op Fl d Ar unit +.Op Fl os +.Op Ar command Ar ... .Nm -.Op Fl o +.Op Fl d Ar unit +.Op Fl os .Fl a .Sh DESCRIPTION The .Nm -utility is used to set and display soundcard mixer device levels. The list of mixer -devices that may be modified are: +utility is used to set and display soundcard mixer device controls. +.Pp +The options are as follows: +.Bl -tag -width "-f device" +.It Fl a +Print the values for all mixer devices available in the system (see FILES). +.It Fl d Ar unit +Change the default audio card to +.Ar unit . +The unit has to be an integer value. To see what unit values are available, look +at the number each mixer device has by running +.Nm . +.It Fl f Ar device +Open +.Ar device +as the mixer device (see FILES). +.It Fl o +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. +.El +.Pp +The list of mixer devices that may be modified are: .Bd -ragged -offset indent vol, bass, treble, synth, pcm, speaker, line, mic, cd, mix, pcm2, rec, igain, ogain, line1, line2, line3, dig1, dig2, dig3, phin, phout, video, radio, and monitor. .Ed .Pp +Not all mixer devices are available. +.Pp Without any arguments, .Nm -displays information and the current settings for all supported devices. -If the +displays all information for each one of the mixer's supported devices to +.Ar stdout . +If the \" TODO: explain what "default mixer" means. .Ar dev argument is specified, .Nm displays only the values for .Ar dev . +More than one device may be specified. +.Pp +Commands use the following format: +.Pp +.Bl -column xxxxxxxxxxxxxxxxxxxxxxxx -offset indent +.It Sy "Name Action" +.It "dev Display all controls" +.It "dev.control Display only the specified control" +.It "dev.control=value Set control value" +.El +.Pp +The available controls are as follows (replace +.Ar dev +with one of the available devices): +.Bl -column xxxxxxxxxxxxxxxxxxxxxxxx -offset indent +.It Sy "Name Value" +.It "dev.volume [[+|-]lvol[:[+|-]rvol]]" +.It "dev.rec {+|-|^|=}" +.El .Pp To modify a device's volume, the optional .Ar lvol and/or .Ar rvol values have to be specified. The values have to be normalized 32-bit floats -(0.0 to 1.0). Omitting -.Ar dev -will change the value of the main channel (usually "vol"). -If the the left or right volume values are prefixed with +(0.0 to 1.0). If the the left or right volume values are prefixed with .Cm + or .Cm - , the value following will be used as a relative adjustment, modifying the current settings by the amount specified. +.Pp +.Nm +marks devices which can be used as a recording source with +.Ar rec . +To modify the recording source you can use one of: +.Bl -tag -width =rec -offset indent +.It Cm ^ +toggles +.Ar dev +of possible recording devices +.It Cm + +adds +.Ar dev +to possible recording devices +.It Cm - +removes +.Ar dev +from possible recording devices +.It Cm = +sets the recording device to +.Ar dev +.El .Sh FILES .Bl -tag -width /dev/mixerN -compact .It Pa /dev/mixerN -the mixer device, where +The mixer device, where .Ar N is the number of that device, for example .Ar /dev/mixer0 . @@ -108,11 +145,64 @@ PCM cards and mixers have a 1:1 relationship, which means that .Ar mixer0 is the mixer for .Ar pcm0 -and so on. +and so on. By default, +.Nm +prints both the audio card's number and the mixer associated with it +in the form of +.Ar pcmN:mixer . +The +.Ar /dev/mixer +file, although it doesn't exist in the filesystem, points to the default +mixer device and is the file +.Nm +opens when the +.Fl f Ar device +option has not been specified. +.El +.Sh EXAMPLES +.Pp +Change the volume for the +.Ar vol +device of the +.Ar /dev/mixer0 +mixer device to 0.65: +.Bl -tag -width Ds -offset indent +.It $ mixer -f /dev/mixer0 vol.volume=0.65 +.El +.Pp +Increase the +.Ar mic +device's left volume by 0.10 and decrease the right +volume by 0.05: +.Bl -tag -width Ds -offset indent +.It $ mixer mic.volume=+0.10:-0.05 +.El +.Pp +Set +.Ar mic +and toggle +.Ar line +recording sources: +.Bl -tag -width Ds -offset indent +.It $ mixer mic.rec=+ line.rec=^ +.El +.Pp +Dump +.Ar /dev/mixer0 +information to a file and retrieve back later +.Bl -tag -width Ds -offset indent +.It $ mixer -f /dev/mixer0 -o > info +.It ... +.It $ mixer -f /dev/mixer0 `cat info` .El -\" TODO: write about the rest of the program. .Sh SEE ALSO .Xr mixer 3 , -.Xr sound 4 +.Xr sound 4 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +utility first appeared in FreeBSD 2.0.5 and was rewritten completely in +FreeBSD 12.2. \" FIXME: replace 12.2 with proper version. .Sh AUTHORS .An Christos Margiolis Aq Mt christos@margiolis.net diff --git a/mixer_prog/mixer_prog.c b/mixer_prog/mixer_prog.c @@ -29,22 +29,28 @@ #include <mixer.h> -static void usage(void) __dead2; +#define CTRL_VOL 0 +#define CTRL_REC 1 + +#define LEN(x) (sizeof(x) / sizeof(x[0])) + +struct ctrl { + char name[NAME_MAX]; + void (*mod)(struct mixer *, const char *, const char *); +}; + static void printall(struct mixer *, int); static void printmixer(struct mixer *); static void printdev(struct mix_dev *, int); static void printrecsrc(struct mixer *, int); +static void modvol(struct mixer *, const char *, const char *); +static void modrecsrc(struct mixer *, const char *, const char *); +static void usage(void) __dead2; -static void __dead2 -usage(void) -{ - printf("usage: %1$s [-f device] [-d unit] [-o] [dev [+|-][lvol[:[+|-]rvol]] ...\n" - " %1$s [-f device] [-d unit] [-o] -s ...\n" - " %1$s [-f device] [-d unit] [-o] {^|+|-|=}rec rdev ...\n" - " %1$s [-o] -a\n", - getprogname()); - exit(1); -} +struct ctrl ctrls[] = { + [CTRL_VOL] = { "volume", modvol }, + [CTRL_REC] = { "rec", modrecsrc }, +}; static void printall(struct mixer *m, int oflag) @@ -81,10 +87,10 @@ printdev(struct mix_dev *d, int oflag) printf(" src"); printf("\n"); } else { - /* FIXME: get rid of the space */ - printf("%s %.2f:%.2f ", d->name, d->lvol, d->rvol); + printf("%s.%s=%.2f:%.2f\n", + d->name, ctrls[CTRL_VOL].name, d->lvol, d->rvol); if (d->f_src) - printf("+rec %s ", d->name); + printf("%s.%s=+\n", d->name, ctrls[CTRL_REC].name); } } @@ -98,31 +104,131 @@ printrecsrc(struct mixer *m, int oflag) return; if (!oflag) { printmixer(m); - printf(" recording source: "); + printf(" recording source(s): "); } TAILQ_FOREACH(dp, &m->devs, devs) { if (M_ISRECSRC(m, dp->devno)) { - if (n) + if (n++) printf("%s ", oflag ? " " : ", "); printf("%s", dp->name); - n++; } } printf("\n"); } +static void +modvol(struct mixer *m, const char *dev, const char *val) +{ + char lstr[8], rstr[8]; + float l, r, lprev, rprev, lrel, rrel; + int n; + + n = sscanf(val, "%7[^:]:%7s", lstr, rstr); + if (n == EOF) { + warnx("invalid value: %s", val); + return; + } + lrel = rrel = 0; + if (n > 0) { + if (*lstr == '+' || *lstr == '-') + lrel = rrel = 1; + l = strtof(lstr, NULL); + } + if (n > 1) { + if (*rstr == '+' || *rstr == '-') + rrel = 1; + r = strtof(rstr, NULL); + } + switch (n) { + case 1: + r = l; /* FALLTHROUGH */ + case 2: + if (lrel) + l += m->dev->lvol; + if (rrel) + r += m->dev->rvol; + + if (l < M_VOLMIN) + l = M_VOLMIN; + else if (l > M_VOLMAX) + l = M_VOLMAX; + if (r < M_VOLMIN) + r = M_VOLMIN; + else if (r > M_VOLMAX) + r = M_VOLMAX; + + if ((m->dev = mixer_getdevbyname(m, dev)) == NULL) { + warn("cannot open device: %s\n", dev); + return; + } + lprev = m->dev->lvol; + rprev = m->dev->rvol; + if (mixer_setvol(m, l, r) < 0) { + warnx("cannot change volume"); + return; + } + printf("%s.%s: %.2f:%.2f -> %.2f:%.2f\n", + m->dev->name, ctrls[CTRL_VOL].name, lprev, rprev, l, r); + } +} + +static void +modrecsrc(struct mixer *m, const char *dev, const char *val) +{ + int n, opt = 0; + + if (*val != '+' && *val != '-' && + *val != '=' && *val != '^') { + warnx("unknown modifier: %c", *val); + return; + } + switch (*val) { + case '+': + opt = M_ADDRECDEV; + break; + case '-': + opt = M_REMOVERECDEV; + break; + case '=': + opt = M_SETRECDEV; + break; + case '^': + opt = M_TOGGLERECDEV; + break; + } + if ((m->dev = mixer_getdevbyname(m, dev)) == NULL) { + warn("unknown recording device: %s", dev); + return; + } + /* Keep the previous state. */ + n = m->dev->f_src != 0; + if (mixer_modrecsrc(m, opt) < 0) + warn("cannot modify device: %c%s", *val, dev); + else + printf("%s.%s: %d -> %d\n", + dev, ctrls[CTRL_REC].name, n, m->dev->f_src != 0); +} + +static void __dead2 +usage(void) +{ + printf("usage: %1$s [-f device] [-d unit] [-os] [command ...]\n" + " %1$s [-d unit] [-os] -a\n", + getprogname()); + exit(1); +} + int main(int argc, char *argv[]) { struct mixer *m; - char lstr[8], rstr[8], *name = NULL, buf[NAME_MAX]; - float l, r, lrel, rrel; - int dusage = 0, opt = 0, dunit; + char *name = NULL, buf[NAME_MAX]; + char *p, *bufp, *devstr, *ctrlstr, *valstr = NULL; + int dunit, i, n; int aflag = 0, dflag = 0, oflag = 0, sflag = 0; - int i, rc = 0; - char ch, n, k; + char ch; - while ((ch = getopt(argc, argv, "ad:f:or:s")) != -1) { + while ((ch = getopt(argc, argv, "ad:f:os")) != -1) { switch (ch) { case 'a': aflag = 1; @@ -139,10 +245,6 @@ main(int argc, char *argv[]) case 'o': oflag = 1; break; - case 'r': - /* Reserved for {+|-|^|=}rec rdev. */ - /* FIXME: problems with -rec */ - break; case 's': sflag = 1; break; @@ -176,11 +278,9 @@ main(int argc, char *argv[]) if ((m = mixer_open(name)) == NULL) err(1, "mixer_open: %s", name); - while (!dusage) { + for (;;) { if (dflag) { - /* We don't want to get in here again. */ dflag = 0; - /* XXX: should we die if any of these two fails? */ if ((n = mixer_getdunit()) < 0) { warn("cannot get default unit"); continue; @@ -193,114 +293,52 @@ main(int argc, char *argv[]) } else if (sflag) { printrecsrc(m, oflag); goto done; - } else if (argc > 0 && strcmp("rec", *argv + 1) == 0) { - if (**argv != '+' && **argv != '-' && - **argv != '=' && **argv != '^') { - warnx("unknown modifier: %c", **argv); - dusage = 1; - break; + } else if (argc > 0) { + if ((p = bufp = strdup(*argv)) == NULL) + err(1, "strdup(%s)", *argv); + /* Split the string into name, control and value. */ + devstr = strsep(&p, "."); + /* + * The input was only the device name, so we'll just + * print all its information. + */ + if (p == NULL) { + /* TODO */ } - switch (**argv) { - case '+': - opt = M_ADDRECDEV; - break; - case '-': - opt = M_REMOVERECDEV; - break; - case '=': - opt = M_SETRECDEV; - break; - case '^': - opt = M_TOGGLERECDEV; - break; + ctrlstr = strsep(&p, "="); + /* + * If we don't have an assignment, print the control's + * values. + */ + if (p == NULL) { + /* TODO */ } - if ((m->dev = mixer_getdevbyname(m, argv[1])) == NULL) { - warn("unknown recording device: %s", argv[1]); - /* XXX */ - rc = 1; - goto done; + valstr = p; + if (ctrlstr == NULL || valstr == NULL) { + /* FIXME: doesn't print everything */ + warnx("invalid syntax: %s", bufp); + goto next; } - /* Keep the previous state. */ - n = m->dev->f_src != 0; - if (mixer_modrecsrc(m, opt) < 0) - warn("cannot modify device: %c%s", **argv, argv[1]); - else - printf("%s.recsrc: %d -> %d\n", argv[1], - n, m->dev->f_src != 0); - argc -= 2; - argv += 2; - } else if (argc > 0) { - if ((k = sscanf(*argv, "%f:%f", &l, &r)) > 0) - ; /* nothing */ - else if ((m->dev = mixer_getdevbyname(m, *argv)) == NULL) { - warn("unknown device: %s", *argv); - rc = 1; - goto done; - } - - lrel = rrel = 0; - if (argc > 1) { - n = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr); - if (n == EOF) { - warnx("invalid value: %s", argv[1]); - dusage = 1; + for (i = 0; i < LEN(ctrls); i++) { + if (strncmp(ctrlstr, ctrls[i].name, + strlen(ctrls[i].name)) == 0) { + ctrls[i].mod(m, devstr, valstr); break; } - if (n > 0) { - if (*lstr == '+' || *lstr == '-') - lrel = rrel = 1; - l = strtof(lstr, NULL); - } - if (n > 1) { - if (*rstr == '+' || *rstr == '-') - rrel = 1; - r = strtof(rstr, NULL); - } - } - switch (argc > 1 ? n : k) { - case 0: - if (!oflag) - printmixer(m); - printdev(m->dev, oflag); - goto done; - case 1: - r = l; /* FALLTHROUGH */ - case 2: - if (lrel) - l += m->dev->lvol; - if (rrel) - r += m->dev->rvol; - - if (l < M_VOLMIN) - l = M_VOLMIN; - else if (l > M_VOLMAX) - l = M_VOLMAX; - if (r < M_VOLMIN) - r = M_VOLMIN; - else if (r > M_VOLMAX) - r = M_VOLMAX; - - printf("%s.volume: %.2f:%.2f -> %.2f:%.2f\n", - m->dev->name, m->dev->lvol, m->dev->rvol, l, r); - - if (mixer_setvol(m, l, r) < 0) { - warnx("cannot change volume"); - rc = 1; - } - argc -= 2; - argv += 2; } + if (i == LEN(ctrls)) + warnx("invalid control: %s", ctrlstr); +next: + free(p); + argc--; + argv++; } else break; } - if (dusage) { - (void)mixer_close(m); - usage(); - } else - printall(m, oflag); + printall(m, oflag); done: (void)mixer_close(m); - return (rc); + return (0); } diff --git a/rc.d/mixer b/rc.d/mixer @@ -0,0 +1,104 @@ +#!/bin/sh - +# +# Copyright (c) 2004 The FreeBSD Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD: releng/12.2/libexec/rc/rc.d/mixer 298514 2016-04-23 16:10:54Z lme $ +# + +# PROVIDE: mixer +# REQUIRE: FILESYSTEMS +# KEYWORD: nojail shutdown + +. /etc/rc.subr + +name="mixer" +desc="Save and restore soundcard mixer values" +rcvar="mixer_enable" +stop_cmd="mixer_stop" +start_cmd="mixer_start" +reload_cmd="mixer_start" +extra_commands="reload" + +# +# List current mixer devices to stdout. +# +list_mixers() +{ + ( cd /dev ; ls mixer* 2>/dev/null ) +} + +# +# Save state of an individual mixer specified as $1 +# +mixer_save() +{ + local dev + + dev="/dev/${1}" + if [ -r ${dev} ]; then + /usr/sbin/mixer_prog -f ${dev} -o > /var/db/${1}-state 2>/dev/null + fi +} + +# +# Restore the state of an individual mixer specified as $1 +# +mixer_restore() +{ + local file dev + + dev="/dev/${1}" + file="/var/db/${1}-state" + if [ -r ${dev} -a -r ${file} ]; then + /usr/sbin/mixer_prog -f ${dev} `cat ${file}` > /dev/null + fi +} + +# +# Restore state of all mixers +# +mixer_start() +{ + local mixer + + for mixer in `list_mixers`; do + mixer_restore ${mixer} + done +} + +# +# Save the state of all mixers +# +mixer_stop() +{ + local mixer + + for mixer in `list_mixers`; do + mixer_save ${mixer} + done +} + +load_rc_config $name +run_rc_command "$1" diff --git a/rc.d/mixer.old b/rc.d/mixer.old @@ -0,0 +1,104 @@ +#!/bin/sh - +# +# Copyright (c) 2004 The FreeBSD Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +# PROVIDE: mixer +# REQUIRE: FILESYSTEMS +# KEYWORD: nojail shutdown + +. /etc/rc.subr + +name="mixer" +desc="Save and restore soundcard mixer values" +rcvar="mixer_enable" +stop_cmd="mixer_stop" +start_cmd="mixer_start" +reload_cmd="mixer_start" +extra_commands="reload" + +# +# List current mixer devices to stdout. +# +list_mixers() +{ + ( cd /dev ; ls mixer* 2>/dev/null ) +} + +# +# Save state of an individual mixer specified as $1 +# +mixer_save() +{ + local dev + + dev="/dev/${1}" + if [ -r ${dev} ]; then + /usr/sbin/mixer -f ${dev} -s > /var/db/${1}-state 2>/dev/null + fi +} + +# +# Restore the state of an individual mixer specified as $1 +# +mixer_restore() +{ + local file dev + + dev="/dev/${1}" + file="/var/db/${1}-state" + if [ -r ${dev} -a -r ${file} ]; then + /usr/sbin/mixer -f ${dev} `cat ${file}` > /dev/null + fi +} + +# +# Restore state of all mixers +# +mixer_start() +{ + local mixer + + for mixer in `list_mixers`; do + mixer_restore ${mixer} + done +} + +# +# Save the state of all mixers +# +mixer_stop() +{ + local mixer + + for mixer in `list_mixers`; do + mixer_save ${mixer} + done +} + +load_rc_config $name +run_rc_command "$1"