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 dictvalid(int n
, int size
, int left
, int right
)
78 if (n
> 0 && left
< 0)
81 return (unsigned)left
< size
&& (unsigned)right
< size
;
84 static int startread(sox_format_t
* ft
)
86 priv_t
*p
= (priv_t
*) ft
->priv
;
89 uint32_t datasize
, rsrcsize
;
90 uint32_t huffcount
, checksum
, compresstype
, divisor
;
91 unsigned short dictsize
;
95 /* Skip first 65 bytes of header */
96 rc
= lsx_skipbytes(ft
, (size_t) 65);
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");
107 /* Skip to byte 83 */
108 rc
= lsx_skipbytes(ft
, (size_t) 83-69);
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);
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");
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");
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");
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
));
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");
168 rc
= lsx_skipbytes(ft
, (size_t) 1); /* skip pad byte */
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
;
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
;
189 unsigned char sample_rate
;
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
)
199 p
->sample
= sample_rate
;
200 *buf
++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p
->sample
,);
209 while (p
->huffcount
> 0) {
211 lsx_readdw(ft
, &(p
->current
));
214 lsx_fail_errno(ft
,SOX_EOF
,"unexpected EOF in HCOM data");
217 p
->cksum
+= p
->current
;
220 if(p
->current
& 0x80000000) {
222 p
->dictionary
[p
->dictentry
].dict_rightson
;
225 p
->dictionary
[p
->dictentry
].dict_leftson
;
227 p
->current
= p
->current
<< 1;
229 if(p
->dictionary
[p
->dictentry
].dict_leftson
< 0) {
231 datum
= p
->dictionary
[p
->dictentry
].dict_rightson
;
232 if (!p
->deltacompression
)
234 p
->sample
= (p
->sample
+ datum
) & 0xff;
236 *buf
++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p
->sample
,);
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");
257 if(p
->cksum
!= p
->checksum
)
259 lsx_fail_errno(ft
,SOX_EFMT
,"checksum error in HCOM data");
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
;
275 p
->data
= lsx_malloc(p
->size
);
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
;
288 if (p
->pos
== INT32_MAX
)
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
++) {
304 p
->data
[p
->pos
++] = SOX_SAMPLE_TO_UNSIGNED_8BIT(datum
, ft
->clips
);
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
;
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
;
330 for(i
= 0; i
< size
; i
++) {
335 if (p
->nbits
== 32) {
336 put32_be(df
, p
->curword
);
337 p
->new_checksum
+= p
->curword
;
345 static void compress(sox_format_t
* ft
, unsigned char **df
, int32_t *dl
)
347 priv_t
*p
= (priv_t
*) ft
->priv
;
349 unsigned char *datafork
= *df
;
350 unsigned char *ddf
, *dfp
;
353 long codes
[256], codesize
[256];
354 dictent newdict
[511];
355 int i
, sample
, j
, k
, d
, l
, frequcount
;
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
];
368 assert(d
>= 0 && d
<= 255); /* check our table is accessed correctly */
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
;
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) {
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
];
404 newdict
[i
].frequ
= l
;
405 newdict
[i
].dict_leftson
= j
;
406 newdict
[i
].dict_rightson
= p
->de
- newdict
;
410 dictsize
= p
->de
- newdict
;
411 makecodes(0, 0, 0, 1, newdict
, codes
, codesize
);
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
);
420 for(i
= 0; i
< dictsize
; i
++) {
421 put16_be(&ddf
, newdict
[i
].dict_leftson
);
422 put16_be(&ddf
, newdict
[i
].dict_rightson
);
429 for (i
= 1; i
< *dl
; i
++)
430 putcode(ft
, codes
, codesize
, *(*df
)++, &ddf
);
433 codesize
[0] = 32 - p
->nbits
;
434 putcode(ft
, codes
, codesize
, 0, &ddf
);
436 memcpy(datafork
, "HCOM", (size_t)4);
439 put32_be(&dfp
, p
->new_checksum
);
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
);
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);
472 lsx_fail_errno(ft
, errno
, "write error in HCOM header");
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");
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));
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
)