xref: /inferno-os/emu/DragonFly/audio.c (revision ebcf8d9b30a1bc12ed7da82c9b1bae3bf11ef0dd)
1 #include "dat.h"
2 #include "fns.h"
3 #include "error.h"
4 #include <fcntl.h>
5 #include <sys/ioctl.h>
6 #include <sys/filio.h>
7 #include "audio.h"
8 #include <sys/soundcard.h>
9 
10 #define 	Audio_Mic_Val		SOUND_MIXER_MIC
11 #define 	Audio_Linein_Val	SOUND_MIXER_LINE
12 
13 #define	Audio_Speaker_Val	SOUND_MIXER_SPEAKER
14 #define	Audio_Headphone_Val	SOUND_MIXER_PHONEOUT
15 #define	Audio_Lineout_Val	SOUND_MIXER_VOLUME
16 
17 #define 	Audio_Pcm_Val		AFMT_S16_LE
18 #define 	Audio_Ulaw_Val		AFMT_MU_LAW
19 #define 	Audio_Alaw_Val		AFMT_A_LAW
20 
21 #include "audio-tbls.c"
22 
23 #define	min(a,b)	((a) < (b) ? (a) : (b))
24 static int debug;
25 
26 #define AUDIO_FILE_STRING	"/dev/dsp"
27 
28 enum {
29 	A_Pause,
30 	A_UnPause
31 };
32 
33 enum {
34 	A_In,
35 	A_Out
36 };
37 
38 static QLock inlock;
39 static QLock outlock;
40 
41 static	int	audio_file  = -1;	/* file in/out */
42 static	int	audio_file_in  = -1;	/* copy of above when opened O_READ/O_RDWR */
43 static	int	audio_file_out  = -1;	/* copy of above when opened O_WRITE/O_RDWR */
44 
45 static	int	audio_swap_flag = 0;	/* endian swap */
46 
47 static	int	audio_in_pause = A_UnPause;
48 
49 static Audio_t av;
50 static int mixerleftvol[32];
51 static int mixerrightvol[32];
52 
53 static int audio_enforce(Audio_t*);
54 static int audio_open(void);
55 static int audio_pause_in(int, int);
56 static int audio_flush(int, int);
57 static int audio_pause_out(int);
58 static int audio_set_blocking(int);
59 static int audio_set_info(int, Audio_d*, int);
60 static void audio_swap_endian(char*, int);
61 
62 void
63 audio_file_init(void)
64 {
65 	int i;
66 	static ushort flag = 1;
67 
68 	audio_swap_flag = *((uchar*)&flag) == 0;	/* big-endian? */
69 	audio_info_init(&av);
70 	for (i = 0; i < 32; i++)
71 		mixerleftvol[i] = mixerrightvol[i] = 100;
72 }
73 
74 void
75 audio_ctl_init(void)
76 {
77 }
78 
79 void
80 audio_file_open(Chan *c, int omode)
81 {
82 	char ebuf[ERRMAX];
83 
84 	if (debug)
85 		print("audio_file_open(0x%.8lux, %d)\n", c, omode);
86 	switch(omode){
87 	case OREAD:
88 		qlock(&inlock);
89 		if(waserror()){
90 			qunlock(&inlock);
91 			nexterror();
92 		}
93 
94 		if(audio_file_in >= 0)
95 			error(Einuse);
96 		if (audio_file < 0)
97 			audio_file = audio_open();
98 		audio_file_in = audio_file;
99 		poperror();
100 		qunlock(&inlock);
101 		break;
102 	case OWRITE:
103 		qlock(&outlock);
104 		if(waserror()){
105 			qunlock(&outlock);
106 			nexterror();
107 		}
108 		if(audio_file_out >= 0)
109 			error(Einuse);
110 		if (audio_file < 0)
111 			audio_file = audio_open();
112 		audio_file_out = audio_file;
113 		poperror();
114 		qunlock(&outlock);
115 		break;
116 	case ORDWR:
117 		qlock(&inlock);
118 		qlock(&outlock);
119 		if(waserror()){
120 			qunlock(&outlock);
121 			qunlock(&inlock);
122 			nexterror();
123 		}
124 		if(audio_file_in >= 0 || audio_file_out >= 0)
125 			error(Einuse);
126 		if (audio_file < 0)
127 			audio_file = audio_open();
128 		audio_file_in = audio_file_out = audio_file;
129 		poperror();
130 		qunlock(&outlock);
131 		qunlock(&inlock);
132 		break;
133 	}
134 	if (debug)
135 		print("audio_file_open: success\nin %d out %d both %d\n",
136 			audio_file_out, audio_file_in, audio_file);
137 }
138 
139 void
140 audio_ctl_open(Chan *c, int omode)
141 {
142 	USED(c);
143 	USED(omode);
144 }
145 
146 void
147 audio_file_close(Chan *c)
148 {
149 	switch(c->mode){
150 	case OREAD:
151 		qlock(&inlock);
152 		qlock(&outlock);
153 		if (audio_file_out < 0) {
154 			close(audio_file);
155 			audio_file = -1;
156 		}
157 		qunlock(&outlock);
158 		audio_file_in = -1;
159 		qunlock(&inlock);
160 		break;
161 	case OWRITE:
162 		qlock(&inlock);
163 		qlock(&outlock);
164 		if (audio_file_in < 0) {
165 			close(audio_file);
166 			audio_file = -1;
167 		}
168 		audio_file_out = -1;
169 		qunlock(&outlock);
170 		qunlock(&inlock);
171 		break;
172 	case ORDWR:
173 		qlock(&inlock);
174 		qlock(&outlock);
175 		close(audio_file);
176 		audio_file_in = audio_file_out = audio_file = -1;
177 		qunlock(&outlock);
178 		qunlock(&inlock);
179 		break;
180 	}
181 }
182 
183 void
184 audio_ctl_close(Chan *c)
185 {
186 }
187 
188 long
189 audio_file_read(Chan *c, void *va, long count, vlong offset)
190 {
191 	struct  timespec time;
192 	long ba, status, chunk, total;
193 	char *pva = (char *) va;
194 
195 	qlock(&inlock);
196 	if(waserror()){
197 		qunlock(&inlock);
198 		nexterror();
199 	}
200 
201 	if(audio_file_in < 0)
202 		error(Eperm);
203 
204 	/* check block alignment */
205 	ba = av.in.bits * av.in.chan / Bits_Per_Byte;
206 
207 	if(count % ba)
208 		error(Ebadarg);
209 
210 	if(!audio_pause_in(audio_file_in, A_UnPause))
211 		error(Eio);
212 
213 	total = 0;
214 	while(total < count) {
215 		chunk = count - total;
216 		osenter();
217 		status = read(audio_file_in, pva + total, chunk);
218 		osleave();
219 		if(status < 0)
220 			error(Eio);
221 		total += status;
222 	}
223 
224 	if(total != count)
225 		error(Eio);
226 
227 	if(audio_swap_flag && av.out.bits == 16)
228 		audio_swap_endian(pva, count);
229 
230 	poperror();
231 	qunlock(&inlock);
232 
233 	return count;
234 }
235 
236 long
237 audio_file_write(Chan *c, void *va, long count, vlong offset)
238 {
239 	struct  timespec time;
240 	long status = -1;
241 	long ba, total, chunk, bufsz;
242 
243 	if (debug > 1)
244 		print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
245 			c, va, count, offset);
246 
247 	qlock(&outlock);
248 	if(waserror()){
249 		qunlock(&outlock);
250 		nexterror();
251 	}
252 
253 	if(audio_file_out < 0)
254 		error(Eperm);
255 
256 	/* check block alignment */
257 	ba = av.out.bits * av.out.chan / Bits_Per_Byte;
258 
259 	if(count % ba)
260 		error(Ebadarg);
261 
262 	if(audio_swap_flag && av.out.bits == 16)
263 		audio_swap_endian(va, count);
264 
265 	total = 0;
266 	bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
267 
268 	if(bufsz == 0)
269 		error(Ebadarg);
270 
271 	while(total < count) {
272 		chunk = min(bufsz, count - total);
273 		osenter();
274 		status = write(audio_file_out, va, chunk);
275 		osleave();
276 		if(status <= 0)
277 			error(Eio);
278 		total += status;
279 	}
280 
281 	poperror();
282 	qunlock(&outlock);
283 
284 	return count;
285 }
286 
287 static int
288 audio_open(void)
289 {
290 	int fd;
291 
292 	/* open non-blocking in case someone already has it open */
293 	/* otherwise we would block until they close! */
294 	fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
295 	if(fd < 0)
296 		oserror();
297 
298 	/* change device to be blocking */
299 	if(!audio_set_blocking(fd)) {
300 		if (debug)
301 			print("audio_open: failed to set blocking\n");
302 		close(fd);
303 		error("cannot set blocking mode");
304 	}
305 
306 	if (debug)
307 		print("audio_open: blocking set\n");
308 
309 	/* set audio info */
310 	av.in.flags = ~0;
311 	av.out.flags = 0;
312 
313 	if(! audio_set_info(fd, &av.in, A_In)) {
314 		close(fd);
315 		error(Ebadarg);
316 	}
317 
318 	av.in.flags = 0;
319 
320 	/* tada, we're open, blocking, paused and flushed */
321 	return fd;
322 }
323 
324 long
325 audio_ctl_write(Chan *c, void *va, long count, vlong offset)
326 {
327 	int	fd;
328 	int	ff;
329 	Audio_t tmpav = av;
330 
331 	tmpav.in.flags = 0;
332 	tmpav.out.flags = 0;
333 
334 	if (!audioparse(va, count, &tmpav))
335 		error(Ebadarg);
336 
337 	if (!audio_enforce(&tmpav))
338 		error(Ebadarg);
339 
340 	qlock(&inlock);
341 	if (waserror()) {
342 		qunlock(&inlock);
343 		nexterror();
344 	}
345 
346 	if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
347 		if (!audio_pause_in(audio_file_in, A_Pause))
348 			error(Ebadarg);
349 		if (!audio_flush(audio_file_in, A_In))
350 			error(Ebadarg);
351 		if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
352 			error(Ebadarg);
353 	}
354 	poperror();
355 	qunlock(&inlock);
356 
357 	qlock(&outlock);
358 	if (waserror()) {
359 		qunlock(&outlock);
360 		nexterror();
361 	}
362 	if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
363 		if (!audio_pause_out(audio_file_out))
364 			error(Ebadarg);
365 		if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
366 			error(Ebadarg);
367 	}
368 	poperror();
369 	qunlock(&outlock);
370 
371 	tmpav.in.flags = 0;
372 	tmpav.out.flags = 0;
373 
374 	av = tmpav;
375 
376 	return count;
377 }
378 
379 
380 
381 static int
382 audio_set_blocking(int fd)
383 {
384 	int val;
385 
386 	if((val = fcntl(fd, F_GETFL, 0)) == -1)
387 		return 0;
388 
389 	val &= ~O_NONBLOCK;
390 
391 	if(fcntl(fd, F_SETFL, val) < 0)
392 		return 0;
393 
394 	return 1;
395 }
396 
397 static int
398 doioctl(int fd, int ctl, int *info)
399 {
400 	int status;
401 	osenter();
402 	status = ioctl(fd, ctl, info);  /* qlock and load general stuff */
403 	osleave();
404 	if (status < 0)
405 		print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
406 	return status;
407 }
408 
409 static int
410 choosefmt(Audio_d *i)
411 {
412 	int newbits, newenc;
413 
414 	newbits = i->bits;
415 	newenc = i->enc;
416 	switch (newenc) {
417 	case Audio_Alaw_Val:
418 		if (newbits == 8)
419 			return AFMT_A_LAW;
420 		break;
421 	case Audio_Ulaw_Val:
422 		if (newbits == 8)
423 			return AFMT_MU_LAW;
424 		break;
425 	case Audio_Pcm_Val:
426 		if (newbits == 8)
427 			return AFMT_U8;
428 		else if (newbits == 16)
429 			return AFMT_S16_LE;
430 		break;
431 	}
432 	return -1;
433 }
434 
435 static int
436 audio_set_info(int fd, Audio_d *i, int d)
437 {
438 	int status;
439 	int unequal_stereo = 0;
440 
441 	if(fd < 0)
442 		return 0;
443 
444 	/* fmt */
445 	if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
446 		int oldfmt, newfmt;
447 		oldfmt = AFMT_QUERY;
448 		if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
449 			return 0;
450 		if (debug)
451 			print("audio_set_info: current format 0x%.8lux\n", oldfmt);
452 		newfmt = choosefmt(i);
453 		if (debug)
454 			print("audio_set_info: new format 0x%.8lux\n", newfmt);
455 		if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
456 			return 0;
457 	}
458 
459 	/* channels */
460 	if(i->flags & AUDIO_CHAN_FLAG) {
461 		int channels = i->chan;
462 		if (debug)
463 			print("audio_set_info: new channels %d\n", channels);
464 		if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
465 			|| channels != i->chan)
466 			return 0;
467 	}
468 
469 	/* sample rate */
470 	if(i->flags & AUDIO_RATE_FLAG) {
471 		int speed = i->rate;
472 		if (debug)
473 			print("audio_set_info: new speed %d\n", speed);
474 		if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
475 			return 0;
476 	}
477 
478 	/* dev volume */
479 	if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
480 		int val;
481 		if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
482 			mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
483 		if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
484 			mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
485 		val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
486 		doioctl(fd, MIXER_WRITE(i->dev), &val);
487 	}
488 
489 	if (i->flags & AUDIO_DEV_FLAG) {
490 	}
491 
492 	return 1;
493 }
494 
495 void
496 audio_swap_endian(char *p, int n)
497 {
498 	int b;
499 
500 	while (n > 1) {
501 		b = p[0];
502 		p[0] = p[1];
503 		p[1] = b;
504 		p += 2;
505 		n -= 2;
506 	}
507 }
508 
509 static int
510 audio_pause_out(int fd)
511 {
512 	USED(fd);
513 	return 1;
514 }
515 
516 static int
517 audio_pause_in(int fd, int f)
518 {
519 	USED(fd);
520 	USED(f);
521 	return 1;
522 }
523 
524 static int
525 audio_flush(int fd, int d)
526 {
527 	int x;
528 	return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
529 }
530 
531 static int
532 audio_enforce(Audio_t *t)
533 {
534 	if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
535 		(t->in.rate != 8000 || t->in.chan != 1))
536 		 return 0;
537 	if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
538 		(t->out.rate != 8000 || t->out.chan != 1))
539 		 return 0;
540 	return 1;
541 }
542 
543 Audio_t*
544 getaudiodev(void)
545 {
546 	return &av;
547 }
548