Mixer programming


Most sound cards have some kind of mixer which can be used for adjusting volumes. OSS API defines a device file (/dev/mixer) which can be used to access mixer functions of the card. It is possible that there are more than one mixers if there are several sound cards installed on the system. Actually mixer device files are numbered (/dev/mixer0, /dev/mixer1, ...) and /dev/mixer is just a symbolic link to one of these device files. Usually the link points to /dev/mixer0 but user has freedom to assign the link differently). 
It is possible that no mixers are present on the system. All sound cards simply don't have mixer functionality. This is common with oldest sound cards. However it is possible that some professional/high end sound cards don't have mixer. So don't assume that there is a mixer in every system. All systems have /dev/mixer0 but the ioctl calls will return ENXIO if no mixer is present. Your program should be prepared to handle ENXIO returned by any of the ioctl calls. 

The mixer API of OSS is based on channels. A mixer channel is a numbered object which represents a physical control/slider of the mixer. Each of the channels have independently adjustable value which may vary between 0 (off) and 100 (maximum volume). Most of the channels are stereo controls so it is possible to set value for both stereo channels separately which permits implementation of balance. The mixer API contains few ioctl calls for setting an getting values of these mixer channels. 

In addition to volumes the mixer API also controls selection of recording source(s). With most sound cards it is possible to record simultaneously only from one source while few cards (PAS16) allow several recording sources to be active at the same time. After boot the microphone input is usually selected as the recording source (there is no guarantee that this is always true). 
Changes to the mixer settings will remain active until the system is rebooted or changed again. The driver doesn't change the mixer settings itself. 

The third class of mixer ioctl calls are functions used for querying capabilities of the mixer. With these calls it is possible to check which mixer channels are actually present and which of them can be used as input sources. 
Set of available mixer channels is not fixed since different sound cards have different mixers. For this reason it is important to check which channels are available before attempting to use mixer. It is possible that even the main volume setting is missing (GUS MAX, MSS, etc). The driver will return an error (EINVAL) if a nonexistent mixer channel is assigned. Mixer channels are bound to "pins" of the mixer chip. Some mixer chips are used in cards made by several manufacturers. It is possible that some manufacturers have connected the mixer chip in different way than the others. In this case some mixer channels may have different meaning than defined below. 

It is recommended that mixer functionality is not embedded in programs whose main function is something else (for example audio). In some sound cards the hardware level mixer implementation may differ significantly from the normal situation. In this case only a mixer program tailored for that card works properly. Adding mixer functionality to programs may cause unexpected support problems in future. 

Types of mixer programs

The mixer API of OSS permits writing of generic mixer programs which work with almost any sound cards. This is possible only if the program uses query functions of this API to check capabilities of the device before trying to use it. 

It is also possible to design a mixer so that it works best with a particular sound card. In this way it is easier to design a nicely looking GUI which matches the hardware properly. Also in this case it is a good idea to check that the required mixer channels are actually present by using the query ioctl functions defined below. In this case you should indicate clearly in the documentation of the program that it requires a particular sound card. 

Mixer channels

The mixer channels have an unique number between 0 and 30. soundcard.h defines some mnemonic names for the channels. Note that these are the current ones. New ones could be added in the future. 

The macro SOUND_MIXER_NRDEVICES gives the number of channels known when this version of soundcard.h was written. Any program should not try to access channels greater or equal than SOUND_MIXER_NRDEVICES. 

