| plan 9 kernel history: overview | file list | diff list |
1998/0825/port/devaudio.c (diff list | history)
| port/devaudio.c on 1995/0119 | ||
| 1995/0119 | /* * SB 16 driver */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "io.h" #include "audio.h" | |
| 1995/0217 | typedef struct AQueue AQueue; | |
| 1995/0119 | typedef struct Buf Buf; enum { Qdir = 0, Qaudio, Qvolume, Fmono = 1, | |
| 1995/0221 | Fin = 2, Fout = 4, | |
| 1995/0119 | ||
| 1995/0214 | Aclosed = 0, Aread, | |
| 1995/0119 | Awrite, | |
| 1995/0221 | Vaudio = 0, Vsynth, Vcd, Vline, Vmic, Vspeaker, Vtreb, Vbass, Vspeed, Nvol, | |
| 1995/0119 | Speed = 44100, Ncmd = 50, /* max volume command words */ }; Dirtab audiodir[] = { "audio", {Qaudio}, 0, 0666, "volume", {Qvolume}, 0, 0666, }; struct Buf { uchar* virt; ulong phys; Buf* next; }; | |
| 1995/0217 | struct AQueue | |
| 1995/0119 | { Lock; Buf* first; Buf* last; }; static struct { | |
| 1995/0214 | QLock; | |
| 1995/0119 | Rendez vous; int bufinit; /* boolean if buffers allocated */ int curcount; /* how much data in current buffer */ int active; /* boolean dma running */ int intr; /* boolean an interrupt has happened */ | |
| 1995/0214 | int amode; /* Aclosed/Aread/Awrite for /audio */ | |
| 1995/0221 | int rivol[Nvol]; /* right/left input/output volumes */ int livol[Nvol]; int rovol[Nvol]; int lovol[Nvol]; | |
| 1995/0119 | int major; /* SB16 major version number (sb 4) */ int minor; /* SB16 minor version number */ Buf buf[Nbuf]; /* buffers and queues */ | |
| 1995/0217 | AQueue empty; AQueue full; | |
| 1995/0119 | Buf* current; Buf* filling; } audio; static struct { char* name; int flag; | |
| 1995/0221 | int ilval; /* initial values */ | |
| 1995/0119 | int irval; } volumes[] = { | |
| 1997/0327 | [Vaudio] "audio", Fout, 50, 50, [Vsynth] "synth", Fin|Fout, 0, 0, [Vcd] "cd", Fin|Fout, 0, 0, [Vline] "line", Fin|Fout, 0, 0, | |
| 1995/0221 | [Vmic] "mic", Fin|Fout|Fmono, 0, 0, [Vspeaker] "speaker", Fout|Fmono, 0, 0, | |
| 1995/0119 | ||
| 1995/0221 | [Vtreb] "treb", Fout, 50, 50, [Vbass] "bass", Fout, 50, 50, | |
| 1995/0119 | ||
| 1997/0327 | [Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed, 0 | |
| 1995/0119 | }; | |
| 1995/0214 | static struct { Lock; int reset; /* io ports to the sound blaster */ int read; int write; int wstatus; int rstatus; int mixaddr; int mixdata; int clri8; int clri16; int clri401; | |
| 1996/1024 | int dma; | |
| 1998/0317 | void (*startdma)(void); void (*intr)(void); | |
| 1995/0214 | } blaster; | |
| 1995/0119 | static void swab(uchar*); | |
| 1996/1024 | static char Emajor[] = "soundblaster not responding/wrong version"; | |
| 1995/0119 | static char Emode[] = "illegal open mode"; static char Evolume[] = "illegal volume specifier"; static int sbcmd(int val) { int i, s; for(i=1<<16; i!=0; i--) { | |
| 1995/0214 | s = inb(blaster.wstatus); | |
| 1995/0119 | if((s & 0x80) == 0) { | |
| 1995/0214 | outb(blaster.write, val); | |
| 1995/0119 | return 0; } } | |
| 1998/0317 | /* print("#A: sbcmd (0x%.2x) timeout\n", val); /**/ | |
| 1995/0119 | return 1; } static int sbread(void) { int i, s; for(i=1<<16; i!=0; i--) { | |
| 1995/0214 | s = inb(blaster.rstatus); | |
| 1995/0119 | if((s & 0x80) != 0) { | |
| 1995/0214 | return inb(blaster.read); | |
| 1995/0119 | } } | |
| 1997/0327 | /* print("#A: sbread did not respond\n"); /**/ | |
| 1998/0317 | return -1; | |
| 1995/0119 | } | |
| 1998/0317 | static int ess1688w(int reg, int val) { if(sbcmd(reg) || sbcmd(val)) return 1; return 0; } static int ess1688r(int reg) { if(sbcmd(0xC0) || sbcmd(reg)) return -1; return sbread(); } | |
| 1995/0119 | static int mxcmd(int addr, int val) { | |
| 1995/0214 | outb(blaster.mixaddr, addr); outb(blaster.mixdata, val); | |
| 1995/0119 | return 1; } static int mxread(int addr) { int s; | |
| 1995/0214 | outb(blaster.mixaddr, addr); s = inb(blaster.mixdata); | |
| 1995/0119 | return s; } static void mxcmds(int s, int v) { if(v > 100) v = 100; if(v < 0) v = 0; mxcmd(s, (v*255)/100); } static void mxcmdt(int s, int v) { if(v > 100) v = 100; if(v <= 0) mxcmd(s, 0); else mxcmd(s, 255-100+v); } static void mxcmdu(int s, int v) { if(v > 100) v = 100; if(v <= 0) v = 0; mxcmd(s, 128-50+v); } static void mxvolume(void) { | |
| 1995/0221 | int *left, *right; int source; if(audio.amode == Aread){ left = audio.livol; right = audio.rivol; }else{ left = audio.lovol; right = audio.rovol; } | |
| 1995/0214 | ilock(&blaster); | |
| 1995/0119 | ||
| 1995/0221 | mxcmd(0x30, 255); /* left master */ mxcmd(0x31, 255); /* right master */ mxcmd(0x3f, 0); /* left igain */ mxcmd(0x40, 0); /* right igain */ mxcmd(0x41, 0); /* left ogain */ mxcmd(0x42, 0); /* right ogain */ | |
| 1995/0119 | ||
| 1995/0221 | mxcmds(0x32, left[Vaudio]); mxcmds(0x33, right[Vaudio]); | |
| 1995/0119 | ||
| 1995/0221 | mxcmds(0x34, left[Vsynth]); mxcmds(0x35, right[Vsynth]); | |
| 1995/0119 | ||
| 1995/0221 | mxcmds(0x36, left[Vcd]); mxcmds(0x37, right[Vcd]); | |
| 1995/0119 | ||
| 1995/0221 | mxcmds(0x38, left[Vline]); mxcmds(0x39, right[Vline]); | |
| 1995/0119 | ||
| 1995/0221 | mxcmds(0x3a, left[Vmic]); mxcmds(0x3b, left[Vspeaker]); | |
| 1995/0119 | ||
| 1995/0221 | mxcmdu(0x44, left[Vtreb]); mxcmdu(0x45, right[Vtreb]); | |
| 1995/0119 | ||
| 1995/0221 | mxcmdu(0x46, left[Vbass]); mxcmdu(0x47, right[Vbass]); | |
| 1995/0119 | ||
| 1995/0221 | 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(audio.amode == Aread) mxcmd(0x3c, 0); /* output switch */ else mxcmd(0x3c, source); mxcmd(0x3d, source); /* input left switch */ mxcmd(0x3e, source); /* input right switch */ | |
| 1995/0214 | iunlock(&blaster); | |
| 1995/0119 | } static Buf* | |
| 1995/0217 | getbuf(AQueue *q) | |
| 1995/0119 | { Buf *b; ilock(q); b = q->first; if(b) q->first = b->next; iunlock(q); return b; } static void | |
| 1995/0217 | putbuf(AQueue *q, Buf *b) | |
| 1995/0119 | { ilock(q); b->next = 0; if(q->first) q->last->next = b; else q->first = b; q->last = b; iunlock(q); } /* * move the dma to the next buffer */ static void contindma(void) { Buf *b; if(!audio.active) goto shutdown; b = audio.current; if(audio.amode == Aread) { | |
| 1998/0317 | if(b) /* shouldn't happen */ | |
| 1995/0119 | putbuf(&audio.full, b); b = getbuf(&audio.empty); } else { | |
| 1998/0317 | if(b) /* shouldn't happen */ | |
| 1995/0119 | putbuf(&audio.empty, b); b = getbuf(&audio.full); } audio.current = b; if(b == 0) goto shutdown; | |
| 1998/0317 | if(dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread) >= 0) return; print("#A: dmasetup fail\n"); putbuf(&audio.empty, b); | |
| 1995/0119 | shutdown: | |
| 1996/1024 | dmaend(blaster.dma); | |
| 1995/0119 | sbcmd(0xd9); /* exit at end of count */ sbcmd(0xd5); /* pause */ audio.curcount = 0; audio.active = 0; } /* * cause sb to get an interrupt per buffer. * start first dma */ static void | |
| 1998/0317 | sb16startdma(void) | |
| 1995/0119 | { ulong count; | |
| 1995/0221 | int speed; | |
| 1995/0119 | ||
| 1995/0214 | ilock(&blaster); | |
| 1996/1024 | dmaend(blaster.dma); | |
| 1995/0221 | if(audio.amode == Aread) { | |
| 1995/0119 | sbcmd(0x42); /* input sampling rate */ | |
| 1995/0221 | speed = audio.livol[Vspeed]; } else { | |
| 1995/0119 | sbcmd(0x41); /* output sampling rate */ | |
| 1995/0221 | speed = audio.lovol[Vspeed]; } sbcmd(speed>>8); sbcmd(speed); | |
| 1995/0119 | count = (Bufsize >> 1) - 1; if(audio.amode == Aread) sbcmd(0xbe); /* A/D, autoinit */ else sbcmd(0xb6); /* D/A, autoinit */ sbcmd(0x30); /* stereo, 16 bit */ sbcmd(count); sbcmd(count>>8); audio.active = 1; contindma(); | |
| 1995/0214 | iunlock(&blaster); | |
| 1995/0119 | } | |
| 1998/0317 | static int ess1688reset(void) { int i; outb(blaster.reset, 3); delay(1); /* >3 υs */ outb(blaster.reset, 0); delay(1); i = sbread(); if(i != 0xAA) { print("#A: no response 0x%.2x\n", i); return 1; } if(sbcmd(0xC6)){ /* extended mode */ print("#A: barf 3\n"); return 1; } return 0; } static void ess1688startdma(void) { ulong count; int speed, x; ilock(&blaster); dmaend(blaster.dma); if(audio.amode == Awrite) ess1688reset(); if(audio.amode == Aread) sbcmd(0xD3); /* speaker off */ /* * Set the speed. */ if(audio.amode == Aread) speed = audio.livol[Vspeed]; else speed = audio.lovol[Vspeed]; if(speed < 4000) speed = 4000; else if(speed > 48000) speed = 48000; if(speed > 22000) x = 0x80|(256-(795500+speed/2)/speed); else x = 128-(397700+speed/2)/speed; ess1688w(0xA1, x & 0xFF); speed = (speed * 9) / 20; x = 256 - 7160000 / (speed * 82); ess1688w(0xA2, x & 0xFF); if(audio.amode == Aread) ess1688w(0xB8, 0x0E); /* A/D, autoinit */ else ess1688w(0xB8, 0x04); /* D/A, autoinit */ x = ess1688r(0xA8) & ~0x03; ess1688w(0xA8, x|0x01); /* 2 channels */ ess1688w(0xB9, 2); /* demand mode, 4 bytes per request */ if(audio.amode == Awrite) ess1688w(0xB6, 0); ess1688w(0xB7, 0x71); ess1688w(0xB7, 0xBC); x = ess1688r(0xB1) & 0x0F; ess1688w(0xB1, x|0x50); x = ess1688r(0xB2) & 0x0F; ess1688w(0xB2, x|0x50); if(audio.amode == Awrite) sbcmd(0xD1); /* speaker on */ count = -Bufsize; ess1688w(0xA4, count & 0xFF); ess1688w(0xA5, (count>>8) & 0xFF); x = ess1688r(0xB8); ess1688w(0xB8, x|0x05); audio.active = 1; contindma(); iunlock(&blaster); } | |
| 1995/0119 | /* * if audio is stopped, * start it up again. */ static void pokeaudio(void) { if(!audio.active) | |
| 1998/0317 | blaster.startdma(); | |
| 1995/0119 | } | |
| 1998/0317 | static void sb16intr(void) | |
| 1995/0119 | { int stat, dummy; stat = mxread(0x82) & 7; /* get irq status */ if(stat) { dummy = 0; if(stat & 2) { | |
| 1995/0214 | ilock(&blaster); dummy = inb(blaster.clri16); | |
| 1995/0119 | contindma(); | |
| 1995/0214 | iunlock(&blaster); | |
| 1995/0119 | audio.intr = 1; wakeup(&audio.vous); } if(stat & 1) { | |
| 1995/0214 | dummy = inb(blaster.clri8); | |
| 1995/0119 | } if(stat & 4) { | |
| 1995/0214 | dummy = inb(blaster.clri401); | |
| 1995/0119 | } USED(dummy); } } | |
| 1998/0317 | static void ess1688intr(void) { int dummy; if(audio.active){ ilock(&blaster); contindma(); dummy = inb(blaster.clri8); iunlock(&blaster); audio.intr = 1; wakeup(&audio.vous); USED(dummy); } else print("#A: unexpected ess1688 interrupt\n"); } | |
| 1995/0119 | void | |
| 1998/0317 | audiosbintr(void) { /* * Carrera interrupt interface. */ blaster.intr(); } static void | |
| 1995/0804 | pcaudiosbintr(Ureg*, void*) | |
| 1995/0214 | { | |
| 1998/0317 | /* * x86 interrupt interface. */ blaster.intr(); | |
| 1995/0214 | } void | |
| 1995/0119 | audiodmaintr(void) { | |
| 1997/0327 | /* print("#A: dma interrupt\n"); /**/ | |
| 1995/0119 | } static int | |
| 1995/0804 | anybuf(void*) | |
| 1995/0119 | { return audio.intr; } /* * wait for some output to get * empty buffers back. */ static void waitaudio(void) { audio.intr = 0; pokeaudio(); tsleep(&audio.vous, anybuf, 0, 10*1000); if(audio.intr == 0) { | |
| 1997/0327 | /* print("#A: audio timeout\n"); /**/ | |
| 1995/0119 | audio.active = 0; pokeaudio(); } } static void sbbufinit(void) { int i; void *p; for(i=0; i<Nbuf; i++) { p = xspanalloc(Bufsize, CACHELINESZ, 64*1024); dcflush(p, Bufsize); audio.buf[i].virt = UNCACHED(uchar, p); audio.buf[i].phys = (ulong)PADDR(p); } } static void setempty(void) { int i; | |
| 1995/0214 | ilock(&blaster); | |
| 1995/0119 | audio.empty.first = 0; audio.empty.last = 0; audio.full.first = 0; audio.full.last = 0; audio.current = 0; audio.filling = 0; for(i=0; i<Nbuf; i++) putbuf(&audio.empty, &audio.buf[i]); | |
| 1995/0214 | iunlock(&blaster); | |
| 1995/0119 | } static void resetlevel(void) { int i; for(i=0; volumes[i].name; i++) { | |
| 1995/0221 | audio.lovol[i] = volumes[i].ilval; audio.rovol[i] = volumes[i].irval; audio.livol[i] = volumes[i].ilval; audio.rivol[i] = volumes[i].irval; | |
| 1995/0119 | } } | |
| 1998/0317 | static int ess1688(ISAConf* sbconf) { int i, major, minor; /* * Try for ESS1688. */ sbcmd(0xE7); /* get version */ major = sbread(); minor = sbread(); if(major != 0x68 || minor != 0x8B){ print("#A: model 0x%.2x 0x%.2x; not ESS1688 compatible\n", major, minor); return 1; } ess1688reset(); switch(sbconf->irq){ case 2: case 9: i = 0x50|(0<<2); break; case 5: i = 0x50|(1<<2); break; case 7: i = 0x50|(2<<2); break; case 10: i = 0x50|(3<<2); break; default: | |
| 1998/0825 | print("#A: bad ESS1688 irq %lud\n", sbconf->irq); | |
| 1998/0317 | return 1; } ess1688w(0xB1, i); switch(sbconf->dma){ case 0: i = 0x50|(1<<2); break; case 1: i = 0xF0|(2<<2); break; case 3: i = 0x50|(3<<2); break; default: | |
| 1998/0825 | print("#A: bad ESS1688 dma %lud\n", sbconf->dma); | |
| 1998/0317 | return 1; } ess1688w(0xB2, i); ess1688reset(); blaster.startdma = ess1688startdma; blaster.intr = ess1688intr; return 0; } | |
| 1997/0327 | static void | |
| 1995/0119 | audioinit(void) { | |
| 1995/0214 | ISAConf sbconf; | |
| 1995/0119 | int i; | |
| 1995/0214 | sbconf.port = 0x220; | |
| 1996/1025 | sbconf.dma = Dma; | |
| 1995/0214 | sbconf.irq = 7; if(isaconfig("audio", 0, &sbconf) == 0) return; | |
| 1998/0317 | if(cistrcmp(sbconf.type, "sb16") != 0 && cistrcmp(sbconf.type, "ess1688") != 0) | |
| 1995/0214 | return; switch(sbconf.port){ case 0x220: case 0x240: case 0x260: case 0x280: break; default: | |
| 1998/0825 | print("#A: bad port 0x%lux\n", sbconf.port); | |
| 1995/0214 | return; } switch(sbconf.irq){ case 2: case 5: case 7: case 10: break; default: | |
| 1998/0825 | print("#A: bad irq %lud\n", sbconf.irq); | |
| 1995/0214 | return; } blaster.reset = sbconf.port + 0x6; blaster.read = sbconf.port + 0xa; blaster.write = sbconf.port + 0xc; blaster.wstatus = sbconf.port + 0xc; blaster.rstatus = sbconf.port + 0xe; blaster.mixaddr = sbconf.port + 0x4; blaster.mixdata = sbconf.port + 0x5; blaster.clri8 = sbconf.port + 0xe; blaster.clri16 = sbconf.port + 0xf; blaster.clri401 = sbconf.port + 0x100; | |
| 1997/0327 | blaster.dma = sbconf.dma; | |
| 1995/0214 | ||
| 1998/0317 | blaster.startdma = sb16startdma; blaster.intr = sb16intr; | |
| 1996/1024 | seteisadma(blaster.dma, audiodmaintr); | |
| 1995/0214 | setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0); | |
| 1995/0119 | ||
| 1995/0214 | audio.amode = Aclosed; | |
| 1995/0119 | resetlevel(); | |
| 1995/0214 | outb(blaster.reset, 1); | |
| 1995/0119 | delay(1); /* >3 υs */ | |
| 1995/0214 | outb(blaster.reset, 0); | |
| 1995/0119 | delay(1); i = sbread(); if(i != 0xaa) { | |
| 1997/0327 | print("#A: no response #%.2x\n", i); | |
| 1995/0119 | return; } sbcmd(0xe1); /* get version */ audio.major = sbread(); audio.minor = sbread(); if(audio.major != 4) { | |
| 1998/0317 | if(audio.major != 3 || audio.minor != 1 || ess1688(&sbconf)){ print("#A: model 0x%.2x 0x%.2x; not SB 16 compatible\n", audio.major, audio.minor); return; } audio.major = 4; | |
| 1995/0119 | } | |
| 1998/0512 | ||
| 1995/0119 | /* * initialize the mixer */ mxcmd(0x00, 0); /* Reset mixer */ mxvolume(); /* * set up irq/dma chans */ mxcmd(0x80, /* irq */ | |
| 1995/0214 | (sbconf.irq==2)? 1: (sbconf.irq==5)? 2: (sbconf.irq==7)? 4: (sbconf.irq==10)? 8: | |
| 1995/0119 | 0); | |
| 1996/1024 | mxcmd(0x81, 1<<blaster.dma); /* dma */ | |
| 1995/0119 | } | |
| 1997/0327 | static Chan* | |
| 1995/0119 | audioattach(char *param) { return devattach('A', param); } | |
| 1997/0327 | static int | |
| 1995/0119 | audiowalk(Chan *c, char *name) { | |
| 1997/0327 | return devwalk(c, name, audiodir, nelem(audiodir), devgen); | |
| 1995/0119 | } | |
| 1997/0327 | static void | |
| 1995/0119 | audiostat(Chan *c, char *db) { | |
| 1997/0327 | devstat(c, db, audiodir, nelem(audiodir), devgen); | |
| 1995/0119 | } | |
| 1997/0327 | static Chan* | |
| 1995/0119 | audioopen(Chan *c, int omode) { | |
| 1995/0214 | int amode; | |
| 1995/0119 | if(audio.major != 4) error(Emajor); switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qvolume: case Qdir: break; case Qaudio: | |
| 1995/0214 | amode = Awrite; if((omode&7) == OREAD) amode = Aread; qlock(&audio); if(audio.amode != Aclosed){ qunlock(&audio); error(Einuse); } | |
| 1995/0119 | if(audio.bufinit == 0) { audio.bufinit = 1; sbbufinit(); } | |
| 1995/0214 | audio.amode = amode; | |
| 1995/0119 | setempty(); audio.curcount = 0; | |
| 1995/0214 | qunlock(&audio); | |
| 1995/0221 | mxvolume(); | |
| 1995/0119 | break; } | |
| 1997/0327 | c = devopen(c, omode, audiodir, nelem(audiodir), devgen); | |
| 1995/0119 | c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } | |
| 1997/0327 | static void | |
| 1995/0119 | audioclose(Chan *c) { | |
| 1998/0624 | Buf *b; | |
| 1995/0119 | switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qdir: case Qvolume: break; case Qaudio: if(c->flag & COPEN) { | |
| 1995/0214 | qlock(&audio); | |
| 1998/0624 | if(audio.amode == Awrite) { | |
| 1998/0603 | /* flush out last partial buffer */ | |
| 1998/0624 | b = audio.filling; if(b) { | |
| 1998/0603 | audio.filling = 0; memset(b->virt, 0, Bufsize-audio.curcount); swab(b->virt); putbuf(&audio.full, b); } | |
| 1998/0624 | if(!audio.active && audio.full.first) | |
| 1998/0603 | pokeaudio(); } | |
| 1995/0214 | audio.amode = Aclosed; if(waserror()){ qunlock(&audio); nexterror(); } | |
| 1995/0119 | while(audio.active) waitaudio(); setempty(); | |
| 1995/0214 | poperror(); qunlock(&audio); | |
| 1995/0119 | } break; } } | |
| 1997/0327 | static long | |
| 1998/0319 | audioread(Chan *c, char *a, long n, vlong off) | |
| 1995/0119 | { | |
| 1995/0221 | int liv, riv, lov, rov; long m, n0; char buf[300]; | |
| 1995/0119 | Buf *b; int j; | |
| 1998/0319 | ulong offset = off; | |
| 1995/0119 | n0 = n; switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qdir: | |
| 1997/0327 | return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); | |
| 1995/0119 | case Qaudio: if(audio.amode != Aread) error(Emode); | |
| 1995/0214 | qlock(&audio); if(waserror()){ qunlock(&audio); nexterror(); } | |
| 1995/0119 | while(n > 0) { b = audio.filling; if(b == 0) { b = getbuf(&audio.full); if(b == 0) { waitaudio(); continue; } audio.filling = b; swab(b->virt); audio.curcount = 0; } m = Bufsize-audio.curcount; if(m > n) m = n; memmove(a, b->virt+audio.curcount, m); audio.curcount += m; n -= m; a += m; if(audio.curcount >= Bufsize) { audio.filling = 0; putbuf(&audio.empty, b); } } | |
| 1995/0214 | poperror(); qunlock(&audio); | |
| 1995/0119 | break; case Qvolume: j = 0; | |
| 1995/0221 | buf[0] = 0; for(m=0; volumes[m].name; m++){ liv = audio.livol[m]; riv = audio.rivol[m]; lov = audio.lovol[m]; rov = audio.rovol[m]; 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) | |
| 1998/0327 | j += snprint(buf+j, sizeof(buf)-j, | |
| 1998/0331 | " in %d", liv); | |
| 1995/0221 | if(volumes[m].flag & Fout) | |
| 1998/0327 | j += snprint(buf+j, sizeof(buf)-j, | |
| 1998/0331 | " out %d", lov); | |
| 1995/0221 | } }else{ | |
| 1998/0327 | if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov) j += snprint(buf+j, sizeof(buf)-j, " left %d right %d", | |
| 1995/0221 | liv, riv); else{ if(volumes[m].flag & Fin) | |
| 1998/0327 | j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d", | |
| 1995/0221 | liv, riv); if(volumes[m].flag & Fout) | |
| 1998/0327 | j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d", | |
| 1995/0221 | lov, rov); } | |
| 1995/0119 | } | |
| 1995/0221 | j += snprint(buf+j, sizeof(buf)-j, "\n"); | |
| 1995/0119 | } return readstr(offset, a, n, buf); } return n0-n; } | |
| 1997/0327 | static long | |
| 1998/0319 | audiowrite(Chan *c, char *a, long n, vlong) | |
| 1995/0119 | { long m, n0; | |
| 1995/0221 | int i, nf, v, left, right, in, out; | |
| 1995/0119 | char buf[255], *field[Ncmd]; Buf *b; n0 = n; switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qvolume: | |
| 1995/0221 | v = Vaudio; | |
| 1995/0119 | left = 1; right = 1; | |
| 1995/0221 | in = 1; out = 1; | |
| 1995/0119 | if(n > sizeof(buf)-1) n = sizeof(buf)-1; memmove(buf, a, n); buf[n] = '\0'; | |
| 1996/0315 | nf = parsefields(buf, field, Ncmd, " \t\n"); | |
| 1995/0119 | for(i = 0; i < nf; i++){ /* * a number is volume */ if(field[i][0] >= '0' && field[i][0] <= '9') { m = strtoul(field[i], 0, 10); | |
| 1995/0221 | if(left && out) audio.lovol[v] = m; if(left && in) audio.livol[v] = m; if(right && out) audio.rovol[v] = m; if(right && in) audio.rivol[v] = m; | |
| 1995/0119 | mxvolume(); goto cont0; } for(m=0; volumes[m].name; m++) { if(strcmp(field[i], volumes[m].name) == 0) { v = m; | |
| 1995/0221 | in = 1; out = 1; left = 1; right = 1; | |
| 1995/0119 | goto cont0; } } if(strcmp(field[i], "reset") == 0) { resetlevel(); mxvolume(); goto cont0; } | |
| 1995/0221 | if(strcmp(field[i], "in") == 0) { in = 1; out = 0; goto cont0; } if(strcmp(field[i], "out") == 0) { in = 0; out = 1; goto cont0; } | |
| 1995/0119 | if(strcmp(field[i], "left") == 0) { left = 1; right = 0; goto cont0; } if(strcmp(field[i], "right") == 0) { left = 0; right = 1; goto cont0; } error(Evolume); break; cont0:; } break; case Qaudio: if(audio.amode != Awrite) error(Emode); | |
| 1995/0214 | qlock(&audio); if(waserror()){ qunlock(&audio); nexterror(); } | |
| 1995/0119 | while(n > 0) { b = audio.filling; if(b == 0) { b = getbuf(&audio.empty); if(b == 0) { waitaudio(); continue; } audio.filling = b; audio.curcount = 0; } m = Bufsize-audio.curcount; if(m > n) m = n; memmove(b->virt+audio.curcount, a, m); audio.curcount += m; n -= m; a += m; if(audio.curcount >= Bufsize) { audio.filling = 0; swab(b->virt); putbuf(&audio.full, b); } } | |
| 1995/0214 | poperror(); qunlock(&audio); | |
| 1995/0119 | break; } return n0 - n; } static void swab(uchar *a) { ulong *p, *ep, b; | |
| 1995/0214 | if(!SBswab) return; | |
| 1995/0119 | p = (ulong*)a; ep = p + (Bufsize>>2); while(p < ep) { b = *p; b = (b>>24) | (b<<24) | ((b&0xff0000) >> 8) | ((b&0x00ff00) << 8); *p++ = b; } } | |
| 1997/0327 | Dev audiodevtab = { | |
| 1997/0408 | 'A', "audio", | |
| 1997/0327 | devreset, audioinit, audioattach, devclone, audiowalk, audiostat, audioopen, devcreate, audioclose, audioread, devbread, audiowrite, devbwrite, devremove, devwstat, }; | |