audioif, mixer control
This commit is contained in:
parent
334c58f95e
commit
4bc74b8aef
|
@ -5,6 +5,7 @@
|
|||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "../port/error.h"
|
||||
#include "../port/audioif.h"
|
||||
|
||||
typedef struct Hwdesc Hwdesc;
|
||||
typedef struct Ctlr Ctlr;
|
||||
|
@ -65,10 +66,6 @@ struct Ctlr {
|
|||
ulong civstat[Ndesc];
|
||||
ulong lvistat[Ndesc];
|
||||
|
||||
int targetrate;
|
||||
int hardrate;
|
||||
|
||||
int attachok;
|
||||
int sis7012;
|
||||
|
||||
/* for probe */
|
||||
|
@ -145,8 +142,8 @@ enum {
|
|||
#define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w)))
|
||||
|
||||
/* audioac97mix */
|
||||
extern int ac97hardrate(Audio *, int);
|
||||
extern void ac97mixreset(Audio *, void (*wr)(Audio*,int,ushort),
|
||||
extern void ac97mixreset(Audio *,
|
||||
void (*wr)(Audio*,int,ushort),
|
||||
ushort (*rr)(Audio*,int));
|
||||
|
||||
static void
|
||||
|
@ -202,11 +199,9 @@ ac97interrupt(Ureg *, void *arg)
|
|||
adev = arg;
|
||||
ctlr = adev->ctlr;
|
||||
stat = csr32r(ctlr, Sta);
|
||||
|
||||
stat &= S2ri | Sri | Pri | Mint | Point | Piint | Moint | Miint | Gsci;
|
||||
|
||||
ilock(ctlr);
|
||||
if(stat & Point){
|
||||
ilock(ctlr);
|
||||
if(ctlr->sis7012)
|
||||
csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch);
|
||||
else
|
||||
|
@ -232,10 +227,11 @@ ac97interrupt(Ureg *, void *arg)
|
|||
if(ctlr->outavail > Bufsize/2)
|
||||
wakeup(&ctlr->outr);
|
||||
stat &= ~Point;
|
||||
iunlock(ctlr);
|
||||
}
|
||||
iunlock(ctlr);
|
||||
if(stat) /* have seen 0x400, which is sdin0 resume */
|
||||
print("#A%d: ac97 unhandled interrupt(s): stat 0x%lux\n", adev->ctlrno, stat);
|
||||
iprint("#A%d: ac97 unhandled interrupt(s): stat 0x%lux\n",
|
||||
adev->ctlrno, stat);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -272,52 +268,30 @@ ac97medianoutrate(Audio *adev)
|
|||
return ts[Nts/2] / BytesPerSample;
|
||||
}
|
||||
|
||||
static void
|
||||
ac97volume(Audio *adev, char *msg)
|
||||
{
|
||||
adev->volwrite(adev, msg, strlen(msg), 0);
|
||||
}
|
||||
|
||||
static void
|
||||
ac97attach(Audio *adev)
|
||||
{
|
||||
Ctlr *ctlr;
|
||||
ctlr = adev->ctlr;
|
||||
if(!ctlr->attachok){
|
||||
ac97hardrate(adev, ctlr->hardrate);
|
||||
ac97volume(adev, "audio 75");
|
||||
ac97volume(adev, "head 100");
|
||||
ac97volume(adev, "master 100");
|
||||
ctlr->attachok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static long
|
||||
ac97status(Audio *adev, void *a, long n, vlong off)
|
||||
ac97status(Audio *adev, void *a, long n, vlong)
|
||||
{
|
||||
char *p, *e;
|
||||
Ctlr *ctlr;
|
||||
char *buf;
|
||||
long i, l;
|
||||
int i;
|
||||
|
||||
ctlr = adev->ctlr;
|
||||
l = 0;
|
||||
buf = malloc(READSTR);
|
||||
l += snprint(buf + l, READSTR - l, "rate %d\n", ctlr->targetrate);
|
||||
l += snprint(buf + l, READSTR - l, "median rate %lud\n", ac97medianoutrate(adev));
|
||||
l += snprint(buf + l, READSTR - l, "hard rate %d\n", ac97hardrate(adev, -1));
|
||||
|
||||
l += snprint(buf + l, READSTR - l, "civ stats");
|
||||
p = a;
|
||||
e = p + n;
|
||||
|
||||
p += snprint(p, e - p, "median rate %lud\n", ac97medianoutrate(adev));
|
||||
p += snprint(p, e - p, "civ stats");
|
||||
for(i = 0; i < Ndesc; i++)
|
||||
l += snprint(buf + l, READSTR - l, " %lud", ctlr->civstat[i]);
|
||||
l += snprint(buf + l, READSTR - l, "\n");
|
||||
p += snprint(p, e - p, " %lud", ctlr->civstat[i]);
|
||||
p += snprint(p, e - p, "\n");
|
||||
|
||||
l += snprint(buf + l, READSTR - l, "lvi stats");
|
||||
p += snprint(p, e - p, "lvi stats");
|
||||
for(i = 0; i < Ndesc; i++)
|
||||
l += snprint(buf + l, READSTR - l, " %lud", ctlr->lvistat[i]);
|
||||
snprint(buf + l, READSTR - l, "\n");
|
||||
p += snprint(p, e - p, " %lud", ctlr->lvistat[i]);
|
||||
p += snprint(p, e - p, "\n");
|
||||
|
||||
n = readstr(off, a, n, buf);
|
||||
free(buf);
|
||||
return n;
|
||||
return p - (char*)a;
|
||||
}
|
||||
|
||||
static long
|
||||
|
@ -328,41 +302,6 @@ ac97buffered(Audio *adev)
|
|||
return Bufsize - Bufsize/Ndesc - ctlr->outavail;
|
||||
}
|
||||
|
||||
static long
|
||||
ac97ctl(Audio *adev, void *a, long n, vlong)
|
||||
{
|
||||
Ctlr *ctlr;
|
||||
char *tok[2], *p;
|
||||
int ntok;
|
||||
long t;
|
||||
|
||||
ctlr = adev->ctlr;
|
||||
if(n > READSTR)
|
||||
n = READSTR - 1;
|
||||
p = malloc(READSTR);
|
||||
|
||||
if(waserror()){
|
||||
free(p);
|
||||
nexterror();
|
||||
}
|
||||
memmove(p, a, n);
|
||||
p[n] = 0;
|
||||
ntok = tokenize(p, tok, nelem(tok));
|
||||
if(ntok > 1 && !strcmp(tok[0], "rate")){
|
||||
t = strtol(tok[1], 0, 10);
|
||||
if(t < 8000 || t > 48000)
|
||||
error("rate must be between 8000 and 48000");
|
||||
ctlr->targetrate = t;
|
||||
ctlr->hardrate = t;
|
||||
ac97hardrate(adev, ctlr->hardrate);
|
||||
poperror();
|
||||
free(p);
|
||||
return n;
|
||||
}
|
||||
error("invalid ctl");
|
||||
return n; /* shut up, you compiler you */
|
||||
}
|
||||
|
||||
static void
|
||||
ac97kick(Ctlr *ctlr, long reg)
|
||||
{
|
||||
|
@ -490,9 +429,6 @@ ac97reset(Audio *adev)
|
|||
|
||||
Found:
|
||||
adev->ctlr = ctlr;
|
||||
ctlr->targetrate = 44100;
|
||||
ctlr->hardrate = 44100;
|
||||
|
||||
if(p->vid == 0x1039 && p->did == 0x7012)
|
||||
ctlr->sis7012 = 1;
|
||||
|
||||
|
@ -588,13 +524,11 @@ Found:
|
|||
csr8w(ctlr, Out+Cr, Ioce); /* | Lvbie | Feie */
|
||||
csr8w(ctlr, Mic+Cr, Ioce); /* | Lvbie | Feie */
|
||||
|
||||
adev->attach = ac97attach;
|
||||
ac97mixreset(adev, ac97mixw, ac97mixr);
|
||||
|
||||
adev->write = ac97write;
|
||||
adev->status = ac97status;
|
||||
adev->ctl = ac97ctl;
|
||||
adev->buffered = ac97buffered;
|
||||
|
||||
ac97mixreset(adev, ac97mixw, ac97mixr);
|
||||
|
||||
intrenable(irq, ac97interrupt, adev, tbdf, adev->name);
|
||||
|
||||
|
|
|
@ -5,19 +5,7 @@
|
|||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "../port/error.h"
|
||||
|
||||
typedef ushort (*ac97rdfn)(Audio *, int);
|
||||
typedef void (*ac97wrfn)(Audio *, int, ushort);
|
||||
|
||||
typedef struct Mixer Mixer;
|
||||
typedef struct Volume Volume;
|
||||
|
||||
struct Mixer {
|
||||
QLock;
|
||||
ac97wrfn wr;
|
||||
ac97rdfn rr;
|
||||
int vra;
|
||||
};
|
||||
#include "../port/audioif.h"
|
||||
|
||||
enum { Maxbusywait = 500000 };
|
||||
|
||||
|
@ -33,22 +21,8 @@ enum {
|
|||
Capadc18 = 0x100,
|
||||
Capadc20 = 0x200,
|
||||
Capenh = 0xfc00,
|
||||
Master = 0x02,
|
||||
Headphone = 0x04,
|
||||
Monomaster = 0x06,
|
||||
Mastertone = 0x08,
|
||||
Pcbeep = 0x0A,
|
||||
Phone = 0x0C,
|
||||
Mic = 0x0E,
|
||||
Line = 0x10,
|
||||
Cd = 0x12,
|
||||
Video = 0x14,
|
||||
Aux = 0x16,
|
||||
Pcmout = 0x18,
|
||||
Mute = 0x8000,
|
||||
|
||||
Recsel = 0x1A,
|
||||
Recgain = 0x1C,
|
||||
Micgain = 0x1E,
|
||||
General = 0x20,
|
||||
ThreeDctl = 0x22,
|
||||
Ac97RESER = 0x24,
|
||||
|
@ -118,14 +92,6 @@ enum {
|
|||
Spdifv = 1<<15,
|
||||
VID1 = 0x7c,
|
||||
VID2 = 0x7e,
|
||||
Speed = 0x1234567,
|
||||
};
|
||||
|
||||
enum {
|
||||
Left,
|
||||
Right,
|
||||
Stereo,
|
||||
Absolute,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -143,195 +109,125 @@ enum {
|
|||
Vaux,
|
||||
Vrecgain,
|
||||
Vmicgain,
|
||||
Vspeed,
|
||||
};
|
||||
|
||||
struct Volume {
|
||||
int reg;
|
||||
int range;
|
||||
int type;
|
||||
int cap;
|
||||
char *name;
|
||||
static Volume voltab[] = {
|
||||
[Vmaster] "master", 0x02, 63, Stereo, 0,
|
||||
[Vaudio] "audio", 0x18, 31, Stereo, 0,
|
||||
[Vhead] "head", 0x04, 31, Stereo, Capheadphones,
|
||||
[Vbass] "bass", 0x08, 15, Left, Captonectl,
|
||||
[Vtreb] "treb", 0x08, 15, Right, Captonectl,
|
||||
[Vbeep] "beep", 0x0a, 31, Right, 0,
|
||||
[Vphone] "phone", 0x0c, 31, Right, 0,
|
||||
[Vmic] "mic", 0x0e, 31, Right, Capmic,
|
||||
[Vline] "line", 0x10, 31, Stereo, 0,
|
||||
[Vcd] "cd", 0x12, 31, Stereo, 0,
|
||||
[Vvideo] "video", 0x14, 31, Stereo, 0,
|
||||
[Vaux] "aux", 0x16, 63, Stereo, 0,
|
||||
[Vrecgain] "recgain", 0x1c, 15, Stereo, 0,
|
||||
[Vmicgain] "micgain", 0x1e, 15, Right, Capmic,
|
||||
[Vspeed] "speed", 0x2c, 0, Absolute, 0,
|
||||
0
|
||||
};
|
||||
|
||||
struct Topology {
|
||||
Volume *this;
|
||||
Volume *next[2];
|
||||
};
|
||||
|
||||
Volume vol[] = {
|
||||
[Vmaster] {Master, 63, Stereo, 0, "master"},
|
||||
[Vaudio] {Pcmout, 31, Stereo, 0, "audio"},
|
||||
[Vhead] {Headphone, 31, Stereo, Capheadphones, "head"},
|
||||
[Vbass] {Mastertone, 15, Left, Captonectl, "bass"},
|
||||
[Vtreb] {Mastertone, 15, Right, Captonectl, "treb"},
|
||||
[Vbeep] {Pcbeep, 31, Right, 0, "beep"},
|
||||
[Vphone] {Phone, 31, Right, 0, "phone"},
|
||||
[Vmic] {Mic, 31, Right, Capmic, "mic"},
|
||||
[Vline] {Line, 31, Stereo, 0, "line"},
|
||||
[Vcd] {Cd, 31, Stereo, 0, "cd"},
|
||||
[Vvideo] {Video, 31, Stereo, 0, "video"},
|
||||
[Vaux] {Aux, 63, Stereo, 0, "aux"},
|
||||
[Vrecgain] {Recgain, 15, Stereo, 0, "recgain"},
|
||||
[Vmicgain] {Micgain, 15, Right, Capmic, "micgain"},
|
||||
{0, 0, 0, 0, 0},
|
||||
};
|
||||
|
||||
long
|
||||
ac97mixtopology(Audio *adev, void *a, long n, vlong off)
|
||||
typedef struct Mixer Mixer;
|
||||
struct Mixer
|
||||
{
|
||||
Mixer *m;
|
||||
char *buf;
|
||||
long l;
|
||||
ulong caps;
|
||||
m = adev->mixer;
|
||||
qlock(m);
|
||||
caps = m->rr(adev, Reset);
|
||||
caps |= m->rr(adev, Extid) << 16;
|
||||
l = 0;
|
||||
buf = malloc(READSTR);
|
||||
l += snprint(buf+l, READSTR-l, "not implemented. have fun.\n");
|
||||
USED(caps);
|
||||
USED(l);
|
||||
qunlock(m);
|
||||
n = readstr(off, a, n, buf);
|
||||
free(buf);
|
||||
return n;
|
||||
}
|
||||
ushort (*rr)(Audio *, int);
|
||||
void (*wr)(Audio *, int, ushort);
|
||||
int vra;
|
||||
};
|
||||
|
||||
long
|
||||
ac97mixread(Audio *adev, void *a, long n, vlong off)
|
||||
static int
|
||||
ac97volget(Audio *adev, int x, int a[2])
|
||||
{
|
||||
Mixer *m;
|
||||
char *nam, *buf;
|
||||
long l;
|
||||
Mixer *m = adev->mixer;
|
||||
Volume *vol;
|
||||
ushort v;
|
||||
ulong caps;
|
||||
int i, rang, le, ri;
|
||||
buf = malloc(READSTR);
|
||||
m = adev->mixer;
|
||||
qlock(m);
|
||||
l = 0;
|
||||
caps = m->rr(adev, Reset);
|
||||
caps |= m->rr(adev, Extid) << 16;
|
||||
for(i = 0; vol[i].name != 0; ++i){
|
||||
if(vol[i].cap && ((vol[i].cap & caps) == 0))
|
||||
continue;
|
||||
v = m->rr(adev, vol[i].reg);
|
||||
nam = vol[i].name;
|
||||
rang = vol[i].range;
|
||||
if(vol[i].type == Absolute){
|
||||
l += snprint(buf+l, READSTR-l, "%s %d", nam, v);
|
||||
|
||||
vol = voltab+x;
|
||||
switch(vol->type){
|
||||
case Absolute:
|
||||
a[0] = m->rr(adev, vol->reg);
|
||||
break;
|
||||
default:
|
||||
v = m->rr(adev, vol->reg);
|
||||
if(v & 0x8000){
|
||||
a[0] = 0;
|
||||
a[1] = 0;
|
||||
} else {
|
||||
ri = ((rang-(v&rang)) * 100) / rang;
|
||||
le = ((rang-((v>>8)&rang)) * 100) / rang;
|
||||
if(vol[i].type == Stereo)
|
||||
l += snprint(buf+l, READSTR-l, "%s %d %d", nam, le, ri);
|
||||
if(vol[i].type == Left)
|
||||
l += snprint(buf+l, READSTR-l, "%s %d", nam, le);
|
||||
if(vol[i].type == Right)
|
||||
l += snprint(buf+l, READSTR-l, "%s %d", nam, ri);
|
||||
if(v&Mute)
|
||||
l += snprint(buf+l, READSTR-l, " mute");
|
||||
a[0] = vol->range - ((v>>8) & 0x7f);
|
||||
a[1] = vol->range - (v & 0x7f);
|
||||
}
|
||||
l += snprint(buf+l, READSTR-l, "\n");
|
||||
}
|
||||
qunlock(m);
|
||||
n = readstr(off, a, n, buf);
|
||||
free(buf);
|
||||
return n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long
|
||||
static int
|
||||
ac97volset(Audio *adev, int x, int a[2])
|
||||
{
|
||||
Mixer *m = adev->mixer;
|
||||
Volume *vol;
|
||||
ushort v, w;
|
||||
|
||||
vol = voltab+x;
|
||||
switch(vol->type){
|
||||
case Absolute:
|
||||
m->wr(adev, vol->reg, a[0]);
|
||||
break;
|
||||
case Left:
|
||||
v = (vol->range - a[0]) & 0x7f;
|
||||
w = m->rr(adev, vol->reg) & 0x7f;
|
||||
m->wr(adev, vol->reg, (v<<8)|w);
|
||||
break;
|
||||
case Right:
|
||||
v = m->rr(adev, vol->reg) & 0x7f00;
|
||||
w = (vol->range - a[1]) & 0x7f;
|
||||
m->wr(adev, vol->reg, v|w);
|
||||
break;
|
||||
case Stereo:
|
||||
v = (vol->range - a[0]) & 0x7f;
|
||||
w = (vol->range - a[1]) & 0x7f;
|
||||
m->wr(adev, vol->reg, (v<<8)|w);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static long
|
||||
ac97mixread(Audio *adev, void *a, long n, vlong)
|
||||
{
|
||||
Mixer *m = adev->mixer;
|
||||
ulong caps;
|
||||
|
||||
caps = m->rr(adev, Reset);
|
||||
caps |= m->rr(adev, Extid) << 16;
|
||||
return genaudiovolread(adev, a, n, 0, voltab, ac97volget, caps);
|
||||
}
|
||||
|
||||
static long
|
||||
ac97mixwrite(Audio *adev, void *a, long n, vlong)
|
||||
{
|
||||
Mixer *m;
|
||||
char *tok[4];
|
||||
int ntok, i, left, right, rang, reg;
|
||||
ushort v;
|
||||
m = adev->mixer;
|
||||
qlock(m);
|
||||
ntok = tokenize(a, tok, 4);
|
||||
for(i = 0; vol[i].name != 0; ++i){
|
||||
if(!strcmp(vol[i].name, tok[0])){
|
||||
rang = vol[i].range;
|
||||
reg = vol[i].reg;
|
||||
left = right = 0;
|
||||
if(ntok > 1)
|
||||
left = right = atoi(tok[1]);
|
||||
if(ntok > 2)
|
||||
right = atoi(tok[2]);
|
||||
Mixer *m = adev->mixer;
|
||||
ulong caps;
|
||||
|
||||
if(vol[i].type == Absolute){
|
||||
m->wr(adev, reg, left);
|
||||
} else {
|
||||
left = rang - ((left*rang)) / 100;
|
||||
right = rang - ((right*rang)) / 100;
|
||||
switch(vol[i].type){
|
||||
default:
|
||||
break;
|
||||
case Left:
|
||||
v = m->rr(adev, reg);
|
||||
v = (v & 0x007f) | (left << 8);
|
||||
m->wr(adev, reg, v);
|
||||
break;
|
||||
case Right:
|
||||
v = m->rr(adev, reg);
|
||||
v = (v & 0x7f00) | right;
|
||||
m->wr(adev, reg, v);
|
||||
break;
|
||||
case Stereo:
|
||||
v = (left<<8) | right;
|
||||
m->wr(adev, reg, v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
qunlock(m);
|
||||
return n;
|
||||
}
|
||||
}
|
||||
if(vol[i].name == nil){
|
||||
char *p;
|
||||
for(p = tok[0]; *p; ++p)
|
||||
if(*p < '0' || *p > '9') {
|
||||
qunlock(m);
|
||||
error("no such volume setting");
|
||||
}
|
||||
rang = vol[0].range;
|
||||
reg = vol[0].reg;
|
||||
left = right = rang - ((atoi(tok[0])*rang)) / 100;
|
||||
v = (left<<8) | right;
|
||||
m->wr(adev, reg, v);
|
||||
}
|
||||
qunlock(m);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
ac97hardrate(Audio *adev, int rate)
|
||||
{
|
||||
Mixer *m;
|
||||
int oldrate;
|
||||
m = adev->mixer;
|
||||
oldrate = m->rr(adev, Pcmfrontdacrate);
|
||||
if(rate > 0)
|
||||
m->wr(adev, Pcmfrontdacrate, rate);
|
||||
return oldrate;
|
||||
caps = m->rr(adev, Reset);
|
||||
caps |= m->rr(adev, Extid) << 16;
|
||||
return genaudiovolwrite(adev, a, n, 0, voltab, ac97volset, caps);
|
||||
}
|
||||
|
||||
void
|
||||
ac97mixreset(Audio *adev, ac97wrfn wr, ac97rdfn rr)
|
||||
ac97mixreset(Audio *adev, void (*wr)(Audio*,int,ushort), ushort (*rr)(Audio*,int))
|
||||
{
|
||||
Mixer *m;
|
||||
int i;
|
||||
ushort t;
|
||||
if(adev->mixer == nil)
|
||||
adev->mixer = malloc(sizeof(Mixer));
|
||||
m = adev->mixer;
|
||||
int i;
|
||||
|
||||
m = malloc(sizeof(Mixer));
|
||||
m->wr = wr;
|
||||
m->rr = rr;
|
||||
adev->volread = ac97mixread;
|
||||
adev->volwrite = ac97mixwrite;
|
||||
m->wr(adev, Reset, 0);
|
||||
m->wr(adev, Powerdowncsr, 0);
|
||||
|
||||
|
@ -361,4 +257,8 @@ ac97mixreset(Audio *adev, ac97wrfn wr, ac97rdfn rr)
|
|||
print("#A%d: ac97 vra extension not supported\n", adev->ctlrno);
|
||||
m->vra = 0;
|
||||
}
|
||||
|
||||
adev->mixer = m;
|
||||
adev->volread = ac97mixread;
|
||||
adev->volwrite = ac97mixwrite;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "../port/error.h"
|
||||
#include "../port/audioif.h"
|
||||
|
||||
typedef struct Ring Ring;
|
||||
typedef struct Blaster Blaster;
|
||||
|
@ -15,11 +16,8 @@ typedef struct Ctlr Ctlr;
|
|||
|
||||
enum
|
||||
{
|
||||
Fmono = 1,
|
||||
Fin = 2,
|
||||
Fout = 4,
|
||||
|
||||
Vaudio = 0,
|
||||
Vmaster,
|
||||
Vaudio,
|
||||
Vsynth,
|
||||
Vcd,
|
||||
Vline,
|
||||
|
@ -27,12 +25,11 @@ enum
|
|||
Vspeaker,
|
||||
Vtreb,
|
||||
Vbass,
|
||||
Vigain,
|
||||
Vogain,
|
||||
Vspeed,
|
||||
Nvol,
|
||||
|
||||
Speed = 44100,
|
||||
Ncmd = 50, /* max volume command words */
|
||||
|
||||
Blocksize = 4096,
|
||||
Blocks = 65536/Blocksize,
|
||||
};
|
||||
|
@ -70,44 +67,34 @@ struct Ctlr
|
|||
QLock;
|
||||
Rendez vous;
|
||||
int active; /* boolean dma running */
|
||||
int rivol[Nvol]; /* right/left input/output volumes */
|
||||
int livol[Nvol];
|
||||
int rovol[Nvol];
|
||||
int lovol[Nvol];
|
||||
int major; /* SB16 major version number (sb 4) */
|
||||
int minor; /* SB16 minor version number */
|
||||
ulong totcount; /* how many bytes processed since open */
|
||||
vlong tottime; /* time at which totcount bytes were processed */
|
||||
Ring ring; /* dma ring buffer */
|
||||
Blaster blaster;
|
||||
int lvol[Nvol];
|
||||
int rvol[Nvol];
|
||||
Audio *adev;
|
||||
};
|
||||
|
||||
static struct
|
||||
{
|
||||
char* name;
|
||||
int flag;
|
||||
int ilval; /* initial values */
|
||||
int irval;
|
||||
} volumes[] = {
|
||||
[Vaudio] "audio", Fout, 50, 50,
|
||||
[Vsynth] "synth", Fin|Fout, 0, 0,
|
||||
[Vcd] "cd", Fin|Fout, 0, 0,
|
||||
[Vline] "line", Fin|Fout, 0, 0,
|
||||
[Vmic] "mic", Fin|Fout|Fmono, 0, 0,
|
||||
[Vspeaker] "speaker", Fout|Fmono, 0, 0,
|
||||
|
||||
[Vtreb] "treb", Fout, 50, 50,
|
||||
[Vbass] "bass", Fout, 50, 50,
|
||||
|
||||
[Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed,
|
||||
0
|
||||
static Volume voltab[] = {
|
||||
[Vmaster] "master", 0x30, 0xff, Stereo, 0,
|
||||
[Vaudio] "audio", 0x32, 0xff, Stereo, 0,
|
||||
[Vsynth] "synth", 0x34, 0xff, Stereo, 0,
|
||||
[Vcd] "cd", 0x36, 0xff, Stereo, 0,
|
||||
[Vline] "line", 0x38, 0xff, Stereo, 0,
|
||||
[Vmic] "mic", 0x3a, 0xff, Mono, 0,
|
||||
[Vspeaker] "speaker", 0x3b, 0xff, Mono, 0,
|
||||
[Vtreb] "treb", 0x44, 0xff, Stereo, 0,
|
||||
[Vbass] "bass", 0x46, 0xff, Stereo, 0,
|
||||
[Vigain] "recgain", 0x3f, 0xff, Stereo, 0,
|
||||
[Vogain] "outgain", 0x41, 0xff, Stereo, 0,
|
||||
[Vspeed] "speed", 0, 0, Absolute, 0,
|
||||
0,
|
||||
};
|
||||
|
||||
static char Emajor[] = "soundblaster not responding/wrong version";
|
||||
static char Emode[] = "illegal open mode";
|
||||
static char Evolume[] = "illegal volume specifier";
|
||||
|
||||
|
||||
static long
|
||||
buffered(Ring *r)
|
||||
|
@ -242,109 +229,44 @@ mxread(Blaster *blaster, int addr)
|
|||
return s;
|
||||
}
|
||||
|
||||
static void
|
||||
mxcmds(Blaster *blaster, int s, int v)
|
||||
{
|
||||
|
||||
if(v > 100)
|
||||
v = 100;
|
||||
if(v < 0)
|
||||
v = 0;
|
||||
mxcmd(blaster, s, (v*255)/100);
|
||||
}
|
||||
|
||||
static void
|
||||
mxcmdt(Blaster *blaster, int s, int v)
|
||||
{
|
||||
|
||||
if(v > 100)
|
||||
v = 100;
|
||||
if(v <= 0)
|
||||
mxcmd(blaster, s, 0);
|
||||
else
|
||||
mxcmd(blaster, s, 255-100+v);
|
||||
}
|
||||
|
||||
static void
|
||||
mxcmdu(Blaster *blaster, int s, int v)
|
||||
{
|
||||
|
||||
if(v > 100)
|
||||
v = 100;
|
||||
if(v <= 0)
|
||||
v = 0;
|
||||
mxcmd(blaster, s, 128-50+v);
|
||||
}
|
||||
|
||||
static void
|
||||
mxvolume(Ctlr *ctlr)
|
||||
static int
|
||||
mxsetvol(Audio *adev, int x, int a[2])
|
||||
{
|
||||
Blaster *blaster;
|
||||
int *left, *right;
|
||||
int source;
|
||||
Ctlr *ctlr = adev->ctlr;
|
||||
Volume *vol;
|
||||
|
||||
if(0){
|
||||
left = ctlr->livol;
|
||||
right = ctlr->rivol;
|
||||
}else{
|
||||
left = ctlr->lovol;
|
||||
right = ctlr->rovol;
|
||||
if(x == Vspeed){
|
||||
ctlr->lvol[x] = ctlr->rvol[x] = a[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
vol = voltab+x;
|
||||
blaster = &ctlr->blaster;
|
||||
|
||||
ilock(blaster);
|
||||
|
||||
mxcmd(blaster, 0x30, 255); /* left master */
|
||||
mxcmd(blaster, 0x31, 255); /* right master */
|
||||
mxcmd(blaster, 0x3f, 0); /* left igain */
|
||||
mxcmd(blaster, 0x40, 0); /* right igain */
|
||||
mxcmd(blaster, 0x41, 0); /* left ogain */
|
||||
mxcmd(blaster, 0x42, 0); /* right ogain */
|
||||
|
||||
mxcmds(blaster, 0x32, left[Vaudio]);
|
||||
mxcmds(blaster, 0x33, right[Vaudio]);
|
||||
|
||||
mxcmds(blaster, 0x34, left[Vsynth]);
|
||||
mxcmds(blaster, 0x35, right[Vsynth]);
|
||||
|
||||
mxcmds(blaster, 0x36, left[Vcd]);
|
||||
mxcmds(blaster, 0x37, right[Vcd]);
|
||||
|
||||
mxcmds(blaster, 0x38, left[Vline]);
|
||||
mxcmds(blaster, 0x39, right[Vline]);
|
||||
|
||||
mxcmds(blaster, 0x3a, left[Vmic]);
|
||||
mxcmds(blaster, 0x3b, left[Vspeaker]);
|
||||
|
||||
mxcmdu(blaster, 0x44, left[Vtreb]);
|
||||
mxcmdu(blaster, 0x45, right[Vtreb]);
|
||||
|
||||
mxcmdu(blaster, 0x46, left[Vbass]);
|
||||
mxcmdu(blaster, 0x47, right[Vbass]);
|
||||
|
||||
source = 0;
|
||||
if(left[Vsynth])
|
||||
source |= 1<<6;
|
||||
if(right[Vsynth])
|
||||
source |= 1<<5;
|
||||
if(left[Vaudio])
|
||||
source |= 1<<4;
|
||||
if(right[Vaudio])
|
||||
source |= 1<<3;
|
||||
if(left[Vcd])
|
||||
source |= 1<<2;
|
||||
if(right[Vcd])
|
||||
source |= 1<<1;
|
||||
if(left[Vmic])
|
||||
source |= 1<<0;
|
||||
if(0)
|
||||
mxcmd(blaster, 0x3c, 0); /* output switch */
|
||||
else
|
||||
mxcmd(blaster, 0x3c, source);
|
||||
mxcmd(blaster, 0x3d, source); /* input left switch */
|
||||
mxcmd(blaster, 0x3e, source); /* input right switch */
|
||||
switch(vol->type){
|
||||
case Stereo:
|
||||
ctlr->rvol[x] = a[1];
|
||||
mxcmd(blaster, vol->reg+1, a[1]);
|
||||
/* no break */
|
||||
case Mono:
|
||||
ctlr->lvol[x] = a[0];
|
||||
mxcmd(blaster, vol->reg, a[0]);
|
||||
}
|
||||
iunlock(blaster);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mxgetvol(Audio *adev, int x, int a[2])
|
||||
{
|
||||
Ctlr *ctlr = adev->ctlr;
|
||||
|
||||
a[0] = ctlr->lvol[x];
|
||||
a[1] = ctlr->rvol[x];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -385,22 +307,20 @@ sb16startdma(Ctlr *ctlr)
|
|||
ring = &ctlr->ring;
|
||||
ilock(blaster);
|
||||
dmaend(blaster->dma);
|
||||
if(0) {
|
||||
sbcmd(blaster, 0x42); /* input sampling rate */
|
||||
speed = ctlr->livol[Vspeed];
|
||||
} else {
|
||||
sbcmd(blaster, 0x41); /* output sampling rate */
|
||||
speed = ctlr->lovol[Vspeed];
|
||||
}
|
||||
if(0)
|
||||
sbcmd(blaster, 0x42); /* input sampling rate */
|
||||
else
|
||||
sbcmd(blaster, 0x41); /* output sampling rate */
|
||||
speed = ctlr->lvol[Vspeed];
|
||||
sbcmd(blaster, speed>>8);
|
||||
sbcmd(blaster, speed);
|
||||
|
||||
if(0)
|
||||
sbcmd(blaster, 0xbe); /* A/D, autoinit */
|
||||
sbcmd(blaster, 0xbe); /* A/D, autoinit */
|
||||
else
|
||||
sbcmd(blaster, 0xb6); /* D/A, autoinit */
|
||||
sbcmd(blaster, 0xb6); /* D/A, autoinit */
|
||||
|
||||
sbcmd(blaster, 0x30); /* stereo, signed 16 bit */
|
||||
sbcmd(blaster, 0x30); /* stereo, signed 16 bit */
|
||||
|
||||
count = (Blocksize>>1) - 1;
|
||||
sbcmd(blaster, count);
|
||||
|
@ -420,7 +340,7 @@ ess1688reset(Blaster *blaster, int ctlrno)
|
|||
int i;
|
||||
|
||||
outb(blaster->reset, 3);
|
||||
delay(1); /* >3 υs */
|
||||
delay(1); /* >3 υs */
|
||||
outb(blaster->reset, 0);
|
||||
delay(1);
|
||||
|
||||
|
@ -430,7 +350,7 @@ ess1688reset(Blaster *blaster, int ctlrno)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if(sbcmd(blaster, 0xC6)){ /* extended mode */
|
||||
if(sbcmd(blaster, 0xC6)){ /* extended mode */
|
||||
print("#A%d: barf 3\n", ctlrno);
|
||||
return 1;
|
||||
}
|
||||
|
@ -457,15 +377,11 @@ ess1688startdma(Ctlr *ctlr)
|
|||
/*
|
||||
* Set the speed.
|
||||
*/
|
||||
if(0)
|
||||
speed = ctlr->livol[Vspeed];
|
||||
else
|
||||
speed = ctlr->lovol[Vspeed];
|
||||
speed = ctlr->lvol[Vspeed];
|
||||
if(speed < 4000)
|
||||
speed = 4000;
|
||||
else if(speed > 48000)
|
||||
speed = 48000;
|
||||
|
||||
if(speed > 22000)
|
||||
x = 0x80|(256-(795500+speed/2)/speed);
|
||||
else
|
||||
|
@ -477,15 +393,15 @@ ess1688startdma(Ctlr *ctlr)
|
|||
ess1688w(blaster, 0xA2, x & 0xFF);
|
||||
|
||||
if(0)
|
||||
ess1688w(blaster, 0xB8, 0x0E); /* A/D, autoinit */
|
||||
ess1688w(blaster, 0xB8, 0x0E); /* A/D, autoinit */
|
||||
else
|
||||
ess1688w(blaster, 0xB8, 0x04); /* D/A, autoinit */
|
||||
ess1688w(blaster, 0xB8, 0x04); /* D/A, autoinit */
|
||||
x = ess1688r(blaster, 0xA8) & ~0x03;
|
||||
ess1688w(blaster, 0xA8, x|0x01); /* 2 channels */
|
||||
ess1688w(blaster, 0xB9, 2); /* demand mode, 4 bytes per request */
|
||||
ess1688w(blaster, 0xA8, x|0x01); /* 2 channels */
|
||||
ess1688w(blaster, 0xB9, 2); /* demand mode, 4 bytes per request */
|
||||
|
||||
if(1)
|
||||
ess1688w(blaster, 0xB6, 0); /* for output */
|
||||
ess1688w(blaster, 0xB6, 0); /* for output */
|
||||
|
||||
ess1688w(blaster, 0xB7, 0x71);
|
||||
ess1688w(blaster, 0xB7, 0xBC);
|
||||
|
@ -496,7 +412,7 @@ ess1688startdma(Ctlr *ctlr)
|
|||
ess1688w(blaster, 0xB2, x|0x50);
|
||||
|
||||
if(1)
|
||||
sbcmd(blaster, 0xD1); /* speaker on */
|
||||
sbcmd(blaster, 0xD1); /* speaker on */
|
||||
|
||||
count = -Blocksize;
|
||||
ess1688w(blaster, 0xA4, count & 0xFF);
|
||||
|
@ -571,19 +487,6 @@ setempty(Ctlr *ctlr)
|
|||
iunlock(&ctlr->blaster);
|
||||
}
|
||||
|
||||
static void
|
||||
resetlevel(Ctlr *ctlr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; volumes[i].name; i++) {
|
||||
ctlr->lovol[i] = volumes[i].ilval;
|
||||
ctlr->rovol[i] = volumes[i].irval;
|
||||
ctlr->livol[i] = volumes[i].ilval;
|
||||
ctlr->rivol[i] = volumes[i].irval;
|
||||
}
|
||||
}
|
||||
|
||||
static long
|
||||
audiobuffered(Audio *adev)
|
||||
{
|
||||
|
@ -591,17 +494,15 @@ audiobuffered(Audio *adev)
|
|||
}
|
||||
|
||||
static long
|
||||
audiostatus(Audio *adev, void *a, long n, vlong off)
|
||||
audiostatus(Audio *adev, void *a, long n, vlong)
|
||||
{
|
||||
char buf[300];
|
||||
Ctlr *ctlr;
|
||||
Ctlr *ctlr = adev->ctlr;
|
||||
|
||||
ctlr = adev->ctlr;
|
||||
snprint(buf, sizeof(buf),
|
||||
"buffered %.4lx/%.4lx offset %10lud time %19lld\n",
|
||||
buffered(&ctlr->ring), available(&ctlr->ring),
|
||||
return snprint((char*)a, n,
|
||||
"bufsize %6lud buffered %6ld "
|
||||
"offset %10lud time %19lld\n",
|
||||
ctlr->ring.nbuf, buffered(&ctlr->ring),
|
||||
ctlr->totcount, ctlr->tottime);
|
||||
return readstr(off, a, n, buf);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -626,7 +527,6 @@ audiowrite(Audio *adev, void *vp, long n, vlong)
|
|||
uchar *p, *e;
|
||||
Ctlr *ctlr;
|
||||
Ring *ring;
|
||||
long m;
|
||||
|
||||
p = vp;
|
||||
e = p + n;
|
||||
|
@ -638,17 +538,15 @@ audiowrite(Audio *adev, void *vp, long n, vlong)
|
|||
}
|
||||
ring = &ctlr->ring;
|
||||
while(p < e) {
|
||||
if((m = writering(ring, p, e - p)) <= 0){
|
||||
if((n = writering(ring, p, e - p)) <= 0){
|
||||
if(!ctlr->active && ring->ri == 0)
|
||||
ctlr->blaster.startdma(ctlr);
|
||||
if(!ctlr->active){
|
||||
if(!ctlr->active)
|
||||
setempty(ctlr);
|
||||
continue;
|
||||
}
|
||||
sleep(&ctlr->vous, anybuf, ctlr);
|
||||
continue;
|
||||
else
|
||||
sleep(&ctlr->vous, anybuf, ctlr);
|
||||
}
|
||||
p += m;
|
||||
p += n;
|
||||
}
|
||||
poperror();
|
||||
qunlock(ctlr);
|
||||
|
@ -673,6 +571,49 @@ audioclose(Audio *adev)
|
|||
qunlock(ctlr);
|
||||
}
|
||||
|
||||
static long
|
||||
audiovolread(Audio *adev, void *a, long n, vlong)
|
||||
{
|
||||
return genaudiovolread(adev, a, n, 0, voltab, mxgetvol, 0);
|
||||
}
|
||||
|
||||
static long
|
||||
audiovolwrite(Audio *adev, void *a, long n, vlong)
|
||||
{
|
||||
Blaster *blaster;
|
||||
Ctlr *ctlr;
|
||||
int source;
|
||||
|
||||
ctlr = adev->ctlr;
|
||||
blaster = &ctlr->blaster;
|
||||
|
||||
n = genaudiovolwrite(adev, a, n, 0, voltab, mxsetvol, 0);
|
||||
|
||||
source = 0;
|
||||
if(ctlr->lvol[Vsynth])
|
||||
source |= 1<<6;
|
||||
if(ctlr->rvol[Vsynth])
|
||||
source |= 1<<5;
|
||||
if(ctlr->lvol[Vaudio])
|
||||
source |= 1<<4;
|
||||
if(ctlr->rvol[Vaudio])
|
||||
source |= 1<<3;
|
||||
if(ctlr->lvol[Vcd])
|
||||
source |= 1<<2;
|
||||
if(ctlr->rvol[Vcd])
|
||||
source |= 1<<1;
|
||||
if(ctlr->lvol[Vmic])
|
||||
source |= 1<<0;
|
||||
|
||||
ilock(blaster);
|
||||
mxcmd(blaster, 0x3c, source); /* output switch */
|
||||
mxcmd(blaster, 0x3d, source); /* input left switch */
|
||||
mxcmd(blaster, 0x3e, source); /* input right switch */
|
||||
iunlock(blaster);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
|
||||
{
|
||||
|
@ -685,7 +626,8 @@ ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
|
|||
major = sbread(blaster);
|
||||
minor = sbread(blaster);
|
||||
if(major != 0x68 || minor != 0x8B){
|
||||
print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n", ctlrno, major, minor);
|
||||
print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n",
|
||||
ctlrno, major, minor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -763,12 +705,14 @@ audioprobe(Audio *adev)
|
|||
}
|
||||
|
||||
if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){
|
||||
print("#A%d: cannot ioalloc range %lux+0x10\n", adev->ctlrno, sbconf.port);
|
||||
print("#A%d: cannot ioalloc range %lux+0x10\n",
|
||||
adev->ctlrno, sbconf.port);
|
||||
return -1;
|
||||
}
|
||||
if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){
|
||||
iofree(sbconf.port);
|
||||
print("#A%d: cannot ioalloc range %lux+0x01\n", adev->ctlrno, sbconf.port+0x100);
|
||||
print("#A%d: cannot ioalloc range %lux+0x01\n",
|
||||
adev->ctlrno, sbconf.port+0x100);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -792,8 +736,6 @@ audioprobe(Audio *adev)
|
|||
blaster->startdma = sb16startdma;
|
||||
blaster->intr = sb16intr;
|
||||
|
||||
resetlevel(ctlr);
|
||||
|
||||
outb(blaster->reset, 1);
|
||||
delay(1); /* >3 υs */
|
||||
outb(blaster->reset, 0);
|
||||
|
@ -802,6 +744,7 @@ audioprobe(Audio *adev)
|
|||
i = sbread(blaster);
|
||||
if(i != 0xaa) {
|
||||
print("#A%d: no response #%.2x\n", adev->ctlrno, i);
|
||||
Errout:
|
||||
iofree(sbconf.port);
|
||||
iofree(sbconf.port+0x100);
|
||||
free(ctlr);
|
||||
|
@ -813,12 +756,11 @@ audioprobe(Audio *adev)
|
|||
ctlr->minor = sbread(blaster);
|
||||
|
||||
if(ctlr->major != 4) {
|
||||
if(ctlr->major != 3 || ctlr->minor != 1 || ess1688(&sbconf, blaster, adev->ctlrno)){
|
||||
if(ctlr->major != 3 || ctlr->minor != 1 ||
|
||||
ess1688(&sbconf, blaster, adev->ctlrno)){
|
||||
print("#A%d: model %#.2x %#.2x; not SB 16 compatible\n",
|
||||
adev->ctlrno, ctlr->major, ctlr->minor);
|
||||
iofree(sbconf.port);
|
||||
iofree(sbconf.port+0x100);
|
||||
return -1;
|
||||
goto Errout;
|
||||
}
|
||||
ctlr->major = 4;
|
||||
}
|
||||
|
@ -827,7 +769,14 @@ audioprobe(Audio *adev)
|
|||
* initialize the mixer
|
||||
*/
|
||||
mxcmd(blaster, 0x00, 0); /* Reset mixer */
|
||||
mxvolume(ctlr);
|
||||
|
||||
for(i=0; i<Nvol; i++){
|
||||
int a[2];
|
||||
|
||||
a[0] = 0;
|
||||
a[1] = 0;
|
||||
mxsetvol(adev, i, a);
|
||||
}
|
||||
|
||||
/* set irq */
|
||||
for(i=0; i<nelem(irq); i++){
|
||||
|
@ -857,33 +806,33 @@ audioprobe(Audio *adev)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if(blaster->dma<5){
|
||||
blaster->dma = 7;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
if(blaster->dma>=5)
|
||||
break;
|
||||
|
||||
blaster->dma = 7;
|
||||
}
|
||||
|
||||
print("#A%d: %s port 0x%04lux irq %d dma %d\n", adev->ctlrno, sbconf.type,
|
||||
sbconf.port, sbconf.irq, blaster->dma);
|
||||
|
||||
ctlr->ring.nbuf = Blocks*Blocksize;
|
||||
if(dmainit(blaster->dma, ctlr->ring.nbuf)){
|
||||
free(ctlr);
|
||||
return -1;
|
||||
}
|
||||
if(dmainit(blaster->dma, ctlr->ring.nbuf))
|
||||
goto Errout;
|
||||
ctlr->ring.buf = dmabva(blaster->dma);
|
||||
|
||||
intrenable(sbconf.irq, audiointr, adev, BUSUNKNOWN, sbconf.type);
|
||||
print("#A%d: %s dma buffer %p-%p\n", adev->ctlrno, sbconf.type,
|
||||
ctlr->ring.buf, ctlr->ring.buf+ctlr->ring.nbuf);
|
||||
|
||||
setempty(ctlr);
|
||||
mxvolume(ctlr);
|
||||
|
||||
adev->write = audiowrite;
|
||||
adev->close = audioclose;
|
||||
adev->volread = audiovolread;
|
||||
adev->volwrite = audiovolwrite;
|
||||
adev->status = audiostatus;
|
||||
adev->buffered = audiobuffered;
|
||||
|
||||
intrenable(sbconf.irq, audiointr, adev, BUSUNKNOWN, sbconf.type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
50
sys/src/9/port/audioif.h
Normal file
50
sys/src/9/port/audioif.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
typedef struct Audio Audio;
|
||||
typedef struct Volume Volume;
|
||||
|
||||
struct Audio
|
||||
{
|
||||
char *name;
|
||||
|
||||
void *ctlr;
|
||||
void *mixer;
|
||||
|
||||
long (*read)(Audio *, void *, long, vlong);
|
||||
long (*write)(Audio *, void *, long, vlong);
|
||||
void (*close)(Audio *);
|
||||
|
||||
long (*volread)(Audio *, void *, long, vlong);
|
||||
long (*volwrite)(Audio *, void *, long, vlong);
|
||||
|
||||
long (*ctl)(Audio *, void *, long, vlong);
|
||||
long (*status)(Audio *, void *, long, vlong);
|
||||
long (*buffered)(Audio *);
|
||||
|
||||
int ctlrno;
|
||||
Audio *next;
|
||||
};
|
||||
|
||||
enum {
|
||||
Left,
|
||||
Right,
|
||||
Stereo,
|
||||
Absolute,
|
||||
};
|
||||
|
||||
#define Mono Left
|
||||
|
||||
struct Volume
|
||||
{
|
||||
char *name;
|
||||
int reg;
|
||||
int range;
|
||||
int type;
|
||||
int cap;
|
||||
};
|
||||
|
||||
extern void addaudiocard(char *, int (*)(Audio *));
|
||||
extern long genaudiovolread(Audio *adev, void *a, long n, vlong off,
|
||||
Volume *vol, int (*volget)(Audio *, int, int *),
|
||||
ulong caps);
|
||||
extern long genaudiovolwrite(Audio *adev, void *a, long n, vlong off,
|
||||
Volume *vol, int (*volset)(Audio *, int, int *),
|
||||
ulong caps);
|
|
@ -5,27 +5,36 @@
|
|||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "../port/error.h"
|
||||
#include "../port/audioif.h"
|
||||
|
||||
typedef struct Audioprobe Audioprobe;
|
||||
struct Audioprobe {
|
||||
typedef struct Audiochan Audiochan;
|
||||
|
||||
struct Audioprobe
|
||||
{
|
||||
char *name;
|
||||
int (*probe)(Audio*);
|
||||
};
|
||||
|
||||
struct Audiochan
|
||||
{
|
||||
QLock;
|
||||
|
||||
Chan *owner;
|
||||
Audio *adev;
|
||||
|
||||
char *data;
|
||||
char buf[1024+1];
|
||||
};
|
||||
|
||||
enum {
|
||||
Qdir = 0,
|
||||
Qaudio,
|
||||
Qaudioctl,
|
||||
Qaudiostatus,
|
||||
Qvolume,
|
||||
|
||||
Maxaudioprobes = 8,
|
||||
};
|
||||
|
||||
static int naudioprobes;
|
||||
static Audioprobe audioprobes[Maxaudioprobes];
|
||||
static Audio *audiodevs;
|
||||
|
||||
static Dirtab audiodir[] = {
|
||||
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
|
||||
"audio", {Qaudio}, 0, 0666,
|
||||
|
@ -34,10 +43,22 @@ static Dirtab audiodir[] = {
|
|||
"volume", {Qvolume}, 0, 0666,
|
||||
};
|
||||
|
||||
|
||||
static int naudioprobes;
|
||||
static Audioprobe audioprobes[16];
|
||||
static Audio *audiodevs;
|
||||
|
||||
static char Evolume[] = "illegal volume specifier";
|
||||
|
||||
|
||||
void
|
||||
addaudiocard(char *name, int (*probefn)(Audio *))
|
||||
{
|
||||
Audioprobe *probe;
|
||||
|
||||
if(naudioprobes >= nelem(audioprobes))
|
||||
return;
|
||||
|
||||
probe = &audioprobes[naudioprobes++];
|
||||
probe->name = name;
|
||||
probe->probe = probefn;
|
||||
|
@ -73,48 +94,107 @@ audioreset(void)
|
|||
*pp = nil;
|
||||
}
|
||||
|
||||
static Audiochan*
|
||||
audioclone(Chan *c, Audio *adev)
|
||||
{
|
||||
Audiochan *ac;
|
||||
|
||||
ac = malloc(sizeof(Audiochan));
|
||||
if(ac == nil){
|
||||
cclose(c);
|
||||
return nil;
|
||||
}
|
||||
|
||||
c->aux = ac;
|
||||
ac->owner = c;
|
||||
ac->adev = adev;
|
||||
ac->data = nil;
|
||||
|
||||
return ac;
|
||||
}
|
||||
|
||||
static Chan*
|
||||
audioattach(char *spec)
|
||||
{
|
||||
static int first = 1;
|
||||
Audiochan *ac;
|
||||
Audio *adev;
|
||||
Chan *c;
|
||||
Audio *p;
|
||||
int i;
|
||||
|
||||
if(spec != nil && *spec != '\0')
|
||||
i = strtol(spec, 0, 10);
|
||||
else
|
||||
i = 0;
|
||||
for(p = audiodevs; p; p = p->next)
|
||||
for(adev = audiodevs; adev; adev = adev->next)
|
||||
if(i-- == 0)
|
||||
break;
|
||||
if(p == nil)
|
||||
if(adev == nil)
|
||||
error(Enodev);
|
||||
|
||||
c = devattach('A', spec);
|
||||
c->qid.path = Qdir;
|
||||
c->aux = p;
|
||||
if(p->attach)
|
||||
p->attach(p);
|
||||
|
||||
if((ac = audioclone(c, adev)) == nil)
|
||||
error(Enomem);
|
||||
|
||||
if(first && adev->volwrite){
|
||||
first = 0;
|
||||
|
||||
strcpy(ac->buf, "speed 44100");
|
||||
if(!waserror()){
|
||||
adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
|
||||
poperror();
|
||||
}
|
||||
strcpy(ac->buf, "master 100");
|
||||
if(!waserror()){
|
||||
adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
|
||||
poperror();
|
||||
}
|
||||
strcpy(ac->buf, "audio 100");
|
||||
if(!waserror()){
|
||||
adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
|
||||
poperror();
|
||||
}
|
||||
strcpy(ac->buf, "head 100");
|
||||
if(!waserror()){
|
||||
adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
|
||||
poperror();
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static Chan*
|
||||
audioopen(Chan *c, int omode)
|
||||
{
|
||||
return devopen(c, omode, audiodir, nelem(audiodir), devgen);
|
||||
}
|
||||
|
||||
static long
|
||||
audioread(Chan *c, void *a, long n, vlong off)
|
||||
{
|
||||
Audiochan *ac;
|
||||
Audio *adev;
|
||||
long (*fn)(Audio *, void *, long, vlong);
|
||||
adev = c->aux;
|
||||
|
||||
ac = c->aux;
|
||||
adev = ac->adev;
|
||||
|
||||
fn = nil;
|
||||
switch((ulong)c->qid.path){
|
||||
default:
|
||||
error("audio bugger (rd)");
|
||||
case Qaudioctl:
|
||||
fn = adev->ctl;
|
||||
break;
|
||||
case Qdir:
|
||||
/* BUG: race */
|
||||
if(adev->buffered)
|
||||
audiodir[Qaudio].length = adev->buffered(adev);
|
||||
return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
|
||||
case Qaudio:
|
||||
fn = adev->read;
|
||||
break;
|
||||
case Qaudioctl:
|
||||
fn = adev->ctl;
|
||||
break;
|
||||
case Qaudiostatus:
|
||||
fn = adev->status;
|
||||
break;
|
||||
|
@ -123,19 +203,49 @@ audioread(Chan *c, void *a, long n, vlong off)
|
|||
break;
|
||||
}
|
||||
if(fn == nil)
|
||||
error("not implemented");
|
||||
error(Egreg);
|
||||
|
||||
switch((ulong)c->qid.path){
|
||||
case Qaudioctl:
|
||||
case Qaudiostatus:
|
||||
case Qvolume:
|
||||
qlock(ac);
|
||||
if(waserror()){
|
||||
qunlock(ac);
|
||||
nexterror();
|
||||
}
|
||||
/* generate the text on first read */
|
||||
if(ac->data == nil || off == 0){
|
||||
long l;
|
||||
|
||||
ac->data = nil;
|
||||
l = fn(adev, ac->buf, sizeof(ac->buf)-1, 0);
|
||||
if(l < 0)
|
||||
l = 0;
|
||||
ac->buf[l] = 0;
|
||||
ac->data = ac->buf;
|
||||
}
|
||||
/* then serve all requests from buffer */
|
||||
n = readstr(off, a, n, ac->data);
|
||||
qunlock(ac);
|
||||
poperror();
|
||||
return n;
|
||||
}
|
||||
return fn(adev, a, n, off);
|
||||
}
|
||||
|
||||
static long
|
||||
audiowrite(Chan *c, void *a, long n, vlong off)
|
||||
{
|
||||
Audiochan *ac;
|
||||
Audio *adev;
|
||||
long (*fn)(Audio *, void *, long, vlong);
|
||||
adev = c->aux;
|
||||
|
||||
ac = c->aux;
|
||||
adev = ac->adev;
|
||||
|
||||
fn = nil;
|
||||
switch((ulong)c->qid.path){
|
||||
default:
|
||||
error("audio bugger (wr)");
|
||||
case Qaudio:
|
||||
fn = adev->write;
|
||||
break;
|
||||
|
@ -147,46 +257,195 @@ audiowrite(Chan *c, void *a, long n, vlong off)
|
|||
break;
|
||||
}
|
||||
if(fn == nil)
|
||||
error("not implemented");
|
||||
error(Egreg);
|
||||
|
||||
switch((ulong)c->qid.path){
|
||||
case Qaudioctl:
|
||||
case Qvolume:
|
||||
if(n >= sizeof(ac->buf))
|
||||
error(Etoobig);
|
||||
|
||||
/* copy data to audiochan buffer so it can be modified */
|
||||
qlock(ac);
|
||||
if(waserror()){
|
||||
qunlock(ac);
|
||||
nexterror();
|
||||
}
|
||||
ac->data = nil;
|
||||
memmove(ac->buf, a, n);
|
||||
ac->buf[n] = 0;
|
||||
n = fn(adev, ac->buf, n, 0);
|
||||
qunlock(ac);
|
||||
poperror();
|
||||
return n;
|
||||
}
|
||||
return fn(adev, a, n, off);
|
||||
}
|
||||
|
||||
static void
|
||||
audioclose(Chan *c)
|
||||
{
|
||||
Audiochan *ac;
|
||||
Audio *adev;
|
||||
adev = c->aux;
|
||||
switch((ulong)c->qid.path){
|
||||
default:
|
||||
return;
|
||||
case Qaudio:
|
||||
if(adev->close == nil)
|
||||
return;
|
||||
adev->close(adev);
|
||||
return;
|
||||
|
||||
ac = c->aux;
|
||||
adev = ac->adev;
|
||||
if(c->qid.path == Qaudio && (c->flag & COPEN))
|
||||
if(adev->close)
|
||||
adev->close(adev);
|
||||
|
||||
if(ac->owner == c){
|
||||
ac->owner = nil;
|
||||
c->aux = nil;
|
||||
free(ac);
|
||||
}
|
||||
}
|
||||
|
||||
static Walkqid*
|
||||
audiowalk(Chan *c, Chan *nc, char **name, int nname)
|
||||
{
|
||||
return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
|
||||
Audiochan *ac;
|
||||
Audio *adev;
|
||||
Walkqid *wq;
|
||||
|
||||
ac = c->aux;
|
||||
adev = ac->adev;
|
||||
wq = devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
|
||||
if(wq && wq->clone){
|
||||
if(audioclone(wq->clone, adev) == nil){
|
||||
free(wq);
|
||||
wq = nil;
|
||||
}
|
||||
}
|
||||
return wq;
|
||||
}
|
||||
|
||||
static int
|
||||
audiostat(Chan *c, uchar *dp, int n)
|
||||
{
|
||||
Audiochan *ac;
|
||||
Audio *adev;
|
||||
adev = c->aux;
|
||||
|
||||
ac = c->aux;
|
||||
adev = ac->adev;
|
||||
|
||||
/* BUG: race */
|
||||
if(adev->buffered && (ulong)c->qid.path == Qaudio)
|
||||
audiodir[Qaudio].length = adev->buffered(adev);
|
||||
|
||||
return devstat(c, dp, n, audiodir, nelem(audiodir), devgen);
|
||||
}
|
||||
|
||||
static Chan*
|
||||
audioopen(Chan *c, int omode)
|
||||
/*
|
||||
* audioread() made sure the buffer is big enougth so a full volume
|
||||
* table can be serialized in one pass.
|
||||
*/
|
||||
long
|
||||
genaudiovolread(Audio *adev, void *a, long n, vlong,
|
||||
Volume *vol, int (*volget)(Audio *, int, int *), ulong caps)
|
||||
{
|
||||
return devopen(c, omode, audiodir, nelem(audiodir), devgen);
|
||||
int i, j, v[2];
|
||||
char *p, *e;
|
||||
|
||||
p = a;
|
||||
e = p + n;
|
||||
for(i = 0; vol[i].name != 0; ++i){
|
||||
if(vol[i].cap && (vol[i].cap & caps) == 0)
|
||||
continue;
|
||||
v[0] = 0;
|
||||
v[1] = 0;
|
||||
if((*volget)(adev, i, v) != 0)
|
||||
continue;
|
||||
if(vol[i].type == Absolute)
|
||||
p += snprint(p, e - p, "%s %d\n", vol[i].name, v[0]);
|
||||
else {
|
||||
for(j=0; j<2; j++){
|
||||
if(v[j] < 0)
|
||||
v[j] = 0;
|
||||
if(v[j] > vol[i].range)
|
||||
v[j] = vol[i].range;
|
||||
v[j] = (v[j]*100)/vol[i].range;
|
||||
}
|
||||
switch(vol[i].type){
|
||||
case Left:
|
||||
p += snprint(p, e - p, "%s %d\n", vol[i].name, v[0]);
|
||||
break;
|
||||
case Right:
|
||||
p += snprint(p, e - p, "%s %d\n", vol[i].name, v[1]);
|
||||
break;
|
||||
case Stereo:
|
||||
p += snprint(p, e - p, "%s %d %d\n", vol[i].name, v[0], v[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p - (char*)a;
|
||||
}
|
||||
|
||||
/*
|
||||
* genaudiovolwrite modifies the buffer that gets passed to it. this
|
||||
* is ok as long as it is called from inside Audio.volwrite() because
|
||||
* audiowrite() copies the data to Audiochan.buf[] and inserts a
|
||||
* terminating \0 byte before calling Audio.volwrite().
|
||||
*/
|
||||
long
|
||||
genaudiovolwrite(Audio *adev, void *a, long n, vlong,
|
||||
Volume *vol, int (*volset)(Audio *, int, int *), ulong caps)
|
||||
{
|
||||
int ntok, i, j, v[2];
|
||||
char *p, *e, *x, *tok[4];
|
||||
|
||||
p = a;
|
||||
e = p + n;
|
||||
|
||||
for(;p < e; p = x){
|
||||
if(x = strchr(p, '\n'))
|
||||
*x++ = 0;
|
||||
else
|
||||
x = e;
|
||||
ntok = tokenize(p, tok, 4);
|
||||
if(ntok <= 0)
|
||||
continue;
|
||||
if(ntok == 1){
|
||||
tok[1] = tok[0];
|
||||
tok[0] = "master";
|
||||
ntok = 2;
|
||||
}
|
||||
for(i = 0; vol[i].name != 0; i++){
|
||||
if(vol[i].cap && (vol[i].cap & caps) == 0)
|
||||
continue;
|
||||
if(cistrcmp(vol[i].name, tok[0]))
|
||||
continue;
|
||||
|
||||
if((ntok>2) && (!cistrcmp(tok[1], "out") || !cistrcmp(tok[1], "in")))
|
||||
memmove(tok+1, tok+2, --ntok);
|
||||
|
||||
v[0] = 0;
|
||||
v[1] = 0;
|
||||
if(ntok > 1)
|
||||
v[0] = v[1] = atoi(tok[1]);
|
||||
if(ntok > 2)
|
||||
v[1] = atoi(tok[2]);
|
||||
if(vol[i].type == Absolute)
|
||||
(*volset)(adev, i, v);
|
||||
else {
|
||||
for(j=0; j<2; j++){
|
||||
v[j] = (50+(v[j]*vol[i].range))/100;
|
||||
if(v[j] < 0)
|
||||
v[j] = 0;
|
||||
if(v[j] > vol[i].range)
|
||||
v[j] = vol[i].range;
|
||||
}
|
||||
(*volset)(adev, i, v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(vol[i].name == nil)
|
||||
error(Evolume);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Dev audiodevtab = {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
typedef struct Alarms Alarms;
|
||||
typedef struct Audio Audio;
|
||||
typedef struct Block Block;
|
||||
typedef struct Chan Chan;
|
||||
typedef struct Cmdbuf Cmdbuf;
|
||||
|
@ -919,23 +918,6 @@ struct Uart
|
|||
|
||||
extern Uart* consuart;
|
||||
|
||||
struct Audio {
|
||||
Audio *next;
|
||||
char *name;
|
||||
void *ctlr;
|
||||
void *mixer;
|
||||
void (*attach)(Audio *);
|
||||
long (*read)(Audio *, void *, long, vlong);
|
||||
long (*write)(Audio *, void *, long, vlong);
|
||||
long (*volread)(Audio *, void *, long, vlong);
|
||||
long (*volwrite)(Audio *, void *, long, vlong);
|
||||
void (*close)(Audio *);
|
||||
long (*ctl)(Audio *, void *, long, vlong);
|
||||
long (*status)(Audio *, void *, long, vlong);
|
||||
long (*buffered)(Audio *);
|
||||
int ctlrno;
|
||||
};
|
||||
|
||||
/*
|
||||
* performance timers, all units in perfticks
|
||||
*/
|
||||
|
|
|
@ -4,7 +4,6 @@ Timer* addclock0link(void (*)(void), int);
|
|||
int addphysseg(Physseg*);
|
||||
void addbootfile(char*, uchar*, ulong);
|
||||
void addwatchdog(Watchdog*);
|
||||
void addaudiocard(char *, int (*)(Audio *));
|
||||
Block* adjustblock(Block*, int);
|
||||
void alarmkproc(void*);
|
||||
Block* allocb(int);
|
||||
|
|
Loading…
Reference in a new issue