add support for playing IEEE float32 and float64 RIFF WAVE

samples on platforms that have these types natively, and
can handle signed linear 32 bit samples.  explicitly
disabled on vax, run-or-compile-time sizeof() check
disabled for everyone else

now i can play a float32 .wav file i found.
float64 not tested.

copyright maint, update HISTORY, update audio drivers list.
This commit is contained in:
mrg 2019-11-09 12:46:44 +00:00
parent 7330f729cc
commit 95676db785
6 changed files with 209 additions and 27 deletions

View File

@ -1,7 +1,7 @@
/* $NetBSD: audio.c,v 1.25 2015/08/05 06:54:39 mrg Exp $ */ /* $NetBSD: audio.c,v 1.26 2019/11/09 12:46:44 mrg Exp $ */
/* /*
* Copyright (c) 1999 Matthew R. Green * Copyright (c) 1999, 2013, 2015, 2019 Matthew R. Green
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -32,7 +32,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifndef lint #ifndef lint
__RCSID("$NetBSD: audio.c,v 1.25 2015/08/05 06:54:39 mrg Exp $"); __RCSID("$NetBSD: audio.c,v 1.26 2019/11/09 12:46:44 mrg Exp $");
#endif #endif
@ -110,6 +110,8 @@ static const struct {
{ AudioEmpeg_l2_packets,AUDIO_ENCODING_MPEG_L2_PACKETS }, { AudioEmpeg_l2_packets,AUDIO_ENCODING_MPEG_L2_PACKETS },
{ AudioEmpeg_l2_system, AUDIO_ENCODING_MPEG_L2_SYSTEM }, { AudioEmpeg_l2_system, AUDIO_ENCODING_MPEG_L2_SYSTEM },
{ AudioEac3, AUDIO_ENCODING_AC3 }, { AudioEac3, AUDIO_ENCODING_AC3 },
{ "ieee_float32", AUDIO_ENCODING_LIBAUDIO_FLOAT32 },
{ "ieee_float64", AUDIO_ENCODING_LIBAUDIO_FLOAT64 },
{ NULL, -1 } { NULL, -1 }
}; };

View File

@ -1,7 +1,7 @@
/* $NetBSD: libaudio.h,v 1.20 2015/08/05 06:54:39 mrg Exp $ */ /* $NetBSD: libaudio.h,v 1.21 2019/11/09 12:46:44 mrg Exp $ */
/* /*
* Copyright (c) 1999, 2009 Matthew R. Green * Copyright (c) 1999, 2009, 2013, 2015, 2019 Matthew R. Green
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -36,6 +36,17 @@
int audio_format_from_str (char *); int audio_format_from_str (char *);
/*
* Audio encoding formats; this is a additional to those set
* in sys/audioio.h, but with a large offset to avoid future
* conflicts (additional ones are libaudio-software only.)
*
* This is to support floating-point WAV files. These require
* software conversion to a supported format.
*/
#define AUDIO_ENCODING_LIBAUDIO_FLOAT32 1001 /* 32-bit IEEE FP. */
#define AUDIO_ENCODING_LIBAUDIO_FLOAT64 1002 /* 64-bit IEEE FP. */
/* /*
* We copy the Sun/NeXT on-disk audio header format and document what * We copy the Sun/NeXT on-disk audio header format and document what
* we know of it here. * we know of it here.
@ -107,10 +118,11 @@ int audio_encoding_to_sun (int, int, int *);
#define WAVAUDIO_FILE_MAGIC_FMT ((u_int32_t)0x666d7420) #define WAVAUDIO_FILE_MAGIC_FMT ((u_int32_t)0x666d7420)
#define WAVAUDIO_FILE_MAGIC_DATA ((u_int32_t)0x64617461) #define WAVAUDIO_FILE_MAGIC_DATA ((u_int32_t)0x64617461)
/* purloined from public Microsoft RIFF docs via sox or mplayer */ /* purloined from public Microsoft RIFF docs via sox, mplayer, or directly */
#define WAVE_FORMAT_UNKNOWN (0x0000) #define WAVE_FORMAT_UNKNOWN (0x0000)
#define WAVE_FORMAT_PCM (0x0001) #define WAVE_FORMAT_PCM (0x0001)
#define WAVE_FORMAT_ADPCM (0x0002) #define WAVE_FORMAT_ADPCM (0x0002)
#define WAVE_FORMAT_IEEE_FLOAT (0x0003)
#define WAVE_FORMAT_ALAW (0x0006) #define WAVE_FORMAT_ALAW (0x0006)
#define WAVE_FORMAT_MULAW (0x0007) #define WAVE_FORMAT_MULAW (0x0007)
#define WAVE_FORMAT_OKI_ADPCM (0x0010) #define WAVE_FORMAT_OKI_ADPCM (0x0010)

