Caution: This version of this document is no longer maintained. For the latest documentation, see http://www.qnx.com/developers/docs.

Appendix: Sample Mixer Source

/*
 *  example_mixer.c
 *  The primary interface into the sample mixer.
 *  24 Jan 2001
 *  Copyright QNX Software Systems GmbH & Co. KG. All rights reserved.
 */

#include <example.h>
#include <proto.h>

static  uint8_t
example_mixer_read (example_t * example, uint32_t reg)
{
 uint8_t val = 0;

 /* Generic code to read a mixer register and set
  * the variable "val" should go here.
  */

 return val;
}

static void
example_mixer_write (example_t * example, uint32_t reg, uint8_t val)
{
 /* Generic code to write a mixer register with the value of
  * the variable "val" should go here.
  */
}

static snd_mixer_voice_t stereo_voices[2] =
{
 {SND_MIXER_VOICE_LEFT, 0},
 {SND_MIXER_VOICE_RIGHT, 0}
};

static struct snd_mixer_element_volume1_range output_range[2] =
{
 {0, 63, -600, 1650},
 {0, 63, -600, 1650}
};

static struct snd_mixer_element_volume1_range input_range[2] =
{
 {0, 15, -2850, 300},
 {0, 15, -2850, 300}
};

static  int32_t
example_master_vol_control (MIXER_CONTEXT_T * example,
  ado_mixer_delement_t * element, uint8_t set, uint32_t * vol,
  void *instance_data)
{
 enum example_mixer_reg reg = (int32_t) instance_data;
 uint32_t data[2];
 int32_t altered = 0;
 int     max = ado_mixer_element_vol_range_max (element);

 data[0] = example_mixer_read (example, reg + 0);
 data[1] = example_mixer_read (example, reg + 2);
 if (set)
 {
  altered = vol[0] != ( data[0] & max ) || vol[1] != ( data[1] & max );
  data[0] = ( data[0] & ~(max) ) | vol[0];
  data[1] = ( data[1] & ~(max) ) | vol[0];
  example_mixer_write (example, reg + 0, data[0]);
  example_mixer_write (example, reg + 2, data[1]);
 }
 else
 {
  vol[0] = (data[0] & max);
  vol[1] = (data[1] & max);
 }

 return (altered);
}

static  int32_t
example_master_mute_control (MIXER_CONTEXT_T * example,
  ado_mixer_delement_t * element, uint8_t set, uint32_t * val,
  void *instance_data)
{
 enum example_mixer_reg reg = (int32_t) instance_data;
 uint32_t data[2];
 int32_t altered = 0;

 data[0] = example_mixer_read (example, reg + 0);
 data[1] = example_mixer_read (example, reg + 2);
 if (set)
 {
  altered = val[0] != ( ( data[0] & 0x40 ) ? ( 1 << 0) : 0 ) |
                      ( ( data[1] & 0x40 ) ? ( 1 << 1) : 0 );
  data[0] = ( data[0] & ~0x40 ) | ( ( val[0] & (1 << 0) ) ? 0x40 : 0 );
  data[1] = ( data[1] & ~0x40 ) | ( ( val[0] & (1 << 1) ) ? 0x40 : 0 );
  example_mixer_write (example, reg + 0, data[0]);
  example_mixer_write (example, reg + 2, data[1]);
 }
 else
  val[0] = ( ( data[0] & 0x40 ) ? (1 << 0) : 0 ) |
           ( ( data[1] & 0x40 ) ? (1 << 1) : 0 );

 return (altered);
}

static  int32_t
example_vol_control (MIXER_CONTEXT_T * example,
  ado_mixer_delement_t * element, uint8_t set, uint32_t * vol,
  void *instance_data)
{
 enum example_mixer_reg reg = (int32_t) instance_data;
 uint32_t data;
 int32_t altered = 0;
 int     max = ado_mixer_element_vol_range_max (element);

 data = example_mixer_read (example, reg);
 if (set)
 {
  altered = vol[0] != ( ( data & (max << 8) ) >> 8 ) || vol[1] != (data & max);
  data = (data & ~( (max << 4) | max) ) | ( (vol[0]) <<  4) | (vol[1]);
  example_mixer_write (example, reg, data);
 }
 else
 {
  vol[0] = ( (data & (max << 4) ) >> 4);
  vol[1] = ( (data & max) );
 }

 return (altered);
}

int
build_in_group( MIXER_CONTEXT_T * example, char *name_p, char *name_c,
  example_group * grp, enum example_mixer_reg reg_p,
  enum example_mixer_reg reg_c)
{
 int     error = 0;
 ado_mixer_delement_t *pre_elem, *elem = NULL;
 char    ename[sizeof (((snd_mixer_eid_t *) 0)->name)];


