/*
 *  Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com>
 *
 *   Parts of the code is derived from the alsa-lib package that is
 *   copyrighted by Jaroslav Kysela and the other ALSA team members.
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License version 2.1 as
 *   published by the Free Software Foundation.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */
#include <stdio.h>
#include "local.h"

#define SND_VERS(a, b, c) (a<<16|b<<8|c)

typedef struct _snd_mixer
{
  int mixdev;
  int nrext;

  oss_mixerinfo info;

  snd_mixer_elem_t *elems;
} snd_mixer_t;

#define MAX_MIXERS	32
static snd_mixer_t *mixers[MAX_MIXERS];
static int nmixers = 0;

typedef struct _snd_mixer_selem_id
{
  int number;
  char name[32];
} snd_mixer_selem_id_t;

typedef struct _snd_mixer_class
{
  int dummy;
} snd_mixer_class_t;

typedef struct _snd_mixer_elem
{
  snd_mixer_t *mixer;
  int ctrl;
  oss_mixext ext;

  snd_mixer_elem_t *next;

  int low, high;
} snd_mixer_elem_t;

/**
 * \brief Opens an empty mixer
 * \param mixerp Returned mixer handle
 * \param mode Open mode
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_open (snd_mixer_t ** mixerp, int mode)
{
  snd_mixer_t *mixer;
  dbg_printf2 ("snd_mixer_open()\n");

  ALIB_INIT ();
  if (!alib_appcheck ())
    return -ENODEV;

  if ((mixer = malloc (sizeof (*mixer))) == NULL)
    return -ENOMEM;

  memset (mixer, 0, sizeof (*mixer));
  *mixerp = mixer;

  return 0;
}

/**
 * \brief Attach an HCTL to an opened mixer
 * \param mixer Mixer handle
 * \param name HCTL name (see #snd_hctl_open)
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_attach (snd_mixer_t * mixer, const char *name)
{
  int dev = 0;

  ALIB_INIT ();
  dbg_printf2 ("snd_mixer_attach(%s)\n", name);


  if (strcmp (name, "default") == 0)
    dev = 0;
  else if (name[0] == 'h' && name[1] == 'w' && name[2] == ':')
    {
      if (sscanf (name + 3, "%d", &dev) != 1)
	return -ENOENT;

      if (dev < 0 || dev >= sysinfo.nummixers)
	return -ENXIO;
    }
  else
    return -ENOENT;

  mixer->mixdev = dev;

  mixer->info.dev = dev;
  if (ioctl (mixer_fd, SNDCTL_MIXERINFO, &mixer->info) == -1)
    return -errno;

  mixer->nrext = mixer->info.nrext;

  if (nmixers < MAX_MIXERS)
    mixers[nmixers++] = mixer;

  return 0;
}


/**
 * \brief Close a mixer and free all related resources
 * \param mixer Mixer handle
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_close (snd_mixer_t * mixer)
{
  dbg_printf2 ("snd_mixer_close()\n");

  free (mixer);
  return 0;
}

/**
 * \brief Register mixer simple element class
 * \param mixer Mixer handle
 * \param options Options container (not used now)
 * \param classp Pointer to returned mixer simple element class handle (or NULL) * \return 0 on success otherwise a negative error code
 */
#if 1
int
snd_mixer_selem_register (snd_mixer_t * mixer,
			  struct snd_mixer_selem_regopt *arg,
			  snd_mixer_class_t ** classp)
#else
int
snd_mixer_selem_register (snd_mixer_t * mixer, void *arg,
			  snd_mixer_class_t ** classp)
#endif
{
  snd_mixer_class_t *class;

  dbg_printf2 ("snd_mixer_selem_register()\n");

  if (classp == NULL)		/* Why this call was ever made ? */
    return 0;

  if ((class = malloc (sizeof (*class))) == NULL)
    return -ENOMEM;

  memset (class, 0, sizeof (*class));
  *classp = class;

  return 0;
}

