README.osx wasn't easily readable in Finder. Revert back to
[sox.git] / src / maud.c
bloba78c616de9557abbe01e0d3066073e6acba4244b
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 * Copyright 1998-2006 Chris Bagwell and SoX Contributors
6 * This source code is freely redistributable and may be used for
7 * any purpose. This copyright notice must be maintained.
8 * Lance Norskog And Sundry Contributors are not responsible for
9 * the consequences of using this software.
12 #include "sox_i.h"
13 #include <string.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <errno.h>
18 /* Private data for MAUD file */
19 typedef struct {
20 uint32_t nsamples;
21 } priv_t;
23 static void maudwriteheader(sox_format_t *);
26 * Do anything required before you start reading samples.
27 * Read file header.
28 * Find out sampling rate,
29 * size and encoding of samples,
30 * mono/stereo/quad.
32 static int startread(sox_format_t * ft)
34 priv_t * p = (priv_t *) ft->priv;
36 char buf[12];
37 char *chunk_buf;
39 unsigned short bitpersam;
40 uint32_t nom;
41 unsigned short denom;
42 unsigned short chaninf;
44 uint32_t chunksize;
45 uint32_t trash32;
46 uint16_t trash16;
47 int rc;
49 /* Needed for rawread() */
50 rc = lsx_rawstartread(ft);
51 if (rc)
52 return rc;
54 /* read FORM chunk */
55 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "FORM", (size_t)4) != 0)
57 lsx_fail_errno(ft,SOX_EHDR,"MAUD: header does not begin with magic word `FORM'");
58 return (SOX_EOF);
61 lsx_readdw(ft, &trash32); /* totalsize */
63 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "MAUD", (size_t)4) != 0)
65 lsx_fail_errno(ft,SOX_EHDR,"MAUD: `FORM' chunk does not specify `MAUD' as type");
66 return(SOX_EOF);
69 /* read chunks until 'BODY' (or end) */
71 while (lsx_reads(ft, buf, (size_t)4) == SOX_SUCCESS && strncmp(buf,"MDAT",(size_t)4) != 0) {
74 buf[4] = 0;
75 lsx_debug("chunk %s",buf);
78 if (strncmp(buf,"MHDR",(size_t)4) == 0) {
80 lsx_readdw(ft, &chunksize);
81 if (chunksize != 8*4)
83 lsx_fail_errno(ft,SOX_EHDR,"MAUD: MHDR chunk has bad size");
84 return(SOX_EOF);
87 /* fseeko(ft->fp,12,SEEK_CUR); */
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 if (lsx_seeki(ft, (off_t)0, 0) != 0)
232 lsx_fail_errno(ft,errno,"can't rewind output file to rewrite MAUD header");
233 return(SOX_EOF);
236 maudwriteheader(ft);
237 return(SOX_SUCCESS);
240 #define MAUDHEADERSIZE (4+(4+4+32)+(4+4+32)+(4+4))
241 static void maudwriteheader(sox_format_t * ft)
243 priv_t * p = (priv_t *) ft->priv;
245 lsx_writes(ft, "FORM");
246 lsx_writedw(ft, (p->nsamples* (ft->encoding.bits_per_sample >> 3)) + MAUDHEADERSIZE); /* size of file */
247 lsx_writes(ft, "MAUD"); /* File type */
249 lsx_writes(ft, "MHDR");
250 lsx_writedw(ft, 8*4); /* number of bytes to follow */
251 lsx_writedw(ft, p->nsamples); /* number of samples stored in MDAT */
253 switch (ft->encoding.encoding) {
255 case SOX_ENCODING_UNSIGNED:
256 lsx_writew(ft, 8); /* number of bits per sample as stored in MDAT */
257 lsx_writew(ft, 8); /* number of bits per sample after decompression */
258 break;
260 case SOX_ENCODING_SIGN2:
261 lsx_writew(ft, 16); /* number of bits per sample as stored in MDAT */
262 lsx_writew(ft, 16); /* number of bits per sample after decompression */
263 break;
265 case SOX_ENCODING_ALAW:
266 case SOX_ENCODING_ULAW:
267 lsx_writew(ft, 8); /* number of bits per sample as stored in MDAT */
268 lsx_writew(ft, 16); /* number of bits per sample after decompression */
269 break;
271 default:
272 break;
275 lsx_writedw(ft, (unsigned)(ft->signal.rate + .5)); /* sample rate, Hz */
276 lsx_writew(ft, (int) 1); /* clock devide */
278 if (ft->signal.channels == 1) {
279 lsx_writew(ft, 0); /* channel information */
280 lsx_writew(ft, 1); /* number of channels (mono: 1, stereo: 2, ...) */
282 else {
283 lsx_writew(ft, 1);
284 lsx_writew(ft, 2);
287 switch (ft->encoding.encoding) {
289 case SOX_ENCODING_UNSIGNED:
290 case SOX_ENCODING_SIGN2:
291 lsx_writew(ft, 0); /* no compression */
292 break;
294 case SOX_ENCODING_ULAW:
295 lsx_writew(ft, 3);
296 break;
298 case SOX_ENCODING_ALAW:
299 lsx_writew(ft, 2);
300 break;
302 default:
303 break;
306 lsx_writedw(ft, 0); /* reserved */
307 lsx_writedw(ft, 0); /* reserved */
308 lsx_writedw(ft, 0); /* reserved */
310 lsx_writes(ft, "ANNO");
311 lsx_writedw(ft, 30); /* length of block */
312 lsx_writes(ft, "file create by Sound eXchange ");
314 lsx_writes(ft, "MDAT");
315 lsx_writedw(ft, p->nsamples * (ft->encoding.bits_per_sample >> 3)); /* samples in file */
318 LSX_FORMAT_HANDLER(maud)
320 static char const * const names[] = {"maud", NULL};
321 static unsigned const write_encodings[] = {
322 SOX_ENCODING_SIGN2, 16, 0,
323 SOX_ENCODING_UNSIGNED, 8, 0,
324 SOX_ENCODING_ULAW, 8, 0,
325 SOX_ENCODING_ALAW, 8, 0,
327 static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
328 "Used with the ‘Toccata’ sound-card on the Amiga",
329 names, SOX_FILE_BIG_END | SOX_FILE_MONO | SOX_FILE_STEREO,
330 startread, lsx_rawread, lsx_rawstopread,
331 startwrite, write_samples, stopwrite,
332 NULL, write_encodings, NULL, sizeof(priv_t)
334 return &handler;