The following channels are currently known by the driver: 
  • SOUND_MIXER_VOLUME - This is the master output level (headphone/line out volume).
  • SOUND_MIXER_TREBLE - This channel controls the treble level of all of the output channels. 
  • SOUND_MIXER_BASS - This channel controls the bass level of all of the output channels.
  • SOUND_MIXER_SYNTH - This channel control volume of the synthesizer input (FM, wave table) of the sound card. In some cases the synthesizer may be connected to some other input too.
  • SOUND_MIXER_PCM - Output level for the audio (CODEC, PCM, ADC) device (/dev/dsp and /dev/audio).
  • SOUND_MIXER_SPEAKER - Output volume for the PC speaker signals. Works only if the speaker output is connected directly to the sound card. Doesn't affect the built in speaker, just the signal which goes through the sound card. On some sound cards this is actually a generic "mono" input which may control some other function. For example, in GUS MAX this control adjusts volume of microphone signal routed to line out.
  • SOUND_MIXER_LINE - Volume level for the line in jack. 
    These are generic mixer channels which are used in cases when precise meaning of a physical mixer channel is not known. Actual meaning of these signals is "vendor defined". Usually these channels are connected to synth, line in and CD inputs of the card but order of the assignment is not known to the driver. 
  • SOUND_MIXER_MIC - Volume for the signal coming from the microphone in jack. In some cases this signal controls only recording volume from microphone and in some cards it controls volume of microphone signal routed to output of the card too. In some cards the microphone is not connected to the "true" microphone input at all but to one of the line level inputs of the mixer chip.
  • SOUND_MIXER_CD - Volume level for signal connected to the CD audio input.
  • SOUND_MIXER_IMIX - Some kind of recording monitor on the PAS16 and some other cards. Controls the output (headphone jack) volume of the selected recording sources while recording. This channel has effect just when recording.
  • SOUND_MIXER_ALTPCM - Volume of the alternate codec device (the SB emulation of the PAS16 board).
  • SOUND_MIXER_RECLEV - Global recording level setting. In the SB16 card this controls the input gain which has just 4 possible levels.
It is important to remember that exact effect of mixer channels may be slightly different in some sound cards. For this reason try to avoid too specific descriptions of the mixer channels in documentation of a mixer program. 

Querying capabilities of the mixer

The mixer interface of OSS driver has been designed so that it is possible to compile a mixer program on one system and to use it on another system with different hardware setup. This is possible only if the mixer program follows some guidelines. It has to query for the hardware configuration before making any other actions with the mixer interface. It is not dangerous if the program tries just to change volume of a channel without making the query since the ioctl call returns an error if there is something wrong with the config. However if a mixer programs shows the complete list of channels even if there is no mixer, the user will get confused. The following ioctl calls give the program a way to determine the current setup. 

Using mixer query interface

All query functions of the mixer API return a bitmask in an integer variable passed as an argument to the ioctl call. The following code fragment shows the generic method used in the calls defined in next chapters. 
     int mask;
    if (ioctl(mixer_fd, SOUND_MIXER_READ_xxxx, &mask) == -1) 
    {No mixer available - Handle "gracefully"}