/**
 * \brief Return info about playback volume control of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if no control is present, 1 if it's present
 */
int
snd_mixer_selem_has_playback_volume (snd_mixer_elem_t * elem)
{
  dbg_printf2 ("snd_mixer_selem_has_playback_volume(%x)\n", elem);
  fflush (stdout);

  if (elem->ext.type == MIXT_STEREOSLIDER)
    return 1;
  if (elem->ext.type == MIXT_MONOSLIDER)
    return 1;
  if (elem->ext.type == MIXT_SLIDER)
    return 1;

  return 0;
}

/**
 * \brief Return info about playback volume control of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if control is separated per channel, 1 if control acts on all channels together
 */
int
snd_mixer_selem_has_playback_volume_joined (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_has_playback_volume_joined()\n");

  return elem->ext.type != MIXT_STEREOSLIDER;
}

/**
 * \brief Return info about capture volume control of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if no control is present, 1 if it's present
 */
int
snd_mixer_selem_has_capture_volume (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_has_capture_volume()\n");

  return 0;
}


/**
 * \brief Return info about playback switch control existence of a mixer simple
element
 * \param elem Mixer simple element handle
 * \return 0 if no control is present, 1 if it's present
 */
int
snd_mixer_selem_has_playback_switch (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_has_playback_switch()\n");

  if (elem->ext.type == MIXT_ONOFF)
    return 1;
  return 0;
}

/**
 * \brief Set value of playback volume control of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param value control value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_set_playback_volume (snd_mixer_elem_t * elem,
				     snd_mixer_selem_channel_id_t channel,
				     long value)
{
  int vol, range;
  oss_mixer_value rec;

  dbg_printf2 ("snd_mixer_selem_set_playback_volume(%ld)\n", value);

  if (value < elem->low)
    value = elem->low;
  if (value > elem->high)
    value = elem->high;

  range = elem->high - elem->low;
  if (range == 0)
    range = 100;
  value -= elem->low;
  vol = (value * elem->ext.maxvalue) / range;

  rec.dev = elem->ext.dev;
  rec.ctrl = elem->ext.ctrl;
  rec.ctrl = elem->ext.ctrl;
  rec.timestamp = elem->ext.timestamp;
  rec.value = vol | (vol << 8);

  if (ioctl (mixer_fd, SNDCTL_MIX_WRITE, &rec) == -1)
    return -errno;

  return 0;
}

/**
 * \brief Set range for capture volume of a mixer simple element
 * \param elem Mixer simple element handle
 * \param min minimum volume value
 * \param max maximum volume value
 */
#if SND_LIB_VERSION > SND_VERS(1,0,9)
int
#else
void
#endif
snd_mixer_selem_set_capture_volume_range (snd_mixer_elem_t * elem,
					  long min, long max)
{
  dbg_printf ("snd_mixer_selem_set_capture_volume_range()\n");

#if SND_LIB_VERSION > SND_VERS(1,0,9)
  return 0;
#endif
}

/**
 * \brief Set value of capture volume control of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param value control value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_set_capture_volume (snd_mixer_elem_t * elem,
				    snd_mixer_selem_channel_id_t channel,
				    long value)
{
  dbg_printf ("snd_mixer_selem_set_capture_volume()\n");

  return 0;
}

/**
 * \brief Set range for playback volume of a mixer simple element
 * \param elem Mixer simple element handle
 * \param min minimum volume value
 * \param max maximum volume value
 */
#if SND_LIB_VERSION > SND_VERS(1,0,9)
int
#else
void
#endif
snd_mixer_selem_set_playback_volume_range (snd_mixer_elem_t * elem,
					   long min, long max)
{
  dbg_printf2 ("snd_mixer_selem_set_playback_volume_range(%s, %d, %d)\n",
	       elem->ext.extname, min, max);

  elem->low = min;
  elem->high = max;

#if SND_LIB_VERSION > SND_VERS(1,0,9)
  return 0;
#endif
}

