1.\"- 2.\" Copyright (c) 2021-2022 Christos Margiolis <christos@FreeBSD.org> 3.\" 4.\" Permission is hereby granted, free of charge, to any person obtaining a copy 5.\" of this software and associated documentation files (the "Software"), to deal 6.\" in the Software without restriction, including without limitation the rights 7.\" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8.\" copies of the Software, and to permit persons to whom the Software is 9.\" furnished to do so, subject to the following conditions: 10.\" 11.\" The above copyright notice and this permission notice shall be included in 12.\" all copies or substantial portions of the Software. 13.\" 14.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15.\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16.\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17.\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18.\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19.\" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20.\" THE SOFTWARE. 21.\" 22.Dd August 4, 2024 23.Dt MIXER 3 24.Os 25.Sh NAME 26.Nm mixer_open , 27.Nm mixer_close , 28.Nm mixer_get_dev , 29.Nm mixer_get_dev_byname , 30.Nm mixer_add_ctl , 31.Nm mixer_add_ctl_s , 32.Nm mixer_remove_ctl , 33.Nm mixer_get_ctl , 34.Nm mixer_get_ctl_byname , 35.Nm mixer_set_vol , 36.Nm mixer_set_mute , 37.Nm mixer_mod_recsrc , 38.Nm mixer_get_dunit , 39.Nm mixer_set_dunit , 40.Nm mixer_get_mode , 41.Nm mixer_get_nmixers , 42.Nm mixer_get_path , 43.Nm MIX_ISDEV , 44.Nm MIX_ISMUTE , 45.Nm MIX_ISREC , 46.Nm MIX_ISRECSRC , 47.Nm MIX_VOLNORM , 48.Nm MIX_VOLDENORM 49.Nd interface to OSS mixers 50.Sh LIBRARY 51Mixer library (libmixer, -lmixer) 52.Sh SYNOPSIS 53.In mixer.h 54.Ft struct mixer * 55.Fn mixer_open "const char *name" 56.Ft int 57.Fn mixer_close "struct mixer *m" 58.Ft struct mix_dev * 59.Fn mixer_get_dev "struct mixer *m" "int devno" 60.Ft struct mix_dev * 61.Fn mixer_get_dev_byname "struct mixer *m" "name" 62.Ft int 63.Fn mixer_add_ctl "struct mix_dev *parent" "int id" "const char *name" \ 64 "int (*mod)(struct mix_dev *d, void *p)" \ 65 "int (*print)(struct mix_dev *d, void *p)" 66.Ft int 67.Fn mixer_add_ctl_s "mix_ctl_t *ctl" 68.Ft int 69.Fn mixer_remove_ctl "mix_ctl_t *ctl" 70.Ft mix_ctl_t * 71.Fn mixer_get_ctl "struct mix_dev *d" "int id" 72.Ft mix_ctl_t * 73.Fn mixer_get_ctl_byname "struct mix_dev *d" "const char *name" 74.Ft int 75.Fn mixer_set_vol "struct mixer *m" "mix_volume_t vol" 76.Ft int 77.Fn mixer_set_mute "struct mixer *m" "int opt" 78.Ft int 79.Fn mixer_mod_recsrc "struct mixer *m" "int opt" 80.Ft int 81.Fn mixer_get_dunit "void" 82.Ft int 83.Fn mixer_set_dunit "struct mixer *m" "int unit" 84.Ft int 85.Fn mixer_get_mode "int unit" 86.Ft int 87.Fn mixer_get_nmixers "void" 88.Ft int 89.Fn mixer_get_path "char * buf" "size_t size" "int unit" 90.Ft int 91.Fn MIX_ISDEV "struct mixer *m" "int devno" 92.Ft int 93.Fn MIX_ISMUTE "struct mixer *m" "int devno" 94.Ft int 95.Fn MIX_ISREC "struct mixer *m" "int devno" 96.Ft int 97.Fn MIX_ISRECSRC "struct mixer *m" "int devno" 98.Ft float 99.Fn MIX_VOLNORM "int v" 100.Ft int 101.Fn MIX_VOLDENORM "float v" 102.Sh DESCRIPTION 103The 104.Nm mixer 105library allows userspace programs to access and manipulate OSS sound mixers in 106a simple way. 107.Ss Mixer 108A mixer is described by the following structure: 109.Bd -literal 110struct mixer { 111 TAILQ_HEAD(mix_devhead, mix_dev) devs; /* device list */ 112 struct mix_dev *dev; /* selected device */ 113 oss_mixerinfo mi; /* mixer info */ 114 oss_card_info ci; /* audio card info */ 115 char name[NAME_MAX]; /* mixer name (e.g /dev/mixer0) */ 116 int fd; /* file descriptor */ 117 int unit; /* audio card unit */ 118 int ndev; /* number of devices */ 119 int devmask; /* supported devices */ 120#define MIX_MUTE 0x01 121#define MIX_UNMUTE 0x02 122#define MIX_TOGGLEMUTE 0x04 123 int mutemask; /* muted devices */ 124 int recmask; /* recording devices */ 125#define MIX_ADDRECSRC 0x01 126#define MIX_REMOVERECSRC 0x02 127#define MIX_SETRECSRC 0x04 128#define MIX_TOGGLERECSRC 0x08 129 int recsrc; /* recording sources */ 130#define MIX_MODE_MIXER 0x01 131#define MIX_MODE_PLAY 0x02 132#define MIX_MODE_REC 0x04 133 int mode; /* dev.pcm.X.mode sysctl */ 134 int f_default; /* default mixer flag */ 135}; 136.Ed 137.Pp 138The fields are follows: 139.Bl -tag -width "f_default" 140.It Fa devs 141A tail queue structure containing all supported mixer devices. 142.It Fa dev 143A pointer to the currently selected device. 144The device is one of the elements in 145.Ar devs . 146.It Fa mi 147OSS information about the mixer. 148Look at the definition of the 149.Ft oss_mixerinfo 150structure in 151.In sys/soundcard.h 152to see its fields. 153.It Fa ci 154OSS audio card information. 155This structure is also defined in 156.In sys/soundcard.h . 157.It Fa name 158Path to the mixer (e.g /dev/mixer0). 159.It Fa fd 160File descriptor returned when the mixer is opened in 161.Fn mixer_open . 162.It Fa unit 163Audio card unit. 164Since each mixer device maps to a pcmX device, 165.Ar unit 166is always equal to the number of that pcmX device. 167For example, if the audio device's number is 0 (i.e pcm0), then 168.Ar unit 169is 0 as well. 170This number is useful when checking if the mixer's audio card is the default 171one. 172.It Fa ndev 173Number of devices in 174.Ar devs . 175.It Fa devmask 176Bit mask containing all supported devices for the mixer. 177For example, if device 10 is supported, then the 10th bit in the mask will be 178set. 179By default, 180.Fn mixer_open 181stores only the supported devices in devs, so it is very unlikely this mask will 182be needed. 183.It Fa mutemask 184Bit mask containing all muted devices. 185The logic is the same as with 186.Ar devmask . 187.It Fa recmask 188Bit mask containing all recording devices. 189Again, same logic as with the other masks. 190.It Fa recsrc 191Bit mask containing all recording sources. 192Yes, same logic again. 193.It Fa mode 194Bit mask containing the supported modes for this audio device. 195It holds the value of the 196.Ar dev.pcm.X.mode 197sysctl. 198.It Fa f_default 199Flag which tells whether the mixer's audio card is the default one. 200.El 201.Ss Mixer device 202Each mixer device stored in a mixer is described as follows: 203.Bd -literal 204struct mix_dev { 205 struct mixer *parent_mixer; /* parent mixer */ 206 char name[NAME_MAX]; /* device name (e.g "vol") */ 207 int devno; /* device number */ 208 struct mix_volume { 209#define MIX_VOLMIN 0.0f 210#define MIX_VOLMAX 1.0f 211#define MIX_VOLNORM(v) ((v) / 100.0f) 212#define MIX_VOLDENORM(v) ((int)((v) * 100.0f + 0.5f)) 213 float left; /* left volume */ 214 float right; /* right volume */ 215 } vol; 216 int nctl; /* number of controls */ 217 TAILQ_HEAD(mix_ctlhead, mix_ctl) ctls; /* control list */ 218 TAILQ_ENTRY(mix_dev) devs; 219}; 220.Ed 221.Pp 222The fields are follows: 223.Bl -tag -width "parent_mixer" 224.It Fa parent_mixer 225Pointer to the mixer the device is attached to. 226.It Fa name 227Device name given by the OSS API. 228Devices can have one of the following names: 229.Bd -ragged 230vol, bass, treble, synth, pcm, speaker, line, mic, cd, mix, 231pcm2, rec, igain, ogain, line1, line2, line3, dig1, dig2, dig3, 232phin, phout, video, radio, and monitor. 233.Ed 234.It Fa devno 235Device's index in the SOUND_MIXER_NRDEVICES macro defined in 236.In sys/soundcard.h . 237This number is used to check against the masks defined in the 238.Ar mixer 239structure. 240.It Fa left right 241Left and right-ear volumes. 242Although the OSS API stores volumes in integers from 0-100, \ 243we normalize them to 32-bit floating point numbers. 244However, the volumes can be denormalized using the 245.Ar MIX_VOLDENORM 246macro if needed. 247.It Fa nctl 248Number of user-defined mixer controls associated with the device. 249.It Fa ctls 250A tail queue containing user-defined mixer controls. 251.El 252.Ss User-defined mixer controls 253Each mixer device can have user-defined controls. 254The control structure is defined as follows: 255.Bd -literal 256struct mix_ctl { 257 struct mix_dev *parent_dev; /* parent device */ 258 int id; /* control id */ 259 char name[NAME_MAX]; /* control name */ 260 int (*mod)(struct mix_dev *, void *); /* modify control values */ 261 int (*print)(struct mix_dev *, void *); /* print control */ 262 TAILQ_ENTRY(mix_ctl) ctls; 263}; 264.Ed 265.Pp 266The fields are follows: 267.Bl -tag -width "parent_dev" 268.It Fa parent_dev 269Pointer to the device the control is attached to. 270.It Fa id 271Control ID assigned by the caller. 272Even though the library will report it, care has to be taken to not give \ 273a control the same ID in case the caller has to choose controls using their ID. 274.It Fa name 275Control name. 276As with 277.Ar id , 278the caller has to make sure the same name is not used more than once. 279.It Fa mod 280Function pointer to a control modification function. 281As in 282.Xr mixer 8 , 283each mixer control's values can be modified. 284For example, if we have a volume control, the 285.Ar mod 286function will be responsible for handling volume changes. 287.It Fa print 288Function pointer to a control print function. 289.El 290.Ss Opening and closing the mixer 291The application must first call the 292.Fn mixer_open 293function to obtain a handle to the device, which is used as an argument \ 294in most other functions and macros. 295The parameter 296.Ar name 297specifies the path to the mixer. 298OSS mixers are stored under 299.Ar /dev/mixerN 300where 301.Ar N 302is the number of the mixer device. 303Each device maps to an actual 304.Ar pcm 305audio card, so 306.Ar /dev/mixer0 307is the mixer for 308.Ar pcm0 , 309and so on. 310If 311.Ar name 312is 313.Ar NULL 314or 315.Ar /dev/mixer , 316.Fn mixer_open 317opens the default mixer (hw.snd.default_unit). 318.Pp 319The 320.Fn mixer_close 321function frees resources and closes the mixer device. 322It is a good practice to always call it when the application is done using the 323mixer. 324.Ss Manipulating the mixer 325The 326.Fn mixer_get_dev 327and 328.Fn mixer_get_dev_byname 329functions select a mixer device, either by its number or by its name 330respectively. 331The mixer structure keeps a list of all the devices, but only one can be 332manipulated at a time. 333Each time a new device is to be manipulated, one of the two functions has to be 334called. 335.Pp 336The 337.Fn mixer_set_vol 338function changes the volume of the selected mixer device. 339The 340.Ar vol 341parameter is a structure that stores the left and right volumes of a given 342device. 343The allowed volume values are between MIX_VOLMIN (0.0) and MIX_VOLMAX (1.0). 344.Pp 345The 346.Fn mixer_set_mute 347function modifies the mute of a selected device. 348The 349.Ar opt 350parameter has to be one of the following options: 351.Bl -tag -width MIX_TOGGLEMUTE -offset indent 352.It Dv MIX_MUTE 353Mute the device. 354.It Dv MIX_UNMUTE 355Unmute the device. 356.It Dv MIX_TOGGLEMUTE 357Toggle the device's mute (e.g mute if unmuted and unmute if muted). 358.El 359.Pp 360The 361.Fn mixer_mod_recsrc 362function modifies a recording device. 363The selected device has to be a recording device, otherwise the function will 364fail. 365The 366.Ar opt 367parameter has to be one of the following options: 368.Bl -tag -width MIX_REMOVERECSRC -offset indent 369.It Dv MIX_ADDRECSRC 370Add device to the recording sources. 371.It Dv MIX_REMOVERECSRC 372Remove device from the recording sources. 373.It Dv MIX_SETRECSRC 374Set device as the only recording source. 375.It Dv MIX_TOGGLERECSRC 376Toggle device from the recording sources. 377.El 378.Pp 379The 380.Fn mixer_get_dunit 381and 382.Fn mixer_set_dunit 383functions get and set the default audio card in the system. 384Although this is not really a mixer feature, it is useful to have instead of \ 385having to use the 386.Xr sysctl 3 387controls. 388.Pp 389The 390.Fn mixer_get_mode 391function returns the operating mode of the audio device the mixer belongs to. 392The following values can be OR'ed in case more than one mode is supported: 393.Bl -tag -width "MIX_MODE_MIXER" -offset indent 394.It Dv MIX_MODE_MIXER 395The audio device has a mixer. 396.It Dv MIX_MODE_PLAY 397The audio device supports playback. 398.It Dv MIX_MODE_REC 399The audio device supports recording. 400.El 401.Pp 402The 403.Fn mixer_get_nmixers 404function returns the maximum mixer unit number. 405Although this might sound as incorrect behavior, given that one would expect 406"nmixers" to refer to the total number of active mixers, it is more intuitive 407for applications that want to loop through all mixer devices (see the 408.Sx EXAMPLES 409section). 410.Pp 411The 412.Fn mixer_get_path 413function writes the path of the mixer device specified in the 414.Ar unit 415argument to the buffer specified in 416.Ar buf . 417.Ar unit 418can be either -1, in which case 419.Fn mixer_get_path 420will fetch the path of the default mixer, or between 0 and the maximum mixer 421unit. 422.Pp 423The 424.Fn MIX_ISDEV 425macro checks if a device is actually a valid device for a given mixer. 426It is very unlikely that this macro will ever be needed since the library \ 427stores only valid devices by default. 428.Pp 429The 430.Fn MIX_ISMUTE 431macro checks if a device is muted. 432.Pp 433The 434.Fn MIX_ISREC 435macro checks if a device is a recording device. 436.Pp 437The 438.Fn MIX_ISRECSRC 439macro checks if a device is a recording source. 440.Pp 441The 442.Fn MIX_VOLNORM 443macro normalizes a value to 32-bit floating point number. 444It is used to normalize the volumes read from the OSS API. 445.Pp 446The 447.Fn MIX_VOLDENORM 448macro denormalizes the left and right volumes stores in the 449.Ft mix_dev 450structure. 451.Ss Defining and using mixer controls 452The 453.Fn mix_add_ctl 454function creates a control and attaches it to the device specified in the 455.Ar parent 456argument. 457.Pp 458The 459.Fn mix_add_ctl_s 460function does the same thing as with 461.Fn mix_add_ctl 462but the caller passes a 463.Ft mix_ctl_t * 464structure instead of each field as a separate argument. 465.Pp 466The 467.Fn mixer_remove_ctl 468functions removes a control from the device its attached to. 469.Pp 470The 471.Fn mixer_get_ctl 472function searches for a control in the device specified in the 473.Ar d 474argument and returns a pointer to it. 475The search is done using the control's ID. 476.Pp 477The 478.Fn mixer_get_ctl_byname 479function is the same as with 480.Fn mixer_get_ctl 481but the search is done using the control's name. 482.Sh RETURN VALUES 483The 484.Fn mixer_open 485function returns the newly created handle on success and NULL on failure. 486.Pp 487The 488.Fn mixer_close , 489.Fn mixer_set_vol , 490.Fn mixer_set_mute , 491.Fn mixer_mod_recsrc , 492.Fn mixer_get_dunut , 493.Fn mixer_set_dunit , 494.Fn mixer_get_nmixers , 495and 496.Fn mixer_get_path 497functions return 0 or positive values on success and -1 on failure. 498.Pp 499The 500.Fn mixer_get_dev 501and 502.Fn mixer_get_dev_byname 503functions return the selected device on success and NULL on failure. 504.Pp 505All functions set the value of 506.Ar errno 507on failure. 508.Sh EXAMPLES 509.Ss Change the volume of a device 510.Bd -literal 511struct mixer *m; 512mix_volume_t vol; 513char *mix_name, *dev_name; 514 515mix_name = ...; 516if ((m = mixer_open(mix_name)) == NULL) 517 err(1, "mixer_open: %s", mix_name); 518 519dev_name = ...; 520if ((m->dev = mixer_get_dev_byname(m, dev_name)) < 0) 521 err(1, "unknown device: %s", dev_name); 522 523vol.left = ...; 524vol.right = ....; 525if (mixer_set_vol(m, vol) < 0) 526 warn("cannot change volume"); 527 528(void)mixer_close(m); 529.Ed 530.Ss Mute all unmuted devices 531.Bd -literal 532struct mixer *m; 533struct mix_dev *dp; 534 535if ((m = mixer_open(NULL)) == NULL) /* Open the default mixer. */ 536 err(1, "mixer_open"); 537TAILQ_FOREACH(dp, &m->devs, devs) { 538 m->dev = dp; /* Select device. */ 539 if (M_ISMUTE(m, dp->devno)) 540 continue; 541 if (mixer_set_mute(m, MIX_MUTE) < 0) 542 warn("cannot mute device: %s", dp->name); 543} 544 545(void)mixer_close(m); 546.Ed 547.Ss Print all recording sources' names and volumes 548.Bd -literal 549struct mixer *m; 550struct mix_dev *dp; 551 552char *mix_name, *dev_name; 553 554mix_name = ...; 555if ((m = mixer_open(mix_name)) == NULL) 556 err(1, "mixer_open: %s", mix_name); 557 558TAILQ_FOREACH(dp, &m->devs, devs) { 559 if (M_ISRECSRC(m, dp->devno)) 560 printf("%s\\t%.2f:%.2f\\n", 561 dp->name, dp->vol.left, dp->vol.right); 562} 563 564(void)mixer_close(m); 565.Ed 566.Ss Loop through all mixer devices in the system 567.Bd -literal 568struct mixer *m; 569char buf[NAME_MAX]; 570int n; 571 572if ((n = mixer_get_nmixers()) < 0) 573 errx(1, "no mixers present in the system"); 574for (i = 0; i < n; i++) { 575 (void)mixer_get_path(buf, sizeof(buf), i); 576 if ((m = mixer_open(buf)) == NULL) 577 continue; 578 ... 579 (void)mixer_close(m); 580} 581.Ed 582.Sh SEE ALSO 583.Xr queue 3 , 584.Xr sysctl 3 , 585.Xr sound 4 , 586.Xr mixer 8 587and 588.Xr errno 2 589.Sh AUTHORS 590.An Christos Margiolis Aq Mt christos@FreeBSD.org 591