diff options
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/s_audio_oss.c')
-rw-r--r-- | apps/plugins/pdbox/PDa/src/s_audio_oss.c | 1688 |
1 files changed, 1688 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/s_audio_oss.c b/apps/plugins/pdbox/PDa/src/s_audio_oss.c new file mode 100644 index 0000000000..efb15d1bc1 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/s_audio_oss.c | |||
@@ -0,0 +1,1688 @@ | |||
1 | /* Copyright (c) 1997-2003 Guenter Geiger, Miller Puckette, Larry Troxler, | ||
2 | * Winfried Ritsch, Karl MacMillan, and others. | ||
3 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL | ||
4 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ | ||
5 | |||
6 | /* this file inputs and outputs audio using the OSS API available on linux. */ | ||
7 | |||
8 | #ifdef USEAPI_OSS | ||
9 | |||
10 | #include <linux/soundcard.h> | ||
11 | |||
12 | #include "m_pd.h" | ||
13 | #include "s_stuff.h" | ||
14 | #include <errno.h> | ||
15 | #include <stdio.h> | ||
16 | #include <unistd.h> | ||
17 | #include <stdlib.h> | ||
18 | #include <string.h> | ||
19 | #include <sys/types.h> | ||
20 | #include <sys/time.h> | ||
21 | #include <sys/stat.h> | ||
22 | #include <sys/ioctl.h> | ||
23 | #include <fcntl.h> | ||
24 | #include <sched.h> | ||
25 | #include <sys/mman.h> | ||
26 | |||
27 | |||
28 | /* Defines */ | ||
29 | #define DEBUG(x) x | ||
30 | #define DEBUG2(x) {x;} | ||
31 | |||
32 | #define OSS_MAXCHPERDEV 32 /* max channels per OSS device */ | ||
33 | #define OSS_MAXDEV 4 /* maximum number of input or output devices */ | ||
34 | #define OSS_DEFFRAGSIZE 256 /* default log fragment size (frames) */ | ||
35 | #define OSS_DEFAUDIOBUF 40000 /* default audiobuffer, microseconds */ | ||
36 | #define OSS_DEFAULTCH 2 | ||
37 | #define RME_DEFAULTCH 8 /* need this even if RME undefined */ | ||
38 | typedef int16_t t_oss_int16; | ||
39 | typedef int32_t t_oss_int32; | ||
40 | #define OSS_MAXSAMPLEWIDTH sizeof(t_oss_int32) | ||
41 | #define OSS_BYTESPERCHAN(width) (DEFDACBLKSIZE * (width)) | ||
42 | #define OSS_XFERSAMPS(chans) (DEFDACBLKSIZE* (chans)) | ||
43 | #define OSS_XFERSIZE(chans, width) (DEFDACBLKSIZE * (chans) * (width)) | ||
44 | |||
45 | /* GLOBALS */ | ||
46 | static int linux_meters; /* true if we're metering */ | ||
47 | static float linux_inmax; /* max input amplitude */ | ||
48 | static float linux_outmax; /* max output amplitude */ | ||
49 | static int linux_fragsize = 0; /* for block mode; block size (sample frames) */ | ||
50 | |||
51 | /* our device handles */ | ||
52 | |||
53 | typedef struct _oss_dev | ||
54 | { | ||
55 | int d_fd; | ||
56 | unsigned int d_space; /* bytes available for writing/reading */ | ||
57 | int d_bufsize; /* total buffer size in blocks for this device */ | ||
58 | int d_dropcount; /* # of buffers to drop for resync (output only) */ | ||
59 | unsigned int d_nchannels; /* number of channels for this device */ | ||
60 | unsigned int d_bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */ | ||
61 | } t_oss_dev; | ||
62 | |||
63 | static t_oss_dev linux_dacs[OSS_MAXDEV]; | ||
64 | static t_oss_dev linux_adcs[OSS_MAXDEV]; | ||
65 | static int linux_noutdevs = 0; | ||
66 | static int linux_nindevs = 0; | ||
67 | |||
68 | /* exported variables */ | ||
69 | float sys_dacsr; | ||
70 | t_sample *sys_soundout; | ||
71 | t_sample *sys_soundin; | ||
72 | |||
73 | /* OSS-specific private variables */ | ||
74 | static int oss_blockmode = 1; /* flag to use "blockmode" */ | ||
75 | static int oss_32bit = 0; /* allow 23 bit transfers in OSS */ | ||
76 | static char ossdsp[] = "/dev/dsp%d"; | ||
77 | |||
78 | /* don't assume we can turn all 31 bits when doing float-to-fix; | ||
79 | otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. */ | ||
80 | #define FMAX 0x7ffff000 | ||
81 | #define CLIP32(x) (((x)>FMAX)?FMAX:((x) < -FMAX)?-FMAX:(x)) | ||
82 | |||
83 | |||
84 | /* ------------- private routines for all APIS ------------------- */ | ||
85 | |||
86 | static void linux_flush_all_underflows_to_zero(void) | ||
87 | { | ||
88 | /* | ||
89 | TODO: Implement similar thing for linux (GGeiger) | ||
90 | |||
91 | One day we will figure this out, I hope, because it | ||
92 | costs CPU time dearly on Intel - LT | ||
93 | */ | ||
94 | /* union fpc_csr f; | ||
95 | f.fc_word = get_fpc_csr(); | ||
96 | f.fc_struct.flush = 1; | ||
97 | set_fpc_csr(f.fc_word); | ||
98 | */ | ||
99 | } | ||
100 | |||
101 | static int oss_ndev = 0; | ||
102 | |||
103 | /* find out how many OSS devices we have. Since this has to | ||
104 | open the devices to find out if they're there, we have | ||
105 | to be called before audio is actually started up. So we | ||
106 | cache the results, which in effect are the number of available | ||
107 | devices. */ | ||
108 | void oss_init(void) | ||
109 | { | ||
110 | int fd, i; | ||
111 | static int countedthem = 0; | ||
112 | if (countedthem) | ||
113 | return; | ||
114 | for (i = 0; i < 10; i++) | ||
115 | { | ||
116 | char devname[100]; | ||
117 | if (i == 0) | ||
118 | strcpy(devname, "/dev/dsp"); | ||
119 | else sprintf(devname, "/dev/dsp%d", i); | ||
120 | if ( (fd = open(devname, O_WRONLY|O_NONBLOCK)) != -1) | ||
121 | { | ||
122 | oss_ndev++; | ||
123 | close(fd); | ||
124 | } | ||
125 | else break; | ||
126 | } | ||
127 | countedthem = 1; | ||
128 | } | ||
129 | |||
130 | |||
131 | void oss_set32bit( void) | ||
132 | { | ||
133 | oss_32bit = 1; | ||
134 | } | ||
135 | |||
136 | |||
137 | typedef struct _multidev { | ||
138 | int fd; | ||
139 | int channels; | ||
140 | int format; | ||
141 | } t_multidev; | ||
142 | |||
143 | int oss_reset(int fd) { | ||
144 | int err; | ||
145 | if ((err = ioctl(fd,SNDCTL_DSP_RESET)) < 0) | ||
146 | error("OSS: Could not reset"); | ||
147 | return err; | ||
148 | } | ||
149 | |||
150 | /* The AFMT_S32_BLOCKED format is not defined in standard linux kernels | ||
151 | but is proposed by Guenter Geiger to support extending OSS to handle | ||
152 | 32 bit sample. This is user in Geiger's OSS driver for RME Hammerfall. | ||
153 | I'm not clear why this isn't called AFMT_S32_[SLN]E... */ | ||
154 | |||
155 | #ifndef AFMT_S32_BLOCKED | ||
156 | #define AFMT_S32_BLOCKED 0x0000400 | ||
157 | #endif | ||
158 | |||
159 | void oss_configure(t_oss_dev *dev, int srate, int dac, int skipblocksize) | ||
160 | { /* IOhannes */ | ||
161 | int orig, param, nblk, fd = dev->d_fd, wantformat; | ||
162 | int nchannels = dev->d_nchannels; | ||
163 | int advwas = sys_schedadvance; | ||
164 | |||
165 | audio_buf_info ainfo; | ||
166 | |||
167 | /* IOhannes : | ||
168 | * pd is very likely to crash if different formats are used on | ||
169 | multiple soundcards | ||
170 | */ | ||
171 | |||
172 | /* set resolution - first try 4 byte samples */ | ||
173 | if (oss_32bit && (ioctl(fd,SNDCTL_DSP_GETFMTS,¶m) >= 0) && | ||
174 | (param & AFMT_S32_BLOCKED)) | ||
175 | { | ||
176 | wantformat = AFMT_S32_BLOCKED; | ||
177 | dev->d_bytespersamp = 4; | ||
178 | } | ||
179 | else | ||
180 | { | ||
181 | wantformat = AFMT_S16_NE; | ||
182 | dev->d_bytespersamp = 2; | ||
183 | } | ||
184 | param = wantformat; | ||
185 | |||
186 | if (sys_verbose) | ||
187 | post("bytes per sample = %d", dev->d_bytespersamp); | ||
188 | if (ioctl(fd, SNDCTL_DSP_SETFMT, ¶m) == -1) | ||
189 | fprintf(stderr,"OSS: Could not set DSP format\n"); | ||
190 | else if (wantformat != param) | ||
191 | fprintf(stderr,"OSS: DSP format: wanted %d, got %d\n", | ||
192 | wantformat, param); | ||
193 | |||
194 | /* sample rate */ | ||
195 | orig = param = srate; | ||
196 | if (ioctl(fd, SNDCTL_DSP_SPEED, ¶m) == -1) | ||
197 | fprintf(stderr,"OSS: Could not set sampling rate for device\n"); | ||
198 | else if( orig != param ) | ||
199 | fprintf(stderr,"OSS: sampling rate: wanted %d, got %d\n", | ||
200 | orig, param ); | ||
201 | |||
202 | if (oss_blockmode && !skipblocksize) | ||
203 | { | ||
204 | int fragbytes, logfragsize, nfragment; | ||
205 | /* setting fragment count and size. */ | ||
206 | if (!linux_fragsize) | ||
207 | { | ||
208 | linux_fragsize = OSS_DEFFRAGSIZE; | ||
209 | while (linux_fragsize > DEFDACBLKSIZE | ||
210 | && linux_fragsize * 4 > sys_advance_samples) | ||
211 | linux_fragsize = linux_fragsize/2; | ||
212 | } | ||
213 | |||
214 | /* post("adv_samples %d", sys_advance_samples); */ | ||
215 | nfragment = (sys_schedadvance * (44100. * 1.e-6)) / linux_fragsize; | ||
216 | |||
217 | fragbytes = linux_fragsize * (dev->d_bytespersamp * nchannels); | ||
218 | logfragsize = ilog2(fragbytes); | ||
219 | |||
220 | if (fragbytes != (1 << logfragsize)) | ||
221 | post("warning: OSS takes only power of 2 blocksize; using %d", | ||
222 | (1 << logfragsize)/(dev->d_bytespersamp * nchannels)); | ||
223 | if (sys_verbose) | ||
224 | post("setting nfrags = %d, fragsize %d\n", nfragment, fragbytes); | ||
225 | |||
226 | param = orig = (nfragment<<16) + logfragsize; | ||
227 | if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) | ||
228 | error("OSS: Could not set or read fragment size\n"); | ||
229 | if (param != orig) | ||
230 | { | ||
231 | nfragment = ((param >> 16) & 0xffff); | ||
232 | logfragsize = (param & 0xffff); | ||
233 | post("warning: actual fragments %d, blocksize %d", | ||
234 | nfragment, (1 << logfragsize)); | ||
235 | } | ||
236 | if (sys_verbose) | ||
237 | post("audiobuffer set to %d msec", (int)(0.001 * sys_schedadvance)); | ||
238 | } | ||
239 | if (dac) | ||
240 | { | ||
241 | /* use "free space" to learn the buffer size. Normally you | ||
242 | should set this to your own desired value; but this seems not | ||
243 | to be implemented uniformly across different sound cards. LATER | ||
244 | we should figure out what to do if the requested scheduler advance | ||
245 | is greater than this buffer size; for now, we just print something | ||
246 | out. */ | ||
247 | |||
248 | int defect; | ||
249 | if (ioctl(fd, SOUND_PCM_GETOSPACE,&ainfo) < 0) | ||
250 | fprintf(stderr,"OSS: ioctl on output device failed"); | ||
251 | dev->d_bufsize = ainfo.bytes; | ||
252 | |||
253 | defect = sys_advance_samples * (dev->d_bytespersamp * nchannels) | ||
254 | - dev->d_bufsize - OSS_XFERSIZE(nchannels, dev->d_bytespersamp); | ||
255 | if (defect > 0) | ||
256 | { | ||
257 | if (sys_verbose || defect > (dev->d_bufsize >> 2)) | ||
258 | fprintf(stderr, | ||
259 | "OSS: requested audio buffer size %d limited to %d\n", | ||
260 | sys_advance_samples * (dev->d_bytespersamp * nchannels), | ||
261 | dev->d_bufsize); | ||
262 | sys_advance_samples = | ||
263 | (dev->d_bufsize - OSS_XFERSAMPS(nchannels)) / | ||
264 | (dev->d_bytespersamp *nchannels); | ||
265 | } | ||
266 | } | ||
267 | } | ||
268 | |||
269 | static int oss_setchannels(int fd, int wantchannels, char *devname) | ||
270 | { /* IOhannes */ | ||
271 | int param = wantchannels; | ||
272 | |||
273 | while (param>1) { | ||
274 | int save = param; | ||
275 | if (ioctl(fd, SNDCTL_DSP_CHANNELS, ¶m) == -1) { | ||
276 | error("OSS: SNDCTL_DSP_CHANNELS failed %s",devname); | ||
277 | } else { | ||
278 | if (param == save) return (param); | ||
279 | } | ||
280 | param=save-1; | ||
281 | } | ||
282 | |||
283 | return (0); | ||
284 | } | ||
285 | |||
286 | #define O_AUDIOFLAG 0 /* O_NDELAY */ | ||
287 | |||
288 | int oss_open_audio(int nindev, int *indev, int nchin, int *chin, | ||
289 | int noutdev, int *outdev, int nchout, int *chout, int rate) | ||
290 | { /* IOhannes */ | ||
291 | int capabilities = 0; | ||
292 | int inchannels = 0, outchannels = 0; | ||
293 | char devname[20]; | ||
294 | int n, i, fd; | ||
295 | char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV]; | ||
296 | int num_devs = 0; | ||
297 | int wantmore=0; | ||
298 | int spread = 0; | ||
299 | audio_buf_info ainfo; | ||
300 | |||
301 | linux_nindevs = linux_noutdevs = 0; | ||
302 | |||
303 | |||
304 | /* mark input devices unopened */ | ||
305 | for (i = 0; i < OSS_MAXDEV; i++) | ||
306 | linux_adcs[i].d_fd = -1; | ||
307 | |||
308 | /* open output devices */ | ||
309 | wantmore=0; | ||
310 | if (noutdev < 0 || nindev < 0) | ||
311 | bug("linux_open_audio"); | ||
312 | |||
313 | for (n = 0; n < noutdev; n++) | ||
314 | { | ||
315 | int gotchans, j, inindex = -1; | ||
316 | int thisdevice = (outdev[n] >= 0 ? outdev[n] : n-1); | ||
317 | int wantchannels = (nchout>n) ? chout[n] : wantmore; | ||
318 | fd = -1; | ||
319 | if (!wantchannels) | ||
320 | goto end_out_loop; | ||
321 | |||
322 | if (thisdevice > 1) | ||
323 | sprintf(devname, "/dev/dsp%d", thisdevice-1); | ||
324 | else sprintf(devname, "/dev/dsp"); | ||
325 | |||
326 | /* search for input request for same device. Succeed only | ||
327 | if the number of channels matches. */ | ||
328 | for (j = 0; j < nindev; j++) | ||
329 | if (indev[j] == thisdevice && chin[j] == wantchannels) | ||
330 | inindex = j; | ||
331 | |||
332 | /* if the same device is requested for input and output, | ||
333 | try to open it read/write */ | ||
334 | if (inindex >= 0) | ||
335 | { | ||
336 | sys_setalarm(1000000); | ||
337 | if ((fd = open(devname, O_RDWR | O_AUDIOFLAG)) == -1) | ||
338 | { | ||
339 | post("%s (read/write): %s", devname, strerror(errno)); | ||
340 | post("(now will try write-only...)"); | ||
341 | } | ||
342 | else | ||
343 | { | ||
344 | if (sys_verbose) | ||
345 | post("opened %s for reading and writing\n", devname); | ||
346 | linux_adcs[inindex].d_fd = fd; | ||
347 | } | ||
348 | } | ||
349 | /* if that didn't happen or if it failed, try write-only */ | ||
350 | if (fd == -1) | ||
351 | { | ||
352 | sys_setalarm(1000000); | ||
353 | if ((fd = open(devname, O_WRONLY | O_AUDIOFLAG)) == -1) | ||
354 | { | ||
355 | post("%s (writeonly): %s", | ||
356 | devname, strerror(errno)); | ||
357 | break; | ||
358 | } | ||
359 | if (sys_verbose) | ||
360 | post("opened %s for writing only\n", devname); | ||
361 | } | ||
362 | if (ioctl(fd, SNDCTL_DSP_GETCAPS, &capabilities) == -1) | ||
363 | error("OSS: SNDCTL_DSP_GETCAPS failed %s", devname); | ||
364 | |||
365 | gotchans = oss_setchannels(fd, | ||
366 | (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, | ||
367 | devname); | ||
368 | |||
369 | if (sys_verbose) | ||
370 | post("opened audio output on %s; got %d channels", | ||
371 | devname, gotchans); | ||
372 | |||
373 | if (gotchans < 2) | ||
374 | { | ||
375 | /* can't even do stereo? just give up. */ | ||
376 | close(fd); | ||
377 | } | ||
378 | else | ||
379 | { | ||
380 | linux_dacs[linux_noutdevs].d_nchannels = gotchans; | ||
381 | linux_dacs[linux_noutdevs].d_fd = fd; | ||
382 | oss_configure(linux_dacs+linux_noutdevs, rate, 1, 0); | ||
383 | |||
384 | linux_noutdevs++; | ||
385 | outchannels += gotchans; | ||
386 | if (inindex >= 0) | ||
387 | { | ||
388 | linux_adcs[inindex].d_nchannels = gotchans; | ||
389 | chin[inindex] = gotchans; | ||
390 | } | ||
391 | } | ||
392 | /* LATER think about spreading large numbers of channels over | ||
393 | various dsp's and vice-versa */ | ||
394 | wantmore = wantchannels - gotchans; | ||
395 | end_out_loop: ; | ||
396 | } | ||
397 | |||
398 | /* open input devices */ | ||
399 | wantmore = 0; | ||
400 | for (n = 0; n < nindev; n++) | ||
401 | { | ||
402 | int gotchans=0; | ||
403 | int thisdevice = (indev[n] >= 0 ? indev[n] : n-1); | ||
404 | int wantchannels = (nchin>n)?chin[n]:wantmore; | ||
405 | int alreadyopened = 0; | ||
406 | if (!wantchannels) | ||
407 | goto end_in_loop; | ||
408 | |||
409 | if (thisdevice > 1) | ||
410 | sprintf(devname, "/dev/dsp%d", thisdevice - 1); | ||
411 | else sprintf(devname, "/dev/dsp"); | ||
412 | |||
413 | sys_setalarm(1000000); | ||
414 | |||
415 | /* perhaps it's already open from the above? */ | ||
416 | if (linux_dacs[n].d_fd >= 0) | ||
417 | { | ||
418 | fd = linux_dacs[n].d_fd; | ||
419 | alreadyopened = 1; | ||
420 | } | ||
421 | else | ||
422 | { | ||
423 | /* otherwise try to open it here. */ | ||
424 | if ((fd = open(devname, O_RDONLY | O_AUDIOFLAG)) == -1) | ||
425 | { | ||
426 | post("%s (readonly): %s", devname, strerror(errno)); | ||
427 | goto end_in_loop; | ||
428 | } | ||
429 | if (sys_verbose) | ||
430 | post("opened %s for reading only\n", devname); | ||
431 | } | ||
432 | linux_adcs[linux_nindevs].d_fd = fd; | ||
433 | gotchans = oss_setchannels(fd, | ||
434 | (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, | ||
435 | devname); | ||
436 | if (sys_verbose) | ||
437 | post("opened audio input device %s; got %d channels", | ||
438 | devname, gotchans); | ||
439 | |||
440 | if (gotchans < 1) | ||
441 | { | ||
442 | close(fd); | ||
443 | goto end_in_loop; | ||
444 | } | ||
445 | |||
446 | linux_adcs[linux_nindevs].d_nchannels = gotchans; | ||
447 | |||
448 | oss_configure(linux_adcs+linux_nindevs, rate, 0, alreadyopened); | ||
449 | |||
450 | inchannels += gotchans; | ||
451 | linux_nindevs++; | ||
452 | |||
453 | wantmore = wantchannels-gotchans; | ||
454 | /* LATER think about spreading large numbers of channels over | ||
455 | various dsp's and vice-versa */ | ||
456 | end_in_loop: ; | ||
457 | } | ||
458 | |||
459 | /* We have to do a read to start the engine. This is | ||
460 | necessary because sys_send_dacs waits until the input | ||
461 | buffer is filled and only reads on a filled buffer. | ||
462 | This is good, because it's a way to make sure that we | ||
463 | will not block. But I wonder why we only have to read | ||
464 | from one of the devices and not all of them??? */ | ||
465 | |||
466 | if (linux_nindevs) | ||
467 | { | ||
468 | if (sys_verbose) | ||
469 | fprintf(stderr,("OSS: issuing first ADC 'read' ... ")); | ||
470 | read(linux_adcs[0].d_fd, buf, | ||
471 | linux_adcs[0].d_bytespersamp * | ||
472 | linux_adcs[0].d_nchannels * DEFDACBLKSIZE); | ||
473 | if (sys_verbose) | ||
474 | fprintf(stderr, "...done.\n"); | ||
475 | } | ||
476 | sys_setalarm(0); | ||
477 | return (0); | ||
478 | } | ||
479 | |||
480 | void oss_close_audio( void) | ||
481 | { | ||
482 | int i; | ||
483 | for (i=0;i<linux_nindevs;i++) | ||
484 | close(linux_adcs[i].d_fd); | ||
485 | |||
486 | for (i=0;i<linux_noutdevs;i++) | ||
487 | close(linux_dacs[i].d_fd); | ||
488 | |||
489 | linux_nindevs = linux_noutdevs = 0; | ||
490 | } | ||
491 | |||
492 | static int linux_dacs_write(int fd,void* buf,long bytes) | ||
493 | { | ||
494 | return write(fd, buf, bytes); | ||
495 | } | ||
496 | |||
497 | static int linux_adcs_read(int fd,void* buf,long bytes) | ||
498 | { | ||
499 | return read(fd, buf, bytes); | ||
500 | } | ||
501 | |||
502 | /* query audio devices for "available" data size. */ | ||
503 | static void oss_calcspace(void) | ||
504 | { | ||
505 | int dev; | ||
506 | audio_buf_info ainfo; | ||
507 | for (dev=0; dev < linux_noutdevs; dev++) | ||
508 | { | ||
509 | if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) | ||
510 | fprintf(stderr,"OSS: ioctl on output device %d failed",dev); | ||
511 | linux_dacs[dev].d_space = ainfo.bytes; | ||
512 | } | ||
513 | |||
514 | for (dev = 0; dev < linux_nindevs; dev++) | ||
515 | { | ||
516 | if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE,&ainfo) < 0) | ||
517 | fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed", | ||
518 | dev, linux_adcs[dev].d_fd); | ||
519 | linux_adcs[dev].d_space = ainfo.bytes; | ||
520 | } | ||
521 | } | ||
522 | |||
523 | void linux_audiostatus(void) | ||
524 | { | ||
525 | int dev; | ||
526 | if (!oss_blockmode) | ||
527 | { | ||
528 | oss_calcspace(); | ||
529 | for (dev=0; dev < linux_noutdevs; dev++) | ||
530 | fprintf(stderr, "dac %d space %d\n", dev, linux_dacs[dev].d_space); | ||
531 | |||
532 | for (dev = 0; dev < linux_nindevs; dev++) | ||
533 | fprintf(stderr, "adc %d space %d\n", dev, linux_adcs[dev].d_space); | ||
534 | |||
535 | } | ||
536 | } | ||
537 | |||
538 | /* this call resyncs audio output and input which will cause discontinuities | ||
539 | in audio output and/or input. */ | ||
540 | |||
541 | static void oss_doresync( void) | ||
542 | { | ||
543 | int dev, zeroed = 0, wantsize; | ||
544 | char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV]; | ||
545 | audio_buf_info ainfo; | ||
546 | |||
547 | /* 1. if any input devices are ahead (have more than 1 buffer stored), | ||
548 | drop one or more buffers worth */ | ||
549 | for (dev = 0; dev < linux_nindevs; dev++) | ||
550 | { | ||
551 | if (linux_adcs[dev].d_space == 0) | ||
552 | { | ||
553 | linux_adcs_read(linux_adcs[dev].d_fd, buf, | ||
554 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
555 | linux_adcs[dev].d_bytespersamp)); | ||
556 | } | ||
557 | else while (linux_adcs[dev].d_space > | ||
558 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
559 | linux_adcs[dev].d_bytespersamp)) | ||
560 | { | ||
561 | linux_adcs_read(linux_adcs[dev].d_fd, buf, | ||
562 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
563 | linux_adcs[dev].d_bytespersamp)); | ||
564 | if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE, &ainfo) < 0) | ||
565 | { | ||
566 | fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed", | ||
567 | dev, linux_adcs[dev].d_fd); | ||
568 | break; | ||
569 | } | ||
570 | linux_adcs[dev].d_space = ainfo.bytes; | ||
571 | } | ||
572 | } | ||
573 | |||
574 | /* 2. if any output devices are behind, feed them zeros to catch them | ||
575 | up */ | ||
576 | for (dev = 0; dev < linux_noutdevs; dev++) | ||
577 | { | ||
578 | while (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - | ||
579 | sys_advance_samples * (linux_dacs[dev].d_nchannels * | ||
580 | linux_dacs[dev].d_bytespersamp)) | ||
581 | { | ||
582 | if (!zeroed) | ||
583 | { | ||
584 | unsigned int i; | ||
585 | for (i = 0; i < OSS_XFERSAMPS(linux_dacs[dev].d_nchannels); | ||
586 | i++) | ||
587 | buf[i] = 0; | ||
588 | zeroed = 1; | ||
589 | } | ||
590 | linux_dacs_write(linux_dacs[dev].d_fd, buf, | ||
591 | OSS_XFERSIZE(linux_dacs[dev].d_nchannels, | ||
592 | linux_dacs[dev].d_bytespersamp)); | ||
593 | if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) | ||
594 | { | ||
595 | fprintf(stderr, "OSS: ioctl on output device %d, fd %d failed", | ||
596 | dev, linux_dacs[dev].d_fd); | ||
597 | break; | ||
598 | } | ||
599 | linux_dacs[dev].d_space = ainfo.bytes; | ||
600 | } | ||
601 | } | ||
602 | /* 3. if any DAC devices are too far ahead, plan to drop the | ||
603 | number of frames which will let the others catch up. */ | ||
604 | for (dev = 0; dev < linux_noutdevs; dev++) | ||
605 | { | ||
606 | if (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - | ||
607 | (sys_advance_samples - 1) * linux_dacs[dev].d_nchannels * | ||
608 | linux_dacs[dev].d_bytespersamp) | ||
609 | { | ||
610 | linux_dacs[dev].d_dropcount = sys_advance_samples - 1 - | ||
611 | (linux_dacs[dev].d_space - linux_dacs[dev].d_bufsize) / | ||
612 | (linux_dacs[dev].d_nchannels * | ||
613 | linux_dacs[dev].d_bytespersamp) ; | ||
614 | } | ||
615 | else linux_dacs[dev].d_dropcount = 0; | ||
616 | } | ||
617 | } | ||
618 | |||
619 | int oss_send_dacs(void) | ||
620 | { | ||
621 | t_sample *fp1, *fp2; | ||
622 | long fill; | ||
623 | int i, j, dev, rtnval = SENDDACS_YES; | ||
624 | char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV]; | ||
625 | t_oss_int16 *sp; | ||
626 | t_oss_int32 *lp; | ||
627 | /* the maximum number of samples we should have in the ADC buffer */ | ||
628 | int idle = 0; | ||
629 | int thischan; | ||
630 | t_time timeref, timenow; | ||
631 | |||
632 | if (!linux_nindevs && !linux_noutdevs) | ||
633 | return (SENDDACS_NO); | ||
634 | |||
635 | if (!oss_blockmode) | ||
636 | { | ||
637 | /* determine whether we're idle. This is true if either (1) | ||
638 | some input device has less than one buffer to read or (2) some | ||
639 | output device has fewer than (sys_advance_samples) blocks buffered | ||
640 | already. */ | ||
641 | oss_calcspace(); | ||
642 | |||
643 | for (dev=0; dev < linux_noutdevs; dev++) | ||
644 | if (linux_dacs[dev].d_dropcount || | ||
645 | (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space > | ||
646 | sys_advance_samples * linux_dacs[dev].d_bytespersamp * | ||
647 | linux_dacs[dev].d_nchannels)) | ||
648 | idle = 1; | ||
649 | for (dev=0; dev < linux_nindevs; dev++) | ||
650 | if (linux_adcs[dev].d_space < | ||
651 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
652 | linux_adcs[dev].d_bytespersamp)) | ||
653 | idle = 1; | ||
654 | } | ||
655 | |||
656 | if (idle && !oss_blockmode) | ||
657 | { | ||
658 | /* sometimes---rarely---when the ADC available-byte-count is | ||
659 | zero, it's genuine, but usually it's because we're so | ||
660 | late that the ADC has overrun its entire kernel buffer. We | ||
661 | distinguish between the two by waiting 2 msec and asking again. | ||
662 | There should be an error flag we could check instead; look for this | ||
663 | someday... */ | ||
664 | for (dev = 0;dev < linux_nindevs; dev++) | ||
665 | if (linux_adcs[dev].d_space == 0) | ||
666 | { | ||
667 | audio_buf_info ainfo; | ||
668 | sys_microsleep(2000); | ||
669 | oss_calcspace(); | ||
670 | if (linux_adcs[dev].d_space != 0) continue; | ||
671 | |||
672 | /* here's the bad case. Give up and resync. */ | ||
673 | sys_log_error(ERR_DATALATE); | ||
674 | oss_doresync(); | ||
675 | return (SENDDACS_NO); | ||
676 | } | ||
677 | /* check for slippage between devices, either because | ||
678 | data got lost in the driver from a previous late condition, or | ||
679 | because the devices aren't synced. When we're idle, no | ||
680 | input device should have more than one buffer readable and | ||
681 | no output device should have less than sys_advance_samples-1 | ||
682 | */ | ||
683 | |||
684 | for (dev=0; dev < linux_noutdevs; dev++) | ||
685 | if (!linux_dacs[dev].d_dropcount && | ||
686 | (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space < | ||
687 | (sys_advance_samples - 2) * | ||
688 | (linux_dacs[dev].d_bytespersamp * | ||
689 | linux_dacs[dev].d_nchannels))) | ||
690 | goto badsync; | ||
691 | for (dev=0; dev < linux_nindevs; dev++) | ||
692 | if (linux_adcs[dev].d_space > 3 * | ||
693 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
694 | linux_adcs[dev].d_bytespersamp)) | ||
695 | goto badsync; | ||
696 | |||
697 | /* return zero to tell the scheduler we're idle. */ | ||
698 | return (SENDDACS_NO); | ||
699 | badsync: | ||
700 | sys_log_error(ERR_RESYNC); | ||
701 | oss_doresync(); | ||
702 | return (SENDDACS_NO); | ||
703 | |||
704 | } | ||
705 | |||
706 | |||
707 | /* do output */ | ||
708 | |||
709 | timeref = sys_getrealtime(); | ||
710 | for (dev=0, thischan = 0; dev < linux_noutdevs; dev++) | ||
711 | { | ||
712 | int nchannels = linux_dacs[dev].d_nchannels; | ||
713 | if (linux_dacs[dev].d_dropcount) | ||
714 | linux_dacs[dev].d_dropcount--; | ||
715 | else | ||
716 | { | ||
717 | if (linux_dacs[dev].d_bytespersamp == 4) | ||
718 | { | ||
719 | for (i = DEFDACBLKSIZE * nchannels, fp1 = sys_soundout + | ||
720 | DEFDACBLKSIZE*thischan, | ||
721 | lp = (t_oss_int32 *)buf; i--; fp1++, lp++) | ||
722 | { | ||
723 | t_sample f = SCALE32(*fp1); | ||
724 | *lp = (f >= 2147483647 ? 2147483647 : | ||
725 | (f < -2147483647 ? -2147483647 : f)); | ||
726 | } | ||
727 | } | ||
728 | else | ||
729 | { | ||
730 | for (i = DEFDACBLKSIZE, fp1 = sys_soundout + | ||
731 | DEFDACBLKSIZE*thischan, | ||
732 | sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) | ||
733 | { | ||
734 | for (j=0, fp2 = fp1; j<nchannels; j++, fp2 += DEFDACBLKSIZE) | ||
735 | { | ||
736 | int s = SCALE16(*fp2); | ||
737 | if (s > 32767) s = 32767; | ||
738 | else if (s < -32767) s = -32767; | ||
739 | sp[j] = s; | ||
740 | } | ||
741 | } | ||
742 | } | ||
743 | |||
744 | |||
745 | #if 0 | ||
746 | #define PR_S "%8d" | ||
747 | { | ||
748 | int nm = 64; | ||
749 | int* sp1 = buf; | ||
750 | post("dac:"); | ||
751 | while (nm > 0) | ||
752 | { | ||
753 | post(PR_S PR_S PR_S PR_S PR_S PR_S PR_S PR_S, | ||
754 | sp1[0], sp1[1], sp1[2], sp1[3], sp1[4], sp1[5], sp1[6], sp1[7]); | ||
755 | nm -= 8; | ||
756 | sp1 += 8; | ||
757 | } | ||
758 | } | ||
759 | #endif | ||
760 | linux_dacs_write(linux_dacs[dev].d_fd, buf, | ||
761 | OSS_XFERSIZE(nchannels, linux_dacs[dev].d_bytespersamp)); | ||
762 | |||
763 | #if 0 | ||
764 | if ((timenow = sys_getrealtime()) - timeref > 200) | ||
765 | { | ||
766 | post("dacslept %d",sys_getrealtime() - timeref); | ||
767 | if (!oss_blockmode) | ||
768 | sys_log_error(ERR_DACSLEPT); | ||
769 | else rtnval = SENDDACS_SLEPT; | ||
770 | } | ||
771 | #endif | ||
772 | timeref = timenow; | ||
773 | } | ||
774 | thischan += nchannels; | ||
775 | } | ||
776 | memset(sys_soundout, 0, | ||
777 | sys_outchannels * (sizeof(float) * DEFDACBLKSIZE)); | ||
778 | |||
779 | /* do input */ | ||
780 | |||
781 | for (dev = 0, thischan = 0; dev < linux_nindevs; dev++) | ||
782 | { | ||
783 | int nchannels = linux_adcs[dev].d_nchannels; | ||
784 | linux_adcs_read(linux_adcs[dev].d_fd, buf, | ||
785 | OSS_XFERSIZE(nchannels, linux_adcs[dev].d_bytespersamp)); | ||
786 | |||
787 | #if 0 | ||
788 | if ((timenow = sys_getrealtime()) - timeref > 200) | ||
789 | { | ||
790 | if (!oss_blockmode) | ||
791 | sys_log_error(ERR_ADCSLEPT); | ||
792 | else | ||
793 | rtnval = SENDDACS_SLEPT; | ||
794 | } | ||
795 | #endif | ||
796 | timeref = timenow; | ||
797 | |||
798 | if (linux_adcs[dev].d_bytespersamp == 4) | ||
799 | { | ||
800 | for (i = DEFDACBLKSIZE*nchannels, | ||
801 | fp1 = sys_soundin + thischan*DEFDACBLKSIZE, | ||
802 | lp = (t_oss_int32 *)buf; i--; fp1++, lp++) | ||
803 | { | ||
804 | *fp1 = ((t_sample)(*lp))*(t_sample)(1./2147483648.); | ||
805 | } | ||
806 | } | ||
807 | else | ||
808 | { | ||
809 | for (i = DEFDACBLKSIZE,fp1 = sys_soundin + thischan*DEFDACBLKSIZE, | ||
810 | sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) | ||
811 | { | ||
812 | for (j=0;j<sys_inchannels;j++) | ||
813 | fp1[j*DEFDACBLKSIZE] = INVSCALE16(sp[j]); | ||
814 | } | ||
815 | } | ||
816 | thischan += nchannels; | ||
817 | } | ||
818 | if (thischan != sys_inchannels) | ||
819 | bug("inchannels"); | ||
820 | return (rtnval); | ||
821 | } | ||
822 | |||
823 | void oss_listdevs( void) | ||
824 | { | ||
825 | post("device listing not implemented in OSS yet\n"); | ||
826 | } | ||
827 | |||
828 | void oss_getdevs(char *indevlist, int *nindevs, | ||
829 | char *outdevlist, int *noutdevs, int *canmulti, | ||
830 | int maxndev, int devdescsize) | ||
831 | { | ||
832 | int i, ndev; | ||
833 | *canmulti = 2; /* supports multiple devices */ | ||
834 | if ((ndev = oss_ndev) > maxndev) | ||
835 | ndev = maxndev; | ||
836 | for (i = 0; i < ndev; i++) | ||
837 | { | ||
838 | sprintf(indevlist + i * devdescsize, "OSS device #%d", i+1); | ||
839 | sprintf(outdevlist + i * devdescsize, "OSS device #%d", i+1); | ||
840 | } | ||
841 | *nindevs = *noutdevs = ndev; | ||
842 | } | ||
843 | |||
844 | #endif | ||
845 | /* Copyright (c) 1997-2003 Guenter Geiger, Miller Puckette, Larry Troxler, | ||
846 | * Winfried Ritsch, Karl MacMillan, and others. | ||
847 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL | ||
848 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ | ||
849 | |||
850 | /* this file inputs and outputs audio using the OSS API available on linux. */ | ||
851 | |||
852 | #ifdef USEAPI_OSS | ||
853 | |||
854 | #include <linux/soundcard.h> | ||
855 | |||
856 | #include "m_pd.h" | ||
857 | #include "s_stuff.h" | ||
858 | #include <errno.h> | ||
859 | #include <stdio.h> | ||
860 | #include <unistd.h> | ||
861 | #include <stdlib.h> | ||
862 | #include <string.h> | ||
863 | #include <sys/types.h> | ||
864 | #include <sys/time.h> | ||
865 | #include <sys/stat.h> | ||
866 | #include <sys/ioctl.h> | ||
867 | #include <fcntl.h> | ||
868 | #include <sched.h> | ||
869 | #include <sys/mman.h> | ||
870 | |||
871 | |||
872 | /* Defines */ | ||
873 | #define DEBUG(x) x | ||
874 | #define DEBUG2(x) {x;} | ||
875 | |||
876 | #define OSS_MAXCHPERDEV 32 /* max channels per OSS device */ | ||
877 | #define OSS_MAXDEV 4 /* maximum number of input or output devices */ | ||
878 | #define OSS_DEFFRAGSIZE 256 /* default log fragment size (frames) */ | ||
879 | #define OSS_DEFAUDIOBUF 40000 /* default audiobuffer, microseconds */ | ||
880 | #define OSS_DEFAULTCH 2 | ||
881 | #define RME_DEFAULTCH 8 /* need this even if RME undefined */ | ||
882 | typedef int16_t t_oss_int16; | ||
883 | typedef int32_t t_oss_int32; | ||
884 | #define OSS_MAXSAMPLEWIDTH sizeof(t_oss_int32) | ||
885 | #define OSS_BYTESPERCHAN(width) (DEFDACBLKSIZE * (width)) | ||
886 | #define OSS_XFERSAMPS(chans) (DEFDACBLKSIZE* (chans)) | ||
887 | #define OSS_XFERSIZE(chans, width) (DEFDACBLKSIZE * (chans) * (width)) | ||
888 | |||
889 | /* GLOBALS */ | ||
890 | static int linux_meters; /* true if we're metering */ | ||
891 | static float linux_inmax; /* max input amplitude */ | ||
892 | static float linux_outmax; /* max output amplitude */ | ||
893 | static int linux_fragsize = 0; /* for block mode; block size (sample frames) */ | ||
894 | |||
895 | /* our device handles */ | ||
896 | |||
897 | typedef struct _oss_dev | ||
898 | { | ||
899 | int d_fd; | ||
900 | unsigned int d_space; /* bytes available for writing/reading */ | ||
901 | int d_bufsize; /* total buffer size in blocks for this device */ | ||
902 | int d_dropcount; /* # of buffers to drop for resync (output only) */ | ||
903 | unsigned int d_nchannels; /* number of channels for this device */ | ||
904 | unsigned int d_bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */ | ||
905 | } t_oss_dev; | ||
906 | |||
907 | static t_oss_dev linux_dacs[OSS_MAXDEV]; | ||
908 | static t_oss_dev linux_adcs[OSS_MAXDEV]; | ||
909 | static int linux_noutdevs = 0; | ||
910 | static int linux_nindevs = 0; | ||
911 | |||
912 | /* exported variables */ | ||
913 | float sys_dacsr; | ||
914 | t_sample *sys_soundout; | ||
915 | t_sample *sys_soundin; | ||
916 | |||
917 | /* OSS-specific private variables */ | ||
918 | static int oss_blockmode = 1; /* flag to use "blockmode" */ | ||
919 | static int oss_32bit = 0; /* allow 23 bit transfers in OSS */ | ||
920 | static char ossdsp[] = "/dev/dsp%d"; | ||
921 | |||
922 | /* don't assume we can turn all 31 bits when doing float-to-fix; | ||
923 | otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. */ | ||
924 | #define FMAX 0x7ffff000 | ||
925 | #define CLIP32(x) (((x)>FMAX)?FMAX:((x) < -FMAX)?-FMAX:(x)) | ||
926 | |||
927 | |||
928 | /* ------------- private routines for all APIS ------------------- */ | ||
929 | |||
930 | static void linux_flush_all_underflows_to_zero(void) | ||
931 | { | ||
932 | /* | ||
933 | TODO: Implement similar thing for linux (GGeiger) | ||
934 | |||
935 | One day we will figure this out, I hope, because it | ||
936 | costs CPU time dearly on Intel - LT | ||
937 | */ | ||
938 | /* union fpc_csr f; | ||
939 | f.fc_word = get_fpc_csr(); | ||
940 | f.fc_struct.flush = 1; | ||
941 | set_fpc_csr(f.fc_word); | ||
942 | */ | ||
943 | } | ||
944 | |||
945 | static int oss_ndev = 0; | ||
946 | |||
947 | /* find out how many OSS devices we have. Since this has to | ||
948 | open the devices to find out if they're there, we have | ||
949 | to be called before audio is actually started up. So we | ||
950 | cache the results, which in effect are the number of available | ||
951 | devices. */ | ||
952 | void oss_init(void) | ||
953 | { | ||
954 | int fd, i; | ||
955 | static int countedthem = 0; | ||
956 | if (countedthem) | ||
957 | return; | ||
958 | for (i = 0; i < 10; i++) | ||
959 | { | ||
960 | char devname[100]; | ||
961 | if (i == 0) | ||
962 | strcpy(devname, "/dev/dsp"); | ||
963 | else sprintf(devname, "/dev/dsp%d", i); | ||
964 | if ( (fd = open(devname, O_WRONLY|O_NONBLOCK)) != -1) | ||
965 | { | ||
966 | oss_ndev++; | ||
967 | close(fd); | ||
968 | } | ||
969 | else break; | ||
970 | } | ||
971 | countedthem = 1; | ||
972 | } | ||
973 | |||
974 | |||
975 | void oss_set32bit( void) | ||
976 | { | ||
977 | oss_32bit = 1; | ||
978 | } | ||
979 | |||
980 | |||
981 | typedef struct _multidev { | ||
982 | int fd; | ||
983 | int channels; | ||
984 | int format; | ||
985 | } t_multidev; | ||
986 | |||
987 | int oss_reset(int fd) { | ||
988 | int err; | ||
989 | if ((err = ioctl(fd,SNDCTL_DSP_RESET)) < 0) | ||
990 | error("OSS: Could not reset"); | ||
991 | return err; | ||
992 | } | ||
993 | |||
994 | /* The AFMT_S32_BLOCKED format is not defined in standard linux kernels | ||
995 | but is proposed by Guenter Geiger to support extending OSS to handle | ||
996 | 32 bit sample. This is user in Geiger's OSS driver for RME Hammerfall. | ||
997 | I'm not clear why this isn't called AFMT_S32_[SLN]E... */ | ||
998 | |||
999 | #ifndef AFMT_S32_BLOCKED | ||
1000 | #define AFMT_S32_BLOCKED 0x0000400 | ||
1001 | #endif | ||
1002 | |||
1003 | void oss_configure(t_oss_dev *dev, int srate, int dac, int skipblocksize) | ||
1004 | { /* IOhannes */ | ||
1005 | int orig, param, nblk, fd = dev->d_fd, wantformat; | ||
1006 | int nchannels = dev->d_nchannels; | ||
1007 | int advwas = sys_schedadvance; | ||
1008 | |||
1009 | audio_buf_info ainfo; | ||
1010 | |||
1011 | /* IOhannes : | ||
1012 | * pd is very likely to crash if different formats are used on | ||
1013 | multiple soundcards | ||
1014 | */ | ||
1015 | |||
1016 | /* set resolution - first try 4 byte samples */ | ||
1017 | if (oss_32bit && (ioctl(fd,SNDCTL_DSP_GETFMTS,¶m) >= 0) && | ||
1018 | (param & AFMT_S32_BLOCKED)) | ||
1019 | { | ||
1020 | wantformat = AFMT_S32_BLOCKED; | ||
1021 | dev->d_bytespersamp = 4; | ||
1022 | } | ||
1023 | else | ||
1024 | { | ||
1025 | wantformat = AFMT_S16_NE; | ||
1026 | dev->d_bytespersamp = 2; | ||
1027 | } | ||
1028 | param = wantformat; | ||
1029 | |||
1030 | if (sys_verbose) | ||
1031 | post("bytes per sample = %d", dev->d_bytespersamp); | ||
1032 | if (ioctl(fd, SNDCTL_DSP_SETFMT, ¶m) == -1) | ||
1033 | fprintf(stderr,"OSS: Could not set DSP format\n"); | ||
1034 | else if (wantformat != param) | ||
1035 | fprintf(stderr,"OSS: DSP format: wanted %d, got %d\n", | ||
1036 | wantformat, param); | ||
1037 | |||
1038 | /* sample rate */ | ||
1039 | orig = param = srate; | ||
1040 | if (ioctl(fd, SNDCTL_DSP_SPEED, ¶m) == -1) | ||
1041 | fprintf(stderr,"OSS: Could not set sampling rate for device\n"); | ||
1042 | else if( orig != param ) | ||
1043 | fprintf(stderr,"OSS: sampling rate: wanted %d, got %d\n", | ||
1044 | orig, param ); | ||
1045 | |||
1046 | if (oss_blockmode && !skipblocksize) | ||
1047 | { | ||
1048 | int fragbytes, logfragsize, nfragment; | ||
1049 | /* setting fragment count and size. */ | ||
1050 | if (!linux_fragsize) | ||
1051 | { | ||
1052 | linux_fragsize = OSS_DEFFRAGSIZE; | ||
1053 | while (linux_fragsize > DEFDACBLKSIZE | ||
1054 | && linux_fragsize * 4 > sys_advance_samples) | ||
1055 | linux_fragsize = linux_fragsize/2; | ||
1056 | } | ||
1057 | |||
1058 | /* post("adv_samples %d", sys_advance_samples); */ | ||
1059 | nfragment = (sys_schedadvance * (44100. * 1.e-6)) / linux_fragsize; | ||
1060 | |||
1061 | fragbytes = linux_fragsize * (dev->d_bytespersamp * nchannels); | ||
1062 | logfragsize = ilog2(fragbytes); | ||
1063 | |||
1064 | if (fragbytes != (1 << logfragsize)) | ||
1065 | post("warning: OSS takes only power of 2 blocksize; using %d", | ||
1066 | (1 << logfragsize)/(dev->d_bytespersamp * nchannels)); | ||
1067 | if (sys_verbose) | ||
1068 | post("setting nfrags = %d, fragsize %d\n", nfragment, fragbytes); | ||
1069 | |||
1070 | param = orig = (nfragment<<16) + logfragsize; | ||
1071 | if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) | ||
1072 | error("OSS: Could not set or read fragment size\n"); | ||
1073 | if (param != orig) | ||
1074 | { | ||
1075 | nfragment = ((param >> 16) & 0xffff); | ||
1076 | logfragsize = (param & 0xffff); | ||
1077 | post("warning: actual fragments %d, blocksize %d", | ||
1078 | nfragment, (1 << logfragsize)); | ||
1079 | } | ||
1080 | if (sys_verbose) | ||
1081 | post("audiobuffer set to %d msec", (int)(0.001 * sys_schedadvance)); | ||
1082 | } | ||
1083 | if (dac) | ||
1084 | { | ||
1085 | /* use "free space" to learn the buffer size. Normally you | ||
1086 | should set this to your own desired value; but this seems not | ||
1087 | to be implemented uniformly across different sound cards. LATER | ||
1088 | we should figure out what to do if the requested scheduler advance | ||
1089 | is greater than this buffer size; for now, we just print something | ||
1090 | out. */ | ||
1091 | |||
1092 | int defect; | ||
1093 | if (ioctl(fd, SOUND_PCM_GETOSPACE,&ainfo) < 0) | ||
1094 | fprintf(stderr,"OSS: ioctl on output device failed"); | ||
1095 | dev->d_bufsize = ainfo.bytes; | ||
1096 | |||
1097 | defect = sys_advance_samples * (dev->d_bytespersamp * nchannels) | ||
1098 | - dev->d_bufsize - OSS_XFERSIZE(nchannels, dev->d_bytespersamp); | ||
1099 | if (defect > 0) | ||
1100 | { | ||
1101 | if (sys_verbose || defect > (dev->d_bufsize >> 2)) | ||
1102 | fprintf(stderr, | ||
1103 | "OSS: requested audio buffer size %d limited to %d\n", | ||
1104 | sys_advance_samples * (dev->d_bytespersamp * nchannels), | ||
1105 | dev->d_bufsize); | ||
1106 | sys_advance_samples = | ||
1107 | (dev->d_bufsize - OSS_XFERSAMPS(nchannels)) / | ||
1108 | (dev->d_bytespersamp *nchannels); | ||
1109 | } | ||
1110 | } | ||
1111 | } | ||
1112 | |||
1113 | static int oss_setchannels(int fd, int wantchannels, char *devname) | ||
1114 | { /* IOhannes */ | ||
1115 | int param = wantchannels; | ||
1116 | |||
1117 | while (param>1) { | ||
1118 | int save = param; | ||
1119 | if (ioctl(fd, SNDCTL_DSP_CHANNELS, ¶m) == -1) { | ||
1120 | error("OSS: SNDCTL_DSP_CHANNELS failed %s",devname); | ||
1121 | } else { | ||
1122 | if (param == save) return (param); | ||
1123 | } | ||
1124 | param=save-1; | ||
1125 | } | ||
1126 | |||
1127 | return (0); | ||
1128 | } | ||
1129 | |||
1130 | #define O_AUDIOFLAG 0 /* O_NDELAY */ | ||
1131 | |||
1132 | int oss_open_audio(int nindev, int *indev, int nchin, int *chin, | ||
1133 | int noutdev, int *outdev, int nchout, int *chout, int rate) | ||
1134 | { /* IOhannes */ | ||
1135 | int capabilities = 0; | ||
1136 | int inchannels = 0, outchannels = 0; | ||
1137 | char devname[20]; | ||
1138 | int n, i, fd; | ||
1139 | char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV]; | ||
1140 | int num_devs = 0; | ||
1141 | int wantmore=0; | ||
1142 | int spread = 0; | ||
1143 | audio_buf_info ainfo; | ||
1144 | |||
1145 | linux_nindevs = linux_noutdevs = 0; | ||
1146 | |||
1147 | |||
1148 | /* mark input devices unopened */ | ||
1149 | for (i = 0; i < OSS_MAXDEV; i++) | ||
1150 | linux_adcs[i].d_fd = -1; | ||
1151 | |||
1152 | /* open output devices */ | ||
1153 | wantmore=0; | ||
1154 | if (noutdev < 0 || nindev < 0) | ||
1155 | bug("linux_open_audio"); | ||
1156 | |||
1157 | for (n = 0; n < noutdev; n++) | ||
1158 | { | ||
1159 | int gotchans, j, inindex = -1; | ||
1160 | int thisdevice = (outdev[n] >= 0 ? outdev[n] : n-1); | ||
1161 | int wantchannels = (nchout>n) ? chout[n] : wantmore; | ||
1162 | fd = -1; | ||
1163 | if (!wantchannels) | ||
1164 | goto end_out_loop; | ||
1165 | |||
1166 | if (thisdevice > 1) | ||
1167 | sprintf(devname, "/dev/dsp%d", thisdevice-1); | ||
1168 | else sprintf(devname, "/dev/dsp"); | ||
1169 | |||
1170 | /* search for input request for same device. Succeed only | ||
1171 | if the number of channels matches. */ | ||
1172 | for (j = 0; j < nindev; j++) | ||
1173 | if (indev[j] == thisdevice && chin[j] == wantchannels) | ||
1174 | inindex = j; | ||
1175 | |||
1176 | /* if the same device is requested for input and output, | ||
1177 | try to open it read/write */ | ||
1178 | if (inindex >= 0) | ||
1179 | { | ||
1180 | sys_setalarm(1000000); | ||
1181 | if ((fd = open(devname, O_RDWR | O_AUDIOFLAG)) == -1) | ||
1182 | { | ||
1183 | post("%s (read/write): %s", devname, strerror(errno)); | ||
1184 | post("(now will try write-only...)"); | ||
1185 | } | ||
1186 | else | ||
1187 | { | ||
1188 | if (sys_verbose) | ||
1189 | post("opened %s for reading and writing\n", devname); | ||
1190 | linux_adcs[inindex].d_fd = fd; | ||
1191 | } | ||
1192 | } | ||
1193 | /* if that didn't happen or if it failed, try write-only */ | ||
1194 | if (fd == -1) | ||
1195 | { | ||
1196 | sys_setalarm(1000000); | ||
1197 | if ((fd = open(devname, O_WRONLY | O_AUDIOFLAG)) == -1) | ||
1198 | { | ||
1199 | post("%s (writeonly): %s", | ||
1200 | devname, strerror(errno)); | ||
1201 | break; | ||
1202 | } | ||
1203 | if (sys_verbose) | ||
1204 | post("opened %s for writing only\n", devname); | ||
1205 | } | ||
1206 | if (ioctl(fd, SNDCTL_DSP_GETCAPS, &capabilities) == -1) | ||
1207 | error("OSS: SNDCTL_DSP_GETCAPS failed %s", devname); | ||
1208 | |||
1209 | gotchans = oss_setchannels(fd, | ||
1210 | (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, | ||
1211 | devname); | ||
1212 | |||
1213 | if (sys_verbose) | ||
1214 | post("opened audio output on %s; got %d channels", | ||
1215 | devname, gotchans); | ||
1216 | |||
1217 | if (gotchans < 2) | ||
1218 | { | ||
1219 | /* can't even do stereo? just give up. */ | ||
1220 | close(fd); | ||
1221 | } | ||
1222 | else | ||
1223 | { | ||
1224 | linux_dacs[linux_noutdevs].d_nchannels = gotchans; | ||
1225 | linux_dacs[linux_noutdevs].d_fd = fd; | ||
1226 | oss_configure(linux_dacs+linux_noutdevs, rate, 1, 0); | ||
1227 | |||
1228 | linux_noutdevs++; | ||
1229 | outchannels += gotchans; | ||
1230 | if (inindex >= 0) | ||
1231 | { | ||
1232 | linux_adcs[inindex].d_nchannels = gotchans; | ||
1233 | chin[inindex] = gotchans; | ||
1234 | } | ||
1235 | } | ||
1236 | /* LATER think about spreading large numbers of channels over | ||
1237 | various dsp's and vice-versa */ | ||
1238 | wantmore = wantchannels - gotchans; | ||
1239 | end_out_loop: ; | ||
1240 | } | ||
1241 | |||
1242 | /* open input devices */ | ||
1243 | wantmore = 0; | ||
1244 | for (n = 0; n < nindev; n++) | ||
1245 | { | ||
1246 | int gotchans=0; | ||
1247 | int thisdevice = (indev[n] >= 0 ? indev[n] : n-1); | ||
1248 | int wantchannels = (nchin>n)?chin[n]:wantmore; | ||
1249 | int alreadyopened = 0; | ||
1250 | if (!wantchannels) | ||
1251 | goto end_in_loop; | ||
1252 | |||
1253 | if (thisdevice > 1) | ||
1254 | sprintf(devname, "/dev/dsp%d", thisdevice - 1); | ||
1255 | else sprintf(devname, "/dev/dsp"); | ||
1256 | |||
1257 | sys_setalarm(1000000); | ||
1258 | |||
1259 | /* perhaps it's already open from the above? */ | ||
1260 | if (linux_dacs[n].d_fd >= 0) | ||
1261 | { | ||
1262 | fd = linux_dacs[n].d_fd; | ||
1263 | alreadyopened = 1; | ||
1264 | } | ||
1265 | else | ||
1266 | { | ||
1267 | /* otherwise try to open it here. */ | ||
1268 | if ((fd = open(devname, O_RDONLY | O_AUDIOFLAG)) == -1) | ||
1269 | { | ||
1270 | post("%s (readonly): %s", devname, strerror(errno)); | ||
1271 | goto end_in_loop; | ||
1272 | } | ||
1273 | if (sys_verbose) | ||
1274 | post("opened %s for reading only\n", devname); | ||
1275 | } | ||
1276 | linux_adcs[linux_nindevs].d_fd = fd; | ||
1277 | gotchans = oss_setchannels(fd, | ||
1278 | (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, | ||
1279 | devname); | ||
1280 | if (sys_verbose) | ||
1281 | post("opened audio input device %s; got %d channels", | ||
1282 | devname, gotchans); | ||
1283 | |||
1284 | if (gotchans < 1) | ||
1285 | { | ||
1286 | close(fd); | ||
1287 | goto end_in_loop; | ||
1288 | } | ||
1289 | |||
1290 | linux_adcs[linux_nindevs].d_nchannels = gotchans; | ||
1291 | |||
1292 | oss_configure(linux_adcs+linux_nindevs, rate, 0, alreadyopened); | ||
1293 | |||
1294 | inchannels += gotchans; | ||
1295 | linux_nindevs++; | ||
1296 | |||
1297 | wantmore = wantchannels-gotchans; | ||
1298 | /* LATER think about spreading large numbers of channels over | ||
1299 | various dsp's and vice-versa */ | ||
1300 | end_in_loop: ; | ||
1301 | } | ||
1302 | |||
1303 | /* We have to do a read to start the engine. This is | ||
1304 | necessary because sys_send_dacs waits until the input | ||
1305 | buffer is filled and only reads on a filled buffer. | ||
1306 | This is good, because it's a way to make sure that we | ||
1307 | will not block. But I wonder why we only have to read | ||
1308 | from one of the devices and not all of them??? */ | ||
1309 | |||
1310 | if (linux_nindevs) | ||
1311 | { | ||
1312 | if (sys_verbose) | ||
1313 | fprintf(stderr,("OSS: issuing first ADC 'read' ... ")); | ||
1314 | read(linux_adcs[0].d_fd, buf, | ||
1315 | linux_adcs[0].d_bytespersamp * | ||
1316 | linux_adcs[0].d_nchannels * DEFDACBLKSIZE); | ||
1317 | if (sys_verbose) | ||
1318 | fprintf(stderr, "...done.\n"); | ||
1319 | } | ||
1320 | sys_setalarm(0); | ||
1321 | return (0); | ||
1322 | } | ||
1323 | |||
1324 | void oss_close_audio( void) | ||
1325 | { | ||
1326 | int i; | ||
1327 | for (i=0;i<linux_nindevs;i++) | ||
1328 | close(linux_adcs[i].d_fd); | ||
1329 | |||
1330 | for (i=0;i<linux_noutdevs;i++) | ||
1331 | close(linux_dacs[i].d_fd); | ||
1332 | |||
1333 | linux_nindevs = linux_noutdevs = 0; | ||
1334 | } | ||
1335 | |||
1336 | static int linux_dacs_write(int fd,void* buf,long bytes) | ||
1337 | { | ||
1338 | return write(fd, buf, bytes); | ||
1339 | } | ||
1340 | |||
1341 | static int linux_adcs_read(int fd,void* buf,long bytes) | ||
1342 | { | ||
1343 | return read(fd, buf, bytes); | ||
1344 | } | ||
1345 | |||
1346 | /* query audio devices for "available" data size. */ | ||
1347 | static void oss_calcspace(void) | ||
1348 | { | ||
1349 | int dev; | ||
1350 | audio_buf_info ainfo; | ||
1351 | for (dev=0; dev < linux_noutdevs; dev++) | ||
1352 | { | ||
1353 | if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) | ||
1354 | fprintf(stderr,"OSS: ioctl on output device %d failed",dev); | ||
1355 | linux_dacs[dev].d_space = ainfo.bytes; | ||
1356 | } | ||
1357 | |||
1358 | for (dev = 0; dev < linux_nindevs; dev++) | ||
1359 | { | ||
1360 | if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE,&ainfo) < 0) | ||
1361 | fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed", | ||
1362 | dev, linux_adcs[dev].d_fd); | ||
1363 | linux_adcs[dev].d_space = ainfo.bytes; | ||
1364 | } | ||
1365 | } | ||
1366 | |||
1367 | void linux_audiostatus(void) | ||
1368 | { | ||
1369 | int dev; | ||
1370 | if (!oss_blockmode) | ||
1371 | { | ||
1372 | oss_calcspace(); | ||
1373 | for (dev=0; dev < linux_noutdevs; dev++) | ||
1374 | fprintf(stderr, "dac %d space %d\n", dev, linux_dacs[dev].d_space); | ||
1375 | |||
1376 | for (dev = 0; dev < linux_nindevs; dev++) | ||
1377 | fprintf(stderr, "adc %d space %d\n", dev, linux_adcs[dev].d_space); | ||
1378 | |||
1379 | } | ||
1380 | } | ||
1381 | |||
1382 | /* this call resyncs audio output and input which will cause discontinuities | ||
1383 | in audio output and/or input. */ | ||
1384 | |||
1385 | static void oss_doresync( void) | ||
1386 | { | ||
1387 | int dev, zeroed = 0, wantsize; | ||
1388 | char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV]; | ||
1389 | audio_buf_info ainfo; | ||
1390 | |||
1391 | /* 1. if any input devices are ahead (have more than 1 buffer stored), | ||
1392 | drop one or more buffers worth */ | ||
1393 | for (dev = 0; dev < linux_nindevs; dev++) | ||
1394 | { | ||
1395 | if (linux_adcs[dev].d_space == 0) | ||
1396 | { | ||
1397 | linux_adcs_read(linux_adcs[dev].d_fd, buf, | ||
1398 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1399 | linux_adcs[dev].d_bytespersamp)); | ||
1400 | } | ||
1401 | else while (linux_adcs[dev].d_space > | ||
1402 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1403 | linux_adcs[dev].d_bytespersamp)) | ||
1404 | { | ||
1405 | linux_adcs_read(linux_adcs[dev].d_fd, buf, | ||
1406 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1407 | linux_adcs[dev].d_bytespersamp)); | ||
1408 | if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE, &ainfo) < 0) | ||
1409 | { | ||
1410 | fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed", | ||
1411 | dev, linux_adcs[dev].d_fd); | ||
1412 | break; | ||
1413 | } | ||
1414 | linux_adcs[dev].d_space = ainfo.bytes; | ||
1415 | } | ||
1416 | } | ||
1417 | |||
1418 | /* 2. if any output devices are behind, feed them zeros to catch them | ||
1419 | up */ | ||
1420 | for (dev = 0; dev < linux_noutdevs; dev++) | ||
1421 | { | ||
1422 | while (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - | ||
1423 | sys_advance_samples * (linux_dacs[dev].d_nchannels * | ||
1424 | linux_dacs[dev].d_bytespersamp)) | ||
1425 | { | ||
1426 | if (!zeroed) | ||
1427 | { | ||
1428 | unsigned int i; | ||
1429 | for (i = 0; i < OSS_XFERSAMPS(linux_dacs[dev].d_nchannels); | ||
1430 | i++) | ||
1431 | buf[i] = 0; | ||
1432 | zeroed = 1; | ||
1433 | } | ||
1434 | linux_dacs_write(linux_dacs[dev].d_fd, buf, | ||
1435 | OSS_XFERSIZE(linux_dacs[dev].d_nchannels, | ||
1436 | linux_dacs[dev].d_bytespersamp)); | ||
1437 | if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) | ||
1438 | { | ||
1439 | fprintf(stderr, "OSS: ioctl on output device %d, fd %d failed", | ||
1440 | dev, linux_dacs[dev].d_fd); | ||
1441 | break; | ||
1442 | } | ||
1443 | linux_dacs[dev].d_space = ainfo.bytes; | ||
1444 | } | ||
1445 | } | ||
1446 | /* 3. if any DAC devices are too far ahead, plan to drop the | ||
1447 | number of frames which will let the others catch up. */ | ||
1448 | for (dev = 0; dev < linux_noutdevs; dev++) | ||
1449 | { | ||
1450 | if (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - | ||
1451 | (sys_advance_samples - 1) * linux_dacs[dev].d_nchannels * | ||
1452 | linux_dacs[dev].d_bytespersamp) | ||
1453 | { | ||
1454 | linux_dacs[dev].d_dropcount = sys_advance_samples - 1 - | ||
1455 | (linux_dacs[dev].d_space - linux_dacs[dev].d_bufsize) / | ||
1456 | (linux_dacs[dev].d_nchannels * | ||
1457 | linux_dacs[dev].d_bytespersamp) ; | ||
1458 | } | ||
1459 | else linux_dacs[dev].d_dropcount = 0; | ||
1460 | } | ||
1461 | } | ||
1462 | |||
1463 | int oss_send_dacs(void) | ||
1464 | { | ||
1465 | t_sample *fp1, *fp2; | ||
1466 | long fill; | ||
1467 | int i, j, dev, rtnval = SENDDACS_YES; | ||
1468 | char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV]; | ||
1469 | t_oss_int16 *sp; | ||
1470 | t_oss_int32 *lp; | ||
1471 | /* the maximum number of samples we should have in the ADC buffer */ | ||
1472 | int idle = 0; | ||
1473 | int thischan; | ||
1474 | t_time timeref, timenow; | ||
1475 | |||
1476 | if (!linux_nindevs && !linux_noutdevs) | ||
1477 | return (SENDDACS_NO); | ||
1478 | |||
1479 | if (!oss_blockmode) | ||
1480 | { | ||
1481 | /* determine whether we're idle. This is true if either (1) | ||
1482 | some input device has less than one buffer to read or (2) some | ||
1483 | output device has fewer than (sys_advance_samples) blocks buffered | ||
1484 | already. */ | ||
1485 | oss_calcspace(); | ||
1486 | |||
1487 | for (dev=0; dev < linux_noutdevs; dev++) | ||
1488 | if (linux_dacs[dev].d_dropcount || | ||
1489 | (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space > | ||
1490 | sys_advance_samples * linux_dacs[dev].d_bytespersamp * | ||
1491 | linux_dacs[dev].d_nchannels)) | ||
1492 | idle = 1; | ||
1493 | for (dev=0; dev < linux_nindevs; dev++) | ||
1494 | if (linux_adcs[dev].d_space < | ||
1495 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1496 | linux_adcs[dev].d_bytespersamp)) | ||
1497 | idle = 1; | ||
1498 | } | ||
1499 | |||
1500 | if (idle && !oss_blockmode) | ||
1501 | { | ||
1502 | /* sometimes---rarely---when the ADC available-byte-count is | ||
1503 | zero, it's genuine, but usually it's because we're so | ||
1504 | late that the ADC has overrun its entire kernel buffer. We | ||
1505 | distinguish between the two by waiting 2 msec and asking again. | ||
1506 | There should be an error flag we could check instead; look for this | ||
1507 | someday... */ | ||
1508 | for (dev = 0;dev < linux_nindevs; dev++) | ||
1509 | if (linux_adcs[dev].d_space == 0) | ||
1510 | { | ||
1511 | audio_buf_info ainfo; | ||
1512 | sys_microsleep(2000); | ||
1513 | oss_calcspace(); | ||
1514 | if (linux_adcs[dev].d_space != 0) continue; | ||
1515 | |||
1516 | /* here's the bad case. Give up and resync. */ | ||
1517 | sys_log_error(ERR_DATALATE); | ||
1518 | oss_doresync(); | ||
1519 | return (SENDDACS_NO); | ||
1520 | } | ||
1521 | /* check for slippage between devices, either because | ||
1522 | data got lost in the driver from a previous late condition, or | ||
1523 | because the devices aren't synced. When we're idle, no | ||
1524 | input device should have more than one buffer readable and | ||
1525 | no output device should have less than sys_advance_samples-1 | ||
1526 | */ | ||
1527 | |||
1528 | for (dev=0; dev < linux_noutdevs; dev++) | ||
1529 | if (!linux_dacs[dev].d_dropcount && | ||
1530 | (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space < | ||
1531 | (sys_advance_samples - 2) * | ||
1532 | (linux_dacs[dev].d_bytespersamp * | ||
1533 | linux_dacs[dev].d_nchannels))) | ||
1534 | goto badsync; | ||
1535 | for (dev=0; dev < linux_nindevs; dev++) | ||
1536 | if (linux_adcs[dev].d_space > 3 * | ||
1537 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1538 | linux_adcs[dev].d_bytespersamp)) | ||
1539 | goto badsync; | ||
1540 | |||
1541 | /* return zero to tell the scheduler we're idle. */ | ||
1542 | return (SENDDACS_NO); | ||
1543 | badsync: | ||
1544 | sys_log_error(ERR_RESYNC); | ||
1545 | oss_doresync(); | ||
1546 | return (SENDDACS_NO); | ||
1547 | |||
1548 | } | ||
1549 | |||
1550 | |||
1551 | /* do output */ | ||
1552 | |||
1553 | timeref = sys_getrealtime(); | ||
1554 | for (dev=0, thischan = 0; dev < linux_noutdevs; dev++) | ||
1555 | { | ||
1556 | int nchannels = linux_dacs[dev].d_nchannels; | ||
1557 | if (linux_dacs[dev].d_dropcount) | ||
1558 | linux_dacs[dev].d_dropcount--; | ||
1559 | else | ||
1560 | { | ||
1561 | if (linux_dacs[dev].d_bytespersamp == 4) | ||
1562 | { | ||
1563 | for (i = DEFDACBLKSIZE * nchannels, fp1 = sys_soundout + | ||
1564 | DEFDACBLKSIZE*thischan, | ||
1565 | lp = (t_oss_int32 *)buf; i--; fp1++, lp++) | ||
1566 | { | ||
1567 | t_sample f = SCALE32(*fp1); | ||
1568 | *lp = (f >= 2147483647 ? 2147483647 : | ||
1569 | (f < -2147483647 ? -2147483647 : f)); | ||
1570 | } | ||
1571 | } | ||
1572 | else | ||
1573 | { | ||
1574 | for (i = DEFDACBLKSIZE, fp1 = sys_soundout + | ||
1575 | DEFDACBLKSIZE*thischan, | ||
1576 | sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) | ||
1577 | { | ||
1578 | for (j=0, fp2 = fp1; j<nchannels; j++, fp2 += DEFDACBLKSIZE) | ||
1579 | { | ||
1580 | int s = SCALE16(*fp2); | ||
1581 | if (s > 32767) s = 32767; | ||
1582 | else if (s < -32767) s = -32767; | ||
1583 | sp[j] = s; | ||
1584 | } | ||
1585 | } | ||
1586 | } | ||
1587 | |||
1588 | |||
1589 | #if 0 | ||
1590 | #define PR_S "%8d" | ||
1591 | { | ||
1592 | int nm = 64; | ||
1593 | int* sp1 = buf; | ||
1594 | post("dac:"); | ||
1595 | while (nm > 0) | ||
1596 | { | ||
1597 | post(PR_S PR_S PR_S PR_S PR_S PR_S PR_S PR_S, | ||
1598 | sp1[0], sp1[1], sp1[2], sp1[3], sp1[4], sp1[5], sp1[6], sp1[7]); | ||
1599 | nm -= 8; | ||
1600 | sp1 += 8; | ||
1601 | } | ||
1602 | } | ||
1603 | #endif | ||
1604 | linux_dacs_write(linux_dacs[dev].d_fd, buf, | ||
1605 | OSS_XFERSIZE(nchannels, linux_dacs[dev].d_bytespersamp)); | ||
1606 | |||
1607 | #if 0 | ||
1608 | if ((timenow = sys_getrealtime()) - timeref > 200) | ||
1609 | { | ||
1610 | post("dacslept %d",sys_getrealtime() - timeref); | ||
1611 | if (!oss_blockmode) | ||
1612 | sys_log_error(ERR_DACSLEPT); | ||
1613 | else rtnval = SENDDACS_SLEPT; | ||
1614 | } | ||
1615 | #endif | ||
1616 | timeref = timenow; | ||
1617 | } | ||
1618 | thischan += nchannels; | ||
1619 | } | ||
1620 | memset(sys_soundout, 0, | ||
1621 | sys_outchannels * (sizeof(float) * DEFDACBLKSIZE)); | ||
1622 | |||
1623 | /* do input */ | ||
1624 | |||
1625 | for (dev = 0, thischan = 0; dev < linux_nindevs; dev++) | ||
1626 | { | ||
1627 | int nchannels = linux_adcs[dev].d_nchannels; | ||
1628 | linux_adcs_read(linux_adcs[dev].d_fd, buf, | ||
1629 | OSS_XFERSIZE(nchannels, linux_adcs[dev].d_bytespersamp)); | ||
1630 | |||
1631 | #if 0 | ||
1632 | if ((timenow = sys_getrealtime()) - timeref > 200) | ||
1633 | { | ||
1634 | if (!oss_blockmode) | ||
1635 | sys_log_error(ERR_ADCSLEPT); | ||
1636 | else | ||
1637 | rtnval = SENDDACS_SLEPT; | ||
1638 | } | ||
1639 | #endif | ||
1640 | timeref = timenow; | ||
1641 | |||
1642 | if (linux_adcs[dev].d_bytespersamp == 4) | ||
1643 | { | ||
1644 | for (i = DEFDACBLKSIZE*nchannels, | ||
1645 | fp1 = sys_soundin + thischan*DEFDACBLKSIZE, | ||
1646 | lp = (t_oss_int32 *)buf; i--; fp1++, lp++) | ||
1647 | { | ||
1648 | *fp1 = ((t_sample)(*lp))*(t_sample)(1./2147483648.); | ||
1649 | } | ||
1650 | } | ||
1651 | else | ||
1652 | { | ||
1653 | for (i = DEFDACBLKSIZE,fp1 = sys_soundin + thischan*DEFDACBLKSIZE, | ||
1654 | sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) | ||
1655 | { | ||
1656 | for (j=0;j<sys_inchannels;j++) | ||
1657 | fp1[j*DEFDACBLKSIZE] = INVSCALE16(sp[j]); | ||
1658 | } | ||
1659 | } | ||
1660 | thischan += nchannels; | ||
1661 | } | ||
1662 | if (thischan != sys_inchannels) | ||
1663 | bug("inchannels"); | ||
1664 | return (rtnval); | ||
1665 | } | ||
1666 | |||
1667 | void oss_listdevs( void) | ||
1668 | { | ||
1669 | post("device listing not implemented in OSS yet\n"); | ||
1670 | } | ||
1671 | |||
1672 | void oss_getdevs(char *indevlist, int *nindevs, | ||
1673 | char *outdevlist, int *noutdevs, int *canmulti, | ||
1674 | int maxndev, int devdescsize) | ||
1675 | { | ||
1676 | int i, ndev; | ||
1677 | *canmulti = 2; /* supports multiple devices */ | ||
1678 | if ((ndev = oss_ndev) > maxndev) | ||
1679 | ndev = maxndev; | ||
1680 | for (i = 0; i < ndev; i++) | ||
1681 | { | ||
1682 | sprintf(indevlist + i * devdescsize, "OSS device #%d", i+1); | ||
1683 | sprintf(outdevlist + i * devdescsize, "OSS device #%d", i+1); | ||
1684 | } | ||
1685 | *nindevs = *noutdevs = ndev; | ||
1686 | } | ||
1687 | |||
1688 | #endif | ||