1 /* Implements a libSoX internal interface for use in implementing file formats.
2  * All public functions & data are prefixed with lsx_ .
3  *
4  * (c) 2005-8 Chris Bagwell and SoX contributors
5  *
6  * This library is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or (at
9  * your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "sox_i.h"
22 #include <string.h>
23 #include <sys/stat.h>
24 #include <stdarg.h>
25 
lsx_fail_errno(sox_format_t * ft,int sox_errno,const char * fmt,...)26 void lsx_fail_errno(sox_format_t * ft, int sox_errno, const char *fmt, ...)
27 {
28   va_list args;
29 
30   ft->sox_errno = sox_errno;
31 
32   va_start(args, fmt);
33 #ifdef HAVE_VSNPRINTF
34   vsnprintf(ft->sox_errstr, sizeof(ft->sox_errstr), fmt, args);
35 #else
36   vsprintf(ft->sox_errstr, fmt, args);
37 #endif
38   va_end(args);
39   ft->sox_errstr[255] = '\0';
40 }
41 
lsx_set_signal_defaults(sox_format_t * ft)42 void lsx_set_signal_defaults(sox_format_t * ft)
43 {
44   if (!ft->signal.rate     ) ft->signal.rate      = SOX_DEFAULT_RATE;
45   if (!ft->signal.precision) ft->signal.precision = SOX_DEFAULT_PRECISION;
46   if (!ft->signal.channels ) ft->signal.channels  = SOX_DEFAULT_CHANNELS;
47 
48   if (!ft->encoding.bits_per_sample)
49     ft->encoding.bits_per_sample = ft->signal.precision;
50   if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN)
51     ft->encoding.encoding = SOX_ENCODING_SIGN2;
52 }
53 
lsx_check_read_params(sox_format_t * ft,unsigned channels,sox_rate_t rate,sox_encoding_t encoding,unsigned bits_per_sample,uint64_t num_samples,sox_bool check_length)54 int lsx_check_read_params(sox_format_t * ft, unsigned channels,
55     sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample,
56     uint64_t num_samples, sox_bool check_length)
57 {
58   ft->signal.length = ft->signal.length == SOX_IGNORE_LENGTH? SOX_UNSPEC : num_samples;
59 
60   if (ft->seekable)
61     ft->data_start = lsx_tell(ft);
62 
63   if (channels && ft->signal.channels && ft->signal.channels != channels)
64     lsx_warn("`%s': overriding number of channels", ft->filename);
65   else ft->signal.channels = channels;
66 
67   if (rate && ft->signal.rate && ft->signal.rate != rate)
68     lsx_warn("`%s': overriding sample rate", ft->filename);
69   else ft->signal.rate = rate;
70 
71   if (encoding && ft->encoding.encoding && ft->encoding.encoding != encoding)
72     lsx_warn("`%s': overriding encoding type", ft->filename);
73   else ft->encoding.encoding = encoding;
74 
75   if (bits_per_sample && ft->encoding.bits_per_sample && ft->encoding.bits_per_sample != bits_per_sample)
76     lsx_warn("`%s': overriding encoding size", ft->filename);
77   ft->encoding.bits_per_sample = bits_per_sample;
78 
79   if (check_length && ft->encoding.bits_per_sample && lsx_filelength(ft)) {
80     uint64_t calculated_length = div_bits(lsx_filelength(ft) - ft->data_start, ft->encoding.bits_per_sample);
81     if (!ft->signal.length)
82       ft->signal.length = calculated_length;
83     else if (num_samples != calculated_length)
84       lsx_warn("`%s': file header gives the total number of samples as %" PRIu64 " but file length indicates the number is in fact %" PRIu64, ft->filename, num_samples, calculated_length);
85   }
86 
87   if (sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample))
88     return SOX_SUCCESS;
89   lsx_fail_errno(ft, EINVAL, "invalid format for this file type");
90   return SOX_EOF;
91 }
92 
93 /* Read in a buffer of data of length len bytes.
94  * Returns number of bytes read.
95  */
lsx_readbuf(sox_format_t * ft,void * buf,size_t len)96 size_t lsx_readbuf(sox_format_t * ft, void *buf, size_t len)
97 {
98   size_t ret = fread(buf, (size_t) 1, len, (FILE*)ft->fp);
99   if (ret != len && ferror((FILE*)ft->fp))
100     lsx_fail_errno(ft, errno, "lsx_readbuf");
101   ft->tell_off += ret;
102   return ret;
103 }
104 
105 /* Skip input without seeking. */
lsx_skipbytes(sox_format_t * ft,size_t n)106 int lsx_skipbytes(sox_format_t * ft, size_t n)
107 {
108   unsigned char trash;
109 
110   while (n--)
111     if (lsx_readb(ft, &trash) == SOX_EOF)
112       return (SOX_EOF);
113 
114   return (SOX_SUCCESS);
115 }
116 
117 /* Pad output. */
lsx_padbytes(sox_format_t * ft,size_t n)118 int lsx_padbytes(sox_format_t * ft, size_t n)
119 {
120   while (n--)
121     if (lsx_writeb(ft, '\0') == SOX_EOF)
122       return (SOX_EOF);
123 
124   return (SOX_SUCCESS);
125 }
126 
127 /* Write a buffer of data of length bytes.
128  * Returns number of bytes written.
129  */
lsx_writebuf(sox_format_t * ft,void const * buf,size_t len)130 size_t lsx_writebuf(sox_format_t * ft, void const * buf, size_t len)
131 {
132   size_t ret = fwrite(buf, (size_t) 1, len, (FILE*)ft->fp);
133   if (ret != len) {
134     lsx_fail_errno(ft, errno, "error writing output file");
135     clearerr((FILE*)ft->fp); /* Allows us to seek back to write header */
136   }
137   ft->tell_off += ret;
138   return ret;
139 }
140 
lsx_filelength(sox_format_t * ft)141 sox_uint64_t lsx_filelength(sox_format_t * ft)
142 {
143   struct stat st;
144   int ret = ft->fp ? fstat(fileno((FILE*)ft->fp), &st) : 0;
145 
146   return (!ret && (st.st_mode & S_IFREG))? (uint64_t)st.st_size : 0;
147 }
148 
lsx_flush(sox_format_t * ft)149 int lsx_flush(sox_format_t * ft)
150 {
151   return fflush((FILE*)ft->fp);
152 }
153 
lsx_tell(sox_format_t * ft)154 off_t lsx_tell(sox_format_t * ft)
155 {
156   return ft->seekable? (off_t)ftello((FILE*)ft->fp) : (off_t)ft->tell_off;
157 }
158 
lsx_eof(sox_format_t * ft)159 int lsx_eof(sox_format_t * ft)
160 {
161   return feof((FILE*)ft->fp);
162 }
163 
lsx_error(sox_format_t * ft)164 int lsx_error(sox_format_t * ft)
165 {
166   return ferror((FILE*)ft->fp);
167 }
168 
lsx_rewind(sox_format_t * ft)169 void lsx_rewind(sox_format_t * ft)
170 {
171   rewind((FILE*)ft->fp);
172   ft->tell_off = 0;
173 }
174 
lsx_clearerr(sox_format_t * ft)175 void lsx_clearerr(sox_format_t * ft)
176 {
177   clearerr((FILE*)ft->fp);
178   ft->sox_errno = 0;
179 }
180 
lsx_unreadb(sox_format_t * ft,unsigned b)181 int lsx_unreadb(sox_format_t * ft, unsigned b)
182 {
183   return ungetc((int)b, ft->fp);
184 }
185 
186 /* Implements traditional fseek() behavior.  Meant to abstract out
187  * file operations so that they could one day also work on memory
188  * buffers.
189  *
190  * N.B. Can only seek forwards on non-seekable streams!
191  */
lsx_seeki(sox_format_t * ft,off_t offset,int whence)192 int lsx_seeki(sox_format_t * ft, off_t offset, int whence)
193 {
194     if (ft->seekable == 0) {
195         /* If a stream peel off chars else EPERM */
196         if (whence == SEEK_CUR) {
197             while (offset > 0 && !feof((FILE*)ft->fp)) {
198                 getc((FILE*)ft->fp);
199                 offset--;
200                 ++ft->tell_off;
201             }
202             if (offset)
203                 lsx_fail_errno(ft,SOX_EOF, "offset past EOF");
204             else
205                 ft->sox_errno = SOX_SUCCESS;
206         } else
207             lsx_fail_errno(ft,SOX_EPERM, "file not seekable");
208     } else {
209         if (fseeko((FILE*)ft->fp, offset, whence) == -1)
210             lsx_fail_errno(ft,errno, "%s", strerror(errno));
211         else
212             ft->sox_errno = SOX_SUCCESS;
213     }
214     return ft->sox_errno;
215 }
216 
lsx_offset_seek(sox_format_t * ft,off_t byte_offset,off_t to_sample)217 int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, off_t to_sample)
218 {
219   double wide_sample = to_sample - (to_sample % ft->signal.channels);
220   double to_d = wide_sample * ft->encoding.bits_per_sample / 8;
221   off_t to = to_d;
222   return (to != to_d)? SOX_EOF : lsx_seeki(ft, (byte_offset + to), SEEK_SET);
223 }
224 
225 /* Read and write known datatypes in "machine format".  Swap if indicated.
226  * They all return SOX_EOF on error and SOX_SUCCESS on success.
227  */
228 /* Read n-char string (and possibly null-terminating).
229  * Stop reading and null-terminate string if either a 0 or \n is reached.
230  */
lsx_reads(sox_format_t * ft,char * c,size_t len)231 int lsx_reads(sox_format_t * ft, char *c, size_t len)
232 {
233     char *sc;
234     char in;
235 
236     sc = c;
237     do
238     {
239         if (lsx_readbuf(ft, &in, (size_t)1) != 1)
240         {
241             *sc = 0;
242             return (SOX_EOF);
243         }
244         if (in == 0 || in == '\n')
245             break;
246 
247         *sc = in;
248         sc++;
249     } while (sc - c < (ptrdiff_t)len);
250     *sc = 0;
251     return(SOX_SUCCESS);
252 }
253 
254 /* Write null-terminated string (without \0). */
lsx_writes(sox_format_t * ft,char const * c)255 int lsx_writes(sox_format_t * ft, char const * c)
256 {
257         if (lsx_writebuf(ft, c, strlen(c)) != strlen(c))
258                 return(SOX_EOF);
259         return(SOX_SUCCESS);
260 }
261 
262 /* return swapped 32-bit float */
lsx_swapf(float * f)263 static void lsx_swapf(float * f)
264 {
265     union {
266         uint32_t dw;
267         float f;
268     } u;
269 
270     u.f= *f;
271     u.dw= (u.dw>>24) | ((u.dw>>8)&0xff00) | ((u.dw<<8)&0xff0000) | (u.dw<<24);
272     *f = u.f;
273 }
274 
swap(void * data,size_t len)275 static void swap(void * data, size_t len)
276 {
277   uint8_t * bytes = (uint8_t *)data;
278   size_t i;
279 
280   for (i = 0; i < len / 2; ++i) {
281     char tmp = bytes[i];
282     bytes[i] = bytes[len - 1 - i];
283     bytes[len - 1 - i] = tmp;
284   }
285 }
286 
lsx_swapdf(double data)287 static double lsx_swapdf(double data)
288 {
289   swap(&data, sizeof(data));
290   return data;
291 }
292 
lsx_swapqw(uint64_t data)293 static uint64_t lsx_swapqw(uint64_t data)
294 {
295   swap(&data, sizeof(data));
296   return data;
297 }
298 
299 /* Lookup table to reverse the bit order of a byte. ie MSB become LSB */
300 static uint8_t const cswap[256] = {
301   0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0,
302   0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
303   0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4,
304   0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
305   0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC,
306   0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
307   0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA,
308   0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
309   0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6,
310   0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
311   0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1,
312   0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
313   0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9,
314   0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
315   0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD,
316   0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
317   0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3,
318   0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
319   0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7,
320   0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
321   0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF,
322   0x3F, 0xBF, 0x7F, 0xFF
323 };
324 
325 /* Utilities to byte-swap values, use libc optimized macros if possible  */
326 #define TWIDDLE_BYTE(ub, type) \
327   do { \
328     if (ft->encoding.reverse_bits) \
329       ub = cswap[ub]; \
330     if (ft->encoding.reverse_nibbles) \
331       ub = ((ub & 15) << 4) | (ub >> 4); \
332   } while (0);
333 
334 #define TWIDDLE_WORD(uw, type) \
335   if (ft->encoding.reverse_bytes) \
336     uw = lsx_swap ## type(uw);
337 
338 #define TWIDDLE_FLOAT(f, type) \
339   if (ft->encoding.reverse_bytes) \
340     lsx_swapf(&f);
341 
342 /* N.B. This macro doesn't work for unaligned types (e.g. 3-byte
343    types). */
344 #define READ_FUNC(type, size, ctype, twiddle) \
345   size_t lsx_read_ ## type ## _buf( \
346       sox_format_t * ft, ctype *buf, size_t len) \
347   { \
348     size_t n, nread; \
349     nread = lsx_readbuf(ft, buf, len * size) / size; \
350     for (n = 0; n < nread; n++) \
351       twiddle(buf[n], type); \
352     return nread; \
353   }
354 
355 /* Unpack a 3-byte value from a uint8_t * */
356 #define sox_unpack3(p) (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN? \
357   ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16)) : \
358   ((p)[2] | ((p)[1] << 8) | ((p)[0] << 16)))
359 
360 /* This (slower) macro works for unaligned types (e.g. 3-byte types)
361    that need to be unpacked. */
362 #define READ_FUNC_UNPACK(type, size, ctype, twiddle) \
363   size_t lsx_read_ ## type ## _buf( \
364       sox_format_t * ft, ctype *buf, size_t len) \
365   { \
366     size_t n, nread; \
367     uint8_t *data = lsx_malloc(size * len); \
368     nread = lsx_readbuf(ft, data, len * size) / size; \
369     for (n = 0; n < nread; n++) \
370       buf[n] = sox_unpack ## size(data + n * size); \
371     free(data); \
372     return n; \
373   }
374 
375 READ_FUNC(b, 1, uint8_t, TWIDDLE_BYTE)
376 READ_FUNC(w, 2, uint16_t, TWIDDLE_WORD)
377 READ_FUNC_UNPACK(3, 3, sox_uint24_t, TWIDDLE_WORD)
378 READ_FUNC(dw, 4, uint32_t, TWIDDLE_WORD)
379 READ_FUNC(qw, 8, uint64_t, TWIDDLE_WORD)
380 READ_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT)
381 READ_FUNC(df, sizeof(double), double, TWIDDLE_WORD)
382 
383 #define READ1_FUNC(type, ctype) \
384 int lsx_read ## type(sox_format_t * ft, ctype * datum) { \
385   if (lsx_read_ ## type ## _buf(ft, datum, (size_t)1) == 1) \
386     return SOX_SUCCESS; \
387   if (!lsx_error(ft)) \
388     lsx_fail_errno(ft, errno, premature_eof); \
389   return SOX_EOF; \
390 }
391 
392 static char const premature_eof[] = "premature EOF";
393 
READ1_FUNC(b,uint8_t)394 READ1_FUNC(b,  uint8_t)
395 READ1_FUNC(w,  uint16_t)
396 READ1_FUNC(3,  sox_uint24_t)
397 READ1_FUNC(dw, uint32_t)
398 READ1_FUNC(qw, uint64_t)
399 READ1_FUNC(f,  float)
400 READ1_FUNC(df, double)
401 
402 int lsx_readchars(sox_format_t * ft, char * chars, size_t len)
403 {
404   size_t ret = lsx_readbuf(ft, chars, len);
405   if (ret == len)
406     return SOX_SUCCESS;
407   if (!lsx_error(ft))
408     lsx_fail_errno(ft, errno, premature_eof);
409   return SOX_EOF;
410 }
411 
lsx_read_fields(sox_format_t * ft,uint32_t * len,const char * spec,...)412 int lsx_read_fields(sox_format_t *ft, uint32_t *len,  const char *spec, ...)
413 {
414     int err = SOX_SUCCESS;
415     va_list ap;
416 
417 #define do_read(type, f, n) do {                                \
418         size_t nr;                                              \
419         if (*len < n * sizeof(type)) {                          \
420             err = SOX_EOF;                                      \
421             goto end;                                           \
422         }                                                       \
423         nr = lsx_read_##f##_buf(ft, va_arg(ap, type *), r);     \
424         if (nr != n)                                            \
425             err = SOX_EOF;                                      \
426         *len -= nr * sizeof(type);                              \
427     } while (0)
428 
429     va_start(ap, spec);
430 
431     while (*spec) {
432         unsigned long r = 1;
433         char c = *spec;
434 
435         if (c >= '0' && c <= '9') {
436             char *next;
437             r = strtoul(spec, &next, 10);
438             spec = next;
439             c = *spec;
440         } else if (c == '*') {
441             r = va_arg(ap, int);
442             c = *++spec;
443         }
444 
445         switch (c) {
446         case 'b': do_read(uint8_t, b, r);       break;
447         case 'h': do_read(uint16_t, w, r);      break;
448         case 'i': do_read(uint32_t, dw, r);     break;
449         case 'q': do_read(uint64_t, qw, r);     break;
450         case 'x': err = lsx_skipbytes(ft, r);   break;
451 
452         default:
453             lsx_fail("lsx_read_fields: invalid format character '%c'", c);
454             err = SOX_EOF;
455             break;
456         }
457 
458         if (err)
459             break;
460 
461         spec++;
462     }
463 
464 end:
465     va_end(ap);
466 
467 #undef do_read
468 
469     return err;
470 }
471 
472 /* N.B. This macro doesn't work for unaligned types (e.g. 3-byte
473    types). */
474 #define WRITE_FUNC(type, size, ctype, twiddle) \
475   size_t lsx_write_ ## type ## _buf( \
476       sox_format_t * ft, ctype *buf, size_t len) \
477   { \
478     size_t n, nwritten; \
479     for (n = 0; n < len; n++) \
480       twiddle(buf[n], type); \
481     nwritten = lsx_writebuf(ft, buf, len * size); \
482     return nwritten / size; \
483   }
484 
485 /* Pack a 3-byte value to a uint8_t * */
486 #define sox_pack3(p, v) do {if (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN)\
487 {(p)[0] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[2] = (v >> 16) & 0xff;} else \
488 {(p)[2] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[0] = (v >> 16) & 0xff;} \
489 } while (0)
490 
491 /* This (slower) macro works for unaligned types (e.g. 3-byte types)
492    that need to be packed. */
493 #define WRITE_FUNC_PACK(type, size, ctype, twiddle) \
494   size_t lsx_write_ ## type ## _buf( \
495       sox_format_t * ft, ctype *buf, size_t len) \
496   { \
497     size_t n, nwritten; \
498     uint8_t *data = lsx_malloc(size * len); \
499     for (n = 0; n < len; n++) \
500       sox_pack ## size(data + n * size, buf[n]); \
501     nwritten = lsx_writebuf(ft, data, len * size); \
502     free(data); \
503     return nwritten / size; \
504   }
505 
506 WRITE_FUNC(b, 1, uint8_t, TWIDDLE_BYTE)
507 WRITE_FUNC(w, 2, uint16_t, TWIDDLE_WORD)
508 WRITE_FUNC_PACK(3, 3, sox_uint24_t, TWIDDLE_WORD)
509 WRITE_FUNC(dw, 4, uint32_t, TWIDDLE_WORD)
510 WRITE_FUNC(qw, 8, uint64_t, TWIDDLE_WORD)
WRITE_FUNC(f,sizeof (float),float,TWIDDLE_FLOAT)511 WRITE_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT)
512 WRITE_FUNC(df, sizeof(double), double, TWIDDLE_WORD)
513 
514 #define WRITE1U_FUNC(type, ctype) \
515   int lsx_write ## type(sox_format_t * ft, unsigned d) \
516   { ctype datum = (ctype)d; \
517     return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \
518   }
519 
520 #define WRITE1S_FUNC(type, ctype) \
521   int lsx_writes ## type(sox_format_t * ft, signed d) \
522   { ctype datum = (ctype)d; \
523     return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \
524   }
525 
526 #define WRITE1_FUNC(type, ctype) \
527   int lsx_write ## type(sox_format_t * ft, ctype datum) \
528   { \
529     return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \
530   }
531 
532 WRITE1U_FUNC(b, uint8_t)
533 WRITE1U_FUNC(w, uint16_t)
534 WRITE1U_FUNC(3, sox_uint24_t)
535 WRITE1U_FUNC(dw, uint32_t)
536 WRITE1_FUNC(qw, uint64_t)
537 WRITE1S_FUNC(b, uint8_t)
538 WRITE1S_FUNC(w, uint16_t)
539 WRITE1_FUNC(df, double)
540 
541 int lsx_writef(sox_format_t * ft, double datum)
542 {
543   float f = datum;
544   return lsx_write_f_buf(ft, &f, (size_t) 1) == 1 ? SOX_SUCCESS : SOX_EOF;
545 }
546