xref: /plan9/sys/src/cmd/unix/drawterm/kern/devaudio.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1499069deSDavid du Colombier #include	"u.h"
2499069deSDavid du Colombier #include	"lib.h"
3499069deSDavid du Colombier #include	"dat.h"
4499069deSDavid du Colombier #include	"fns.h"
5499069deSDavid du Colombier #include	"error.h"
6499069deSDavid du Colombier #include	"devaudio.h"
7499069deSDavid du Colombier 
8499069deSDavid du Colombier enum
9499069deSDavid du Colombier {
10499069deSDavid du Colombier 	Qdir		= 0,
11499069deSDavid du Colombier 	Qaudio,
12499069deSDavid du Colombier 	Qvolume,
13499069deSDavid du Colombier 
14499069deSDavid du Colombier 	Aclosed		= 0,
15499069deSDavid du Colombier 	Aread,
16499069deSDavid du Colombier 	Awrite,
17499069deSDavid du Colombier 
18499069deSDavid du Colombier 	Speed		= 44100,
19499069deSDavid du Colombier 	Ncmd		= 50,		/* max volume command words */
20499069deSDavid du Colombier };
21499069deSDavid du Colombier 
22499069deSDavid du Colombier Dirtab
23499069deSDavid du Colombier audiodir[] =
24499069deSDavid du Colombier {
25499069deSDavid du Colombier 	".",	{Qdir, 0, QTDIR},		0,	DMDIR|0555,
26499069deSDavid du Colombier 	"audio",	{Qaudio},		0,	0666,
27499069deSDavid du Colombier 	"volume",	{Qvolume},		0,	0666,
28499069deSDavid du Colombier };
29499069deSDavid du Colombier 
30499069deSDavid du Colombier static	struct
31499069deSDavid du Colombier {
32499069deSDavid du Colombier 	QLock	lk;
33499069deSDavid du Colombier 	Rendez	vous;
34499069deSDavid du Colombier 	int	amode;		/* Aclosed/Aread/Awrite for /audio */
35499069deSDavid du Colombier } audio;
36499069deSDavid du Colombier 
37499069deSDavid du Colombier #define aqlock(a) qlock(&(a)->lk)
38499069deSDavid du Colombier #define aqunlock(a) qunlock(&(a)->lk)
39499069deSDavid du Colombier 
40499069deSDavid du Colombier static	struct
41499069deSDavid du Colombier {
42499069deSDavid du Colombier 	char*	name;
43499069deSDavid du Colombier 	int	flag;
44499069deSDavid du Colombier 	int	ilval;		/* initial values */
45499069deSDavid du Colombier 	int	irval;
46499069deSDavid du Colombier } volumes[] =
47499069deSDavid du Colombier {
48*ec59a3ddSDavid du Colombier 	"audio",	Fout, 		50,	50,
49*ec59a3ddSDavid du Colombier 	"synth",	Fin|Fout,	0,	0,
50*ec59a3ddSDavid du Colombier 	"cd",		Fin|Fout,	0,	0,
51*ec59a3ddSDavid du Colombier 	"line",	Fin|Fout,	0,	0,
52*ec59a3ddSDavid du Colombier 	"mic",	Fin|Fout|Fmono,	0,	0,
53*ec59a3ddSDavid du Colombier 	"speaker",	Fout|Fmono,	0,	0,
54499069deSDavid du Colombier 
55*ec59a3ddSDavid du Colombier 	"treb",		Fout, 		50,	50,
56*ec59a3ddSDavid du Colombier 	"bass",		Fout, 		50,	50,
57499069deSDavid du Colombier 
58*ec59a3ddSDavid du Colombier 	"speed",	Fin|Fout|Fmono,	Speed,	Speed,
59499069deSDavid du Colombier 	0
60499069deSDavid du Colombier };
61499069deSDavid du Colombier 
62499069deSDavid du Colombier static	char	Emode[]		= "illegal open mode";
63499069deSDavid du Colombier static	char	Evolume[]	= "illegal volume specifier";
64499069deSDavid du Colombier 
65499069deSDavid du Colombier static	void
resetlevel(void)66499069deSDavid du Colombier resetlevel(void)
67499069deSDavid du Colombier {
68499069deSDavid du Colombier 	int i;
69499069deSDavid du Colombier 
70499069deSDavid du Colombier 	for(i=0; volumes[i].name; i++)
71499069deSDavid du Colombier 		audiodevsetvol(i, volumes[i].ilval, volumes[i].irval);
72499069deSDavid du Colombier }
73499069deSDavid du Colombier 
74499069deSDavid du Colombier static void
audioinit(void)75499069deSDavid du Colombier audioinit(void)
76499069deSDavid du Colombier {
77499069deSDavid du Colombier }
78499069deSDavid du Colombier 
79499069deSDavid du Colombier static Chan*
audioattach(char * param)80499069deSDavid du Colombier audioattach(char *param)
81499069deSDavid du Colombier {
82499069deSDavid du Colombier 	return devattach('A', param);
83499069deSDavid du Colombier }
84499069deSDavid du Colombier 
85499069deSDavid du Colombier static Walkqid*
audiowalk(Chan * c,Chan * nc,char ** name,int nname)86499069deSDavid du Colombier audiowalk(Chan *c, Chan *nc, char **name, int nname)
87499069deSDavid du Colombier {
88499069deSDavid du Colombier 	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
89499069deSDavid du Colombier }
90499069deSDavid du Colombier 
91499069deSDavid du Colombier static int
audiostat(Chan * c,uchar * db,int n)92499069deSDavid du Colombier audiostat(Chan *c, uchar *db, int n)
93499069deSDavid du Colombier {
94499069deSDavid du Colombier 	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
95499069deSDavid du Colombier }
96499069deSDavid du Colombier 
97499069deSDavid du Colombier static Chan*
audioopen(Chan * c,int omode)98499069deSDavid du Colombier audioopen(Chan *c, int omode)
99499069deSDavid du Colombier {
100499069deSDavid du Colombier 	int amode;
101499069deSDavid du Colombier 
102499069deSDavid du Colombier 	switch((ulong)c->qid.path) {
103499069deSDavid du Colombier 	default:
104499069deSDavid du Colombier 		error(Eperm);
105499069deSDavid du Colombier 		break;
106499069deSDavid du Colombier 
107499069deSDavid du Colombier 	case Qvolume:
108499069deSDavid du Colombier 	case Qdir:
109499069deSDavid du Colombier 		break;
110499069deSDavid du Colombier 
111499069deSDavid du Colombier 	case Qaudio:
112499069deSDavid du Colombier 		amode = Awrite;
113499069deSDavid du Colombier 		if((omode&7) == OREAD)
114499069deSDavid du Colombier 			amode = Aread;
115499069deSDavid du Colombier 		aqlock(&audio);
116499069deSDavid du Colombier 		if(waserror()){
117499069deSDavid du Colombier 			aqunlock(&audio);
118499069deSDavid du Colombier 			nexterror();
119499069deSDavid du Colombier 		}
120499069deSDavid du Colombier 		if(audio.amode != Aclosed)
121499069deSDavid du Colombier 			error(Einuse);
122499069deSDavid du Colombier 		audiodevopen();
123499069deSDavid du Colombier 		audio.amode = amode;
124499069deSDavid du Colombier 		poperror();
125499069deSDavid du Colombier 		aqunlock(&audio);
126499069deSDavid du Colombier 		break;
127499069deSDavid du Colombier 	}
128499069deSDavid du Colombier 	c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
129499069deSDavid du Colombier 	c->mode = openmode(omode);
130499069deSDavid du Colombier 	c->flag |= COPEN;
131499069deSDavid du Colombier 	c->offset = 0;
132499069deSDavid du Colombier 
133499069deSDavid du Colombier 	return c;
134499069deSDavid du Colombier }
135499069deSDavid du Colombier 
136499069deSDavid du Colombier static void
audioclose(Chan * c)137499069deSDavid du Colombier audioclose(Chan *c)
138499069deSDavid du Colombier {
139499069deSDavid du Colombier 	switch((ulong)c->qid.path) {
140499069deSDavid du Colombier 	default:
141499069deSDavid du Colombier 		error(Eperm);
142499069deSDavid du Colombier 		break;
143499069deSDavid du Colombier 
144499069deSDavid du Colombier 	case Qdir:
145499069deSDavid du Colombier 	case Qvolume:
146499069deSDavid du Colombier 		break;
147499069deSDavid du Colombier 
148499069deSDavid du Colombier 	case Qaudio:
149499069deSDavid du Colombier 		if(c->flag & COPEN) {
150499069deSDavid du Colombier 			aqlock(&audio);
151499069deSDavid du Colombier 			audiodevclose();
152499069deSDavid du Colombier 			audio.amode = Aclosed;
153499069deSDavid du Colombier 			aqunlock(&audio);
154499069deSDavid du Colombier 		}
155499069deSDavid du Colombier 		break;
156499069deSDavid du Colombier 	}
157499069deSDavid du Colombier }
158499069deSDavid du Colombier 
159499069deSDavid du Colombier static long
audioread(Chan * c,void * v,long n,vlong off)160499069deSDavid du Colombier audioread(Chan *c, void *v, long n, vlong off)
161499069deSDavid du Colombier {
162499069deSDavid du Colombier 	int liv, riv, lov, rov;
163499069deSDavid du Colombier 	long m;
164499069deSDavid du Colombier 	char buf[300];
165499069deSDavid du Colombier 	int j;
166499069deSDavid du Colombier 	ulong offset = off;
167499069deSDavid du Colombier 	char *a;
168499069deSDavid du Colombier 
169499069deSDavid du Colombier 	a = v;
170499069deSDavid du Colombier 	switch((ulong)c->qid.path) {
171499069deSDavid du Colombier 	default:
172499069deSDavid du Colombier 		error(Eperm);
173499069deSDavid du Colombier 		break;
174499069deSDavid du Colombier 
175499069deSDavid du Colombier 	case Qdir:
176499069deSDavid du Colombier 		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
177499069deSDavid du Colombier 
178499069deSDavid du Colombier 	case Qaudio:
179499069deSDavid du Colombier 		if(audio.amode != Aread)
180499069deSDavid du Colombier 			error(Emode);
181499069deSDavid du Colombier 		aqlock(&audio);
182499069deSDavid du Colombier 		if(waserror()){
183499069deSDavid du Colombier 			aqunlock(&audio);
184499069deSDavid du Colombier 			nexterror();
185499069deSDavid du Colombier 		}
186499069deSDavid du Colombier 		n = audiodevread(v, n);
187499069deSDavid du Colombier 		poperror();
188499069deSDavid du Colombier 		aqunlock(&audio);
189499069deSDavid du Colombier 		break;
190499069deSDavid du Colombier 
191499069deSDavid du Colombier 	case Qvolume:
192499069deSDavid du Colombier 		j = 0;
193499069deSDavid du Colombier 		buf[0] = 0;
194499069deSDavid du Colombier 		for(m=0; volumes[m].name; m++){
195499069deSDavid du Colombier 			audiodevgetvol(m, &lov, &rov);
196499069deSDavid du Colombier 			liv = lov;
197499069deSDavid du Colombier 			riv = rov;
198499069deSDavid du Colombier 			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
199499069deSDavid du Colombier 			if((volumes[m].flag & Fmono) || (liv==riv && lov==rov)){
200499069deSDavid du Colombier 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
201499069deSDavid du Colombier 					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
202499069deSDavid du Colombier 				else{
203499069deSDavid du Colombier 					if(volumes[m].flag & Fin)
204499069deSDavid du Colombier 						j += snprint(buf+j, sizeof(buf)-j,
205499069deSDavid du Colombier 							" in %d", liv);
206499069deSDavid du Colombier 					if(volumes[m].flag & Fout)
207499069deSDavid du Colombier 						j += snprint(buf+j, sizeof(buf)-j,
208499069deSDavid du Colombier 							" out %d", lov);
209499069deSDavid du Colombier 				}
210499069deSDavid du Colombier 			}else{
211499069deSDavid du Colombier 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
212499069deSDavid du Colombier 				    liv==lov && riv==rov)
213499069deSDavid du Colombier 					j += snprint(buf+j, sizeof(buf)-j,
214499069deSDavid du Colombier 						" left %d right %d",
215499069deSDavid du Colombier 						liv, riv);
216499069deSDavid du Colombier 				else{
217499069deSDavid du Colombier 					if(volumes[m].flag & Fin)
218499069deSDavid du Colombier 						j += snprint(buf+j, sizeof(buf)-j,
219499069deSDavid du Colombier 							" in left %d right %d",
220499069deSDavid du Colombier 							liv, riv);
221499069deSDavid du Colombier 					if(volumes[m].flag & Fout)
222499069deSDavid du Colombier 						j += snprint(buf+j, sizeof(buf)-j,
223499069deSDavid du Colombier 							" out left %d right %d",
224499069deSDavid du Colombier 							lov, rov);
225499069deSDavid du Colombier 				}
226499069deSDavid du Colombier 			}
227499069deSDavid du Colombier 			j += snprint(buf+j, sizeof(buf)-j, "\n");
228499069deSDavid du Colombier 		}
229499069deSDavid du Colombier 		return readstr(offset, a, n, buf);
230499069deSDavid du Colombier 	}
231499069deSDavid du Colombier 	return n;
232499069deSDavid du Colombier }
233499069deSDavid du Colombier 
234499069deSDavid du Colombier static long
audiowrite(Chan * c,void * vp,long n,vlong off)235499069deSDavid du Colombier audiowrite(Chan *c, void *vp, long n, vlong off)
236499069deSDavid du Colombier {
237499069deSDavid du Colombier 	long m;
238499069deSDavid du Colombier 	int i, v, left, right, in, out;
239499069deSDavid du Colombier 	Cmdbuf *cb;
240499069deSDavid du Colombier 	char *a;
241499069deSDavid du Colombier 
242499069deSDavid du Colombier 	USED(off);
243499069deSDavid du Colombier 	a = vp;
244499069deSDavid du Colombier 	switch((ulong)c->qid.path) {
245499069deSDavid du Colombier 	default:
246499069deSDavid du Colombier 		error(Eperm);
247499069deSDavid du Colombier 		break;
248499069deSDavid du Colombier 
249499069deSDavid du Colombier 	case Qvolume:
250499069deSDavid du Colombier 		v = Vaudio;
251499069deSDavid du Colombier 		left = 1;
252499069deSDavid du Colombier 		right = 1;
253499069deSDavid du Colombier 		in = 1;
254499069deSDavid du Colombier 		out = 1;
255499069deSDavid du Colombier 		cb = parsecmd(vp, n);
256499069deSDavid du Colombier 		if(waserror()){
257499069deSDavid du Colombier 			free(cb);
258499069deSDavid du Colombier 			nexterror();
259499069deSDavid du Colombier 		}
260499069deSDavid du Colombier 
261499069deSDavid du Colombier 		for(i = 0; i < cb->nf; i++){
262499069deSDavid du Colombier 			/*
263499069deSDavid du Colombier 			 * a number is volume
264499069deSDavid du Colombier 			 */
265499069deSDavid du Colombier 			if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
266499069deSDavid du Colombier 				m = strtoul(cb->f[i], 0, 10);
267499069deSDavid du Colombier 				if(!out)
268499069deSDavid du Colombier 					goto cont0;
269499069deSDavid du Colombier 				if(left && right)
270499069deSDavid du Colombier 					audiodevsetvol(v, m, m);
271499069deSDavid du Colombier 				else if(left)
272499069deSDavid du Colombier 					audiodevsetvol(v, m, -1);
273499069deSDavid du Colombier 				else if(right)
274499069deSDavid du Colombier 					audiodevsetvol(v, -1, m);
275499069deSDavid du Colombier 				goto cont0;
276499069deSDavid du Colombier 			}
277499069deSDavid du Colombier 
278499069deSDavid du Colombier 			for(m=0; volumes[m].name; m++) {
279499069deSDavid du Colombier 				if(strcmp(cb->f[i], volumes[m].name) == 0) {
280499069deSDavid du Colombier 					v = m;
281499069deSDavid du Colombier 					in = 1;
282499069deSDavid du Colombier 					out = 1;
283499069deSDavid du Colombier 					left = 1;
284499069deSDavid du Colombier 					right = 1;
285499069deSDavid du Colombier 					goto cont0;
286499069deSDavid du Colombier 				}
287499069deSDavid du Colombier 			}
288499069deSDavid du Colombier 
289499069deSDavid du Colombier 			if(strcmp(cb->f[i], "reset") == 0) {
290499069deSDavid du Colombier 				resetlevel();
291499069deSDavid du Colombier 				goto cont0;
292499069deSDavid du Colombier 			}
293499069deSDavid du Colombier 			if(strcmp(cb->f[i], "in") == 0) {
294499069deSDavid du Colombier 				in = 1;
295499069deSDavid du Colombier 				out = 0;
296499069deSDavid du Colombier 				goto cont0;
297499069deSDavid du Colombier 			}
298499069deSDavid du Colombier 			if(strcmp(cb->f[i], "out") == 0) {
299499069deSDavid du Colombier 				in = 0;
300499069deSDavid du Colombier 				out = 1;
301499069deSDavid du Colombier 				goto cont0;
302499069deSDavid du Colombier 			}
303499069deSDavid du Colombier 			if(strcmp(cb->f[i], "left") == 0) {
304499069deSDavid du Colombier 				left = 1;
305499069deSDavid du Colombier 				right = 0;
306499069deSDavid du Colombier 				goto cont0;
307499069deSDavid du Colombier 			}
308499069deSDavid du Colombier 			if(strcmp(cb->f[i], "right") == 0) {
309499069deSDavid du Colombier 				left = 0;
310499069deSDavid du Colombier 				right = 1;
311499069deSDavid du Colombier 				goto cont0;
312499069deSDavid du Colombier 			}
313499069deSDavid du Colombier 			error(Evolume);
314499069deSDavid du Colombier 			break;
315499069deSDavid du Colombier 		cont0:;
316499069deSDavid du Colombier 		}
317499069deSDavid du Colombier 		free(cb);
318499069deSDavid du Colombier 		poperror();
319499069deSDavid du Colombier 		break;
320499069deSDavid du Colombier 
321499069deSDavid du Colombier 	case Qaudio:
322499069deSDavid du Colombier 		if(audio.amode != Awrite)
323499069deSDavid du Colombier 			error(Emode);
324499069deSDavid du Colombier 		aqlock(&audio);
325499069deSDavid du Colombier 		if(waserror()){
326499069deSDavid du Colombier 			aqunlock(&audio);
327499069deSDavid du Colombier 			nexterror();
328499069deSDavid du Colombier 		}
329499069deSDavid du Colombier 		n = audiodevwrite(vp, n);
330499069deSDavid du Colombier 		poperror();
331499069deSDavid du Colombier 		aqunlock(&audio);
332499069deSDavid du Colombier 		break;
333499069deSDavid du Colombier 	}
334499069deSDavid du Colombier 	return n;
335499069deSDavid du Colombier }
336499069deSDavid du Colombier 
337499069deSDavid du Colombier void
audioswab(uchar * a,uint n)338499069deSDavid du Colombier audioswab(uchar *a, uint n)
339499069deSDavid du Colombier {
340499069deSDavid du Colombier 	ulong *p, *ep, b;
341499069deSDavid du Colombier 
342499069deSDavid du Colombier 	p = (ulong*)a;
343499069deSDavid du Colombier 	ep = p + (n>>2);
344499069deSDavid du Colombier 	while(p < ep) {
345499069deSDavid du Colombier 		b = *p;
346499069deSDavid du Colombier 		b = (b>>24) | (b<<24) |
347499069deSDavid du Colombier 			((b&0xff0000) >> 8) |
348499069deSDavid du Colombier 			((b&0x00ff00) << 8);
349499069deSDavid du Colombier 		*p++ = b;
350499069deSDavid du Colombier 	}
351499069deSDavid du Colombier }
352499069deSDavid du Colombier 
353499069deSDavid du Colombier Dev audiodevtab = {
354499069deSDavid du Colombier 	'A',
355499069deSDavid du Colombier 	"audio",
356499069deSDavid du Colombier 
357499069deSDavid du Colombier 	devreset,
358499069deSDavid du Colombier 	audioinit,
359499069deSDavid du Colombier 	devshutdown,
360499069deSDavid du Colombier 	audioattach,
361499069deSDavid du Colombier 	audiowalk,
362499069deSDavid du Colombier 	audiostat,
363499069deSDavid du Colombier 	audioopen,
364499069deSDavid du Colombier 	devcreate,
365499069deSDavid du Colombier 	audioclose,
366499069deSDavid du Colombier 	audioread,
367499069deSDavid du Colombier 	devbread,
368499069deSDavid du Colombier 	audiowrite,
369499069deSDavid du Colombier 	devbwrite,
370499069deSDavid du Colombier 	devremove,
371499069deSDavid du Colombier 	devwstat,
372499069deSDavid du Colombier };
373