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