1 /* libSoX Macintosh HCOM format.
2 * These are really FSSD type files with Huffman compression,
4 * TODO: make the MacBinary format optional (so that .data files
5 * are also acceptable). (How to do this on output?)
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
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;
39 static void put16_be(unsigned char **p
, int val
)
41 *(*p
)++ = (val
>> 8) & 0xff;
45 /* Dictionary entry for Huffman (de)compression */
53 /* Static data from the header */
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 */
76 static int startread(sox_format_t
* ft
)
78 priv_t
*p
= (priv_t
*) ft
->priv
;
81 uint32_t datasize
, rsrcsize
;
82 uint32_t huffcount
, checksum
, compresstype
, divisor
;
83 unsigned short dictsize
;
87 /* Skip first 65 bytes of header */
88 rc
= lsx_skipbytes(ft
, (size_t) 65);
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");
100 rc
= lsx_skipbytes(ft
, (size_t) 83-69);
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);
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");
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");
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");
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
));
151 p
->dictionary
[i
].dict_leftson
,
152 p
->dictionary
[i
].dict_rightson
);
154 rc
= lsx_skipbytes(ft
, (size_t) 1); /* skip pad byte */
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
;
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
;
175 unsigned char sample_rate
;
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
)
185 p
->sample
= sample_rate
;
186 *buf
++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p
->sample
,);
195 while (p
->huffcount
> 0) {
197 lsx_readdw(ft
, &(p
->current
));
200 lsx_fail_errno(ft
,SOX_EOF
,"unexpected EOF in HCOM data");
203 p
->cksum
+= p
->current
;
206 if(p
->current
& 0x80000000) {
208 p
->dictionary
[p
->dictentry
].dict_rightson
;
211 p
->dictionary
[p
->dictentry
].dict_leftson
;
213 p
->current
= p
->current
<< 1;
215 if(p
->dictionary
[p
->dictentry
].dict_leftson
< 0) {
217 datum
= p
->dictionary
[p
->dictentry
].dict_rightson
;
218 if (!p
->deltacompression
)
220 p
->sample
= (p
->sample
+ datum
) & 0xff;
222 *buf
++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p
->sample
,);
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");
243 if(p
->cksum
!= p
->checksum
)
245 lsx_fail_errno(ft
,SOX_EFMT
,"checksum error in HCOM data");
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
;
261 p
->data
= lsx_malloc(p
->size
);
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
;
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
++) {
282 p
->data
[p
->pos
++] = SOX_SAMPLE_TO_UNSIGNED_8BIT(datum
, ft
->clips
);
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
;
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
;
308 for(i
= 0; i
< size
; i
++) {
313 if (p
->nbits
== 32) {
314 put32_be(df
, p
->curword
);
315 p
->new_checksum
+= p
->curword
;
323 static void compress(sox_format_t
* ft
, unsigned char **df
, int32_t *dl
)
325 priv_t
*p
= (priv_t
*) ft
->priv
;
327 unsigned char *datafork
= *df
;
328 unsigned char *ddf
, *dfp
;
331 long codes
[256], codesize
[256];
332 dictent newdict
[511];
333 int i
, sample
, j
, k
, d
, l
, frequcount
;
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
];
345 assert(d
>= 0 && d
<= 255); /* check our table is accessed correctly */
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
;
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) {
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
];
381 newdict
[i
].frequ
= l
;
382 newdict
[i
].dict_leftson
= j
;
383 newdict
[i
].dict_rightson
= p
->de
- newdict
;
387 dictsize
= p
->de
- newdict
;
388 makecodes(0, 0, 0, 1, newdict
, codes
, codesize
);
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
);
397 for(i
= 0; i
< dictsize
; i
++) {
398 put16_be(&ddf
, newdict
[i
].dict_leftson
);
399 put16_be(&ddf
, newdict
[i
].dict_rightson
);
406 for (i
= 1; i
< *dl
; i
++)
407 putcode(ft
, codes
, codesize
, *(*df
)++, &ddf
);
410 codesize
[0] = 32 - p
->nbits
;
411 putcode(ft
, codes
, codesize
, 0, &ddf
);
413 memcpy(datafork
, "HCOM", (size_t)4);
416 put32_be(&dfp
, p
->new_checksum
);
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 */
436 compress(ft
, &compressed_data
, (int32_t *)&compressed_len
);
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);
448 lsx_fail_errno(ft
, errno
, "write error in HCOM header");
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");
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));
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
)