db6ba1e0c5
- Use correct e-mail address. - Set FreeBSD 14.0 as introduction for the updated mixer(8) utility. Submitted by: christos@ Differential Revision: https://reviews.freebsd.org/D31636 Sponsored by: NVIDIA Networking
541 lines
14 KiB
Groff
541 lines
14 KiB
Groff
.\"-
|
|
.\" Copyright (c) 2021 Christos Margiolis <christos@FreeBSD.org>
|
|
.\"
|
|
.\" Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
.\" of this software and associated documentation files (the "Software"), to deal
|
|
.\" in the Software without restriction, including without limitation the rights
|
|
.\" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
.\" copies of the Software, and to permit persons to whom the Software is
|
|
.\" furnished to do so, subject to the following conditions:
|
|
.\"
|
|
.\" The above copyright notice and this permission notice shall be included in
|
|
.\" all copies or substantial portions of the Software.
|
|
.\"
|
|
.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
.\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
.\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
.\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
.\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
.\" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
.\" THE SOFTWARE.
|
|
.\"
|
|
.\" $FreeBSD$
|
|
.\"
|
|
|
|
.Dd September 22, 2021
|
|
.Dt mixer 3
|
|
.Os
|
|
.Sh NAME
|
|
.Nm mixer_open ,
|
|
.Nm mixer_close ,
|
|
.Nm mixer_get_dev ,
|
|
.Nm mixer_get_dev_byname ,
|
|
.Nm mixer_add_ctl ,
|
|
.Nm mixer_add_ctl_s ,
|
|
.Nm mixer_remove_ctl ,
|
|
.Nm mixer_get_ctl ,
|
|
.Nm mixer_get_ctl_byname ,
|
|
.Nm mixer_set_vol ,
|
|
.Nm mixer_set_mute ,
|
|
.Nm mixer_mod_recsrc ,
|
|
.Nm mixer_get_dunit ,
|
|
.Nm mixer_set_dunit ,
|
|
.Nm mixer_get_mode,
|
|
.Nm mixer_get_nmixers ,
|
|
.Nm MIX_ISDEV ,
|
|
.Nm MIX_ISMUTE ,
|
|
.Nm MIX_ISREC ,
|
|
.Nm MIX_ISRECSRC ,
|
|
.Nm MIX_VOLNORM ,
|
|
.Nm MIX_VOLDENORM
|
|
.Nd interface to OSS mixers
|
|
.Sh LIBRARY
|
|
Mixer library (libmixer, -lmixer)
|
|
.Sh SYNOPSIS
|
|
.In mixer.h
|
|
.Ft struct mixer *
|
|
.Fn mixer_open "const char *name"
|
|
.Ft int
|
|
.Fn mixer_close "struct mixer *m"
|
|
.Ft struct mix_dev *
|
|
.Fn mixer_get_dev "struct mixer *m" "int devno"
|
|
.Ft struct mix_dev *
|
|
.Fn mixer_get_dev_byname "struct mixer *m" "name"
|
|
.Ft int
|
|
.Fn mixer_add_ctl "struct mix_dev *parent" "int id" "const char *name" \
|
|
"int (*mod)(struct mix_dev *d, void *p)" \
|
|
"int (*print)(struct mix_dev *d, void *p)
|
|
.Ft int
|
|
.Fn mixer_add_ctl_s "mix_ctl_t *ctl"
|
|
.Ft int
|
|
.Fn mixer_remove_ctl "mix_ctl_t *ctl"
|
|
.Ft mix_ctl_t *
|
|
.Fn mixer_get_ctl "struct mix_dev *d" "int id"
|
|
.Ft mix_ctl_t *
|
|
.Fn mixer_get_ctl_byname "struct mix_dev *d" "const char *name"
|
|
.Ft int
|
|
.Fn mixer_set_vol "struct mixer *m" "mix_volume_t vol"
|
|
.Ft int
|
|
.Fn mixer_set_mute "struct mixer *m" "int opt"
|
|
.Ft int
|
|
.Fn mixer_mod_recsrc "struct mixer *m" "int opt"
|
|
.Ft int
|
|
.Fn mixer_get_dunit "void"
|
|
.Ft int
|
|
.Fn mixer_set_dunit "struct mixer *m" "int unit"
|
|
.Ft int
|
|
.Fn mixer_get_mode "int unit"
|
|
.Ft int
|
|
.Fn mixer_get_nmixers "void"
|
|
.Ft int
|
|
.Fn MIX_ISDEV "struct mixer *m" "int devno"
|
|
.Ft int
|
|
.Fn MIX_ISMUTE "struct mixer *m" "int devno"
|
|
.Ft int
|
|
.Fn MIX_ISREC "struct mixer *m" "int devno"
|
|
.Ft int
|
|
.Fn MIX_ISRECSRC "struct mixer *m" "int devno"
|
|
.Ft float
|
|
.Fn MIX_VOLNORM "int v"
|
|
.Ft int
|
|
.Fn MIX_VOLDENORM "float v"
|
|
.Sh DESCRIPTION
|
|
The
|
|
.Nm mixer
|
|
library allows userspace programs to access and manipulate OSS sound mixers in
|
|
a simple way.
|
|
.Ss Mixer
|
|
.Pp
|
|
A mixer is described by the following structure:
|
|
.Bd -literal
|
|
struct mixer {
|
|
TAILQ_HEAD(, mix_dev) devs; /* device list */
|
|
struct mix_dev *dev; /* selected device */
|
|
oss_mixerinfo mi; /* mixer info */
|
|
oss_card_info ci; /* audio card info */
|
|
char name[NAME_MAX]; /* mixer name (e.g /dev/mixer0) */
|
|
int fd; /* file descriptor */
|
|
int unit; /* audio card unit */
|
|
int ndev; /* number of devices */
|
|
int devmask; /* supported devices */
|
|
#define MIX_MUTE 0x01
|
|
#define MIX_UNMUTE 0x02
|
|
#define MIX_TOGGLEMUTE 0x04
|
|
int mutemask; /* muted devices */
|
|
int recmask; /* recording devices */
|
|
#define MIX_ADDRECSRC 0x01
|
|
#define MIX_REMOVERECSRC 0x02
|
|
#define MIX_SETRECSRC 0x04
|
|
#define MIX_TOGGLERECSRC 0x08
|
|
int recsrc; /* recording sources */
|
|
#define MIX_MODE_MIXER 0x01
|
|
#define MIX_MODE_PLAY 0x02
|
|
#define MIX_MODE_REC 0x04
|
|
int mode; /* dev.pcm.X.mode sysctl */
|
|
int f_default; /* default mixer flag */
|
|
};
|
|
.Ed
|
|
.Pp
|
|
The fields are follows:
|
|
.Bl -tag -width "f_default"
|
|
.It Fa devs
|
|
A tail queue structure containing all supported mixer devices.
|
|
.It Fa dev
|
|
A pointer to the currently selected device. The device is one of the elements in
|
|
.Ar devs .
|
|
.It Fa mi
|
|
OSS information about the mixer. Look at the definition of the
|
|
.Ft oss_mixerinfo
|
|
structure in
|
|
.In sys/soundcard.h
|
|
to see its fields.
|
|
.It Fa ci
|
|
OSS audio card information. This structure is also defined in
|
|
.In sys/soundcard.h .
|
|
.It Fa name
|
|
Path to the mixer (e.g /dev/mixer0).
|
|
.It Fa fd
|
|
File descriptor returned when the mixer is opened in
|
|
.Fn mixer_open .
|
|
.It Fa unit
|
|
Audio card unit. Since each mixer device maps to a pcmX device,
|
|
.Ar unit
|
|
is always equal to the number of that pcmX device. For example, if the audio
|
|
device's number is 0 (i.e pcm0), then
|
|
.Ar unit
|
|
is 0 as well. This number is useful when checking if the mixer's audio
|
|
card is the default one.
|
|
.It Fa ndev
|
|
Number of devices in
|
|
.Ar devs .
|
|
.It Fa devmask
|
|
Bit mask containing all supported devices for the mixer. For example
|
|
if device 10 is supported, then the 10th bit in the mask will be set. By default,
|
|
.Fn mixer_open
|
|
stores only the supported devices in devs, so it's very unlikely this mask will
|
|
be needed.
|
|
.It Fa mutemask
|
|
Bit mask containing all muted devices. The logic is the same as with
|
|
.Ar devmask .
|
|
.It Fa recmask
|
|
Bit mask containing all recording devices. Again, same logic as with the
|
|
other masks.
|
|
.It Fa recsrc
|
|
Bit mask containing all recording sources. Yes, same logic again.
|
|
.It Fa mode
|
|
Bit mask containing the supported modes for this audio device. It holds the value
|
|
of the
|
|
.Ar dev.pcm.X.mode
|
|
sysctl.
|
|
.It Fa f_default
|
|
Flag which tells whether the mixer's audio card is the default one.
|
|
.El
|
|
.Ss Mixer device
|
|
.Pp
|
|
Each mixer device stored in a mixer is described as follows:
|
|
.Bd -literal
|
|
struct mix_dev {
|
|
struct mixer *parent_mixer; /* parent mixer */
|
|
char name[NAME_MAX]; /* device name (e.g "vol") */
|
|
int devno; /* device number */
|
|
struct mix_volume {
|
|
#define MIX_VOLMIN 0.0f
|
|
#define MIX_VOLMAX 1.0f
|
|
#define MIX_VOLNORM(v) ((v) / 100.0f)
|
|
#define MIX_VOLDENORM(v) ((int)((v) * 100.0f + 0.5f))
|
|
float left; /* left volume */
|
|
float right; /* right volume */
|
|
} vol;
|
|
int nctl; /* number of controls */
|
|
TAILQ_HEAD(, mix_ctl) ctls; /* control list */
|
|
TAILQ_ENTRY(mix_dev) devs;
|
|
};
|
|
.Ed
|
|
.Pp
|
|
The fields are follows:
|
|
.Bl -tag -width "parent_mixer"
|
|
.It Fa parent_mixer
|
|
Pointer to the mixer the device is attached to.
|
|
.It Fa name
|
|
Device name given by the OSS API. Devices can have one of the following names:
|
|
.Bd -ragged
|
|
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
|
|
.It Fa devno
|
|
Device's index in the SOUND_MIXER_NRDEVICES macro defined in
|
|
.In sys/soundcard.h .
|
|
This number is used to check against the masks defined in the
|
|
.Ar mixer
|
|
structure.
|
|
.It Fa left, right
|
|
Left and right-ear volumes. Although the OSS API stores volumes in integers from
|
|
0-100, we normalize them to 32-bit floating point numbers. However, the volumes
|
|
can be denormalized using the
|
|
.Ar MIX_VOLDENORM
|
|
macro if needed.
|
|
.It Fa nctl
|
|
Number of user-defined mixer controls associated with the device.
|
|
.It Fa ctls
|
|
A tail queue containing user-defined mixer controls.
|
|
.El
|
|
.Ss User-defined mixer controls
|
|
.Pp
|
|
Each mixer device can have user-defined controls. The control structure
|
|
is defined as follows:
|
|
.Bd -literal
|
|
struct mix_ctl {
|
|
struct mix_dev *parent_dev; /* parent device */
|
|
int id; /* control id */
|
|
char name[NAME_MAX]; /* control name */
|
|
int (*mod)(struct mix_dev *, void *); /* modify control values */
|
|
int (*print)(struct mix_dev *, void *); /* print control */
|
|
TAILQ_ENTRY(mix_ctl) ctls;
|
|
};
|
|
.Ed
|
|
.Pp
|
|
The fields are follows:
|
|
.Bl -tag -width "parent_dev"
|
|
.It Fa parent_dev
|
|
Pointer to the device the control is attached to.
|
|
.It Fa id
|
|
Control ID assigned by the caller. Even though the library will
|
|
report it, care has to be taken to not give a control the same ID in case
|
|
the caller has to choose controls using their ID.
|
|
.It Fa name
|
|
Control name. As with
|
|
.Ar id ,
|
|
the caller has to make sure the same name is not used more than once.
|
|
.It Fa mod
|
|
Function pointer to a control modification function. As in
|
|
.Xr mixer 8 ,
|
|
each mixer control's values can be modified. For example, if we have a
|
|
volume control, the
|
|
.Ar mod
|
|
function will be responsible for handling volume changes.
|
|
.It Fa print
|
|
Function pointer to a control print function.
|
|
.El
|
|
.Ss Opening and closing the mixer
|
|
.Pp
|
|
The application must first call the
|
|
.Fn mixer_open
|
|
function to obtain a handle to the device, which is used as an argument
|
|
in most other functions and macros. The parameter
|
|
.Ar name
|
|
specifies the path to the mixer. OSS mixers are stored under
|
|
.Ar /dev/mixerN
|
|
where
|
|
.Ar N
|
|
is the number of the mixer device. Each device maps to an actual
|
|
.Ar pcm
|
|
audio card, so
|
|
.Ar /dev/mixer0
|
|
is the mixer for
|
|
.Ar pcm0 ,
|
|
and so on. If
|
|
.Ar name
|
|
is
|
|
.Ar NULL
|
|
or
|
|
.Ar /dev/mixer ,
|
|
.Fn mixer_open
|
|
opens the default mixer (hw.snd.defaul_unit).
|
|
.Pp
|
|
The
|
|
.Fn mixer_close
|
|
function frees resources and closes the mixer device. It's a good practice to
|
|
always call it when the application is done using the mixer.
|
|
.Ss Manipulating the mixer
|
|
.Pp
|
|
The
|
|
.Fn mixer_get_dev
|
|
and
|
|
.Fn mixer_get_dev_byname
|
|
functions select a mixer device, either by its number or by its name
|
|
respectively. The mixer structure keeps a list of all the devices, but only
|
|
one can be manipulated at a time. Each time a new device is to be manipulated,
|
|
one of the two functions has to be called.
|
|
.Pp
|
|
The
|
|
.Fn mixer_set_vol
|
|
function changes the volume of the selected mixer device. The
|
|
.Ar vol
|
|
parameter is a structure that stores the left and right volumes of a given
|
|
device. The allowed volume values are between MIX_VOLMIN (0.0) and
|
|
MIX_VOLMAX (1.0).
|
|
.Pp
|
|
The
|
|
.Fn mixer_set_mute
|
|
function modifies the mute of a selected device. The
|
|
.Ar opt
|
|
parameter has to be one of the following options:
|
|
.Bl -tag -width MIX_TOGGLEMUTE -offset indent
|
|
.It Dv MIX_MUTE
|
|
Mute the device.
|
|
.It Dv MIX_UNMUTE
|
|
Unmute the device.
|
|
.It Dv MIX_TOGGLEMUTE
|
|
Toggle the device's mute (e.g mute if unmuted and unmute if muted).
|
|
.El
|
|
.Pp
|
|
The
|
|
.Fn mixer_mod_recsrc
|
|
function modifies a recording device. The selected device has to be
|
|
a recording device, otherwise the function will fail. The
|
|
.Ar opt
|
|
parameter has to be one of the following options:
|
|
.Bl -tag -width MIX_REMOVERECSRC -offset indent
|
|
.It Dv MIX_ADDRECSRC
|
|
Add device to the recording sources.
|
|
.It Dv MIX_REMOVERECSRC
|
|
Remove device from the recording sources.
|
|
.It Dv MIX_SETRECSRC
|
|
Set device as the only recording source.
|
|
.It Dv MIX_TOGGLERECSRC
|
|
Toggle device from the recording sources.
|
|
.El
|
|
.Pp
|
|
The
|
|
.Fn mixer_get_dunit
|
|
and
|
|
.Fn mixer_set_dunit
|
|
functions get and set the default audio card in the system. Although this is
|
|
not really a mixer feature, it's useful to have instead of having to use
|
|
the
|
|
.Xr sysctl 3
|
|
controls.
|
|
.Pp
|
|
The
|
|
.Fn mixer_get_mode
|
|
function returns the playback/recording mode of the audio device the mixer
|
|
belongs to. The available values are the following:
|
|
.Bl -tag -width "MIX_STATUS_PLAY | MIX_STATUS_REC" -offset indent
|
|
.It Dv MIX_STATUS_NONE
|
|
Neither playback nor recording.
|
|
.It Dv MIX_STATUS_PLAY
|
|
Playback.
|
|
.It Dv MIX_STATUS_REC
|
|
Recording.
|
|
.It Dv MIX_STATUS_PLAY | MIX_STATUS_REC
|
|
Playback and recording.
|
|
.El
|
|
.Pp
|
|
The
|
|
.Fn mixer_get_nmixers
|
|
function returns the total number of mixer devices in the system.
|
|
.Pp
|
|
The
|
|
.Fn MIX_ISDEV
|
|
macro checks if a device is actually a valid device for a given mixer. It's very
|
|
unlikely that this macro will ever be needed since the library stores only
|
|
valid devices by default.
|
|
.Pp
|
|
The
|
|
.Fn MIX_ISMUTE
|
|
macro checks if a device is muted.
|
|
.Pp
|
|
The
|
|
.Fn MIX_ISREC
|
|
macro checks if a device is a recording device.
|
|
.Pp
|
|
The
|
|
.Fn MIX_ISRECSRC
|
|
macro checks if a device is a recording source.
|
|
.Pp
|
|
The
|
|
.Fn MIX_VOLNORM
|
|
macro normalizes a value to 32-bit floating point number. It's used
|
|
to normalize the volumes read from the OSS API.
|
|
.Pp
|
|
The
|
|
.Fn MIX_VOLDENORM
|
|
macro denormalizes the left and right volumes stores in the
|
|
.Ft mix_dev
|
|
structure.
|
|
.Ss Defining and using mixer controls
|
|
.Pp
|
|
The
|
|
.Fn mix_add_ctl
|
|
function creates a control and attaches it to the device specified in the
|
|
.Ar parent
|
|
argument.
|
|
.Pp
|
|
The
|
|
.Fn mix_add_ctl_s
|
|
function does the same thing as with
|
|
.Fn mix_add_ctl
|
|
but the caller passes a
|
|
.Ft mix_ctl_t *
|
|
structure instead of each field as a seperate argument.
|
|
.Pp
|
|
The
|
|
.Fn mixer_remove_ctl
|
|
functions removes a control from the device its attached to.
|
|
.Pp
|
|
The
|
|
.Fn mixer_get_ctl
|
|
function searches for a control in the device specified in the
|
|
.Ar d
|
|
argument and returns a pointer to it. The search is done using the control's ID.
|
|
.Pp
|
|
The
|
|
.Fn mixer_get_ctl_byname
|
|
function is the same as with
|
|
.Fn mixer_get_ctl
|
|
but the search is done using the control's name.
|
|
.Sh RETURN VALUES
|
|
.Pp
|
|
The
|
|
.Fn mixer_open
|
|
function returns the newly created handle on success and NULL on failure.
|
|
.Pp
|
|
The
|
|
.Fn mixer_close ,
|
|
.Fn mixer_set_vol ,
|
|
.Fn mixer_set_mute ,
|
|
.Fn mixer_mod_recsrc ,
|
|
.Fn mixer_get_dunut ,
|
|
.Fn mixer_set_dunit
|
|
and
|
|
.Fn mixer_get_nmixers
|
|
functions return 0 or positive values on success and -1 on failure.
|
|
.Pp
|
|
The
|
|
.Fn mixer_get_dev
|
|
and
|
|
.Fn mixer_get_dev_byname
|
|
functions return the selected device on success and NULL on failure.
|
|
.Pp
|
|
All functions set the value of
|
|
.Ar errno
|
|
on failure.
|
|
.Sh EXAMPLES
|
|
.Ss Change the volume of a device
|
|
.Bd -literal
|
|
struct mixer *m;
|
|
mix_volume_t vol;
|
|
char *mix_name, *dev_name;
|
|
|
|
mix_name = ...;
|
|
if ((m = mixer_open(mix_name)) == NULL)
|
|
err(1, "mixer_open: %s", mix_name);
|
|
|
|
dev_name = ...;
|
|
if ((m->dev = mixer_get_dev_byname(m, dev_name)) < 0)
|
|
err(1, "unknown device: %s", dev_name);
|
|
|
|
vol.left = ...;
|
|
vol.right = ....;
|
|
if (mixer_set_vol(m, vol) < 0)
|
|
warn("cannot change volume");
|
|
|
|
(void)mixer_close(m);
|
|
.Ed
|
|
.Ss Mute all unmuted devices
|
|
.Bd -literal
|
|
struct mixer *m;
|
|
struct mix_dev *dp;
|
|
|
|
if ((m = mixer_open(NULL)) == NULL) /* Open the default mixer. */
|
|
err(1, "mixer_open");
|
|
TAILQ_FOREACH(dp, &m->devs, devs) {
|
|
m->dev = dp; /* Select device. */
|
|
if (M_ISMUTE(m, dp->devno))
|
|
continue;
|
|
if (mixer_set_mute(m, MIX_MUTE) < 0)
|
|
warn("cannot mute device: %s", dp->name);
|
|
}
|
|
|
|
(void)mixer_close(m);
|
|
.Ed
|
|
.Ss Print all recording sources' names and volumes
|
|
.Bd -literal
|
|
struct mixer *m;
|
|
struct mix_dev *dp;
|
|
|
|
char *mix_name, *dev_name;
|
|
|
|
mix_name = ...;
|
|
if ((m = mixer_open(mix_name)) == NULL)
|
|
err(1, "mixer_open: %s", mix_name);
|
|
|
|
TAILQ_FOREACH(dp, &m->devs, devs) {
|
|
if (M_ISRECSRC(m, dp->devno))
|
|
printf("%s\\t%.2f:%.2f\\n",
|
|
dp->name, dp->vol.left, dp->vol.right);
|
|
}
|
|
|
|
(void)mixer_close(m);
|
|
.Ed
|
|
.Sh SEE ALSO
|
|
.Xr mixer 8 ,
|
|
.Xr sound 4 ,
|
|
.Xr sysctl 3 ,
|
|
.Xr queue 3
|
|
and
|
|
.Xr errno 2
|
|
.Sh AUTHORS
|
|
.An Christos Margiolis Aq Mt christos@FreeBSD.org
|