1 #include "dat.h"
2 #include "fns.h"
3 #include "error.h"
4 #include "audio.h"
5 #include <sys/ioctl.h>
6 #include <sys/soundcard.h>
7
8 #define Audio_Mic_Val SOUND_MIXER_MIC
9 #define Audio_Linein_Val SOUND_MIXER_LINE
10
11 #define Audio_Speaker_Val SOUND_MIXER_PCM // SOUND_MIXER_VOLUME
12 #define Audio_Headphone_Val SOUND_MIXER_ALTPCM
13 #define Audio_Lineout_Val SOUND_MIXER_CD
14
15 #define Audio_Pcm_Val AFMT_S16_LE
16 #define Audio_Ulaw_Val AFMT_MU_LAW
17 #define Audio_Alaw_Val AFMT_A_LAW
18
19 #include "audio-tbls.c"
20 #define min(a,b) ((a) < (b) ? (a) : (b))
21
22 #define DEVAUDIO "/dev/dsp"
23 #define DEVMIXER "/dev/mixer"
24
25 #define DPRINT if(1)print
26
27 enum {
28 A_Pause,
29 A_UnPause,
30 A_In,
31 A_Out,
32 };
33
34 static struct {
35 int data; /* dsp data fd */
36 int ctl; /* mixer fd */
37 int pause;
38 QLock lk;
39 } afd = {.data = -1, .ctl = -1, .pause =A_UnPause };
40
41 static Audio_t av;
42 static QLock inlock;
43 static QLock outlock;
44
45 static int audio_open(int);
46 static int audio_pause(int, int);
47 static int audio_set_info(int, Audio_d*, int);
48
49 Audio_t*
getaudiodev(void)50 getaudiodev(void)
51 {
52 return &av;
53 }
54
55 void
audio_file_init(void)56 audio_file_init(void)
57 {
58 audio_info_init(&av);
59 }
60
61 void
audio_file_open(Chan * c,int omode)62 audio_file_open(Chan *c, int omode)
63 {
64 USED(c);
65 DPRINT("audio_file_open %d %d %x\n", afd.data, afd.ctl, omode);
66 qlock(&afd.lk);
67 if(waserror()){
68 qunlock(&afd.lk);
69 nexterror();
70 }
71 if(afd.data >= 0)
72 error(Einuse);
73 if(afd.ctl < 0){
74 afd.ctl = open(DEVMIXER, ORDWR);
75 if(afd.ctl < 0)
76 oserror();
77 }
78 afd.data = audio_open(omode);
79 if(afd.data < 0)
80 oserror();
81 poperror();
82 qunlock(&afd.lk);
83 }
84
85 void
audio_file_close(Chan * c)86 audio_file_close(Chan *c)
87 {
88 USED(c);
89 DPRINT("audio_file_close %d %d\n", afd.data, afd.ctl);
90 qlock(&afd.lk);
91 if(waserror()){
92 qunlock(&afd.lk);
93 nexterror();
94 }
95 close(afd.data);
96 afd.data = -1;
97 qunlock(&afd.lk);
98 poperror();
99 }
100
101 long
audio_file_read(Chan * c,void * va,long count,vlong offset)102 audio_file_read(Chan *c, void *va, long count, vlong offset)
103 {
104 long ba, status, chunk, total;
105
106 USED(c);
107 USED(offset);
108 DPRINT("audio_file_read %d %d\n", afd.data, afd.ctl);
109 qlock(&inlock);
110 if(waserror()){
111 qunlock(&inlock);
112 nexterror();
113 }
114
115 if(afd.data < 0)
116 error(Eperm);
117
118 /* check block alignment */
119 ba = av.in.bits * av.in.chan / Bits_Per_Byte;
120
121 if(count % ba)
122 error(Ebadarg);
123
124 if(!audio_pause(afd.data, A_UnPause))
125 error(Eio);
126
127 total = 0;
128 while(total < count){
129 chunk = count - total;
130 status = read (afd.data, va + total, chunk);
131 if(status < 0)
132 error(Eio);
133 total += status;
134 }
135
136 if(total != count)
137 error(Eio);
138
139 poperror();
140 qunlock(&inlock);
141
142 return count;
143 }
144
145 long
audio_file_write(Chan * c,void * va,long count,vlong offset)146 audio_file_write(Chan *c, void *va, long count, vlong offset)
147 {
148 long status = -1;
149 long ba, total, chunk, bufsz;
150
151 USED(c);
152 USED(offset);
153 DPRINT("audio_file_write %d %d\n", afd.data, afd.ctl);
154 qlock(&outlock);
155 if(waserror()){
156 qunlock(&outlock);
157 nexterror();
158 }
159
160 if(afd.data < 0)
161 error(Eperm);
162
163 /* check block alignment */
164 ba = av.out.bits * av.out.chan / Bits_Per_Byte;
165
166 if(count % ba)
167 error(Ebadarg);
168
169 total = 0;
170 bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
171
172 if(bufsz == 0)
173 error(Ebadarg);
174
175 while(total < count){
176 chunk = min(bufsz, count - total);
177 status = write(afd.data, va, chunk);
178 if(status <= 0)
179 error(Eio);
180 total += status;
181 }
182
183 poperror();
184 qunlock(&outlock);
185
186 return count;
187 }
188
189 long
audio_ctl_write(Chan * c,void * va,long count,vlong offset)190 audio_ctl_write(Chan *c, void *va, long count, vlong offset)
191 {
192 Audio_t tmpav = av;
193 int tfd;
194
195 USED(c);
196 USED(offset);
197 tmpav.in.flags = 0;
198 tmpav.out.flags = 0;
199
200 DPRINT ("audio_ctl_write %X %X\n", afd.data, afd.ctl);
201 if(!audioparse(va, count, &tmpav))
202 error(Ebadarg);
203
204 if(!canqlock(&inlock))
205 error("device busy");
206 if(waserror()){
207 qunlock(&inlock);
208 nexterror();
209 }
210 if(!canqlock(&outlock))
211 error("device busy");
212 if(waserror()){
213 qunlock(&outlock);
214 nexterror();
215 }
216
217 /* DEVAUDIO needs to be open to issue an ioctl */
218 tfd = afd.data;
219 if(tfd < 0){
220 tfd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
221 if(tfd < 0)
222 oserror();
223 }
224 if(waserror()){
225 if(tfd != afd.data)
226 close(tfd);
227 nexterror();
228 }
229
230 if(tmpav.in.flags & AUDIO_MOD_FLAG){
231 if(!audio_pause(tfd, A_Pause))
232 error(Ebadarg);
233 if(!audio_set_info(tfd, &tmpav.in, A_In))
234 error(Ebadarg);
235 }
236
237 poperror();
238 if(tfd != afd.data)
239 close(tfd);
240
241 tmpav.in.flags = 0;
242 av = tmpav;
243
244 poperror();
245 qunlock(&outlock);
246 poperror();
247 qunlock(&inlock);
248 return count;
249 }
250
251 /* Linux/OSS specific stuff */
252
253 static int
choosefmt(Audio_d * i)254 choosefmt(Audio_d *i)
255 {
256 switch(i->bits){
257 case 8:
258 switch(i->enc){
259 case Audio_Alaw_Val:
260 return AFMT_A_LAW;
261 case Audio_Ulaw_Val:
262 return AFMT_MU_LAW;
263 case Audio_Pcm_Val:
264 return AFMT_U8;
265 }
266 break;
267 case 16:
268 if(i->enc == Audio_Pcm_Val)
269 return AFMT_S16_LE;
270 break;
271 }
272 return -1;
273 }
274
275 static int
setvolume(int fd,int what,int left,int right)276 setvolume(int fd, int what, int left, int right)
277 {
278 int can, v;
279
280 if(ioctl(fd, SOUND_MIXER_READ_DEVMASK, &can) < 0)
281 can = ~0;
282
283 DPRINT("setvolume fd%d %X can mix 0x%X (mask %X)\n", fd, what, (can & (1<<what)), can);
284 if(!(can & (1<<what)))
285 return 0;
286 v = left | (right<<8);
287 if(ioctl(afd.ctl, MIXER_WRITE(what), &v) < 0)
288 return 0;
289 return 1;
290 }
291
292 static int
audio_set_info(int fd,Audio_d * i,int d)293 audio_set_info(int fd, Audio_d *i, int d)
294 {
295 int status, arg;
296 int oldfmt, newfmt;
297
298 USED(d);
299 DPRINT("audio_set_info (%d) %d %d\n", fd, afd.data, afd.ctl);
300 if(fd < 0)
301 return 0;
302
303 /* sample rate */
304 if(i->flags & AUDIO_RATE_FLAG){
305 arg = i->rate;
306 if(ioctl(fd, SNDCTL_DSP_SPEED, &arg) < 0)
307 return 0;
308 }
309
310 /* channels */
311 if(i->flags & AUDIO_CHAN_FLAG){
312 arg = i->chan;
313 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
314 return 0;
315 }
316
317 /* precision */
318 if(i->flags & AUDIO_BITS_FLAG){
319 arg = i->bits;
320 if(ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &arg) < 0)
321 return 0;
322 }
323
324 /* encoding */
325 if(i->flags & AUDIO_ENC_FLAG){
326 ioctl(fd, SNDCTL_DSP_GETFMTS, &oldfmt);
327
328 newfmt = choosefmt(i);
329 if(newfmt < 0)
330 return 0;
331 if(newfmt != oldfmt){
332 status = ioctl(fd, SNDCTL_DSP_SETFMT, &arg);
333 DPRINT ("enc oldfmt newfmt %x status %d\n", oldfmt, newfmt, status);
334 }
335 }
336
337 /* dev volume */
338 if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG))
339 return setvolume(afd.ctl, i->dev, i->left, i->right);
340
341 return 1;
342 }
343
344 static int
audio_set_blocking(int fd)345 audio_set_blocking(int fd)
346 {
347 int val;
348
349 if((val = fcntl(fd, F_GETFL, 0)) == -1)
350 return 0;
351
352 val &= ~O_NONBLOCK;
353
354 if(fcntl(fd, F_SETFL, val) < 0)
355 return 0;
356
357 return 1;
358 }
359
360 static int
audio_open(int omode)361 audio_open(int omode)
362 {
363 int fd;
364
365 /* open non-blocking in case someone already has it open */
366 /* otherwise we would block until they close! */
367 switch (omode){
368 case OREAD:
369 fd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
370 break;
371 case OWRITE:
372 fd = open(DEVAUDIO, O_WRONLY|O_NONBLOCK);
373 break;
374 case ORDWR:
375 fd = open(DEVAUDIO, O_RDWR|O_NONBLOCK);
376 break;
377 }
378
379 DPRINT("audio_open %d\n", fd);
380 if(fd < 0)
381 oserror();
382
383 /* change device to be blocking */
384 if(!audio_set_blocking(fd)){
385 close(fd);
386 error("cannot set blocking mode");
387 }
388
389 if(!audio_pause(fd, A_Pause)){
390 close(fd);
391 error(Eio);
392 }
393
394 /* set audio info */
395 av.in.flags = ~0;
396 av.out.flags = ~0;
397
398 if(!audio_set_info(fd, &av.in, A_In)){
399 close(fd);
400 error(Ebadarg);
401 }
402
403 av.in.flags = 0;
404
405 /* tada, we're open, blocking, paused and flushed */
406 return fd;
407 }
408
409 static int
dspsync(int fd)410 dspsync(int fd)
411 {
412 return ioctl(fd, SNDCTL_DSP_RESET, NULL) >= 0 &&
413 ioctl(fd, SNDCTL_DSP_SYNC, NULL) >= 0;
414 }
415
416 static int
audio_pause(int fd,int f)417 audio_pause(int fd, int f)
418 {
419 int status;
420
421 // DPRINT ("audio_pause (%d) %d %d\n", fd, afd.data, afd.ctl);
422 if(fd < 0)
423 return 0;
424 if(fd != afd.data)
425 return dspsync(fd);
426 qlock(&afd.lk);
427 if(afd.pause == f){
428 qunlock(&afd.lk);
429 return 1;
430 }
431 if(waserror()){
432 qunlock(&afd.lk);
433 nexterror();
434 }
435 status = dspsync(afd.data);
436 if(status)
437 afd.pause = f;
438 poperror();
439 qunlock(&afd.lk);
440 return status;
441 }
442