From 64fdb009695828b788fce074135b20a5e52c5fc4 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Tue, 23 Sep 2003 00:21:28 +0000 Subject: imported version 0.37-0 svn path=/trunk/; revision=1016 --- pd/src/s_linux.c | 3087 ------------------------------------------------------ 1 file changed, 3087 deletions(-) delete mode 100644 pd/src/s_linux.c (limited to 'pd/src/s_linux.c') diff --git a/pd/src/s_linux.c b/pd/src/s_linux.c deleted file mode 100644 index 54cdb978..00000000 --- a/pd/src/s_linux.c +++ /dev/null @@ -1,3087 +0,0 @@ -/* Copyright (c) 1997-1999 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 implements the sys_ functions profiled in m_imp.h for - audio and MIDI I/O. In Linux there might be several APIs for doing the - audio part; right now there are three (OSS, ALSA, RME); the third is - for the RME 9652 driver by Ritsch (but not for the OSS compatible - one by Geiger; for that one, OSS should work.) - - FUNCTION PREFIXES. - sys_ -- functions which must be exported to Pd on all platforms - linux_ -- linux-specific objects which don't depend on API, - mostly static but some exported. - oss_, alsa_, rme_ -- API-specific functions, all of which are - static. - - ALSA SUPPORT. If ALSA99 is defined we support ALSA 0.5x; if ALSA01, - ALSA 0.9x. (the naming scheme reflects the possibility of further API - changes in the future...) We define "ALSA" for code relevant to both - APIs. - - For MIDI, we only offer the OSS API; ALSA has to emulate OSS for us. -*/ - -/* OSS include (whether we're doing OSS audio or not we need this for MIDI) */ - - -/* IOhannes::: - * hacked this to add advanced multidevice-support - * 1311:forum::für::umläute:2001 - */ - -#include - -#if (defined(ALSA01) || defined(ALSA99)) -#define ALSA -#endif - -#ifdef ALSA99 -#include -#endif -#ifdef ALSA01 -#include -#endif - -#include "m_imp.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* local function prototypes */ - -static void linux_close_midi( void); - -static int oss_open_audio(int naudioindev, int *audioindev, int nchindev, - int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, - int *choutdev, int rate); /* IOhannes */ - -static void oss_close_audio(void); -static int oss_send_dacs(void); -static void oss_reportidle(void); - -#ifdef ALSA -typedef int16_t t_alsa_sample16; -typedef int32_t t_alsa_sample32; -#define ALSA_SAMPLEWIDTH_16 sizeof(t_alsa_sample16) -#define ALSA_SAMPLEWIDTH_32 sizeof(t_alsa_sample32) -#define ALSA_XFERSIZE16 (signed int)(sizeof(t_alsa_sample16) * DACBLKSIZE) -#define ALSA_XFERSIZE32 (signed int)(sizeof(t_alsa_sample32) * DACBLKSIZE) -#define ALSA_MAXDEV 1 -#define ALSA_JITTER 1024 -#define ALSA_EXTRABUFFER 2048 -#define ALSA_DEFFRAGSIZE 64 -#define ALSA_DEFNFRAG 12 - -#ifdef ALSA99 -typedef struct _alsa_dev -{ - snd_pcm_t *handle; - snd_pcm_channel_info_t info; - snd_pcm_channel_setup_t setup; -} t_alsa_dev; - -t_alsa_dev alsa_device[ALSA_MAXDEV]; -static int n_alsa_dev; -static char *alsa_buf; -static int alsa_samplewidth; -#endif /* ALSA99 */ - -#ifdef ALSA01 -typedef struct _alsa_dev -{ - snd_pcm_t *inhandle; - snd_pcm_t *outhandle; -} t_alsa_dev; - -t_alsa_dev alsa_device; -static short *alsa_buf; -static int alsa_samplewidth; -static snd_pcm_status_t* in_status; -static snd_pcm_status_t* out_status; -#endif /* ALSA01 */ - -#if 0 /* early alsa 0.9 beta dists had different names for these: */ -#define SND_PCM_ACCESS_RW_INTERLEAVED SNDRV_PCM_ACCESS_RW_INTERLEAVED -#define SND_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32 -#define SND_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16 -#define SND_PCM_SUBFORMAT_STD SNDRV_PCM_SUBFORMAT_STD -#endif - -static int alsa_mode; -static int alsa_open_audio(int inchans, int outchans, int rate); -static void alsa_close_audio(void); -static int alsa_send_dacs(void); -static void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices); -static void alsa_reportidle(void); -#endif /* ALSA */ - -#ifdef RME_HAMMERFALL -static int rme9652_open_audio(int inchans, int outchans, int rate); -static void rme9652_close_audio(void); -static int rme9652_send_dacs(void); -static void rme9652_reportidle(void); -#endif /* RME_HAMMERFALL */ - -/* 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) (DACBLKSIZE * (width)) -#define OSS_XFERSAMPS(chans) (DACBLKSIZE* (chans)) -#define OSS_XFERSIZE(chans, width) (DACBLKSIZE * (chans) * (width)) - -#ifdef RME_HAMMERFALL -typedef int32_t t_rme_sample; -#define RME_SAMPLEWIDTH sizeof(t_rme_sample) -#define RME_BYTESPERCHAN (DACBLKSIZE * RME_SAMPLEWIDTH) -#endif /* RME_HAMMERFALL */ - -/* GLOBALS */ -static int linux_whichapi = API_OSS; -static int linux_inchannels; -static int linux_outchannels; -static int linux_advance_samples; /* scheduler advance in samples */ -static int linux_meters; /* true if we're metering */ -static float linux_inmax; /* max input amplitude */ -static float linux_outmax; /* max output amplitude */ -static int linux_fragsize = 0; /* for block mode; block size (sample frames) */ -static int linux_nfragment = 0; /* ... and number of blocks. */ - -#ifdef ALSA99 -static int alsa_devno = 1; -#endif -#ifdef ALSA01 -static char alsa_devname[512] = "hw:0,0"; -static int alsa_use_plugin = 0; -#endif - -/* our device handles */ - -typedef struct _oss_dev -{ - int d_fd; - unsigned int d_space; /* bytes available for writing/reading */ - int d_bufsize; /* total buffer size in blocks for this device */ - int d_dropcount; /* # of buffers to drop for resync (output only) */ - unsigned int d_nchannels; /* number of channels for this device */ - unsigned int d_bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */ -} t_oss_dev; - -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; - - /* exported variables */ -int sys_schedadvance = OSS_DEFAUDIOBUF; /* scheduler advance in microsecs */ -float sys_dacsr; -int sys_hipriority = 0; -t_sample *sys_soundout; -t_sample *sys_soundin; - - /* OSS-specific private variables */ -static int oss_blockmode = 1; /* flag to use "blockmode" */ -static int oss_32bit = 0; /* allow 23 bit transfers in OSS */ -static char ossdsp[] = "/dev/dsp%d"; - -#ifndef INT32_MAX -#define INT32_MAX 0x7fffffff -#endif - /* 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)) - - -/* ------------- private routines for all APIS ------------------- */ - -static void linux_flush_all_underflows_to_zero(void) -{ -/* - TODO: Implement similar thing for linux (GGeiger) - - One day we will figure this out, I hope, because it - costs CPU time dearly on Intel - LT - */ - /* union fpc_csr f; - f.fc_word = get_fpc_csr(); - f.fc_struct.flush = 1; - set_fpc_csr(f.fc_word); - */ -} - - /* set sample rate and channels. Must set sample rate before "configuring" - any devices so we know scheduler advance in samples. */ - -static void linux_setsr(int sr) -{ - sys_dacsr = sr; - linux_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.); - if (linux_advance_samples < 3 * DACBLKSIZE) - linux_advance_samples = 3 * DACBLKSIZE; -} - -static void linux_setch(int chin, int chout) -{ - int nblk; - int inbytes = chin * (DACBLKSIZE*sizeof(float)); - int outbytes = chout * (DACBLKSIZE*sizeof(float)); - - linux_inchannels = chin; - linux_outchannels = chout; - if (sys_soundin) - free(sys_soundin); - sys_soundin = (t_float *)malloc(inbytes); - memset(sys_soundin, 0, inbytes); - - if (sys_soundout) - free(sys_soundout); - sys_soundout = (t_float *)malloc(outbytes); - memset(sys_soundout, 0, outbytes); - - if (sys_verbose) - post("input channels = %d, output channels = %d", - linux_inchannels, linux_outchannels); -} - -/* ---------------- MIDI routines -------------------------- */ - -static int oss_nmidiin; -static int oss_midiinfd[MAXMIDIINDEV]; -static int oss_nmidiout; -static int oss_midioutfd[MAXMIDIOUTDEV]; - -static void oss_midiout(int fd, int n) -{ - char b = n; - if ((write(fd, (char *) &b, 1)) != 1) - perror("midi write"); -} - -#define O_MIDIFLAG O_NDELAY - -void linux_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) -{ - int i; - for (i = 0; i < nmidiout; i++) - oss_midioutfd[i] = -1; - for (i = 0, oss_nmidiin = 0; i < nmidiin; i++) - { - int fd = -1, j, outdevindex = -1; - char namebuf[80]; - int devno = midiinvec[i]; - - for (j = 0; j < nmidiout; j++) - if (midioutvec[j] == midiinvec[i]) - outdevindex = j; - - /* try to open the device for read/write. */ - if (devno == 1 && fd < 0 && outdevindex >= 0) - { - sys_setalarm(1000000); - fd = open("/dev/midi", O_RDWR | O_MIDIFLAG); - if (sys_verbose) - fprintf(stderr, - "device 1: tried /dev/midi READ/WRITE; returned %d\n", fd); - if (outdevindex >= 0 && fd >= 0) - oss_midioutfd[outdevindex] = fd; - } - if (fd < 0 && outdevindex >= 0) - { - sys_setalarm(1000000); - sprintf(namebuf, "/dev/midi%2.2d", devno-1); - fd = open(namebuf, O_RDWR | O_MIDIFLAG); - if (sys_verbose) - fprintf(stderr, - "device %d: tried %s READ/WRITE; returned %d\n", - devno, namebuf, fd); - if (outdevindex >= 0 && fd >= 0) - oss_midioutfd[outdevindex] = fd; - } - if (fd < 0 && outdevindex >= 0) - { - sys_setalarm(1000000); - sprintf(namebuf, "/dev/midi%d", devno-1); - fd = open(namebuf, O_RDWR | O_MIDIFLAG); - if (sys_verbose) - fprintf(stderr, "device %d: tried %s READ/WRITE; returned %d\n", - devno, namebuf, fd); - if (outdevindex >= 0 && fd >= 0) - oss_midioutfd[outdevindex] = fd; - } - if (devno == 1 && fd < 0) - { - sys_setalarm(1000000); - fd = open("/dev/midi", O_RDONLY | O_MIDIFLAG); - if (sys_verbose) - fprintf(stderr, - "device 1: tried /dev/midi READONLY; returned %d\n", fd); - } - if (fd < 0) - { - sys_setalarm(1000000); - sprintf(namebuf, "/dev/midi%2.2d", devno-1); - fd = open(namebuf, O_RDONLY | O_MIDIFLAG); - if (sys_verbose) - fprintf(stderr, "device %d: tried %s READONLY; returned %d\n", - devno, namebuf, fd); - } - if (fd < 0) - { - sys_setalarm(1000000); - sprintf(namebuf, "/dev/midi%d", devno-1); - fd = open(namebuf, O_RDONLY | O_MIDIFLAG); - if (sys_verbose) - fprintf(stderr, "device %d: tried %s READONLY; returned %d\n", - devno, namebuf, fd); - } - if (fd >= 0) - oss_midiinfd[oss_nmidiin++] = fd; - else post("couldn't open MIDI input device %d", devno); - } - for (i = 0, oss_nmidiout = 0; i < nmidiout; i++) - { - int fd = oss_midioutfd[i]; - char namebuf[80]; - int devno = midioutvec[i]; - if (devno == 1 && fd < 0) - { - sys_setalarm(1000000); - fd = open("/dev/midi", O_WRONLY | O_MIDIFLAG); - if (sys_verbose) - fprintf(stderr, - "device 1: tried /dev/midi WRITEONLY; returned %d\n", fd); - } - if (fd < 0) - { - sys_setalarm(1000000); - sprintf(namebuf, "/dev/midi%2.2d", devno-1); - fd = open(namebuf, O_WRONLY | O_MIDIFLAG); - if (sys_verbose) - fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n", - devno, namebuf, fd); - } - if (fd < 0) - { - sys_setalarm(1000000); - sprintf(namebuf, "/dev/midi%d", devno-1); - fd = open(namebuf, O_WRONLY | O_MIDIFLAG); - if (sys_verbose) - fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n", - devno, namebuf, fd); - } - if (fd >= 0) - oss_midioutfd[oss_nmidiout++] = fd; - else post("couldn't open MIDI output device %d", devno); - } - - if (oss_nmidiin < nmidiin || oss_nmidiout < nmidiout || sys_verbose) - post("opened %d MIDI input device(s) and %d MIDI output device(s).", - oss_nmidiin, oss_nmidiout); -} - -#define md_msglen(x) (((x)<0xC0)?2:((x)<0xE0)?1:((x)<0xF0)?2:\ - ((x)==0xF2)?2:((x)<0xF4)?1:0) - -void sys_putmidimess(int portno, int a, int b, int c) -{ - if (portno >= 0 && portno < oss_nmidiout) - { - switch (md_msglen(a)) - { - case 2: - oss_midiout(oss_midioutfd[portno],a); - oss_midiout(oss_midioutfd[portno],b); - oss_midiout(oss_midioutfd[portno],c); - return; - case 1: - oss_midiout(oss_midioutfd[portno],a); - oss_midiout(oss_midioutfd[portno],b); - return; - case 0: - oss_midiout(oss_midioutfd[portno],a); - return; - }; - } -} - -void sys_putmidibyte(int portno, int byte) -{ - if (portno >= 0 && portno < oss_nmidiout) - oss_midiout(oss_midioutfd[portno], byte); -} - -#if 0 /* this is the "select" version which doesn't work with OSS - driver for emu10k1 (it doesn't implement select.) */ -void sys_poll_midi(void) -{ - int i, throttle = 100; - struct timeval timout; - int did = 1, maxfd = 0; - while (did) - { - fd_set readset, writeset, exceptset; - did = 0; - if (throttle-- < 0) - break; - timout.tv_sec = 0; - timout.tv_usec = 0; - - FD_ZERO(&writeset); - FD_ZERO(&readset); - FD_ZERO(&exceptset); - for (i = 0; i < oss_nmidiin; i++) - { - if (oss_midiinfd[i] > maxfd) - maxfd = oss_midiinfd[i]; - FD_SET(oss_midiinfd[i], &readset); - } - select(maxfd+1, &readset, &writeset, &exceptset, &timout); - for (i = 0; i < oss_nmidiin; i++) - if (FD_ISSET(oss_midiinfd[i], &readset)) - { - char c; - int ret = read(oss_midiinfd[i], &c, 1); - if (ret <= 0) - fprintf(stderr, "Midi read error\n"); - else sys_midibytein(i, (c & 0xff)); - did = 1; - } - } -} -#else - - /* this version uses the asynchronous "read()" ... */ -void sys_poll_midi(void) -{ - int i, throttle = 100; - struct timeval timout; - int did = 1, maxfd = 0; - while (did) - { - fd_set readset, writeset, exceptset; - did = 0; - if (throttle-- < 0) - break; - for (i = 0; i < oss_nmidiin; i++) - { - char c; - int ret = read(oss_midiinfd[i], &c, 1); - if (ret < 0) - { - if (errno != EAGAIN) - perror("MIDI"); - } - else if (ret != 0) - { - sys_midibytein(i, (c & 0xff)); - did = 1; - } - } - } -} -#endif - -void linux_close_midi() -{ - int i; - for (i = 0; i < oss_nmidiin; i++) - close(oss_midiinfd[i]); - for (i = 0; i < oss_nmidiout; i++) - close(oss_midioutfd[i]); - oss_nmidiin = oss_nmidiout = 0; -} - -#define MAXAUDIODEV 4 -#define DEFAULTINDEV 1 -#define DEFAULTOUTDEV 1 - -/* ----------------------- public routines ----------------------- */ -void sys_listdevs( void) -{ - post("device listing not implemented in Linux yet\n"); -} - -void sys_open_audio(int naudioindev, int *audioindev, int nchindev, - int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, - int *choutdev, int rate) -{ /* IOhannes */ - int i, *ip; - int defaultchannels = - (linux_whichapi == API_RME ? RME_DEFAULTCH : OSS_DEFAULTCH); - if (rate < 1) - rate=44100; - - if (naudioindev == -1) - { /* not set */ - if (nchindev==-1) - { - nchindev=1; - chindev[0]=defaultchannels; - naudioindev=1; - audioindev[0] = DEFAULTINDEV; - } - else - { - for (i = 0; i < MAXAUDIODEV; i++) - audioindev[i]=i+1; - naudioindev = nchindev; - } - } - else - { - if (nchindev == -1) - { - nchindev = naudioindev; - for (i = 0; i < naudioindev; i++) - chindev[i] = defaultchannels; - } - else if (nchindev > naudioindev) - { - for (i = naudioindev; i < nchindev; i++) - { - if (i == 0) - audioindev[0] = DEFAULTINDEV; - else audioindev[i] = audioindev[i-1] + 1; - } - naudioindev = nchindev; - } - else if (nchindev < naudioindev) - { - for (i = nchindev; i < naudioindev; i++) - { - if (i == 0) - chindev[0] = defaultchannels; - else chindev[i] = chindev[i-1]; - } - naudioindev = nchindev; - } - } - - if (naudiooutdev == -1) - { /* not set */ - if (nchoutdev==-1) - { - nchoutdev=1; - choutdev[0]=defaultchannels; - naudiooutdev=1; - audiooutdev[0] = DEFAULTOUTDEV; - } - else - { - for (i = 0; i < MAXAUDIODEV; i++) - audiooutdev[i] = i+1; - naudiooutdev = nchoutdev; - } - } - else - { - if (nchoutdev == -1) - { - nchoutdev = naudiooutdev; - for (i = 0; i < naudiooutdev; i++) - choutdev[i] = defaultchannels; - } - else if (nchoutdev > naudiooutdev) - { - for (i = naudiooutdev; i < nchoutdev; i++) - { - if (i == 0) - audiooutdev[0] = DEFAULTOUTDEV; - else audiooutdev[i] = audiooutdev[i-1] + 1; - } - naudiooutdev = nchoutdev; - } - else if (nchoutdev < naudiooutdev) - { - for (i = nchoutdev; i < naudiooutdev; i++) - { - if (i == 0) - choutdev[0] = defaultchannels; - else choutdev[i] = choutdev[i-1]; - } - naudiooutdev = nchoutdev; - } - } - - linux_flush_all_underflows_to_zero(); -#ifdef ALSA - if (linux_whichapi == API_ALSA) - alsa_open_audio((naudioindev > 0 ? chindev[0] : 0), - (naudiooutdev > 0 ? choutdev[0] : 0), rate); - else -#endif -#ifdef RME_HAMMERFALL - if (linux_whichapi == API_RME) - rme9652_open_audio((naudioindev > 0 ? chindev[0] : 0), - (naudiooutdev > 0 ? choutdev[0] : 0), rate); - else -#endif - oss_open_audio(naudioindev, audioindev, nchindev, chindev, - naudiooutdev, audiooutdev, nchoutdev, choutdev, rate); -} - -void sys_close_audio(void) -{ - /* set timeout to avoid hanging close() call */ - - sys_setalarm(1000000); - -#ifdef ALSA - if (linux_whichapi == API_ALSA) - alsa_close_audio(); - else -#endif -#ifdef RME_HAMMERFALL - if (linux_whichapi == API_RME) - rme9652_close_audio(); - else -#endif - oss_close_audio(); - - sys_setalarm(0); -} - -void sys_open_midi(int nmidiin, int *midiinvec, - int nmidiout, int *midioutvec) -{ - linux_open_midi(nmidiin, midiinvec, nmidiout, midioutvec); -} - -void sys_close_midi( void) -{ - sys_setalarm(1000000); - linux_close_midi(); - sys_setalarm(0); -} - -int sys_send_dacs(void) -{ - if (linux_meters) - { - int i, n; - float maxsamp; - for (i = 0, n = linux_inchannels * DACBLKSIZE, maxsamp = linux_inmax; - i < n; i++) - { - float f = sys_soundin[i]; - if (f > maxsamp) maxsamp = f; - else if (-f > maxsamp) maxsamp = -f; - } - linux_inmax = maxsamp; - for (i = 0, n = linux_outchannels * DACBLKSIZE, maxsamp = linux_outmax; - i < n; i++) - { - float f = sys_soundout[i]; - if (f > maxsamp) maxsamp = f; - else if (-f > maxsamp) maxsamp = -f; - } - linux_outmax = maxsamp; - } -#ifdef ALSA - if (linux_whichapi == API_ALSA) - return alsa_send_dacs(); -#endif -#ifdef RME_HAMMERFALL - if (linux_whichapi == API_RME) - return rme9652_send_dacs(); -#endif - return oss_send_dacs(); -} - -float sys_getsr(void) -{ - return (sys_dacsr); -} - -int sys_get_outchannels(void) -{ - return (linux_outchannels); -} - -int sys_get_inchannels(void) -{ - return (linux_inchannels); -} - -void sys_audiobuf(int n) -{ - /* set the size, in milliseconds, of the audio FIFO */ - if (n < 5) n = 5; - else if (n > 5000) n = 5000; - sys_schedadvance = n * 1000; -} - -void sys_getmeters(float *inmax, float *outmax) -{ - if (inmax) - { - linux_meters = 1; - *inmax = linux_inmax; - *outmax = linux_outmax; - } - else - linux_meters = 0; - linux_inmax = linux_outmax = 0; -} - -void sys_reportidle(void) -{ -} - -void sys_set_priority(int higher) -{ - struct sched_param par; - int p1 ,p2, p3; -#ifdef _POSIX_PRIORITY_SCHEDULING - - p1 = sched_get_priority_min(SCHED_FIFO); - p2 = sched_get_priority_max(SCHED_FIFO); - p3 = (higher ? p2 - 1 : p2 - 3); - par.sched_priority = p3; - - if (sched_setscheduler(0,SCHED_FIFO,&par) != -1) - fprintf(stderr, "priority %d scheduling enabled.\n", p3); -#endif - -#ifdef _POSIX_MEMLOCK - if (mlockall(MCL_FUTURE) != -1) - fprintf(stderr, "memory locking enabled.\n"); -#endif -} - -void sys_setblocksize(int n) -{ - if (n < 1) - n = 1; - linux_fragsize = n; - oss_blockmode = 1; -} - -/* ------------ linux-specific command-line flags -------------- */ - -void linux_setfrags(int n) -{ - linux_nfragment = n; - oss_blockmode = 1; -} - -void linux_streammode( void) -{ - oss_blockmode = 0; -} - -void linux_32bit( void) -{ - oss_32bit = 1; -} - -void linux_set_sound_api(int which) -{ - linux_whichapi = which; - if (sys_verbose) - post("linux_whichapi %d", linux_whichapi); -} - -#ifdef ALSA99 -void linux_alsa_devno(int devno) -{ - alsa_devno = devno; -} - -#endif - -#ifdef ALSA01 -void linux_alsa_devname(char *devname) -{ - strncpy(alsa_devname, devname, 511); -} - -void linux_alsa_use_plugin(int t) -{ - if (t == 1) - alsa_use_plugin = 1; - else - alsa_use_plugin = 0; -} - -#endif - -/* -------------- Audio I/O using the OSS API ------------------ */ - -typedef struct _multidev { - int fd; - int channels; - int format; -} t_multidev; - -int oss_reset(int fd) { - int err; - if ((err = ioctl(fd,SNDCTL_DSP_RESET)) < 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, nblk, fd = dev->d_fd, wantformat; - int nchannels = dev->d_nchannels; - int advwas = sys_schedadvance; - - 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->d_bytespersamp = 4; - } - else - { - wantformat = AFMT_S16_NE; - dev->d_bytespersamp = 2; - } - param = wantformat; - - if (sys_verbose) - post("bytes per sample = %d", dev->d_bytespersamp); - if (ioctl(fd, SNDCTL_DSP_SETFMT, ¶m) == -1) - fprintf(stderr,"OSS: Could not set DSP format\n"); - else if (wantformat != param) - fprintf(stderr,"OSS: DSP format: wanted %d, got %d\n", - wantformat, param); - - /* sample rate */ - orig = param = srate; - if (ioctl(fd, SNDCTL_DSP_SPEED, ¶m) == -1) - fprintf(stderr,"OSS: Could not set sampling rate for device\n"); - else if( orig != param ) - fprintf(stderr,"OSS: sampling rate: wanted %d, got %d\n", - orig, param ); - - if (oss_blockmode && !skipblocksize) - { - int fragbytes, logfragsize, nfragment; - /* setting fragment count and size. */ - if (linux_nfragment) /* if nfrags specified, take literally */ - { - nfragment = linux_nfragment; - if (!linux_fragsize) - linux_fragsize = OSS_DEFFRAGSIZE; - sys_schedadvance = ((nfragment * linux_fragsize) * 1.e6) - / (float)srate; - linux_setsr(srate); - } - else - { - if (!linux_fragsize) - { - linux_fragsize = OSS_DEFFRAGSIZE; - while (linux_fragsize > DACBLKSIZE - && linux_fragsize * 4 > linux_advance_samples) - linux_fragsize = linux_fragsize/2; - } - /* post("adv_samples %d", linux_advance_samples); */ - nfragment = (sys_schedadvance * (44100. * 1.e-6)) / linux_fragsize; - } - fragbytes = linux_fragsize * (dev->d_bytespersamp * nchannels); - logfragsize = ilog2(fragbytes); - - if (fragbytes != (1 << logfragsize)) - post("warning: OSS takes only power of 2 blocksize; using %d", - (1 << logfragsize)/(dev->d_bytespersamp * nchannels)); - if (sys_verbose) - post("setting nfrags = %d, fragsize %d\n", nfragment, fragbytes); - - param = orig = (nfragment<<16) + logfragsize; - if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) - error("OSS: Could not set or read fragment size\n"); - 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) - fprintf(stderr,"OSS: ioctl on output device failed"); - dev->d_bufsize = ainfo.bytes; - - defect = linux_advance_samples * (dev->d_bytespersamp * nchannels) - - dev->d_bufsize - OSS_XFERSIZE(nchannels, dev->d_bytespersamp); - if (defect > 0) - { - if (sys_verbose || defect > (dev->d_bufsize >> 2)) - fprintf(stderr, - "OSS: requested audio buffer size %d limited to %d\n", - linux_advance_samples * (dev->d_bytespersamp * nchannels), - dev->d_bufsize); - linux_advance_samples = - (dev->d_bufsize - OSS_XFERSAMPS(nchannels)) / - (dev->d_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 0 /* O_NDELAY */ - -int oss_open_audio(int nindev, int *indev, int nchin, int *chin, - int noutdev, int *outdev, int nchout, int *chout, int rate) -{ /* IOhannes */ - int capabilities = 0; - int inchannels = 0, outchannels = 0; - char devname[20]; - int n, i, fd; - char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV]; - int num_devs = 0; - int wantmore=0; - int spread = 0; - audio_buf_info ainfo; - - linux_nindevs = linux_noutdevs = 0; - - /* set logical sample rate amd calculate linux_advance_samples. */ - linux_setsr(rate); - - /* mark input devices unopened */ - for (i = 0; i < OSS_MAXDEV; i++) - linux_adcs[i].d_fd = -1; - - /* open output devices */ - wantmore=0; - if (noutdev < 0 || nindev < 0) - bug("linux_open_audio"); - - for (n = 0; n < noutdev; n++) - { - int gotchans, j, inindex = -1; - int thisdevice=outdev[n]; - int wantchannels = (nchout>n) ? chout[n] : wantmore; - fd = -1; - if (!wantchannels) - goto end_out_loop; - - if (thisdevice > 1) - sprintf(devname, "/dev/dsp%d", thisdevice-1); - 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 (sys_verbose) - post("opened %s for reading and writing\n", devname); - linux_adcs[inindex].d_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 (sys_verbose) - post("opened %s for writing only\n", 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) - { - /* can't even do stereo? just give up. */ - close(fd); - } - else - { - linux_dacs[linux_noutdevs].d_nchannels = gotchans; - linux_dacs[linux_noutdevs].d_fd = fd; - oss_configure(linux_dacs+linux_noutdevs, rate, 1, 0); - - linux_noutdevs++; - outchannels += gotchans; - if (inindex >= 0) - { - linux_adcs[inindex].d_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; - if (nindev==-1) - nindev=4; /* spread channels over default-devices */ - for (n = 0; n < nindev; n++) - { - int gotchans=0; - int thisdevice=indev[n]; - int wantchannels = (nchin>n)?chin[n]:wantmore; - int alreadyopened = 0; - if (!wantchannels) - goto end_in_loop; - - if (thisdevice > 1) - sprintf(devname, "/dev/dsp%d", thisdevice - 1); - else sprintf(devname, "/dev/dsp"); - - sys_setalarm(1000000); - - /* perhaps it's already open from the above? */ - if (linux_dacs[n].d_fd >= 0) - { - fd = linux_dacs[n].d_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 (sys_verbose) - post("opened %s for reading only\n", devname); - } - linux_adcs[linux_nindevs].d_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].d_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: ; - } - - linux_setch(inchannels, outchannels); - - /* 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) - fprintf(stderr,("OSS: issuing first ADC 'read' ... ")); - read(linux_adcs[0].d_fd, buf, - linux_adcs[0].d_bytespersamp * - linux_adcs[0].d_nchannels * DACBLKSIZE); - if (sys_verbose) - fprintf(stderr, "...done.\n"); - } - sys_setalarm(0); - return (0); -} - -void oss_close_audio( void) -{ - int i; - for (i=0;i - OSS_XFERSIZE(linux_adcs[dev].d_nchannels, - linux_adcs[dev].d_bytespersamp)) - { - linux_adcs_read(linux_adcs[dev].d_fd, buf, - OSS_XFERSIZE(linux_adcs[dev].d_nchannels, - linux_adcs[dev].d_bytespersamp)); - if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE, &ainfo) < 0) - { - fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed", - dev, linux_adcs[dev].d_fd); - break; - } - linux_adcs[dev].d_space = ainfo.bytes; - } - } - - /* 2. if any output devices are behind, feed them zeros to catch them - up */ - for (dev = 0; dev < linux_noutdevs; dev++) - { - while (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - - linux_advance_samples * (linux_dacs[dev].d_nchannels * - linux_dacs[dev].d_bytespersamp)) - { - if (!zeroed) - { - unsigned int i; - for (i = 0; i < OSS_XFERSAMPS(linux_dacs[dev].d_nchannels); - i++) - buf[i] = 0; - zeroed = 1; - } - linux_dacs_write(linux_dacs[dev].d_fd, buf, - OSS_XFERSIZE(linux_dacs[dev].d_nchannels, - linux_dacs[dev].d_bytespersamp)); - if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) - { - fprintf(stderr, "OSS: ioctl on output device %d, fd %d failed", - dev, linux_dacs[dev].d_fd); - break; - } - linux_dacs[dev].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 (dev = 0; dev < linux_noutdevs; dev++) - { - if (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - - (linux_advance_samples - 1) * linux_dacs[dev].d_nchannels * - linux_dacs[dev].d_bytespersamp) - { - linux_dacs[dev].d_dropcount = linux_advance_samples - 1 - - (linux_dacs[dev].d_space - linux_dacs[dev].d_bufsize) / - (linux_dacs[dev].d_nchannels * - linux_dacs[dev].d_bytespersamp) ; - } - else linux_dacs[dev].d_dropcount = 0; - } -} - -int oss_send_dacs(void) -{ - float *fp1, *fp2; - long fill; - int i, j, dev, rtnval = SENDDACS_YES; - char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * 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; - int thischan; - 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 (linux_advance_samples) blocks buffered - already. */ - oss_calcspace(); - - for (dev=0; dev < linux_noutdevs; dev++) - if (linux_dacs[dev].d_dropcount || - (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space > - linux_advance_samples * linux_dacs[dev].d_bytespersamp * - linux_dacs[dev].d_nchannels)) - idle = 1; - for (dev=0; dev < linux_nindevs; dev++) - if (linux_adcs[dev].d_space < - OSS_XFERSIZE(linux_adcs[dev].d_nchannels, - linux_adcs[dev].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 (dev = 0;dev < linux_nindevs; dev++) - if (linux_adcs[dev].d_space == 0) - { - audio_buf_info ainfo; - sys_microsleep(2000); - oss_calcspace(); - if (linux_adcs[dev].d_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 linux_advance_samples-1 - */ - - for (dev=0; dev < linux_noutdevs; dev++) - if (!linux_dacs[dev].d_dropcount && - (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space < - (linux_advance_samples - 2) * - (linux_dacs[dev].d_bytespersamp * - linux_dacs[dev].d_nchannels))) - goto badsync; - for (dev=0; dev < linux_nindevs; dev++) - if (linux_adcs[dev].d_space > 3 * - OSS_XFERSIZE(linux_adcs[dev].d_nchannels, - linux_adcs[dev].d_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 (dev=0, thischan = 0; dev < linux_noutdevs; dev++) - { - int nchannels = linux_dacs[dev].d_nchannels; - if (linux_dacs[dev].d_dropcount) - linux_dacs[dev].d_dropcount--; - else - { - if (linux_dacs[dev].d_bytespersamp == 4) - { - for (i = DACBLKSIZE * nchannels, fp1 = sys_soundout + - DACBLKSIZE*thischan, - lp = (t_oss_int32 *)buf; i--; fp1++, lp++) - { - float f = *fp1 * 2147483648.; - *lp = (f >= 2147483647. ? 2147483647. : - (f < -2147483648. ? -2147483648. : f)); - } - } - else - { - for (i = DACBLKSIZE, fp1 = sys_soundout + - DACBLKSIZE*thischan, - sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) - { - for (j=0, fp2 = fp1; j 32767) s = 32767; - else if (s < -32767) s = -32767; - sp[j] = s; - } - } - } - linux_dacs_write(linux_dacs[dev].d_fd, buf, - OSS_XFERSIZE(nchannels, linux_dacs[dev].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, - linux_outchannels * (sizeof(float) * DACBLKSIZE)); - - /* do input */ - - for (dev = 0, thischan = 0; dev < linux_nindevs; dev++) - { - int nchannels = linux_adcs[dev].d_nchannels; - linux_adcs_read(linux_adcs[dev].d_fd, buf, - OSS_XFERSIZE(nchannels, linux_adcs[dev].d_bytespersamp)); - - if ((timenow = sys_getrealtime()) - timeref > 0.002) - { - if (!oss_blockmode) - sys_log_error(ERR_ADCSLEPT); - else - rtnval = SENDDACS_SLEPT; - } - timeref = timenow; - - if (linux_adcs[dev].d_bytespersamp == 4) - { - for (i = DACBLKSIZE*nchannels, - fp1 = sys_soundin + thischan*DACBLKSIZE, - lp = (t_oss_int32 *)buf; i--; fp1++, lp++) - { - *fp1 = ((float)(*lp))*(float)(1./2147483648.); - } - } - else - { - for (i = DACBLKSIZE,fp1 = sys_soundin + thischan*DACBLKSIZE, - sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) - { - for (j=0;j channelinfo.max_voices) - post("decreasing input channels to maximum of %d\n", - wantinchans = channelinfo.max_voices); - if (alsa_samplewidth == 4 && - !(channelinfo.formats & (1< channelinfo.max_voices) - post("decreasing output channels to maximum of %d\n", - wantoutchans = channelinfo.max_voices); - if (alsa_samplewidth == 4 && - !(channelinfo.formats & (1< linux_outchannels ? linux_inchannels : - linux_outchannels) * DACBLKSIZE; - alsa_buf = malloc(bsize); - if (!alsa_buf) - return (1); - memset(alsa_buf, 0, bsize); - return 0; -} - -void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices) -{ - int err; - struct snd_pcm_channel_params params; - - memset(&dev->info, 0, sizeof(dev->info)); - dev->info.channel = dir; - if ((err = snd_pcm_channel_info(dev->handle, &dev->info) < 0)) - { - fprintf(stderr, "PD-ALSA: error getting channel info: %s\n", - snd_strerror(err)); - } - memset(¶ms, 0, sizeof(params)); - params.format.interleave = 1; /* may do non-interleaved later */ - /* format is 2 or 4 bytes per sample depending on what was possible */ - params.format.format = - (alsa_samplewidth == 4 ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE); - - /*will check this further down -just try for now*/ - params.format.rate = rate; - params.format.voices = voices; - params.start_mode = SND_PCM_START_GO; /* seems most reliable */ - /*do not stop at overrun/underrun*/ - params.stop_mode = SND_PCM_STOP_ROLLOVER; - - params.channel = dir; /* playback|capture */ - params.buf.stream.queue_size = - (ALSA_EXTRABUFFER + linux_advance_samples) - * alsa_samplewidth * voices; - params.buf.stream.fill = SND_PCM_FILL_SILENCE_WHOLE; - params.mode = SND_PCM_MODE_STREAM; - - if ((err = snd_pcm_channel_params(dev->handle, ¶ms)) < 0) - { - printf("PD-ALSA: error setting parameters %s", snd_strerror(err)); - } - - /* This should clear the buffers but does not. There is often noise at - startup that sounds like crap left in the buffers - maybe in the lib - instead of the driver? Some solution needs to be found. - */ - - if ((err = snd_pcm_channel_prepare(dev->handle, dir)) < 0) - { - printf("PD-ALSA: error preparing channel %s", snd_strerror(err)); - } - dev->setup.channel = dir; - - if ((err = snd_pcm_channel_setup(dev->handle, &dev->setup)) < 0) - { - printf("PD-ALSA: error getting setup %s", snd_strerror(err)); - } - /* for some reason, if you don't writesomething before starting the - converters we get trash on startup */ - if (dir == SND_PCM_CHANNEL_PLAYBACK) - { - char foo[1024]; - int xxx = 1024 - (1024 % (linux_outchannels * alsa_samplewidth)); - int i, r; - for (i = 0; i < xxx; i++) - foo[i] = 0; - if ((r = snd_pcm_write(dev->handle, foo, xxx)) < xxx) - fprintf(stderr, "alsa_write: %s\n", snd_strerror(errno)); - } - snd_pcm_channel_go(dev->handle, dir); -} - -void alsa_close_audio(void) -{ - int i; - for(i = 0; i < n_alsa_dev; i++) - snd_pcm_close(alsa_device[i].handle); -} - -/* #define DEBUG_ALSA_XFER */ - -int alsa_send_dacs(void) -{ - static int16_t *sp; - t_sample *fp, *fp1, *fp2; - int i, j, k, err, devno = 0; - int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0; - int result; - snd_pcm_channel_status_t stat; - static int callno = 0; - static int xferno = 0; - int countwas = 0; - double timelast; - static double timenow; - int inchannels = linux_inchannels; - int outchannels = linux_outchannels; - int inbytesperframe = inchannels * alsa_samplewidth; - int outbytesperframe = outchannels * alsa_samplewidth; - int intransfersize = DACBLKSIZE * inbytesperframe; - int outtransfersize = DACBLKSIZE * outbytesperframe; - int alsaerror; - int loggederror = 0; - - if (!inchannels && !outchannels) - return (SENDDACS_NO); - timelast = timenow; - timenow = sys_getrealtime(); - -#ifdef DEBUG_ALSA_XFER - if (timenow - timelast > 0.050) - fprintf(stderr, "(%d)", - (int)(1000 * (timenow - timelast))), fflush(stderr); -#endif - - callno++; - /* get input and output channel status */ - if (inchannels > 0) - { - devno = 0; - stat.channel = SND_PCM_CHANNEL_CAPTURE; - if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle, - &stat)) - { - fprintf(stderr, "snd_pcm_channel_status (input): %s\n", - snd_strerror(alsaerror)); - return (SENDDACS_NO); - } - inputcount = stat.count; - inputlate = (stat.underrun > 0 || stat.overrun > 0); - } - if (outchannels > 0) - { - devno = 0; - stat.channel = SND_PCM_CHANNEL_PLAYBACK; - if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle, - &stat)) - { - fprintf(stderr, "snd_pcm_channel_status (output): %s\n", - snd_strerror(alsaerror)); - return (SENDDACS_NO); - } - outputcount = stat.count; - outputlate = (stat.underrun > 0 || stat.overrun > 0); - } - - /* check if input not ready */ - if (inputcount < intransfersize) - { - /* fprintf(stderr, "no adc; count %d, free %d, call %d, xfer %d\n", - stat.count, - stat.free, - callno, xferno); */ - if (outchannels > 0) - { - /* if there's no input but output is hungry, feed output. */ - while (outputcount < (linux_advance_samples + ALSA_JITTER) - * outbytesperframe) - { - if (!loggederror) - sys_log_error(ERR_RESYNC), loggederror = 1; - memset(alsa_buf, 0, outtransfersize); - result = snd_pcm_write(alsa_device[devno].handle, - alsa_buf, outtransfersize); - if (result < outtransfersize) - { -#ifdef DEBUG_ALSA_XFER - if (result >= 0 || errno == EAGAIN) - fprintf(stderr, "ALSA: write returned %d of %d\n", - result, outtransfersize); - else fprintf(stderr, "ALSA: write: %s\n", - snd_strerror(errno)); - fprintf(stderr, - "inputcount %d, outputcount %d, outbufsize %d\n", - inputcount, outputcount, - (ALSA_EXTRABUFFER + linux_advance_samples) - * alsa_samplewidth * outchannels); -#endif - return (SENDDACS_NO); - } - stat.channel = SND_PCM_CHANNEL_PLAYBACK; - if (alsaerror = - snd_pcm_channel_status(alsa_device[devno].handle, - &stat)) - { - fprintf(stderr, "snd_pcm_channel_status (output): %s\n", - snd_strerror(alsaerror)); - return (SENDDACS_NO); - } - outputcount = stat.count; - } - } - - return SENDDACS_NO; - } - - /* if output buffer has at least linux_advance_samples in it, we're - not ready for this batch. */ - if (outputcount > linux_advance_samples * outbytesperframe) - { - if (inchannels > 0) - { - while (inputcount > (DACBLKSIZE + ALSA_JITTER) * outbytesperframe) - { - if (!loggederror) - sys_log_error(ERR_RESYNC), loggederror = 1; - devno = 0; - result = snd_pcm_read(alsa_device[devno].handle, alsa_buf, - intransfersize); - if (result < intransfersize) - { -#ifdef DEBUG_ALSA_XFER - if (result < 0) - fprintf(stderr, - "snd_pcm_read %d %d: %s\n", - callno, xferno, snd_strerror(errno)); - else fprintf(stderr, - "snd_pcm_read %d %d returned only %d\n", - callno, xferno, result); - fprintf(stderr, - "inputcount %d, outputcount %d, inbufsize %d\n", - inputcount, outputcount, - (ALSA_EXTRABUFFER + linux_advance_samples) - * alsa_samplewidth * inchannels); -#endif - return (SENDDACS_NO); - } - devno = 0; - stat.channel = SND_PCM_CHANNEL_CAPTURE; - if (alsaerror = - snd_pcm_channel_status(alsa_device[devno].handle, - &stat)) - { - fprintf(stderr, "snd_pcm_channel_status (input): %s\n", - snd_strerror(alsaerror)); - return (SENDDACS_NO); - } - inputcount = stat.count; - inputlate = (stat.underrun > 0 || stat.overrun > 0); - } - return (SENDDACS_NO); - } - } - if (sys_getrealtime() - timenow > 0.002) - { -#ifdef DEBUG_ALSA_XFER - fprintf(stderr, "check %d took %d msec\n", - callno, (int)(1000 * (timenow - timelast))), fflush(stderr); -#endif - sys_log_error(ERR_DACSLEPT); - timenow = sys_getrealtime(); - } - if (inputlate || outputlate) - sys_log_error(ERR_DATALATE); - - /* do output */ - /* this "for" loop won't work for more than one device. */ - for (devno = 0, fp = sys_soundout; devno < (outchannels > 0); devno++, - fp += 128) - { - if (alsa_samplewidth == 4) - { - for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) - { - for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; - j += outchannels, fp2++) - { - float s1 = *fp2 * INT32_MAX; - ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1); - } - } - } - else - { - for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) - { - for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; - j += outchannels, fp2++) - { - int s = *fp2 * 32767.; - if (s > 32767) - s = 32767; - else if (s < -32767) - s = -32767; - ((t_alsa_sample16 *)alsa_buf)[j] = s; - } - } - } - - result = snd_pcm_write(alsa_device[devno].handle, alsa_buf, - outtransfersize); - if (result < outtransfersize) - { -#ifdef DEBUG_ALSA_XFER - if (result >= 0 || errno == EAGAIN) - fprintf(stderr, "ALSA: write returned %d of %d\n", - result, outtransfersize); - else fprintf(stderr, "ALSA: write: %s\n", - snd_strerror(errno)); - fprintf(stderr, - "inputcount %d, outputcount %d, outbufsize %d\n", - inputcount, outputcount, - (ALSA_EXTRABUFFER + linux_advance_samples) - * alsa_samplewidth * outchannels); -#endif - sys_log_error(ERR_DACSLEPT); - return (SENDDACS_NO); - } - } - /* zero out the output buffer */ - memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) * - linux_outchannels); - if (sys_getrealtime() - timenow > 0.002) - { -#if DEBUG_ALSA_XFER - fprintf(stderr, "output %d took %d msec\n", - callno, (int)(1000 * (timenow - timelast))), fflush(stderr); -#endif - timenow = sys_getrealtime(); - sys_log_error(ERR_DACSLEPT); - } - - /* do input */ - for (devno = 0, fp = sys_soundin; devno < (linux_inchannels > 0); devno++, - fp += 128) - { - result = snd_pcm_read(alsa_device[devno].handle, alsa_buf, - intransfersize); - if (result < intransfersize) - { -#ifdef DEBUG_ALSA_XFER - if (result < 0) - fprintf(stderr, - "snd_pcm_read %d %d: %s\n", - callno, xferno, snd_strerror(errno)); - else fprintf(stderr, - "snd_pcm_read %d %d returned only %d\n", - callno, xferno, result); - fprintf(stderr, - "inputcount %d, outputcount %d, inbufsize %d\n", - inputcount, outputcount, - (ALSA_EXTRABUFFER + linux_advance_samples) - * alsa_samplewidth * inchannels); -#endif - sys_log_error(ERR_ADCSLEPT); - return (SENDDACS_NO); - } - if (alsa_samplewidth == 4) - { - for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) - { - for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; - j += inchannels, fp2++) - *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j] - * (1./ INT32_MAX); - } - } - else - { - for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) - { - for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels, fp2++) - *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j] - * 3.051850e-05; - } - } - } - xferno++; - if (sys_getrealtime() - timenow > 0.002) - { -#ifdef DEBUG_ALSA_XFER - fprintf(stderr, "routine took %d msec\n", - (int)(1000 * (sys_getrealtime() - timenow))); -#endif - sys_log_error(ERR_ADCSLEPT); - } - return SENDDACS_YES; -} - -#endif /* ALSA99 */ - -/* support for ALSA pcmv2 api by Karl MacMillan */ - -#ifdef ALSA01 - -static void check_error(int err, const char *why) -{ - if (err < 0) - fprintf(stderr, "%s: %s\n", why, snd_strerror(err)); -} - -static int alsa_open_audio(int wantinchans, int wantoutchans, int srate) -{ - int err, inchans = 0, outchans = 0, subunitdir; - char devname[512]; - snd_pcm_hw_params_t* hw_params; - snd_pcm_sw_params_t* sw_params; - snd_output_t* out; - int frag_size = (linux_fragsize ? linux_fragsize : ALSA_DEFFRAGSIZE); - int nfrags, i; - short* tmp_buf; - unsigned int tmp_uint; - int advwas = sys_schedadvance; - - if (linux_nfragment) - { - nfrags = linux_nfragment; - sys_schedadvance = (frag_size * linux_nfragment * 1.0e6) / srate; - } - else nfrags = sys_schedadvance * (float)srate / (1e6 * frag_size); - - if (sys_verbose || (sys_schedadvance != advwas)) - post("audio buffer set to %d", (int)(0.001 * sys_schedadvance)); - if (wantinchans || wantoutchans) - alsa_checkversion(); - if (wantinchans) - { - err = snd_pcm_open(&alsa_device.inhandle, alsa_devname, - SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); - - check_error(err, "snd_pcm_open (input)"); - if (err < 0) - inchans = 0; - else - { - inchans = wantinchans; - snd_pcm_nonblock(alsa_device.inhandle, 1); - } - } - if (wantoutchans) - { - err = snd_pcm_open(&alsa_device.outhandle, alsa_devname, - SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); - - check_error(err, "snd_pcm_open (output)"); - if (err < 0) - outchans = 0; - else - { - outchans = wantoutchans; - snd_pcm_nonblock(alsa_device.outhandle, 1); - } - } - if (inchans) - { - if (sys_verbose) - post("opening sound input..."); - err = snd_pcm_hw_params_malloc(&hw_params); - check_error(err, "snd_pcm_hw_params_malloc (input)"); - - // get the default params - err = snd_pcm_hw_params_any(alsa_device.inhandle, hw_params); - check_error(err, "snd_pcm_hw_params_any (input)"); - // set interleaved access - FIXME deal with other access types - err = snd_pcm_hw_params_set_access(alsa_device.inhandle, hw_params, - SND_PCM_ACCESS_RW_INTERLEAVED); - check_error(err, "snd_pcm_hw_params_set_access (input)"); - // Try to set 32 bit format first - err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, - SND_PCM_FORMAT_S32); - if (err < 0) - { - /* fprintf(stderr, - "PD-ALSA: 32 bit format not available - using 16\n"); */ - err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, - SND_PCM_FORMAT_S16); - check_error(err, "snd_pcm_hw_params_set_format (input)"); - alsa_samplewidth = 2; - } - else - { - alsa_samplewidth = 4; - } - post("Sample width set to %d bytes", alsa_samplewidth); - // set the subformat - err = snd_pcm_hw_params_set_subformat(alsa_device.inhandle, hw_params, - SND_PCM_SUBFORMAT_STD); - check_error(err, "snd_pcm_hw_params_set_subformat (input)"); - // set the number of channels - tmp_uint = inchans; - err = snd_pcm_hw_params_set_channels_min(alsa_device.inhandle, - hw_params, &tmp_uint); - check_error(err, "snd_pcm_hw_params_set_channels (input)"); - if (tmp_uint != (unsigned)inchans) - post("ALSA: set input channels to %d", tmp_uint); - inchans = tmp_uint; - // set the sampling rate - err = snd_pcm_hw_params_set_rate_min(alsa_device.inhandle, hw_params, - &srate, 0); - check_error(err, "snd_pcm_hw_params_set_rate_min (input)"); -#if 0 - err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); - post("input sample rate %d", err); -#endif - // set the period - ie frag size - // post("fragsize a %d", frag_size); - - /* LATER try this to get a recommended period size... - right now, it trips an assertion failure in ALSA lib */ -#if 0 - post("input period was %d, min %d, max %d\n", - snd_pcm_hw_params_get_period_size(hw_params, 0), - snd_pcm_hw_params_get_period_size_min(hw_params, 0), - snd_pcm_hw_params_get_period_size_max(hw_params, 0)); -#endif - err = snd_pcm_hw_params_set_period_size_near(alsa_device.inhandle, - hw_params, - (snd_pcm_uframes_t) - frag_size, 0); - check_error(err, "snd_pcm_hw_params_set_period_size_near (input)"); - // post("fragsize b %d", frag_size); - // set the number of periods - ie numfrags - // post("nfrags a %d", nfrags); - err = snd_pcm_hw_params_set_periods_near(alsa_device.inhandle, - hw_params, nfrags, 0); - check_error(err, "snd_pcm_hw_params_set_periods_near (input)"); - // set the buffer size - err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.inhandle, - hw_params, nfrags * frag_size); - check_error(err, "snd_pcm_hw_params_set_buffer_size_near (input)"); - - err = snd_pcm_hw_params(alsa_device.inhandle, hw_params); - check_error(err, "snd_pcm_hw_params (input)"); - - snd_pcm_hw_params_free(hw_params); - - err = snd_pcm_sw_params_malloc(&sw_params); - check_error(err, "snd_pcm_sw_params_malloc (input)"); - err = snd_pcm_sw_params_current(alsa_device.inhandle, sw_params); - check_error(err, "snd_pcm_sw_params_current (input)"); -#if 1 - err = snd_pcm_sw_params_set_start_mode(alsa_device.inhandle, sw_params, - SND_PCM_START_EXPLICIT); - check_error(err, "snd_pcm_sw_params_set_start_mode (input)"); - err = snd_pcm_sw_params_set_xrun_mode(alsa_device.inhandle, sw_params, - SND_PCM_XRUN_NONE); - check_error(err, "snd_pcm_sw_params_set_xrun_mode (input)"); -#else - err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle, - sw_params, nfrags * frag_size); - check_error(err, "snd_pcm_sw_params_set_start_threshold (input)"); - err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle, - sw_params, 1); - check_error(err, "snd_pcm_sw_params_set_stop_threshold (input)"); -#endif - - err = snd_pcm_sw_params_set_avail_min(alsa_device.inhandle, sw_params, - frag_size); - check_error(err, "snd_pcm_sw_params_set_avail_min (input)"); - err = snd_pcm_sw_params(alsa_device.inhandle, sw_params); - check_error(err, "snd_pcm_sw_params (input)"); - - snd_pcm_sw_params_free(sw_params); - - snd_output_stdio_attach(&out, stderr, 0); -#if 0 - if (sys_verbose) - { - snd_pcm_dump_hw_setup(alsa_device.inhandle, out); - snd_pcm_dump_sw_setup(alsa_device.inhandle, out); - } -#endif - } - - if (outchans) - { - int foo; - if (sys_verbose) - post("opening sound output..."); - err = snd_pcm_hw_params_malloc(&hw_params); - check_error(err, "snd_pcm_sw_params (output)"); - - // get the default params - err = snd_pcm_hw_params_any(alsa_device.outhandle, hw_params); - check_error(err, "snd_pcm_hw_params_any (output)"); - // set interleaved access - FIXME deal with other access types - err = snd_pcm_hw_params_set_access(alsa_device.outhandle, hw_params, - SND_PCM_ACCESS_RW_INTERLEAVED); - check_error(err, "snd_pcm_hw_params_set_access (output)"); - // Try to set 32 bit format first - err = snd_pcm_hw_params_set_format(alsa_device.outhandle, hw_params, - SND_PCM_FORMAT_S32); - if (err < 0) - { - err = snd_pcm_hw_params_set_format(alsa_device.outhandle, - hw_params,SND_PCM_FORMAT_S16); - check_error(err, "snd_pcm_hw_params_set_format (output)"); - /* fprintf(stderr, - "PD-ALSA: 32 bit format not available - using 16\n"); */ - alsa_samplewidth = 2; - } - else - { - alsa_samplewidth = 4; - } - // set the subformat - err = snd_pcm_hw_params_set_subformat(alsa_device.outhandle, hw_params, - SND_PCM_SUBFORMAT_STD); - check_error(err, "snd_pcm_hw_params_set_subformat (output)"); - // set the number of channels - tmp_uint = outchans; - err = snd_pcm_hw_params_set_channels_min(alsa_device.outhandle, - hw_params, &tmp_uint); - check_error(err, "snd_pcm_hw_params_set_channels (output)"); - if (tmp_uint != (unsigned)outchans) - post("alsa: set output channels to %d", tmp_uint); - outchans = tmp_uint; - // set the sampling rate - err = snd_pcm_hw_params_set_rate_min(alsa_device.outhandle, hw_params, - &srate, 0); - check_error(err, "snd_pcm_hw_params_set_rate_min (output)"); -#if 0 - err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); - post("output sample rate %d", err); -#endif - // set the period - ie frag size -#if 0 - post("output period was %d, min %d, max %d\n", - snd_pcm_hw_params_get_period_size(hw_params, 0), - snd_pcm_hw_params_get_period_size_min(hw_params, 0), - snd_pcm_hw_params_get_period_size_max(hw_params, 0)); -#endif - // post("fragsize c %d", frag_size); - err = snd_pcm_hw_params_set_period_size_near(alsa_device.outhandle, - hw_params, - (snd_pcm_uframes_t) - frag_size, 0); - // post("fragsize d %d", frag_size); - check_error(err, "snd_pcm_hw_params_set_period_size_near (output)"); - // set the number of periods - ie numfrags - err = snd_pcm_hw_params_set_periods_near(alsa_device.outhandle, - hw_params, nfrags, 0); - check_error(err, "snd_pcm_hw_params_set_periods_near (output)"); - // set the buffer size - err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.outhandle, - hw_params, nfrags * frag_size); - - check_error(err, "snd_pcm_hw_params_set_buffer_size_near (output)"); - - err = snd_pcm_hw_params(alsa_device.outhandle, hw_params); - check_error(err, "snd_pcm_hw_params (output)"); - - snd_pcm_hw_params_free(hw_params); - - err = snd_pcm_sw_params_malloc(&sw_params); - check_error(err, "snd_pcm_sw_params_malloc (output)"); - err = snd_pcm_sw_params_current(alsa_device.outhandle, sw_params); - check_error(err, "snd_pcm_sw_params_current (output)"); -#if 1 - err = snd_pcm_sw_params_set_start_mode(alsa_device.outhandle, - sw_params, - SND_PCM_START_EXPLICIT); - check_error(err, "snd_pcm_sw_params_set_start_mode (output)"); - err = snd_pcm_sw_params_set_xrun_mode(alsa_device.outhandle, sw_params, - SND_PCM_XRUN_NONE); - check_error(err, "snd_pcm_sw_params_set_xrun_mode (output)"); -#else - err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle, - sw_params, nfrags * frag_size); - check_error(err, "snd_pcm_sw_params_set_start_threshold (output)"); - err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle, - sw_params, 1); - check_error(err, "snd_pcm_sw_params_set_stop_threshold (output)"); -#endif - - err = snd_pcm_sw_params_set_avail_min(alsa_device.outhandle, sw_params, - frag_size); - check_error(err, "snd_pcm_sw_params_set_avail_min (output)"); - err = snd_pcm_sw_params(alsa_device.outhandle, sw_params); - check_error(err, "snd_pcm_sw_params (output)"); - - snd_pcm_sw_params_free(sw_params); - - snd_output_stdio_attach(&out, stderr, 0); -#if 0 - if (sys_verbose) - { - snd_pcm_dump_hw_setup(alsa_device.outhandle, out); - snd_pcm_dump_sw_setup(alsa_device.outhandle, out); - } -#endif - } - - linux_setsr(srate); - linux_setch(inchans, outchans); - - if (inchans) - snd_pcm_prepare(alsa_device.inhandle); - if (outchans) - snd_pcm_prepare(alsa_device.outhandle); - - // if duplex we can link the channels so they start together - if (inchans && outchans) - snd_pcm_link(alsa_device.inhandle, alsa_device.outhandle); - - // set up the buffer - if (outchans > inchans) - alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE - * outchans); - else - alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE - * inchans); - // fill the buffer with silence - if (outchans) - { - i = nfrags + 1; - while (i--) - snd_pcm_writei(alsa_device.outhandle, alsa_buf, frag_size); - } - - // set up the status variables - err = snd_pcm_status_malloc(&in_status); - check_error(err, "snd_pcm_status_malloc"); - err = snd_pcm_status_malloc(&out_status); - check_error(err, "snd_pcm_status_malloc"); - - // start the device -#if 1 - if (outchans) - { - err = snd_pcm_start(alsa_device.outhandle); - check_error(err, "snd_pcm_start"); - } - else if (inchans) - { - err = snd_pcm_start(alsa_device.inhandle); - check_error(err, "snd_pcm_start"); - } -#endif - - return 0; -} - -void alsa_close_audio(void) -{ - int err; - if (linux_inchannels) - { - err = snd_pcm_close(alsa_device.inhandle); - check_error(err, "snd_pcm_close (input)"); - } - if (linux_outchannels) - { - err = snd_pcm_close(alsa_device.outhandle); - check_error(err, "snd_pcm_close (output)"); - } -} - -// #define DEBUG_ALSA_XFER - -int alsa_send_dacs(void) -{ - static int16_t *sp; - static int xferno = 0; - static int callno = 0; - static double timenow; - double timelast; - t_sample *fp, *fp1, *fp2; - int i, j, k, err, devno = 0; - int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0; - int result; - int inchannels = linux_inchannels; - int outchannels = linux_outchannels; - unsigned int intransfersize = DACBLKSIZE; - unsigned int outtransfersize = DACBLKSIZE; - - // get the status - if (!inchannels && !outchannels) - { - return SENDDACS_NO; - } - - timelast = timenow; - timenow = sys_getrealtime(); - -#ifdef DEBUG_ALSA_XFER - if (timenow - timelast > 0.050) - fprintf(stderr, "(%d)", - (int)(1000 * (timenow - timelast))), fflush(stderr); -#endif - - callno++; - - if (inchannels) - { - snd_pcm_status(alsa_device.inhandle, in_status); - if (snd_pcm_status_get_avail(in_status) < intransfersize) - return SENDDACS_NO; - } - if (outchannels) - { - snd_pcm_status(alsa_device.outhandle, out_status); - if (snd_pcm_status_get_avail(out_status) < outtransfersize) - return SENDDACS_NO; - } - - /* do output */ - if (outchannels) - { - fp = sys_soundout; - if (alsa_samplewidth == 4) - { - for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) - { - for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; - j += outchannels, fp2++) - { - float s1 = *fp2 * INT32_MAX; - ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1); - } - } - } - else - { - for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) - { - for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; - j += outchannels, fp2++) - { - int s = *fp2 * 32767.; - if (s > 32767) - s = 32767; - else if (s < -32767) - s = -32767; - ((t_alsa_sample16 *)alsa_buf)[j] = s; - } - } - } - - result = snd_pcm_writei(alsa_device.outhandle, alsa_buf, - outtransfersize); - if (result != (int)outtransfersize) - { - #ifdef DEBUG_ALSA_XFER - if (result >= 0 || errno == EAGAIN) - fprintf(stderr, "ALSA: write returned %d of %d\n", - result, outtransfersize); - else fprintf(stderr, "ALSA: write: %s\n", - snd_strerror(errno)); - fprintf(stderr, - "inputcount %d, outputcount %d, outbufsize %d\n", - inputcount, outputcount, - (ALSA_EXTRABUFFER + linux_advance_samples) - * alsa_samplewidth * outchannels); - #endif - sys_log_error(ERR_DACSLEPT); - return (SENDDACS_NO); - } - - /* zero out the output buffer */ - memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) * - linux_outchannels); - if (sys_getrealtime() - timenow > 0.002) - { - #ifdef DEBUG_ALSA_XFER - fprintf(stderr, "output %d took %d msec\n", - callno, (int)(1000 * (timenow - timelast))), fflush(stderr); - #endif - timenow = sys_getrealtime(); - sys_log_error(ERR_DACSLEPT); - } - } - /* do input */ - if (linux_inchannels) - { - result = snd_pcm_readi(alsa_device.inhandle, alsa_buf, intransfersize); - if (result < (int)intransfersize) - { -#ifdef DEBUG_ALSA_XFER - if (result < 0) - fprintf(stderr, - "snd_pcm_read %d %d: %s\n", - callno, xferno, snd_strerror(errno)); - else fprintf(stderr, - "snd_pcm_read %d %d returned only %d\n", - callno, xferno, result); - fprintf(stderr, - "inputcount %d, outputcount %d, inbufsize %d\n", - inputcount, outputcount, - (ALSA_EXTRABUFFER + linux_advance_samples) - * alsa_samplewidth * inchannels); -#endif - sys_log_error(ERR_ADCSLEPT); - return (SENDDACS_NO); - } - fp = sys_soundin; - if (alsa_samplewidth == 4) - { - for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) - { - for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; - j += inchannels, fp2++) - *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j] - * (1./ INT32_MAX); - } - } - else - { - for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) - { - for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels, - fp2++) - *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j] - * 3.051850e-05; - } - } - } - xferno++; - if (sys_getrealtime() - timenow > 0.002) - { -#ifdef DEBUG_ALSA_XFER - fprintf(stderr, "routine took %d msec\n", - (int)(1000 * (sys_getrealtime() - timenow))); -#endif - sys_log_error(ERR_ADCSLEPT); - } - return SENDDACS_YES; -} - -void alsa_resync( void) -{ - int i, result; - if (linux_whichapi != API_ALSA) - { - error("restart-audio: implemented for ALSA only."); - return; - } - memset(alsa_buf, 0, - sizeof(char) * alsa_samplewidth * DACBLKSIZE * linux_outchannels); - for (i = 0; i < 1000000; i++) - { - result = snd_pcm_writei(alsa_device.outhandle, alsa_buf, - DACBLKSIZE); - if (result != (int)DACBLKSIZE) - break; - } - post("%d written", i); -} - - -#endif /* ALSA01 */ - -/*************************************************** - * Code using the RME_9652 API - */ - - /* - trying native device for future use of native memory map: - because of busmaster if you dont use the dac, you dont need - CPU Power und also no nearly no CPU-Power is used in device - - since always all DAs and ADs are synced (else they wouldnt work) - we use linux_dacs[0], linux_adcs[0] - */ - -#ifdef RME_HAMMERFALL - -#define RME9652_MAX_CHANNELS 26 - -#define RME9652_CH_PER_NATIVE_DEVICE 1 - -static int rme9652_dac_devices[RME9652_MAX_CHANNELS]; -static int rme9652_adc_devices[RME9652_MAX_CHANNELS]; - -static char rme9652_dsp_dac[] = "/dev/rme9652/C0da%d"; -static char rme9652_dsp_adc[] = "/dev/rme9652/C0ad%d"; - -static int num_of_rme9652_dac = 0; -static int num_of_rme9652_adc = 0; - -static int rme_soundindevonset = 1; -static int rme_soundoutdevonset = 1; - -void rme_soundindev(int which) -{ - rme_soundindevonset = which; -} - -void rme_soundoutdev(int which) -{ - rme_soundoutdevonset = which; -} - -void rme9652_configure(int dev, int fd,int srate, int dac) { - int orig, param, nblk; - audio_buf_info ainfo; - orig = param = srate; - - /* samplerate */ - - fprintf(stderr,"RME9652: configuring %d, fd=%d, sr=%d\n, dac=%d\n", - dev,fd,srate,dac); - - if (ioctl(fd,SNDCTL_DSP_SPEED,¶m) == -1) - fprintf(stderr,"RME9652: Could not set sampling rate for device\n"); - else if( orig != param ) - fprintf(stderr,"RME9652: sampling rate: wanted %d, got %d\n", - orig, param ); - - // setting the correct samplerate (could be different than expected) - srate = param; - - - /* setting resolution */ - - /* use ctrlpanel to change, experiment, channels 1 */ - - orig = param = AFMT_S16_NE; - if (ioctl(fd,SNDCTL_DSP_SETFMT,¶m) == -1) - fprintf(stderr,"RME9652: Could not set DSP format\n"); - else if( orig != param ) - fprintf(stderr,"RME9652: DSP format: wanted %d, got %d\n",orig, param ); - - /* setting channels */ - orig = param = RME9652_CH_PER_NATIVE_DEVICE; - - if (ioctl(fd,SNDCTL_DSP_CHANNELS,¶m) == -1) - fprintf(stderr,"RME9652: Could not set channels\n"); - else if( orig != param ) - fprintf(stderr,"RME9652: num channels: wanted %d, got %d\n",orig, param ); - - 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. */ - - if( ioctl(linux_dacs[0].d_fd, SOUND_PCM_GETOSPACE,&ainfo) < 0 ) - fprintf(stderr,"RME: ioctl on output device %d failed",dev); - - linux_dacs[0].d_bufsize = ainfo.bytes; - - fprintf(stderr,"RME: ioctl SOUND_PCM_GETOSPACE says %d buffsize\n", - linux_dacs[0].d_bufsize); - - - if (linux_advance_samples * (RME_SAMPLEWIDTH * - RME9652_CH_PER_NATIVE_DEVICE) - > linux_dacs[0].d_bufsize - RME_BYTESPERCHAN) - { - fprintf(stderr, - "RME: requested audio buffer size %d limited to %d\n", - linux_advance_samples - * (RME_SAMPLEWIDTH * RME9652_CH_PER_NATIVE_DEVICE), - linux_dacs[0].d_bufsize); - linux_advance_samples = - (linux_dacs[0].d_bufsize - RME_BYTESPERCHAN) - / (RME_SAMPLEWIDTH *RME9652_CH_PER_NATIVE_DEVICE); - } - } -} - - -int rme9652_open_audio(int inchans, int outchans,int srate) -{ - int orig; - int tmp; - int inchannels = 0,outchannels = 0; - char devname[20]; - int i; - char buf[RME_SAMPLEWIDTH*RME9652_CH_PER_NATIVE_DEVICE*DACBLKSIZE]; - int num_devs = 0; - audio_buf_info ainfo; - - linux_nindevs = linux_noutdevs = 0; - - if (sys_verbose) - post("RME open"); - /* First check if we can */ - /* open the write ports */ - - for (num_devs=0; outchannels < outchans; num_devs++) - { - int channels = RME9652_CH_PER_NATIVE_DEVICE; - - sprintf(devname, rme9652_dsp_dac, num_devs + rme_soundoutdevonset); - if ((tmp = open(devname,O_WRONLY)) == -1) - { - DEBUG(fprintf(stderr,"RME9652: failed to open %s writeonly\n", - devname);) - break; - } - DEBUG(fprintf(stderr,"RME9652: out device Nr. %d (%d) on %s\n", - linux_noutdevs+1,tmp,devname);) - - if (outchans > outchannels) - { - rme9652_dac_devices[linux_noutdevs] = tmp; - linux_noutdevs++; - outchannels += channels; - } - else close(tmp); - } - if( linux_noutdevs > 0) - linux_dacs[0].d_fd = rme9652_dac_devices[0]; - - /* Second check if we can */ - /* open the read ports */ - - for (num_devs=0; inchannels < inchans; num_devs++) - { - int channels = RME9652_CH_PER_NATIVE_DEVICE; - - sprintf(devname, rme9652_dsp_adc, num_devs+rme_soundindevonset); - - if ((tmp = open(devname,O_RDONLY)) == -1) - { - DEBUG(fprintf(stderr,"RME9652: failed to open %s readonly\n", - devname);) - break; - } - DEBUG(fprintf(stderr,"RME9652: in device Nr. %d (%d) on %s\n", - linux_nindevs+1,tmp,devname);) - - if (inchans > inchannels) - { - rme9652_adc_devices[linux_nindevs] = tmp; - linux_nindevs++; - inchannels += channels; - } - else - close(tmp); - } - if(linux_nindevs > 0) - linux_adcs[0].d_fd = rme9652_adc_devices[0]; - - /* configure soundcards */ - - rme9652_configure(0, linux_adcs[0].d_fd,srate, 0); - rme9652_configure(0, linux_dacs[0].d_fd,srate, 1); - - /* 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 */ - - if (linux_nindevs) - { - fprintf(stderr,("RME9652: starting read engine ... ")); - - - for (num_devs=0; num_devs < linux_nindevs; num_devs++) - read(rme9652_adc_devices[num_devs], - buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE* - DACBLKSIZE); - - - for (num_devs=0; num_devs < linux_noutdevs; num_devs++) - write(rme9652_dac_devices[num_devs], - buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE* - DACBLKSIZE); - - if(linux_noutdevs) - ioctl(rme9652_dac_devices[0],SNDCTL_DSP_SYNC); - - fprintf(stderr,"done\n"); - } - - linux_setsr(srate); - linux_setch(linux_nindevs, linux_noutdevs); - - num_of_rme9652_dac = linux_noutdevs; - num_of_rme9652_adc = linux_nindevs; - - if(linux_noutdevs)linux_noutdevs=1; - if(linux_nindevs)linux_nindevs=1; - - /* trick RME9652 behaves as one device fromread write pointers */ - return (0); -} - -void rme9652_close_audio( void) -{ - int i; - for (i=0;i>2;i--;) - { - float s1 = *(fp1+=4) * INT32_MAX; - float s2 = *(fp2+=4) * INT32_MAX; - float s3 = *(fp3+=4) * INT32_MAX; - float s4 = *(fp4+=4) * INT32_MAX; - - *(a+=4) = CLIP32(s1); - *(b+=4) = CLIP32(s2); - *(c+=4) = CLIP32(s3); - *(d+=4) = CLIP32(s4); - } - - linux_dacs_write(rme9652_dac_devices[j],buf,RME_BYTESPERCHAN); - } - } - - if ((timenow = sys_getrealtime()) - timeref > 0.02) - sys_log_error(ERR_DACSLEPT); - timeref = timenow; - } - - memset(sys_soundout, 0, - linux_outchannels * (sizeof(float) * DACBLKSIZE)); - - /* do input */ - - if(linux_nindevs) { - - for(j=0;j 0.02) - sys_log_error(ERR_ADCSLEPT); - timeref = timenow; - { - t_rme_sample *a,*b,*c,*d; - float *fp1,*fp2,*fp3,*fp4; - - fp1 = sys_soundin + j*DACBLKSIZE-4; - fp2 = fp1 + 1; - fp3 = fp1 + 2; - fp4 = fp1 + 3; - a = buf-4; - b=a+1; - c=a+2; - d=a+3; - - for (i = (DACBLKSIZE>>2);i--;) - { - *(fp1+=4) = *(a+=4) * (float)(1./INT32_MAX); - *(fp2+=4) = *(b+=4) * (float)(1./INT32_MAX); - *(fp3+=4) = *(c+=4) * (float)(1./INT32_MAX); - *(fp4+=4) = *(d+=4) * (float)(1./INT32_MAX); - } - } - } - } - /* fprintf(stderr,"ready \n");*/ - - return (1); -} - -#endif /* RME_HAMMERFALL */ -- cgit v1.2.1