stats: improve bit depth reporting [bug #267]
[sox.git] / src / maud.c
blob7bc716c5daf2ace7f715a7b849dbb2bbff327f36
1 /* libSoX MAUD file format handler, by Lutz Vieweg 1993
3 * supports: mono and stereo, linear, a-law and u-law reading and writing
5 * an IFF format; description at http://lclevy.free.fr/amiga/MAUDINFO.TXT
7 * Copyright 1998-2006 Chris Bagwell and SoX Contributors
8 * This source code is freely redistributable and may be used for
9 * any purpose. This copyright notice must be maintained.
10 * Lance Norskog And Sundry Contributors are not responsible for
11 * the consequences of using this software.
14 #include "sox_i.h"
15 #include <string.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <errno.h>
20 /* Private data for MAUD file */
21 typedef struct {
22 uint32_t nsamples;
23 } priv_t;
25 static void maudwriteheader(sox_format_t *);
28 * Do anything required before you start reading samples.
29 * Read file header.
30 * Find out sampling rate,
31 * size and encoding of samples,
32 * mono/stereo/quad.
34 static int startread(sox_format_t * ft)
36 priv_t * p = (priv_t *) ft->priv;
38 char buf[12];
39 char *chunk_buf;
41 unsigned short bitpersam;
42 uint32_t nom;
43 unsigned short denom;
44 unsigned short chaninf;
46 uint32_t chunksize;
47 uint32_t trash32;
48 uint16_t trash16;
49 int rc;
51 /* Needed for rawread() */
52 rc = lsx_rawstartread(ft);
53 if (rc)
54 return rc;
56 /* read FORM chunk */
57 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "FORM", (size_t)4) != 0)
59 lsx_fail_errno(ft,SOX_EHDR,"MAUD: header does not begin with magic word `FORM'");
60 return (SOX_EOF);
63 lsx_readdw(ft, &trash32); /* totalsize */
65 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "MAUD", (size_t)4) != 0)
67 lsx_fail_errno(ft,SOX_EHDR,"MAUD: `FORM' chunk does not specify `MAUD' as type");
68 return(SOX_EOF);
71 /* read chunks until 'BODY' (or end) */
73 while (lsx_reads(ft, buf, (size_t)4) == SOX_SUCCESS && strncmp(buf,"MDAT",(size_t)4) != 0) {
76 buf[4] = 0;
77 lsx_debug("chunk %s",buf);
80 if (strncmp(buf,"MHDR",(size_t)4) == 0) {
82 lsx_readdw(ft, &chunksize);
83 if (chunksize != 8*4)
85 lsx_fail_errno(ft,SOX_EHDR,"MAUD: MHDR chunk has bad size");
86 return(SOX_EOF);
89 /* number of samples stored in MDAT */
90 lsx_readdw(ft, &(p->nsamples));
92 /* number of bits per sample as stored in MDAT */
93 lsx_readw(ft, &bitpersam);
95 /* number of bits per sample after decompression */
96 lsx_readw(ft, &trash16);
98 lsx_readdw(ft, &nom); /* clock source frequency */
99 lsx_readw(ft, &denom); /* clock devide */
100 if (denom == 0)
102 lsx_fail_errno(ft,SOX_EHDR,"MAUD: frequency denominator == 0, failed");
103 return (SOX_EOF);
106 ft->signal.rate = nom / denom;
108 lsx_readw(ft, &chaninf); /* channel information */
109 switch (chaninf) {
110 case 0:
111 ft->signal.channels = 1;
112 break;
113 case 1:
114 ft->signal.channels = 2;
115 break;
116 default:
117 lsx_fail_errno(ft,SOX_EFMT,"MAUD: unsupported number of channels in file");
118 return (SOX_EOF);
121 lsx_readw(ft, &chaninf); /* number of channels (mono: 1, stereo: 2, ...) */
122 if (chaninf != ft->signal.channels)
124 lsx_fail_errno(ft,SOX_EFMT,"MAUD: unsupported number of channels in file");
125 return(SOX_EOF);
128 lsx_readw(ft, &chaninf); /* compression type */
130 lsx_readdw(ft, &trash32); /* rest of chunk, unused yet */
131 lsx_readdw(ft, &trash32);
132 lsx_readdw(ft, &trash32);
134 if (bitpersam == 8 && chaninf == 0) {
135 ft->encoding.bits_per_sample = 8;
136 ft->encoding.encoding = SOX_ENCODING_UNSIGNED;
138 else if (bitpersam == 8 && chaninf == 2) {
139 ft->encoding.bits_per_sample = 8;
140 ft->encoding.encoding = SOX_ENCODING_ALAW;
142 else if (bitpersam == 8 && chaninf == 3) {
143 ft->encoding.bits_per_sample = 8;
144 ft->encoding.encoding = SOX_ENCODING_ULAW;
146 else if (bitpersam == 16 && chaninf == 0) {
147 ft->encoding.bits_per_sample = 16;
148 ft->encoding.encoding = SOX_ENCODING_SIGN2;
150 else
152 lsx_fail_errno(ft,SOX_EFMT,"MAUD: unsupported compression type detected");
153 return(SOX_EOF);
156 continue;
159 if (strncmp(buf,"ANNO",(size_t)4) == 0) {
160 lsx_readdw(ft, &chunksize);
161 if (chunksize & 1)
162 chunksize++;
163 chunk_buf = lsx_malloc(chunksize + (size_t)1);
164 if (lsx_readbuf(ft, chunk_buf, (size_t)chunksize)
165 != chunksize)
167 lsx_fail_errno(ft,SOX_EOF,"MAUD: Unexpected EOF in ANNO header");
168 return(SOX_EOF);
170 chunk_buf[chunksize] = '\0';
171 lsx_debug("%s",chunk_buf);
172 free(chunk_buf);
174 continue;
177 /* some other kind of chunk */
178 lsx_readdw(ft, &chunksize);
179 if (chunksize & 1)
180 chunksize++;
181 lsx_seeki(ft, (off_t)chunksize, SEEK_CUR);
182 continue;
186 if (strncmp(buf,"MDAT",(size_t)4) != 0)
188 lsx_fail_errno(ft,SOX_EFMT,"MAUD: MDAT chunk not found");
189 return(SOX_EOF);
191 lsx_readdw(ft, &(p->nsamples));
192 return(SOX_SUCCESS);
195 static int startwrite(sox_format_t * ft)
197 priv_t * p = (priv_t *) ft->priv;
198 int rc;
200 /* Needed for rawwrite() */
201 rc = lsx_rawstartwrite(ft);
202 if (rc)
203 return rc;
205 /* If you have to seek around the output file */
206 if (! ft->seekable)
208 lsx_fail_errno(ft,SOX_EOF,"Output .maud file must be a file, not a pipe");
209 return (SOX_EOF);
211 p->nsamples = 0x7f000000;
212 maudwriteheader(ft);
213 p->nsamples = 0;
214 return (SOX_SUCCESS);
217 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t len)
219 priv_t * p = (priv_t *) ft->priv;
221 p->nsamples += len;
223 return lsx_rawwrite(ft, buf, len);
226 static int stopwrite(sox_format_t * ft)
228 /* All samples are already written out. */
230 priv_t *p = (priv_t*)ft->priv;
231 uint32_t mdat_size; /* MDAT chunk size */
232 mdat_size = p->nsamples * (ft->encoding.bits_per_sample >> 3);
233 lsx_padbytes(ft, (size_t) (mdat_size%2));
235 if (lsx_seeki(ft, (off_t)0, 0) != 0)
237 lsx_fail_errno(ft,errno,"can't rewind output file to rewrite MAUD header");
238 return(SOX_EOF);
241 maudwriteheader(ft);
242 return(SOX_SUCCESS);
245 #define MAUDHEADERSIZE (4+(4+4+32)+(4+4+19+1)+(4+4))
246 static void maudwriteheader(sox_format_t * ft)
248 priv_t * p = (priv_t *) ft->priv;
249 uint32_t mdat_size; /* MDAT chunk size */
251 mdat_size = p->nsamples * (ft->encoding.bits_per_sample >> 3);
253 lsx_writes(ft, "FORM");
254 lsx_writedw(ft, MAUDHEADERSIZE + mdat_size + mdat_size%2); /* size of file */
255 lsx_writes(ft, "MAUD"); /* File type */
257 lsx_writes(ft, "MHDR");
258 lsx_writedw(ft, 8*4); /* number of bytes to follow */
259 lsx_writedw(ft, p->nsamples); /* number of samples stored in MDAT */
261 switch (ft->encoding.encoding) {
263 case SOX_ENCODING_UNSIGNED:
264 lsx_writew(ft, 8); /* number of bits per sample as stored in MDAT */
265 lsx_writew(ft, 8); /* number of bits per sample after decompression */
266 break;
268 case SOX_ENCODING_SIGN2:
269 lsx_writew(ft, 16); /* number of bits per sample as stored in MDAT */
270 lsx_writew(ft, 16); /* number of bits per sample after decompression */
271 break;
273 case SOX_ENCODING_ALAW:
274 case SOX_ENCODING_ULAW:
275 lsx_writew(ft, 8); /* number of bits per sample as stored in MDAT */
276 lsx_writew(ft, 16); /* number of bits per sample after decompression */
277 break;
279 default:
280 break;
283 lsx_writedw(ft, (unsigned)(ft->signal.rate + .5)); /* sample rate, Hz */
284 lsx_writew(ft, (int) 1); /* clock devide */
286 if (ft->signal.channels == 1) {
287 lsx_writew(ft, 0); /* channel information */
288 lsx_writew(ft, 1); /* number of channels (mono: 1, stereo: 2, ...) */
290 else {
291 lsx_writew(ft, 1);
292 lsx_writew(ft, 2);
295 switch (ft->encoding.encoding) {
297 case SOX_ENCODING_UNSIGNED:
298 case SOX_ENCODING_SIGN2:
299 lsx_writew(ft, 0); /* no compression */
300 break;
302 case SOX_ENCODING_ULAW:
303 lsx_writew(ft, 3);
304 break;
306 case SOX_ENCODING_ALAW:
307 lsx_writew(ft, 2);
308 break;
310 default:
311 break;
314 lsx_writedw(ft, 0); /* reserved */
315 lsx_writedw(ft, 0); /* reserved */
316 lsx_writedw(ft, 0); /* reserved */
318 lsx_writes(ft, "ANNO");
319 lsx_writedw(ft, 19); /* length of block */
320 lsx_writes(ft, "file created by SoX");
321 lsx_padbytes(ft, (size_t)1);
323 lsx_writes(ft, "MDAT");
324 lsx_writedw(ft, p->nsamples * (ft->encoding.bits_per_sample >> 3)); /* samples in file */
327 LSX_FORMAT_HANDLER(maud)
329 static char const * const names[] = {"maud", NULL};
330 static unsigned const write_encodings[] = {
331 SOX_ENCODING_SIGN2, 16, 0,
332 SOX_ENCODING_UNSIGNED, 8, 0,
333 SOX_ENCODING_ULAW, 8, 0,
334 SOX_ENCODING_ALAW, 8, 0,
336 static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
337 "Used with the ‘Toccata’ sound-card on the Amiga",
338 names, SOX_FILE_BIG_END | SOX_FILE_MONO | SOX_FILE_STEREO,
339 startread, lsx_rawread, lsx_rawstopread,
340 startwrite, write_samples, stopwrite,
341 NULL, write_encodings, NULL, sizeof(priv_t)
343 return &handler;