ref: 37ef30916cf30b47f92cf8f815caa23424263a5a
parent: b7aa0db7304009a3321f59c6272924c87f184b8a
author: Russ Cox <rsc@swtch.com>
date: Tue Mar 7 23:24:23 EST 2006
add audio
--- a/Make.config
+++ b/Make.config
@@ -1,1 +1,2 @@
+AUDIO=none
include $(ROOT)/Make.$(CONF)
--- a/Make.unix
+++ b/Make.unix
@@ -13,6 +13,8 @@
LDADD=-L$(X11)/lib -lX11 -ggdb
LDFLAGS=$(PTHREAD)
TARG=drawterm
+# AUDIO=none
+AUDIO=unix
all: default
--- a/kern/Makefile
+++ b/kern/Makefile
@@ -8,6 +8,8 @@
chan.$O\
data.$O\
dev.$O\
+ devaudio.$O\
+ devaudio-$(AUDIO).$O\
devcons.$O\
devdraw.$O\
devfs-$(OS).$O\
--- /dev/null
+++ b/kern/devaudio-none.c
@@ -1,0 +1,35 @@
+/*
+ * Linux and BSD
+ */
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "devaudio.h"
+
+/* maybe this should return -1 instead of sysfatal */
+void
+audiodevopen(void)
+{
+ error("no audio support");
+}
+
+void
+audiodevclose(void)
+{
+ error("no audio support");
+}
+
+void
+audiodevsetvol(int what, int left, int right)
+{
+ error("no audio support");
+}
+
+void
+audiodevgetvol(int what, int *left, int *right)
+{
+ error("no audio support");
+}
+
--- /dev/null
+++ b/kern/devaudio-unix.c
@@ -1,0 +1,182 @@
+/*
+ * Linux and BSD
+ */
+#include <sys/ioctl.h>
+#ifdef __linux__
+#include <linux/soundcard.h>
+#else
+#include <sys/soundcard.h>
+#endif
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "devaudio.h"
+
+enum
+{
+ Channels = 2,
+ Rate = 44100,
+ Bits = 16,
+ Bigendian = 1,
+};
+
+static int afd = -1;
+static int cfd= -1;
+static int speed;
+
+/* maybe this should return -1 instead of sysfatal */
+void
+audiodevopen(void)
+{
+ int t;
+ ulong ul;
+
+ afd = -1;
+ cfd = -1;
+ if((afd = open("/dev/dsp", OWRITE)) < 0)
+ goto err;
+ if((cfd = open("/dev/mixer", ORDWR)) < 0)
+ goto err;
+
+ t = Bits;
+ if(ioctl(afd, SNDCTL_DSP_SAMPLESIZE, &t) < 0)
+ goto err;
+
+ t = Channels-1;
+ if(ioctl(afd, SNDCTL_DSP_STEREO, &t) < 0)
+ goto err;
+
+ speed = Rate;
+ ul = Rate;
+ if(ioctl(afd, SNDCTL_DSP_SPEED, &ul) < 0)
+ goto err;
+
+ return;
+
+err:
+ if(afd >= 0)
+ close(afd);
+ afd = -1;
+ oserror();
+}
+
+void
+audiodevclose(void)
+{
+ close(afd);
+ close(cfd);
+ afd = -1;
+ cfd = -1;
+}
+
+static struct {
+ int id9;
+ int id;
+} names[] = {
+ Vaudio, SOUND_MIXER_VOLUME,
+ Vbass, SOUND_MIXER_BASS,
+ Vtreb, SOUND_MIXER_TREBLE,
+ Vline, SOUND_MIXER_LINE,
+ Vpcm, SOUND_MIXER_PCM,
+ Vsynth, SOUND_MIXER_SYNTH,
+ Vcd, SOUND_MIXER_CD,
+ Vmic, SOUND_MIXER_MIC,
+// "record", SOUND_MIXER_RECLEV,
+// "mix", SOUND_MIXER_IMIX,
+// "pcm2", SOUND_MIXER_ALTPCM,
+ Vspeaker, SOUND_MIXER_SPEAKER
+// "line1", SOUND_MIXER_LINE1,
+// "line2", SOUND_MIXER_LINE2,
+// "line3", SOUND_MIXER_LINE3,
+// "digital1", SOUND_MIXER_DIGITAL1,
+// "digital2", SOUND_MIXER_DIGITAL2,
+// "digital3", SOUND_MIXER_DIGITAL3,
+// "phonein", SOUND_MIXER_PHONEIN,
+// "phoneout", SOUND_MIXER_PHONEOUT,
+// "radio", SOUND_MIXER_RADIO,
+// "video", SOUND_MIXER_VIDEO,
+// "monitor", SOUND_MIXER_MONITOR,
+// "igain", SOUND_MIXER_IGAIN,
+// "ogain", SOUND_MIXER_OGAIN,
+};
+
+static int
+lookname(int id9)
+{
+ int i;
+
+ for(i=0; i<nelem(names); i++)
+ if(names[i].id9 == id9)
+ return names[i].id;
+ return -1;
+}
+
+void
+audiodevsetvol(int what, int left, int right)
+{
+ int id;
+ ulong x;
+ int can, v;
+
+ if(cfd < 0)
+ error("audio device not open");
+ if(what == Vspeed){
+ x = left;
+ if(ioctl(afd, SNDCTL_DSP_SPEED, &x) < 0)
+ oserror();
+ speed = x;
+ return;
+ }
+ if((id = lookname(what)) < 0)
+ return;
+ if(ioctl(cfd, SOUND_MIXER_READ_DEVMASK, &can) < 0)
+ can = ~0;
+ if(!(can & (1<<id)))
+ return;
+ v = left | (right<<8);
+ if(ioctl(cfd, MIXER_WRITE(id), &v) < 0)
+ oserror();
+}
+
+void
+audiodevgetvol(int what, int *left, int *right)
+{
+ int id;
+ int can, v;
+
+ if(cfd < 0)
+ error("audio device not open");
+ if(what == Vspeed){
+ *left = *right = speed;
+ return;
+ }
+ if((id = lookname(what)) < 0)
+ return;
+ if(ioctl(cfd, SOUND_MIXER_READ_DEVMASK, &can) < 0)
+ can = ~0;
+ if(!(can & (1<<id)))
+ return;
+ if(ioctl(cfd, MIXER_READ(id), &v) < 0)
+ oserror();
+ *left = v&0xFF;
+ *right = (v>>8)&0xFF;
+}
+
+int
+audiodevwrite(void *v, int n)
+{
+ int m, tot;
+
+ for(tot=0; tot<n; tot+=m)
+ if((m = write(afd, (uchar*)v+tot, n-tot)) <= 0)
+ oserror();
+ return tot;
+}
+
+int
+audiodevread(void *v, int n)
+{
+ error("no reading");
+}
--- /dev/null
+++ b/kern/devaudio.c
@@ -1,0 +1,372 @@
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "devaudio.h"
+
+enum
+{
+ Qdir = 0,
+ Qaudio,
+ Qvolume,
+
+ Aclosed = 0,
+ Aread,
+ Awrite,
+
+ Speed = 44100,
+ Ncmd = 50, /* max volume command words */
+};
+
+Dirtab
+audiodir[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "audio", {Qaudio}, 0, 0666,
+ "volume", {Qvolume}, 0, 0666,
+};
+
+static struct
+{
+ QLock lk;
+ Rendez vous;
+ int amode; /* Aclosed/Aread/Awrite for /audio */
+} audio;
+
+#define aqlock(a) qlock(&(a)->lk)
+#define aqunlock(a) qunlock(&(a)->lk)
+
+static struct
+{
+ char* name;
+ int flag;
+ int ilval; /* initial values */
+ int irval;
+} volumes[] =
+{
+[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 char Emode[] = "illegal open mode";
+static char Evolume[] = "illegal volume specifier";
+
+static void
+resetlevel(void)
+{
+ int i;
+
+ for(i=0; volumes[i].name; i++)
+ audiodevsetvol(i, volumes[i].ilval, volumes[i].irval);
+}
+
+static void
+audioinit(void)
+{
+}
+
+static Chan*
+audioattach(char *param)
+{
+ return devattach('A', param);
+}
+
+static Walkqid*
+audiowalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
+}
+
+static int
+audiostat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
+}
+
+static Chan*
+audioopen(Chan *c, int omode)
+{
+ int amode;
+
+ switch((ulong)c->qid.path) {
+ default:
+ error(Eperm);
+ break;
+
+ case Qvolume:
+ case Qdir:
+ break;
+
+ case Qaudio:
+ amode = Awrite;
+ if((omode&7) == OREAD)
+ amode = Aread;
+ aqlock(&audio);
+ if(waserror()){
+ aqunlock(&audio);
+ nexterror();
+ }
+ if(audio.amode != Aclosed)
+ error(Einuse);
+ audiodevopen();
+ audio.amode = amode;
+ poperror();
+ aqunlock(&audio);
+ break;
+ }
+ c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+
+ return c;
+}
+
+static void
+audioclose(Chan *c)
+{
+ switch((ulong)c->qid.path) {
+ default:
+ error(Eperm);
+ break;
+
+ case Qdir:
+ case Qvolume:
+ break;
+
+ case Qaudio:
+ if(c->flag & COPEN) {
+ aqlock(&audio);
+ audiodevclose();
+ audio.amode = Aclosed;
+ aqunlock(&audio);
+ }
+ break;
+ }
+}
+
+static long
+audioread(Chan *c, void *v, long n, vlong off)
+{
+ int liv, riv, lov, rov;
+ long m;
+ char buf[300];
+ int j;
+ ulong offset = off;
+ char *a;
+
+ a = v;
+ switch((ulong)c->qid.path) {
+ default:
+ error(Eperm);
+ break;
+
+ case Qdir:
+ return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
+
+ case Qaudio:
+ if(audio.amode != Aread)
+ error(Emode);
+ aqlock(&audio);
+ if(waserror()){
+ aqunlock(&audio);
+ nexterror();
+ }
+ n = audiodevread(v, n);
+ poperror();
+ aqunlock(&audio);
+ break;
+
+ case Qvolume:
+ j = 0;
+ buf[0] = 0;
+ for(m=0; volumes[m].name; m++){
+ audiodevgetvol(m, &lov, &rov);
+ liv = lov;
+ riv = rov;
+ j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
+ if((volumes[m].flag & Fmono) || (liv==riv && lov==rov)){
+ if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
+ j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
+ else{
+ if(volumes[m].flag & Fin)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " in %d", liv);
+ if(volumes[m].flag & Fout)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " out %d", lov);
+ }
+ }else{
+ if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
+ liv==lov && riv==rov)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " left %d right %d",
+ liv, riv);
+ else{
+ if(volumes[m].flag & Fin)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " in left %d right %d",
+ liv, riv);
+ if(volumes[m].flag & Fout)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " out left %d right %d",
+ lov, rov);
+ }
+ }
+ j += snprint(buf+j, sizeof(buf)-j, "\n");
+ }
+ return readstr(offset, a, n, buf);
+ }
+ return n;
+}
+
+static long
+audiowrite(Chan *c, void *vp, long n, vlong off)
+{
+ long m;
+ int i, v, left, right, in, out;
+ Cmdbuf *cb;
+ char *a;
+
+ USED(off);
+ a = vp;
+ switch((ulong)c->qid.path) {
+ default:
+ error(Eperm);
+ break;
+
+ case Qvolume:
+ v = Vaudio;
+ left = 1;
+ right = 1;
+ in = 1;
+ out = 1;
+ cb = parsecmd(vp, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+
+ for(i = 0; i < cb->nf; i++){
+ /*
+ * a number is volume
+ */
+ if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
+ m = strtoul(cb->f[i], 0, 10);
+ if(!out)
+ goto cont0;
+ if(left && right)
+ audiodevsetvol(v, m, m);
+ else if(left)
+ audiodevsetvol(v, m, -1);
+ else if(right)
+ audiodevsetvol(v, -1, m);
+ goto cont0;
+ }
+
+ for(m=0; volumes[m].name; m++) {
+ if(strcmp(cb->f[i], volumes[m].name) == 0) {
+ v = m;
+ in = 1;
+ out = 1;
+ left = 1;
+ right = 1;
+ goto cont0;
+ }
+ }
+
+ if(strcmp(cb->f[i], "reset") == 0) {
+ resetlevel();
+ goto cont0;
+ }
+ if(strcmp(cb->f[i], "in") == 0) {
+ in = 1;
+ out = 0;
+ goto cont0;
+ }
+ if(strcmp(cb->f[i], "out") == 0) {
+ in = 0;
+ out = 1;
+ goto cont0;
+ }
+ if(strcmp(cb->f[i], "left") == 0) {
+ left = 1;
+ right = 0;
+ goto cont0;
+ }
+ if(strcmp(cb->f[i], "right") == 0) {
+ left = 0;
+ right = 1;
+ goto cont0;
+ }
+ error(Evolume);
+ break;
+ cont0:;
+ }
+ free(cb);
+ poperror();
+ break;
+
+ case Qaudio:
+ if(audio.amode != Awrite)
+ error(Emode);
+ aqlock(&audio);
+ if(waserror()){
+ aqunlock(&audio);
+ nexterror();
+ }
+ n = audiodevwrite(vp, n);
+ poperror();
+ aqunlock(&audio);
+ break;
+ }
+ return n;
+}
+
+void
+audioswab(uchar *a, uint n)
+{
+ ulong *p, *ep, b;
+
+ p = (ulong*)a;
+ ep = p + (n>>2);
+ while(p < ep) {
+ b = *p;
+ b = (b>>24) | (b<<24) |
+ ((b&0xff0000) >> 8) |
+ ((b&0x00ff00) << 8);
+ *p++ = b;
+ }
+}
+
+Dev audiodevtab = {
+ 'A',
+ "audio",
+
+ devreset,
+ audioinit,
+ devshutdown,
+ audioattach,
+ audiowalk,
+ audiostat,
+ audioopen,
+ devcreate,
+ audioclose,
+ audioread,
+ devbread,
+ audiowrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- a/kern/devtab.c
+++ b/kern/devtab.c
@@ -14,6 +14,7 @@
extern Dev fsdevtab;
extern Dev mntdevtab;
extern Dev lfddevtab;
+extern Dev audiodevtab;
Dev *devtab[] = {
&rootdevtab,
@@ -26,6 +27,7 @@
&fsdevtab,
&mntdevtab,
&lfddevtab,
+ &audiodevtab,
0
};
--- a/main.c
+++ b/main.c
@@ -56,6 +56,7 @@
panic("bind #I: %r");
if(bind("#U", "/", MAFTER) < 0)
panic("bind #U: %r");
+ bind("#A", "/dev", MAFTER);
if(open("/dev/cons", OREAD) != 0)
panic("open0: %r");