/*
* enlightenment sound daemon for plan9front
*
* usage: aux/listen1 -t 'tcp!*!16001' esd
*/

#include <u.h>
#include <libc.h>

int     bigendian = 0;

void
die(char *msg)
{
       exits(msg);
}

ulong
get4(void)
{
       uchar buf[4];

       if(readn(0, buf, 4) != 4)
               die("read");
       if(bigendian)
               return buf[3] | buf[2]<<8 | buf[1]<<16 | buf[0]<<24;
       else
               return buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24;
}

char*
getname(char *buf)
{
       if(readn(0, buf, 128) != 128)
               die("read");
       return buf;
}

void
put4(ulong v)
{
       uchar buf[4];

       if(bigendian){
               buf[3] = v & 0xFF, v >>= 8;
               buf[2] = v & 0xFF, v >>= 8;
               buf[1] = v & 0xFF, v >>= 8;
               buf[0] = v & 0xFF;
       } else {
               buf[0] = v & 0xFF, v >>= 8;
               buf[1] = v & 0xFF, v >>= 8;
               buf[2] = v & 0xFF, v >>= 8;
               buf[3] = v & 0xFF;
       }
       if(write(1, buf, 4) != 4)
               die("write");
}

char*
pcmfmt(ulong fmt, ulong rate)
{
       static char buf[32];

       snprint(buf, sizeof(buf), "s%dc%dr%lud",
               (fmt & 0x000F) == 0x01 ? 16 : 8,
               (fmt & 0x00F0) == 0x20 ? 2 : 1,
               rate);
       return buf;
}

void
main(void)
{
       ulong op, id, len, fmt, rate;
       char buf[256];
       int i, fd;

Init:
       /* initial protocol */
       if(readn(0, buf, 16) != 16)     /* key */
               die("read");
       if((get4() & 0xFF) == 'E')      /* endian */
               bigendian = 1;
       put4(1);

       for(;;){
               op = get4();
               switch(op){
               case 0:         /* init */
               case 1:         /* lock */
               case 2:         /* unlock */
                       goto Init;
               case 3:         /* stream-play */
                       fmt = get4();
                       rate = get4();
                       getname(buf);
                       fd = -1;
                       /* wait 2 seconds, device might be busy */
                       for(i=0; i<2000; i+=100){
                               fd = open("/dev/audio", OWRITE);
                               if(fd >= 0)
                                       break;
                               sleep(100);
                       }
                       if(fd < 0)
                               die("open");
                       dup(fd, 1);
                       execl("/bin/audio/pcmconv", "pcmconv",
                               "-i", pcmfmt(fmt, rate), 0);
                       die("exec");
                       break;
               case 4:
               case 5:         /* stream-mon */
                       fmt = get4();
                       rate = get4();
                       getname(buf);
                       fd = open("/dev/audio", OREAD);
                       if(fd < 0)
                               die("open");
                       dup(fd, 0);
                       execl("/bin/audio/pcmconv", "pcmconv",
                               "-o", pcmfmt(fmt, rate), 0);
                       die("exec");
                       break;
               case 6:         /* sample-cache */
                       fmt = get4();   /* format */
                       rate = get4();  /* rate */
                       len = get4();   /* size */
                       getname(buf);
                       id = get4();    /* sample-id */
                       if(fork() == 0){
                               /* TODO */
                               fd = open("/dev/null", OWRITE);
                               if(fd < 0)
                                       die("open");
                               dup(fd, 1);
                               snprint(buf, sizeof(buf), "%lud", len);
                               execl("/bin/audio/pcmconv", "pcmconv",
                                       "-l", buf,
                                       "-i", pcmfmt(fmt, rate), 0);
                               die("exec");
                       }
                       waitpid();
                       put4(id);
                       break;
               case 7:         /* sample-free */
               case 8:         /* sample-play */
               case 9:         /* sample-loop */
               case 10:        /* sample-stop */
               case 11:        /* sample-kill */
                       id = get4();
                       put4(id);
                       break;
               case 12:        /* standby */
               case 13:        /* resume */
                       goto Init;
               case 14:        /* sample-getid */
                       getname(buf);
                       put4(0);/* sample-id */
                       break;
               case 15:        /* stream-filter */
                       fmt = get4();
                       rate = get4();
                       USED(fmt);
                       USED(rate);
                       getname(buf);
                       put4(1);
                       break;
               case 16:        /* server-info */
               case 17:        /* server-all-info */
                       put4(1);        /* version */
                       put4(44100);    /* rate */
                       put4(0x0021);   /* fmt */
                       if(op == 16)
                               break;
                       put4(0);
                       memset(buf, 0, sizeof(buf));
                       if(write(1, buf, 32) != 32)
                               die("write");
                       break;
               case 18:        /* subscribe */
               case 19:        /* unsubscribe */
                       break;
               case 20:        /* stream-pan */
               case 21:        /* sample-pan */
                       id = get4();
                       USED(id);
                       get4(); /* left */
                       get4(); /* reight */
                       put4(1);
                       break;
               case 22:        /* standby-mode */
                       get4(); /* version */
                       put4(0);/* mode */
                       put4(0);/* ok */
                       break;
               case 23:        /* latency */
                       put4(0);
               }
       }
}