/**
 * \brief Return value of playback volume control of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param value pointer to returned value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_get_playback_volume (snd_mixer_elem_t * elem,
				     snd_mixer_selem_channel_id_t channel,
				     long *value)
{
  int vol, range, left, right;
  oss_mixer_value rec;

  dbg_printf2 ("snd_mixer_selem_get_playback_volume()\n");

  rec.dev = elem->ext.dev;
  rec.ctrl = elem->ext.ctrl;
  rec.timestamp = elem->ext.timestamp;

  if (ioctl (mixer_fd, SNDCTL_MIX_READ, &rec) == -1)
    return -errno;

  left = rec.value & 0xff;
  right = (rec.value >> 8) & 0xff;

  if (left > right)
    vol = left;
  else
    vol = right;
  range = elem->high - elem->low;

  vol = (vol * range) / elem->ext.maxvalue;
  vol += elem->low;
  *value = vol;

  return 0;
}

/**
 * \brief Get range for playback volume of a mixer simple element
 * \param elem Mixer simple element handle
 * \param min Pointer to returned minimum
 * \param max Pointer to returned maximum
 */
#if SND_LIB_VERSION > SND_VERS(1,0,9)
int
#else
void
#endif
snd_mixer_selem_get_playback_volume_range (snd_mixer_elem_t * elem,
					   long *min, long *max)
{
  dbg_printf2 ("snd_mixer_selem_get_playback_volume_range()\n");
#if 1
  *min = elem->low;
  *max = elem->high;
#endif

#if SND_LIB_VERSION > SND_VERS(1,0,9)
  return 0;
#endif
}

/**
 * \brief get first element for a mixer
 * \param mixer Mixer handle
 * \return pointer to first element
 */
snd_mixer_elem_t *
snd_mixer_first_elem (snd_mixer_t * mixer)
{
  dbg_printf2 ("snd_mixer_first_elem()\n");

  return mixer->elems;
}

/**
 * \brief get next mixer element
 * \param elem mixer element
 * \return pointer to next element
 */
snd_mixer_elem_t *
snd_mixer_elem_next (snd_mixer_elem_t * elem)
{
  dbg_printf2 ("snd_mixer_elem_next(%x/%d)\n", elem, elem);

  if (elem == NULL || (long) elem < 4096)
    {
      dbg_printf2 ("Returning NULL\n");
      return NULL;
    }

  if (elem->next == NULL)
    dbg_printf ("No more elemsnts\n");
  else
    dbg_printf2 ("Returning %d/%s\n", elem->next->ctrl,
		 elem->next->ext.extname);
  return elem->next;
}

/**
 * \brief Find a mixer simple element
 * \param mixer Mixer handle
 * \param id Mixer simple element identifier
 * \return mixer simple element handle or NULL if not found
 *
 * Wuld somebody kindly explain me what in hell is the logic
 * behind this idiotic "simple" mixer stuff.
 */
snd_mixer_elem_t *
snd_mixer_find_selem (snd_mixer_t * mixer, const snd_mixer_selem_id_t * id)
{
  int i;

  dbg_printf2 ("snd_mixer_find_selem(%d/%s)\n", id->number, id->name);

  if (*id->name == 0 && id->number == 0)
    return NULL;

  for (i = 0; i < mixer->nrext; i++)
    {
      oss_mixext *ext = &mixer->elems[i].ext;

      if (ext->type == MIXT_GROUP ||
	  ext->type == MIXT_DEVROOT || ext->type == MIXT_MARKER)
	continue;

      if (strcasecmp (ext->extname, id->name) == 0)
	{
	  return &mixer->elems[i];
	}

      if (ext->ctrl == id->number)
	{
	  return &mixer->elems[i];
	}
    }

  return NULL;
}

/**
 * \brief Handle pending mixer events invoking callbacks
 * \param mixer Mixer handle
 * \return 0 otherwise a negative error code on failure
 */
