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. */
6
/* this file inputs and outputs audio using the OSS API available on linux. */
8
#if defined(__FreeBSD_kernel__)
9
# include <sys/soundcard.h>
11
# include <linux/soundcard.h>
21
#include <sys/types.h>
24
#include <sys/ioctl.h>
32
#define DEBUG2(x) {x;}
34
#define OSS_MAXCHPERDEV 32 /* max channels per OSS device */
35
#define OSS_MAXDEV 4 /* maximum number of input or output devices */
36
#define OSS_DEFFRAGSIZE 256 /* default log fragment size (frames) */
37
#define OSS_DEFAUDIOBUF 40000 /* default audiobuffer, microseconds */
38
#define OSS_DEFAULTCH 2
39
#define RME_DEFAULTCH 8 /* need this even if RME undefined */
40
typedef int16_t t_oss_int16;
41
typedef int32_t t_oss_int32;
42
#define OSS_MAXSAMPLEWIDTH sizeof(t_oss_int32)
43
#define OSS_BYTESPERCHAN(width) (DEFDACBLKSIZE * (width))
44
#define OSS_XFERSAMPS(chans) (DEFDACBLKSIZE* (chans))
45
#define OSS_XFERSIZE(chans, width) (DEFDACBLKSIZE * (chans) * (width))
48
static int linux_meters; /* true if we're metering */
49
static t_sample linux_inmax; /* max input amplitude */
50
static t_sample linux_outmax; /* max output amplitude */
51
static int linux_fragsize = 0; /* for block mode; block size (sample frames) */
52
extern int audio_blocksize; /* stolen from s_audio.c */
53
/* our device handles */
55
typedef struct _oss_dev
58
unsigned int d_space; /* bytes available for writing/reading */
59
int d_bufsize; /* total buffer size in blocks for this device */
60
int d_dropcount; /* # of buffers to drop for resync (output only) */
61
unsigned int d_nchannels; /* number of channels for this device */
62
unsigned int d_bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */
65
static t_oss_dev linux_dacs[OSS_MAXDEV];
66
static t_oss_dev linux_adcs[OSS_MAXDEV];
67
static int linux_noutdevs = 0;
68
static int linux_nindevs = 0;
70
/* exported variables */
72
t_sample *sys_soundout;
73
t_sample *sys_soundin;
75
/* OSS-specific private variables */
76
static int oss_blockmode = 1; /* flag to use "blockmode" */
77
static char ossdsp[] = "/dev/dsp%d";
79
/* don't assume we can turn all 31 bits when doing float-to-fix;
80
otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. */
81
#define FMAX 0x7ffff000
82
#define CLIP32(x) (((x)>FMAX)?FMAX:((x) < -FMAX)?-FMAX:(x))
84
/* ---------------- public routines ----------------------- */
86
static int oss_ndev = 0;
88
/* find out how many OSS devices we have. Since this has to
89
open the devices to find out if they're there, we have
90
to be called before audio is actually started up. So we
91
cache the results, which in effect are the number of available
96
static int countedthem = 0;
99
for (i = 0; i < 10; i++)
103
strcpy(devname, "/dev/dsp");
104
else sprintf(devname, "/dev/dsp%d", i);
105
if ( (fd = open(devname, O_WRONLY|O_NONBLOCK)) != -1)
115
typedef struct _multidev {
121
int oss_reset(int fd) {
123
if ((err = ioctl(fd,SNDCTL_DSP_RESET)) < 0)
124
error("OSS: Could not reset");
128
void oss_configure(t_oss_dev *dev, int srate, int dac, int skipblocksize,
129
int suggestedblocksize)
131
int orig, param, nblk, fd = dev->d_fd, wantformat;
132
int nchannels = dev->d_nchannels;
133
int advwas = sys_schedadvance;
135
audio_buf_info ainfo;
137
/* we only know how to do 2 byte samples */
138
wantformat = AFMT_S16_NE;
139
dev->d_bytespersamp = 2;
143
if (ioctl(fd, SNDCTL_DSP_SETFMT, ¶m) == -1)
144
fprintf(stderr,"OSS: Could not set DSP format\n");
145
else if (wantformat != param)
146
fprintf(stderr,"OSS: DSP format: wanted %d, got %d\n",
150
orig = param = srate;
151
if (ioctl(fd, SNDCTL_DSP_SPEED, ¶m) == -1)
152
fprintf(stderr,"OSS: Could not set sampling rate for device\n");
153
else if( orig != param )
154
fprintf(stderr,"OSS: sampling rate: wanted %d, got %d\n",
157
if (oss_blockmode && !skipblocksize)
159
int fragbytes, logfragsize, nfragment;
160
/* setting fragment count and size. */
161
linux_fragsize = suggestedblocksize;
164
linux_fragsize = OSS_DEFFRAGSIZE;
165
while (linux_fragsize > DEFDACBLKSIZE
166
&& linux_fragsize * 6 > sys_advance_samples)
167
linux_fragsize = linux_fragsize/2;
169
/* post("adv_samples %d", sys_advance_samples); */
170
nfragment = (sys_schedadvance * (44100. * 1.e-6)) / linux_fragsize;
172
fragbytes = linux_fragsize * (dev->d_bytespersamp * nchannels);
173
logfragsize = ilog2(fragbytes);
175
if (fragbytes != (1 << logfragsize))
176
post("warning: OSS takes only power of 2 blocksize; using %d",
177
(1 << logfragsize)/(dev->d_bytespersamp * nchannels));
179
post("setting nfrags = %d, fragsize %d\n", nfragment, fragbytes);
181
param = orig = (nfragment<<16) + logfragsize;
182
if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, ¶m) == -1)
183
error("OSS: Could not set or read fragment size\n");
186
nfragment = ((param >> 16) & 0xffff);
187
logfragsize = (param & 0xffff);
188
post("warning: actual fragments %d, blocksize %d",
189
nfragment, (1 << logfragsize));
192
post("audiobuffer set to %d msec", (int)(0.001 * sys_schedadvance));
197
/* use "free space" to learn the buffer size. Normally you
198
should set this to your own desired value; but this seems not
199
to be implemented uniformly across different sound cards. LATER
200
we should figure out what to do if the requested scheduler advance
201
is greater than this buffer size; for now, we just print something
205
if (ioctl(fd, SOUND_PCM_GETOSPACE,&ainfo) < 0)
206
fprintf(stderr,"OSS: ioctl on output device failed");
207
dev->d_bufsize = ainfo.bytes;
209
defect = sys_advance_samples * (dev->d_bytespersamp * nchannels)
210
- dev->d_bufsize - OSS_XFERSIZE(nchannels, dev->d_bytespersamp);
213
if (sys_verbose || defect > (dev->d_bufsize >> 2))
215
"OSS: requested audio buffer size %d limited to %d\n",
216
sys_advance_samples * (dev->d_bytespersamp * nchannels),
218
sys_advance_samples =
219
(dev->d_bufsize - OSS_XFERSAMPS(nchannels)) /
220
(dev->d_bytespersamp *nchannels);
225
static int oss_setchannels(int fd, int wantchannels, char *devname)
229
post("setchan %d", wantchannels);
230
if (ioctl(fd, SNDCTL_DSP_CHANNELS, ¶m) == -1)
233
error("OSS: SOUND_DSP_READ_CHANNELS failed %s", devname);
238
post("channels originally %d for %s", param, devname);
239
if (param == wantchannels)
242
post("number of channels doesn't need setting\n");
243
return (wantchannels);
246
param = wantchannels;
251
if (ioctl(fd, SNDCTL_DSP_CHANNELS, ¶m) == -1)
252
error("OSS: SNDCTL_DSP_CHANNELS failed %s",devname);
253
else if (param == save)
261
#define O_AUDIOFLAG O_NDELAY
263
int oss_open_audio(int nindev, int *indev, int nchin, int *chin,
264
int noutdev, int *outdev, int nchout, int *chout, int rate,
267
int capabilities = 0;
268
int inchannels = 0, outchannels = 0;
271
char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV];
275
audio_buf_info ainfo;
277
linux_nindevs = linux_noutdevs = 0;
278
/* mark devices unopened */
279
for (i = 0; i < OSS_MAXDEV; i++)
280
linux_adcs[i].d_fd = linux_dacs[i].d_fd = -1;
282
/* open output devices */
284
if (noutdev < 0 || nindev < 0)
285
bug("linux_open_audio");
287
for (n = 0; n < noutdev; n++)
289
int gotchans, j, inindex = -1;
290
int thisdevice = (outdev[n] >= 0 ? outdev[n] : 0);
291
int wantchannels = (nchout>n) ? chout[n] : wantmore;
297
sprintf(devname, "/dev/dsp%d", thisdevice);
298
else sprintf(devname, "/dev/dsp");
299
/* search for input request for same device. Succeed only
300
if the number of channels matches. */
301
for (j = 0; j < nindev; j++)
302
if (indev[j] == thisdevice && chin[j] == wantchannels)
305
/* if the same device is requested for input and output,
306
try to open it read/write */
309
sys_setalarm(1000000);
310
if ((fd = open(devname, O_RDWR | O_AUDIOFLAG)) == -1)
312
post("%s (read/write): %s", devname, strerror(errno));
313
post("(now will try write-only...)");
317
if (fcntl(fd, F_SETFD, 1) < 0)
318
post("couldn't set close-on-exec flag on audio");
319
if ((flags = fcntl(fd, F_GETFL)) < 0)
320
post("couldn't get audio device flags");
321
else if (fcntl(fd, F_SETFL, flags & (!O_NDELAY)) < 0)
322
post("couldn't set audio device flags");
324
post("opened %s for reading and writing\n", devname);
325
linux_adcs[inindex].d_fd = fd;
328
/* if that didn't happen or if it failed, try write-only */
331
sys_setalarm(1000000);
332
if ((fd = open(devname, O_WRONLY | O_AUDIOFLAG)) == -1)
334
post("%s (writeonly): %s",
335
devname, strerror(errno));
338
if (fcntl(fd, F_SETFD, 1) < 0)
339
post("couldn't set close-on-exec flag on audio");
340
if ((flags = fcntl(fd, F_GETFL)) < 0)
341
post("couldn't get audio device flags");
342
else if (fcntl(fd, F_SETFL, flags & (!O_NDELAY)) < 0)
343
post("couldn't set audio device flags");
345
post("opened %s for writing only\n", devname);
347
if (ioctl(fd, SNDCTL_DSP_GETCAPS, &capabilities) == -1)
348
error("OSS: SNDCTL_DSP_GETCAPS failed %s", devname);
350
gotchans = oss_setchannels(fd,
351
(wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels,
355
post("opened audio output on %s; got %d channels",
360
/* can't even do stereo? just give up. */
365
linux_dacs[linux_noutdevs].d_nchannels = gotchans;
366
linux_dacs[linux_noutdevs].d_fd = fd;
367
oss_configure(linux_dacs+linux_noutdevs, rate, 1, 0, blocksize);
370
outchannels += gotchans;
373
linux_adcs[inindex].d_nchannels = gotchans;
374
chin[inindex] = gotchans;
377
/* LATER think about spreading large numbers of channels over
378
various dsp's and vice-versa */
379
wantmore = wantchannels - gotchans;
383
/* open input devices */
385
for (n = 0; n < nindev; n++)
388
int thisdevice = (indev[n] >= 0 ? indev[n] : 0);
389
int wantchannels = (nchin>n)?chin[n]:wantmore;
390
int alreadyopened = 0;
395
sprintf(devname, "/dev/dsp%d", thisdevice);
396
else sprintf(devname, "/dev/dsp");
398
sys_setalarm(1000000);
400
/* perhaps it's already open from the above? */
401
if (linux_adcs[n].d_fd >= 0)
403
fd = linux_adcs[n].d_fd;
406
post("already opened it");
410
/* otherwise try to open it here. */
411
if ((fd = open(devname, O_RDONLY | O_AUDIOFLAG)) == -1)
413
post("%s (readonly): %s", devname, strerror(errno));
416
if (fcntl(fd, F_SETFD, 1) < 0)
417
post("couldn't set close-on-exec flag on audio");
418
if ((flags = fcntl(fd, F_GETFL)) < 0)
419
post("couldn't get audio device flags");
420
else if (fcntl(fd, F_SETFL, flags & (!O_NDELAY)) < 0)
421
post("couldn't set audio device flags");
423
post("opened %s for reading only\n", devname);
425
linux_adcs[linux_nindevs].d_fd = fd;
427
gotchans = oss_setchannels(fd,
428
(wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels,
431
post("opened audio input device %s; got %d channels",
440
linux_adcs[linux_nindevs].d_nchannels = gotchans;
442
oss_configure(linux_adcs+linux_nindevs, rate, 0, alreadyopened,
445
inchannels += gotchans;
448
wantmore = wantchannels-gotchans;
449
/* LATER think about spreading large numbers of channels over
450
various dsp's and vice-versa */
454
/* We have to do a read to start the engine. This is
455
necessary because sys_send_dacs waits until the input
456
buffer is filled and only reads on a filled buffer.
457
This is good, because it's a way to make sure that we
458
will not block. But I wonder why we only have to read
459
from one of the devices and not all of them??? */
464
fprintf(stderr,("OSS: issuing first ADC 'read' ... "));
465
read(linux_adcs[0].d_fd, buf,
466
linux_adcs[0].d_bytespersamp *
467
linux_adcs[0].d_nchannels * DEFDACBLKSIZE);
469
fprintf(stderr, "...done.\n");
471
/* now go and fill all the output buffers. */
472
for (i = 0; i < linux_noutdevs; i++)
475
memset(buf, 0, linux_dacs[i].d_bytespersamp *
476
linux_dacs[i].d_nchannels * DEFDACBLKSIZE);
477
for (j = 0; j < sys_advance_samples/DEFDACBLKSIZE; j++)
478
write(linux_dacs[i].d_fd, buf,
479
linux_dacs[i].d_bytespersamp *
480
linux_dacs[i].d_nchannels * DEFDACBLKSIZE);
483
sys_inchannels = inchannels;
484
sys_outchannels = outchannels;
488
void oss_close_audio( void)
491
for (i=0;i<linux_nindevs;i++)
492
close(linux_adcs[i].d_fd);
494
for (i=0;i<linux_noutdevs;i++)
495
close(linux_dacs[i].d_fd);
497
linux_nindevs = linux_noutdevs = 0;
500
static int linux_dacs_write(int fd,void* buf,long bytes)
502
return write(fd, buf, bytes);
505
static int linux_adcs_read(int fd,void* buf,long bytes)
507
return read(fd, buf, bytes);
510
/* query audio devices for "available" data size. */
511
static void oss_calcspace(void)
514
audio_buf_info ainfo;
515
for (dev=0; dev < linux_noutdevs; dev++)
517
if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0)
518
fprintf(stderr,"OSS: ioctl on output device %d failed",dev);
519
linux_dacs[dev].d_space = ainfo.bytes;
522
for (dev = 0; dev < linux_nindevs; dev++)
524
if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE,&ainfo) < 0)
525
fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed",
526
dev, linux_adcs[dev].d_fd);
527
linux_adcs[dev].d_space = ainfo.bytes;
531
void linux_audiostatus(void)
537
for (dev=0; dev < linux_noutdevs; dev++)
538
fprintf(stderr, "dac %d space %d\n", dev, linux_dacs[dev].d_space);
540
for (dev = 0; dev < linux_nindevs; dev++)
541
fprintf(stderr, "adc %d space %d\n", dev, linux_adcs[dev].d_space);
546
/* this call resyncs audio output and input which will cause discontinuities
547
in audio output and/or input. */
549
static void oss_doresync( void)
551
int dev, zeroed = 0, wantsize;
552
char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV];
553
audio_buf_info ainfo;
555
/* 1. if any input devices are ahead (have more than 1 buffer stored),
556
drop one or more buffers worth */
557
for (dev = 0; dev < linux_nindevs; dev++)
559
if (linux_adcs[dev].d_space == 0)
561
linux_adcs_read(linux_adcs[dev].d_fd, buf,
562
OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
563
linux_adcs[dev].d_bytespersamp));
565
else while (linux_adcs[dev].d_space >
566
OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
567
linux_adcs[dev].d_bytespersamp))
569
linux_adcs_read(linux_adcs[dev].d_fd, buf,
570
OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
571
linux_adcs[dev].d_bytespersamp));
572
if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE, &ainfo) < 0)
574
fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed",
575
dev, linux_adcs[dev].d_fd);
578
linux_adcs[dev].d_space = ainfo.bytes;
582
/* 2. if any output devices are behind, feed them zeros to catch them
584
for (dev = 0; dev < linux_noutdevs; dev++)
586
while (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize -
587
sys_advance_samples * (linux_dacs[dev].d_nchannels *
588
linux_dacs[dev].d_bytespersamp))
593
for (i = 0; i < OSS_XFERSAMPS(linux_dacs[dev].d_nchannels);
598
linux_dacs_write(linux_dacs[dev].d_fd, buf,
599
OSS_XFERSIZE(linux_dacs[dev].d_nchannels,
600
linux_dacs[dev].d_bytespersamp));
601
if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0)
603
fprintf(stderr, "OSS: ioctl on output device %d, fd %d failed",
604
dev, linux_dacs[dev].d_fd);
607
linux_dacs[dev].d_space = ainfo.bytes;
610
/* 3. if any DAC devices are too far ahead, plan to drop the
611
number of frames which will let the others catch up. */
612
for (dev = 0; dev < linux_noutdevs; dev++)
614
if (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize -
615
(sys_advance_samples - 1) * linux_dacs[dev].d_nchannels *
616
linux_dacs[dev].d_bytespersamp)
618
linux_dacs[dev].d_dropcount = sys_advance_samples - 1 -
619
(linux_dacs[dev].d_space - linux_dacs[dev].d_bufsize) /
620
(linux_dacs[dev].d_nchannels *
621
linux_dacs[dev].d_bytespersamp) ;
623
else linux_dacs[dev].d_dropcount = 0;
627
int oss_send_dacs(void)
631
int i, j, dev, rtnval = SENDDACS_YES;
632
char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV];
635
/* the maximum number of samples we should have in the ADC buffer */
638
double timeref, timenow;
640
if (!linux_nindevs && !linux_noutdevs)
641
return (SENDDACS_NO);
645
/* determine whether we're idle. This is true if either (1)
646
some input device has less than one buffer to read or (2) some
647
output device has fewer than (sys_advance_samples) blocks buffered
651
for (dev=0; dev < linux_noutdevs; dev++)
652
if (linux_dacs[dev].d_dropcount ||
653
(linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space >
654
sys_advance_samples * linux_dacs[dev].d_bytespersamp *
655
linux_dacs[dev].d_nchannels))
657
for (dev=0; dev < linux_nindevs; dev++)
658
if (linux_adcs[dev].d_space <
659
OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
660
linux_adcs[dev].d_bytespersamp))
664
if (idle && !oss_blockmode)
666
/* sometimes---rarely---when the ADC available-byte-count is
667
zero, it's genuine, but usually it's because we're so
668
late that the ADC has overrun its entire kernel buffer. We
669
distinguish between the two by waiting 2 msec and asking again.
670
There should be an error flag we could check instead; look for this
672
for (dev = 0;dev < linux_nindevs; dev++)
673
if (linux_adcs[dev].d_space == 0)
675
audio_buf_info ainfo;
676
sys_microsleep(2000);
678
if (linux_adcs[dev].d_space != 0) continue;
680
/* here's the bad case. Give up and resync. */
681
sys_log_error(ERR_DATALATE);
683
return (SENDDACS_NO);
685
/* check for slippage between devices, either because
686
data got lost in the driver from a previous late condition, or
687
because the devices aren't synced. When we're idle, no
688
input device should have more than one buffer readable and
689
no output device should have less than sys_advance_samples-1
692
for (dev=0; dev < linux_noutdevs; dev++)
693
if (!linux_dacs[dev].d_dropcount &&
694
(linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space <
695
(sys_advance_samples - 2) *
696
(linux_dacs[dev].d_bytespersamp *
697
linux_dacs[dev].d_nchannels)))
699
for (dev=0; dev < linux_nindevs; dev++)
700
if (linux_adcs[dev].d_space > 3 *
701
OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
702
linux_adcs[dev].d_bytespersamp))
705
/* return zero to tell the scheduler we're idle. */
706
return (SENDDACS_NO);
708
sys_log_error(ERR_RESYNC);
710
return (SENDDACS_NO);
716
timeref = sys_getrealtime();
717
for (dev=0, thischan = 0; dev < linux_noutdevs; dev++)
719
int nchannels = linux_dacs[dev].d_nchannels;
720
if (linux_dacs[dev].d_dropcount)
721
linux_dacs[dev].d_dropcount--;
724
if (linux_dacs[dev].d_bytespersamp == 2)
726
for (i = DEFDACBLKSIZE, fp1 = sys_soundout +
727
DEFDACBLKSIZE*thischan,
728
sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels)
730
for (j=0, fp2 = fp1; j<nchannels; j++, fp2 += DEFDACBLKSIZE)
732
int s = *fp2 * 32767.;
733
if (s > 32767) s = 32767;
734
else if (s < -32767) s = -32767;
739
linux_dacs_write(linux_dacs[dev].d_fd, buf,
740
OSS_XFERSIZE(nchannels, linux_dacs[dev].d_bytespersamp));
741
if ((timenow = sys_getrealtime()) - timeref > 0.002)
744
sys_log_error(ERR_DACSLEPT);
745
else rtnval = SENDDACS_SLEPT;
749
thischan += nchannels;
751
memset(sys_soundout, 0,
752
sys_outchannels * (sizeof(t_sample) * DEFDACBLKSIZE));
756
for (dev = 0, thischan = 0; dev < linux_nindevs; dev++)
758
int nchannels = linux_adcs[dev].d_nchannels;
759
linux_adcs_read(linux_adcs[dev].d_fd, buf,
760
OSS_XFERSIZE(nchannels, linux_adcs[dev].d_bytespersamp));
762
if ((timenow = sys_getrealtime()) - timeref > 0.002)
765
sys_log_error(ERR_ADCSLEPT);
767
rtnval = SENDDACS_SLEPT;
771
if (linux_adcs[dev].d_bytespersamp == 2)
773
for (i = DEFDACBLKSIZE,fp1 = sys_soundin + thischan*DEFDACBLKSIZE,
774
sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels)
776
for (j=0;j<nchannels;j++)
777
fp1[j*DEFDACBLKSIZE] = (float)sp[j]*(float)3.051850e-05;
780
thischan += nchannels;
785
void oss_getdevs(char *indevlist, int *nindevs,
786
char *outdevlist, int *noutdevs, int *canmulti,
787
int maxndev, int devdescsize)
790
*canmulti = 2; /* supports multiple devices */
791
if ((ndev = oss_ndev) > maxndev)
793
for (i = 0; i < ndev; i++)
795
sprintf(indevlist + i * devdescsize, "OSS device #%d", i+1);
796
sprintf(outdevlist + i * devdescsize, "OSS device #%d", i+1);
798
*nindevs = *noutdevs = ndev;