It is important to note that any ioctl call of the mixer API may return "errno==ENXIO" if no mixer is present (it is always possible to open /dev/mixer0 even when no mixer is available). Meaning of the bits of the mask are defined in later sections. Testing the bit corresponding a mixer channel can be done using expression "mask & (1 << channel_no)". The channel_no may be one of the SOUND_MIXER_ macrose defined earlier (####insert pointer here) or an integer value between 0 and SOUND_MIXER_NRDEVICES. The later alternative is usefull when writing a mixer that dynamically adapts to capabilities of any card. 

Checking available mixer channels (SOUND_MIXER_READ_DEVMASK)

SOUND_MIXER_READ_DEVMASK returns a bitmask in the variable pointed by the argument (mask in this case). To see if a particular mixer channel is supported you need to test if the bit corresponding the channel number is set. Any attempt to access undefined mixer channels using channel specific ioctl calls will return an error (errno==EINVAL). 

Checking available recording devices (SOUND_MIXER_READ_RECMASK)

SOUND_MIXER_READ_RECMASK returns a bitmask where each bit represents a mixer channels. Only the channels having their bit set may be used as a recording channel. 

Checking if a device is mono or stereo (SOUND_MIXER_READ_STEREODEVS)

Most mixer devices have stereo capability so it is possible to set volumes independently for both the left and the right stereo channel of the mixer channel. However some devices are mono only and just volume of the left stereo channel is defined for them. ioctl call SOUND_MIXER_READ_STEREODEVS returns a bitmask where a 1 bit tells that the corresponding channels is stereo. A mixer program should use this information to deciding if it should draw sliders for both stereo channels or not. Having a stereo control displayed for a mono channel is not a big error but it may confuse user in some cases. 

Checking general capabilities of a mixer (SOUND_MIXER_READ_CAPS)

The ioctl call SOUND_MIXER_READ_CAPS returns a bitmask which describes general capabilities of a mixer. These capabilities are not related to any particular mixer channel. Currently just one mixer capability is defined. Bit SOUND_CAP_EXCL_INPUT is set to 1 if only one mixer channel can be selected as recording at the same time. If the bit is 0 then it is possible to have several recording devices selected at the same time. Actually checking this bit is not important since the difference between these alternatives is handled by the ioctl call used for selecting the recording channel. 

Naming of mixer channels

####Incomplete section 

soundcard.h defines also two sets of printable names for the channels. These name should be used when labeling or naming the mixer channels by application programs. The macro SOUND_DEVICE_LABELS contains a list of printable strings which can be used for example to label the sliders for the channels. You could access the names by defining a variable as: 

For example labels[SOUND_MIXER_VOLUME] contains a textual label for the main volume channel. 

The macro SOUND_DEVICE_NAMES is similar but it contains names to be used for example when parsing command lines etc. The names in this macro don't contain blancs or capital letters. 

Meaning of volume levels

OSS driver accepts volumes between 0 and 100. The 0 means off and the 100 means maximum. 

Most of the mixers have 3 to 8 bits for the volume and the driver scales between the local and hardware defined volume. Since this scaling is not accurate, the application should be careful when using the volume returned by the ioctl calls. If the application writes the volume and reads it back, the returned volume is usually slightly different (smaller) than the requested one. If the write-read sequence is repeated several times, the volume slides to zero even if the application makes no changes itself. It is recommended that the application reads the volume just during initialization and ignores the volume returned later. Note! The MIXERWRITE returns the modified volume in the argument after call. A temporary variable should be used as the argument. Otherwise the volume will slide down on each access. 

Getting and setting volumes (SOUND_MIXER_READ/SOUND_MIXER_WRITE)

An application program can read and/or write volume of a device by calling ioctl functions SOUND_MIXER_READ(chn) and SOUND_MIXER_WRITE(chn). The mixer channel is given as the chn argument to the macro. The channel number may be one of the channel identifiers defined in soundcard.h or an integer between 0 and SOUND_MIXER_NRDEVICES. For example the following call reads current volunme of microphone input: 
int vol;
if (ioctl(mixer_fd, SOUND_MIXER_READ(SOUND_MIXER_MIC), &vol)==-1)
/* An undefined mixer channel was accessed */
The program should check if an error was returned from the ioctl call. The driver will return an error if the mixer channel is not known or if there is no mixer at all. (Programs should not blindly terminate if this call returns an error). 

The volume for both stereo channels are returned in the same int variable. The least significant byte gives volume for the left channel and the next 8 bits for the for the right channel. The upper 16 bits are undefined and should be ingnored. For mono devices just the left channel value is valid (the right channel value is set to the left channel value by the driver). 

The volume setting can be altered by using ioctl SOUND_MIXER_WRITE. It works just like SOUND_MIXER_READ but in addition it alters the actual "hardware" volume of the channel. Note that this call also returns the new volume in the variable passed as an argument to the ioctl call. In some cases the value may be slightly different than the value passed to the call. 

Selecting the recording sources

OSS driver has two calls for selecting recording sources. In addition the SOUND_MIXER_READ_RECMASK returns the devices which can be used as recording devices. 

The SOUND_MIXER_READ_RECSRC returns a bitmask having a bit set for each of the currently active recording sources. The default is currently mic in but the application should not assume this. The recording source could have been changed after boot. 

The SOUND_MIXER_WRITE_RECSRC can be used to alter the recording source selection. If no bits are on, the mic input will be used. 

The SB Pro allows just one active input source at the same time. The driver has a simple expert system to handle invalid recording source selections. A mixer program should always check the recording mask after changing it. It should also update the display if the returned mask is something else than the requested one. 

Previous Section soundcard.h  Index Guide Menu Audio programming