int
snd_mixer_handle_events (snd_mixer_t * mixer)
{
  dbg_printf2 ("snd_mixer_handle_events()\n");
  // NOP
}

/**
 * \brief Load a mixer elements
 * \param mixer Mixer handle
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_load (snd_mixer_t * mixer)
{
  snd_mixer_elem_t *elems;
  int i, n = 0;

  dbg_printf2 ("snd_mixer_load()\n");

  if (mixer->elems != NULL)
    free (mixer->elems);

  if ((elems = malloc (sizeof (*elems) * mixer->nrext)) == NULL)
    return -ENOMEM;

  memset (elems, 0, sizeof (*elems) * mixer->nrext);

  for (i = 0; i < mixer->nrext; i++)
    {
      oss_mixext *ext = &elems[n].ext;
      snd_mixer_elem_t *elem;

      elem = &elems[n];

      ext->dev = mixer->mixdev;
      ext->ctrl = i;

      if (ioctl (mixer_fd, SNDCTL_MIX_EXTINFO, ext) < 0)
	{
	  int e = errno;
	  perror ("SNDCTL_MIX_EXTINFO");
	  return -e;
	}

      if (ext->type == MIXT_DEVROOT)
	continue;
      if (ext->type == MIXT_GROUP)
	continue;
      if (ext->type == MIXT_MARKER)
	continue;

      elem->low = 0;
      elem->high = ext->maxvalue;
      elem->ctrl = elem->ext.ctrl;

      if (n > 0)
	elems[n - 1].next = &elems[n];

      n++;
    }

  mixer->nrext = n;
  mixer->elems = elems;
  return 0;
}

/**
 * \brief Get name part of mixer simple element identifier
 * \param elem Mixer simple element handle
 * \return name part of simple element identifier
 */
const char *
snd_mixer_selem_get_name (snd_mixer_elem_t * elem)
{
  dbg_printf2 ("snd_mixer_selem_get_name()\n");

  return elem->ext.extname;
}

/**
 * \brief Set index part of a mixer simple element identifier
 * \param obj Mixer simple element identifier
 * \param val index part
 */
void
snd_mixer_selem_id_set_index (snd_mixer_selem_id_t * obj, unsigned int val)
{
  dbg_printf2 ("snd_mixer_selem_id_set_index(%u)\n", val);

  obj->number = val;
}

/**
 * \brief Set name part of a mixer simple element identifier
 * \param obj Mixer simple element identifier
 * \param val name part
 */
void
snd_mixer_selem_id_set_name (snd_mixer_selem_id_t * obj, const char *val)
{
  dbg_printf2 ("snd_mixer_selem_id_set_name(%s)\n", val);

  strcpy (obj->name, val);
}

/**
 * \brief get size of #snd_mixer_selem_id_t
 * \return size in bytes
 */
size_t
snd_mixer_selem_id_sizeof ()
{
  return sizeof (snd_mixer_selem_id_t);
}


/**
 * \brief Get info about the active state of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if not active, 1 if active
 */
int
snd_mixer_selem_is_active (snd_mixer_elem_t * elem)
{
  dbg_printf2 ("snd_mixer_selem_is_active()\n");
  return 1;
}

/**
 * \brief Return value of capture switch control of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param value pointer to returned value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_get_capture_switch (snd_mixer_elem_t * elem,
				    snd_mixer_selem_channel_id_t channel,
				    int *value)
{
  dbg_printf ("snd_mixer_selem_get_capture_switch()\n");

  *value = 0;
  return 0;
}

/**
 * \brief Return value of playback switch control of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param value pointer to returned value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_get_playback_switch (snd_mixer_elem_t * elem,
				     snd_mixer_selem_channel_id_t channel,
				     int *value)
{
  oss_mixer_value rec;

  dbg_printf ("snd_mixer_selem_get_playback_switch()\n");

  *value = 0;

  rec.dev = elem->ext.dev;
  rec.ctrl = elem->ext.ctrl;
  rec.ctrl = elem->ext.ctrl;
  rec.timestamp = elem->ext.timestamp;

  if (ioctl (mixer_fd, SNDCTL_MIX_READ, &rec) == -1)
    return -errno;

  *value = !!rec.value;
  return 0;
}

/**
 * \brief Get info about channels of capture stream of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel Mixer simple element channel identifier
 * \return 0 if channel is not present, 1 if present
 */
