From 4bc74b8aefb222cbc58f42b0fdbc28f50e5f1a35 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Fri, 20 May 2011 18:30:46 +0000 Subject: [PATCH] audioif, mixer control --- sys/src/9/pc/audioac97.c | 114 +++--------- sys/src/9/pc/audioac97mix.c | 302 ++++++++++-------------------- sys/src/9/pc/audiosb16.c | 355 +++++++++++++++--------------------- sys/src/9/port/audioif.h | 50 +++++ sys/src/9/port/devaudio.c | 335 ++++++++++++++++++++++++++++++---- sys/src/9/port/portdat.h | 18 -- sys/src/9/port/portfns.h | 1 - 7 files changed, 624 insertions(+), 551 deletions(-) create mode 100644 sys/src/9/port/audioif.h diff --git a/sys/src/9/pc/audioac97.c b/sys/src/9/pc/audioac97.c index 129449d66..8337d08c4 100644 --- a/sys/src/9/pc/audioac97.c +++ b/sys/src/9/pc/audioac97.c @@ -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); diff --git a/sys/src/9/pc/audioac97mix.c b/sys/src/9/pc/audioac97mix.c index f86e4ba1c..e6ef52a58 100644 --- a/sys/src/9/pc/audioac97mix.c +++ b/sys/src/9/pc/audioac97mix.c @@ -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; } diff --git a/sys/src/9/pc/audiosb16.c b/sys/src/9/pc/audiosb16.c index ac80cbda0..3c983c042 100644 --- a/sys/src/9/pc/audiosb16.c +++ b/sys/src/9/pc/audiosb16.c @@ -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; idma<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; } diff --git a/sys/src/9/port/audioif.h b/sys/src/9/port/audioif.h new file mode 100644 index 000000000..9deaec039 --- /dev/null +++ b/sys/src/9/port/audioif.h @@ -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); diff --git a/sys/src/9/port/devaudio.c b/sys/src/9/port/devaudio.c index 9234e15a9..2af6d9779 100644 --- a/sys/src/9/port/devaudio.c +++ b/sys/src/9/port/devaudio.c @@ -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 = { diff --git a/sys/src/9/port/portdat.h b/sys/src/9/port/portdat.h index ec1361f5f..6d8a59c0b 100644 --- a/sys/src/9/port/portdat.h +++ b/sys/src/9/port/portdat.h @@ -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 */ diff --git a/sys/src/9/port/portfns.h b/sys/src/9/port/portfns.h index e9deb83bb..ca56a6cf0 100644 --- a/sys/src/9/port/portfns.h +++ b/sys/src/9/port/portfns.h @@ -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);