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