diff options
author | IOhannes m zmölnig <zmoelnig@users.sourceforge.net> | 2008-02-08 13:00:32 +0000 |
---|---|---|
committer | IOhannes m zmölnig <zmoelnig@users.sourceforge.net> | 2008-02-08 13:00:32 +0000 |
commit | 4d84d14ac1aa13958eaa2971b03f7f929a519105 (patch) | |
tree | 6579d3f2cea5410a10c4baac8d0f372fb0dff372 /desiredata/src/s_audio_oss.c | |
parent | b334d38aefbd8e0e159d7af6c20d63c5d2b64859 (diff) |
reorganized
svn path=/trunk/; revision=9400
Diffstat (limited to 'desiredata/src/s_audio_oss.c')
-rw-r--r-- | desiredata/src/s_audio_oss.c | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/desiredata/src/s_audio_oss.c b/desiredata/src/s_audio_oss.c new file mode 100644 index 00000000..d38051b7 --- /dev/null +++ b/desiredata/src/s_audio_oss.c @@ -0,0 +1,532 @@ +/* Copyright (c) 1997-2003 Guenter Geiger, Miller Puckette, Larry Troxler, +* Winfried Ritsch, Karl MacMillan, and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file inputs and outputs audio using the OSS API available on linux. */ +#include <linux/soundcard.h> + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "s_stuff.h" +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <sched.h> +#include <sys/mman.h> + +/* Defines */ +#define DEBUG(x) x +#define DEBUG2(x) {x;} + +#define OSS_MAXCHPERDEV 32 /* max channels per OSS device */ +#define OSS_MAXDEV 4 /* maximum number of input or output devices */ +#define OSS_DEFFRAGSIZE 256 /* default log fragment size (frames) */ +#define OSS_DEFAUDIOBUF 40000 /* default audiobuffer, microseconds */ +#define OSS_DEFAULTCH 2 +#define RME_DEFAULTCH 8 /* need this even if RME undefined */ +typedef int16_t t_oss_int16; +typedef int32_t t_oss_int32; +#define OSS_MAXSAMPLEWIDTH sizeof(t_oss_int32) +#define OSS_BYTESPERCHAN(width) (sys_dacblocksize * (width)) +#define OSS_XFERSAMPS(chans) (sys_dacblocksize* (chans)) +#define OSS_XFERSIZE(chans, width) (sys_dacblocksize * (chans) * (width)) + +static int linux_fragsize = 0; /* for block mode; block size (sample frames) */ + +/* our device handles */ +struct t_oss_dev { + int fd; + unsigned int space; /* bytes available for writing/reading */ + int bufsize; /* total buffer size in blocks for this device */ + int dropcount; /* # of buffers to drop for resync (output only) */ + unsigned int nchannels; /* number of channels for this device */ + unsigned int bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */ +}; + +static t_oss_dev linux_dacs[OSS_MAXDEV]; +static t_oss_dev linux_adcs[OSS_MAXDEV]; +static int linux_noutdevs = 0; +static int linux_nindevs = 0; + +/* OSS-specific private variables */ +static int oss_blockmode = 0; /* flag to use "blockmode" */ +static int oss_32bit = 0; /* allow 23 bit transfers in OSS */ + +/* don't assume we can turn all 31 bits when doing float-to-fix; + otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. */ +#define FMAX 0x7ffff000 +#define CLIP32(x) (((x)>FMAX)?FMAX:((x) < -FMAX)?-FMAX:(x)) + +/* ---------------- public routines ----------------------- */ +static int oss_ndev = 0; + +/* find out how many OSS devices we have. Since this has to + open the devices to find out if they're there, we have + to be called before audio is actually started up. So we + cache the results, which in effect are the number of available devices. */ +void oss_init() { + static int countedthem = 0; + if (countedthem) return; + for (int i=0; i<10; i++) { + char devname[100]; + if (i == 0) strcpy(devname, "/dev/dsp"); else sprintf(devname, "/dev/dsp%d", i); + int fd = open(devname, O_WRONLY|O_NONBLOCK); + if (fd<0) break; + oss_ndev++; + close(fd); + } + countedthem = 1; +} + +void oss_set32bit() {oss_32bit=1;} + +typedef struct _multidev { + int fd; + int channels; + int format; +} t_multidev; + +int oss_reset(int fd) { + int err = ioctl(fd,SNDCTL_DSP_RESET); + if (err<0) error("OSS: Could not reset"); + return err; +} + +/* The AFMT_S32_BLOCKED format is not defined in standard linux kernels + but is proposed by Guenter Geiger to support extending OSS to handle + 32 bit sample. This is user in Geiger's OSS driver for RME Hammerfall. + I'm not clear why this isn't called AFMT_S32_[SLN]E... */ + +#ifndef AFMT_S32_BLOCKED +#define AFMT_S32_BLOCKED 0x0000400 +#endif + +void oss_configure(t_oss_dev *dev, int srate, int dac, int skipblocksize) { + /* IOhannes */ + int orig, param, fd = dev->fd, wantformat; + int nchannels = dev->nchannels; + audio_buf_info ainfo; + /* IOhannes : pd is very likely to crash if different formats are used on multiple soundcards */ + /* set resolution - first try 4 byte samples */ + if (oss_32bit && (ioctl(fd,SNDCTL_DSP_GETFMTS,¶m) >= 0) && (param & AFMT_S32_BLOCKED)) { + wantformat = AFMT_S32_BLOCKED; + dev->bytespersamp = 4; + } else { + wantformat = AFMT_S16_NE; + dev->bytespersamp = 2; + } + param = wantformat; + + if (sys_verbose) post("bytes per sample = %d", dev->bytespersamp); + if (ioctl(fd, SNDCTL_DSP_SETFMT, ¶m) == -1) error("OSS: Could not set DSP format"); + else if (wantformat != param) error("OSS: DSP format: wanted %d, got %d", wantformat, param); + /* sample rate */ + orig = param = srate; + if (ioctl(fd, SNDCTL_DSP_SPEED, ¶m) == -1) error("OSS: Could not set sampling rate for device"); + else if (orig != param) error("OSS: sampling rate: wanted %d, got %d", orig, param ); + + if (oss_blockmode && !skipblocksize) { + int fragbytes, logfragsize, nfragment; + /* setting fragment count and size. */ + linux_fragsize = sys_blocksize; + if (!linux_fragsize) { + linux_fragsize = OSS_DEFFRAGSIZE; + while (linux_fragsize > sys_dacblocksize && linux_fragsize * 6 > sys_advance_samples) + linux_fragsize = linux_fragsize/2; + } + /* post("adv_samples %d", sys_advance_samples); */ + nfragment = int(sys_schedadvance * 44100.e-6 / linux_fragsize); + fragbytes = linux_fragsize * (dev->bytespersamp * nchannels); + logfragsize = ilog2(fragbytes); + if (fragbytes != (1 << logfragsize)) + post("warning: OSS takes only power of 2 blocksize; using %d", + (1 << logfragsize)/(dev->bytespersamp * nchannels)); + if (sys_verbose) post("setting nfrags = %d, fragsize %d", nfragment, fragbytes); + + param = orig = (nfragment<<16) + logfragsize; + if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) + error("OSS: Could not set or read fragment size"); + if (param != orig) { + nfragment = ((param >> 16) & 0xffff); + logfragsize = (param & 0xffff); + post("warning: actual fragments %d, blocksize %d", nfragment, 1<<logfragsize); + } + if (sys_verbose) post("audiobuffer set to %d msec", (int)(0.001 * sys_schedadvance)); + } + + if (dac) { + /* use "free space" to learn the buffer size. Normally you + should set this to your own desired value; but this seems not + to be implemented uniformly across different sound cards. LATER + we should figure out what to do if the requested scheduler advance + is greater than this buffer size; for now, we just print something + out. */ + int defect; + if (ioctl(fd, SOUND_PCM_GETOSPACE,&ainfo) < 0) error("OSS: ioctl on output device failed"); + dev->bufsize = ainfo.bytes; + defect = sys_advance_samples * (dev->bytespersamp * nchannels) + - dev->bufsize - OSS_XFERSIZE(nchannels, dev->bytespersamp); + if (defect > 0) { + if (sys_verbose || defect > (dev->bufsize >> 2)) + error("OSS: requested audio buffer size %d limited to %d", + sys_advance_samples * (dev->bytespersamp * nchannels), dev->bufsize); + sys_advance_samples = (dev->bufsize-OSS_XFERSAMPS(nchannels)) / (dev->bytespersamp*nchannels); + } + } +} + +static int oss_setchannels(int fd, int wantchannels, char *devname) { + /* IOhannes */ + int param = wantchannels; + while (param > 1) { + int save = param; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, ¶m) == -1) error("OSS: SNDCTL_DSP_CHANNELS failed %s",devname); + else if (param == save) return param; + param = save - 1; + } + return 0; +} + +#define O_AUDIOFLAG O_NDELAY +/* what's the deal with (!O_NDELAY) ? does it make sense to you? */ + +int oss_open_audio(int nindev, int *indev, int nchin, int *chin, + int noutdev, int *outdev, int nchout, int *chout, int rate, int bogus) +{ /* IOhannes */ + int capabilities = 0; + int inchannels = 0, outchannels = 0; + char devname[20]; + int n, i, fd, flags; + char buf[OSS_MAXSAMPLEWIDTH * sys_dacblocksize * OSS_MAXCHPERDEV]; + int wantmore=0; + + linux_nindevs = linux_noutdevs = 0; + /* mark devices unopened */ + for (int i=0; i<OSS_MAXDEV; i++) linux_adcs[i].fd = linux_dacs[i].fd = -1; + + /* open output devices */ + wantmore=0; + if (noutdev < 0 || nindev < 0) bug("linux_open_audio"); + for (int n=0; n<noutdev; n++) { + int gotchans, j, inindex = -1; + int thisdevice = (outdev[n] >= 0 ? outdev[n] : 0); + int wantchannels = (nchout>n) ? chout[n] : wantmore; + fd = -1; + if (!wantchannels) goto end_out_loop; + if (thisdevice > 0) sprintf(devname, "/dev/dsp%d", thisdevice); else sprintf(devname, "/dev/dsp"); + /* search for input request for same device. Succeed only if the number of channels matches. */ + for (j = 0; j < nindev; j++) if (indev[j] == thisdevice && chin[j] == wantchannels) inindex = j; + /* if the same device is requested for input and output, try to open it read/write */ + if (inindex >= 0) { + sys_setalarm(1000000); + if ((fd = open(devname, O_RDWR | O_AUDIOFLAG)) == -1) { + post("%s (read/write): %s", devname, strerror(errno)); + post("(now will try write-only...)"); + } else { + if (fcntl(fd, F_SETFD, 1) < 0) post("couldn't set close-on-exec flag on audio"); + if ((flags = fcntl(fd, F_GETFL)) < 0) post("couldn't get audio device flags"); + else if (fcntl(fd, F_SETFL, flags & (!O_NDELAY)) < 0) post("couldn't set audio device flags"); + if (sys_verbose) post("opened %s for reading and writing", devname); + linux_adcs[inindex].fd = fd; + } + } + /* if that didn't happen or if it failed, try write-only */ + if (fd == -1) { + sys_setalarm(1000000); + if ((fd = open(devname, O_WRONLY | O_AUDIOFLAG)) == -1) { + post("%s (writeonly): %s", devname, strerror(errno)); + break; + } + if (fcntl(fd, F_SETFD, 1) < 0) post("couldn't set close-on-exec flag on audio"); + if ((flags = fcntl(fd, F_GETFL)) < 0) post("couldn't get audio device flags"); + else if (fcntl(fd, F_SETFL, flags & (!O_NDELAY)) < 0) post("couldn't set audio device flags"); + if (sys_verbose) post("opened %s for writing only", devname); + } + if (ioctl(fd, SNDCTL_DSP_GETCAPS, &capabilities) == -1) error("OSS: SNDCTL_DSP_GETCAPS failed %s", devname); + gotchans = oss_setchannels(fd, (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, devname); + if (sys_verbose) post("opened audio output on %s; got %d channels", devname, gotchans); + if (gotchans < 2) { + close(fd); /* can't even do stereo? just give up. */ + } else { + linux_dacs[linux_noutdevs].nchannels = gotchans; + linux_dacs[linux_noutdevs].fd = fd; + oss_configure(&linux_dacs[linux_noutdevs], rate, 1, 0); + linux_noutdevs++; + outchannels += gotchans; + if (inindex >= 0) { + linux_adcs[inindex].nchannels = gotchans; + chin[inindex] = gotchans; + } + } + /* LATER think about spreading large numbers of channels over + various dsp's and vice-versa */ + wantmore = wantchannels - gotchans; + end_out_loop: ; + } + + /* open input devices */ + wantmore = 0; + for (n = 0; n < nindev; n++) { + int gotchans=0; + int thisdevice = (indev[n] >= 0 ? indev[n] : 0); + int wantchannels = (nchin>n)?chin[n]:wantmore; + int alreadyopened = 0; + if (!wantchannels) goto end_in_loop; + if (thisdevice > 0) sprintf(devname, "/dev/dsp%d", thisdevice); else sprintf(devname, "/dev/dsp"); + sys_setalarm(1000000); + /* perhaps it's already open from the above? */ + if (linux_dacs[n].fd >= 0) { + fd = linux_adcs[n].fd; + alreadyopened = 1; + } else { + /* otherwise try to open it here. */ + if ((fd = open(devname, O_RDONLY | O_AUDIOFLAG)) == -1) { + post("%s (readonly): %s", devname, strerror(errno)); + goto end_in_loop; + } + if (fcntl(fd, F_SETFD, 1) < 0) post("couldn't set close-on-exec flag on audio"); + if ((flags = fcntl(fd, F_GETFL)) < 0) post("couldn't get audio device flags"); + else if (fcntl(fd, F_SETFL, flags & (!O_NDELAY)) < 0) post("couldn't set audio device flags"); + if (sys_verbose) post("opened %s for reading only", devname); + } + linux_adcs[linux_nindevs].fd = fd; + gotchans = oss_setchannels(fd, (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, devname); + if (sys_verbose) post("opened audio input device %s; got %d channels", devname, gotchans); + if (gotchans < 1) { + close(fd); + goto end_in_loop; + } + linux_adcs[linux_nindevs].nchannels = gotchans; + oss_configure(linux_adcs+linux_nindevs, rate, 0, alreadyopened); + inchannels += gotchans; + linux_nindevs++; + wantmore = wantchannels-gotchans; + /* LATER think about spreading large numbers of channels over various dsp's and vice-versa */ + end_in_loop: ; + } + /* We have to do a read to start the engine. This is necessary because sys_send_dacs waits until the input + buffer is filled and only reads on a filled buffer. This is good, because it's a way to make sure that we + will not block. But I wonder why we only have to read from one of the devices and not all of them??? */ + if (linux_nindevs) { + if (sys_verbose) post("OSS: issuing first ADC 'read'..."); + read(linux_adcs[0].fd, buf, linux_adcs[0].bytespersamp * linux_adcs[0].nchannels * sys_dacblocksize); + if (sys_verbose) post("...done."); + } + /* now go and fill all the output buffers. */ + for (i = 0; i < linux_noutdevs; i++) { + t_oss_dev &d = linux_dacs[i]; + memset(buf, 0, d.bytespersamp * d.nchannels * sys_dacblocksize); + for (int j = 0; j < sys_advance_samples/sys_dacblocksize; j++) + write(d.fd, buf, d.bytespersamp * d.nchannels * sys_dacblocksize); + } + sys_setalarm(0); + sys_inchannels = inchannels; + sys_outchannels = outchannels; + return 0; +} + +void oss_close_audio() { + for (int i=0;i<linux_nindevs ;i++) close(linux_adcs[i].fd); + for (int i=0;i<linux_noutdevs;i++) close(linux_dacs[i].fd); + linux_nindevs = linux_noutdevs = 0; +} + +static int linux_dacs_write(int fd,void *buf,long bytes) {return write(fd, buf, bytes);} +static int linux_adcs_read (int fd,void *buf,long bytes) {return read(fd, buf, bytes);} + + /* query audio devices for "available" data size. */ +static void oss_calcspace() { + audio_buf_info ainfo; + for (int dev=0; dev<linux_noutdevs; dev++) { + if (ioctl(linux_dacs[dev].fd, SOUND_PCM_GETOSPACE,&ainfo) < 0) + error("OSS: ioctl on output device %d failed",dev); + linux_dacs[dev].space = ainfo.bytes; + } + for (int dev=0; dev<linux_nindevs; dev++) { + if (ioctl(linux_adcs[dev].fd, SOUND_PCM_GETISPACE,&ainfo) < 0) + error("OSS: ioctl on input device %d, fd %d failed", dev, linux_adcs[dev].fd); + linux_adcs[dev].space = ainfo.bytes; + } +} + +void linux_audiostatus() { + if (!oss_blockmode) { + oss_calcspace(); + for (int dev=0; dev < linux_noutdevs; dev++) post("dac %d space %d", dev, linux_dacs[dev].space); + for (int dev=0; dev < linux_nindevs; dev++) post("adc %d space %d", dev, linux_adcs[dev].space); + } +} + +/* this call resyncs audio output and input which will cause discontinuities +in audio output and/or input. */ + +static void oss_doresync() { + int zeroed = 0; + char buf[OSS_MAXSAMPLEWIDTH * sys_dacblocksize * OSS_MAXCHPERDEV]; + audio_buf_info ainfo; + /* 1. if any input devices are ahead (have more than 1 buffer stored), drop one or more buffers worth */ + for (int dev=0; dev<linux_nindevs; dev++) { + t_oss_dev d = linux_adcs[dev]; + if (d.space == 0) { + linux_adcs_read(d.fd, buf, OSS_XFERSIZE(d.nchannels, d.bytespersamp)); + } else while (d.space > OSS_XFERSIZE(d.nchannels, d.bytespersamp)) { + linux_adcs_read(d.fd, buf, OSS_XFERSIZE(d.nchannels, d.bytespersamp)); + if (ioctl(d.fd, SOUND_PCM_GETISPACE, &ainfo) < 0) { error("OSS: ioctl on input device %d, fd %d failed",dev,d.fd); break;} + d.space = ainfo.bytes; + } + } + /* 2. if any output devices are behind, feed them zeros to catch them up */ + for (int dev=0; dev<linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + while (d.space > d.bufsize - sys_advance_samples*d.nchannels*d.bytespersamp) { + if (!zeroed) { + for (unsigned int i = 0; i < OSS_XFERSAMPS(d.nchannels); i++) buf[i] = 0; + zeroed = 1; + } + linux_dacs_write(d.fd, buf, OSS_XFERSIZE(d.nchannels, d.bytespersamp)); + if (ioctl(d.fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) {error("OSS: ioctl on output device %d, fd %d failed",dev,d.fd); break;} + d.space = ainfo.bytes; + } + } + /* 3. if any DAC devices are too far ahead, plan to drop the number of frames which will let the others catch up. */ + for (int dev=0; dev<linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + if (d.space > d.bufsize - (sys_advance_samples - 1) * d.nchannels * d.bytespersamp) { + d.dropcount = sys_advance_samples - 1 - (d.space - d.bufsize) / (d.nchannels * d.bytespersamp); + } else d.dropcount = 0; + } +} + +int oss_send_dacs() { + float *fp1, *fp2; + int i, j, rtnval = SENDDACS_YES; + char buf[OSS_MAXSAMPLEWIDTH * sys_dacblocksize * OSS_MAXCHPERDEV]; + t_oss_int16 *sp; + t_oss_int32 *lp; + /* the maximum number of samples we should have in the ADC buffer */ + int idle = 0; + double timeref, timenow; + if (!linux_nindevs && !linux_noutdevs) return SENDDACS_NO; + if (!oss_blockmode) { + /* determine whether we're idle. This is true if either (1) + some input device has less than one buffer to read or (2) some + output device has fewer than (sys_advance_samples) blocks buffered already. */ + oss_calcspace(); + for (int dev=0; dev<linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + if (d.dropcount || (d.bufsize - d.space > sys_advance_samples * d.bytespersamp * d.nchannels)) idle = 1; + } + for (int dev=0; dev<linux_nindevs; dev++) { + t_oss_dev d = linux_adcs[dev]; + if (d.space < OSS_XFERSIZE(d.nchannels, d.bytespersamp)) idle = 1; + } + } + if (idle && !oss_blockmode) { + /* sometimes---rarely---when the ADC available-byte-count is + zero, it's genuine, but usually it's because we're so + late that the ADC has overrun its entire kernel buffer. We + distinguish between the two by waiting 2 msec and asking again. + There should be an error flag we could check instead; look for this someday... */ + for (int dev=0; dev<linux_nindevs; dev++) if (linux_adcs[dev].space == 0) { + sys_microsleep(sys_sleepgrain); /* tb: changed to sys_sleepgrain */ + oss_calcspace(); + if (linux_adcs[dev].space != 0) continue; + /* here's the bad case. Give up and resync. */ + sys_log_error(ERR_DATALATE); + oss_doresync(); + return SENDDACS_NO; + } + /* check for slippage between devices, either because + data got lost in the driver from a previous late condition, or + because the devices aren't synced. When we're idle, no + input device should have more than one buffer readable and + no output device should have less than sys_advance_samples-1 */ + for (int dev=0; dev<linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + if (!d.dropcount && (d.bufsize - d.space < (sys_advance_samples - 2)*d.bytespersamp*d.nchannels)) + goto badsync; + } + for (int dev=0; dev<linux_nindevs; dev++) + if (linux_adcs[dev].space > 3 * OSS_XFERSIZE(linux_adcs[dev].nchannels, linux_adcs[dev].bytespersamp)) goto badsync; + /* return zero to tell the scheduler we're idle. */ + return SENDDACS_NO; + badsync: + sys_log_error(ERR_RESYNC); + oss_doresync(); + return SENDDACS_NO; + } + /* do output */ + timeref = sys_getrealtime(); + for (int dev=0, thischan = 0; dev < linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + int nchannels = d.nchannels; + if (d.dropcount) d.dropcount--; + else { + fp1 = sys_soundout + sys_dacblocksize*thischan; + if (d.bytespersamp == 4) { + for (i = sys_dacblocksize * nchannels, lp = (t_oss_int32 *)buf; i--; fp1++, lp++) { + float f = *fp1 * 2147483648.; + *lp = int(f >= 2147483647. ? 2147483647. : (f < -2147483648. ? -2147483648. : f)); + } + } else { + for (i = sys_dacblocksize, sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) { + for (j=0, fp2 = fp1; j<nchannels; j++, fp2 += sys_dacblocksize) { + int s = int(*fp2 * 32767.); + if (s > 32767) s = 32767; else if (s < -32767) s = -32767; + sp[j] = s; + } + } + } + linux_dacs_write(d.fd, buf, OSS_XFERSIZE(nchannels, d.bytespersamp)); + if ((timenow = sys_getrealtime()) - timeref > 0.002) { + if (!oss_blockmode) sys_log_error(ERR_DACSLEPT); else rtnval = SENDDACS_SLEPT; + } + timeref = timenow; + } + thischan += nchannels; + } + memset(sys_soundout, 0, sys_outchannels * (sizeof(float) * sys_dacblocksize)); + for (int dev=0, thischan = 0; dev < linux_nindevs; dev++) { + int nchannels = linux_adcs[dev].nchannels; + linux_adcs_read(linux_adcs[dev].fd, buf, OSS_XFERSIZE(nchannels, linux_adcs[dev].bytespersamp)); + if ((timenow = sys_getrealtime()) - timeref > 0.002) { + if (!oss_blockmode) sys_log_error(ERR_ADCSLEPT); else rtnval = SENDDACS_SLEPT; + } + timeref = timenow; + fp1 = sys_soundin + thischan*sys_dacblocksize; + if (linux_adcs[dev].bytespersamp == 4) { + for (i = sys_dacblocksize*nchannels, lp = (t_oss_int32 *)buf; i--; fp1++, lp++) + *fp1 = float(*lp)*float(1./2147483648.); + } else { + for (i = sys_dacblocksize, sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) + for (j=0; j<nchannels; j++) fp1[j*sys_dacblocksize] = (float)sp[j]*(float)3.051850e-05; + } + thischan += nchannels; + } + return rtnval; +} + +void oss_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + int ndev = min(oss_ndev,maxndev); + *canmulti = 2; /* supports multiple devices */ + for (int i=0; i<ndev; i++) { + sprintf(indevlist + i*devdescsize, "OSS device #%d", i+1); + sprintf(outdevlist + i*devdescsize, "OSS device #%d", i+1); + } + *nindevs = *noutdevs = ndev; +} + +struct t_audioapi oss_api = { + oss_open_audio, + oss_close_audio, + oss_send_dacs, + oss_getdevs, +}; |