#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <linux/soundcard.h>

int mixer_fd=-1;
unsigned supported;
int source;
char **p; /* parameters */

#define ALL_OPT "-all"
#define MASTER_OPT "-master"
#define DSP_OPT "-dsp"
#define MIC_OPT "-mic"
#define FM_OPT "-fm"
#define SOURCE_OPT "-source"
#define LINE_OPT "-line"
#define SPKR_OPT "-spkr"
#define	RECLEV_OPT "-reclev"

#define NOT_CMP 0
#define GET -1
#define SET 1

#define LEFT  0x0001
#define RIGHT 0x0100
#define LEFT_MASK 0x00ff
#define RIGHT_MASK 0xff00

int all_chk;

int str_cmp(char *pattern,char *s)
{
int i,j,k,l;

    all_chk=all_chk ^ 1;
    if(!all_chk)
	if(str_cmp(ALL_OPT,s))
	    return GET;


    for(i=0;(pattern[i])&&(s[i])&&(pattern[i]==s[i]);i++);

    if(pattern[i]) return 0; /* not this one */

    if(!s[i]) return GET;

    if(!s[i+1]) return GET;

    if(s[i+1]=='(') /* we got stereo */
    {
	for(k=i+1;s[k] && (s[k]!=',');k++);

	if(!s[k])return GET;

	s[k]=0;

	for(l=k+1;s[k] && (s[k]!=')');k++);

	s[k]=0;
	j=atoi(s+i+2);
	l=atoi(s+k+1);

	return LEFT*j + RIGHT*l+SET;
    }
    else 
    {
	j=atoi(s+i+1);
	return (LEFT+RIGHT)*j+SET;
    }
}




void get_source(void)
{
    source=0;
    if (ioctl(mixer_fd,SOUND_MIXER_READ_RECSRC,&source) == -1)
        perror("Error reading mixer recording source");
  
    if (source == 0)
    {
      printf("Setting sound source to microphone.\n");
      source = 1 << SOUND_MIXER_MIC;
      if (ioctl(mixer_fd,SOUND_MIXER_WRITE_RECSRC,&source) == -1)
        perror("Mixer.get_source()");   
    }
    
    switch(source & ~(1<<SOUND_MIXER_SPEAKER))	//wms - ignore the SPEAKER bit!
    {
    //wms cheat: if both MIC bit and SPKR bit set - we are using int mic
    case 1<<SOUND_MIXER_MIC:
	if(source & (1<<SOUND_MIXER_SPEAKER))
	 printf(" dsp input: Internal microphone\n");
	else
	 printf(" dsp input: Handset Microphone\n");
         break;

//  case 1<<SOUND_MIXER_SPEAKER:
//	 printf(" dsp input: Internal microphone\n");
//	 break;

    case 1<<SOUND_MIXER_LINE:
	 printf(" dsp input: Line\n");
	 break;

    case 1<<SOUND_MIXER_LINE1:
	 printf(" dsp input: Aux1 - Analog phone\n");
	 break;
    }
}

void set_source(int id)
{

    source=1 << id;
    if(ioctl(mixer_fd,SOUND_MIXER_WRITE_RECSRC,&source)<0)
	  perror("Mixer.set_source()");
}
 

int check_source(char *s)
{
int k;

    k=str_cmp(SOURCE_OPT,s);

    switch(k)

    {
    case GET:
        get_source();
        return 1;

    case NOT_CMP:
	return 0;

    default:
//	set_source(SOUND_MIXER_LINE);
	set_source((k-1)&0x0F);		//convert from "stereo"+1 to a number
        return 1;
    }

}



void set_volume(int mixer_id,int volume)
{
    if(ioctl(mixer_fd,MIXER_WRITE(mixer_id),&volume)<0)
	perror("Mixer.set_mixer()");
}

int get_volume(int mixer_id)
{
int volume;

    if(ioctl(mixer_fd,MIXER_READ(mixer_id),&volume)<0)
	{perror("Mixer.get_volume()");return 0;} 
    return volume;
}

char *sget_volume(int mixer_id)
{
char *s;
int v;

    v=get_volume(mixer_id);
    s=NULL;
    s=calloc(100,sizeof(char));
    if(s==NULL)
    {
       perror("Mixer.sget_volume()");
       return "ERROR";
    }
    sprintf(s,"(L=%d R=%d)",(v & LEFT_MASK),(v & RIGHT_MASK)>>8);
    return s;
}
   
