README.osx wasn't easily readable in Finder. Revert back to
[sox.git] / src / hcom.c
blobe76820e9333f32b9b23b5cbca2c83e3f950b89c7
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 startread(sox_format_t * ft)
78 priv_t *p = (priv_t *) ft->priv;
79 int i;
80 char buf[5];
81 uint32_t datasize, rsrcsize;
82 uint32_t huffcount, checksum, compresstype, divisor;
83 unsigned short dictsize;
84 int rc;
87 /* Skip first 65 bytes of header */
88 rc = lsx_skipbytes(ft, (size_t) 65);
89 if (rc)
90 return rc;
92 /* Check the file type (bytes 65-68) */
93 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "FSSD", (size_t)4) != 0)
95 lsx_fail_errno(ft,SOX_EHDR,"Mac header type is not FSSD");
96 return (SOX_EOF);
99 /* Skip to byte 83 */
100 rc = lsx_skipbytes(ft, (size_t) 83-69);
101 if (rc)
102 return rc;
104 /* Get essential numbers from the header */
105 lsx_readdw(ft, &datasize); /* bytes 83-86 */
106 lsx_readdw(ft, &rsrcsize); /* bytes 87-90 */
108 /* Skip the rest of the header (total 128 bytes) */
109 rc = lsx_skipbytes(ft, (size_t) 128-91);
110 if (rc != 0)
111 return rc;
113 /* The data fork must contain a "HCOM" header */
114 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "HCOM", (size_t)4) != 0)
116 lsx_fail_errno(ft,SOX_EHDR,"Mac data fork is not HCOM");
117 return (SOX_EOF);
120 /* Then follow various parameters */
121 lsx_readdw(ft, &huffcount);
122 lsx_readdw(ft, &checksum);
123 lsx_readdw(ft, &compresstype);
124 if (compresstype > 1)
126 lsx_fail_errno(ft,SOX_EHDR,"Bad compression type in HCOM header");
127 return (SOX_EOF);
129 lsx_readdw(ft, &divisor);
130 if (divisor == 0 || divisor > 4)
132 lsx_fail_errno(ft,SOX_EHDR,"Bad sampling rate divisor in HCOM header");
133 return (SOX_EOF);
135 lsx_readw(ft, &dictsize);
137 /* Translate to sox parameters */
138 ft->encoding.encoding = SOX_ENCODING_HCOM;
139 ft->encoding.bits_per_sample = 8;
140 ft->signal.rate = 22050 / divisor;
141 ft->signal.channels = 1;
143 /* Allocate memory for the dictionary */
144 p->dictionary = lsx_malloc(511 * sizeof(dictent));
146 /* Read dictionary */
147 for(i = 0; i < dictsize; i++) {
148 lsx_readsw(ft, &(p->dictionary[i].dict_leftson));
149 lsx_readsw(ft, &(p->dictionary[i].dict_rightson));
150 lsx_debug("%d %d",
151 p->dictionary[i].dict_leftson,
152 p->dictionary[i].dict_rightson);
154 rc = lsx_skipbytes(ft, (size_t) 1); /* skip pad byte */
155 if (rc)
156 return rc;
158 /* Initialized the decompression engine */
159 p->checksum = checksum;
160 p->deltacompression = compresstype;
161 if (!p->deltacompression)
162 lsx_debug("HCOM data using value compression");
163 p->huffcount = huffcount;
164 p->cksum = 0;
165 p->dictentry = 0;
166 p->nrbits = -1; /* Special case to get first byte */
168 return (SOX_SUCCESS);
171 static size_t read_samples(sox_format_t * ft, sox_sample_t *buf, size_t len)
173 register priv_t *p = (priv_t *) ft->priv;
174 int done = 0;
175 unsigned char sample_rate;
177 if (p->nrbits < 0) {
178 /* The first byte is special */
179 if (p->huffcount == 0)
180 return 0; /* Don't know if this can happen... */
181 if (lsx_readb(ft, &sample_rate) == SOX_EOF)
183 return (0);
185 p->sample = sample_rate;
186 *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p->sample,);
187 p->huffcount--;
188 p->nrbits = 0;
189 done++;
190 len--;
191 if (len == 0)
192 return done;
195 while (p->huffcount > 0) {
196 if(p->nrbits == 0) {
197 lsx_readdw(ft, &(p->current));
198 if (lsx_eof(ft))
200 lsx_fail_errno(ft,SOX_EOF,"unexpected EOF in HCOM data");
201 return (0);
203 p->cksum += p->current;
204 p->nrbits = 32;
206 if(p->current & 0x80000000) {
207 p->dictentry =
208 p->dictionary[p->dictentry].dict_rightson;
209 } else {
210 p->dictentry =
211 p->dictionary[p->dictentry].dict_leftson;
213 p->current = p->current << 1;
214 p->nrbits--;
215 if(p->dictionary[p->dictentry].dict_leftson < 0) {
216 short datum;
217 datum = p->dictionary[p->dictentry].dict_rightson;
218 if (!p->deltacompression)
219 p->sample = 0;
220 p->sample = (p->sample + datum) & 0xff;
221 p->huffcount--;
222 *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p->sample,);
223 p->dictentry = 0;
224 done++;
225 len--;
226 if (len == 0)
227 break;
231 return done;
234 static int stopread(sox_format_t * ft)
236 register priv_t *p = (priv_t *) ft->priv;
238 if (p->huffcount != 0)
240 lsx_fail_errno(ft,SOX_EFMT,"not all HCOM data read");
241 return (SOX_EOF);
243 if(p->cksum != p->checksum)
245 lsx_fail_errno(ft,SOX_EFMT,"checksum error in HCOM data");
246 return (SOX_EOF);
248 free(p->dictionary);
249 p->dictionary = NULL;
250 return (SOX_SUCCESS);
253 #define BUFINCR (10*BUFSIZ)
255 static int startwrite(sox_format_t * ft)
257 priv_t * p = (priv_t *) ft->priv;
259 p->size = BUFINCR;
260 p->pos = 0;
261 p->data = lsx_malloc(p->size);
262 return SOX_SUCCESS;
265 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t len)
267 priv_t *p = (priv_t *) ft->priv;
268 sox_sample_t datum;
269 size_t i;
271 if (len == 0)
272 return 0;
274 if (p->pos + len > p->size) {
275 p->size = ((p->pos + len) / BUFINCR + 1) * BUFINCR;
276 p->data = lsx_realloc(p->data, p->size);
279 for (i = 0; i < len; i++) {
280 SOX_SAMPLE_LOCALS;
281 datum = *buf++;
282 p->data[p->pos++] = SOX_SAMPLE_TO_UNSIGNED_8BIT(datum, ft->clips);
285 return len;
288 static void makecodes(int e, int c, int s, int b, dictent newdict[511], long codes[256], long codesize[256])
290 assert(b); /* Prevent stack overflow */
291 if (newdict[e].dict_leftson < 0) {
292 codes[newdict[e].dict_rightson] = c;
293 codesize[newdict[e].dict_rightson] = s;
294 } else {
295 makecodes(newdict[e].dict_leftson, c, s + 1, b << 1, newdict, codes, codesize);
296 makecodes(newdict[e].dict_rightson, c + b, s + 1, b << 1, newdict, codes, codesize);
300 static void putcode(sox_format_t * ft, long codes[256], long codesize[256], unsigned c, unsigned char **df)
302 priv_t *p = (priv_t *) ft->priv;
303 long code, size;
304 int i;
306 code = codes[c];
307 size = codesize[c];
308 for(i = 0; i < size; i++) {
309 p->curword <<= 1;
310 if (code & 1)
311 p->curword += 1;
312 p->nbits++;
313 if (p->nbits == 32) {
314 put32_be(df, p->curword);
315 p->new_checksum += p->curword;
316 p->nbits = 0;
317 p->curword = 0;
319 code >>= 1;
323 static void compress(sox_format_t * ft, unsigned char **df, int32_t *dl)
325 priv_t *p = (priv_t *) ft->priv;
326 int samplerate;
327 unsigned char *datafork = *df;
328 unsigned char *ddf, *dfp;
329 short dictsize;
330 int frequtable[256];
331 long codes[256], codesize[256];
332 dictent newdict[511];
333 int i, sample, j, k, d, l, frequcount;
335 sample = *datafork;
336 memset(frequtable, 0, sizeof(frequtable));
337 memset(codes, 0, sizeof(codes));
338 memset(codesize, 0, sizeof(codesize));
339 memset(newdict, 0, sizeof(newdict));
341 for (i = 1; i < *dl; i++) {
342 d = (datafork[i] - (sample & 0xff)) & 0xff; /* creates absolute entries LMS */
343 sample = datafork[i];
344 datafork[i] = d;
345 assert(d >= 0 && d <= 255); /* check our table is accessed correctly */
346 frequtable[d]++;
348 p->de = newdict;
349 for (i = 0; i < 256; i++)
350 if (frequtable[i] != 0) {
351 p->de->frequ = -frequtable[i];
352 p->de->dict_leftson = -1;
353 p->de->dict_rightson = i;
354 p->de++;
356 frequcount = p->de - newdict;
357 for (i = 0; i < frequcount; i++) {
358 for (j = i + 1; j < frequcount; j++) {
359 if (newdict[i].frequ > newdict[j].frequ) {
360 k = newdict[i].frequ;
361 newdict[i].frequ = newdict[j].frequ;
362 newdict[j].frequ = k;
363 k = newdict[i].dict_leftson;
364 newdict[i].dict_leftson = newdict[j].dict_leftson;
365 newdict[j].dict_leftson = k;
366 k = newdict[i].dict_rightson;
367 newdict[i].dict_rightson = newdict[j].dict_rightson;
368 newdict[j].dict_rightson = k;
372 while (frequcount > 1) {
373 j = frequcount - 1;
374 p->de->frequ = newdict[j - 1].frequ;
375 p->de->dict_leftson = newdict[j - 1].dict_leftson;
376 p->de->dict_rightson = newdict[j - 1].dict_rightson;
377 l = newdict[j - 1].frequ + newdict[j].frequ;
378 for (i = j - 2; i >= 0 && l < newdict[i].frequ; i--)
379 newdict[i + 1] = newdict[i];
380 i = i + 1;
381 newdict[i].frequ = l;
382 newdict[i].dict_leftson = j;
383 newdict[i].dict_rightson = p->de - newdict;
384 p->de++;
385 frequcount--;
387 dictsize = p->de - newdict;
388 makecodes(0, 0, 0, 1, newdict, codes, codesize);
389 l = 0;
390 for (i = 0; i < 256; i++)
391 l += frequtable[i] * codesize[i];
392 l = (((l + 31) >> 5) << 2) + 24 + dictsize * 4;
393 lsx_debug(" Original size: %6d bytes", *dl);
394 lsx_debug("Compressed size: %6d bytes", l);
395 datafork = lsx_malloc((size_t)l);
396 ddf = datafork + 22;
397 for(i = 0; i < dictsize; i++) {
398 put16_be(&ddf, newdict[i].dict_leftson);
399 put16_be(&ddf, newdict[i].dict_rightson);
401 *ddf++ = 0;
402 *ddf++ = *(*df)++;
403 p->new_checksum = 0;
404 p->nbits = 0;
405 p->curword = 0;
406 for (i = 1; i < *dl; i++)
407 putcode(ft, codes, codesize, *(*df)++, &ddf);
408 if (p->nbits != 0) {
409 codes[0] = 0;
410 codesize[0] = 32 - p->nbits;
411 putcode(ft, codes, codesize, 0, &ddf);
413 memcpy(datafork, "HCOM", (size_t)4);
414 dfp = datafork + 4;
415 put32_be(&dfp, *dl);
416 put32_be(&dfp, p->new_checksum);
417 put32_be(&dfp, 1);
418 samplerate = 22050 / ft->signal.rate + .5;
419 put32_be(&dfp, samplerate);
420 put16_be(&dfp, dictsize);
421 *df = datafork; /* reassign passed pointer to new datafork */
422 *dl = l; /* and its compressed length */
425 /* End of hcom utility routines */
427 static int stopwrite(sox_format_t * ft)
429 priv_t *p = (priv_t *) ft->priv;
430 unsigned char *compressed_data = p->data;
431 size_t compressed_len = p->pos;
432 int rc = SOX_SUCCESS;
434 /* Compress it all at once */
435 if (compressed_len)
436 compress(ft, &compressed_data, (int32_t *)&compressed_len);
437 free(p->data);
439 /* Write the header */
440 lsx_writebuf(ft, "\000\001A", (size_t) 3); /* Dummy file name "A" */
441 lsx_padbytes(ft, (size_t) 65-3);
442 lsx_writes(ft, "FSSD");
443 lsx_padbytes(ft, (size_t) 83-69);
444 lsx_writedw(ft, (unsigned) compressed_len); /* compressed_data size */
445 lsx_writedw(ft, 0); /* rsrc size */
446 lsx_padbytes(ft, (size_t) 128 - 91);
447 if (lsx_error(ft)) {
448 lsx_fail_errno(ft, errno, "write error in HCOM header");
449 rc = SOX_EOF;
450 } else if (lsx_writebuf(ft, compressed_data, compressed_len) != compressed_len) {
451 /* Write the compressed_data fork */
452 lsx_fail_errno(ft, errno, "can't write compressed HCOM data");
453 rc = SOX_EOF;
455 free(compressed_data);
457 if (rc == SOX_SUCCESS)
458 /* Pad the compressed_data fork to a multiple of 128 bytes */
459 lsx_padbytes(ft, 128u - (compressed_len % 128));
461 return rc;
464 LSX_FORMAT_HANDLER(hcom)
466 static char const * const names[] = {"hcom", NULL};
467 static sox_rate_t const write_rates[] = {22050,22050/2,22050/3,22050/4.,0};
468 static unsigned const write_encodings[] = {
469 SOX_ENCODING_HCOM, 8, 0, 0};
470 static sox_format_handler_t handler = {SOX_LIB_VERSION_CODE,
471 "Mac FSSD files with Huffman compression",
472 names, SOX_FILE_BIG_END|SOX_FILE_MONO,
473 startread, read_samples, stopread,
474 startwrite, write_samples, stopwrite,
475 NULL, write_encodings, write_rates, sizeof(priv_t)
477 return &handler;