1/*
2 * Copyright (c) 2008-2014 Alexandre Ratchov <alex@caoua.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <fcntl.h>
18#include <string.h>
19#include <unistd.h>
20#include "afile.h"
21#include "utils.h"
22
23typedef struct {
24	unsigned char ld[4];
25} le32_t;
26
27typedef struct {
28	unsigned char lw[2];
29} le16_t;
30
31typedef struct {
32	unsigned char bd[4];
33} be32_t;
34
35typedef struct {
36	unsigned char bw[2];
37} be16_t;
38
39struct wav_riff {
40	char id[4];
41	le32_t size;
42	char type[4];
43};
44
45struct wav_chunk {
46	char id[4];
47	le32_t size;
48};
49
50struct wav_fmt {
51#define WAV_FMT_PCM	1
52#define WAV_FMT_FLOAT	3
53#define WAV_FMT_ALAW	6
54#define WAV_FMT_ULAW	7
55#define WAV_FMT_EXT	0xfffe
56	le16_t fmt;
57	le16_t nch;
58	le32_t rate;
59	le32_t byterate;
60	le16_t blkalign;
61	le16_t bits;
62#define WAV_FMT_SIZE		 16
63#define WAV_FMT_EXT_SIZE	(16 + 24)
64	le16_t extsize;
65	le16_t valbits;
66	le32_t chanmask;
67	le16_t extfmt;
68	char guid[14];
69};
70
71struct wav_hdr {
72	struct wav_riff riff;		/* 00..11 */
73	struct wav_chunk fmt_hdr;	/* 12..20 */
74	struct wav_fmt fmt;
75	struct wav_chunk data_hdr;
76};
77
78struct aiff_form {
79	char id[4];
80	be32_t size;
81	char type[4];
82};
83
84struct aiff_chunk {
85	char id[4];
86	be32_t size;
87};
88
89struct aiff_comm {
90	struct aiff_commbase {
91		be16_t nch;
92		be32_t nfr;
93		be16_t bits;
94		/* rate in 80-bit floating point */
95		be16_t rate_ex;
96		be32_t rate_hi;
97		be32_t rate_lo;
98	} base;
99	char comp_id[4];
100	/* followed by stuff we don't care about */
101};
102
103struct aiff_data {
104	be32_t offs;
105	be32_t blksz;
106};
107
108struct aiff_hdr {
109	struct aiff_form form;
110	struct aiff_chunk comm_hdr;
111	struct aiff_commbase comm;
112	struct aiff_chunk data_hdr;
113	struct aiff_data data;
114};
115
116struct au_hdr {
117	char id[4];
118	be32_t offs;
119	be32_t size;
120#define AU_FMT_PCM8	2
121#define AU_FMT_PCM16	3
122#define AU_FMT_PCM24	4
123#define AU_FMT_PCM32	5
124#define AU_FMT_FLOAT	6
125#define AU_FMT_ALAW	0x1b
126#define AU_FMT_ULAW	1
127	be32_t fmt;
128	be32_t rate;
129	be32_t nch;
130	char desc[8];
131	/* followed by optional desc[] continuation */
132};
133
134const char wav_id_riff[4] = {'R', 'I', 'F', 'F'};
135const char wav_id_wave[4] = {'W', 'A', 'V', 'E'};
136const char wav_id_data[4] = {'d', 'a', 't', 'a'};
137const char wav_id_fmt[4] = {'f', 'm', 't', ' '};
138const char wav_guid[14] = {
139	0x00, 0x00, 0x00, 0x00,
140	0x10, 0x00, 0x80, 0x00,
141	0x00, 0xAA, 0x00, 0x38,
142	0x9B, 0x71
143};
144
145const char aiff_id_form[4] = {'F', 'O', 'R', 'M'};
146const char aiff_id_aiff[4] = {'A', 'I', 'F', 'F'};
147const char aiff_id_aifc[4] = {'A', 'I', 'F', 'C'};
148const char aiff_id_data[4] = {'S', 'S', 'N', 'D'};
149const char aiff_id_comm[4] = {'C', 'O', 'M', 'M'};
150const char aiff_id_none[4] = {'N', 'O', 'N', 'E'};
151const char aiff_id_fl32[4] = {'f', 'l', '3', '2'};
152const char aiff_id_ulaw[4] = {'u', 'l', 'a', 'w'};
153const char aiff_id_alaw[4] = {'a', 'l', 'a', 'w'};
154
155const char au_id[4] = {'.', 's', 'n', 'd'};
156
157static inline unsigned int
158le16_get(le16_t *p)
159{
160	return p->lw[0] | p->lw[1] << 8;
161}
162
163static inline void
164le16_set(le16_t *p, unsigned int v)
165{
166	p->lw[0] = v;
167	p->lw[1] = v >> 8;
168}
169
170static inline unsigned int
171le32_get(le32_t *p)
172{
173	return p->ld[0] |
174	       p->ld[1] << 8 |
175	       p->ld[2] << 16 |
176	       p->ld[3] << 24;
177}
178
179static inline void
180le32_set(le32_t *p, unsigned int v)
181{
182	p->ld[0] = v;
183	p->ld[1] = v >> 8;
184	p->ld[2] = v >> 16;
185	p->ld[3] = v >> 24;
186}
187
188static inline unsigned int
189be16_get(be16_t *p)
190{
191	return p->bw[1] | p->bw[0] << 8;
192}
193
194static inline void
195be16_set(be16_t *p, unsigned int v)
196{
197	p->bw[1] = v;
198	p->bw[0] = v >> 8;
199}
200
201static inline unsigned int
202be32_get(be32_t *p)
203{
204	return p->bd[3] |
205	       p->bd[2] << 8 |
206	       p->bd[1] << 16 |
207	       p->bd[0] << 24;
208}
209
210static inline void
211be32_set(be32_t *p, unsigned int v)
212{
213	p->bd[3] = v;
214	p->bd[2] = v >> 8;
215	p->bd[1] = v >> 16;
216	p->bd[0] = v >> 24;
217}
218
219static int
220afile_readhdr(struct afile *f, void *addr, size_t size)
221{
222	if (lseek(f->fd, 0, SEEK_SET) == -1) {
223		log_puts(f->path);
224		log_puts(": failed to seek to beginning of file\n");
225		return 0;
226	}
227	if (read(f->fd, addr, size) != size) {
228		log_puts(f->path);
229		log_puts(": failed to read header\n");
230		return 0;
231	}
232	return 1;
233}
234
235static int
236afile_writehdr(struct afile *f, void *addr, size_t size)
237{
238	if (lseek(f->fd, 0, SEEK_SET) == -1) {
239		log_puts(f->path);
240		log_puts(": failed to seek back to header\n");
241		return 0;
242	}
243	if (write(f->fd, addr, size) != size) {
244		log_puts(f->path);
245		log_puts(": failed to write header\n");
246		return 0;
247	}
248	f->curpos = f->startpos;
249	return 1;
250}
251
252static int
253afile_checkpar(struct afile *f)
254{
255	if (f->nch == 0 || f->nch > NCHAN_MAX) {
256		log_puts(f->path);
257		log_puts(": ");
258		log_putu(f->nch);
259		log_puts(": unsupported number of channels\n");
260		return 0;
261	}
262	if (f->rate < RATE_MIN || f->rate > RATE_MAX) {
263		log_puts(f->path);
264		log_puts(": ");
265		log_putu(f->rate);
266		log_puts(": unsupported rate\n");
267		return 0;
268	}
269	if (f->par.bits < BITS_MIN || f->par.bits > BITS_MAX) {
270		log_puts(f->path);
271		log_puts(": ");
272		log_putu(f->par.bits);
273		log_puts(": unsupported bits per sample\n");
274		return 0;
275	}
276	if (f->par.bits > f->par.bps * 8) {
277		log_puts(f->path);
278		log_puts(": bits larger than bytes-per-sample\n");
279		return 0;
280	}
281	if (f->fmt == AFILE_FMT_FLOAT && f->par.bits != 32) {
282		log_puts(f->path);
283		log_puts(": only 32-bit floating points are supported\n");
284		return 0;
285	}
286	return 1;
287}
288
289static int
290afile_wav_readfmt(struct afile *f, unsigned int csize)
291{
292	struct wav_fmt fmt;
293	unsigned int wenc;
294
295	if (csize < WAV_FMT_SIZE) {
296		log_puts(f->path);
297		log_puts(": ");
298		log_putu(csize);
299		log_puts(": bogus format chunk size\n");
300		return 0;
301	}
302	if (csize > WAV_FMT_EXT_SIZE)
303		csize = WAV_FMT_EXT_SIZE;
304	if (read(f->fd, &fmt, csize) != csize) {
305		log_puts(f->path);
306		log_puts(": failed to read format chunk\n");
307		return 0;
308	}
309	wenc = le16_get(&fmt.fmt);
310	f->par.bits = le16_get(&fmt.bits);
311	if (wenc == WAV_FMT_EXT) {
312		if (csize != WAV_FMT_EXT_SIZE) {
313			log_puts(f->path);
314			log_puts(": missing extended format chunk\n");
315			return 0;
316		}
317		if (memcmp(fmt.guid, wav_guid, sizeof(wav_guid)) != 0) {
318			log_puts(f->path);
319			log_puts(": unknown format (GUID)\n");
320			return 0;
321		}
322		f->par.bps = (f->par.bits + 7) / 8;
323		f->par.bits = le16_get(&fmt.valbits);
324		wenc = le16_get(&fmt.extfmt);
325	} else
326		f->par.bps = (f->par.bits + 7) / 8;
327	f->nch = le16_get(&fmt.nch);
328	f->rate = le32_get(&fmt.rate);
329	f->par.le = 1;
330	f->par.msb = 1;
331	switch (wenc) {
332	case WAV_FMT_PCM:
333		f->fmt = AFILE_FMT_PCM;
334		f->par.sig = (f->par.bits <= 8) ? 0 : 1;
335		break;
336	case WAV_FMT_ALAW:
337		f->fmt = AFILE_FMT_ALAW;
338		f->par.bits = 8;
339		f->par.bps = 1;
340		break;
341	case WAV_FMT_ULAW:
342		f->fmt = AFILE_FMT_ULAW;
343		f->par.bits = 8;
344		f->par.bps = 1;
345		break;
346	case WAV_FMT_FLOAT:
347		f->fmt = AFILE_FMT_FLOAT;
348		break;
349	default:
350		log_putu(wenc);
351		log_puts(": unsupported encoding\n");
352		return 0;
353	}
354	return afile_checkpar(f);
355}
356
357static int
358afile_wav_readhdr(struct afile *f)
359{
360	struct wav_riff riff;
361	struct wav_chunk chunk;
362	unsigned int csize, rsize, pos = 0;
363	int fmt_done = 0;
364
365	if (!afile_readhdr(f, &riff, sizeof(struct wav_riff)))
366		return 0;
367	if (memcmp(&riff.id, &wav_id_riff, 4) != 0 ||
368	    memcmp(&riff.type, &wav_id_wave, 4) != 0) {
369		log_puts(f->path);
370		log_puts(": not a .wav file\n");
371		return 0;
372	}
373	rsize = le32_get(&riff.size);
374	for (;;) {
375		if (pos + sizeof(struct wav_chunk) > rsize) {
376			log_puts(f->path);
377			log_puts(": missing data chunk\n");
378			return 0;
379		}
380		if (read(f->fd, &chunk, sizeof(chunk)) != sizeof(chunk)) {
381			log_puts(f->path);
382			log_puts(": failed to read chunk header\n");
383			return 0;
384		}
385		csize = le32_get(&chunk.size);
386		if (memcmp(chunk.id, wav_id_fmt, 4) == 0) {
387			if (!afile_wav_readfmt(f, csize))
388				return 0;
389			fmt_done = 1;
390		} else if (memcmp(chunk.id, wav_id_data, 4) == 0) {
391			f->startpos = pos + sizeof(riff) + sizeof(chunk);
392			f->endpos = f->startpos + csize;
393			break;
394		} else {
395#ifdef DEBUG
396			if (log_level >= 2) {
397				log_puts(f->path);
398				log_puts(": skipped unknown chunk\n");
399			}
400#endif
401		}
402
403		/*
404		 * next chunk
405		 */
406		pos += sizeof(struct wav_chunk) + csize;
407		if (lseek(f->fd, sizeof(riff) + pos, SEEK_SET) == -1) {
408			log_puts(f->path);
409			log_puts(": failed to seek to chunk\n");
410			return 0;
411		}
412	}
413	if (!fmt_done) {
414		log_puts(f->path);
415		log_puts(": missing format chunk\n");
416		return 0;
417	}
418	return 1;
419}
420
421/*
422 * Write header and seek to start position
423 */
424static int
425afile_wav_writehdr(struct afile *f)
426{
427	struct wav_hdr hdr;
428
429	memset(&hdr, 0, sizeof(struct wav_hdr));
430	memcpy(hdr.riff.id, wav_id_riff, 4);
431	memcpy(hdr.riff.type, wav_id_wave, 4);
432	le32_set(&hdr.riff.size, f->endpos - sizeof(hdr.riff));
433	memcpy(hdr.fmt_hdr.id, wav_id_fmt, 4);
434	le32_set(&hdr.fmt_hdr.size, sizeof(hdr.fmt));
435	le16_set(&hdr.fmt.fmt, WAV_FMT_EXT);
436	le16_set(&hdr.fmt.nch, f->nch);
437	le32_set(&hdr.fmt.rate, f->rate);
438	le32_set(&hdr.fmt.byterate, f->rate * f->par.bps * f->nch);
439	le16_set(&hdr.fmt.blkalign, f->par.bps * f->nch);
440	le16_set(&hdr.fmt.bits, f->par.bits);
441	le16_set(&hdr.fmt.extsize,
442	    WAV_FMT_EXT_SIZE - WAV_FMT_SIZE - sizeof(hdr.fmt.extsize));
443	le16_set(&hdr.fmt.valbits, f->par.bits);
444	le16_set(&hdr.fmt.extfmt, 1);
445	memcpy(&hdr.fmt.guid, wav_guid, sizeof(hdr.fmt.guid));
446	memcpy(hdr.data_hdr.id, wav_id_data, 4);
447	le32_set(&hdr.data_hdr.size, f->endpos - f->startpos);
448	return afile_writehdr(f, &hdr, sizeof(struct wav_hdr));
449}
450
451static int
452afile_aiff_readcomm(struct afile *f, unsigned int csize,
453    int comp, unsigned int *nfr)
454{
455	struct aiff_comm comm;
456	unsigned int csize_min;
457	unsigned int e, m;
458
459	csize_min = comp ?
460	    sizeof(struct aiff_comm) : sizeof(struct aiff_commbase);
461	if (csize < csize_min) {
462		log_puts(f->path);
463		log_puts(": ");
464		log_putu(csize);
465		log_puts(": bogus comm chunk size\n");
466		return 0;
467	}
468	if (read(f->fd, &comm, csize_min) != csize_min) {
469		log_puts(f->path);
470		log_puts(": failed to read comm chunk\n");
471		return 0;
472	}
473	f->nch = be16_get(&comm.base.nch);
474	e = be16_get(&comm.base.rate_ex);
475	m = be32_get(&comm.base.rate_hi);
476	if (e < 0x3fff || e > 0x3fff + 31) {
477		log_puts(f->path);
478		log_puts(": malformed sample rate\n");
479		return 0;
480	}
481	f->rate = m >> (0x3fff + 31 - e);
482	if (comp) {
483		if (memcmp(comm.comp_id, aiff_id_none, 4) == 0) {
484			f->fmt = AFILE_FMT_PCM;
485			f->par.bits = be16_get(&comm.base.bits);
486		} else if (memcmp(comm.comp_id, aiff_id_fl32, 4) == 0) {
487			f->fmt = AFILE_FMT_FLOAT;
488			f->par.bits = 32;
489		} else if (memcmp(comm.comp_id, aiff_id_ulaw, 4) == 0) {
490			f->fmt = AFILE_FMT_ULAW;
491			f->par.bits = 8;
492		} else if (memcmp(comm.comp_id, aiff_id_alaw, 4) == 0) {
493			f->fmt = AFILE_FMT_ALAW;
494			f->par.bits = 8;
495		} else {
496			log_puts(f->path);
497			log_puts(": unsupported encoding\n");
498			return 0;
499		}
500	} else {
501		f->fmt = AFILE_FMT_PCM;
502		f->par.bits = be16_get(&comm.base.bits);
503	}
504	f->par.le = 0;
505	f->par.sig = 1;
506	f->par.msb = 1;
507	f->par.bps = (f->par.bits + 7) / 8;
508	*nfr = be32_get(&comm.base.nfr);
509	return afile_checkpar(f);
510}
511
512static int
513afile_aiff_readdata(struct afile *f, unsigned int csize, unsigned int *roffs)
514{
515	struct aiff_data data;
516
517	if (csize < sizeof(struct aiff_data)) {
518		log_puts(f->path);
519		log_puts(": ");
520		log_putu(csize);
521		log_puts(": bogus data chunk size\n");
522		return 0;
523	}
524	csize = sizeof(struct aiff_data);
525	if (read(f->fd, &data, csize) != csize) {
526		log_puts(f->path);
527		log_puts(": failed to read data chunk\n");
528		return 0;
529	}
530	*roffs = csize + be32_get(&data.offs);
531	return 1;
532}
533
534static int
535afile_aiff_readhdr(struct afile *f)
536{
537	struct aiff_form form;
538	struct aiff_chunk chunk;
539	unsigned int csize, rsize, nfr = 0, pos = 0, offs;
540	int comm_done = 0, comp;
541
542	if (!afile_readhdr(f, &form, sizeof(struct aiff_form)))
543		return 0;
544	if (memcmp(&form.id, &aiff_id_form, 4) != 0) {
545		log_puts(f->path);
546		log_puts(": not an aiff file\n");
547		return 0;
548	}
549	if (memcmp(&form.type, &aiff_id_aiff, 4) == 0) {
550		comp = 0;
551	} else if (memcmp(&form.type, &aiff_id_aifc, 4) == 0)
552		comp = 1;
553	else {
554		log_puts(f->path);
555		log_puts(": unsupported aiff file sub-type\n");
556		return 0;
557	}
558	rsize = be32_get(&form.size);
559	for (;;) {
560		if (pos + sizeof(struct aiff_chunk) > rsize) {
561			log_puts(f->path);
562			log_puts(": missing data chunk\n");
563			return 0;
564		}
565		if (read(f->fd, &chunk, sizeof(chunk)) != sizeof(chunk)) {
566			log_puts(f->path);
567			log_puts(": failed to read chunk header\n");
568			return 0;
569		}
570		csize = be32_get(&chunk.size);
571		if (memcmp(chunk.id, aiff_id_comm, 4) == 0) {
572			if (!afile_aiff_readcomm(f, csize, comp, &nfr))
573				return 0;
574			comm_done = 1;
575		} else if (memcmp(chunk.id, aiff_id_data, 4) == 0) {
576			if (!afile_aiff_readdata(f, csize, &offs))
577				return 0;
578			f->startpos = sizeof(form) + pos +
579			    sizeof(chunk) + offs;
580			break;
581		} else {
582#ifdef DEBUG
583			if (log_level >= 2) {
584				log_puts(f->path);
585				log_puts(": skipped unknown chunk\n");
586			}
587#endif
588		}
589
590		/*
591		 * The aiff spec says "Each Chunk must contain an even
592		 * number of bytes. For those Chunks whose total
593		 * contents would yield an odd number of bytes, a zero
594		 * pad byte must be added at the end of the Chunk. This
595		 * pad byte is not included in ckDataSize, which
596		 * indicates the size of the data in the Chunk."
597		 */
598		csize = (csize + 1) & ~1;
599		pos += sizeof(struct aiff_chunk) + csize;
600
601		if (lseek(f->fd, sizeof(form) + pos, SEEK_SET) == -1) {
602			log_puts(f->path);
603			log_puts(": failed to seek to chunk\n");
604			return 0;
605		}
606	}
607	if (!comm_done) {
608		log_puts(f->path);
609		log_puts(": missing comm chunk\n");
610		return 0;
611	}
612	f->endpos = f->startpos + f->par.bps * f->nch * nfr;
613	return 1;
614}
615
616/*
617 * Write header and seek to start position
618 */
619static int
620afile_aiff_writehdr(struct afile *f)
621{
622	struct aiff_hdr hdr;
623	unsigned int bpf;
624	unsigned int e, m;
625
626	/* convert rate to 80-bit float (exponent and fraction part) */
627	m = f->rate;
628	e = 0x3fff + 31;
629	while ((m & 0x80000000) == 0) {
630		e--;
631		m <<= 1;
632	}
633
634	/* bytes per frame */
635	bpf = f->nch * f->par.bps;
636
637	memset(&hdr, 0, sizeof(struct aiff_hdr));
638	memcpy(hdr.form.id, aiff_id_form, 4);
639	memcpy(hdr.form.type, aiff_id_aiff, 4);
640	be32_set(&hdr.form.size, f->endpos - sizeof(hdr.form));
641
642	memcpy(hdr.comm_hdr.id, aiff_id_comm, 4);
643	be32_set(&hdr.comm_hdr.size, sizeof(hdr.comm));
644	be16_set(&hdr.comm.nch, f->nch);
645	be16_set(&hdr.comm.bits, f->par.bits);
646	be16_set(&hdr.comm.rate_ex, e);
647	be32_set(&hdr.comm.rate_hi, m);
648	be32_set(&hdr.comm.rate_lo, 0);
649	be32_set(&hdr.comm.nfr, (f->endpos - f->startpos) / bpf);
650
651	memcpy(hdr.data_hdr.id, aiff_id_data, 4);
652	be32_set(&hdr.data_hdr.size, f->endpos - f->startpos);
653	be32_set(&hdr.data.offs, 0);
654	be32_set(&hdr.data.blksz, 0);
655	return afile_writehdr(f, &hdr, sizeof(struct aiff_hdr));
656}
657
658static int
659afile_au_readhdr(struct afile *f)
660{
661	struct au_hdr hdr;
662	unsigned int fmt;
663
664	if (!afile_readhdr(f, &hdr, sizeof(struct au_hdr)))
665		return 0;
666	if (memcmp(&hdr.id, &au_id, 4) != 0) {
667		log_puts(f->path);
668		log_puts(": not a .au file\n");
669		return 0;
670	}
671	f->startpos = be32_get(&hdr.offs);
672	f->endpos = f->startpos + be32_get(&hdr.size);
673	fmt = be32_get(&hdr.fmt);
674	switch (fmt) {
675	case AU_FMT_PCM8:
676		f->fmt = AFILE_FMT_PCM;
677		f->par.bits = 8;
678		break;
679	case AU_FMT_PCM16:
680		f->fmt = AFILE_FMT_PCM;
681		f->par.bits = 16;
682		break;
683	case AU_FMT_PCM24:
684		f->fmt = AFILE_FMT_PCM;
685		f->par.bits = 24;
686		break;
687	case AU_FMT_PCM32:
688		f->fmt = AFILE_FMT_PCM;
689		f->par.bits = 32;
690		break;
691	case AU_FMT_ULAW:
692		f->fmt = AFILE_FMT_ULAW;
693		f->par.bits = 8;
694		break;
695	case AU_FMT_ALAW:
696		f->fmt = AFILE_FMT_ALAW;
697		f->par.bits = 8;
698		break;
699	case AU_FMT_FLOAT:
700		f->fmt = AFILE_FMT_FLOAT;
701		f->par.bits = 32;
702		break;
703	default:
704		log_puts(f->path);
705		log_puts(": ");
706		log_putu(fmt);
707		log_puts(": unsupported encoding\n");
708		return 0;
709	}
710	f->par.le = 0;
711	f->par.sig = 1;
712	f->par.bps = f->par.bits / 8;
713	f->par.msb = 0;
714	f->rate = be32_get(&hdr.rate);
715	f->nch = be32_get(&hdr.nch);
716	if (lseek(f->fd, f->startpos, SEEK_SET) == -1) {
717		log_puts(f->path);
718		log_puts(": ");
719		log_puts("failed to seek to data chunk\n");
720		return 0;
721	}
722	return afile_checkpar(f);
723}
724
725/*
726 * Write header and seek to start position
727 */
728static int
729afile_au_writehdr(struct afile *f)
730{
731	struct au_hdr hdr;
732	unsigned int fmt;
733
734	memset(&hdr, 0, sizeof(struct au_hdr));
735	memcpy(hdr.id, au_id, 4);
736	be32_set(&hdr.offs, f->startpos);
737	be32_set(&hdr.size, f->endpos - f->startpos);
738	switch (f->par.bits) {
739	case 8:
740		fmt = AU_FMT_PCM8;
741		break;
742	case 16:
743		fmt = AU_FMT_PCM16;
744		break;
745	case 24:
746		fmt = AU_FMT_PCM24;
747		break;
748	case 32:
749		fmt = AU_FMT_PCM32;
750		break;
751#ifdef DEBUG
752	default:
753		log_puts(f->path);
754		log_puts(": wrong precision\n");
755		panic();
756		return 0;
757#endif
758	}
759	be32_set(&hdr.fmt, fmt);
760	be32_set(&hdr.rate, f->rate);
761	be32_set(&hdr.nch, f->nch);
762	return afile_writehdr(f, &hdr, sizeof(struct au_hdr));
763}
764
765size_t
766afile_read(struct afile *f, void *data, size_t count)
767{
768	off_t maxread;
769	ssize_t n;
770
771	if (f->endpos >= 0) {
772		maxread = f->endpos - f->curpos;
773		if (maxread == 0) {
774#ifdef DEBUG
775			if (log_level >= 3) {
776				log_puts(f->path);
777				log_puts(": end reached\n");
778			}
779#endif
780			return 0;
781		}
782		if (count > maxread)
783			count = maxread;
784	}
785	n = read(f->fd, data, count);
786	if (n == -1) {
787		log_puts(f->path);
788		log_puts(": couldn't read\n");
789		return 0;
790	}
791	f->curpos += n;
792	return n;
793}
794
795size_t
796afile_write(struct afile *f, void *data, size_t count)
797{
798	off_t maxwrite;
799	int n;
800
801	if (f->maxpos >= 0) {
802		maxwrite = f->maxpos - f->curpos;
803		if (maxwrite == 0) {
804#ifdef DEBUG
805			if (log_level >= 3) {
806				log_puts(f->path);
807				log_puts(": max file size reached\n");
808			}
809#endif
810			return 0;
811		}
812		if (count > maxwrite)
813			count = maxwrite;
814	}
815	n = write(f->fd, data, count);
816	if (n == -1) {
817		log_puts(f->path);
818		log_puts(": couldn't write\n");
819		return 0;
820	}
821	f->curpos += n;
822	if (f->endpos < f->curpos)
823		f->endpos = f->curpos;
824	return n;
825}
826
827int
828afile_seek(struct afile *f, off_t pos)
829{
830	pos += f->startpos;
831	if (f->endpos >= 0 && pos > f->endpos && !f->par.sig) {
832		log_puts(f->path);
833		log_puts(": attempt to seek outside file boundaries\n");
834		return 0;
835	}
836
837	/*
838	 * seek only if needed to avoid errors with pipes & sockets
839	 */
840	if (pos != f->curpos) {
841		if (lseek(f->fd, pos, SEEK_SET) == -1) {
842			log_puts(f->path);
843			log_puts(": couldn't seek\n");
844			return 0;
845		}
846		f->curpos = pos;
847	}
848	return 1;
849}
850
851void
852afile_close(struct afile *f)
853{
854	if (f->flags & AFILE_FWRITE) {
855		if (f->hdr == AFILE_HDR_WAV)
856			afile_wav_writehdr(f);
857		else if (f->hdr == AFILE_HDR_AIFF)
858			afile_aiff_writehdr(f);
859		else if (f->hdr == AFILE_HDR_AU)
860			afile_au_writehdr(f);
861	}
862	close(f->fd);
863}
864
865int
866afile_open(struct afile *f, char *path, int hdr, int flags,
867    struct aparams *par, int rate, int nch)
868{
869	char *ext;
870	static union {
871		struct wav_hdr wav;
872		struct aiff_hdr aiff;
873		struct au_hdr au;
874	} dummy;
875
876	f->par = *par;
877	f->rate = rate;
878	f->nch = nch;
879	f->flags = flags;
880	f->hdr = hdr;
881	if (hdr == AFILE_HDR_AUTO) {
882		f->hdr = AFILE_HDR_RAW;
883		ext = strrchr(path, '.');
884		if (ext != NULL) {
885			ext++;
886			if (strcasecmp(ext, "aif") == 0 ||
887			    strcasecmp(ext, "aiff") == 0 ||
888			    strcasecmp(ext, "aifc") == 0)
889				f->hdr = AFILE_HDR_AIFF;
890			else if (strcasecmp(ext, "au") == 0 ||
891			    strcasecmp(ext, "snd") == 0)
892				f->hdr = AFILE_HDR_AU;
893			else if (strcasecmp(ext, "wav") == 0)
894				f->hdr = AFILE_HDR_WAV;
895		}
896	}
897	if (f->flags == AFILE_FREAD) {
898		if (strcmp(path, "-") == 0) {
899			f->path = "stdin";
900			f->fd = STDIN_FILENO;
901		} else {
902			f->path = path;
903			f->fd = open(f->path, O_RDONLY);
904			if (f->fd == -1) {
905				log_puts(f->path);
906				log_puts(": failed to open for reading\n");
907				return 0;
908			}
909		}
910		if (f->hdr == AFILE_HDR_WAV) {
911			if (!afile_wav_readhdr(f))
912				goto bad_close;
913		} else if (f->hdr == AFILE_HDR_AIFF) {
914			if (!afile_aiff_readhdr(f))
915				goto bad_close;
916		} else if (f->hdr == AFILE_HDR_AU) {
917			if (!afile_au_readhdr(f))
918				goto bad_close;
919		} else {
920			f->startpos = 0;
921			f->endpos = -1; /* read until EOF */
922			f->fmt = AFILE_FMT_PCM;
923		}
924		f->curpos = f->startpos;
925	} else if (flags == AFILE_FWRITE) {
926		if (strcmp(path, "-") == 0) {
927			f->path = "stdout";
928			f->fd = STDOUT_FILENO;
929		} else {
930			f->path = path;
931			f->fd = open(f->path,
932			    O_WRONLY | O_TRUNC | O_CREAT, 0666);
933			if (f->fd == -1) {
934				log_puts(f->path);
935				log_puts(": failed to create file\n");
936				return 0;
937			}
938		}
939		if (f->hdr == AFILE_HDR_WAV) {
940			f->par.bps = (f->par.bits + 7) >> 3;
941			if (f->par.bits > 8) {
942				f->par.le = 1;
943				f->par.sig = 1;
944			} else
945				f->par.sig = 0;
946			if (f->par.bits & 7)
947				f->par.msb = 1;
948			f->endpos = f->startpos = sizeof(struct wav_hdr);
949			f->maxpos = 0x7fffffff;
950			if (!afile_writehdr(f, &dummy, sizeof(struct wav_hdr)))
951				goto bad_close;
952		} else if (f->hdr == AFILE_HDR_AIFF) {
953			f->par.bps = (f->par.bits + 7) >> 3;
954			if (f->par.bps > 1)
955				f->par.le = 0;
956			f->par.sig = 1;
957			if (f->par.bits & 7)
958				f->par.msb = 1;
959			f->endpos = f->startpos = sizeof(struct aiff_hdr);
960			f->maxpos = 0x7fffffff;
961			if (!afile_writehdr(f, &dummy,
962				sizeof(struct aiff_hdr)))
963				goto bad_close;
964		} else if (f->hdr == AFILE_HDR_AU) {
965			f->par.bits = (f->par.bits + 7) & ~7;
966			f->par.bps = f->par.bits / 8;
967			f->par.le = 0;
968			f->par.sig = 1;
969			f->par.msb = 1;
970			f->endpos = f->startpos = sizeof(struct au_hdr);
971			f->maxpos = 0x7fffffff;
972			if (!afile_writehdr(f, &dummy, sizeof(struct au_hdr)))
973				goto bad_close;
974		} else {
975			f->endpos = f->startpos = 0;
976			f->maxpos = -1;
977		}
978		f->curpos = f->startpos;
979	} else {
980#ifdef DEBUG
981		log_puts("afile_open: wrong flags\n");
982		panic();
983#endif
984	}
985	return 1;
986bad_close:
987	close(f->fd);
988	return 0;
989}
990