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