Module Name:    src
Committed By:   martin
Date:           Tue Mar 12 12:41:39 UTC 2024

Modified Files:
        src/usr.bin/audio/common [netbsd-9]: auconv.h audio.c decode.c
            libaudio.h sun.c wav.c
        src/usr.bin/audio/ctl [netbsd-9]: audioctl.1
        src/usr.bin/audio/play [netbsd-9]: audioplay.1 play.c
        src/usr.bin/audio/record [netbsd-9]: audiorecord.1 record.c

Log Message:
Pull up the following revisions, requested by mrg in ticket #1818:

        usr.bin/audio/common/auconv.h                   up to 1.6
        usr.bin/audio/common/audio.c                    up to 1.27
        usr.bin/audio/common/decode.c                   up to 1.2
        usr.bin/audio/common/libaudio.h                 up to 1.22
        usr.bin/audio/common/sun.c                      up to 1.11
        usr.bin/audio/common/wav.c                      up to 1.22
        usr.bin/audio/ctl/audioctl.1                    up to 1.25
        usr.bin/audio/play/audioplay.1                  up to 1.34
        usr.bin/audio/play/play.c                       up to 1.64
        usr.bin/audio/record/audiorecord.1              up to 1.45
        usr.bin/audio/record/record.c                   up to 1.58

- support for playing 32-bit and 64-bit IEEE FP .wav files
- support for recording 24 bit .wav files
- read-overflow fixes for .wav files, and other fixes for the
  wav parser
- audioplay gains -n flag (no play, like make -n)
- audioctl manual spruced up
- audiorecord manual gained useful examples


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.5.64.1 src/usr.bin/audio/common/auconv.h
cvs rdiff -u -r1.25 -r1.25.18.1 src/usr.bin/audio/common/audio.c
cvs rdiff -u -r1.1 -r1.1.18.1 src/usr.bin/audio/common/decode.c
cvs rdiff -u -r1.20 -r1.20.18.1 src/usr.bin/audio/common/libaudio.h
cvs rdiff -u -r1.9 -r1.9.18.1 src/usr.bin/audio/common/sun.c
cvs rdiff -u -r1.14 -r1.14.6.1 src/usr.bin/audio/common/wav.c
cvs rdiff -u -r1.23 -r1.23.2.1 src/usr.bin/audio/ctl/audioctl.1
cvs rdiff -u -r1.26.28.1 -r1.26.28.2 src/usr.bin/audio/play/audioplay.1
cvs rdiff -u -r1.57 -r1.57.2.1 src/usr.bin/audio/play/play.c
cvs rdiff -u -r1.42 -r1.42.28.1 src/usr.bin/audio/record/audiorecord.1
cvs rdiff -u -r1.54 -r1.54.18.1 src/usr.bin/audio/record/record.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/audio/common/auconv.h
diff -u src/usr.bin/audio/common/auconv.h:1.5 src/usr.bin/audio/common/auconv.h:1.5.64.1
--- src/usr.bin/audio/common/auconv.h:1.5	Mon Apr 28 20:24:12 2008
+++ src/usr.bin/audio/common/auconv.h	Tue Mar 12 12:41:38 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: auconv.h,v 1.5 2008/04/28 20:24:12 martin Exp $	*/
+/*	$NetBSD: auconv.h,v 1.5.64.1 2024/03/12 12:41:38 martin Exp $	*/
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -78,6 +78,24 @@ change_sign16_be(u_char *p, int cc)
 }
 
 static inline void
