xref: /openbsd/usr.bin/aucat/afile.c (revision 09467b48)
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 
23 typedef struct {
24 	unsigned char ld[4];
25 } le32_t;
26 
27 typedef struct {
28 	unsigned char lw[2];
29 } le16_t;
30 
31 typedef struct {
32 	unsigned char bd[4];
33 } be32_t;
34 
35 typedef struct {
36 	unsigned char bw[2];
37 } be16_t;
38 
39 struct wav_riff {
40 	char id[4];
41 	le32_t size;
42 	char type[4];
43 };
44 
45 struct wav_chunk {
46 	char id[4];
47 	le32_t size;
48 };
49 
50 struct 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 
71 struct 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 
78 struct aiff_form {
79 	char id[4];
80 	be32_t size;
81 	char type[4];
82 };
83 
84 struct aiff_chunk {
85 	char id[4];
86 	be32_t size;
87 };
88 
89 struct 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 
103 struct aiff_data {
104 	be32_t offs;
105 	be32_t blksz;
106 };
107 
108 struct 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 
116 struct 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 
134 char wav_id_riff[4] = {'R', 'I', 'F', 'F'};
135 char wav_id_wave[4] = {'W', 'A', 'V', 'E'};
136 char wav_id_data[4] = {'d', 'a', 't', 'a'};
137 char wav_id_fmt[4] = {'f', 'm', 't', ' '};
138 char wav_guid[14] = {
139 	0x00, 0x00, 0x00, 0x00,
140 	0x10, 0x00, 0x80, 0x00,
141 	0x00, 0xAA, 0x00, 0x38,
142 	0x9B, 0x71
143 };
144 
145 char aiff_id_form[4] = {'F', 'O', 'R', 'M'};
146 char aiff_id_aiff[4] = {'A', 'I', 'F', 'F'};
147 char aiff_id_aifc[4] = {'A', 'I', 'F', 'C'};
148 char aiff_id_data[4] = {'S', 'S', 'N', 'D'};
149 char aiff_id_comm[4] = {'C', 'O', 'M', 'M'};
150 char aiff_id_none[4] = {'N', 'O', 'N', 'E'};
151 char aiff_id_fl32[4] = {'f', 'l', '3', '2'};
152 char aiff_id_ulaw[4] = {'u', 'l', 'a', 'w'};
153 char aiff_id_alaw[4] = {'a', 'l', 'a', 'w'};
154 
155 char au_id[4] = {'.', 's', 'n', 'd'};
156 
157 static inline unsigned int
158 le16_get(le16_t *p)
159 {
160 	return p->lw[0] | p->lw[1] << 8;
161 }
162 
163 static inline void
164 le16_set(le16_t *p, unsigned int v)
165 {
166 	p->lw[0] = v;
167 	p->lw[1] = v >> 8;
168 }
169 
170 static inline unsigned int
171 le32_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 
179 static inline void
180 le32_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 
188 static inline unsigned int
189 be16_get(be16_t *p)
190 {
191 	return p->bw[1] | p->bw[0] << 8;
192 }
193 
194 static inline void
195 be16_set(be16_t *p, unsigned int v)
196 {
197 	p->bw[1] = v;
198 	p->bw[0] = v >> 8;
199 }
200 
201 static inline unsigned int
202 be32_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 
210 static inline void
211 be32_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 
219 static int
220 afile_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 
235 static int
236 afile_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 
252 static int
253 afile_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 
289 static int
290 afile_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 
357 static int
358 afile_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)) {
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(": filed 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  */
424 static int
425 afile_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, 1);
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 	memcpy(hdr.data_hdr.id, wav_id_data, 4);
442 	le32_set(&hdr.data_hdr.size, f->endpos - f->startpos);
443 	return afile_writehdr(f, &hdr, sizeof(struct wav_hdr));
444 }
445 
446 static int
447 afile_aiff_readcomm(struct afile *f, unsigned int csize,
448     int comp, unsigned int *nfr)
449 {
450 	struct aiff_comm comm;
451 	unsigned int csize_min;
452 	unsigned int e, m;
453 
454 	csize_min = comp ?
455 	    sizeof(struct aiff_comm) : sizeof(struct aiff_commbase);
456 	if (csize < csize_min) {
457 		log_puts(f->path);
458 		log_puts(": ");
459 		log_putu(csize);
460 		log_puts(": bogus comm chunk size\n");
461 		return 0;
462 	}
463 	if (read(f->fd, &comm, csize_min) != csize_min) {
464 		log_puts(f->path);
465 		log_puts(": failed to read comm chunk\n");
466 		return 0;
467 	}
468 	f->nch = be16_get(&comm.base.nch);
469 	e = be16_get(&comm.base.rate_ex);
470 	m = be32_get(&comm.base.rate_hi);
471 	if (e < 0x3fff || e > 0x3fff + 31) {
472 		log_puts(f->path);
473 		log_puts(": malformed sample rate\n");
474 		return 0;
475 	}
476 	f->rate = m >> (0x3fff + 31 - e);
477 	if (comp) {
478 		if (memcmp(comm.comp_id, aiff_id_none, 4) == 0) {
479 			f->fmt = AFILE_FMT_PCM;
480 			f->par.bits = be16_get(&comm.base.bits);
481 		} else if (memcmp(comm.comp_id, aiff_id_fl32, 4) == 0) {
482 			f->fmt = AFILE_FMT_FLOAT;
483 			f->par.bits = 32;
484 		} else if (memcmp(comm.comp_id, aiff_id_ulaw, 4) == 0) {
485 			f->fmt = AFILE_FMT_ULAW;
486 			f->par.bits = 8;
487 		} else if (memcmp(comm.comp_id, aiff_id_alaw, 4) == 0) {
488 			f->fmt = AFILE_FMT_ALAW;
489 			f->par.bits = 8;
490 		} else {
491 			log_puts(f->path);
492 			log_puts(": unsupported encoding\n");
493 			return 0;
494 		}
495 	} else {
496 		f->fmt = AFILE_FMT_PCM;
497 		f->par.bits = be16_get(&comm.base.bits);
498 	}
499 	f->par.le = 0;
500 	f->par.sig = 1;
501 	f->par.msb = 1;
502 	f->par.bps = (f->par.bits + 7) / 8;
503 	*nfr = be32_get(&comm.base.nfr);
504 	return afile_checkpar(f);
505 }
506 
507 static int
508 afile_aiff_readdata(struct afile *f, unsigned int csize, unsigned int *roffs)
509 {
510 	struct aiff_data data;
511 
512 	if (csize < sizeof(struct aiff_data)) {
513 		log_puts(f->path);
514 		log_puts(": ");
515 		log_putu(csize);
516 		log_puts(": bogus data chunk size\n");
517 		return 0;
518 	}
519 	csize = sizeof(struct aiff_data);
520 	if (read(f->fd, &data, csize) != csize) {
521 		log_puts(f->path);
522 		log_puts(": failed to read data chunk\n");
523 		return 0;
524 	}
525 	*roffs = csize + be32_get(&data.offs);
526 	return 1;
527 }
528 
529 static int
530 afile_aiff_readhdr(struct afile *f)
531 {
532 	struct aiff_form form;
533 	struct aiff_chunk chunk;
534 	unsigned int csize, rsize, nfr = 0, pos = 0, offs;
535 	int comm_done = 0, comp;
536 
537 	if (!afile_readhdr(f, &form, sizeof(struct aiff_form)))
538 		return 0;
539 	if (memcmp(&form.id, &aiff_id_form, 4) != 0) {
540 		log_puts(f->path);
541 		log_puts(": not an aiff file\n");
542 		return 0;
543 	}
544 	if (memcmp(&form.type, &aiff_id_aiff, 4) == 0) {
545 		comp = 0;
546 	} else if (memcmp(&form.type, &aiff_id_aifc, 4) == 0)
547 		comp = 1;
548 	else {
549 		log_puts(f->path);
550 		log_puts(": unsupported aiff file sub-type\n");
551 		return 0;
552 	}
553 	rsize = be32_get(&form.size);
554 	for (;;) {
555 		if (pos + sizeof(struct aiff_chunk) > rsize) {
556 			log_puts(f->path);
557 			log_puts(": missing data chunk\n");
558 			return 0;
559 		}
560 		if (read(f->fd, &chunk, sizeof(chunk)) != sizeof(chunk)) {
561 			log_puts(f->path);
562 			log_puts(": failed to read chunk header\n");
563 			return 0;
564 		}
565 		csize = be32_get(&chunk.size);
566 		if (memcmp(chunk.id, aiff_id_comm, 4) == 0) {
567 			if (!afile_aiff_readcomm(f, csize, comp, &nfr))
568 				return 0;
569 			comm_done = 1;
570 		} else if (memcmp(chunk.id, aiff_id_data, 4) == 0) {
571 			if (!afile_aiff_readdata(f, csize, &offs))
572 				return 0;
573 			f->startpos = sizeof(form) + pos +
574 			    sizeof(chunk) + offs;
575 			break;
576 		} else {
577 #ifdef DEBUG
578 			if (log_level >= 2) {
579 				log_puts(f->path);
580 				log_puts(": skipped unknown chunk\n");
581 			}
582 #endif
583 		}
584 
585 		/*
586 		 * The aiff spec says "Each Chunk must contain an even
587 		 * number of bytes. For those Chunks whose total
588 		 * contents would yield an odd number of bytes, a zero
589 		 * pad byte must be added at the end of the Chunk. This
590 		 * pad byte is not included in ckDataSize, which
591 		 * indicates the size of the data in the Chunk."
592 		 */
593 		csize = (csize + 1) & ~1;
594 		pos += sizeof(struct aiff_chunk) + csize;
595 
596 		if (lseek(f->fd, sizeof(form) + pos, SEEK_SET) == -1) {
597 			log_puts(f->path);
598 			log_puts(": filed to seek to chunk\n");
599 			return 0;
600 		}
601 	}
602 	if (!comm_done) {
603 		log_puts(f->path);
604 		log_puts(": missing comm chunk\n");
605 		return 0;
606 	}
607 	f->endpos = f->startpos + f->par.bps * f->nch * nfr;
608 	return 1;
609 }
610 
611 /*
612  * Write header and seek to start position
613  */
614 static int
615 afile_aiff_writehdr(struct afile *f)
616 {
617 	struct aiff_hdr hdr;
618 	unsigned int bpf;
619 	unsigned int e, m;
620 
621 	/* convert rate to 80-bit float (exponent and fraction part) */
622 	m = f->rate;
623 	e = 0x3fff + 31;
624 	while ((m & 0x80000000) == 0) {
625 		e--;
626 		m <<= 1;
627 	}
628 
629 	/* bytes per frame */
630 	bpf = f->nch * f->par.bps;
631 
632 	memset(&hdr, 0, sizeof(struct aiff_hdr));
633 	memcpy(hdr.form.id, aiff_id_form, 4);
634 	memcpy(hdr.form.type, aiff_id_aiff, 4);
635 	be32_set(&hdr.form.size, f->endpos - sizeof(hdr.form));
636 
637 	memcpy(hdr.comm_hdr.id, aiff_id_comm, 4);
638 	be32_set(&hdr.comm_hdr.size, sizeof(hdr.comm));
639 	be16_set(&hdr.comm.nch, f->nch);
640 	be16_set(&hdr.comm.bits, f->par.bits);
641 	be16_set(&hdr.comm.rate_ex, e);
642 	be32_set(&hdr.comm.rate_hi, m);
643 	be32_set(&hdr.comm.rate_lo, 0);
644 	be32_set(&hdr.comm.nfr, (f->endpos - f->startpos) / bpf);
645 
646 	memcpy(hdr.data_hdr.id, aiff_id_data, 4);
647 	be32_set(&hdr.data_hdr.size, f->endpos - f->startpos);
648 	be32_set(&hdr.data.offs, 0);
649 	be32_set(&hdr.data.blksz, 0);
650 	return afile_writehdr(f, &hdr, sizeof(struct aiff_hdr));
651 }
652 
653 static int
654 afile_au_readhdr(struct afile *f)
655 {
656 	struct au_hdr hdr;
657 	unsigned int fmt;
658 
659 	if (!afile_readhdr(f, &hdr, sizeof(struct au_hdr)))
660 		return 0;
661 	if (memcmp(&hdr.id, &au_id, 4) != 0) {
662 		log_puts(f->path);
663 		log_puts(": not a .au file\n");
664 		return 0;
665 	}
666 	f->startpos = be32_get(&hdr.offs);
667 	f->endpos = f->startpos + be32_get(&hdr.size);
668 	fmt = be32_get(&hdr.fmt);
669 	switch (fmt) {
670 	case AU_FMT_PCM8:
671 		f->fmt = AFILE_FMT_PCM;
672 		f->par.bits = 8;
673 		break;
674 	case AU_FMT_PCM16:
675 		f->fmt = AFILE_FMT_PCM;
676 		f->par.bits = 16;
677 		break;
678 	case AU_FMT_PCM24:
679 		f->fmt = AFILE_FMT_PCM;
680 		f->par.bits = 24;
681 		break;
682 	case AU_FMT_PCM32:
683 		f->fmt = AFILE_FMT_PCM;
684 		f->par.bits = 32;
685 		break;
686 	case AU_FMT_ULAW:
687 		f->fmt = AFILE_FMT_ULAW;
688 		f->par.bits = 8;
689 		break;
690 	case AU_FMT_ALAW:
691 		f->fmt = AFILE_FMT_ALAW;
692 		f->par.bits = 8;
693 		break;
694 	case AU_FMT_FLOAT:
695 		f->fmt = AFILE_FMT_FLOAT;
696 		f->par.bits = 32;
697 		break;
698 	default:
699 		log_puts(f->path);
700 		log_puts(": ");
701 		log_putu(fmt);
702 		log_puts(": unsupported encoding\n");
703 		return 0;
704 	}
705 	f->par.le = 0;
706 	f->par.sig = 1;
707 	f->par.bps = f->par.bits / 8;
708 	f->par.msb = 0;
709 	f->rate = be32_get(&hdr.rate);
710 	f->nch = be32_get(&hdr.nch);
711 	if (lseek(f->fd, f->startpos, SEEK_SET) == -1) {
712 		log_puts(f->path);
713 		log_puts(": ");
714 		log_puts("failed to seek to data chunk\n");
715 		return 0;
716 	}
717 	return afile_checkpar(f);
718 }
719 
720 /*
721  * Write header and seek to start position
722  */
723 static int
724 afile_au_writehdr(struct afile *f)
725 {
726 	struct au_hdr hdr;
727 	unsigned int fmt;
728 
729 	memset(&hdr, 0, sizeof(struct au_hdr));
730 	memcpy(hdr.id, au_id, 4);
731 	be32_set(&hdr.offs, f->startpos);
732 	be32_set(&hdr.size, f->endpos - f->startpos);
733 	switch (f->par.bits) {
734 	case 8:
735 		fmt = AU_FMT_PCM8;
736 		break;
737 	case 16:
738 		fmt = AU_FMT_PCM16;
739 		break;
740 	case 24:
741 		fmt = AU_FMT_PCM24;
742 		break;
743 	case 32:
744 		fmt = AU_FMT_PCM32;
745 		break;
746 #ifdef DEBUG
747 	default:
748 		log_puts(f->path);
749 		log_puts(": wrong precision\n");
750 		panic();
751 		return 0;
752 #endif
753 	}
754 	be32_set(&hdr.fmt, fmt);
755 	be32_set(&hdr.rate, f->rate);
756 	be32_set(&hdr.nch, f->nch);
757 	return afile_writehdr(f, &hdr, sizeof(struct au_hdr));
758 }
759 
760 size_t
761 afile_read(struct afile *f, void *data, size_t count)
762 {
763 	off_t maxread;
764 	ssize_t n;
765 
766 	if (f->endpos >= 0) {
767 		maxread = f->endpos - f->curpos;
768 		if (maxread == 0) {
769 #ifdef DEBUG
770 			if (log_level >= 3) {
771 				log_puts(f->path);
772 				log_puts(": end reached\n");
773 			}
774 #endif
775 			return 0;
776 		}
777 		if (count > maxread)
778 			count = maxread;
779 	}
780 	n = read(f->fd, data, count);
781 	if (n == -1) {
782 		log_puts(f->path);
783 		log_puts(": couldn't read\n");
784 		return 0;
785 	}
786 	f->curpos += n;
787 	return n;
788 }
789 
790 size_t
791 afile_write(struct afile *f, void *data, size_t count)
792 {
793 	off_t maxwrite;
794 	int n;
795 
796 	if (f->maxpos >= 0) {
797 		maxwrite = f->maxpos - f->curpos;
798 		if (maxwrite == 0) {
799 #ifdef DEBUG
800 			if (log_level >= 3) {
801 				log_puts(f->path);
802 				log_puts(": max file size reached\n");
803 			}
804 #endif
805 			return 0;
806 		}
807 		if (count > maxwrite)
808 			count = maxwrite;
809 	}
810 	n = write(f->fd, data, count);
811 	if (n == -1) {
812 		log_puts(f->path);
813 		log_puts(": couldn't write\n");
814 		return 0;
815 	}
816 	f->curpos += n;
817 	if (f->endpos < f->curpos)
818 		f->endpos = f->curpos;
819 	return n;
820 }
821 
822 int
823 afile_seek(struct afile *f, off_t pos)
824 {
825 	pos += f->startpos;
826 	if (f->endpos >= 0 && pos > f->endpos && !f->par.sig) {
827 		log_puts(f->path);
828 		log_puts(": attempt to seek outside file boundaries\n");
829 		return 0;
830 	}
831 
832 	/*
833 	 * seek only if needed to avoid errors with pipes & sockets
834 	 */
835 	if (pos != f->curpos) {
836 		if (lseek(f->fd, pos, SEEK_SET) == -1) {
837 			log_puts(f->path);
838 			log_puts(": couldn't seek\n");
839 			return 0;
840 		}
841 		f->curpos = pos;
842 	}
843 	return 1;
844 }
845 
846 void
847 afile_close(struct afile *f)
848 {
849 	if (f->flags & AFILE_FWRITE) {
850 		if (f->hdr == AFILE_HDR_WAV)
851 			afile_wav_writehdr(f);
852 		else if (f->hdr == AFILE_HDR_AIFF)
853 			afile_aiff_writehdr(f);
854 		else if (f->hdr == AFILE_HDR_AU)
855 			afile_au_writehdr(f);
856 	}
857 	close(f->fd);
858 }
859 
860 int
861 afile_open(struct afile *f, char *path, int hdr, int flags,
862     struct aparams *par, int rate, int nch)
863 {
864 	char *ext;
865 	static union {
866 		struct wav_hdr wav;
867 		struct aiff_hdr aiff;
868 		struct au_hdr au;
869 	} dummy;
870 
871 	f->par = *par;
872 	f->rate = rate;
873 	f->nch = nch;
874 	f->flags = flags;
875 	f->hdr = hdr;
876 	if (hdr == AFILE_HDR_AUTO) {
877 		f->hdr = AFILE_HDR_RAW;
878 		ext = strrchr(path, '.');
879 		if (ext != NULL) {
880 			ext++;
881 			if (strcasecmp(ext, "aif") == 0 ||
882 			    strcasecmp(ext, "aiff") == 0 ||
883 			    strcasecmp(ext, "aifc") == 0)
884 				f->hdr = AFILE_HDR_AIFF;
885 			else if (strcasecmp(ext, "au") == 0 ||
886 			    strcasecmp(ext, "snd") == 0)
887 				f->hdr = AFILE_HDR_AU;
888 			else if (strcasecmp(ext, "wav") == 0)
889 				f->hdr = AFILE_HDR_WAV;
890 		}
891 	}
892 	if (f->flags == AFILE_FREAD) {
893 		if (strcmp(path, "-") == 0) {
894 			f->path = "stdin";
895 			f->fd = STDIN_FILENO;
896 		} else {
897 			f->path = path;
898 			f->fd = open(f->path, O_RDONLY, 0);
899 			if (f->fd == -1) {
900 				log_puts(f->path);
901 				log_puts(": failed to open for reading\n");
902 				return 0;
903 			}
904 		}
905 		if (f->hdr == AFILE_HDR_WAV) {
906 			if (!afile_wav_readhdr(f))
907 				goto bad_close;
908 		} else if (f->hdr == AFILE_HDR_AIFF) {
909 			if (!afile_aiff_readhdr(f))
910 				goto bad_close;
911 		} else if (f->hdr == AFILE_HDR_AU) {
912 			if (!afile_au_readhdr(f))
913 				goto bad_close;
914 		} else {
915 			f->startpos = 0;
916 			f->endpos = -1; /* read until EOF */
917 			f->fmt = AFILE_FMT_PCM;
918 		}
919 		f->curpos = f->startpos;
920 	} else if (flags == AFILE_FWRITE) {
921 		if (strcmp(path, "-") == 0) {
922 			f->path = "stdout";
923 			f->fd = STDOUT_FILENO;
924 		} else {
925 			f->path = path;
926 			f->fd = open(f->path,
927 			    O_WRONLY | O_TRUNC | O_CREAT, 0666);
928 			if (f->fd == -1) {
929 				log_puts(f->path);
930 				log_puts(": failed to create file\n");
931 				return 0;
932 			}
933 		}
934 		if (f->hdr == AFILE_HDR_WAV) {
935 			f->par.bps = (f->par.bits + 7) >> 3;
936 			if (f->par.bits > 8) {
937 				f->par.le = 1;
938 				f->par.sig = 1;
939 			} else
940 				f->par.sig = 0;
941 			if (f->par.bits & 7)
942 				f->par.msb = 1;
943 			f->endpos = f->startpos = sizeof(struct wav_hdr);
944 			f->maxpos = 0x7fffffff;
945 			if (!afile_writehdr(f, &dummy, sizeof(struct wav_hdr)))
946 				goto bad_close;
947 		} else if (f->hdr == AFILE_HDR_AIFF) {
948 			f->par.bps = (f->par.bits + 7) >> 3;
949 			if (f->par.bps > 1)
950 				f->par.le = 0;
951 			f->par.sig = 1;
952 			if (f->par.bits & 7)
953 				f->par.msb = 1;
954 			f->endpos = f->startpos = sizeof(struct aiff_hdr);
955 			f->maxpos = 0x7fffffff;
956 			if (!afile_writehdr(f, &dummy,
957 				sizeof(struct aiff_hdr)))
958 				goto bad_close;
959 		} else if (f->hdr == AFILE_HDR_AU) {
960 			f->par.bits = (f->par.bits + 7) & ~7;
961 			f->par.bps = f->par.bits / 8;
962 			f->par.le = 0;
963 			f->par.sig = 1;
964 			f->par.msb = 1;
965 			f->endpos = f->startpos = sizeof(struct au_hdr);
966 			f->maxpos = 0x7fffffff;
967 			if (!afile_writehdr(f, &dummy, sizeof(struct au_hdr)))
968 				goto bad_close;
969 		} else {
970 			f->endpos = f->startpos = 0;
971 			f->maxpos = -1;
972 		}
973 		f->curpos = f->startpos;
974 	} else {
975 #ifdef DEBUG
976 		log_puts("afile_open: wrong flags\n");
977 		panic();
978 #endif
979 	}
980 	return 1;
981 bad_close:
982 	close(f->fd);
983 	return 0;
984 }
985