shithub: drawterm

Download patch

ref: 37ef30916cf30b47f92cf8f815caa23424263a5a
parent: b7aa0db7304009a3321f59c6272924c87f184b8a
author: Russ Cox <[email protected]>
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");