Building the mixer

You must next construct a description of the mixer from its component parts. As mentioned earlier, a mixer consists of mixer elements, routes, and groups. In this example, there are 17 mixer elements, 18 routes, and 8 groups. The elements and routes are relatively straightforward to identify.

Elements are any of the symbols, and routes are the paths that data can travel between them. Use the functions listed above to create the elements; use ado_mixer_element_route_add() to create the routes.

Note: Don't forget to count the point sources and point sinks as elements. Though they may not be drawn as solid symbols, they are important parts of the audio architecture.

Identifying the groups is a little more troublesome. That's the reason why we enforce the rules on what can be in a group. It simplifies choosing how to divide the elements up into groups, and makes the drivers more consistent in form and behaviour. The eight groups are Master Output, Input Gain, PCM OUT, MIC OUT, CD OUT, PCM IN, MIC IN, and CD IN.

Groups in an audio mixer Figure 1. The groups in the sample analog mixer.

The PCM IN, MIC IN, and CD IN groups include the multiplexer, but specify a different input to it.

To build the mixer, first create the elements and routes, then pass pointers to the required elements to the functions that create the mixer group.

Here's the section of code that creates the master group, including all elements and routes:

int
build_example_mixer (MIXER_CONTEXT_T * example,
                     ado_mixer_t * mixer)
{
  int     error = 0;
  ado_mixer_delement_t *pre_elem, *elem = NULL;

  /* ################ */
  /* the OUTPUT GROUP */
  /* ################ */
  if ( (example->output_accu = ado_mixer_element_accu1
       (mixer, SND_MIXER_ELEMENT_OUTPUT_ACCU, 0)) == NULL )
    error++;

  pre_elem = example->output_accu;

  if ( !error && (elem = ado_mixer_element_volume1
       (mixer, "Output Volume", 2, output_range,
        example_master_vol_control,
        (void *) EXAMPLE_MASTER_LEFT, NULL)) == NULL)
    error++;

  if ( !error && ado_mixer_element_route_add
       (mixer, pre_elem, elem) != 0 )
    error++;

  example->master_vol = elem;
  pre_elem = elem;

  if ( !error && (elem = ado_mixer_element_sw2
       (mixer, "Output Mute", example_master_mute_control, 
       (void *) EXAMPLE_MASTER_LEFT, NULL)) == NULL )
    error++;

  if ( !error && ado_mixer_element_route_add
       (mixer, pre_elem, elem) != 0 )
     error++;

  example->master_mute = elem;

  pre_elem = elem;

  if ( !error && (elem = ado_mixer_element_io
       (mixer, "Output", SND_MIXER_ETYPE_OUTPUT, 0, 2,
        stereo_voices)) == NULL )
    error++;

  if ( !error && ado_mixer_element_route_add
       (mixer, pre_elem, elem) != 0 )
    error++;

  if ( !error &&
       (example->master_grp = ado_mixer_playback_group_create
       (mixer, SND_MIXER_MASTER_OUT, SND_MIXER_CHN_MASK_STEREO,
       example->master_vol, example->master_mute)) == NULL )
    error++;

  return (0);
}

Don't feel that you must have all the mixer elements represented in the mixer groups. This isn't the point. The mixer elements and mixer groups are meant to be complementary. Nonstandard, complex, or just plain weird controls may not be needed at the mixer group level. They may be better as a simple mixer element or mixer switch. The mixer groups are intended to help the developer of audio applications figure out which mixer elements are related to each other and to a particular connection (e.g. PCM OUT).

In this sample mixer, none of the individual input groups (PCM IN, MIC IN, CD IN) has volume or mute controls. They're still required because they contain the capture selection switch, but the only volume and mute controls on the input side are in the Input Gain group. This is important to note because it points out that you don't need to completely fill the requirements to specify a group. If you're missing a mixer element in your hardware, you can specify NULL for the missing element, if it makes sense to group them that way.