sox.1: fix example for mcompand
[sox.git] / src / hcom.c
blob594c8706064c46945d6b072f71c49bffff7a43b8
1 /* libSoX Macintosh HCOM format.
2 * These are really FSSD type files with Huffman compression,
3 * in MacBinary format.
4 * TODO: make the MacBinary format optional (so that .data files
5 * are also acceptable). (How to do this on output?)
7 * September 25, 1991
8 * Copyright 1991 Guido van Rossum And Sundry Contributors
9 * This source code is freely redistributable and may be used for
10 * any purpose. This copyright notice must be maintained.
11 * Guido van Rossum And Sundry Contributors are not responsible for
12 * the consequences of using this software.
14 * April 28, 1998 - Chris Bagwell (cbagwell@sprynet.com)
16 * Rearranged some functions so that they are declared before they are
17 * used, clearing up some compiler warnings. Because these functions
18 * passed floats, it helped some dumb compilers pass stuff on the
19 * stack correctly.
23 #include "sox_i.h"
24 #include <assert.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
29 /* FIXME: eliminate these 2 functions */
31 static void put32_be(unsigned char **p, int32_t val)
33 *(*p)++ = (val >> 24) & 0xff;
34 *(*p)++ = (val >> 16) & 0xff;
35 *(*p)++ = (val >> 8) & 0xff;
36 *(*p)++ = val & 0xff;
39 static void put16_be(unsigned char **p, int val)
41 *(*p)++ = (val >> 8) & 0xff;
42 *(*p)++ = val & 0xff;
45 /* Dictionary entry for Huffman (de)compression */
46 typedef struct {
47 long frequ;
48 short dict_leftson;
49 short dict_rightson;
50 } dictent;
52 typedef struct {
53 /* Static data from the header */
54 dictent *dictionary;
55 int32_t checksum;
56 int deltacompression;
57 /* Engine state */
58 long huffcount;
59 long cksum;
60 int dictentry;
61 int nrbits;
62 uint32_t current;
63 short sample;
64 /* Dictionary */
65 dictent *de;
66 int32_t new_checksum;
67 int nbits;
68 int32_t curword;
70 /* Private data used by writer */
71 unsigned char *data; /* Buffer allocated with lsx_malloc */
72 size_t size; /* Size of allocated buffer */
73 size_t pos; /* Where next byte goes */
74 } priv_t;
76 static int dictvalid(int n, int size, int left, int right)
78 if (n > 0 && left < 0)
79 return 1;
81 return (unsigned)left < size && (unsigned)right < size;
84 static int startread(sox_format_t * ft)
86 priv_t *p = (priv_t *) ft->priv;
87 int i;
88 char buf[5];
89 uint32_t datasize, rsrcsize;
90 uint32_t huffcount, checksum, compresstype, divisor;
91 unsigned short dictsize;
92 int rc;
95 /* Skip first 65 bytes of header */
96 rc = lsx_skipbytes(ft, (size_t) 65);
97 if (rc)
98 return rc;
100 /* Check the file type (bytes 65-68) */
101 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "FSSD", (size_t)4) != 0)
103 lsx_fail_errno(ft,SOX_EHDR,"Mac header type is not FSSD");
104 return (SOX_EOF);
107 /* Skip to byte 83 */
108 rc = lsx_skipbytes(ft, (size_t) 83-69);
109 if (rc)
110 return rc;
112 /* Get essential numbers from the header */
113 lsx_readdw(ft, &datasize); /* bytes 83-86 */
114 lsx_readdw(ft, &rsrcsize); /* bytes 87-90 */
116 /* Skip the rest of the header (total 128 bytes) */
117 rc = lsx_skipbytes(ft, (size_t) 128-91);
118 if (rc != 0)
119 return rc;
121 /* The data fork must contain a "HCOM" header */
122 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "HCOM", (size_t)4) != 0)
124 lsx_fail_errno(ft,SOX_EHDR,"Mac data fork is not HCOM");
125 return (SOX_EOF);
128 /* Then follow various parameters */
129 lsx_readdw(ft, &huffcount);
130 lsx_readdw(ft, &checksum);
131 lsx_readdw(ft, &compresstype);
132 if (compresstype > 1)
134 lsx_fail_errno(ft,SOX_EHDR,"Bad compression type in HCOM header");
135 return (SOX_EOF);
137 lsx_readdw(ft, &divisor);
138 if (divisor == 0 || divisor > 4)
140 lsx_fail_errno(ft,SOX_EHDR,"Bad sampling rate divisor in HCOM header");
141 return (SOX_EOF);
143 lsx_readw(ft, &dictsize);
145 /* Translate to sox parameters */
146 ft->encoding.encoding = SOX_ENCODING_HCOM;
147 ft->encoding.bits_per_sample = 8;
148 ft->signal.rate = 22050 / divisor;
149 ft->signal.channels = 1;
150 ft->signal.length = huffcount;
152 /* Allocate memory for the dictionary */
153 p->dictionary = lsx_malloc(511 * sizeof(dictent));
155 /* Read dictionary */
156 for(i = 0; i < dictsize; i++) {
157 lsx_readsw(ft, &(p->dictionary[i].dict_leftson));
158 lsx_readsw(ft, &(p->dictionary[i].dict_rightson));
159 lsx_debug("%d %d",
160 p->dictionary[i].dict_leftson,
161 p->dictionary[i].dict_rightson);
162 if (!dictvalid(i, dictsize, p->dictionary[i].dict_leftson,
163 p->dictionary[i].dict_rightson)) {
164 lsx_fail_errno(ft, SOX_EHDR, "Invalid dictionary");
165 return SOX_EOF;
168 rc = lsx_skipbytes(ft, (size_t) 1); /* skip pad byte */
169 if (rc)
170 return rc;
172 /* Initialized the decompression engine */
173 p->checksum = checksum;
174 p->deltacompression = compresstype;
175 if (!p->deltacompression)
176 lsx_debug("HCOM data using value compression");
177 p->huffcount = huffcount;
178 p->cksum = 0;
179 p->dictentry = 0;
180 p->nrbits = -1; /* Special case to get first byte */
182 return (SOX_SUCCESS);
185 static size_t read_samples(sox_format_t * ft, sox_sample_t *buf, size_t len)
187 register priv_t *p = (priv_t *) ft->priv;
188 int done = 0;
189 unsigned char sample_rate;
191 if (p->nrbits < 0) {
192 /* The first byte is special */
193 if (p->huffcount == 0)
194 return 0; /* Don't know if this can happen... */
195 if (lsx_readb(ft, &sample_rate) == SOX_EOF)
197 return (0);
199 p->sample = sample_rate;
200 *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p->sample,);
201 p->huffcount--;
202 p->nrbits = 0;
203 done++;
204 len--;
205 if (len == 0)
206 return done;
209 while (p->huffcount > 0) {
210 if(p->nrbits == 0) {
211 lsx_readdw(ft, &(p->current));
212 if (lsx_eof(ft))
214 lsx_fail_errno(ft,SOX_EOF,"unexpected EOF in HCOM data");
215 return (0);
217 p->cksum += p->current;
218 p->nrbits = 32;
220 if(p->current & 0x80000000) {
221 p->dictentry =
222 p->dictionary[p->dictentry].dict_rightson;
223 } else {
224 p->dictentry =
225 p->dictionary[p->dictentry].dict_leftson;
227 p->current = p->current << 1;
228 p->nrbits--;
229 if(p->dictionary[p->dictentry].dict_leftson < 0) {
230 short datum;
231 datum = p->dictionary[p->dictentry].dict_rightson;
232 if (!p->deltacompression)
233 p->sample = 0;
234 p->sample = (p->sample + datum) & 0xff;
235 p->huffcount--;
236 *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p->sample,);
237 p->dictentry = 0;
238 done++;
239 len--;
240 if (len == 0)
241 break;
245 return done;
248 static int stopread(sox_format_t * ft)
250 register priv_t *p = (priv_t *) ft->priv;
252 if (p->huffcount != 0)
254 lsx_fail_errno(ft,SOX_EFMT,"not all HCOM data read");
255 return (SOX_EOF);
257 if(p->cksum != p->checksum)
259 lsx_fail_errno(ft,SOX_EFMT,"checksum error in HCOM data");
260 return (SOX_EOF);
262 free(p->dictionary);
263 p->dictionary = NULL;
264 return (SOX_SUCCESS);
267 #define BUFINCR (10*BUFSIZ)
269 static int startwrite(sox_format_t * ft)
271 priv_t * p = (priv_t *) ft->priv;
273 p->size = BUFINCR;
274 p->pos = 0;
275 p->data = lsx_malloc(p->size);
276 return SOX_SUCCESS;
279 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t len)
281 priv_t *p = (priv_t *) ft->priv;
282 sox_sample_t datum;
283 size_t i;
285 if (len == 0)
286 return 0;
288 if (p->pos == INT32_MAX)
289 return SOX_EOF;
291 if (p->pos + len > INT32_MAX) {
292 lsx_warn("maximum file size exceeded");
293 len = INT32_MAX - p->pos;
296 if (p->pos + len > p->size) {
297 p->size = ((p->pos + len) / BUFINCR + 1) * BUFINCR;
298 p->data = lsx_realloc(p->data, p->size);
301 for (i = 0; i < len; i++) {
302 SOX_SAMPLE_LOCALS;
303 datum = *buf++;
304 p->data[p->pos++] = SOX_SAMPLE_TO_UNSIGNED_8BIT(datum, ft->clips);
307 return len;
310 static void makecodes(int e, int c, int s, int b, dictent newdict[511], long codes[256], long codesize[256])
312 assert(b); /* Prevent stack overflow */
313 if (newdict[e].dict_leftson < 0) {
314 codes[newdict[e].dict_rightson] = c;
315 codesize[newdict[e].dict_rightson] = s;
316 } else {
317 makecodes(newdict[e].dict_leftson, c, s + 1, b << 1, newdict, codes, codesize);
318 makecodes(newdict[e].dict_rightson, c + b, s + 1, b << 1, newdict, codes, codesize);
322 static void putcode(sox_format_t * ft, long codes[256], long codesize[256], unsigned c, unsigned char **df)
324 priv_t *p = (priv_t *) ft->priv;
325 long code, size;
326 int i;
328 code = codes[c];
329 size = codesize[c];
330 for(i = 0; i < size; i++) {
331 p->curword <<= 1;
332 if (code & 1)
333 p->curword += 1;
334 p->nbits++;
335 if (p->nbits == 32) {
336 put32_be(df, p->curword);
337 p->new_checksum += p->curword;
338 p->nbits = 0;
339 p->curword = 0;
341 code >>= 1;
345 static void compress(sox_format_t * ft, unsigned char **df, int32_t *dl)
347 priv_t *p = (priv_t *) ft->priv;
348 int samplerate;
349 unsigned char *datafork = *df;
350 unsigned char *ddf, *dfp;
351 short dictsize;
352 int frequtable[256];
353 long codes[256], codesize[256];
354 dictent newdict[511];
355 int i, sample, j, k, d, l, frequcount;
356 int64_t csize;
358 sample = *datafork;
359 memset(frequtable, 0, sizeof(frequtable));
360 memset(codes, 0, sizeof(codes));
361 memset(codesize, 0, sizeof(codesize));
362 memset(newdict, 0, sizeof(newdict));
364 for (i = 1; i < *dl; i++) {
365 d = (datafork[i] - (sample & 0xff)) & 0xff; /* creates absolute entries LMS */
366 sample = datafork[i];
367 datafork[i] = d;
368 assert(d >= 0 && d <= 255); /* check our table is accessed correctly */
369 frequtable[d]++;
371 p->de = newdict;
372 for (i = 0; i < 256; i++)
373 if (frequtable[i] != 0) {
374 p->de->frequ = -frequtable[i];
375 p->de->dict_leftson = -1;
376 p->de->dict_rightson = i;
377 p->de++;
379 frequcount = p->de - newdict;
380 for (i = 0; i < frequcount; i++) {
381 for (j = i + 1; j < frequcount; j++) {
382 if (newdict[i].frequ > newdict[j].frequ) {
383 k = newdict[i].frequ;
384 newdict[i].frequ = newdict[j].frequ;
385 newdict[j].frequ = k;
386 k = newdict[i].dict_leftson;
387 newdict[i].dict_leftson = newdict[j].dict_leftson;
388 newdict[j].dict_leftson = k;
389 k = newdict[i].dict_rightson;
390 newdict[i].dict_rightson = newdict[j].dict_rightson;
391 newdict[j].dict_rightson = k;
395 while (frequcount > 1) {
396 j = frequcount - 1;
397 p->de->frequ = newdict[j - 1].frequ;
398 p->de->dict_leftson = newdict[j - 1].dict_leftson;
399 p->de->dict_rightson = newdict[j - 1].dict_rightson;
400 l = newdict[j - 1].frequ + newdict[j].frequ;
401 for (i = j - 2; i >= 0 && l < newdict[i].frequ; i--)
402 newdict[i + 1] = newdict[i];
403 i = i + 1;
404 newdict[i].frequ = l;
405 newdict[i].dict_leftson = j;
406 newdict[i].dict_rightson = p->de - newdict;
407 p->de++;
408 frequcount--;
410 dictsize = p->de - newdict;
411 makecodes(0, 0, 0, 1, newdict, codes, codesize);
412 csize = 0;
413 for (i = 0; i < 256; i++)
414 csize += frequtable[i] * codesize[i];
415 l = (((csize + 31) >> 5) << 2) + 24 + dictsize * 4;
416 lsx_debug(" Original size: %6d bytes", *dl);
417 lsx_debug("Compressed size: %6d bytes", l);
418 datafork = lsx_malloc((size_t)l);
419 ddf = datafork + 22;
420 for(i = 0; i < dictsize; i++) {
421 put16_be(&ddf, newdict[i].dict_leftson);
422 put16_be(&ddf, newdict[i].dict_rightson);
424 *ddf++ = 0;
425 *ddf++ = *(*df)++;
426 p->new_checksum = 0;
427 p->nbits = 0;
428 p->curword = 0;
429 for (i = 1; i < *dl; i++)
430 putcode(ft, codes, codesize, *(*df)++, &ddf);
431 if (p->nbits != 0) {
432 codes[0] = 0;
433 codesize[0] = 32 - p->nbits;
434 putcode(ft, codes, codesize, 0, &ddf);
436 memcpy(datafork, "HCOM", (size_t)4);
437 dfp = datafork + 4;
438 put32_be(&dfp, *dl);
439 put32_be(&dfp, p->new_checksum);
440 put32_be(&dfp, 1);
441 samplerate = 22050 / ft->signal.rate + .5;
442 put32_be(&dfp, samplerate);
443 put16_be(&dfp, dictsize);
444 *df = datafork; /* reassign passed pointer to new datafork */
445 *dl = l; /* and its compressed length */
448 /* End of hcom utility routines */
450 static int stopwrite(sox_format_t * ft)
452 priv_t *p = (priv_t *) ft->priv;
453 unsigned char *compressed_data = p->data;
454 int32_t compressed_len = p->pos;
455 int rc = SOX_SUCCESS;
457 /* Compress it all at once */
458 if (compressed_len) {
459 compress(ft, &compressed_data, &compressed_len);
460 free(p->data);
463 /* Write the header */
464 lsx_writebuf(ft, "\000\001A", (size_t) 3); /* Dummy file name "A" */
465 lsx_padbytes(ft, (size_t) 65-3);
466 lsx_writes(ft, "FSSD");
467 lsx_padbytes(ft, (size_t) 83-69);
468 lsx_writedw(ft, (unsigned) compressed_len); /* compressed_data size */
469 lsx_writedw(ft, 0); /* rsrc size */
470 lsx_padbytes(ft, (size_t) 128 - 91);
471 if (lsx_error(ft)) {
472 lsx_fail_errno(ft, errno, "write error in HCOM header");
473 rc = SOX_EOF;
474 } else if (lsx_writebuf(ft, compressed_data, compressed_len) != compressed_len) {
475 /* Write the compressed_data fork */
476 lsx_fail_errno(ft, errno, "can't write compressed HCOM data");
477 rc = SOX_EOF;
479 free(compressed_data);
481 if (rc == SOX_SUCCESS)
482 /* Pad the compressed_data fork to a multiple of 128 bytes */
483 lsx_padbytes(ft, 128u - (compressed_len % 128));
485 return rc;
488 LSX_FORMAT_HANDLER(hcom)
490 static char const * const names[] = {"hcom", NULL};
491 static sox_rate_t const write_rates[] = {22050,22050/2,22050/3,22050/4.,0};
492 static unsigned const write_encodings[] = {
493 SOX_ENCODING_HCOM, 8, 0, 0};
494 static sox_format_handler_t handler = {SOX_LIB_VERSION_CODE,
495 "Mac FSSD files with Huffman compression",
496 names, SOX_FILE_BIG_END|SOX_FILE_MONO,
497 startread, read_samples, stopread,
498 startwrite, write_samples, stopwrite,
499 NULL, write_encodings, write_rates, sizeof(priv_t)
501 return &handler;