int
snd_mixer_selem_has_capture_channel (snd_mixer_elem_t * elem,
				     snd_mixer_selem_channel_id_t channel)
{
  dbg_printf ("snd_mixer_selem_has_capture_channel()\n");

  return (channel < 2);
}

/**
 * \brief Return info about capture switch control existence of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if no control is present, 1 if it's present
 */
int
snd_mixer_selem_has_capture_switch (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_has_capture_switch()\n");

  return 0;
}

/**
 * \brief Return info about capture switch control of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if control is separated per channel, 1 if control acts on all channels together
 */
int
snd_mixer_selem_has_capture_switch_joined (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_has_capture_switch_joined()\n");

  return 1;
}

/**
 * \brief Get info about channels of capture stream of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if not mono, 1 if mono
 */
int
snd_mixer_selem_is_capture_mono (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_is_capture_mono()\n");
  return 0;
}

/**
 * \brief Get info about channels of playback stream of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if not mono, 1 if mono
 */
int
snd_mixer_selem_is_playback_mono (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_is_playback_mono()\n");
  return 0;
}

/**
 * \brief Set value of capture switch control of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param value control value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_set_capture_switch (snd_mixer_elem_t * elem,
				    snd_mixer_selem_channel_id_t channel,
				    int value)
{
  dbg_printf ("snd_mixer_selem_set_capture_switch()\n");

  return 0;
}

/**
 * \brief Set value of capture switch control for all channels of a mixer simple element
 * \param elem Mixer simple element handle
 * \param value control value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_set_capture_switch_all (snd_mixer_elem_t * elem, int value)
{
  dbg_printf ("snd_mixer_selem_set_capture_switch_all()\n");

  return 0;
}

/**
 * \brief Set value of playback switch control for all channels of a mixer simple element
 * \param elem Mixer simple element handle
 * \param value control value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_set_playback_switch_all (snd_mixer_elem_t * elem, int value)
{
  oss_mixer_value rec;

  dbg_printf ("snd_mixer_selem_set_playback_switch_all()\n");

  rec.dev = elem->ext.dev;
  rec.ctrl = elem->ext.ctrl;
  rec.ctrl = elem->ext.ctrl;
  rec.timestamp = elem->ext.timestamp;
  rec.value = !!value;

  if (ioctl (mixer_fd, SNDCTL_MIX_WRITE, &rec) == -1)
    return -errno;

  return 0;
}

/**
 * \brief Return value of capture volume control of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param value pointer to returned value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_get_capture_volume (snd_mixer_elem_t * elem,
				    snd_mixer_selem_channel_id_t channel,
				    long *value)
{
  dbg_printf ("snd_mixer_selem_get_capture_volume()\n");

  *value = 0;
  return 0;
}

/**
 * \brief Get mixer simple element identifier
 * \param elem Mixer simple element handle
 * \param id returned mixer simple element identifier
 */
void
snd_mixer_selem_get_id (snd_mixer_elem_t * elem, snd_mixer_selem_id_t * id)
{
  dbg_printf ("snd_mixer_selem_get_id()\n");
  // What in hell is this?

  id->number = elem->ctrl;
  strcpy (id->name, elem->ext.extname);
  dbg_printf ("ID=%d / %s\n", id->number, id->name);
}

/**
 * \brief Get name part of a mixer simple element identifier
 * \param obj Mixer simple element identifier
 * \return name part
 */
const char *
snd_mixer_selem_id_get_name (const snd_mixer_selem_id_t * obj)
{
  dbg_printf ("snd_mixer_selem_id_get_name()=%s\n", obj->name);
//return "Objname";
  return obj->name;
}