int check_master(char *s)
{
int k;

    if((supported & SOUND_MASK_VOLUME)==0)
	return 0;
    k=str_cmp(MASTER_OPT,s);

    switch(k)
    {
    case GET:
      printf("Master volume=%s\n",sget_volume(SOUND_MIXER_VOLUME));
      return 1;
    case NOT_CMP:
      return 0;
    default:
      set_volume(SOUND_MIXER_VOLUME,k-1);
      return 1;
    }
}

int check_mic(char *s)
{
int k;

    if((supported & SOUND_MASK_MIC)==0)
	return 0;
	
    k=str_cmp(MIC_OPT,s);
    switch(k)
    {
    case GET:
      printf("Microphone (handset) volume=%s\n",sget_volume(SOUND_MIXER_MIC));
      return 1;
    case NOT_CMP:
      return 0;
    default:
      set_volume(SOUND_MIXER_MIC,k-1);
      return 1;
    }
}

int check_reclev(char *s)
{
int k;
if((supported & SOUND_MASK_RECLEV)==0)return 0;
k=str_cmp(RECLEV_OPT,s);
switch(k)
{
case GET:
  printf("RECLEV volume=%s\n",sget_volume(SOUND_MIXER_RECLEV));
  return 1;
case NOT_CMP:
  return 0;
default:
  set_volume(SOUND_MIXER_RECLEV,k-1);
  return 1;
}
}

int check_dsp(char *s)
{
int k;
if((supported & SOUND_MASK_PCM)==0)return 0;
k=str_cmp(DSP_OPT,s);
switch(k)
{
case GET:
  printf("DSP volume=%s\n",sget_volume(SOUND_MIXER_PCM));
  return 1;
case NOT_CMP:
  return 0;
default:
  set_volume(SOUND_MIXER_PCM,k-1);
  return 1;
}
}

int check_fm(char *s)
{
int k;
if((supported & SOUND_MASK_SYNTH)==0)return 0;
k=str_cmp(FM_OPT,s);
switch(k)
{
case GET:
  printf("FM volume=%s\n",sget_volume(SOUND_MIXER_SYNTH));
  return 1;
case NOT_CMP:
  return 0;
default:
  set_volume(SOUND_MIXER_SYNTH,k-1);
  return 1;
}

}

int check_line(char *s)
{
int k;
if((supported & SOUND_MASK_LINE)==0)return 0;
k=str_cmp(LINE_OPT,s);
switch(k)
{
case GET:
  printf("LINE volume=%s\n",sget_volume(SOUND_MIXER_LINE));
  return 1;
case NOT_CMP:
  return 0;
default:
  set_volume(SOUND_MIXER_LINE,k-1);
  return 1;
}
}

int check_spkr(char *s)
{
int k;
if((supported & SOUND_MASK_SPEAKER)==0)return 0;
k=str_cmp(SPKR_OPT,s);
switch(k)
{
case GET:
  printf("SPKR (built-in mic) volume=%s\n",sget_volume(SOUND_MIXER_SPEAKER));
  return 1;
case NOT_CMP:
  return 0;
default:
  set_volume(SOUND_MIXER_SPEAKER,k-1);
  return 1;
}
}

void check_all(char *s)
{
check_master(strdup(s));
check_mic(strdup(s));
check_dsp(strdup(s));
check_fm(strdup(s));
check_line(strdup(s));
//check_spkr(strdup(s));
check_reclev(strdup(s));
check_source(strdup(s));
}

int main(int argc,char *argv[])
{
int k;

all_chk=1;

mixer_fd=open("/dev/mixer",O_RDWR);
if(mixer_fd<0){
  perror("Mixer.main()");
  exit(-1);
    }
 
 if(ioctl(mixer_fd,SOUND_MIXER_READ_DEVMASK,&supported)<0)
      supported=0xffff;   


if(argc==1){
         check_all("-all");
         close(mixer_fd);
         exit(0);
          }
for(k=1;k<argc;k++)check_all(argv[k]);
close(mixer_fd);
exit(0);
}

