Annotation of src/usr.bin/aucat/headers.c, Revision 1.23
1.23 ! ratchov 1: /* $OpenBSD$ */
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>
1.16 ratchov 22: #include <stdint.h>
1.1 ratchov 23: #include <stdlib.h>
24: #include <string.h>
25: #include <unistd.h>
26:
1.7 ratchov 27: #include "aparams.h"
1.1 ratchov 28: #include "conf.h"
1.6 ratchov 29: #include "wav.h"
30:
31: /*
1.7 ratchov 32: * Encoding IDs used in .wav headers.
1.6 ratchov 33: */
34: #define WAV_ENC_PCM 1
35: #define WAV_ENC_ALAW 6
36: #define WAV_ENC_ULAW 7
1.15 ratchov 37: #define WAV_ENC_EXT 0xfffe
1.1 ratchov 38:
39: struct wavriff {
40: char magic[4];
41: uint32_t size;
42: char type[4];
43: } __packed;
44:
45: struct wavchunk {
46: char id[4];
47: uint32_t size;
48: } __packed;
49:
50: struct wavfmt {
51: uint16_t fmt;
52: uint16_t nch;
53: uint32_t rate;
54: uint32_t byterate;
55: uint16_t blkalign;
1.4 ratchov 56: uint16_t bits;
1.15 ratchov 57: #define WAV_FMT_SIZE 16
58: #define WAV_FMT_SIZE2 (16 + 2)
59: #define WAV_FMT_EXT_SIZE (16 + 24)
60: uint16_t extsize;
61: uint16_t valbits;
62: uint32_t chanmask;
63: uint16_t extfmt;
64: char guid[14];
1.1 ratchov 65: } __packed;
66:
67: char wav_id_riff[4] = { 'R', 'I', 'F', 'F' };
68: char wav_id_wave[4] = { 'W', 'A', 'V', 'E' };
69: char wav_id_data[4] = { 'd', 'a', 't', 'a' };
70: char wav_id_fmt[4] = { 'f', 'm', 't', ' ' };
1.15 ratchov 71: char wav_guid[14] = {
72: 0x00, 0x00, 0x00, 0x00,
73: 0x10, 0x00, 0x80, 0x00,
74: 0x00, 0xAA, 0x00, 0x38,
75: 0x9B, 0x71
76: };
1.23 ! ratchov 77:
! 78: int wav_readfmt(int, unsigned int, struct aparams *, short **);
1.1 ratchov 79:
80: int
1.22 ratchov 81: wav_readfmt(int fd, unsigned int csize, struct aparams *par, short **map)
1.1 ratchov 82: {
83: struct wavfmt fmt;
1.22 ratchov 84: unsigned int nch, cmax, rate, bits, bps, enc;
1.1 ratchov 85:
1.15 ratchov 86: if (csize < WAV_FMT_SIZE) {
87: warnx("%u: bugus format chunk size", csize);
1.1 ratchov 88: return 0;
89: }
1.15 ratchov 90: if (csize > WAV_FMT_EXT_SIZE)
91: csize = WAV_FMT_EXT_SIZE;
92: if (read(fd, &fmt, csize) != csize) {
1.1 ratchov 93: warn("riff_read: chunk");
94: return 0;
95: }
96: enc = letoh16(fmt.fmt);
1.15 ratchov 97: bits = letoh16(fmt.bits);
98: if (enc == WAV_ENC_EXT) {
99: if (csize != WAV_FMT_EXT_SIZE) {
100: warnx("missing extended format chunk in .wav file");
101: return 0;
102: }
103: if (memcmp(fmt.guid, wav_guid, sizeof(wav_guid)) != 0) {
104: warnx("unknown format (GUID) in .wav file");
105: return 0;
106: }
107: bps = (bits + 7) / 8;
108: bits = letoh16(fmt.valbits);
109: enc = letoh16(fmt.extfmt);
110: } else
111: bps = (bits + 7) / 8;
1.6 ratchov 112: switch (enc) {
113: case WAV_ENC_PCM:
114: *map = NULL;
115: break;
116: case WAV_ENC_ALAW:
117: *map = wav_alawmap;
118: break;
119: case WAV_ENC_ULAW:
120: *map = wav_ulawmap;
121: break;
122: default:
123: errx(1, "%u: unsupported encoding in .wav file", enc);
1.1 ratchov 124: }
125: nch = letoh16(fmt.nch);
126: if (nch == 0) {
127: warnx("zero number of channels");
128: return 0;
129: }
130: cmax = par->cmin + nch - 1;
1.2 ratchov 131: if (cmax >= NCHAN_MAX) {
1.1 ratchov 132: warnx("%u:%u: bad range", par->cmin, cmax);
133: return 0;
134: }
135: rate = letoh32(fmt.rate);
1.9 ratchov 136: if (rate < RATE_MIN || rate > RATE_MAX) {
1.1 ratchov 137: warnx("%u: bad sample rate", rate);
138: return 0;
139: }
1.9 ratchov 140: if (bits == 0 || bits > 32) {
1.1 ratchov 141: warnx("%u: bad number of bits", bits);
142: return 0;
143: }
1.15 ratchov 144: if (bits > bps * 8) {
145: warnx("%u: bits larger than bytes-per-sample", bps);
146: return 0;
147: }
1.6 ratchov 148: if (enc == WAV_ENC_PCM) {
1.15 ratchov 149: par->bps = bps;
1.6 ratchov 150: par->bits = bits;
151: par->le = 1;
152: par->sig = (bits <= 8) ? 0 : 1; /* ask microsoft why... */
1.19 ratchov 153: par->msb = 1;
1.6 ratchov 154: } else {
155: if (bits != 8) {
156: warnx("%u: mulaw/alaw encoding not 8-bit", bits);
157: return 0;
158: }
1.19 ratchov 159: par->bits = ADATA_BITS;
160: par->bps = sizeof(adata_t);
161: par->le = ADATA_LE;
1.6 ratchov 162: par->sig = 1;
1.19 ratchov 163: par->msb = 0;
1.6 ratchov 164: }
1.1 ratchov 165: par->cmax = cmax;
166: par->rate = rate;
167: return 1;
168: }
169:
170: int
1.13 ratchov 171: wav_readhdr(int fd, struct aparams *par, off_t *startpos, off_t *datasz, short **map)
1.1 ratchov 172: {
173: struct wavriff riff;
174: struct wavchunk chunk;
1.22 ratchov 175: unsigned int csize, rsize, pos = 0;
1.1 ratchov 176: int fmt_done = 0;
177:
178: if (lseek(fd, 0, SEEK_SET) < 0) {
179: warn("lseek: 0");
180: return 0;
181: }
182: if (read(fd, &riff, sizeof(riff)) != sizeof(riff)) {
1.8 ratchov 183: warn("wav_readhdr: header");
1.1 ratchov 184: return 0;
185: }
186: if (memcmp(&riff.magic, &wav_id_riff, 4) != 0 ||
187: memcmp(&riff.type, &wav_id_wave, 4)) {
188: warnx("not a wave file");
189: return 0;
190: }
191: rsize = letoh32(riff.size);
1.17 ratchov 192: for (;;) {
193: if (pos + sizeof(struct wavchunk) > rsize) {
194: warnx("missing data chunk");
195: return 0;
196: }
1.1 ratchov 197: if (read(fd, &chunk, sizeof(chunk)) != sizeof(chunk)) {
1.8 ratchov 198: warn("wav_readhdr: chunk");
1.1 ratchov 199: return 0;
200: }
201: csize = letoh32(chunk.size);
202: if (memcmp(chunk.id, wav_id_fmt, 4) == 0) {
1.6 ratchov 203: if (!wav_readfmt(fd, csize, par, map))
1.1 ratchov 204: return 0;
205: fmt_done = 1;
206: } else if (memcmp(chunk.id, wav_id_data, 4) == 0) {
1.18 ratchov 207: *startpos = pos + sizeof(riff) + sizeof(chunk);
1.1 ratchov 208: *datasz = csize;
209: break;
210: } else {
1.10 ratchov 211: #ifdef DEBUG
1.14 ratchov 212: if (debug_level >= 2)
1.21 ratchov 213: warnx("ignoring chunk <%.4s>\n", chunk.id);
1.10 ratchov 214: #endif
1.1 ratchov 215: }
216:
217: /*
218: * next chunk
219: */
220: pos += sizeof(struct wavchunk) + csize;
221: if (lseek(fd, sizeof(riff) + pos, SEEK_SET) < 0) {
222: warn("lseek");
223: return 0;
224: }
225: }
226: if (!fmt_done) {
227: warnx("missing format chunk");
228: return 0;
229: }
230: return 1;
231: }
232:
1.13 ratchov 233: /*
234: * Write header and seek to start position
235: */
1.1 ratchov 236: int
1.13 ratchov 237: wav_writehdr(int fd, struct aparams *par, off_t *startpos, off_t datasz)
1.1 ratchov 238: {
1.22 ratchov 239: unsigned int nch = par->cmax - par->cmin + 1;
1.1 ratchov 240: struct {
241: struct wavriff riff;
242: struct wavchunk fmt_hdr;
243: struct wavfmt fmt;
244: struct wavchunk data_hdr;
1.20 ratchov 245: } __packed hdr;
1.1 ratchov 246:
247: /*
1.7 ratchov 248: * Check that encoding is supported by .wav file format.
1.1 ratchov 249: */
250: if (par->bits > 8 && !par->le) {
251: warnx("samples must be little endian");
252: return 0;
253: }
254: if (8 * par->bps - par->bits >= 8) {
255: warnx("padding must be less than 8 bits");
256: return 0;
257: }
258: if ((par->bits <= 8 && par->sig) || (par->bits > 8 && !par->sig)) {
1.7 ratchov 259: warnx("samples with more (less) than 8 bits must be signed "
260: "(unsigned)");
1.1 ratchov 261: return 0;
262: }
263: if (8 * par->bps != par->bits && !par->msb) {
264: warnx("samples must be MSB justified");
265: return 0;
266: }
267:
268: memcpy(hdr.riff.magic, wav_id_riff, 4);
269: memcpy(hdr.riff.type, wav_id_wave, 4);
270: hdr.riff.size = htole32(datasz + sizeof(hdr) - sizeof(hdr.riff));
271:
272: memcpy(hdr.fmt_hdr.id, wav_id_fmt, 4);
273: hdr.fmt_hdr.size = htole32(sizeof(hdr.fmt));
274: hdr.fmt.fmt = htole16(1);
275: hdr.fmt.nch = htole16(nch);
276: hdr.fmt.rate = htole32(par->rate);
277: hdr.fmt.byterate = htole32(par->rate * par->bps * nch);
1.20 ratchov 278: hdr.fmt.blkalign = par->bps * nch;
1.1 ratchov 279: hdr.fmt.bits = htole16(par->bits);
280:
281: memcpy(hdr.data_hdr.id, wav_id_data, 4);
282: hdr.data_hdr.size = htole32(datasz);
1.4 ratchov 283:
1.1 ratchov 284: if (lseek(fd, 0, SEEK_SET) < 0) {
285: warn("wav_writehdr: lseek");
286: return 0;
287: }
288: if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
289: warn("wav_writehdr: write");
290: return 0;
291: }
1.13 ratchov 292: *startpos = sizeof(hdr);
1.1 ratchov 293: return 1;
294: }