xref: /plan9/sys/src/cmd/unix/drawterm/kern/devaudio.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include	"u.h"
2 #include	"lib.h"
3 #include	"dat.h"
4 #include	"fns.h"
5 #include	"error.h"
6 #include	"devaudio.h"
7 
8 enum
9 {
10 	Qdir		= 0,
11 	Qaudio,
12 	Qvolume,
13 
14 	Aclosed		= 0,
15 	Aread,
16 	Awrite,
17 
18 	Speed		= 44100,
19 	Ncmd		= 50,		/* max volume command words */
20 };
21 
22 Dirtab
23 audiodir[] =
24 {
25 	".",	{Qdir, 0, QTDIR},		0,	DMDIR|0555,
26 	"audio",	{Qaudio},		0,	0666,
27 	"volume",	{Qvolume},		0,	0666,
28 };
29 
30 static	struct
31 {
32 	QLock	lk;
33 	Rendez	vous;
34 	int	amode;		/* Aclosed/Aread/Awrite for /audio */
35 } audio;
36 
37 #define aqlock(a) qlock(&(a)->lk)
38 #define aqunlock(a) qunlock(&(a)->lk)
39 
40 static	struct
41 {
42 	char*	name;
43 	int	flag;
44 	int	ilval;		/* initial values */
45 	int	irval;
46 } volumes[] =
47 {
48 	"audio",	Fout, 		50,	50,
49 	"synth",	Fin|Fout,	0,	0,
50 	"cd",		Fin|Fout,	0,	0,
51 	"line",	Fin|Fout,	0,	0,
52 	"mic",	Fin|Fout|Fmono,	0,	0,
53 	"speaker",	Fout|Fmono,	0,	0,
54 
55 	"treb",		Fout, 		50,	50,
56 	"bass",		Fout, 		50,	50,
57 
58 	"speed",	Fin|Fout|Fmono,	Speed,	Speed,
59 	0
60 };
61 
62 static	char	Emode[]		= "illegal open mode";
63 static	char	Evolume[]	= "illegal volume specifier";
64 
65 static	void
resetlevel(void)66 resetlevel(void)
67 {
68 	int i;
69 
70 	for(i=0; volumes[i].name; i++)
71 		audiodevsetvol(i, volumes[i].ilval, volumes[i].irval);
72 }
73 
74 static void
audioinit(void)75 audioinit(void)
76 {
77 }
78 
79 static Chan*
audioattach(char * param)80 audioattach(char *param)
81 {
82 	return devattach('A', param);
83 }
84 
85 static Walkqid*
audiowalk(Chan * c,Chan * nc,char ** name,int nname)86 audiowalk(Chan *c, Chan *nc, char **name, int nname)
87 {
88 	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
89 }
90 
91 static int
audiostat(Chan * c,uchar * db,int n)92 audiostat(Chan *c, uchar *db, int n)
93 {
94 	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
95 }
96 
97 static Chan*
audioopen(Chan * c,int omode)98 audioopen(Chan *c, int omode)
99 {
100 	int amode;
101 
102 	switch((ulong)c->qid.path) {
103 	default:
104 		error(Eperm);
105 		break;
106 
107 	case Qvolume:
108 	case Qdir:
109 		break;
110 
111 	case Qaudio:
112 		amode = Awrite;
113 		if((omode&7) == OREAD)
114 			amode = Aread;
115 		aqlock(&audio);
116 		if(waserror()){
117 			aqunlock(&audio);
118 			nexterror();
119 		}
120 		if(audio.amode != Aclosed)
121 			error(Einuse);
122 		audiodevopen();
123 		audio.amode = amode;
124 		poperror();
125 		aqunlock(&audio);
126 		break;
127 	}
128 	c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
129 	c->mode = openmode(omode);
130 	c->flag |= COPEN;
131 	c->offset = 0;
132 
133 	return c;
134 }
135 
136 static void
audioclose(Chan * c)137 audioclose(Chan *c)
138 {
139 	switch((ulong)c->qid.path) {
140 	default:
141 		error(Eperm);
142 		break;
143 
144 	case Qdir:
145 	case Qvolume:
146 		break;
147 
148 	case Qaudio:
149 		if(c->flag & COPEN) {
150 			aqlock(&audio);
151 			audiodevclose();
152 			audio.amode = Aclosed;
153 			aqunlock(&audio);
154 		}
155 		break;
156 	}
157 }
158 
159 static long
audioread(Chan * c,void * v,long n,vlong off)160 audioread(Chan *c, void *v, long n, vlong off)
161 {
162 	int liv, riv, lov, rov;
163 	long m;
164 	char buf[300];
165 	int j;
166 	ulong offset = off;
167 	char *a;
168 
169 	a = v;
170 	switch((ulong)c->qid.path) {
171 	default:
172 		error(Eperm);
173 		break;
174 
175 	case Qdir:
176 		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
177 
178 	case Qaudio:
179 		if(audio.amode != Aread)
180 			error(Emode);
181 		aqlock(&audio);
182 		if(waserror()){
183 			aqunlock(&audio);
184 			nexterror();
185 		}
186 		n = audiodevread(v, n);
187 		poperror();
188 		aqunlock(&audio);
189 		break;
190 
191 	case Qvolume:
192 		j = 0;
193 		buf[0] = 0;
194 		for(m=0; volumes[m].name; m++){
195 			audiodevgetvol(m, &lov, &rov);
196 			liv = lov;
197 			riv = rov;
198 			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
199 			if((volumes[m].flag & Fmono) || (liv==riv && lov==rov)){
200 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
201 					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
202 				else{
203 					if(volumes[m].flag & Fin)
204 						j += snprint(buf+j, sizeof(buf)-j,
205 							" in %d", liv);
206 					if(volumes[m].flag & Fout)
207 						j += snprint(buf+j, sizeof(buf)-j,
208 							" out %d", lov);
209 				}
210 			}else{
211 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
212 				    liv==lov && riv==rov)
213 					j += snprint(buf+j, sizeof(buf)-j,
214 						" left %d right %d",
215 						liv, riv);
216 				else{
217 					if(volumes[m].flag & Fin)
218 						j += snprint(buf+j, sizeof(buf)-j,
219 							" in left %d right %d",
220 							liv, riv);
221 					if(volumes[m].flag & Fout)
222 						j += snprint(buf+j, sizeof(buf)-j,
223 							" out left %d right %d",
224 							lov, rov);
225 				}
226 			}
227 			j += snprint(buf+j, sizeof(buf)-j, "\n");
228 		}
229 		return readstr(offset, a, n, buf);
230 	}
231 	return n;
232 }
233 
234 static long
audiowrite(Chan * c,void * vp,long n,vlong off)235 audiowrite(Chan *c, void *vp, long n, vlong off)
236 {
237 	long m;
238 	int i, v, left, right, in, out;
239 	Cmdbuf *cb;
240 	char *a;
241 
242 	USED(off);
243 	a = vp;
244 	switch((ulong)c->qid.path) {
245 	default:
246 		error(Eperm);
247 		break;
248 
249 	case Qvolume:
250 		v = Vaudio;
251 		left = 1;
252 		right = 1;
253 		in = 1;
254 		out = 1;
255 		cb = parsecmd(vp, n);
256 		if(waserror()){
257 			free(cb);
258 			nexterror();
259 		}
260 
261 		for(i = 0; i < cb->nf; i++){
262 			/*
263 			 * a number is volume
264 			 */
265 			if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
266 				m = strtoul(cb->f[i], 0, 10);
267 				if(!out)
268 					goto cont0;
269 				if(left && right)
270 					audiodevsetvol(v, m, m);
271 				else if(left)
272 					audiodevsetvol(v, m, -1);
273 				else if(right)
274 					audiodevsetvol(v, -1, m);
275 				goto cont0;
276 			}
277 
278 			for(m=0; volumes[m].name; m++) {
279 				if(strcmp(cb->f[i], volumes[m].name) == 0) {
280 					v = m;
281 					in = 1;
282 					out = 1;
283 					left = 1;
284 					right = 1;
285 					goto cont0;
286 				}
287 			}
288 
289 			if(strcmp(cb->f[i], "reset") == 0) {
290 				resetlevel();
291 				goto cont0;
292 			}
293 			if(strcmp(cb->f[i], "in") == 0) {
294 				in = 1;
295 				out = 0;
296 				goto cont0;
297 			}
298 			if(strcmp(cb->f[i], "out") == 0) {
299 				in = 0;
300 				out = 1;
301 				goto cont0;
302 			}
303 			if(strcmp(cb->f[i], "left") == 0) {
304 				left = 1;
305 				right = 0;
306 				goto cont0;
307 			}
308 			if(strcmp(cb->f[i], "right") == 0) {
309 				left = 0;
310 				right = 1;
311 				goto cont0;
312 			}
313 			error(Evolume);
314 			break;
315 		cont0:;
316 		}
317 		free(cb);
318 		poperror();
319 		break;
320 
321 	case Qaudio:
322 		if(audio.amode != Awrite)
323 			error(Emode);
324 		aqlock(&audio);
325 		if(waserror()){
326 			aqunlock(&audio);
327 			nexterror();
328 		}
329 		n = audiodevwrite(vp, n);
330 		poperror();
331 		aqunlock(&audio);
332 		break;
333 	}
334 	return n;
335 }
336 
337 void
audioswab(uchar * a,uint n)338 audioswab(uchar *a, uint n)
339 {
340 	ulong *p, *ep, b;
341 
342 	p = (ulong*)a;
343 	ep = p + (n>>2);
344 	while(p < ep) {
345 		b = *p;
346 		b = (b>>24) | (b<<24) |
347 			((b&0xff0000) >> 8) |
348 			((b&0x00ff00) << 8);
349 		*p++ = b;
350 	}
351 }
352 
353 Dev audiodevtab = {
354 	'A',
355 	"audio",
356 
357 	devreset,
358 	audioinit,
359 	devshutdown,
360 	audioattach,
361 	audiowalk,
362 	audiostat,
363 	audioopen,
364 	devcreate,
365 	audioclose,
366 	audioread,
367 	devbread,
368 	audiowrite,
369 	devbwrite,
370 	devremove,
371 	devwstat,
372 };
373