/*
 * Jamie Guinan, December 1998.
 *
 * Interactive sound parameter mucker.
 *
 */
#include <linux/soundcard.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

static struct
{
  const char *label;
  int value;
  int supported;
  int mask;
  int write_ioctl;
  int read_ioctl;
} 
sound_params[] = 
{
  "volume", 0, 0, SOUND_MASK_VOLUME, SOUND_MIXER_WRITE_VOLUME, SOUND_MIXER_READ_VOLUME,
  "bass", 0, 0, SOUND_MASK_BASS, SOUND_MIXER_WRITE_BASS, SOUND_MIXER_READ_BASS,
  "treble", 0, 0, SOUND_MASK_TREBLE, SOUND_MIXER_WRITE_TREBLE, SOUND_MIXER_READ_TREBLE,
  "synth", 0, 0, SOUND_MASK_SYNTH, SOUND_MIXER_WRITE_SYNTH, SOUND_MIXER_READ_SYNTH,
  "pcm", 0, 0, SOUND_MASK_PCM, SOUND_MIXER_WRITE_PCM, SOUND_MIXER_READ_PCM,
  "speaker", 0, 0, SOUND_MASK_SPEAKER, SOUND_MIXER_WRITE_SPEAKER, SOUND_MIXER_READ_SPEAKER,
  "line", 0, 0, SOUND_MASK_LINE, SOUND_MIXER_WRITE_LINE, SOUND_MIXER_READ_LINE,
  "mic", 0, 0, SOUND_MASK_MIC, SOUND_MIXER_WRITE_MIC, SOUND_MIXER_READ_MIC,
  "cd", 0, 0, SOUND_MASK_CD, SOUND_MIXER_WRITE_CD, SOUND_MIXER_READ_CD,
  "imix", 0, 0, SOUND_MASK_IMIX, SOUND_MIXER_WRITE_IMIX, SOUND_MIXER_READ_IMIX,
  "altpcm", 0, 0, SOUND_MASK_ALTPCM, SOUND_MIXER_WRITE_ALTPCM, SOUND_MIXER_READ_ALTPCM,
  "reclev", 0, 0, SOUND_MASK_RECLEV, SOUND_MIXER_WRITE_RECLEV, SOUND_MIXER_READ_RECLEV,
  "igain", 0, 0, SOUND_MASK_IGAIN, SOUND_MIXER_WRITE_IGAIN, SOUND_MIXER_READ_IGAIN,
  "ogain", 0, 0, SOUND_MASK_OGAIN, SOUND_MIXER_WRITE_OGAIN, SOUND_MIXER_READ_OGAIN,
  "line1", 0, 0, SOUND_MASK_LINE1, SOUND_MIXER_WRITE_LINE1, SOUND_MIXER_READ_LINE1,
  NULL, 0, 0, 0 ,0
};

static void get_supported()
{
  int index;
  int mixer_fd;
  int value;

  mixer_fd = open("/dev/mixer", O_RDWR);
  if (mixer_fd == -1)
    {
      printf("failed to open /dev/mixer\n");
      return;
    }

  ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &value);
  close(mixer_fd);

  index = 0;
  while (sound_params[index].label != NULL)
    {
      if (value & sound_params[index].mask)
	{
	  sound_params[index].supported = 1;
	}
      index++;
    }
}

static void get_params()
{
  int index;
  int mixer_fd;

  mixer_fd = open("/dev/mixer", O_RDWR);
  if (mixer_fd == -1)
    {
      printf("failed to open /dev/mixer\n");
      return;
    }

  index = 0;
  while (sound_params[index].label != NULL)
    {
      ioctl(mixer_fd, sound_params[index].read_ioctl, &sound_params[index].value);
      index++;
    }
  
  close(mixer_fd);
}

static void show_params()
{
  int index;

  index = 0;
  while (sound_params[index].label != NULL)
    {
      printf("%8s: left=%03d right=%03d %s\n", 
	     sound_params[index].label,
	     sound_params[index].value & 0x7F, 
	     (sound_params[index].value >> 8) & 0x7F,
	     (sound_params[index].supported ? "":"(not supported)"));
      index++;
    }
  putchar('\n');
}

static int set_param(const char *label, int left, int right)
{
  int index;
  int done;
  int mixer_fd;
  int value;

  value = (left & 0x7f) + ((right & 0x7F) << 8);
  index = 0;
  done = 0;

  while (!done && (sound_params[index].label != NULL))
    {
      if (strncmp(sound_params[index].label, label, strlen(label)) == 0)
	{
	  mixer_fd = open("/dev/mixer", O_RDWR);
	  if (mixer_fd == -1)
	    {
	      printf("failed to open /dev/mixer\n");
	      return -1;
	    }
	  ioctl(mixer_fd, sound_params[index].write_ioctl, &value);
	  close(mixer_fd);
	  done=1;
	}
      index++;
    }

  if (!done)
    {
      return -1;
    }

  return 0;
}

static void main_usage()
{
  printf("\nPlease type:  label left right\n");
  printf("Example:      volume 80 80\n");
  printf("Matching suffixes work, such as \"v\" for volume or \"r\" for reclev\n");
  printf("cntrl-d (EOF) quits\n\n");
}

int main()
{
  char buff[80];
  int l, r;
  char label[80];

#define PROMPT "(ctrl-d:quit  ?:help  return:show settings)\njmix> "
  get_supported();
  get_params();
  show_params();

  fputs(PROMPT, stdout);
  while (fgets(buff, sizeof(buff), stdin))
    {
      if (buff[0] == '\r' || buff[0] == '\n')
	{
	  show_params();
	}
      else if (sscanf(buff, "%s %d %d", label, &l, &r) != 3)
	{
	  main_usage();
	}
      else
	{
	  if (set_param(label, l, r) == 0)
	    {
	      get_params();
	      show_params();
	    }
	  else
	    {
	      main_usage();
	    }
	}
      fputs(PROMPT, stdout);
    }
  
  putchar('\n');
  return 0;
}

