commit 47529790452349925011e29913d897d103cd64af
parent 03d70146d1eb749e65aaf90dbaf4f5cd32bc5268
Author: Christos Margiolis <christos@margiolis.net>
Date: Sun, 1 Aug 2021 18:33:19 +0300
updated manpage and improved mixer_close
Diffstat:
3 files changed, 268 insertions(+), 76 deletions(-)
diff --git a/mixer_lib/mixer.3 b/mixer_lib/mixer.3
@@ -26,19 +26,26 @@
.Sh NAME
.Nm mixer_open ,
.Nm mixer_close ,
-.Nm mixer_getdev ,
-.Nm mixer_getdevbyname ,
-.Nm mixer_setvol ,
-.Nm mixer_setmute ,
-.Nm mixer_modrecsrc ,
-.Nm mixer_getdunit ,
-.Nm mixer_setdunit ,
-.Nm mixer_getstatus ,
-.Nm mixer_getnmixers ,
+.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_ISRECSRC ,
+.Nm MIX_VOLNORM ,
+.Nm MIX_VOLDENORM
.Nd interface to OSS mixers
.Sh LIBRARY
Mixer library (libmixer, -lmixer)
@@ -49,23 +56,35 @@ Mixer library (libmixer, -lmixer)
.Ft int
.Fn mixer_close "struct mixer *m"
.Ft struct mix_dev *
-.Fn mixer_getdev "struct mixer *m" "int devno"
+.Fn mixer_get_dev "struct mixer *m" "int devno"
.Ft struct mix_dev *
-.Fn mixer_getdevbyname "struct mixer *m" "name"
+.Fn mixer_get_dev_byname "struct mixer *m" "name"
.Ft int
-.Fn mixer_setvol "struct mixer *m" "mix_volume_t vol"
+.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_setmute "struct mixer *m" "int opt"
+.Fn mixer_add_ctl_s "mix_ctl_t *ctl"
.Ft int
-.Fn mixer_modrecsrc "struct mixer *m" "int opt"
+.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_getdunit "void"
+.Fn mixer_set_vol "struct mixer *m" "mix_volume_t vol"
.Ft int
-.Fn mixer_setdunit "struct mixer *m" "int unit"
+.Fn mixer_set_mute "struct mixer *m" "int opt"
.Ft int
-.Fn mixer_getstatus "int unit"
+.Fn mixer_mod_recsrc "struct mixer *m" "int opt"
.Ft int
-.Fn mixer_getnmixers "void"
+.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
@@ -74,6 +93,10 @@ Mixer library (libmixer, -lmixer)
.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
@@ -84,57 +107,174 @@ a simple way.
A mixer is described by the following structure:
.Bd -literal
struct mixer {
- TAILQ_HEAD(, mix_dev) devs;
- struct mix_dev *dev;
- oss_mixerinfo mi;
- oss_card_info ci;
- char name[NAME_MAX];
- int fd;
- int unit;
- int ndev;
- int devmask;
+ 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;
- int recmask;
+ 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;
- int f_default;
-#define MIX_STATUS_NONE 0x00
-#define MIX_STATUS_PLAY 0x01
-#define MIX_STATUS_REC 0x02
- int status;
+ 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 structure is described as follows:
+Each mixer device stored in a mixer is described as follows:
.Bd -literal
struct mix_dev {
- char name[NAME_MAX];
- int devno;
+ 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)roundf((v) * 100.0f))
- float left;
- float right;
+ 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
-.Ss Device names
-The name a device is guaranteed to be one of the following:
-.Bd -ragged -offset indent
+.Pp
+The fields are follows:
+.Bl -tag -width "parent_mixer"
+.It Fa parent_mixer
+Pointer to the parent mixer.
+.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
+Parent device for this control.
+.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
@@ -168,16 +308,16 @@ always call it when the application is done using the mixer.
.Ss Manipulating the mixer
.Pp
The
-.Fn mixer_getdev
+.Fn mixer_get_dev
and
-.Fn mixer_getdevbyname
+.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_setvol
+.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
@@ -185,7 +325,7 @@ device. The allowed volume values are between MIX_VOLMIN (0.0) and
MIX_VOLMAX (1.0).
.Pp
The
-.Fn mixer_setmute
+.Fn mixer_set_mute
function modifies the mute of a selected device. The
.Ar opt
parameter has to be one of the following options:
@@ -199,7 +339,7 @@ Toggle the device's mute (e.g mute if unmuted and unmute if muted).
.El
.Pp
The
-.Fn mixer_modrecsrc
+.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
@@ -216,9 +356,9 @@ Toggle device from the recording sources.
.El
.Pp
The
-.Fn mixer_getdunit
+.Fn mixer_get_dunit
and
-.Fn mixer_setdunit
+.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
@@ -226,8 +366,8 @@ the
controls.
.Pp
The
-.Fn mixer_getstatus
-function returns the playback/recording status of the audio device the mixer
+.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
@@ -241,7 +381,7 @@ Playback and recording.
.El
.Pp
The
-.Fn mixer_getnmixers
+.Fn mixer_get_nmixers
function returns the total number of mixer devices in the system.
.Pp
The
@@ -261,6 +401,48 @@ macro checks if a device is a recording device.
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
@@ -269,19 +451,19 @@ function returns the newly created handle on success and NULL on failure.
.Pp
The
.Fn mixer_close ,
-.Fn mixer_setvol ,
-.Fn mixer_setmute ,
-.Fn mixer_modrecsrc ,
-.Fn mixer_getdunut ,
-.Fn mixer_setdunit
+.Fn mixer_set_vol ,
+.Fn mixer_set_mute ,
+.Fn mixer_mod_recsrc ,
+.Fn mixer_get_dunut ,
+.Fn mixer_set_dunit
and
-.Fn mixer_getnmixers
+.Fn mixer_get_nmixers
functions return 0 or positive values on success and -1 on failure.
.Pp
The
-.Fn mixer_getdev
+.Fn mixer_get_dev
and
-.Fn mixer_getdevbyname
+.Fn mixer_get_dev_byname
functions return the selected device on success and NULL on failure.
.Pp
All functions set the value of
@@ -299,12 +481,12 @@ if ((m = mixer_open(mix_name)) == NULL)
err(1, "mixer_open: %s", mix_name);
dev_name = ...;
-if ((m->dev = mixer_getdevbyname(m, dev_name)) < 0)
+if ((m->dev = mixer_get_dev_byname(m, dev_name)) < 0)
err(1, "unknown device: %s", dev_name);
vol.left = ...;
vol.right = ....;
-if (mixer_setvol(m, vol) < 0)
+if (mixer_set_vol(m, vol) < 0)
warn("cannot change volume");
(void)mixer_close(m);
@@ -320,7 +502,7 @@ TAILQ_FOREACH(dp, &m->devs, devs) {
m->dev = dp; /* Select device. */
if (M_ISMUTE(m, dp->devno))
continue;
- if (mixer_setmute(m, MIX_MUTE) < 0)
+ if (mixer_set_mute(m, MIX_MUTE) < 0)
warn("cannot mute device: %s", dp->name);
}
@@ -348,6 +530,8 @@ TAILQ_FOREACH(dp, &m->devs, devs) {
.Sh SEE ALSO
.Xr mixer 8 ,
.Xr sound 4 ,
+.Xr sysctl 3 ,
+.Xr queue 3 ,
.Xr errno 2
.Sh AUTHORS
.An Christos Margiolis Aq Mt christos@margiolis.net
diff --git a/mixer_lib/mixer.c b/mixer_lib/mixer.c
@@ -143,18 +143,14 @@ int
mixer_close(struct mixer *m)
{
struct mix_dev *dp;
- mix_ctl_t *cp;
int r;
r = close(m->fd);
while (!TAILQ_EMPTY(&m->devs)) {
dp = TAILQ_FIRST(&m->devs);
TAILQ_REMOVE(&m->devs, dp, devs);
- while (!TAILQ_EMPTY(&dp->ctls)) {
- cp = TAILQ_FIRST(&dp->ctls);
- TAILQ_REMOVE(&dp->ctls, cp, ctls);
- free(cp);
- }
+ while (!TAILQ_EMPTY(&dp->ctls))
+ (void)mixer_remove_ctl(TAILQ_FIRST(&dp->ctls));
free(dp);
}
free(m);
@@ -215,7 +211,8 @@ mixer_add_ctl(struct mix_dev *parent_dev, int id, const char *name,
int (*mod)(struct mix_dev *, void *),
int (*print)(struct mix_dev *, void *))
{
- mix_ctl_t *ctl;
+ struct mix_dev *dp;
+ mix_ctl_t *ctl, *cp;
/* XXX: should we accept NULL name? */
if (parent_dev == NULL) {
@@ -230,8 +227,19 @@ mixer_add_ctl(struct mix_dev *parent_dev, int id, const char *name,
(void)strlcpy(ctl->name, name, sizeof(ctl->name));
ctl->mod = mod;
ctl->print = print;
- TAILQ_INSERT_TAIL(&parent_dev->ctls, ctl, ctls);
- parent_dev->nctl++;
+ dp = ctl->parent_dev;
+ /* Make sure the same ID or name already exists. */
+ if (!TAILQ_EMPTY(&dp->ctls)) {
+ TAILQ_FOREACH(cp, &dp->ctls, ctls) {
+ if (!strncmp(cp->name, name, sizeof(cp->name)) ||
+ cp->id == id) {
+ errno = EINVAL;
+ return (-1);
+ }
+ }
+ }
+ TAILQ_INSERT_TAIL(&dp->ctls, ctl, ctls);
+ dp->nctl++;
return (0);
}
diff --git a/mixer_lib/mixer.h b/mixer_lib/mixer.h
@@ -83,7 +83,7 @@ struct mixer {
int fd; /* file descriptor */
int unit; /* audio card unit */
int ndev; /* number of devices */
- int devmask; /* valid devices */
+ int devmask; /* supported devices */
#define MIX_MUTE 0x01
#define MIX_UNMUTE 0x02
#define MIX_TOGGLEMUTE 0x04
@@ -94,11 +94,11 @@ struct mixer {
#define MIX_SETRECSRC 0x04
#define MIX_TOGGLERECSRC 0x08
int recsrc; /* recording sources */
- int f_default; /* default mixer flag */
#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 */
};
struct mixer *mixer_open(const char *);