mirror of https://github.com/ericonr/sndio.git
193 lines
5.3 KiB
C
193 lines
5.3 KiB
C
/* $OpenBSD: legacy.c,v 1.12 2010/04/06 20:07:01 ratchov Exp $ */
|
|
/*
|
|
* Copyright (c) 1997 Kenneth Stailey. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Kenneth Stailey.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sndio.h>
|
|
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "wav.h"
|
|
|
|
/*
|
|
* Headerless data files. Played at /dev/audio's defaults.
|
|
*/
|
|
#define FMT_RAW 0
|
|
|
|
/*
|
|
* Sun/NeXT .au files. Header is skipped and /dev/audio is configured
|
|
* for monaural 8-bit ulaw @ 8kHz, the de facto format for .au files,
|
|
* as well as the historical default configuration for /dev/audio.
|
|
*/
|
|
#define FMT_AU 1
|
|
|
|
/*
|
|
* RIFF WAV files. Header is parsed for format details which are
|
|
* applied to /dev/audio.
|
|
*/
|
|
#define FMT_WAV 2
|
|
|
|
|
|
int
|
|
legacy_play(char *dev, char *aufile)
|
|
{
|
|
struct sio_hdl *hdl;
|
|
struct sio_par spar, par;
|
|
struct aparams apar;
|
|
ssize_t rd;
|
|
off_t datasz, dummy;
|
|
char buf[5120];
|
|
size_t readsz;
|
|
int fd, fmt = FMT_RAW;
|
|
u_int32_t pos = 0, snd_fmt = 1, rate = 8000, chan = 1;
|
|
char magic[4];
|
|
short *map;
|
|
|
|
if ((fd = open(aufile, O_RDONLY)) < 0) {
|
|
warn("cannot open %s", aufile);
|
|
return(1);
|
|
}
|
|
|
|
if (read(fd, magic, sizeof(magic)) != sizeof(magic)) {
|
|
/*
|
|
* read() error, or the file is smaller than sizeof(magic).
|
|
* treat as a raw file, like previous versions of aucat.
|
|
*/
|
|
} else if (!strncmp(magic, ".snd", 4)) {
|
|
fmt = FMT_AU;
|
|
if (read(fd, &pos, sizeof(pos)) == sizeof(pos))
|
|
pos = ntohl(pos);
|
|
/* data size */
|
|
if (lseek(fd, 4, SEEK_CUR) == -1)
|
|
warn("lseek hdr");
|
|
if (read(fd, &snd_fmt, sizeof(snd_fmt)) == sizeof(snd_fmt))
|
|
snd_fmt = ntohl(snd_fmt);
|
|
if (read(fd, &rate, sizeof(rate)) == sizeof(rate))
|
|
rate = ntohl(rate);
|
|
if (read(fd, &chan, sizeof(chan)) == sizeof(chan))
|
|
chan = ntohl(chan);
|
|
} else if (!strncmp(magic, "RIFF", 4) &&
|
|
wav_readhdr(fd, &apar, &dummy, &datasz, &map)) {
|
|
fmt = FMT_WAV;
|
|
}
|
|
|
|
/*
|
|
* Seek to start of audio data. wav_readhdr already took care
|
|
* of this for FMT_WAV.
|
|
*/
|
|
if (fmt == FMT_RAW || fmt == FMT_AU)
|
|
if (lseek(fd, (off_t)pos, SEEK_SET) == -1)
|
|
warn("lseek");
|
|
|
|
if ((hdl = sio_open(dev, SIO_PLAY, 0)) == NULL) {
|
|
warnx("can't get sndio handle");
|
|
return(1);
|
|
}
|
|
|
|
sio_initpar(&par);
|
|
switch(fmt) {
|
|
case FMT_WAV:
|
|
par.rate = apar.rate;
|
|
par.pchan = apar.cmax - apar.cmin + 1;
|
|
par.sig = apar.sig;
|
|
par.bits = apar.bits;
|
|
par.le = apar.le;
|
|
break;
|
|
case FMT_AU:
|
|
par.rate = rate;
|
|
par.pchan = chan;
|
|
par.sig = 1;
|
|
par.bits = 16;
|
|
par.le = SIO_LE_NATIVE;
|
|
map = wav_ulawmap;
|
|
if (snd_fmt == 27)
|
|
map = wav_alawmap;
|
|
break;
|
|
case FMT_RAW:
|
|
default:
|
|
break;
|
|
}
|
|
spar = par;
|
|
|
|
if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par)) {
|
|
warnx("can't set audio parameters");
|
|
/*
|
|
* Only WAV could fail in previous aucat versions (unless
|
|
* the parameters returned by AUDIO_GETINFO would fail,
|
|
* which is unlikely).
|
|
*/
|
|
if (fmt == FMT_WAV)
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* Parameters may be silently modified. See audio(9)'s
|
|
* description of set_params. For compatability with previous
|
|
* aucat versions, continue running if something doesn't match.
|
|
*/
|
|
if (par.bits != spar.bits ||
|
|
par.sig != par.sig ||
|
|
par.le != spar.le ||
|
|
par.pchan != spar.pchan ||
|
|
/*
|
|
* Devices may return a very close rate, such as 44099 when
|
|
* 44100 was requested. The difference is inaudible. Allow
|
|
* 2% deviation as an example of how to cope.
|
|
*/
|
|
(par.rate > spar.rate * 1.02 || par.rate < spar.rate * 0.98)) {
|
|
warnx("format not supported");
|
|
}
|
|
if (!sio_start(hdl)) {
|
|
warnx("could not start sndio");
|
|
exit(1);
|
|
}
|
|
|
|
readsz = sizeof(buf);
|
|
if (map)
|
|
readsz /= 2;
|
|
while ((rd = read(fd, buf, readsz)) > 0) {
|
|
if (map) {
|
|
wav_conv(buf, rd, map);
|
|
rd *= 2;
|
|
}
|
|
if (sio_write(hdl, buf, rd) != rd)
|
|
warnx("sio_write: short write");
|
|
}
|
|
if (rd == -1)
|
|
warn("read");
|
|
|
|
sio_close(hdl);
|
|
close(fd);
|
|
|
|
return(0);
|
|
}
|