/*
   NAME:     Xmix
   AUTHOR:   Ole Gunnar Westgaard
   DATE:     Jul 28 1995
   
*/

#include "otools.h"
#include "macros.h"
#include "protos.h"
#include "colors.h"

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>

#define FILENAME ".Xmix"                                    // Name of savefile

char   *dev[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES,
       *name;
int     devmask=0,
        recmask=0,
        recsrc =0,
        mixer,
        n = 0;
Color  *co1,*co2;
Slider *mix[SOUND_MIXER_NRDEVICES*2]; // Volumebar sliders
Figlet *nme[SOUND_MIXER_NRDEVICES]; // Record source buttons,NB Figlet ! Slider
Button *mno[SOUND_MIXER_NRDEVICES]; // Mono / Stereo volume slider button

void a_Error(char *error)
{
    fprintf(stderr,"%s: %s.\n",name,error);
    o_Quit();
    exit(-1);
}

void a_Warning(char *error)
{
    fprintf(stderr,"%s: %s.\n",name,error);
}

void a_MonoStereo(Button *s,int x,int y)
{
  if ( strcmp(s->message,"locked") == 0 )
    s->message = "free";
  else
    s->message = "locked";
  s->Draw();
}

void a_Save(Button *s,int x,int y)
{
  int   value;
  FILE *fout;
  if ((fout = fopen(FILENAME,"wb")) == NULL)
    a_Warning("a_Save(); could not write savefile");
  // Save important data for each device
  for (int i=0; i < SOUND_MIXER_NRDEVICES; i++) if ((1 << i) & devmask) {
    fprintf(fout,"%c",'A'+i);
    if (((1 << i) & recmask))
      ((Switch *)nme[i])->state==IN ? fprintf(fout,"S") : fprintf(fout,"R");
    else
      fprintf(fout," ");
    mno[i]->state == IN ? fprintf(fout,"H") : fprintf(fout," ");
    if (ioctl(mixer,MIXER_READ(i),&value)==-1)
      a_Error("a_Save(); Could not do MIXER_READ");
    fprintf(fout,"%4X\n",value);
  }
  fclose(fout);
}

void a_RecordSource(Switch *s,int x,int y)
{
  int i = s->flag;
  if (s->state == OUT) recsrc |= (1<<i); else recsrc &= ~(1<<i);
  if ( ioctl(mixer,SOUND_MIXER_WRITE_RECSRC,&recsrc)==-1 )
    a_Error("a_RecordSource(); Could not do SOUND_MIXER_WRITE_RECSRC");
  if ( ioctl(mixer,SOUND_MIXER_READ_RECSRC,&recsrc)==-1 )
    a_Error("a_RecordSource(); Could not do SOUND_MIXER_READ_RECSRC");
  for (i=0; i<SOUND_MIXER_NRDEVICES; i++) if ( ((1<<i)&recmask) ) {
    Switch *p = (Switch *)nme[i];
    if ((1<<i)&recsrc) p->state = OUT; else p->state = IN;
    p->Draw();
  }
}

void a_Load(Button *s,int x,int y)
{
  int   value;
  char  i;
  FILE *fin;
  char  buf[8];
  if ((fin = fopen(FILENAME,"rb")) == NULL) {
    a_Warning("a_Load(); could not read savefile");
    return;
  }
  // Load important data for each device
  while (fread(buf,8,1,fin)) {
    i = buf[0]-'A';
    if ((1 << i) & devmask) {                     // Saved device in use here ?
      if (buf[1]!=' ' && nme[i]) {     // Does saved device have a rec switch ?
	((Switch *)nme[i])->state = (buf[1]=='R' ? OUT : IN);
	a_RecordSource((Switch *)nme[i],0,0);
      }
      mno[i]->state   = (buf[2]=='H') ? IN : OUT;
      mno[i]->message = (buf[2]=='H') ? "locked" : "free";
      mno[i]->Draw();
      sscanf(&buf[3],"%X",&value);
      if ( ioctl(mixer,MIXER_WRITE(i),&value)==-1 )
	a_Error("a_Load(); Could not do WRITE MIXER");
      if ( ioctl(mixer,MIXER_READ(i),&value)==-1 )
	a_Error("a_Load(); Could not do MIXER_READ");
      mix[i*2  ]->value = (float)( value     & 0x7f)/100.0;
      mix[i*2+1]->value = (float)((value>>8) & 0x7f)/100.0;
      mix[i*2]->Draw();
      mix[i*2+1]->Draw();
    }
  }
  fclose(fin);
}

void a_Update(Slider *s,int x,int y)
{
  int value,
      i = s->flag/2; // Sound device real number
  if (mno[i]->state&IN) { // Check if this is a forced mono
    if (s->flag == (i*2))
      mix[i*2+1]->value = mix[i*2]->value;
    else
      mix[i*2]->value = mix[i*2+1]->value;
  }
  value = (int)(floor(mix[i*2]->value*(float)100)) |
          ((int)(floor(mix[i*2+1]->value*(float)100))<<8);
  if ( ioctl(mixer,MIXER_WRITE(i),&value)==-1 )
    a_Error("a_Update(); Could not do WRITE MIXER");
  if ( ioctl(mixer,MIXER_READ(i),&value)==-1 )
    a_Error("a_Update(); Could not do MIXER_READ");
  mix[i*2  ]->value = (float)( value     & 0x7f)/100.0;
  mix[i*2+1]->value = (float)((value>>8) & 0x7f)/100.0;
  mix[i*2]->Draw();
  mix[i*2+1]->Draw();
}

void a_Quit(Button *b,int x,int y)
{
  o_running=0;
}

void a_Init()
{
  int nr = 0,
      value, // Volume value
      i,
      size = ((xMax-4)/n); // Size of each device in window

  for (i=0; i<SOUND_MIXER_NRDEVICES; i++) if ((1<<i)&devmask) {
    // Get value of this volumebar
    if (ioctl(mixer,MIXER_READ(i),&value)==-1)
      a_Error("a_Init(); Could not do MIXER_READ");
    // Define volumebars in window
    mix[i*2] = new Slider(nr*size+5,25,nr*size+size/2+2,yMax-38,SINGLE,co1,
			  i*2,"",BAR,Y,6,a_Update,
			  (float)(value & 0x7f)/(float)100);
    o_draw->Add(mix[i*2]);
    mix[i*2+1]=new Slider(nr*size+size/2+4,25,(nr+1)*size-1,yMax-38,SINGLE,co1,
			  i*2+1,"",BAR,Y,6,a_Update,
			  (float)((value>>8)&0x7f)/(float)100);
    o_draw->Add(mix[i*2+1]);

    // Initialize Recording Switches
    if ((1<<i)&recmask) {
      if ((1<<i) & recsrc)
	nme[i]=new Switch(nr*size+5,yMax-33,(nr+1)*size-1,yMax-19,SINGLE,
			  &o_color,i,dev[i],FLIP,OUT,0,a_RecordSource);
      else
	nme[i]=new Switch(nr*size+5,yMax-33,(nr+1)*size-1,yMax-19,SINGLE,
			  &o_color,i,dev[i],FLIP,IN,0,a_RecordSource);
    } else
      nme[i]=new Figlet(nr*size+5,yMax-33,(nr+1)*size-1,yMax-19,SINGLE,
			&o_color,i,dev[i]);
    o_draw->Add(nme[i]);

    // Initialize free/locked buttons (stereo/mono sliders)
    mno[i] = new Button(nr*size+5,yMax-17,(nr+1)*size-1,yMax-3,SINGLE,co2,
			i,"free",HOLD,OUT,a_MonoStereo);
    o_draw->Add(mno[i]);
    nr++;
  }
}  

void a_EnterWindow(int x,int y)
{
  int value;
  float v;
  for (int i=0; i<SOUND_MIXER_NRDEVICES; i++) if ((1<<i)&devmask) {
    // Get value of this volumebar
    if (ioctl(mixer,MIXER_READ(i),&value)==-1)
      a_Error("a_EnterWindow(); Could not do MIXER_READ");
    // Set volumebars 
    v=(float)(value & 0x7f)/(float)100;
    if (v != mix[i*2]->value) { mix[i*2]->value = v; mix[i*2]->Draw(); }
    v = (float)((value>>8)&0x7f)/(float)100;
    if (v != mix[i*2+1]->value) { mix[i*2+1]->value = v; mix[i*2+1]->Draw(); }
    // Set recbuttons
    if ( ioctl(mixer,SOUND_MIXER_READ_RECSRC,&recsrc)==-1 )
      a_Error("a_EnterWindow(); Could not do SOUND_MIXER_READ_RECSRC");
    if ((1<<i)&recmask) {
      ((Switch*)nme[i])->state = ((1<<i) & recsrc) ? OUT : IN;
      nme[i]->Draw();
    }
  }
}

int main(int argc,char *argv[])
{
  char devmixer[30] = "/dev/mixer"; // Name of sound-device
  name = argv[0]; // Name of application

  // Initialize the sounddevice
  if ( (mixer=open(devmixer,O_RDWR))<0 )
    a_Error("main(); Could not open mixerdevice");
  if ( ioctl(mixer,SOUND_MIXER_READ_DEVMASK,&devmask)==-1 )
    a_Error("main(); Could not do SOUND_MIXER_READ_DEVMASK");
  if ( ioctl(mixer,SOUND_MIXER_READ_RECMASK,&recmask)==-1 )
    a_Error("main(); Could not do SOUND_MIXER_READ_RECMASK");
  if ( ioctl(mixer,SOUND_MIXER_READ_RECSRC,&recsrc)==-1 )
    a_Error("main(); Could not do SOUND_MIXER_READ_RECSRC");

  // Count number of accessible devices
  for (int i=0; i<SOUND_MIXER_NRDEVICES; i++)
    if ((1<<i)&devmask) n++;

  // Set up this application
  o_Init("Xmix","Xmix","6x10",620-n*52,0,n*52+5,150,n*52+5,150,n*52+5,150,
	 C_GRAY,argc,argv);

  co1 = new Color(C_BLUE);
  co2 = new Color(C_DGRAY);

  o_draw=new Chain(new Figlet(7,7,xMax-145,18,DOUBLE,&o_color,0,
			      "Xmix / olew@nta.no"));
  o_draw->Add(new Button(xMax-135,5,xMax-95,20,SINGLE,co2,0,
			 "Load",CLICK,OUT,a_Load) );
  o_draw->Add(new Button(xMax-90,5,xMax-50,20,SINGLE,co2,0,
			 "Save",CLICK,OUT,a_Save) );
  o_draw->Add(new Button(xMax-45,5,xMax-5,20,SINGLE,co2,0,
			 "Quit",CLICK,OUT,a_Quit));
  a_Init();
  a_Load(NULL,0,0); // Load .Xmix
  o_test = o_draw;
  o_EnterWindow = a_EnterWindow;
  o_EnterLoop();
  o_Quit();
  close(mixer);
}