/**
 * \brief Get index part of a mixer simple element identifier
 * \param obj Mixer simple element identifier
 * \return index part
 */
unsigned int
snd_mixer_selem_id_get_index (const snd_mixer_selem_id_t * obj)
{
  dbg_printf ("snd_mixer_selem_id_get_index()\n");

  return 0;
}


/**
 * \brief Get elements count for a mixer
 * \param mixer mixer handle
 * \return elements count
 */
unsigned int
snd_mixer_get_count (const snd_mixer_t * obj)
{
  dbg_printf ("snd_mixer_get_count()\n");

  return obj->nrext;
}

/**
 * \brief get poll descriptors
 * \param mixer Mixer handle
 * \param pfds array of poll descriptors
 * \param space space in the poll descriptor array
 * \return count of filled descriptors
 */
int
snd_mixer_poll_descriptors (snd_mixer_t * mixer, struct pollfd *pfds,
			    unsigned int space)
{
  dbg_printf ("snd_mixer_poll_descriptors()\n");

  return 0;
}

/**
 * \brief get count of poll descriptors for mixer handle
 * \param mixer Mixer handle
 * \return count of poll descriptors
 */
int
snd_mixer_poll_descriptors_count (snd_mixer_t * mixer)
{
  dbg_printf ("snd_mixer_poll_descriptors_count()\n");

  return 0;
}

/**
 * \brief get returned events from poll descriptors
 * \param mixer Mixer handle
 * \param pfds array of poll descriptors
 * \param nfds count of poll descriptors
 * \param revents returned events
 * \return zero if success, otherwise a negative error code
 */
int
snd_mixer_poll_descriptors_revents (snd_mixer_t * mixer, struct pollfd *pfds,
				    unsigned int nfds,
				    unsigned short *revents)
{
  dbg_printf ("snd_mixer_poll_descriptors_revents()\n");

  return 0;
}

/**
 * \brief Set callback function for a mixer
 * \param mixer mixer handle
 * \param callback callback function
 */
void
snd_mixer_set_callback (snd_mixer_t * obj, snd_mixer_callback_t val)
{
  dbg_printf0 ("snd_mixer_set_callback()\n");

}

/**
 * \brief Get range for capture volume of a mixer simple element
 * \param elem Mixer simple element handle
 * \param min Pointer to returned minimum
 * \param max Pointer to returned maximum
 */
#if SND_LIB_VERSION > SND_VERS(1,0,9)
int
#else
void
#endif
snd_mixer_selem_get_capture_volume_range (snd_mixer_elem_t * elem,
					  long *min, long *max)
{
  dbg_printf ("snd_mixer_selem_get_capture_volume_range()\n");

  *min = 0;
  *max = 100;

#if SND_LIB_VERSION > SND_VERS(1,0,9)
  return 0;
#endif
}

/**
 * \brief Return true if mixer simple element is an enumerated control
 * \param elem Mixer simple element handle
 * \return 0 normal volume/switch control, 1 enumerated control
 */
int
snd_mixer_selem_is_enumerated (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_is_enumerated()\n");

  return 0;
}

/**
 * \brief get the current selected enumerated item for the given mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param itemp the pointer to store the index of the enumerated item
 * \return 0 if successful, otherwise a negative error code
 */
int
snd_mixer_selem_get_enum_item (snd_mixer_elem_t * elem,
			       snd_mixer_selem_channel_id_t channel,
			       unsigned int *itemp)
{
  dbg_printf ("snd_mixer_selem_get_enum_item()\n");

  return 0;
}

/**
 * \brief Return the number of enumerated items of the given mixer simple element
 * \param elem Mixer simple element handle
 * \return the number of enumerated items, otherwise a negative error code
 */
int
snd_mixer_selem_get_enum_items (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_get_enum_items()\n");
  return 0;
}

