#include "u.h"#include "lib.h"#include "dat.h"#include "fns.h"#include "error.h"#include "devaudio.h" enum{ Qdir = 0, Qaudio, Qvolume, Aclosed = 0, Aread, Awrite, Speed = 44100, Ncmd = 50, /* max volume command words */}; Dirtabaudiodir[] ={ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "audio", {Qaudio}, 0, 0666, "volume", {Qvolume}, 0, 0666,}; static struct{ QLock lk; Rendez vous; int amode; /* Aclosed/Aread/Awrite for /audio */} audio; #define aqlock(a) qlock(&(a)->lk)#define aqunlock(a) qunlock(&(a)->lk) static struct{ char* name; int flag; int ilval; /* initial values */ int irval;} volumes[] ={ "audio", Fout, 50, 50, "synth", Fin|Fout, 0, 0, "cd", Fin|Fout, 0, 0, "line", Fin|Fout, 0, 0, "mic", Fin|Fout|Fmono, 0, 0, "speaker", Fout|Fmono, 0, 0, "treb", Fout, 50, 50, "bass", Fout, 50, 50, "speed", Fin|Fout|Fmono, Speed, Speed, 0}; static char Emode[] = "illegal open mode";static char Evolume[] = "illegal volume specifier"; static voidresetlevel(void){ int i; for(i=0; volumes[i].name; i++) audiodevsetvol(i, volumes[i].ilval, volumes[i].irval);} static voidaudioinit(void){} static Chan*audioattach(char *param){ return devattach('A', param);} static Walkqid*audiowalk(Chan *c, Chan *nc, char **name, int nname){ return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);} static intaudiostat(Chan *c, uchar *db, int n){ return devstat(c, db, n, audiodir, nelem(audiodir), devgen);} static Chan*audioopen(Chan *c, int omode){ int amode; switch((ulong)c->qid.path) { default: error(Eperm); break; case Qvolume: case Qdir: break; case Qaudio: amode = Awrite; if((omode&7) == OREAD) amode = Aread; aqlock(&audio); if(waserror()){ aqunlock(&audio); nexterror(); } if(audio.amode != Aclosed) error(Einuse); audiodevopen(); audio.amode = amode; poperror(); aqunlock(&audio); break; } c = devopen(c, omode, audiodir, nelem(audiodir), devgen); c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c;} static voidaudioclose(Chan *c){ switch((ulong)c->qid.path) { default: error(Eperm); break; case Qdir: case Qvolume: break; case Qaudio: if(c->flag & COPEN) { aqlock(&audio); audiodevclose(); audio.amode = Aclosed; aqunlock(&audio); } break; }} static longaudioread(Chan *c, void *v, long n, vlong off){ int liv, riv, lov, rov; long m; char buf[300]; int j; ulong offset = off; char *a; a = v; switch((ulong)c->qid.path) { default: error(Eperm); break; case Qdir: return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); case Qaudio: if(audio.amode != Aread) error(Emode); aqlock(&audio); if(waserror()){ aqunlock(&audio); nexterror(); } n = audiodevread(v, n); poperror(); aqunlock(&audio); break; case Qvolume: j = 0; buf[0] = 0; for(m=0; volumes[m].name; m++){ audiodevgetvol(m, &lov, &rov); liv = lov; riv = rov; j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); if((volumes[m].flag & Fmono) || (liv==riv && lov==rov)){ if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) j += snprint(buf+j, sizeof(buf)-j, " %d", liv); else{ if(volumes[m].flag & Fin) j += snprint(buf+j, sizeof(buf)-j, " in %d", liv); if(volumes[m].flag & Fout) j += snprint(buf+j, sizeof(buf)-j, " out %d", lov); } }else{ if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov) j += snprint(buf+j, sizeof(buf)-j, " left %d right %d", liv, riv); else{ if(volumes[m].flag & Fin) j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d", liv, riv); if(volumes[m].flag & Fout) j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d", lov, rov); } } j += snprint(buf+j, sizeof(buf)-j, "\n"); } return readstr(offset, a, n, buf); } return n;} static longaudiowrite(Chan *c, void *vp, long n, vlong off){ long m; int i, v, left, right, in, out; Cmdbuf *cb; USED(off); switch((ulong)c->qid.path) { default: error(Eperm); break; case Qvolume: v = Vaudio; left = 1; right = 1; in = 1; out = 1; cb = parsecmd(vp, n); if(waserror()){ free(cb); nexterror(); } for(i = 0; i < cb->nf; i++){ /* * a number is volume */ if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') { m = strtoul(cb->f[i], 0, 10); USED(in); if(!out) goto cont0; if(left && right) audiodevsetvol(v, m, m); else if(left) audiodevsetvol(v, m, -1); else if(right) audiodevsetvol(v, -1, m); goto cont0; } for(m=0; volumes[m].name; m++) { if(strcmp(cb->f[i], volumes[m].name) == 0) { v = m; in = 1; out = 1; left = 1; right = 1; goto cont0; } } if(strcmp(cb->f[i], "reset") == 0) { resetlevel(); goto cont0; } if(strcmp(cb->f[i], "in") == 0) { in = 1; out = 0; goto cont0; } if(strcmp(cb->f[i], "out") == 0) { in = 0; out = 1; goto cont0; } if(strcmp(cb->f[i], "left") == 0) { left = 1; right = 0; goto cont0; } if(strcmp(cb->f[i], "right") == 0) { left = 0; right = 1; goto cont0; } error(Evolume); break; cont0:; } free(cb); poperror(); break; case Qaudio: if(audio.amode != Awrite) error(Emode); aqlock(&audio); if(waserror()){ aqunlock(&audio); nexterror(); } n = audiodevwrite(vp, n); poperror(); aqunlock(&audio); break; } return n;} voidaudioswab(uchar *a, uint n){ ulong *p, *ep, b; p = (ulong*)a; ep = p + (n>>2); while(p < ep) { b = *p; b = (b>>24) | (b<<24) | ((b&0xff0000) >> 8) | ((b&0x00ff00) << 8); *p++ = b; }} Dev audiodevtab = { 'A', "audio", devreset, audioinit, devshutdown, audioattach, audiowalk, audiostat, audioopen, devcreate, audioclose, audioread, devbread, audiowrite, devbwrite, devremove, devwstat,};
#include "u.h"
#include "lib.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
#include "devaudio.h"
enum
{
Qdir = 0,
Qaudio,
Qvolume,
Aclosed = 0,
Aread,
Awrite,
Speed = 44100,
Ncmd = 50, /* max volume command words */
};
Dirtab
audiodir[] =
{
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
"audio", {Qaudio}, 0, 0666,
"volume", {Qvolume}, 0, 0666,
};
static struct
{
QLock lk;
Rendez vous;
int amode; /* Aclosed/Aread/Awrite for /audio */
} audio;
#define aqlock(a) qlock(&(a)->lk)
#define aqunlock(a) qunlock(&(a)->lk)
static struct
{
char* name;
int flag;
int ilval; /* initial values */
int irval;
} volumes[] =
{
"audio", Fout, 50, 50,
"synth", Fin|Fout, 0, 0,
"cd", Fin|Fout, 0, 0,
"line", Fin|Fout, 0, 0,
"mic", Fin|Fout|Fmono, 0, 0,
"speaker", Fout|Fmono, 0, 0,
"treb", Fout, 50, 50,
"bass", Fout, 50, 50,
"speed", Fin|Fout|Fmono, Speed, Speed,
0
};
static char Emode[] = "illegal open mode";
static char Evolume[] = "illegal volume specifier";
static void
resetlevel(void)
{
int i;
for(i=0; volumes[i].name; i++)
audiodevsetvol(i, volumes[i].ilval, volumes[i].irval);
}
static void
audioinit(void)
{
}
static Chan*
audioattach(char *param)
{
return devattach('A', param);
}
static Walkqid*
audiowalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
}
static int
audiostat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
}
static Chan*
audioopen(Chan *c, int omode)
{
int amode;
switch((ulong)c->qid.path) {
default:
error(Eperm);
break;
case Qvolume:
case Qdir:
break;
case Qaudio:
amode = Awrite;
if((omode&7) == OREAD)
amode = Aread;
aqlock(&audio);
if(waserror()){
aqunlock(&audio);
nexterror();
}
if(audio.amode != Aclosed)
error(Einuse);
audiodevopen();
audio.amode = amode;
poperror();
aqunlock(&audio);
break;
}
c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
return c;
}
static void
audioclose(Chan *c)
{
switch((ulong)c->qid.path) {
default:
error(Eperm);
break;
case Qdir:
case Qvolume:
break;
case Qaudio:
if(c->flag & COPEN) {
aqlock(&audio);
audiodevclose();
audio.amode = Aclosed;
aqunlock(&audio);
}
break;
}
}
static long
audioread(Chan *c, void *v, long n, vlong off)
{
int liv, riv, lov, rov;
long m;
char buf[300];
int j;
ulong offset = off;
char *a;
a = v;
switch((ulong)c->qid.path) {
default:
error(Eperm);
break;
case Qdir:
return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
case Qaudio:
if(audio.amode != Aread)
error(Emode);
aqlock(&audio);
if(waserror()){
aqunlock(&audio);
nexterror();
}
n = audiodevread(v, n);
poperror();
aqunlock(&audio);
break;
case Qvolume:
j = 0;
buf[0] = 0;
for(m=0; volumes[m].name; m++){
audiodevgetvol(m, &lov, &rov);
liv = lov;
riv = rov;
j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
if((volumes[m].flag & Fmono) || (liv==riv && lov==rov)){
if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
else{
if(volumes[m].flag & Fin)
j += snprint(buf+j, sizeof(buf)-j,
" in %d", liv);
if(volumes[m].flag & Fout)
j += snprint(buf+j, sizeof(buf)-j,
" out %d", lov);
}
}else{
if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
liv==lov && riv==rov)
j += snprint(buf+j, sizeof(buf)-j,
" left %d right %d",
liv, riv);
else{
if(volumes[m].flag & Fin)
j += snprint(buf+j, sizeof(buf)-j,
" in left %d right %d",
liv, riv);
if(volumes[m].flag & Fout)
j += snprint(buf+j, sizeof(buf)-j,
" out left %d right %d",
lov, rov);
}
}
j += snprint(buf+j, sizeof(buf)-j, "\n");
}
return readstr(offset, a, n, buf);
}
return n;
}
static long
audiowrite(Chan *c, void *vp, long n, vlong off)
{
long m;
int i, v, left, right, in, out;
Cmdbuf *cb;
USED(off);
switch((ulong)c->qid.path) {
default:
error(Eperm);
break;
case Qvolume:
v = Vaudio;
left = 1;
right = 1;
in = 1;
out = 1;
cb = parsecmd(vp, n);
if(waserror()){
free(cb);
nexterror();
}
for(i = 0; i < cb->nf; i++){
/*
* a number is volume
*/
if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
m = strtoul(cb->f[i], 0, 10);
USED(in);
if(!out)
goto cont0;
if(left && right)
audiodevsetvol(v, m, m);
else if(left)
audiodevsetvol(v, m, -1);
else if(right)
audiodevsetvol(v, -1, m);
goto cont0;
}
for(m=0; volumes[m].name; m++) {
if(strcmp(cb->f[i], volumes[m].name) == 0) {
v = m;
in = 1;
out = 1;
left = 1;
right = 1;
goto cont0;
}
}
if(strcmp(cb->f[i], "reset") == 0) {
resetlevel();
goto cont0;
}
if(strcmp(cb->f[i], "in") == 0) {
in = 1;
out = 0;
goto cont0;
}
if(strcmp(cb->f[i], "out") == 0) {
in = 0;
out = 1;
goto cont0;
}
if(strcmp(cb->f[i], "left") == 0) {
left = 1;
right = 0;
goto cont0;
}
if(strcmp(cb->f[i], "right") == 0) {
left = 0;
right = 1;
goto cont0;
}
error(Evolume);
break;
cont0:;
}
free(cb);
poperror();
break;
case Qaudio:
if(audio.amode != Awrite)
error(Emode);
aqlock(&audio);
if(waserror()){
aqunlock(&audio);
nexterror();
}
n = audiodevwrite(vp, n);
poperror();
aqunlock(&audio);
break;
}
return n;
}
void
audioswab(uchar *a, uint n)
{
ulong *p, *ep, b;
p = (ulong*)a;
ep = p + (n>>2);
while(p < ep) {
b = *p;
b = (b>>24) | (b<<24) |
((b&0xff0000) >> 8) |
((b&0x00ff00) << 8);
*p++ = b;
}
}
Dev audiodevtab = {
'A',
"audio",
devreset,
audioinit,
devshutdown,
audioattach,
audiowalk,
audiostat,
audioopen,
devcreate,
audioclose,
audioread,
devbread,
audiowrite,
devbwrite,
devremove,
devwstat,
};