 if ( grp == &example->pcm )
 {
  if ( !error && (elem = ado_mixer_element_io (example->mixer, name_p,
       SND_MIXER_ETYPE_PLAYBACK1, 0, 2, stereo_voices)) == NULL )
   error++;
 }
 else
 {
  if ( !error && (elem = ado_mixer_element_io (example->mixer, name_p,
       SND_MIXER_ETYPE_INPUT, 0, 2, stereo_voices)) == NULL )
   error++;
 }
 pre_elem = elem;

 sprintf (ename, "%s %s", name_p, "Volume");

 if ( !error && (elem = ado_mixer_element_volume1 (example->mixer, ename,
      2, input_range, example_vol_control, (void *) reg_p, NULL)) == NULL )
  error++;

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

 grp->vol_out = elem;

 if ( !error && ado_mixer_element_route_add (example->mixer, elem,
      example->output_accu) != 0 )
  error++;

 if (name_c)
 {
  if ( !error && ado_mixer_element_route_add (example->mixer, pre_elem,
       example->input_accu) != 0 )
   error++;
 }

 if ( !error && name_p && (grp->group_p = ado_mixer_playback_group_create (example->mixer,
      name_p, SND_MIXER_CHN_MASK_STEREO, grp->vol_out, NULL)) == NULL )
  error++;

 if ( !error && name_c && (grp->group_c = ado_mixer_capture_group_create (example->mixer,
      name_c, SND_MIXER_CHN_MASK_STEREO, NULL, NULL, NULL, NULL)) == NULL )
  error++;

 return (0);
}

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++;


 /* ############### */
 /* the INPUT GROUP */
 /* ############### */
 if ( (example->input_accu = ado_mixer_element_accu1 (mixer,
      SND_MIXER_ELEMENT_INPUT_ACCU, 0)) == NULL )
  error++;
 pre_elem = example->input_accu;

 if ( !error && (elem = ado_mixer_element_volume1 (mixer, "Input Volume", 2,
      input_range, example_vol_control,
      (void *) EXAMPLE_RECORD_LEVEL, 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_io (mixer, SND_MIXER_ELEMENT_CAPTURE,
      SND_MIXER_ETYPE_CAPTURE1, 0, 2, stereo_voices)) == NULL )
  error++;

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

 if ( !error && (example->input_grp = ado_mixer_capture_group_create (mixer,
      SND_MIXER_GRP_IGAIN, SND_MIXER_CHN_MASK_STEREO,
      example->master_vol, NULL, NULL, NULL)) == NULL )
  error++;


 /* ################ */
 /* the INPUT GROUPS */
 /* ################ */
 if ( !error && build_in_group (example, SND_MIXER_PCM_OUT, NULL, &example->pcm,
      EXAMPLE_PCM_OUT_VOL, NULL) != 0 )
  error++;

 if ( !error && build_in_group (example, SND_MIXER_MIC_OUT, SND_MIXER_MIC_IN,
      &example->mic, EXAMPLE_MIC_OUT_VOL, EXAMPLE_MIC_IN_VOL) != 0 )
  error++;

 if ( !error && build_in_group (example, SND_MIXER_CD_OUT, SND_MIXER_CD_IN,
      &example->cd, EXAMPLE_CD_OUT_VOL, EXAMPLE_CD_IN_VOL) != 0 )
  error++;

 return (0);
}

ado_mixer_reset_t example_reset;
int
example_reset (MIXER_CONTEXT_T * example)
{
 /* This function, if included, should restore the mixer to a default state */

 example_mixer_write( example, EXAMPLE_PCM_OUT_VOL, 0xff );   /* set PCM vol 100% */
 example_mixer_write( example, EXAMPLE_CD_OUT_VOL, 0xff );   /* set cd vol 100% */
 example_mixer_write( example, EXAMPLE_REC_SEL, 0x05 );    /* set record src to mixer */
 return (0);
}

ado_mixer_destroy_t example_destroy;
int
example_destroy (MIXER_CONTEXT_T * example)
{
 /* This function, if included, should set the mixer to a safe state */

 example_mixer_write( example, EXAMPLE_PCM_OUT_VOL, 0x0 );    /* set PCM vol 0% */
 example_mixer_write( example, EXAMPLE_CD_OUT_VOL, 0x00 );    /* set cd vol 0% */
 return (0);
}

int
example_mixer (ado_card_t * card, HW_CONTEXT_T * example)
{
 int32_t status;

 if ( (status = ado_mixer_create (card, "Example", &example->mixer, example)) != EOK )
  return (status);

 example_mixer_write (example, 0x00, 0x00);       /* reset the mixer */

 if ( build_example_mixer (example, example->mixer) )
  return (-1);

 if ( example_reset (example) )
  return (-1);

 /* The following functions are optional, but if you have actions
  * that should be performed by the hardware whenever the mixer is 
  * reset or destroyed. These functions are specifically for
  * hardware specific requirements.
  */

 ado_mixer_set_reset_func( example->mixer, example_reset );

 ado_mixer_set_destroy_func( example->mixer, example_destroy );

 return (0);
}