/**
 * \brief get the enumerated item string for the given mixer simple element
 * \param elem Mixer simple element handle
 * \param item the index of the enumerated item to query
 * \param maxlen the maximal length to be stored
 * \param buf the buffer to store the name string
 * \return 0 if successful, otherwise a negative error code
 */
int
snd_mixer_selem_get_enum_item_name (snd_mixer_elem_t * elem,
				    unsigned int item,
				    size_t maxlen, char *buf)
{
  dbg_printf ("snd_mixer_selem_get_enum_item_name()\n");
  strncpy (buf, "Enum", maxlen);
  buf[maxlen - 1] = 0;

  return 0;
}

/**
 * \brief Return info about capture volume control of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if control is separated per channel, 1 if control acts on all channels together
 */
int
snd_mixer_selem_has_capture_volume_joined (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_has_capture_volume_joined()\n");
  return elem->ext.type != MIXT_STEREOSLIDER;
}

/**
 * \brief Get info about channels of playback stream of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel Mixer simple element channel identifier
 * \return 0 if channel is not present, 1 if present
 */
int
snd_mixer_selem_has_playback_channel (snd_mixer_elem_t * elem,
				      snd_mixer_selem_channel_id_t channel)
{
  dbg_printf ("snd_mixer_selem_has_playback_channel(%s, %d)\n",
	      elem->ext.extname, channel);

  if (elem->ext.type == MIXT_SLIDER && channel == 0)
    return 1;
  if (elem->ext.type == MIXT_MONOSLIDER && channel == 0)
    return 1;
  if (elem->ext.type == MIXT_STEREOSLIDER && channel < 2)
    return 1;

  return 0;
}

/**
 * \brief Return info about playback switch control of a mixer simple element
 * \param elem Mixer simple element handle
 * \return 0 if control is separated per channel, 1 if control acts on all channels together
 */
int
snd_mixer_selem_has_playback_switch_joined (snd_mixer_elem_t * elem)
{
  dbg_printf ("snd_mixer_selem_has_playback_switch_joined()\n");

  return 0;
}

/**
 * \brief copy one #snd_mixer_selem_id_t to another
 * \param dst pointer to destination
 * \param src pointer to source
 */
void
snd_mixer_selem_id_copy (snd_mixer_selem_id_t * dst,
			 const snd_mixer_selem_id_t * src)
{
  dbg_printf ("snd_mixer_selem_id_copy()\n");
  memcpy (dst, src, sizeof (*dst));
}


/**
 * \brief set the current selected enumerated item for the given mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param item the enumerated item index
 * \return 0 if successful, otherwise a negative error code
 */
int
snd_mixer_selem_set_enum_item (snd_mixer_elem_t * elem,
			       snd_mixer_selem_channel_id_t channel,
			       unsigned int item)
{
  dbg_printf ("snd_mixer_selem_set_enum_item()\n");

  return 0;
}

/**
 * \brief Set value of playback switch control of a mixer simple element
 * \param elem Mixer simple element handle
 * \param channel mixer simple element channel identifier
 * \param value control value
 * \return 0 on success otherwise a negative error code
 */
int
snd_mixer_selem_set_playback_switch (snd_mixer_elem_t * elem,
				     snd_mixer_selem_channel_id_t channel,
				     int value)
{
  dbg_printf ("snd_mixer_selem_set_playback_switch()\n");

  return 0;
}

/**
 *  * \brief Return true if mixer simple element has only one volume control for both playback and capture
 *   * \param elem Mixer simple element handle
 *    * \return 0 separated control, 1 common control
 *     */
int
snd_mixer_selem_has_common_volume (snd_mixer_elem_t * elem)
{
  return 0;			// TODO
}

/**
 *  * \brief Return true if mixer simple element has only one switch control for both playback and capture
 *   * \param elem Mixer simple element handle
 *    * \return 0 separated control, 1 common control
 *     */
int
snd_mixer_selem_has_common_switch (snd_mixer_elem_t * elem)
{
  return 0;			// TODO
}
