shithub: sox

Download patch

ref: 3874634dfd0b94b018b8529fedbe0b933074012f
parent: 205443a1cccac1dcd3e9648ef53b7b7eba4e1fb8
author: rrt <rrt>
date: Sun Apr 8 19:25:27 EDT 2007

Speed up reading/writing of 3-byte formats.

Rationalise and simplify I/O macros.

Move I/O of encoded types into soxio.c, and keep just sample
reading/writing in raw.c.

--- a/src/raw.c
+++ b/src/raw.c
@@ -93,162 +93,59 @@
   return SOX_SUCCESS;
 }
 
-/* Dummy macros */
-#define sox_swapb(d) (d)
-#define idcast(x, y) (x)
-
-#define TWIDDLE_BYTE_READ(ub, type) \
-  if (ft->signal.reverse_bits) \
-    ub = cswap[ub]; \
-  if (ft->signal.reverse_nibbles) \
-    ub = ((ub & 15) << 4) | (ub >> 4);
-
-#define TWIDDLE_BYTE_WRITE(ub, type) \
-  if (ft->signal.reverse_nibbles) \
-    ub = ((ub & 15) << 4) | (ub >> 4); \
-  if (ft->signal.reverse_bits) \
-    ub = cswap[ub];
-
-#define TWIDDLE_WORD(uw, type) \
-  if (ft->signal.reverse_bytes ^ SOX_IS_BIGENDIAN) \
-    uw = sox_swap ## type(uw);
-
-/* N.B. This macro doesn't work for types, like 3-byte types, which
-   don't fill the type used to represent them. */
-#define READ_FUNC(type, size, sign, ctype, uctype, outtype, cast, suffix, twiddle) \
-  sox_size_t sox_read_ ## sign ## type ## suffix( \
-      ft_t ft, outtype *buf, sox_size_t len) \
+#define READ_SAMPLES_FUNC(type, size, sign, ctype, cast) \
+  sox_size_t sox_read_ ## sign ## type ## _samples( \
+      ft_t ft, sox_sample_t *buf, sox_size_t len) \
   { \
-    sox_size_t n = 0, nread; \
+    sox_size_t n, nread; \
     ctype *data = xmalloc(sizeof(ctype) * len); \
-    if ((nread = sox_readbuf(ft, data, len * size)) != len * size) \
+    if ((nread = sox_read_ ## type ## _buf(ft, data, len)) != len) \
       sox_fail_errno(ft, errno, sox_readerr); \
-    nread /= size; \
-    for (; n < nread; n++) { \
-      twiddle(((uctype *)data)[n], type); \
+    for (n = 0; n < nread; n++) \
       *buf++ = cast(data[n], ft->clips); \
-    } \
     free(data); \
     return nread; \
   }
 
-/* This (slower) macro works for 3-byte types. */
-#define READ_FUNC2(type, size, sign, ctype, uctype, outtype, cast, suffix, twiddle) \
-  sox_size_t sox_read_ ## sign ## type ## suffix( \
-      ft_t ft, outtype *buf, sox_size_t len) \
-  { \
-    sox_size_t n; \
-    for (n = 0; n < len; n++) { \
-      ctype datum = 0; \
-      if (sox_readbuf(ft, &datum, size) != size) { \
-        sox_fail_errno(ft, errno, sox_readerr); \
-        break; \
-      } \
-      twiddle(datum, type); \
-      *buf++ = cast(datum, ft->clips); \
-    } \
-    return n; \
-  }
+static READ_SAMPLES_FUNC(b, 1, u, uint8_t, SOX_UNSIGNED_BYTE_TO_SAMPLE)
+static READ_SAMPLES_FUNC(b, 1, s, int8_t, SOX_SIGNED_BYTE_TO_SAMPLE)
+static READ_SAMPLES_FUNC(b, 1, ulaw, uint8_t, SOX_ULAW_BYTE_TO_SAMPLE)
+static READ_SAMPLES_FUNC(b, 1, alaw, uint8_t, SOX_ALAW_BYTE_TO_SAMPLE)
+static READ_SAMPLES_FUNC(w, 2, u, uint16_t, SOX_UNSIGNED_WORD_TO_SAMPLE)
+static READ_SAMPLES_FUNC(w, 2, s, int16_t, SOX_SIGNED_WORD_TO_SAMPLE)
+static READ_SAMPLES_FUNC(3, 3, u, uint24_t, SOX_UNSIGNED_24BIT_TO_SAMPLE)
+static READ_SAMPLES_FUNC(3, 3, s, int24_t, SOX_SIGNED_24BIT_TO_SAMPLE)
+static READ_SAMPLES_FUNC(dw, 4, u, uint32_t, SOX_UNSIGNED_DWORD_TO_SAMPLE)
+static READ_SAMPLES_FUNC(dw, 4, s, int32_t, SOX_SIGNED_DWORD_TO_SAMPLE)
+static READ_SAMPLES_FUNC(f, sizeof(float), su, float, SOX_FLOAT_DWORD_TO_SAMPLE)
+static READ_SAMPLES_FUNC(df, sizeof(double), su, double, SOX_FLOAT_DDWORD_TO_SAMPLE)
 
-READ_FUNC(b, 1, u, uint8_t, uint8_t, uint8_t, idcast, _buf, TWIDDLE_BYTE_READ)
-READ_FUNC(b, 1, s, int8_t, uint8_t, int8_t, idcast, _buf, TWIDDLE_BYTE_READ)
-READ_FUNC(b, 1, ulaw, uint8_t, uint8_t, uint8_t, idcast, _buf, TWIDDLE_BYTE_READ)
-READ_FUNC(b, 1, alaw, uint8_t, uint8_t, uint8_t, idcast, _buf, TWIDDLE_BYTE_READ)
-READ_FUNC(w, 2, u, uint16_t, uint16_t, uint16_t, idcast, _buf, TWIDDLE_WORD)
-READ_FUNC(w, 2, s, int16_t, uint16_t, int16_t, idcast, _buf, TWIDDLE_WORD)
-READ_FUNC2(3, 3, u, uint24_t, uint24_t, uint24_t, idcast, _buf, TWIDDLE_WORD)
-READ_FUNC2(3, 3, s, int24_t, uint24_t, int24_t, idcast, _buf, TWIDDLE_WORD)
-READ_FUNC(dw, 4, u, uint32_t, uint32_t, uint32_t, idcast, _buf, TWIDDLE_WORD)
-READ_FUNC(dw, 4, s, int32_t, uint32_t, int32_t, idcast, _buf, TWIDDLE_WORD)
-READ_FUNC(f, sizeof(float), su, float, float, float, idcast, _buf, TWIDDLE_WORD)
-READ_FUNC(df, sizeof(double), su, double, double, double, idcast, _buf, TWIDDLE_WORD)
-
-static READ_FUNC(b, 1, u, uint8_t, uint8_t, sox_sample_t, SOX_UNSIGNED_BYTE_TO_SAMPLE, _samples, TWIDDLE_BYTE_READ)
-static READ_FUNC(b, 1, s, int8_t, uint8_t, sox_sample_t, SOX_SIGNED_BYTE_TO_SAMPLE, _samples, TWIDDLE_BYTE_READ)
-static READ_FUNC(b, 1, ulaw, uint8_t, uint8_t, sox_sample_t, SOX_ULAW_BYTE_TO_SAMPLE, _samples, TWIDDLE_BYTE_READ)
-static READ_FUNC(b, 1, alaw, uint8_t, uint8_t, sox_sample_t, SOX_ALAW_BYTE_TO_SAMPLE, _samples, TWIDDLE_BYTE_READ)
-static READ_FUNC(w, 2, u, uint16_t, uint16_t, sox_sample_t, SOX_UNSIGNED_WORD_TO_SAMPLE, _samples, TWIDDLE_WORD)
-static READ_FUNC(w, 2, s, int16_t, uint16_t, sox_sample_t, SOX_SIGNED_WORD_TO_SAMPLE, _samples, TWIDDLE_WORD)
-static READ_FUNC2(3, 3, u, uint24_t, uint24_t, sox_sample_t, SOX_UNSIGNED_24BIT_TO_SAMPLE, _samples, TWIDDLE_WORD)
-static READ_FUNC2(3, 3, s, int24_t, uint24_t, sox_sample_t, SOX_SIGNED_24BIT_TO_SAMPLE, _samples, TWIDDLE_WORD)
-static READ_FUNC(dw, 4, u, uint32_t, uint32_t, sox_sample_t, SOX_UNSIGNED_DWORD_TO_SAMPLE, _samples, TWIDDLE_WORD)
-static READ_FUNC(dw, 4, s, int32_t, uint32_t, sox_sample_t, SOX_SIGNED_DWORD_TO_SAMPLE, _samples, TWIDDLE_WORD)
-static READ_FUNC(f, sizeof(float), su, float, float, sox_sample_t, SOX_FLOAT_DWORD_TO_SAMPLE, _samples, TWIDDLE_WORD)
-static READ_FUNC(df, sizeof(double), su, double, double, sox_sample_t, SOX_FLOAT_DDWORD_TO_SAMPLE, _samples, TWIDDLE_WORD)
-
-/* N.B. This macro doesn't work for types, like 3-byte types, which
-   don't fill the type used to represent them. */
-#define WRITE_FUNC(type, size, sign, ctype, uctype, outtype, cast, suffix, twiddle) \
-  sox_size_t sox_write_ ## sign ## type ## suffix( \
-      ft_t ft, outtype *buf, sox_size_t len) \
+#define WRITE_SAMPLES_FUNC(type, size, sign, ctype, cast) \
+  sox_size_t sox_write_ ## sign ## type ## _samples( \
+      ft_t ft, sox_sample_t *buf, sox_size_t len) \
   { \
-    sox_size_t n = 0, nwritten; \
+    sox_size_t n, nwritten; \
     ctype *data = xmalloc(sizeof(ctype) * len); \
-    for (; n < len; n++) { \
-      data[n] = cast(*buf++, ft->clips); \
-      twiddle(((uctype *)data)[n], type); \
-    } \
-    if ((nwritten = sox_writebuf(ft, data, len * size)) != len * size) \
-      sox_fail_errno(ft, errno, sox_readerr); \
+    for (n = 0; n < len; n++) \
+      data[n] = cast(buf[n], ft->clips); \
+    if ((nwritten = sox_write_ ## type ## _buf(ft, data, len)) != len) \
+      sox_fail_errno(ft, errno, sox_writerr); \
     free(data); \
-    return nwritten / size; \
+    return nwritten; \
   }
 
-/* This (slower) macro works for 3-byte types. */
-#define WRITE_FUNC2(type, size, sign, ctype, uctype, intype, cast, suffix, twiddle) \
-  sox_size_t sox_write_ ## sign ## type ## suffix( \
-      ft_t ft, intype *buf, sox_size_t len) \
-  { \
-    sox_size_t n; \
-    for (n = 0; n < len; n++) { \
-      ctype datum = cast(*buf++, ft->clips); \
-      twiddle(datum, type); \
-      if (sox_writebuf(ft, &datum, size) != size) { \
-        sox_fail_errno(ft, errno, sox_readerr); \
-        break; \
-      } \
-    } \
-    return n; \
-  }
-
-WRITE_FUNC(b, 1, u, uint8_t, uint8_t, uint8_t, idcast, _buf, TWIDDLE_BYTE_WRITE)
-WRITE_FUNC(b, 1, s, int8_t, uint8_t, int8_t, idcast, _buf, TWIDDLE_BYTE_WRITE)
-WRITE_FUNC(b, 1, ulaw, uint8_t, uint8_t, uint8_t, idcast, _buf, TWIDDLE_BYTE_WRITE)
-WRITE_FUNC(b, 1, alaw, uint8_t, uint8_t, uint8_t, idcast, _buf, TWIDDLE_BYTE_WRITE)
-WRITE_FUNC(w, 2, u, uint16_t, uint16_t, uint16_t, idcast, _buf, TWIDDLE_WORD)
-WRITE_FUNC(w, 2, s, int16_t, uint16_t, int16_t, idcast, _buf, TWIDDLE_WORD)
-WRITE_FUNC2(3, 3, u, uint24_t, uint24_t, uint24_t, idcast, _buf, TWIDDLE_WORD)
-WRITE_FUNC2(3, 3, s, int24_t, uint24_t, int24_t, idcast, _buf, TWIDDLE_WORD)
-WRITE_FUNC(dw, 4, u, uint32_t, uint32_t, uint32_t, idcast, _buf, TWIDDLE_WORD)
-WRITE_FUNC(dw, 4, s, int32_t, uint32_t, int32_t, idcast, _buf, TWIDDLE_WORD)
-WRITE_FUNC(f, sizeof(float), su, float, float, float, idcast, _buf, TWIDDLE_WORD)
-WRITE_FUNC(df, sizeof(double), su, double, double, double, idcast, _buf, TWIDDLE_WORD)
-
-static WRITE_FUNC(b, 1, u, uint8_t, uint8_t, sox_sample_t, SOX_SAMPLE_TO_UNSIGNED_BYTE, _samples, TWIDDLE_BYTE_WRITE)
-static WRITE_FUNC(b, 1, s, int8_t, uint8_t, sox_sample_t, SOX_SAMPLE_TO_SIGNED_BYTE, _samples, TWIDDLE_BYTE_WRITE)
-static WRITE_FUNC(b, 1, ulaw, uint8_t, uint8_t, sox_sample_t, SOX_SAMPLE_TO_ULAW_BYTE, _samples, TWIDDLE_BYTE_WRITE)
-static WRITE_FUNC(b, 1, alaw, uint8_t, uint8_t, sox_sample_t, SOX_SAMPLE_TO_ALAW_BYTE, _samples, TWIDDLE_BYTE_WRITE)
-static WRITE_FUNC(w, 2, u, uint16_t, uint16_t, sox_sample_t, SOX_SAMPLE_TO_UNSIGNED_WORD, _samples, TWIDDLE_WORD)
-static WRITE_FUNC(w, 2, s, int16_t, uint16_t, sox_sample_t, SOX_SAMPLE_TO_SIGNED_WORD, _samples, TWIDDLE_WORD)
-static WRITE_FUNC2(3, 3, u, uint24_t, uint24_t, sox_sample_t, SOX_SAMPLE_TO_UNSIGNED_24BIT, _samples, TWIDDLE_WORD)
-static WRITE_FUNC2(3, 3, s, int24_t, uint24_t, sox_sample_t, SOX_SAMPLE_TO_SIGNED_24BIT, _samples, TWIDDLE_WORD)
-static WRITE_FUNC(dw, 4, u, uint32_t, uint32_t, sox_sample_t, SOX_SAMPLE_TO_UNSIGNED_DWORD, _samples, TWIDDLE_WORD)
-static WRITE_FUNC(dw, 4, s, int32_t, uint32_t, sox_sample_t, SOX_SAMPLE_TO_SIGNED_DWORD, _samples, TWIDDLE_WORD)
-static WRITE_FUNC(f, sizeof(float), su, float, float, sox_sample_t, SOX_SAMPLE_TO_FLOAT_DWORD, _samples, TWIDDLE_WORD)
-static WRITE_FUNC(df, sizeof(double), su, double, double, sox_sample_t, SOX_SAMPLE_TO_FLOAT_DDWORD, _samples, TWIDDLE_WORD)
-
-#define WRITE1_FUNC(type, sign, ctype) \
-  int sox_write ## type(ft_t ft, ctype datum) \
-  { \
-    return sox_write_ ## sign ## type ## _buf(ft, &datum, 1) == 1 ? SOX_SUCCESS : SOX_EOF; \
-  }
-
-WRITE1_FUNC(b, u, uint8_t)
-WRITE1_FUNC(w, u, uint16_t)
-WRITE1_FUNC(3, u, uint24_t)
-WRITE1_FUNC(dw, u, uint32_t)
-WRITE1_FUNC(f, su, float)
-WRITE1_FUNC(df, su, double)
+static WRITE_SAMPLES_FUNC(b, 1, u, uint8_t, SOX_SAMPLE_TO_UNSIGNED_BYTE)
+static WRITE_SAMPLES_FUNC(b, 1, s, int8_t, SOX_SAMPLE_TO_SIGNED_BYTE)
+static WRITE_SAMPLES_FUNC(b, 1, ulaw, uint8_t, SOX_SAMPLE_TO_ULAW_BYTE)
+static WRITE_SAMPLES_FUNC(b, 1, alaw, uint8_t, SOX_SAMPLE_TO_ALAW_BYTE)
+static WRITE_SAMPLES_FUNC(w, 2, u, uint16_t, SOX_SAMPLE_TO_UNSIGNED_WORD)
+static WRITE_SAMPLES_FUNC(w, 2, s, int16_t, SOX_SAMPLE_TO_SIGNED_WORD)
+static WRITE_SAMPLES_FUNC(3, 3, u, uint24_t, SOX_SAMPLE_TO_UNSIGNED_24BIT)
+static WRITE_SAMPLES_FUNC(3, 3, s, int24_t, SOX_SAMPLE_TO_SIGNED_24BIT)
+static WRITE_SAMPLES_FUNC(dw, 4, u, uint32_t, SOX_SAMPLE_TO_UNSIGNED_DWORD)
+static WRITE_SAMPLES_FUNC(dw, 4, s, int32_t, SOX_SAMPLE_TO_SIGNED_DWORD)
+static WRITE_SAMPLES_FUNC(f, sizeof(float), su, float, SOX_SAMPLE_TO_FLOAT_DWORD)
+static WRITE_SAMPLES_FUNC(df, sizeof(double), su, double, SOX_SAMPLE_TO_FLOAT_DDWORD)
 
 typedef sox_size_t (ft_io_fun)(ft_t ft, sox_sample_t *buf, sox_size_t len);
 
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -106,43 +106,31 @@
 int sox_reads(ft_t ft, char *c, sox_size_t len);
 int sox_writes(ft_t ft, char const * c);
 
-sox_size_t sox_read_ub_buf(ft_t ft, uint8_t *buf, sox_size_t len);
-sox_size_t sox_read_sb_buf(ft_t ft, int8_t *buf, sox_size_t len);
-sox_size_t sox_read_ulawb_buf(ft_t ft, uint8_t *buf, sox_size_t len);
-sox_size_t sox_read_alawb_buf(ft_t ft, uint8_t *buf, sox_size_t len);
-sox_size_t sox_read_uw_buf(ft_t ft, uint16_t *buf, sox_size_t len);
-sox_size_t sox_read_sw_buf(ft_t ft, int16_t *buf, sox_size_t len);
-sox_size_t sox_read_u3_buf(ft_t ft, uint24_t *buf, sox_size_t len);
-sox_size_t sox_read_s3_buf(ft_t ft, int24_t *buf, sox_size_t len);
-sox_size_t sox_read_udw_buf(ft_t ft, uint32_t *buf, sox_size_t len);
-sox_size_t sox_read_sdw_buf(ft_t ft, int32_t *buf, sox_size_t len);
-sox_size_t sox_read_suf_buf(ft_t ft, float *buf, sox_size_t len);
-sox_size_t sox_read_sudf_buf(ft_t ft, double *buf, sox_size_t len);
+sox_size_t sox_read_b_buf(ft_t ft, uint8_t *buf, sox_size_t len);
+sox_size_t sox_read_w_buf(ft_t ft, uint16_t *buf, sox_size_t len);
+sox_size_t sox_read_3_buf(ft_t ft, uint24_t *buf, sox_size_t len);
+sox_size_t sox_read_dw_buf(ft_t ft, uint32_t *buf, sox_size_t len);
+sox_size_t sox_read_f_buf(ft_t ft, float *buf, sox_size_t len);
+sox_size_t sox_read_df_buf(ft_t ft, double *buf, sox_size_t len);
 
-sox_size_t sox_write_ub_buf(ft_t ft, uint8_t *buf, sox_size_t len);
-sox_size_t sox_write_sb_buf(ft_t ft, int8_t *buf, sox_size_t len);
-sox_size_t sox_write_ulawb_buf(ft_t ft, uint8_t *buf, sox_size_t len);
-sox_size_t sox_write_alawb_buf(ft_t ft, uint8_t *buf, sox_size_t len);
-sox_size_t sox_write_uw_buf(ft_t ft, uint16_t *buf, sox_size_t len);
-sox_size_t sox_write_sw_buf(ft_t ft, int16_t *buf, sox_size_t len);
-sox_size_t sox_write_u3_buf(ft_t ft, uint24_t *buf, sox_size_t len);
-sox_size_t sox_write_s3_buf(ft_t ft, int24_t *buf, sox_size_t len);
-sox_size_t sox_write_udw_buf(ft_t ft, uint32_t *buf, sox_size_t len);
-sox_size_t sox_write_sdw_buf(ft_t ft, int32_t *buf, sox_size_t len);
-sox_size_t sox_write_suf_buf(ft_t ft, float *buf, sox_size_t len);
-sox_size_t sox_write_sudf_buf(ft_t ft, double *buf, sox_size_t len);
+sox_size_t sox_write_b_buf(ft_t ft, uint8_t *buf, sox_size_t len);
+sox_size_t sox_write_w_buf(ft_t ft, uint16_t *buf, sox_size_t len);
+sox_size_t sox_write_3_buf(ft_t ft, uint24_t *buf, sox_size_t len);
+sox_size_t sox_write_dw_buf(ft_t ft, uint32_t *buf, sox_size_t len);
+sox_size_t sox_write_f_buf(ft_t ft, float *buf, sox_size_t len);
+sox_size_t sox_write_df_buf(ft_t ft, double *buf, sox_size_t len);
 
-#define sox_readb(ft, ub) (sox_read_ub_buf(ft, ub, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
+#define sox_readb(ft, ub) (sox_read_b_buf(ft, ub, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
 int sox_writeb(ft_t ft, uint8_t ub);
-#define sox_readw(ft, uw) (sox_read_uw_buf(ft, uw, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
+#define sox_readw(ft, uw) (sox_read_w_buf(ft, uw, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
 int sox_writew(ft_t ft, uint16_t uw);
-#define sox_read3(ft, u3) (sox_read_u3_buf(ft, u3, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
+#define sox_read3(ft, u3) (sox_read_3_buf(ft, u3, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
 int sox_write3(ft_t ft, uint24_t u3);
-#define sox_readdw(ft, udw) (sox_read_udw_buf(ft, udw, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
+#define sox_readdw(ft, udw) (sox_read_dw_buf(ft, udw, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
 int sox_writedw(ft_t ft, uint32_t udw);
-#define sox_readf(ft, f) (sox_read_suf_buf(ft, f, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
+#define sox_readf(ft, f) (sox_read_f_buf(ft, f, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
 int sox_writef(ft_t ft, float f);
-#define sox_readdf(ft, d) (sox_read_sudf_buf(ft, d, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
+#define sox_readdf(ft, d) (sox_read_df_buf(ft, d, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
 int sox_writedf(ft_t ft, double d);
 int sox_seeki(ft_t ft, sox_ssize_t offset, int whence);
 sox_size_t sox_filelength(ft_t ft);
--- a/src/soxio.c
+++ b/src/soxio.c
@@ -309,6 +309,122 @@
     return (*ft->h->write)(ft, buf, len);
 }
 
