Annotation of src/usr.bin/aucat/headers.c, Revision 1.15
1.15 ! ratchov 1: /* $OpenBSD: headers.c,v 1.14 2010/05/02 11:54:26 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: #include <sys/param.h>
19:
20: #include <err.h>
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <string.h>
24: #include <unistd.h>
25:
1.7 ratchov 26: #include "aparams.h"
1.1 ratchov 27: #include "conf.h"
1.6 ratchov 28: #include "wav.h"
29:
30: /*
1.7 ratchov 31: * Encoding IDs used in .wav headers.
1.6 ratchov 32: */
33: #define WAV_ENC_PCM 1
34: #define WAV_ENC_ALAW 6
35: #define WAV_ENC_ULAW 7
1.15 ! ratchov 36: #define WAV_ENC_EXT 0xfffe
1.1 ratchov 37:
38: struct wavriff {
39: char magic[4];
40: uint32_t size;
41: char type[4];
42: } __packed;
43:
44: struct wavchunk {
45: char id[4];
46: uint32_t size;
47: } __packed;
48:
49: struct wavfmt {
50: uint16_t fmt;
51: uint16_t nch;
52: uint32_t rate;
53: uint32_t byterate;
54: uint16_t blkalign;
1.4 ratchov 55: uint16_t bits;
1.15 ! ratchov 56: #define WAV_FMT_SIZE 16
! 57: #define WAV_FMT_SIZE2 (16 + 2)
! 58: #define WAV_FMT_EXT_SIZE (16 + 24)
! 59: uint16_t extsize;
! 60: uint16_t valbits;
! 61: uint32_t chanmask;
! 62: uint16_t extfmt;
! 63: char guid[14];
1.1 ratchov 64: } __packed;
65:
66: char wav_id_riff[4] = { 'R', 'I', 'F', 'F' };
67: char wav_id_wave[4] = { 'W', 'A', 'V', 'E' };
68: char wav_id_data[4] = { 'd', 'a', 't', 'a' };
69: char wav_id_fmt[4] = { 'f', 'm', 't', ' ' };
1.15 ! ratchov 70: char wav_guid[14] = {
! 71: 0x00, 0x00, 0x00, 0x00,
! 72: 0x10, 0x00, 0x80, 0x00,
! 73: 0x00, 0xAA, 0x00, 0x38,
! 74: 0x9B, 0x71
! 75: };
1.1 ratchov 76:
77: int
1.6 ratchov 78: wav_readfmt(int fd, unsigned csize, struct aparams *par, short **map)
1.1 ratchov 79: {
80: struct wavfmt fmt;
1.15 ! ratchov 81: unsigned nch, cmax, rate, bits, bps, enc;
1.1 ratchov 82:
1.15 ! ratchov 83: if (csize < WAV_FMT_SIZE) {
! 84: warnx("%u: bugus format chunk size", csize);
1.1 ratchov 85: return 0;
86: }
1.15 ! ratchov 87: if (csize > WAV_FMT_EXT_SIZE)
! 88: csize = WAV_FMT_EXT_SIZE;
! 89: if (read(fd, &fmt, csize) != csize) {
1.1 ratchov 90: warn("riff_read: chunk");
91: return 0;
92: }
93: enc = letoh16(fmt.fmt);
1.15 ! ratchov 94: bits = letoh16(fmt.bits);
! 95: if (enc == WAV_ENC_EXT) {
! 96: if (csize != WAV_FMT_EXT_SIZE) {
! 97: warnx("missing extended format chunk in .wav file");
! 98: return 0;
! 99: }
! 100: if (memcmp(fmt.guid, wav_guid, sizeof(wav_guid)) != 0) {
! 101: warnx("unknown format (GUID) in .wav file");
! 102: return 0;
! 103: }
! 104: bps = (bits + 7) / 8;
! 105: bits = letoh16(fmt.valbits);
! 106: enc = letoh16(fmt.extfmt);
! 107: } else
! 108: bps = (bits + 7) / 8;
1.6 ratchov 109: switch (enc) {
110: case WAV_ENC_PCM:
111: *map = NULL;
112: break;
113: case WAV_ENC_ALAW:
114: *map = wav_alawmap;
115: break;
116: case WAV_ENC_ULAW:
117: *map = wav_ulawmap;
118: break;
119: default:
120: errx(1, "%u: unsupported encoding in .wav file", enc);
1.1 ratchov 121: }
122: nch = letoh16(fmt.nch);
123: if (nch == 0) {
124: warnx("zero number of channels");
125: return 0;
126: }
127: cmax = par->cmin + nch - 1;
1.2 ratchov 128: if (cmax >= NCHAN_MAX) {
1.1 ratchov 129: warnx("%u:%u: bad range", par->cmin, cmax);
130: return 0;
131: }
132: rate = letoh32(fmt.rate);
1.9 ratchov 133: if (rate < RATE_MIN || rate > RATE_MAX) {
1.1 ratchov 134: warnx("%u: bad sample rate", rate);
135: return 0;
136: }
1.9 ratchov 137: if (bits == 0 || bits > 32) {
1.1 ratchov 138: warnx("%u: bad number of bits", bits);
139: return 0;
140: }
1.15 ! ratchov 141: if (bits > bps * 8) {
! 142: warnx("%u: bits larger than bytes-per-sample", bps);
! 143: return 0;
! 144: }
1.6 ratchov 145: if (enc == WAV_ENC_PCM) {
1.15 ! ratchov 146: par->bps = bps;
1.6 ratchov 147: par->bits = bits;
148: par->le = 1;
149: par->sig = (bits <= 8) ? 0 : 1; /* ask microsoft why... */
150: } else {
151: if (bits != 8) {
152: warnx("%u: mulaw/alaw encoding not 8-bit", bits);
153: return 0;
154: }
155: par->bits = 8 * sizeof(short);
156: par->bps = sizeof(short);
157: par->le = NATIVE_LE;
158: par->sig = 1;
159: }
1.1 ratchov 160: par->msb = 1;
161: par->cmax = cmax;
162: par->rate = rate;
163: return 1;
164: }
165:
166: int
1.13 ratchov 167: wav_readhdr(int fd, struct aparams *par, off_t *startpos, off_t *datasz, short **map)
1.1 ratchov 168: {
169: struct wavriff riff;
170: struct wavchunk chunk;
171: unsigned csize, rsize, pos = 0;
172: int fmt_done = 0;
173:
174: if (lseek(fd, 0, SEEK_SET) < 0) {
175: warn("lseek: 0");
176: return 0;
177: }
178: if (read(fd, &riff, sizeof(riff)) != sizeof(riff)) {
1.8 ratchov 179: warn("wav_readhdr: header");
1.1 ratchov 180: return 0;
181: }
182: if (memcmp(&riff.magic, &wav_id_riff, 4) != 0 ||
183: memcmp(&riff.type, &wav_id_wave, 4)) {
184: warnx("not a wave file");
185: return 0;
186: }
187: rsize = letoh32(riff.size);
188: while (pos + sizeof(struct wavchunk) <= rsize) {
189: if (read(fd, &chunk, sizeof(chunk)) != sizeof(chunk)) {
1.8 ratchov 190: warn("wav_readhdr: chunk");
1.1 ratchov 191: return 0;
192: }
193: csize = letoh32(chunk.size);
194: if (memcmp(chunk.id, wav_id_fmt, 4) == 0) {
1.6 ratchov 195: if (!wav_readfmt(fd, csize, par, map))
1.1 ratchov 196: return 0;
197: fmt_done = 1;
198: } else if (memcmp(chunk.id, wav_id_data, 4) == 0) {
1.13 ratchov 199: *startpos = pos;
1.1 ratchov 200: *datasz = csize;
201: break;
202: } else {
1.10 ratchov 203: #ifdef DEBUG
1.14 ratchov 204: if (debug_level >= 2)
1.10 ratchov 205: warnx("ignoring chuck <%.4s>\n", chunk.id);
206: #endif
1.1 ratchov 207: }
208:
209: /*
210: * next chunk
211: */
212: pos += sizeof(struct wavchunk) + csize;
213: if (lseek(fd, sizeof(riff) + pos, SEEK_SET) < 0) {
214: warn("lseek");
215: return 0;
216: }
217: }
218: if (!fmt_done) {
219: warnx("missing format chunk");
220: return 0;
221: }
222: return 1;
223: }
224:
1.13 ratchov 225: /*
226: * Write header and seek to start position
227: */
1.1 ratchov 228: int
1.13 ratchov 229: wav_writehdr(int fd, struct aparams *par, off_t *startpos, off_t datasz)
1.1 ratchov 230: {
231: unsigned nch = par->cmax - par->cmin + 1;
232: struct {
233: struct wavriff riff;
234: struct wavchunk fmt_hdr;
235: struct wavfmt fmt;
236: struct wavchunk data_hdr;
237: } hdr;
238:
239: /*
1.7 ratchov 240: * Check that encoding is supported by .wav file format.
1.1 ratchov 241: */
242: if (par->bits > 8 && !par->le) {
243: warnx("samples must be little endian");
244: return 0;
245: }
246: if (8 * par->bps - par->bits >= 8) {
247: warnx("padding must be less than 8 bits");
248: return 0;
249: }
250: if ((par->bits <= 8 && par->sig) || (par->bits > 8 && !par->sig)) {
1.7 ratchov 251: warnx("samples with more (less) than 8 bits must be signed "
252: "(unsigned)");
1.1 ratchov 253: return 0;
254: }
255: if (8 * par->bps != par->bits && !par->msb) {
256: warnx("samples must be MSB justified");
257: return 0;
258: }
259:
260: memcpy(hdr.riff.magic, wav_id_riff, 4);
261: memcpy(hdr.riff.type, wav_id_wave, 4);
262: hdr.riff.size = htole32(datasz + sizeof(hdr) - sizeof(hdr.riff));
263:
264: memcpy(hdr.fmt_hdr.id, wav_id_fmt, 4);
265: hdr.fmt_hdr.size = htole32(sizeof(hdr.fmt));
266: hdr.fmt.fmt = htole16(1);
267: hdr.fmt.nch = htole16(nch);
268: hdr.fmt.rate = htole32(par->rate);
269: hdr.fmt.byterate = htole32(par->rate * par->bps * nch);
270: hdr.fmt.bits = htole16(par->bits);
1.15 ! ratchov 271: hdr.fmt.blkalign = par->bps * nch;
1.1 ratchov 272:
273: memcpy(hdr.data_hdr.id, wav_id_data, 4);
274: hdr.data_hdr.size = htole32(datasz);
1.4 ratchov 275:
1.1 ratchov 276: if (lseek(fd, 0, SEEK_SET) < 0) {
277: warn("wav_writehdr: lseek");
278: return 0;
279: }
280: if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
281: warn("wav_writehdr: write");
282: return 0;
283: }
1.13 ratchov 284: *startpos = sizeof(hdr);
1.1 ratchov 285: return 1;
286: }