View File

@ -1,7 +1,7 @@
/* $NetBSD: sun.c,v 1.9 2015/08/05 06:54:39 mrg Exp $ */ /* $NetBSD: sun.c,v 1.10 2019/11/09 12:46:44 mrg Exp $ */
/* /*
* Copyright (c) 2002 Matthew R. Green * Copyright (c) 2002, 2013, 2015 Matthew R. Green
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -32,7 +32,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifndef lint #ifndef lint
__RCSID("$NetBSD: sun.c,v 1.9 2015/08/05 06:54:39 mrg Exp $"); __RCSID("$NetBSD: sun.c,v 1.10 2019/11/09 12:46:44 mrg Exp $");
#endif #endif

View File

@ -1,7 +1,7 @@
/* $NetBSD: wav.c,v 1.14 2017/11/25 17:18:15 jdolecek Exp $ */ /* $NetBSD: wav.c,v 1.15 2019/11/09 12:46:44 mrg Exp $ */
/* /*
* Copyright (c) 2002, 2009 Matthew R. Green * Copyright (c) 2002, 2009, 2013, 2015, 2019 Matthew R. Green
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -33,7 +33,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifndef lint #ifndef lint
__RCSID("$NetBSD: wav.c,v 1.14 2017/11/25 17:18:15 jdolecek Exp $"); __RCSID("$NetBSD: wav.c,v 1.15 2019/11/09 12:46:44 mrg Exp $");
#endif #endif
@ -60,6 +60,7 @@ static const struct {
{ WAVE_FORMAT_UNKNOWN, "Microsoft Official Unknown" }, { WAVE_FORMAT_UNKNOWN, "Microsoft Official Unknown" },
{ WAVE_FORMAT_PCM, "Microsoft PCM" }, { WAVE_FORMAT_PCM, "Microsoft PCM" },
{ WAVE_FORMAT_ADPCM, "Microsoft ADPCM" }, { WAVE_FORMAT_ADPCM, "Microsoft ADPCM" },
{ WAVE_FORMAT_IEEE_FLOAT,"Microsoft IEEE Floating-Point" },
{ WAVE_FORMAT_ALAW, "Microsoft A-law" }, { WAVE_FORMAT_ALAW, "Microsoft A-law" },
{ WAVE_FORMAT_MULAW, "Microsoft mu-law" }, { WAVE_FORMAT_MULAW, "Microsoft mu-law" },
{ WAVE_FORMAT_OKI_ADPCM,"OKI ADPCM" }, { WAVE_FORMAT_OKI_ADPCM,"OKI ADPCM" },
@ -188,6 +189,20 @@ audio_wav_parse_hdr(void *hdr, size_t sz, u_int *enc, u_int *prec,
newenc = AUDIO_ENCODING_ULAW; newenc = AUDIO_ENCODING_ULAW;
newprec = 8; newprec = 8;
break; break;
case WAVE_FORMAT_IEEE_FLOAT:
switch (getle16(fmt.bits_per_sample)) {
case 32:
newenc = AUDIO_ENCODING_LIBAUDIO_FLOAT32;
newprec = 32;
break;
case 64:
newenc = AUDIO_ENCODING_LIBAUDIO_FLOAT64;
newprec = 32;
break;
default:
return (AUDIO_EWAVBADPCM);
}
break;
} }
do { do {

View File

@ -1,6 +1,6 @@
.\" $NetBSD: audioplay.1,v 1.26 2014/03/18 18:20:44 riastradh Exp $ .\" $NetBSD: audioplay.1,v 1.27 2019/11/09 12:46:44 mrg Exp $
.\" .\"
.\" Copyright (c) 1998, 1999, 2002, 2010 Matthew R. Green .\" Copyright (c) 1998, 1999, 2002, 2010, 2019 Matthew R. Green
.\" All rights reserved. .\" All rights reserved.
.\" .\"
.\" Redistribution and use in source and binary forms, with or without .\" Redistribution and use in source and binary forms, with or without
@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd December 30, 2010 .Dd November 8, 2019
.Dt AUDIOPLAY 1 .Dt AUDIOPLAY 1
.Os .Os
.Sh NAME .Sh NAME
@ -172,6 +172,15 @@ can be used to play Sun/NeXT audio files, and also RIFF WAVE audio files.
can be configured in the can be configured in the
.Dq Netscape .Dq Netscape
web browser as the program to use when playing audio files. web browser as the program to use when playing audio files.
.Pp
In addition to the audio driver encodings list in the EXAMPLES section,
.Nm
supports playing IEEE floating point data in RIFF WAVE audio files
(with one caveat that the flaoting point size must be native.)
In this case
.Nm
converts the floating point data into signed linear samples before
they are passed to the chosen audio device.
.Sh ERRORS .Sh ERRORS
If the audio device or the control device can not be opened, and error is If the audio device or the control device can not be opened, and error is
returned. returned.
@ -182,29 +191,55 @@ hardware driver.
.Sh SEE ALSO .Sh SEE ALSO
.Xr audioctl 1 , .Xr audioctl 1 ,
.Xr audiorecord 1 , .Xr audiorecord 1 ,
.Xr aica 4 ,
.Xr arcofi 4 ,
.Xr aria 4 , .Xr aria 4 ,
.Xr audio 4 , .Xr audio 4 ,
.Xr audioamd 4 , .Xr audioamd 4 ,
.Xr auich 4 , .Xr auich 4 ,
.Xr auixp 4 ,
.Xr ausoc 4 ,
.Xr autri 4 , .Xr autri 4 ,
.Xr auvia 4 , .Xr auvia 4 ,
.Xr awacs 4 ,
.Xr bba 4 ,
.Xr btsco 4 ,
.Xr clcs 4 , .Xr clcs 4 ,
.Xr clct 4 , .Xr clct 4 ,
.Xr cmpci 4 , .Xr cmpci 4 ,
.Xr dbri 4 ,
.Xr eap 4 , .Xr eap 4 ,
.Xr emuxki 4 , .Xr emuxki 4 ,
.Xr esm 4 , .Xr esm 4 ,
.Xr eso 4 , .Xr eso 4 ,
.Xr ess 4 , .Xr ess 4 ,
.Xr fms 4 , .Xr fms 4 ,
.Xr gcscaudio 4 ,
.Xr gus 4 , .Xr gus 4 ,
.Xr guspnp 4 , .Xr guspnp 4 ,
.Xr haltwo 4 ,
.Xr harmony 4 ,
.Xr hdafg 4 ,
.Xr mavb 4 ,
.Xr neo 4 , .Xr neo 4 ,
.Xr pad 4 ,
.Xr pas 4 ,
.Xr paud 4 ,
.Xr repluse 4 ,
.Xr sb 4 , .Xr sb 4 ,
.Xr snapper 4 ,
.Xr sv 4 , .Xr sv 4 ,
.Xr toccata 4 ,
.Xr uaudio 4 ,
.Xr vaudio 4 ,
.Xr vcaudio 4 ,
.Xr vraiu 4 ,
.Xr vs 4 ,
.Xr vsaudio 4 ,
.Xr wss 4 , .Xr wss 4 ,
.Xr yds 4 , .Xr yds 4 ,
.Xr ym 4 .Xr ym 4 ,
.Xr zaudio 4
.Sh HISTORY .Sh HISTORY
The The
.Nm .Nm
@ -214,6 +249,10 @@ The
.Nm .Nm
was first made available in was first made available in
.Nx 1.4 . .Nx 1.4 .
Support for RIFF WAVE recording was introduced in
.Nx 1.6 .
Support for RIFF WAVE IEEE floating point data was introduced in
.Nx 10.0 .
.Sh AUTHORS .Sh AUTHORS
The The
.Nm .Nm

View File

@ -1,4 +1,4 @@
/* $NetBSD: play.c,v 1.57 2019/05/04 08:27:30 isaki Exp $ */ /* $NetBSD: play.c,v 1.58 2019/11/09 12:46:44 mrg Exp $ */
/* /*
* Copyright (c) 1999, 2000, 2001, 2002, 2010 Matthew R. Green * Copyright (c) 1999, 2000, 2001, 2002, 2010 Matthew R. Green
@ -28,10 +28,9 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifndef lint #ifndef lint
__RCSID("$NetBSD: play.c,v 1.57 2019/05/04 08:27:30 isaki Exp $"); __RCSID("$NetBSD: play.c,v 1.58 2019/11/09 12:46:44 mrg Exp $");
#endif #endif
#include <sys/param.h> #include <sys/param.h>
#include <sys/audioio.h> #include <sys/audioio.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
@ -51,10 +50,14 @@ __RCSID("$NetBSD: play.c,v 1.57 2019/05/04 08:27:30 isaki Exp $");
#include "libaudio.h" #include "libaudio.h"
typedef size_t (*convert)(void *inbuf, void *outbuf, size_t len);
static void usage(void) __dead; static void usage(void) __dead;
static void play(char *); static void play(char *);
static void play_fd(const char *, int); static void play_fd(const char *, int);
static ssize_t audioctl_write_fromhdr(void *, size_t, int, off_t *, const char *); static ssize_t audioctl_write_fromhdr(void *, size_t, int,
off_t *, const char *,
convert *);
static void cleanup(int) __dead; static void cleanup(int) __dead;
static audio_info_t info; static audio_info_t info;
@ -214,9 +217,90 @@ cleanup(int signo)
exit(exitstatus); exit(exitstatus);
} }
#ifndef __vax__
static size_t
float32_to_linear32(void *inbuf, void *outbuf, size_t len)
{
uint8_t *inbuf8 = inbuf, *end = inbuf8 + len;
uint8_t *outbuf8 = outbuf;
float f;
int32_t i;
while (inbuf8 + sizeof f <= end) {
memcpy(&f, inbuf8, sizeof f);
/* saturate */
if (f < -1.0)
f = -1.0;
if (f > 1.0)
f = 1.0;
/* Convert -1.0 to +1.0 into a 32 bit signed value */
i = f * ((1u << 31) - 1);
memcpy(outbuf8, &i, sizeof i);
inbuf8 += sizeof f;
outbuf8 += sizeof i;
}
return len;
}
static size_t
float64_to_linear32(void *inbuf, void *outbuf, size_t len)
{
uint8_t *inbuf8 = inbuf, *end = inbuf8 + len;
uint8_t *outbuf8 = outbuf;
double f;
int32_t i;
while (inbuf8 + sizeof f <= end) {
memcpy(&f, inbuf8, sizeof f);
/* saturate */
if (f < -1.0)
f = -1.0;
if (f > 1.0)
f = 1.0;
/* Convert -1.0 to +1.0 into a 32 bit signed value */
i = f * ((1u << 31) - 1);
memcpy(outbuf8, &i, sizeof i);
inbuf8 += sizeof f;
outbuf8 += sizeof i;
}
return len / 2;
}
#endif /* __vax__ */
static size_t
audio_write(int fd, void *buf, size_t len, convert conv)
{
static void *convert_buffer;
static size_t convert_buffer_size;
if (conv == NULL)
return write(fd, buf, len);
if (convert_buffer == NULL || convert_buffer_size < len) {
free(convert_buffer);
convert_buffer = malloc(len);
if (convert_buffer == NULL)
err(1, "malloc of convert buffer failed");
convert_buffer_size = len;
}
len = conv(buf, convert_buffer, len);
return write(fd, convert_buffer, len);
}
static void static void
play(char *file) play(char *file)
{ {
convert conv = NULL;
struct stat sb; struct stat sb;
void *addr, *oaddr; void *addr, *oaddr;
off_t filesize; off_t filesize;
@ -265,10 +349,11 @@ play(char *file)
madvise(addr, sizet_filesize, MADV_SEQUENTIAL); madvise(addr, sizet_filesize, MADV_SEQUENTIAL);
/* /*
* get the header length and set up the audio device * get the header length and set up the audio device, and
* determine any conversion needed.
*/ */
if ((hdrlen = audioctl_write_fromhdr(addr, if ((hdrlen = audioctl_write_fromhdr(addr,
sizet_filesize, audiofd, &datasize, file)) < 0) { sizet_filesize, audiofd, &datasize, file, &conv)) < 0) {
if (play_errstring) if (play_errstring)
errx(1, "%s: %s", play_errstring, file); errx(1, "%s: %s", play_errstring, file);
else else
@ -284,7 +369,7 @@ play(char *file)
} }
while ((uint64_t)datasize > bufsize) { while ((uint64_t)datasize > bufsize) {
nw = write(audiofd, addr, bufsize); nw = audio_write(audiofd, addr, bufsize, conv);
if (nw == -1) if (nw == -1)
err(1, "write failed"); err(1, "write failed");
if ((size_t)nw != bufsize) if ((size_t)nw != bufsize)
@ -292,7 +377,7 @@ play(char *file)
addr = (char *)addr + bufsize; addr = (char *)addr + bufsize;
datasize -= bufsize; datasize -= bufsize;
} }
nw = write(audiofd, addr, datasize); nw = audio_write(audiofd, addr, datasize, conv);
if (nw == -1) if (nw == -1)
err(1, "final write failed"); err(1, "final write failed");
if ((off_t)nw != datasize) if ((off_t)nw != datasize)
@ -312,6 +397,7 @@ play(char *file)
static void static void
play_fd(const char *file, int fd) play_fd(const char *file, int fd)
{ {
convert conv = NULL;
char *buffer = malloc(bufsize); char *buffer = malloc(bufsize);
ssize_t hdrlen; ssize_t hdrlen;
int nr, nw; int nr, nw;
@ -331,7 +417,7 @@ play_fd(const char *file, int fd)
} }
err(1, "unexpected EOF"); err(1, "unexpected EOF");
} }
hdrlen = audioctl_write_fromhdr(buffer, nr, audiofd, &datasize, file); hdrlen = audioctl_write_fromhdr(buffer, nr, audiofd, &datasize, file, &conv);
if (hdrlen < 0) { if (hdrlen < 0) {
if (play_errstring) if (play_errstring)
errx(1, "%s: %s", play_errstring, file); errx(1, "%s: %s", play_errstring, file);
@ -347,7 +433,7 @@ play_fd(const char *file, int fd)
while (datasize == 0 || dataout < datasize) { while (datasize == 0 || dataout < datasize) {
if (datasize != 0 && dataout + nr > datasize) if (datasize != 0 && dataout + nr > datasize)
nr = datasize - dataout; nr = datasize - dataout;
nw = write(audiofd, buffer, nr); nw = audio_write(audiofd, buffer, nr, conv);
if (nw == -1) if (nw == -1)
err(1, "audio device write failed"); err(1, "audio device write failed");
if (nw != nr) if (nw != nr)
@ -374,11 +460,13 @@ read_error:
* uses the local "info" variable. blah... fix me! * uses the local "info" variable. blah... fix me!
*/ */
static ssize_t static ssize_t
audioctl_write_fromhdr(void *hdr, size_t fsz, int fd, off_t *datasize, const char *file) audioctl_write_fromhdr(void *hdr, size_t fsz, int fd, off_t *datasize, const char *file, convert *conv)
{ {
sun_audioheader *sunhdr; sun_audioheader *sunhdr;
ssize_t hdr_len = 0; ssize_t hdr_len = 0;
*conv = NULL;
AUDIO_INITINFO(&info); AUDIO_INITINFO(&info);
sunhdr = hdr; sunhdr = hdr;
if (ntohl(sunhdr->magic) == AUDIO_FILE_MAGIC) { if (ntohl(sunhdr->magic) == AUDIO_FILE_MAGIC) {
@ -457,6 +545,32 @@ set_audio_mode:
enc ? enc : ""); enc ? enc : "");
} }
#ifndef __vax__
if (info.play.encoding == AUDIO_ENCODING_LIBAUDIO_FLOAT32 ||
info.play.encoding == AUDIO_ENCODING_LIBAUDIO_FLOAT64) {
const char *msg;
if (info.play.encoding == AUDIO_ENCODING_LIBAUDIO_FLOAT32) {
if (sizeof(float) * CHAR_BIT != 32)
return -1;
*conv = float32_to_linear32;
msg = "32";
} else {
if (sizeof(double) * CHAR_BIT != 64)
return -1;
*conv = float64_to_linear32;
msg = "32";
}
info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
info.play.precision = 32;
if (verbose)
printf("%s: converting IEEE float%s to precision=%d "
"encoding=%s\n", file, msg,
info.play.precision,
audio_enc_from_val(info.play.encoding));
}
#endif /* __vax__ */
if (ioctl(fd, AUDIO_SETINFO, &info) < 0) if (ioctl(fd, AUDIO_SETINFO, &info) < 0)
err(1, "failed to set audio info"); err(1, "failed to set audio info");