+#define TWIDDLE_BYTE(ub, type) \
+  do { \
+    if (ft->signal.reverse_bits) \
+      ub = cswap[ub]; \
+    if (ft->signal.reverse_nibbles) \
+      ub = ((ub & 15) << 4) | (ub >> 4); \
+  } while (0);
+
+#define TWIDDLE_WORD(uw, type) \
+  if (ft->signal.reverse_bytes ^ SOX_IS_BIGENDIAN) \
+    uw = sox_swap ## type(uw);
+
+/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte
+   types). */
+#define READ_FUNC(type, size, ctype, twiddle) \
+  sox_size_t sox_read_ ## type ## _buf( \
+      ft_t ft, ctype *buf, sox_size_t len) \
+  { \
+    sox_size_t n, nread; \
+    if ((nread = sox_readbuf(ft, buf, len * size)) != len * size) \
+      sox_fail_errno(ft, errno, sox_readerr); \
+    nread /= size; \
+    for (n = 0; n < nread; n++) \
+      twiddle(buf[n], type); \
+    return nread; \
+  }
+
+/* Unpack a 3-byte value from a uint8_t * */
+#define sox_unpack3(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16))
+
+/* This (slower) macro works for unaligned types (e.g. 3-byte types)
+   that need to be unpacked. */
+#define READ_FUNC_UNPACK(type, size, ctype, twiddle) \
+  sox_size_t sox_read_ ## type ## _buf( \
+      ft_t ft, ctype *buf, sox_size_t len) \
+  { \
+    sox_size_t n, nread; \
+    uint8_t *data = xmalloc(size * len); \
+    if ((nread = sox_readbuf(ft, data, len * size)) != len * size) \
+      sox_fail_errno(ft, errno, sox_readerr); \
+    nread /= size; \
+    for (n = 0; n < nread; n++) { \
+      ctype datum = sox_unpack ## size(data + n * size); \
+      twiddle(datum, type); \
+      buf[n] = datum; \
+    } \
+    free(data); \
+    return n; \
+  }
+
+READ_FUNC(b, 1, uint8_t, TWIDDLE_BYTE)
+READ_FUNC(w, 2, uint16_t, TWIDDLE_WORD)
+READ_FUNC_UNPACK(3, 3, uint24_t, TWIDDLE_WORD)
+READ_FUNC(dw, 4, uint32_t, TWIDDLE_WORD)
+READ_FUNC(f, sizeof(float), float, TWIDDLE_WORD)
+READ_FUNC(df, sizeof(double), double, TWIDDLE_WORD)
+
+/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte
+   types). */
+#define WRITE_FUNC(type, size, ctype, twiddle) \
+  sox_size_t sox_write_ ## type ## _buf( \
+      ft_t ft, ctype *buf, sox_size_t len) \
+  { \
+    sox_size_t n, nwritten; \
+    for (n = 0; n < len; n++) \
+      twiddle(buf[n], type); \
+    if ((nwritten = sox_writebuf(ft, buf, len * size)) != len * size) \
+      sox_fail_errno(ft, errno, sox_writerr); \
+    return nwritten / size; \
+  }
+
+/* Pack a 3-byte value to a uint8_t * */
+#define sox_pack3(p, v) \
+  (p)[0] = v & 0xff; \
+  (p)[1] = (v >> 8) & 0xff; \
+  (p)[2] = (v >> 16) & 0xff;
+
+/* This (slower) macro works for unaligned types (e.g. 3-byte types)
+   that need to be packed. */
+#define WRITE_FUNC_PACK(type, size, ctype, twiddle) \
+  sox_size_t sox_write_ ## type ## _buf( \
+      ft_t ft, ctype *buf, sox_size_t len) \
+  { \
+    sox_size_t n, nwritten; \
+    uint8_t *data = xmalloc(size * len); \
+    for (n = 0; n < len; n++) { \
+      ctype datum = buf[n]; \
+      twiddle(datum, type); \
+      sox_pack ## size(data + n * size, datum); \
+    } \
+    if ((nwritten = sox_writebuf(ft, data, len * size)) != len * size) \
+      sox_fail_errno(ft, errno, sox_writerr); \
+    free(data); \
+    return nwritten / size; \
+  }
+
+WRITE_FUNC(b, 1, uint8_t, TWIDDLE_BYTE)
+WRITE_FUNC(w, 2, uint16_t, TWIDDLE_WORD)
+WRITE_FUNC_PACK(3, 3, uint24_t, TWIDDLE_WORD)
+WRITE_FUNC(dw, 4, uint32_t, TWIDDLE_WORD)
+WRITE_FUNC(f, sizeof(float), float, TWIDDLE_WORD)
+WRITE_FUNC(df, sizeof(double), double, TWIDDLE_WORD)
+
+#define WRITE1_FUNC(type, sign, ctype) \
+  int sox_write ## type(ft_t ft, ctype datum) \
+  { \
+    return sox_write_ ## type ## _buf(ft, &datum, 1) == 1 ? SOX_SUCCESS : SOX_EOF; \
+  }
+
+WRITE1_FUNC(b, u, uint8_t)
+WRITE1_FUNC(w, u, uint16_t)
+WRITE1_FUNC(3, u, uint24_t)
+WRITE1_FUNC(dw, u, uint32_t)
+WRITE1_FUNC(f, su, float)
+WRITE1_FUNC(df, su, double)
+
 /* N.B. The file (if any) may already have been deleted. */
 int sox_close(ft_t ft)
 {