+change_sign24_le(u_char *p, int cc)
+{
+	while ((cc -= 3) >= 0) {
+		p[2] ^= 0x80;
+		p += 3;
+	}
+}
+
+static inline void
+change_sign24_be(u_char *p, int cc)
+{
+	while ((cc -= 3) >= 0) {
+		p[0] ^= 0x80;
+		p += 3;
+	}
+}
+
+static inline void
 change_sign32_le(u_char *p, int cc)
 {
 	while ((cc -= 4) >= 0) {
@@ -163,6 +181,44 @@ change_sign16_swap_bytes_be(u_char *p, i
 }
 
 static inline void
+swap_bytes_change_sign24_le(u_char *p, int cc)
+{
+	u_char t;
+
+	while ((cc -= 3) >= 0) {
+		t = p[2];
+		p[2] = p[0] ^ 0x80;
+		p[0] = t;
+		p += 3;
+	}
+}
+
+static inline void
+swap_bytes_change_sign24_be(u_char *p, int cc)
+{
+	u_char t;
+
+	while ((cc -= 3) >= 0) {
+		t = p[0];
+		p[0] = p[2] ^ 0x80;
+		p[2] = t;
+		p += 3;
+	}
+}
+
+static inline void
+change_sign24_swap_bytes_le(u_char *p, int cc)
+{
+	swap_bytes_change_sign24_be(p, cc);
+}
+
+static inline void
+change_sign24_swap_bytes_be(u_char *p, int cc)
+{
+	swap_bytes_change_sign24_le(p, cc);
+}
+
+static inline void
 swap_bytes_change_sign32_le(u_char *p, int cc)
 {
 	u_char t;

Index: src/usr.bin/audio/common/audio.c
diff -u src/usr.bin/audio/common/audio.c:1.25 src/usr.bin/audio/common/audio.c:1.25.18.1
--- src/usr.bin/audio/common/audio.c:1.25	Wed Aug  5 06:54:39 2015
+++ src/usr.bin/audio/common/audio.c	Tue Mar 12 12:41:38 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: audio.c,v 1.25 2015/08/05 06:54:39 mrg Exp $	*/
+/*	$NetBSD: audio.c,v 1.25.18.1 2024/03/12 12:41:38 martin Exp $	*/
 
 /*
- * Copyright (c) 1999 Matthew R. Green
+ * Copyright (c) 1999, 2013, 2015, 2019 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,7 +32,7 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: audio.c,v 1.25 2015/08/05 06:54:39 mrg Exp $");
+__RCSID("$NetBSD: audio.c,v 1.25.18.1 2024/03/12 12:41:38 martin Exp $");
 #endif
 
 
@@ -110,6 +110,8 @@ static const struct {
 	{ AudioEmpeg_l2_packets,AUDIO_ENCODING_MPEG_L2_PACKETS },
 	{ AudioEmpeg_l2_system,	AUDIO_ENCODING_MPEG_L2_SYSTEM },
 	{ AudioEac3,		AUDIO_ENCODING_AC3 },
+	{ "ieee_float32",	AUDIO_ENCODING_LIBAUDIO_FLOAT32 },
+	{ "ieee_float64",	AUDIO_ENCODING_LIBAUDIO_FLOAT64 },
 	{ NULL, -1 }
 };
 
@@ -203,7 +205,7 @@ write_header(struct track_info *ti)
 
 	veclen = 0;
 	tlen = 0;
-		
+
 	if (hdrlen != 0) {
 		iv[veclen].iov_base = hdr;
 		iv[veclen].iov_len = hdrlen;

Index: src/usr.bin/audio/common/decode.c
diff -u src/usr.bin/audio/common/decode.c:1.1 src/usr.bin/audio/common/decode.c:1.1.18.1
--- src/usr.bin/audio/common/decode.c:1.1	Sun Jun 21 06:06:01 2015
+++ src/usr.bin/audio/common/decode.c	Tue Mar 12 12:41:38 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: decode.c,v 1.1 2015/06/21 06:06:01 mrg Exp $	*/
+/*	$NetBSD: decode.c,v 1.1.18.1 2024/03/12 12:41:38 martin Exp $	*/
 
 /*
  * Copyright (c) 1999 Matthew R. Green
@@ -29,7 +29,7 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: decode.c,v 1.1 2015/06/21 06:06:01 mrg Exp $");
+__RCSID("$NetBSD: decode.c,v 1.1.18.1 2024/03/12 12:41:38 martin Exp $");
 #endif
 
 #include <sys/types.h>
@@ -85,7 +85,7 @@ decode_time(const char *arg, struct time
 
 	tvp->tv_sec = tvp->tv_usec = 0;
 	s = copy;
-	
+
 	/* handle [hh:]mm:ss.dd */
 	if ((colon = strchr(s, ':')) != NULL) {
 		*colon++ = '\0';

Index: src/usr.bin/audio/common/libaudio.h
diff -u src/usr.bin/audio/common/libaudio.h:1.20 src/usr.bin/audio/common/libaudio.h:1.20.18.1
--- src/usr.bin/audio/common/libaudio.h:1.20	Wed Aug  5 06:54:39 2015
+++ src/usr.bin/audio/common/libaudio.h	Tue Mar 12 12:41:38 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: libaudio.h,v 1.20 2015/08/05 06:54:39 mrg Exp $	*/
+/*	$NetBSD: libaudio.h,v 1.20.18.1 2024/03/12 12:41:38 martin Exp $	*/
 
 /*
- * Copyright (c) 1999, 2009 Matthew R. Green
+ * Copyright (c) 1999, 2009, 2013, 2015, 2019, 2024 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,6 +37,17 @@
 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 know of it here.
  *
@@ -95,7 +106,10 @@ int	audio_sun_to_encoding (int, u_int *,
 int	audio_encoding_to_sun (int, int, int *);
 
 /*
- * M$ WAV files, info gleamed from sox sources
+ * RIFF WAVE files.  Sources: RFC 2361, and various Microsoft docs
+ * https://learn.microsoft.com/en-us/windows/win32/xaudio2/resource-interchange-file-format--riff-
+ * https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653308(v=vs.85)
+ * "Multimedia Programming Interface and Data Specifications 1.0" chapter 4
  */
 
 /*
@@ -107,29 +121,112 @@ int	audio_encoding_to_sun (int, int, int
 #define	WAVAUDIO_FILE_MAGIC_FMT		((u_int32_t)0x666d7420)
 #define	WAVAUDIO_FILE_MAGIC_DATA	((u_int32_t)0x64617461)
 
-/* purloined from public Microsoft RIFF docs via sox or mplayer */
+/* From RFC 2361 */
 #define WAVE_FORMAT_UNKNOWN		(0x0000)
 #define WAVE_FORMAT_PCM			(0x0001)
 #define WAVE_FORMAT_ADPCM		(0x0002)
+#define WAVE_FORMAT_IEEE_FLOAT		(0x0003)
+#define WAVE_FORMAT_VSELP		(0x0004)
+#define WAVE_FORMAT_IBM_CVSD		(0x0005)
 #define WAVE_FORMAT_ALAW		(0x0006)
 #define WAVE_FORMAT_MULAW		(0x0007)
 #define WAVE_FORMAT_OKI_ADPCM		(0x0010)
 #define WAVE_FORMAT_IMA_ADPCM		(0x0011)
+#define WAVE_FORMAT_MEDIASPACE_ADPCM	(0x0012)
+#define WAVE_FORMAT_SIERRA_ADPCM	(0x0013)
+#define WAVE_FORMAT_G723_ADPCM		(0x0014)
 #define WAVE_FORMAT_DIGISTD		(0x0015)
 #define WAVE_FORMAT_DIGIFIX		(0x0016)
+#define WAVE_FORMAT_DIALOGIC_OKI_ADPCM	(0x0017)
+#define WAVE_FORMAT_MEDIAVISION_ADPCM	(0x0018)
+#define WAVE_FORMAT_CU_CODEC		(0x0019)
+#define WAVE_FORMAT_YAMAHA_ADPCM	(0x0020)
+#define WAVE_FORMAT_SONARC		(0x0021)
+#define WAVE_FORMAT_DSPGROUP_TRUESPEECH	(0x0022)
+#define WAVE_FORMAT_ECHOSC1		(0x0023)
+#define WAVE_FORMAT_AUDIOFILE_AF36	(0x0024)
+#define WAVE_FORMAT_APTX		(0x0025)
+#define WAVE_FORMAT_AUDIOFILE_AF10	(0x0026)
+#define WAVE_FORMAT_PROSODY_1612	(0x0027)
+#define WAVE_FORMAT_LRC			(0x0028)
 #define WAVE_FORMAT_DOLBY_AC2		(0x0030)
 #define WAVE_FORMAT_GSM610		(0x0031)
+#define WAVE_FORMAT_MSNAUDIO		(0x0032)
+#define WAVE_FORMAT_ANTEX_ADPCME	(0x0033)
+#define WAVE_FORMAT_CONTROL_RES_VQLPC	(0x0034)
+#define WAVE_FORMAT_DIGIREAL		(0x0035)
+#define WAVE_FORMAT_DIGIADPCM		(0x0036)
+#define WAVE_FORMAT_CONTROL_RES_CR10	(0x0037)
+#define WAVE_FORMAT_NMS_VBXADPCM	(0x0038)
+#define WAVE_FORMAT_ROLAND_RDAC		(0x0039)
+#define WAVE_FORMAT_ECHOSC3		(0x003a)
 #define WAVE_FORMAT_ROCKWELL_ADPCM	(0x003b)
 #define WAVE_FORMAT_ROCKWELL_DIGITALK	(0x003c)
+#define WAVE_FORMAT_XEBEC		(0x003d)
 #define WAVE_FORMAT_G721_ADPCM		(0x0040)
 #define WAVE_FORMAT_G728_CELP		(0x0041)
+#define WAVE_FORMAT_MSG723		(0x0042)
 #define WAVE_FORMAT_MPEG		(0x0050)
+#define WAVE_FORMAT_RT24		(0x0052)
+#define WAVE_FORMAT_PAC			(0x0053)
 #define WAVE_FORMAT_MPEGLAYER3		(0x0055)
+#define WAVE_FORMAT_LUCENT_G723		(0x0059)
+#define WAVE_FORMAT_CIRRUS		(0x0060)
+#define WAVE_FORMAT_ESPCM		(0x0061)
+#define WAVE_FORMAT_VOXWARE		(0x0062)
+#define WAVE_FORMAT_CANOPUS_ATRAC	(0x0063)
 #define WAVE_FORMAT_G726_ADPCM		(0x0064)
 #define WAVE_FORMAT_G722_ADPCM		(0x0065)
-#define IBM_FORMAT_MULAW		(0x0101)
-#define IBM_FORMAT_ALAW			(0x0102)
-#define IBM_FORMAT_ADPCM		(0x0103)
+#define WAVE_FORMAT_DSAT		(0x0066)
+#define WAVE_FORMAT_DSAT_DISPLAY	(0x0067)
+#define WAVE_FORMAT_VOXWARE_BYTE_ALIGNED (0x0069)
+#define WAVE_FORMAT_VOXWARE_AC8		(0x0070)
+#define WAVE_FORMAT_VOXWARE_AC10	(0x0071)
+#define WAVE_FORMAT_VOXWARE_AC16	(0x0072)
+#define WAVE_FORMAT_VOXWARE_AC20	(0x0073)
+#define WAVE_FORMAT_VOXWARE_RT24	(0x0074)
+#define WAVE_FORMAT_VOXWARE_RT29	(0x0075)
+#define WAVE_FORMAT_VOXWARE_RT29HW	(0x0076)
+#define WAVE_FORMAT_VOXWARE_VR12	(0x0077)
+#define WAVE_FORMAT_VOXWARE_VR18	(0x0078)
+#define WAVE_FORMAT_VOXWARE_TQ40	(0x0079)
+#define WAVE_FORMAT_SOFTSOUND		(0x0080)
+#define WAVE_FORMAT_VOXWARE_TQ60	(0x0081)
+#define WAVE_FORMAT_MSRT24		(0x0082)
+#define WAVE_FORMAT_G729A		(0x0083)
+#define WAVE_FORMAT_MVI_MV12		(0x0084)
+#define WAVE_FORMAT_DF_G726		(0x0085)
+#define WAVE_FORMAT_DF_GSM610		(0x0086)
+#define WAVE_FORMAT_ISIAUDIO		(0x0088)
+#define WAVE_FORMAT_ONLIVE		(0x0089)
+#define WAVE_FORMAT_SBC24		(0x0091)
+#define WAVE_FORMAT_DOLBY_AC3_SPDIF	(0x0092)
+#define WAVE_FORMAT_ZYXEL_ADPCM		(0x0097)
+#define WAVE_FORMAT_PHILIPS_LPCBB	(0x0098)
+#define WAVE_FORMAT_PACKED		(0x0099)
+#define WAVE_FORMAT_RHETOREX_ADPCM	(0x0100)
+#define WAVE_FORMAT_IRAT		(0x0101)
+#define WAVE_FORMAT_VIVO_G723		(0x0111)
+#define WAVE_FORMAT_VIVO_SIREN		(0x0112)
+#define WAVE_FORMAT_DIGITAL_G723	(0x0123)
+#define WAVE_FORMAT_CREATIVE_ADPCM	(0x0200)
+#define WAVE_FORMAT_CREATIVE_FASTSPEECH8 (0x0202)
+#define WAVE_FORMAT_CREATIVE_FASTSPEECH10 (0x0203)
+#define WAVE_FORMAT_QUARTERDECK		(0x0220)
+#define WAVE_FORMAT_FM_TOWNS_SND	(0x0300)
+#define WAVE_FORMAT_BTV_DIGITAL		(0x0400)
+#define WAVE_FORMAT_VME_VMPCM		(0x0680)
+#define WAVE_FORMAT_OLIGSM		(0x1000)
+#define WAVE_FORMAT_OLIADPCM		(0x1001)
+#define WAVE_FORMAT_OLICELP		(0x1002)
+#define WAVE_FORMAT_OLISBC		(0x1003)
+#define WAVE_FORMAT_OLIOPR		(0x1004)
+#define WAVE_FORMAT_LH_CODEC		(0x1100)
+#define WAVE_FORMAT_NORRIS		(0x1400)
+#define WAVE_FORMAT_ISIAUDIO2		(0x1401)
+#define WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS (0x1500)
+#define WAVE_FORMAT_DVM			(0x2000)
+
 #define WAVE_FORMAT_EXTENSIBLE		(0xfffe)
 
 const char *wav_enc_from_val (int);
@@ -143,7 +240,7 @@ typedef struct {
 	u_int16_t	tag;
 	u_int16_t	channels;
 	u_int32_t	sample_rate;
-	u_int32_t	avg_bps;
+	u_int32_t	avg_bytes_per_sec;
 	u_int16_t	alignment;
 	u_int16_t	bits_per_sample;
 } __packed wav_audioheaderfmt;
@@ -153,7 +250,7 @@ typedef struct {
 	u_int16_t	valid_bits;
 	u_int32_t	speaker_pos_mask;
 	u_int16_t	sub_tag;
-	u_int8_t	dummy[14];
+	u_int8_t	guid[14];
 } __packed wav_audiohdrextensible;
 
 /* returns size of header, or -ve for failure */

Index: src/usr.bin/audio/common/sun.c
diff -u src/usr.bin/audio/common/sun.c:1.9 src/usr.bin/audio/common/sun.c:1.9.18.1
--- src/usr.bin/audio/common/sun.c:1.9	Wed Aug  5 06:54:39 2015
+++ src/usr.bin/audio/common/sun.c	Tue Mar 12 12:41:38 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: sun.c,v 1.9 2015/08/05 06:54:39 mrg Exp $	*/
+/*	$NetBSD: sun.c,v 1.9.18.1 2024/03/12 12:41:38 martin Exp $	*/
 
 /*
- * Copyright (c) 2002 Matthew R. Green
+ * Copyright (c) 2002, 2013, 2015 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,7 +32,7 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: sun.c,v 1.9 2015/08/05 06:54:39 mrg Exp $");
+__RCSID("$NetBSD: sun.c,v 1.9.18.1 2024/03/12 12:41:38 martin Exp $");
 #endif
 
 
@@ -149,7 +149,7 @@ sun_prepare_header(struct track_info *ti
 		break;
 #endif
 	}
-	
+
 	/* if we can't express this as a Sun header, don't write any */
 	if (audio_encoding_to_sun(ti->encoding, ti->precision, &sunenc) != 0) {
 		if (!ti->qflag && !warned) {

Index: src/usr.bin/audio/common/wav.c
diff -u src/usr.bin/audio/common/wav.c:1.14 src/usr.bin/audio/common/wav.c:1.14.6.1
--- src/usr.bin/audio/common/wav.c:1.14	Sat Nov 25 17:18:15 2017
+++ src/usr.bin/audio/common/wav.c	Tue Mar 12 12:41:38 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: wav.c,v 1.14 2017/11/25 17:18:15 jdolecek Exp $	*/
+/*	$NetBSD: wav.c,v 1.14.6.1 2024/03/12 12:41:38 martin Exp $	*/
 
 /*
- * Copyright (c) 2002, 2009 Matthew R. Green
+ * Copyright (c) 2002, 2009, 2013, 2015, 2019, 2024 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -33,7 +33,7 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: wav.c,v 1.14 2017/11/25 17:18:15 jdolecek Exp $");
+__RCSID("$NetBSD: wav.c,v 1.14.6.1 2024/03/12 12:41:38 martin Exp $");
 #endif
 
 
@@ -49,6 +49,7 @@ __RCSID("$NetBSD: wav.c,v 1.14 2017/11/2
 #include <string.h>
 #include <stdint.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 #include "libaudio.h"
 #include "auconv.h"
@@ -60,6 +61,7 @@ static const struct {
 	{ WAVE_FORMAT_UNKNOWN, 	"Microsoft Official Unknown" },
 	{ WAVE_FORMAT_PCM,	"Microsoft PCM" },
 	{ WAVE_FORMAT_ADPCM,	"Microsoft ADPCM" },
+	{ WAVE_FORMAT_IEEE_FLOAT,"Microsoft IEEE Floating-Point" },
 	{ WAVE_FORMAT_ALAW,	"Microsoft A-law" },
 	{ WAVE_FORMAT_MULAW,	"Microsoft mu-law" },
 	{ WAVE_FORMAT_OKI_ADPCM,"OKI ADPCM" },
@@ -88,6 +90,49 @@ wav_enc_from_val(int encoding)
 /*
  * WAV format helpers
  */
+
+static bool
+find_riff_chunk(const char search[4], size_t *remainp, char **wherep, uint32_t *partlen)
+{
+	wav_audioheaderpart part;
+
+	*partlen = 0;
+
+#define ADJUST(l) do {				\
+	if (l >= *(remainp))			\
+		return false;			\
+	*(wherep) += (l);			\
+	*(remainp) -= (l);			\
+} while (0)
+
+	while (*remainp >= sizeof part) {
+		const char *emsg = "";
+		uint32_t len;
+
+		memcpy(&part, *wherep, sizeof part);
+		ADJUST(sizeof part);
+		len = getle32(part.len);
+		if (len % 2) {
+			emsg = " (odd length, adjusted)";
+			len += 1;
+		}
+		if (strncmp(part.name, search, sizeof *search) == 0) {
+			*partlen = len;
+			if (verbose > 1)
+				fprintf(stderr, "Found part %.04s length %d%s\n",
+				    part.name, len, emsg);
+			return true;
+		}
+		ADJUST(len);
+		if (verbose > 1)
+			fprintf(stderr, "Skipping part %.04s length %d%s\n",
+			    part.name, len, emsg);
+	}
+#undef ADJUST
+
+	return false;
+}
+
 /*
  * find a .wav header, etc. returns header length on success
  */
@@ -95,61 +140,87 @@ ssize_t
 audio_wav_parse_hdr(void *hdr, size_t sz, u_int *enc, u_int *prec,
     u_int *sample, u_int *channels, off_t *datasize)
 {
-	char	*where = hdr, *owhere;
-	wav_audioheaderpart part;
+	char	*where = hdr;
 	wav_audioheaderfmt fmt;
 	wav_audiohdrextensible ext;
-	char	*end = (((char *)hdr) + sz);
+	size_t remain = sz;
 	u_int	newenc, newprec;
+	uint32_t len = 0;
 	u_int16_t fmttag;
 	static const char
 	    strfmt[4] = "fmt ",
 	    strRIFF[4] = "RIFF",
 	    strWAVE[4] = "WAVE",
 	    strdata[4] = "data";
-		
+	bool found;
+
 	if (sz < 32)
 		return (AUDIO_ENOENT);
 
-	if (strncmp(where, strRIFF, sizeof strRIFF))
+#define ADJUST(l) do {				\
+	if (l >= remain)			\
+		return (AUDIO_ESHORTHDR);	\
+	where += (l);				\
+	remain -= (l);				\
+} while (0)
+
+	if (memcmp(where, strRIFF, sizeof strRIFF) != 0)
 		return (AUDIO_ENOENT);
-	where += 8;
-	if (strncmp(where, strWAVE, sizeof strWAVE))
+	ADJUST(sizeof strRIFF);
+	/* XXX we ignore the RIFF length here */
+	ADJUST(4);
+	if (memcmp(where, strWAVE, sizeof strWAVE) != 0)
 		return (AUDIO_ENOENT);
-	where += 4;
+	ADJUST(sizeof strWAVE);
 
-	do {
-		memcpy(&part, where, sizeof part);
-		owhere = where;
-		where += getle32(part.len) + 8;
-	} while (where < end && strncmp(part.name, strfmt, sizeof strfmt));
+	found = find_riff_chunk(strfmt, &remain, &where, &len);
 
 	/* too short ? */
-	if (where + sizeof fmt > end)
+	if (!found || remain <= sizeof fmt)
 		return (AUDIO_ESHORTHDR);
 
-	memcpy(&fmt, (owhere + 8), sizeof fmt);
-
+	memcpy(&fmt, where, sizeof fmt);
 	fmttag = getle16(fmt.tag);
 	if (verbose)
-		printf("WAVE format tag: %x\n", fmttag);
+		printf("WAVE format tag/len: %04x/%u\n", fmttag, len);
 
 	if (fmttag == WAVE_FORMAT_EXTENSIBLE) {
-		if ((uintptr_t)(where - owhere) < sizeof(fmt) + sizeof(ext))
+		if (len < sizeof(fmt) + sizeof(ext)) {
+			if (verbose)
+				fprintf(stderr, "short WAVE ext fmt\n");
 			return (AUDIO_ESHORTHDR);
-		memcpy(&ext, owhere + sizeof fmt, sizeof ext);
-		if (getle16(ext.len) < sizeof(ext) - sizeof(ext.len))
+		}
+		if (remain <= sizeof ext + sizeof fmt) {
+			if (verbose)
+				fprintf(stderr, "WAVE ext truncated\n");
 			return (AUDIO_ESHORTHDR);
+		}
+		memcpy(&ext, where + sizeof fmt, sizeof ext);
 		fmttag = getle16(ext.sub_tag);
+		uint16_t sublen = getle16(ext.len);
 		if (verbose)
-			printf("WAVE extensible sub tag: %x\n", fmttag);
+			printf("WAVE extensible tag/len: %04x/%u\n", fmttag, sublen);
+
+		/*
+		 * XXXMRG: it may be that part.len (aka sizeof fmt + sizeof ext)
+		 * should equal sizeof fmt + sizeof ext.len + sublen?  this block
+		 * is only entered for part.len == 40, where ext.len is expected
+		 * to be 22 (sizeof ext.len = 2, sizeof fmt = 16).
+		 *
+		 * warn about this, but don't consider it an error.
+		 */
+		if (getle16(ext.len) != 22 && verbose) {
+			fprintf(stderr, "warning: WAVE ext.len %u not 22\n",
+			    getle16(ext.len));
+		}
+	} else if (len < sizeof(fmt)) {
+		if (verbose)
+			fprintf(stderr, "WAVE fmt unsupported size %u\n", len);
+		return (AUDIO_EWAVUNSUPP);
 	}
+	ADJUST(len);
 
 	switch (fmttag) {
-	case WAVE_FORMAT_UNKNOWN:
-	case IBM_FORMAT_MULAW:
-	case IBM_FORMAT_ALAW:
-	case IBM_FORMAT_ADPCM:
 	default:
 		return (AUDIO_EWAVUNSUPP);
 
@@ -188,15 +259,27 @@ audio_wav_parse_hdr(void *hdr, size_t sz
 		newenc = AUDIO_ENCODING_ULAW;
 		newprec = 8;
 		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 {
-		memcpy(&part, where, sizeof part);
-		owhere = where;
-		where += (getle32(part.len) + 8);
-	} while (where < end && strncmp(part.name, strdata, sizeof strdata));
+	found = find_riff_chunk(strdata, &remain, &where, &len);
+	if (!found)
+		return (AUDIO_EWAVNODATA);
 
-	if ((where - getle32(part.len)) <= end) {
+	if (len) {
 		if (channels)
 			*channels = (u_int)getle16(fmt.channels);
 		if (sample)
@@ -206,10 +289,12 @@ audio_wav_parse_hdr(void *hdr, size_t sz
 		if (prec)
 			*prec = newprec;
 		if (datasize)
-			*datasize = (off_t)getle32(part.len);
-		return (owhere - (char *)hdr + 8);
+			*datasize = (off_t)len;
+		return (where - (char *)hdr);
 	}
 	return (AUDIO_EWAVNODATA);
+
+#undef ADJUST
 }
 
 
@@ -225,7 +310,7 @@ wav_prepare_header(struct track_info *ti
 	 *
 	 *      bytes   purpose
 	 *      0-3     "RIFF"
-	 *      4-7     file length (minus 8)
+	 *      4-7     RIFF chunk length (file length minus 8)
 	 *      8-15    "WAVEfmt "
 	 *      16-19   format size
 	 *      20-21   format tag
@@ -272,6 +357,8 @@ wav_prepare_header(struct track_info *ti
 		break;
 	case 16:
 		break;
+	case 24:
+		break;
 	case 32:
 		break;
 	default:
@@ -357,7 +444,7 @@ wav_prepare_header(struct track_info *ti
 	abps = (double)align*ti->sample_rate / (double)1 + 0.5;
 
 	nsample = (datalen / ti->precision) / ti->sample_rate;
-	
+
 	/*
 	 * now we've calculated the info, write it out!
 	 */

Index: src/usr.bin/audio/ctl/audioctl.1
diff -u src/usr.bin/audio/ctl/audioctl.1:1.23 src/usr.bin/audio/ctl/audioctl.1:1.23.2.1
--- src/usr.bin/audio/ctl/audioctl.1:1.23	Thu May  9 09:37:11 2019
+++ src/usr.bin/audio/ctl/audioctl.1	Tue Mar 12 12:41:38 2024
@@ -1,4 +1,4 @@
-.\" $NetBSD: audioctl.1,v 1.23 2019/05/09 09:37:11 wiz Exp $
+.\" $NetBSD: audioctl.1,v 1.23.2.1 2024/03/12 12:41:38 martin Exp $
 .\"
 .\" Copyright (c) 1997, 1999 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -26,12 +26,12 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd May 8, 2019
+.Dd March 21, 2021
 .Dt AUDIOCTL 1
 .Os
 .Sh NAME
 .Nm audioctl
-.Nd control audio device
+.Nd control software audio format
 .Sh SYNOPSIS
 .Nm
 .Op Fl n
@@ -49,7 +49,26 @@
 .Sh DESCRIPTION
 The
 .Nm
-command displays or sets various audio system driver variables.
+command displays or sets the parameters that determine the playback and
+recording format for software using an audio device.
+It is most useful when the full
+.Xr audio 4
+API is not available, e.g. when playing or recording raw audio data from a
+.Xr sh 1
+script, or from the command line.
+It does not control the underlying hardware format, which can be
+changed with
+.Xr audiocfg 1 .
+.Pp
+The variables that can be inspected and changed with
+.Nm
+are normally per-application and are reset when a
+.Pa /dev/audioX
+device is opened.
+This can be circumvented by using
+.Pa /dev/soundX
+instead, which retains global state.
+.Pp
 If a list of variables is present on the command line, then
 .Nm
 prints the current value of those variables for the specified device.
@@ -77,32 +96,23 @@ the audio control device to use.
 .El
 .Sh FILES
 .Bl -tag -width /dev/audioctl0 -compact
-.It Pa /dev/audio0
-audio I/O device (resets on open)
+.It Pa /dev/sound0
+audio I/O device
 .It Pa /dev/audioctl0
 audio control device
-.It Pa /dev/sound0
-audio I/O device (does not reset on open)
 .El
 .Sh EXAMPLES
 To set the playing sampling rate to 11025, you can use
 .Dl audioctl -w play.sample_rate=11025
 To set all of the play parameters for CD-quality audio, you can use
 .Dl audioctl -w play=44100,2,16,slinear_le
-Note that many of the variables that can be inspected and changed with
-.Nm
-are reset when
-.Pa /dev/audio0
-is opened.
-This can be circumvented by using
-.Pa /dev/sound0
-instead.
 .Sh COMPATIBILITY
 The old
 .Fl f
 flag is still supported.
 This support will be removed eventually.
 .Sh SEE ALSO
+.Xr audiocfg 1 ,
 .Xr audioplay 1 ,
 .Xr audiorecord 1 ,
 .Xr mixerctl 1 ,
@@ -113,3 +123,21 @@ The
 .Nm
 command first appeared in
 .Nx 1.3 .
+.Sh CAVEATS
+Since the parameters controlled by
+.Nm
+are global, they can be changed unexpectedly if another application
+uses the same audio device.
+.Pp
+It is always preferable to use
+.Dv AUDIO_SETINFO
+on a per-process
+.Pa /dev/audioX
+device, if the
+.Xr audio 4
+ioctls are available in the programming environment.
+Similarly,
+.Xr audioplay 1
+and
+.Xr audiorecord 1
+are more safe for use in scripting.

Index: src/usr.bin/audio/play/audioplay.1
diff -u src/usr.bin/audio/play/audioplay.1:1.26.28.1 src/usr.bin/audio/play/audioplay.1:1.26.28.2
--- src/usr.bin/audio/play/audioplay.1:1.26.28.1	Tue Feb 14 16:03:17 2023
+++ src/usr.bin/audio/play/audioplay.1	Tue Mar 12 12:41:39 2024
@@ -1,6 +1,6 @@
-.\"	$NetBSD: audioplay.1,v 1.26.28.1 2023/02/14 16:03:17 martin Exp $
+.\"	$NetBSD: audioplay.1,v 1.26.28.2 2024/03/12 12:41:39 martin Exp $
 .\"
-.\" Copyright (c) 1998, 1999, 2002, 2010 Matthew R. Green
+.\" Copyright (c) 1998, 1999, 2002, 2010, 2019, 2024 Matthew R. Green
 .\" All rights reserved.
 .\"
 .\" 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
 .\" SUCH DAMAGE.
 .\"
-.Dd December 30, 2010
+.Dd March 3, 2024
 .Dt AUDIOPLAY 1
 .Os
 .Sh NAME
@@ -32,7 +32,7 @@
 .Nd play audio files
 .Sh SYNOPSIS
 .Nm
-.Op Fl hiqV
+.Op Fl hinqV
 .Op Fl B Ar buffersize
 .Op Fl b Ar balance
 .Op Fl d Ar device
@@ -118,6 +118,8 @@ sample rate.
 Print a help message.
 .It Fl i
 If the audio device cannot be opened, exit now rather than wait for it.
+.It Fl n
+Do not write audio data, only parse files for sanity.
 .It Fl P
 When combined with the
 .Fl f
@@ -169,9 +171,17 @@ program can be used to show the availabl
 .Nm
 can be used to play Sun/NeXT audio files, and also RIFF WAVE audio files.
 .Nm
-can be configured in the
-.Dq Netscape
-web browser as the program to use when playing audio files.
+can be configured in a 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 floating 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
 If the audio device or the control device can not be opened, an error is
 returned.
@@ -182,29 +192,7 @@ hardware driver.
 .Sh SEE ALSO
 .Xr audioctl 1 ,
 .Xr audiorecord 1 ,
-.Xr aria 4 ,
-.Xr audio 4 ,
-.Xr audioamd 4 ,
-.Xr auich 4 ,
-.Xr autri 4 ,
-.Xr auvia 4 ,
-.Xr clcs 4 ,
-.Xr clct 4 ,
-.Xr cmpci 4 ,
-.Xr eap 4 ,
-.Xr emuxki 4 ,
-.Xr esm 4 ,
-.Xr eso 4 ,
-.Xr ess 4 ,
-.Xr fms 4 ,
-.Xr gus 4 ,
-.Xr guspnp 4 ,
-.Xr neo 4 ,
-.Xr sb 4 ,
-.Xr sv 4 ,
-.Xr wss 4 ,
-.Xr yds 4 ,
-.Xr ym 4
+.Xr audio 4
 .Sh HISTORY
 The
 .Nm
@@ -214,8 +202,12 @@ The
 .Nm
 was first made available in
 .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
 The
 .Nm
 program was written by
-.An Matthew R. Green Aq Mt m...@eterna.com.au .
+.An Matthew R. Green Aq Mt m...@eterna23.net .

Index: src/usr.bin/audio/play/play.c
diff -u src/usr.bin/audio/play/play.c:1.57 src/usr.bin/audio/play/play.c:1.57.2.1
--- src/usr.bin/audio/play/play.c:1.57	Sat May  4 08:27:30 2019
+++ src/usr.bin/audio/play/play.c	Tue Mar 12 12:41:39 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: play.c,v 1.57 2019/05/04 08:27:30 isaki Exp $	*/
+/*	$NetBSD: play.c,v 1.57.2.1 2024/03/12 12:41:39 martin Exp $	*/
 
 /*
- * Copyright (c) 1999, 2000, 2001, 2002, 2010 Matthew R. Green
+ * Copyright (c) 1999, 2000, 2001, 2002, 2010, 2015, 2019, 2021 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,10 +28,9 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: play.c,v 1.57 2019/05/04 08:27:30 isaki Exp $");
+__RCSID("$NetBSD: play.c,v 1.57.2.1 2024/03/12 12:41:39 martin Exp $");
 #endif
 
-
 #include <sys/param.h>
 #include <sys/audioio.h>
 #include <sys/ioctl.h>
@@ -51,10 +50,14 @@ __RCSID("$NetBSD: play.c,v 1.57 2019/05/
 
 #include "libaudio.h"
 
+typedef size_t (*convert)(void *inbuf, void *outbuf, size_t len);
+
 static void usage(void) __dead;
 static void play(char *);
 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 audio_info_t	info;
@@ -62,6 +65,7 @@ static int	volume;
 static int	balance;
 static int	port;
 static int	fflag;
+static int	nflag;
 static int	qflag;
 int	verbose;
 static int	sample_rate;
@@ -72,7 +76,7 @@ static int	channels;
 
 static char	const *play_errstring = NULL;
 static size_t	bufsize;
-static int	audiofd;
+static int	audiofd = -1;
 static int	exitstatus = EXIT_SUCCESS;
 
 int
@@ -84,7 +88,7 @@ main(int argc, char *argv[])
 	const char *defdevice = _PATH_SOUND;
 	const char *device = NULL;
 
-	while ((ch = getopt(argc, argv, "b:B:C:c:d:e:fhip:P:qs:Vv:")) != -1) {
+	while ((ch = getopt(argc, argv, "b:B:C:c:d:e:fhinp:P:qs:Vv:")) != -1) {
 		switch (ch) {
 		case 'b':
 			decode_int(optarg, &balance);
@@ -115,6 +119,9 @@ main(int argc, char *argv[])
 		case 'i':
 			iflag++;
 			break;
+		case 'n':
+			nflag++;
+			break;
 		case 'q':
 			qflag++;
 			break;
@@ -170,22 +177,22 @@ main(int argc, char *argv[])
 	    (device = getenv("AUDIODEV")) == NULL) /* Sun compatibility */
 		device = defdevice;
 
-	audiofd = open(device, O_WRONLY);
-	if (audiofd < 0 && device == defdevice) {
-		device = _PATH_SOUND0;
+	if (!nflag) {
 		audiofd = open(device, O_WRONLY);
-	}
-
-	if (audiofd < 0)
-		err(1, "failed to open %s", device);
+		if (audiofd < 0 && device == defdevice) {
+			device = _PATH_SOUND0;
+			audiofd = open(device, O_WRONLY);
+		}
+		if (audiofd < 0)
+			err(1, "failed to open %s", device);
 
-	if (ioctl(audiofd, AUDIO_GETINFO, &info) < 0)
-		err(1, "failed to get audio info");
-	if (bufsize == 0) {
-		bufsize = info.play.buffer_size;
-		if (bufsize < 32 * 1024)
-			bufsize = 32 * 1024;
+		if (ioctl(audiofd, AUDIO_GETINFO, &info) < 0)
+			err(1, "failed to get audio info");
+		if (bufsize == 0)
+			bufsize = info.play.buffer_size;
 	}
+	if (bufsize == 0)
+		bufsize = 32 * 1024;
 
 	signal(SIGINT, cleanup);
 	signal(SIGTERM, cleanup);
@@ -205,18 +212,105 @@ static void
 cleanup(int signo)
 {
 
-	(void)ioctl(audiofd, AUDIO_FLUSH, NULL);
-	(void)ioctl(audiofd, AUDIO_SETINFO, &info);
-	close(audiofd);
+	if (audiofd != -1) {
+		(void)ioctl(audiofd, AUDIO_FLUSH, NULL);
+		(void)ioctl(audiofd, AUDIO_SETINFO, &info);
+		close(audiofd);
+		audiofd = -1;
+	}
 	if (signo != 0) {
 		(void)raise_default_signal(signo);
 	}
 	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 * (float)INT32_MAX;
+
+		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 (nflag)
+		return len;
+
+	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
 play(char *file)
 {
+	convert conv = NULL;
 	struct stat sb;
 	void *addr, *oaddr;
 	off_t	filesize;
@@ -233,8 +327,7 @@ play(char *file)
 
 	fd = open(file, O_RDONLY);
 	if (fd < 0) {
-		if (!qflag)
-			warn("could not open %s", file);
+		warn("could not open %s", file);
 		exitstatus = EXIT_FAILURE;
 		return;
 	}
@@ -265,15 +358,18 @@ play(char *file)
 	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,
-	    sizet_filesize, audiofd, &datasize, file)) < 0) {
+	    sizet_filesize, audiofd, &datasize, file, &conv)) < 0) {
 		if (play_errstring)
 			errx(1, "%s: %s", play_errstring, file);
 		else
 			errx(1, "unknown audio file: %s", file);
 	}
+	if (verbose)
+		printf("header length: %zd\n", hdrlen);
 
 	filesize -= hdrlen;
 	addr = (char *)addr + hdrlen;
@@ -284,7 +380,7 @@ play(char *file)
 	}
 
 	while ((uint64_t)datasize > bufsize) {
-		nw = write(audiofd, addr, bufsize);
+		nw = audio_write(audiofd, addr, bufsize, conv);
 		if (nw == -1)
 			err(1, "write failed");
 		if ((size_t)nw != bufsize)
@@ -292,13 +388,13 @@ play(char *file)
 		addr = (char *)addr + bufsize;
 		datasize -= bufsize;
 	}
-	nw = write(audiofd, addr, datasize);
+	nw = audio_write(audiofd, addr, datasize, conv);
 	if (nw == -1)
 		err(1, "final write failed");
 	if ((off_t)nw != datasize)
 		errx(1, "final write failed");
 
-	if (ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag)
+	if (!nflag && ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag)
 		warn("audio drain ioctl failed");
 	if (munmap(oaddr, sizet_filesize) < 0)
 		err(1, "munmap failed");
@@ -312,6 +408,7 @@ play(char *file)
 static void
 play_fd(const char *file, int fd)
 {
+	convert conv = NULL;
 	char    *buffer = malloc(bufsize);
 	ssize_t hdrlen;
 	int     nr, nw;
@@ -331,7 +428,7 @@ play_fd(const char *file, int fd)
 		}
 		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 (play_errstring)
 			errx(1, "%s: %s", play_errstring, file);
@@ -347,7 +444,7 @@ play_fd(const char *file, int fd)
 	while (datasize == 0 || dataout < datasize) {
 		if (datasize != 0 && dataout + nr > datasize)
 			nr = datasize - dataout;
-		nw = write(audiofd, buffer, nr);
+		nw = audio_write(audiofd, buffer, nr, conv);
 		if (nw == -1)
 			err(1, "audio device write failed");
 		if (nw != nr)
@@ -360,7 +457,7 @@ play_fd(const char *file, int fd)
 			break;
 	}
 	/* something to think about: no message given for dataout < datasize */
-	if (ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag)
+	if (!nflag && ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag)
 		warn("audio drain ioctl failed");
 	return;
 read_error:
@@ -374,11 +471,13 @@ read_error:
  * uses the local "info" variable. blah... fix me!
  */
 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;
 	ssize_t	hdr_len = 0;
 
+	*conv = NULL;
+
 	AUDIO_INITINFO(&info);
 	sunhdr = hdr;
 	if (ntohl(sunhdr->magic) == AUDIO_FILE_MAGIC) {
@@ -457,7 +556,33 @@ set_audio_mode:
 		   enc ? enc : "");
 	}
 
-	if (ioctl(fd, AUDIO_SETINFO, &info) < 0)
+#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 = "64";
+		}
+		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 (!nflag && ioctl(fd, AUDIO_SETINFO, &info) < 0)
 		err(1, "failed to set audio info");
 
 	return (hdr_len);
@@ -467,7 +592,7 @@ static void
 usage(void)
 {
 
-	fprintf(stderr, "Usage: %s [-hiqV] [options] files\n", getprogname());
+	fprintf(stderr, "Usage: %s [-hinqV] [options] files\n", getprogname());
 	fprintf(stderr, "Options:\n\t"
 	    "-B buffer size\n\t"
 	    "-b balance (0-63)\n\t"

Index: src/usr.bin/audio/record/audiorecord.1
diff -u src/usr.bin/audio/record/audiorecord.1:1.42 src/usr.bin/audio/record/audiorecord.1:1.42.28.1
--- src/usr.bin/audio/record/audiorecord.1:1.42	Tue Mar 18 18:20:44 2014
+++ src/usr.bin/audio/record/audiorecord.1	Tue Mar 12 12:41:39 2024
@@ -1,4 +1,4 @@
-.\"	$NetBSD: audiorecord.1,v 1.42 2014/03/18 18:20:44 riastradh Exp $
+.\"	$NetBSD: audiorecord.1,v 1.42.28.1 2024/03/12 12:41:39 martin Exp $
 .\"
 .\" Copyright (c) 1998, 1999, 2001, 2002, 2010 Matthew R. Green
 .\" All rights reserved.
@@ -24,7 +24,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd December 30, 2010
+.Dd March 25, 2021
 .Dt AUDIORECORD 1
 .Os
 .Sh NAME
@@ -181,12 +181,14 @@ the audio control device to be used.
 .It AUDIODEVICE
 the audio device to be used.
 .El
+.Sh EXAMPLES
+Record CD quality audio to a WAVE file:
+.Dl audiorecord -c 2 -e slinear_le -P 16 -s 44100 recording.wav
 .Sh SEE ALSO
 .Xr audioctl 1 ,
 .Xr audioplay 1 ,
 .Xr aria 4 ,
 .Xr audio 4 ,
-.Xr audioamd 4 ,
 .Xr auich 4 ,
 .Xr autri 4 ,
 .Xr auvia 4 ,
@@ -203,6 +205,7 @@ the audio device to be used.
 .Xr guspnp 4 ,
 .Xr neo 4 ,
 .Xr sb 4 ,
+.Xr sparc/audioamd 4 ,
 .Xr sv 4 ,
 .Xr wss 4 ,
 .Xr yds 4 ,
@@ -220,7 +223,7 @@ big/little-endian samples was first made
 The
 .Nm
 program was written by
-.An Matthew R. Green Aq Mt m...@eterna.com.au .
+.An Matthew R. Green Aq Mt m...@eterna23.net .
 .Sh BUGS
 WAV big-endian samples are converted to little-endian, rather than
 a RIFX header being written.

Index: src/usr.bin/audio/record/record.c
diff -u src/usr.bin/audio/record/record.c:1.54 src/usr.bin/audio/record/record.c:1.54.18.1
--- src/usr.bin/audio/record/record.c:1.54	Wed Aug  5 06:54:39 2015
+++ src/usr.bin/audio/record/record.c	Tue Mar 12 12:41:39 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: record.c,v 1.54 2015/08/05 06:54:39 mrg Exp $	*/
+/*	$NetBSD: record.c,v 1.54.18.1 2024/03/12 12:41:39 martin Exp $	*/
 
 /*
  * Copyright (c) 1999, 2002, 2003, 2005, 2010 Matthew R. Green
@@ -32,7 +32,7 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: record.c,v 1.54 2015/08/05 06:54:39 mrg Exp $");
+__RCSID("$NetBSD: record.c,v 1.54.18.1 2024/03/12 12:41:39 martin Exp $");
 #endif
 
 
@@ -68,6 +68,7 @@ static char	*encoding_str;
 static struct track_info ti;
 static struct timeval record_time;
 static struct timeval start_time;
+static int no_time_limit = 1;
 
 static void (*conv_func) (u_char *, int);
 
@@ -75,13 +76,21 @@ static void usage (void) __dead;
 static int timeleft (struct timeval *, struct timeval *);
 static void cleanup (int) __dead;
 static void rewrite_header (void);
+static void stop (int);
+
+static void stop (int sig)
+{
+	no_time_limit = 0;
+	timerclear(&record_time);
+}
 
 int
 main(int argc, char *argv[])
 {
 	u_char	*buffer;
 	size_t	len, bufsize = 0;
-	int	ch, no_time_limit = 1;
+	ssize_t	nread;
+	int	ch;
 	const char *defdevice = _PATH_SOUND;
 
 	/*
@@ -283,7 +292,7 @@ main(int argc, char *argv[])
 	if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0)
 		err(1, "failed to set audio info");
 
-	signal(SIGINT, cleanup);
+	signal(SIGINT, stop);
 
 	ti.total_size = 0;
 
@@ -303,19 +312,27 @@ main(int argc, char *argv[])
 			s = "change sign (big-endian, 16 bit)";
 		else if (conv_func == change_sign16_le)
 			s = "change sign (little-endian, 16 bit)";
+		else if (conv_func == change_sign24_be)
+			s = "change sign (big-endian, 24 bit)";
+		else if (conv_func == change_sign24_le)
+			s = "change sign (little-endian, 24 bit)";
 		else if (conv_func == change_sign32_be)
 			s = "change sign (big-endian, 32 bit)";
 		else if (conv_func == change_sign32_le)
 			s = "change sign (little-endian, 32 bit)";
 		else if (conv_func == change_sign16_swap_bytes_be)
 			s = "change sign & swap bytes (big-endian, 16 bit)";
-		else if (conv_func == change_sign16_swap_bytes_le)
+		else if (conv_func == change_sign24_swap_bytes_le)
 			s = "change sign & swap bytes (little-endian, 16 bit)";
+		else if (conv_func == change_sign16_swap_bytes_be)
+			s = "change sign & swap bytes (big-endian, 24 bit)";
+		else if (conv_func == change_sign24_swap_bytes_le)
+			s = "change sign & swap bytes (little-endian, 24 bit)";
 		else if (conv_func == change_sign32_swap_bytes_be)
 			s = "change sign (big-endian, 32 bit)";
 		else if (conv_func == change_sign32_swap_bytes_le)
 			s = "change sign & swap bytes (little-endian, 32 bit)";
-		
+
 		if (s)
 			fprintf(stderr, "%s: converting, using function: %s\n",
 			    getprogname(), s);
@@ -337,13 +354,15 @@ main(int argc, char *argv[])
 
 	(void)gettimeofday(&start_time, NULL);
 	while (no_time_limit || timeleft(&start_time, &record_time)) {
-		if ((size_t)read(audiofd, buffer, bufsize) != bufsize)
+		if ((nread = read(audiofd, buffer, bufsize)) == -1)
 			err(1, "read failed");
+		if (nread == 0)
+			break;
 		if (conv_func)
-			(*conv_func)(buffer, bufsize);
-		if ((size_t)write(ti.outfd, buffer, bufsize) != bufsize)
+			(*conv_func)(buffer, nread);
+		if (write(ti.outfd, buffer, nread) != nread)
 			err(1, "write failed");
-		ti.total_size += bufsize;
+		ti.total_size += nread;
 	}
 	cleanup(0);
 }
@@ -386,7 +405,6 @@ rewrite_header(void)
 	/* can't do this here! */
 	if (ti.outfd == STDOUT_FILENO)
 		return;
-
 	if (lseek(ti.outfd, (off_t)0, SEEK_SET) == (off_t)-1)
 		err(1, "could not seek to start of file for header rewrite");
 	write_header(&ti);

Reply via email to