ref: a9fddb06a4e0fba4ef9fd585626bd674b959934b
dir: /src/oss.c/
#if defined(OSS_PLAYER) /* * Copyright 1997 Chris Bagwell And Sundry Contributors * This source code is freely redistributable and may be used for * any purpose. This copyright notice must be maintained. * Chris Bagwell And Sundry Contributors are not * responsible for the consequences of using this software. */ /* Direct to Open Sound System (OSS) sound driver * OSS is a popular unix sound driver for Intel x86 unices (eg. Linux) * and several other unixes (such as SunOS/Solaris). * This driver is compatible with OSS original source that was called * USS, Voxware and TASD. * * added by Chris Bagwell (cbagwell@sprynet.com) on 2/19/96 * based on info grabed from vplay.c in Voxware snd-utils-3.5 package. * and on LINUX_PLAYER patches added by Greg Lee * which was originally from Directo to Sound Blaster device driver (sbdsp.c). * SBLAST patches by John T. Kohl. */ #include <malloc.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <sys/soundcard.h> #include <sys/ioctl.h> #include <signal.h> #include "st.h" #include "libst.h" static int got_int = 0; static int abuf_size = 0; static int abuf_cnt = 0; static char *audiobuf; /* This is how we know when to stop recording. User sends interrupt * (eg. control-c) and then we mark a flag to show we are done. * Must call "sigint(0)" during init so that the OS can be notified * what to do. */ static void sigint(s) { if (s) got_int = 1; else signal(SIGINT, sigint); } /* * Do anything required before you start reading samples. * Read file header. * Find out sampling rate, * size and style of samples, * mono/stereo/quad. */ void ossdspstartread(ft) ft_t ft; { int tmp; int samplesize = 8, dsp_stereo; if (ft->info.rate == 0.0) ft->info.rate = 8000; if (ft->info.size == -1) ft->info.size = BYTE; if (ft->info.size == BYTE) { samplesize = 8; if (ft->info.style == -1) ft->info.style = UNSIGNED; if (ft->info.style != UNSIGNED) { fail("OSS driver only supports unsigned with bytes"); } } else if (ft->info.size == WORD) { samplesize = 16; if (ft->info.style == -1) ft->info.style = SIGN2; if (ft->info.style != SIGN2) { fail("OSS driver only supports signed with words"); } } else { fail("OSS driver only supports bytes and words"); } if (ft->info.channels == -1) ft->info.channels = 1; else if (ft->info.channels > 2) ft->info.channels = 2; ioctl(fileno(ft->fp), SNDCTL_DSP_RESET, 0); ioctl (fileno(ft->fp), SNDCTL_DSP_GETBLKSIZE, &abuf_size); if (abuf_size < 4 || abuf_size > 65536) { fail("Invalid audio buffer size %d", abuf_size); } if ((audiobuf = malloc (abuf_size)) == NULL) { fail("Unable to allocate input/output buffer of size %d", abuf_size); } if (ioctl(fileno(ft->fp), SNDCTL_DSP_SYNC, NULL) < 0) { fail("Unable to sync dsp"); } tmp = samplesize; ioctl(fileno(ft->fp), SNDCTL_DSP_SAMPLESIZE, &tmp); if (tmp != samplesize) { fail("Unable to set the sample size to %d", samplesize); } if (ft->info.channels == 2) dsp_stereo = 1; else dsp_stereo = 0; tmp = dsp_stereo; ioctl(fileno(ft->fp), SNDCTL_DSP_STEREO, &tmp); if (tmp != dsp_stereo) { ft->info.channels = 1; warn("Couldn't set to %s", dsp_stereo? "stereo":"mono"); dsp_stereo = 0; } tmp = ft->info.rate; ioctl (fileno(ft->fp), SNDCTL_DSP_SPEED, &tmp); if (ft->info.rate != tmp) { if (ft->info.rate - tmp > tmp/10 || tmp - ft->info.rate > tmp/10) warn("Unable to set audio speed to %d (set to %d)", ft->info.rate, tmp); ft->info.rate = tmp; } sigint(0); /* Prepare to catch SIGINT */ } int dspget(ft) ft_t ft; { int rval; if (abuf_cnt < 1) { abuf_cnt = read (fileno(ft->fp), (char *)audiobuf, abuf_size); if (abuf_cnt == 0) { got_int = 1; /* Act like user said end record */ return(0); } } rval = *(audiobuf + (abuf_size-abuf_cnt)); abuf_cnt--; return(rval); } /* Read short. */ unsigned short dsprshort(ft) ft_t ft; { unsigned short rval; if (abuf_cnt < 2) { abuf_cnt = read (fileno(ft->fp), (char *)audiobuf, abuf_size); if (abuf_cnt == 0) { got_int = 1; /* act like user said end recording */ return(0); } } rval = *((unsigned short *)(audiobuf + (abuf_size-abuf_cnt))); abuf_cnt -= 2; return(rval); } /* * Read up to len samples from file. * Convert to signed longs. * Place in buf[]. * Return number of samples read. */ LONG ossdspread(ft, buf, len) ft_t ft; LONG *buf, len; { register int datum; int done = 0; if (got_int) return(0); /* Return with length 0 read so program will end */ switch(ft->info.size) { case BYTE: switch(ft->info.style) { case SIGN2: while(done < len) { datum = dspget(ft); if (got_int || feof(ft->fp)) return(done); /* scale signed up to long's range */ *buf++ = LEFT(datum, 24); done++; } return done; case UNSIGNED: while(done < len) { datum = dspget(ft); if (got_int || feof(ft->fp)) return(done); /* Convert to unsigned */ datum ^= 128; /* scale signed up to long's range */ *buf++ = LEFT(datum, 24); done++; } return done; case ULAW: /* grab table from Posk stuff */ while(done < len) { datum = dspget(ft); if (got_int || feof(ft->fp)) return(done); datum = st_ulaw_to_linear(datum); /* scale signed up to long's range */ *buf++ = LEFT(datum, 16); done++; } return done; case ALAW: while(done < len) { datum = dspget(ft); if (got_int || feof(ft->fp)) return(done); datum = st_Alaw_to_linear(datum); /* scale signed up to long's range */ *buf++ = LEFT(datum, 16); done++; } return done; } case WORD: switch(ft->info.style) { case SIGN2: while(done < len) { datum = dsprshort(ft); if (got_int || feof(ft->fp)) return(done); /* scale signed up to long's range */ *buf++ = LEFT(datum, 16); done++; } return done; case UNSIGNED: while(done < len) { datum = dsprshort(ft); if (got_int || feof(ft->fp)) return(done); /* Convert to unsigned */ datum ^= 0x8000; /* scale signed up to long's range */ *buf++ = LEFT(datum, 16); done++; } return done; case ULAW: fail("No U-Law support for shorts"); return done; case ALAW: fail("No A-Law support"); return done; } } fail("Drop through in ossdspread!"); /* Return number of samples read */ return(done); } /* * Do anything required when you stop reading samples. * Don't close input file! */ void ossdspstopread(ft) ft_t ft; { } void ossdspstartwrite(ft) ft_t ft; { int samplesize = 8, dsp_stereo; int tmp; if (ft->info.rate == 0.0) ft->info.rate = 8000; if (ft->info.size == -1) ft->info.size = BYTE; if (ft->info.size == BYTE) { samplesize = 8; if (ft->info.style == -1) ft->info.style = UNSIGNED; if (ft->info.style != UNSIGNED) { report("OSS driver only supports unsigned with bytes"); report("Forcing to unsigned"); ft->info.style = UNSIGNED; } } else if (ft->info.size == WORD) { samplesize = 16; if (ft->info.style == -1) ft->info.style = SIGN2; if (ft->info.style != SIGN2) { report("OSS driver only supports signed with words"); report("Forcing to signed linear"); ft->info.style = SIGN2; } } else { ft->info.size = WORD; ft->info.style = SIGN2; report("OSS driver only supports bytes and words"); report("Forcing to signed linear word"); } if (ft->info.channels == -1) ft->info.channels = 1; else if (ft->info.channels > 2) ft->info.channels = 2; ioctl(fileno(ft->fp), SNDCTL_DSP_RESET, 0); ioctl (fileno(ft->fp), SNDCTL_DSP_GETBLKSIZE, &abuf_size); if (abuf_size < 4 || abuf_size > 65536) { fail("Invalid audio buffer size %d", abuf_size); } if ((audiobuf = malloc (abuf_size)) == NULL) { fail("Unable to allocate input/output buffer of size %d", abuf_size); } if (ioctl(fileno(ft->fp), SNDCTL_DSP_SYNC, NULL) < 0) { fail("Unable to sync dsp"); } tmp = samplesize; ioctl(fileno(ft->fp), SNDCTL_DSP_SAMPLESIZE, &tmp); if (tmp != samplesize) { fail("Unable to set the sample size to %d", samplesize); } if (ft->info.channels == 2) dsp_stereo = 1; else dsp_stereo = 0; tmp = dsp_stereo; ioctl(fileno(ft->fp), SNDCTL_DSP_STEREO, &tmp); if (tmp != dsp_stereo) { ft->info.channels = 1; warn("Couldn't set to %s", dsp_stereo? "stereo":"mono"); dsp_stereo = 0; } tmp = ft->info.rate; ioctl (fileno(ft->fp), SNDCTL_DSP_SPEED, &tmp); if (ft->info.rate != tmp) { if (ft->info.rate - tmp > tmp/10 || tmp - ft->info.rate > tmp/10) warn("Unable to set audio speed to %d (set to %d)", ft->info.rate, tmp); ft->info.rate = tmp; } } void dspflush(ft) ft_t ft; { if (write (fileno(ft->fp), audiobuf, abuf_cnt) != abuf_cnt) { fail("Error writing to sound driver"); } abuf_cnt = 0; } void dspput(ft,c) ft_t ft; int c; { if (abuf_cnt > abuf_size-1) dspflush(ft); *(audiobuf + abuf_cnt) = c; abuf_cnt++; } /* Write short. */ void dspshort(ft,ui) ft_t ft; unsigned short ui; { if (abuf_cnt > abuf_size-2) dspflush(ft); *((unsigned short *)(audiobuf + abuf_cnt)) = ui; abuf_cnt += 2; } void ossdspwrite(ft, buf, len) ft_t ft; LONG *buf, len; { register int datum; int done = 0; switch(ft->info.size) { case BYTE: switch(ft->info.style) { case SIGN2: while(done < len) { /* scale signed up to long's range */ datum = RIGHT(*buf++, 24); dspput(ft,datum); done++; } return; case UNSIGNED: while(done < len) { /* scale signed up to long's range */ datum = RIGHT(*buf++, 24); /* Convert to unsigned */ datum ^= 128; dspput(ft,datum); done++; } return; case ULAW: /* grab table from Posk stuff */ while(done < len) { /* scale signed up to long's range */ datum = RIGHT(*buf++, 16); datum = st_linear_to_ulaw(datum); dspput(ft,datum); done++; } return; case ALAW: while(done < len) { /* scale signed up to long's range */ datum = RIGHT(*buf++, 16); /* round up to 12 bits of data */ datum += 0x8; /* + 0b1000 */ datum = st_linear_to_Alaw(datum); dspput(ft,datum); done++; } return; } case WORD: switch(ft->info.style) { case SIGN2: while(done < len) { /* scale signed up to long's range */ datum = RIGHT(*buf++, 16); dspshort(ft,datum); done++; } return; case UNSIGNED: while(done < len) { /* scale signed up to long's range */ datum = RIGHT(*buf++, 16); /* Convert to unsigned */ datum ^= 0x8000; dspshort(ft,datum); done++; } return; case ULAW: fail("No U-Law support for shorts"); return; case ALAW: fail("No A-Law support"); return; } } fail("Drop through in ossdspwrite!"); } void ossdspstopwrite(ft) ft_t ft; { dspflush(ft); } #endif