1 /* libSoX MAUD file format handler, by Lutz Vieweg 1993
2  *
3  * supports: mono and stereo, linear, a-law and u-law reading and writing
4  *
5  * an IFF format; description at http://lclevy.free.fr/amiga/MAUDINFO.TXT
6  *
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.
12  */
13 
14 #include "sox_i.h"
15 #include <string.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <errno.h>
19 
20 /* Private data for MAUD file */
21 typedef struct {
22         uint32_t nsamples;
23 } priv_t;
24 
25 static void maudwriteheader(sox_format_t *);
26 
27 /*
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.
33  */
startread(sox_format_t * ft)34 static int startread(sox_format_t * ft)
35 {
36         priv_t * p = (priv_t *) ft->priv;
37 
38         char buf[12];
39         char *chunk_buf;
40 
41         unsigned short bitpersam;
42         uint32_t nom;
43         unsigned short denom;
44         unsigned short chaninf;
45 
46         uint32_t chunksize;
47         uint32_t trash32;
48         uint16_t trash16;
49         int rc;
50 
51         /* Needed for rawread() */
52         rc = lsx_rawstartread(ft);
53         if (rc)
54             return rc;
55 
56         /* read FORM chunk */
57         if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "FORM", (size_t)4) != 0)
58         {
59                 lsx_fail_errno(ft,SOX_EHDR,"MAUD: header does not begin with magic word `FORM'");
60                 return (SOX_EOF);
61         }
62 
63         lsx_readdw(ft, &trash32); /* totalsize */
64 
65         if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "MAUD", (size_t)4) != 0)
66         {
67                 lsx_fail_errno(ft,SOX_EHDR,"MAUD: `FORM' chunk does not specify `MAUD' as type");
68                 return(SOX_EOF);
69         }
70 
71         /* read chunks until 'BODY' (or end) */
72 
73         while (lsx_reads(ft, buf, (size_t)4) == SOX_SUCCESS && strncmp(buf,"MDAT",(size_t)4) != 0) {
74 
75                 /*
76                 buf[4] = 0;
77                 lsx_debug("chunk %s",buf);
78                 */
79 
80                 if (strncmp(buf,"MHDR",(size_t)4) == 0) {
81 
82                         lsx_readdw(ft, &chunksize);
83                         if (chunksize != 8*4)
84                         {
85                             lsx_fail_errno(ft,SOX_EHDR,"MAUD: MHDR chunk has bad size");
86                             return(SOX_EOF);
87                         }
88 
89                         /* number of samples stored in MDAT */
90                         lsx_readdw(ft, &(p->nsamples));
91 
92                         /* number of bits per sample as stored in MDAT */
93                         lsx_readw(ft, &bitpersam);
94 
95                         /* number of bits per sample after decompression */
96                         lsx_readw(ft, &trash16);
97 
98                         lsx_readdw(ft, &nom);         /* clock source frequency */
99                         lsx_readw(ft, &denom);       /* clock devide           */
100                         if (denom == 0)
101                         {
102                             lsx_fail_errno(ft,SOX_EHDR,"MAUD: frequency denominator == 0, failed");
103                             return (SOX_EOF);
104                         }
105 
106                         ft->signal.rate = nom / denom;
107 
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);
119                         }
120 
121                         lsx_readw(ft, &chaninf); /* number of channels (mono: 1, stereo: 2, ...) */
122                         if (chaninf != ft->signal.channels)
123                         {
124                                 lsx_fail_errno(ft,SOX_EFMT,"MAUD: unsupported number of channels in file");
125                             return(SOX_EOF);
126                         }
127 
128                         lsx_readw(ft, &chaninf); /* compression type */
129 
130                         lsx_readdw(ft, &trash32); /* rest of chunk, unused yet */
131                         lsx_readdw(ft, &trash32);
132                         lsx_readdw(ft, &trash32);
133 
134                         if (bitpersam == 8 && chaninf == 0) {
135                                 ft->encoding.bits_per_sample = 8;
136                                 ft->encoding.encoding = SOX_ENCODING_UNSIGNED;
137                         }
138                         else if (bitpersam == 8 && chaninf == 2) {
139                                 ft->encoding.bits_per_sample = 8;
140                                 ft->encoding.encoding = SOX_ENCODING_ALAW;
141                         }
142                         else if (bitpersam == 8 && chaninf == 3) {
143                                 ft->encoding.bits_per_sample = 8;
144                                 ft->encoding.encoding = SOX_ENCODING_ULAW;
145                         }
146                         else if (bitpersam == 16 && chaninf == 0) {
147                                 ft->encoding.bits_per_sample = 16;
148                                 ft->encoding.encoding = SOX_ENCODING_SIGN2;
149                         }
150                         else
151                         {
152                                 lsx_fail_errno(ft,SOX_EFMT,"MAUD: unsupported compression type detected");
153                                 return(SOX_EOF);
154                         }
155 
156                         continue;
157                 }
158 
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)
166                         {
167                                 lsx_fail_errno(ft,SOX_EOF,"MAUD: Unexpected EOF in ANNO header");
168                                 return(SOX_EOF);
169                         }
170                         chunk_buf[chunksize] = '\0';
171                         lsx_debug("%s",chunk_buf);
172                         free(chunk_buf);
173 
174                         continue;
175                 }
176 
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;
183 
184         }
185 
186         if (strncmp(buf,"MDAT",(size_t)4) != 0)
187         {
188             lsx_fail_errno(ft,SOX_EFMT,"MAUD: MDAT chunk not found");
189             return(SOX_EOF);
190         }
191         lsx_readdw(ft, &(p->nsamples));
192         return(SOX_SUCCESS);
193 }
194 
startwrite(sox_format_t * ft)195 static int startwrite(sox_format_t * ft)
196 {
197         priv_t * p = (priv_t *) ft->priv;
198         int rc;
199 
200         /* Needed for rawwrite() */
201         rc = lsx_rawstartwrite(ft);
202         if (rc)
203             return rc;
204 
205         /* If you have to seek around the output file */
206         if (! ft->seekable)
207         {
208             lsx_fail_errno(ft,SOX_EOF,"Output .maud file must be a file, not a pipe");
209             return (SOX_EOF);
210         }
211         p->nsamples = 0x7f000000;
212         maudwriteheader(ft);
213         p->nsamples = 0;
214         return (SOX_SUCCESS);
215 }
216 
write_samples(sox_format_t * ft,const sox_sample_t * buf,size_t len)217 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t len)
218 {
219         priv_t * p = (priv_t *) ft->priv;
220 
221         p->nsamples += len;
222 
223         return lsx_rawwrite(ft, buf, len);
224 }
225 
stopwrite(sox_format_t * ft)226 static int stopwrite(sox_format_t * ft)
227 {
228         /* All samples are already written out. */
229 
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));
234 
235         if (lsx_seeki(ft, (off_t)0, 0) != 0)
236         {
237             lsx_fail_errno(ft,errno,"can't rewind output file to rewrite MAUD header");
238             return(SOX_EOF);
239         }
240 
241         maudwriteheader(ft);
242         return(SOX_SUCCESS);
243 }
244 
245 #define MAUDHEADERSIZE (4+(4+4+32)+(4+4+19+1)+(4+4))
maudwriteheader(sox_format_t * ft)246 static void maudwriteheader(sox_format_t * ft)
247 {
248         priv_t * p = (priv_t *) ft->priv;
249         uint32_t mdat_size; /* MDAT chunk size */
250 
251         mdat_size = p->nsamples * (ft->encoding.bits_per_sample >> 3);
252 
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 */
256 
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 */
260 
261         switch (ft->encoding.encoding) {
262 
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;
267 
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;
272 
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;
278 
279         default:
280           break;
281         }
282 
283         lsx_writedw(ft, (unsigned)(ft->signal.rate + .5)); /* sample rate, Hz */
284         lsx_writew(ft, (int) 1); /* clock devide */
285 
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, ...) */
289         }
290         else {
291           lsx_writew(ft, 1);
292           lsx_writew(ft, 2);
293         }
294 
295         switch (ft->encoding.encoding) {
296 
297         case SOX_ENCODING_UNSIGNED:
298         case SOX_ENCODING_SIGN2:
299           lsx_writew(ft, 0); /* no compression */
300           break;
301 
302         case SOX_ENCODING_ULAW:
303           lsx_writew(ft, 3);
304           break;
305 
306         case SOX_ENCODING_ALAW:
307           lsx_writew(ft, 2);
308           break;
309 
310         default:
311           break;
312         }
313 
314         lsx_writedw(ft, 0); /* reserved */
315         lsx_writedw(ft, 0); /* reserved */
316         lsx_writedw(ft, 0); /* reserved */
317 
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);
322 
323         lsx_writes(ft, "MDAT");
324         lsx_writedw(ft, p->nsamples * (ft->encoding.bits_per_sample >> 3)); /* samples in file */
325 }
326 
LSX_FORMAT_HANDLER(maud)327 LSX_FORMAT_HANDLER(maud)
328 {
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,
335     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)
342   };
343   return &handler;
344 }
345