id3: fix COMM frame handling
[sox.git] / src / prc.c
blob58f765ed51879e33637a317949dd4a3a867a2804
1 /* Psion Record format (format of sound files used for EPOC machines).
2 * The file normally has no extension, so SoX uses .prc (Psion ReCord).
3 * Based (heavily) on the wve.c format file.
4 * Hacked by Bert van Leeuwen (bert@e.co.za)
6 * Header check improved, ADPCM encoding added, and other improvements
7 * by Reuben Thomas <rrt@sc3d.org>, using file format info at
8 * http://software.frodo.looijaard.name/psiconv/formats/
10 * This library is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or (at
13 * your option) any later version.
15 * This library is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
18 * General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this library; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 * Includes code for ADPCM framing based on code carrying the
25 * following copyright:
27 *******************************************************************
28 Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The
29 Netherlands.
31 All Rights Reserved
33 Permission to use, copy, modify, and distribute this software and its
34 documentation for any purpose and without fee is hereby granted,
35 provided that the above copyright notice appear in all copies and that
36 both that copyright notice and this permission notice appear in
37 supporting documentation, and that the names of Stichting Mathematisch
38 Centrum or CWI not be used in advertising or publicity pertaining to
39 distribution of the software without specific, written prior permission.
41 STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
42 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
43 FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
44 FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
45 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
46 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
47 OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
48 ******************************************************************/
51 #include "sox_i.h"
53 #include "adpcms.h"
55 #include <assert.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <limits.h>
60 typedef struct {
61 uint32_t nsamp, nbytes;
62 short padding;
63 short repeats;
64 off_t data_start; /* for seeking */
65 adpcm_io_t adpcm;
66 unsigned frame_samp; /* samples left to read in current frame */
67 } priv_t;
69 static void prcwriteheader(sox_format_t * ft);
71 static int seek(sox_format_t * ft, uint64_t offset)
73 priv_t * p = (priv_t *)ft->priv;
74 if (ft->encoding.encoding == SOX_ENCODING_ALAW)
75 return lsx_offset_seek(ft, (off_t)p->data_start, (off_t)offset);
76 return SOX_EOF;
79 /* File header. The first 4 words are fixed; the rest of the header
80 could theoretically be different, and this is the first place to
81 check with apparently invalid files.
83 N.B. All offsets are from start of file. */
84 static const char prc_header[41] = {
85 /* Header section */
86 '\x37','\x00','\x00','\x10', /* 0x00: File type (UID 1) */
87 '\x6d','\x00','\x00','\x10', /* 0x04: File kind (UID 2) */
88 '\x7e','\x00','\x00','\x10', /* 0x08: Application ID (UID 3) */
89 '\xcf','\xac','\x08','\x55', /* 0x0c: Checksum of UIDs 1-3 */
90 '\x14','\x00','\x00','\x00', /* 0x10: File offset of Section Table Section */
91 /* Section Table Section: a BListL, i.e. a list of longs preceded by
92 length byte.
93 The longs are in (ID, offset) pairs, each pair identifying a
94 section. */
95 '\x04', /* 0x14: List has 4 bytes, i.e. 2 pairs */
96 '\x52','\x00','\x00','\x10', /* 0x15: ID: Record Section */
97 '\x34','\x00','\x00','\x00', /* 0x19: Offset to Record Section */
98 '\x89','\x00','\x00','\x10', /* 0x1d: ID: Application ID Section */
99 '\x25','\x00','\x00','\x00', /* 0x21: Offset to Application ID Section */
100 '\x7e','\x00','\x00','\x10', /* 0x25: Application ID Section:
101 Record.app identifier */
102 /* Next comes the string, which can be either case. */
105 /* Format of the Record Section (offset 0x34):
107 00 L Uncompressed data length
108 04 ID a1 01 00 10 for ADPCM, 00 00 00 00 for A-law
109 08 W number of times sound will be repeated (0 = played once)
110 0a B Volume setting (01-05)
111 0b B Always 00 (?)
112 0c L Time between repeats in usec
113 10 LListB (i.e. long giving number of bytes followed by bytes) Sound Data
116 static int prc_checkheader(sox_format_t * ft, char *head)
118 lsx_readbuf(ft, head, sizeof(prc_header));
119 return memcmp(head, prc_header, sizeof(prc_header)) == 0;
122 static int startread(sox_format_t * ft)
124 priv_t * p = (priv_t *)ft->priv;
125 char head[sizeof(prc_header)];
126 uint8_t byte;
127 uint16_t reps;
128 uint32_t len, listlen, encoding, repgap;
129 unsigned char volume;
130 char appname[0x40]; /* Maximum possible length of name */
132 /* Check the header */
133 if (prc_checkheader(ft, head))
134 lsx_debug("Found Psion Record header");
135 else {
136 lsx_fail_errno(ft,SOX_EHDR,"Not a Psion Record file");
137 return (SOX_EOF);
140 lsx_readb(ft, &byte);
141 if ((byte & 0x3) != 0x2) {
142 lsx_fail_errno(ft, SOX_EHDR, "Invalid length byte for application name string %d", (int)(byte));
143 return SOX_EOF;
146 byte >>= 2;
147 assert(byte < 64);
148 lsx_reads(ft, appname, (size_t)byte);
149 if (strncasecmp(appname, "record.app", (size_t) byte) != 0) {
150 lsx_fail_errno(ft, SOX_EHDR, "Invalid application name string %.63s", appname);
151 return SOX_EOF;
154 lsx_readdw(ft, &len);
155 p->nsamp = len;
156 lsx_debug("Number of samples: %d", len);
158 lsx_readdw(ft, &encoding);
159 lsx_debug("Encoding of samples: %x", encoding);
160 if (encoding == 0)
161 ft->encoding.encoding = SOX_ENCODING_ALAW;
162 else if (encoding == 0x100001a1)
163 ft->encoding.encoding = SOX_ENCODING_IMA_ADPCM;
164 else {
165 lsx_fail_errno(ft, SOX_EHDR, "Unrecognised encoding");
166 return SOX_EOF;
169 lsx_readw(ft, &reps); /* Number of repeats */
170 lsx_debug("Repeats: %d", reps);
172 lsx_readb(ft, &volume);
173 lsx_debug("Volume: %d", (unsigned)volume);
174 if (volume < 1 || volume > 5)
175 lsx_warn("Volume %d outside range 1..5", volume);
177 lsx_readb(ft, &byte); /* Unused and seems always zero */
179 lsx_readdw(ft, &repgap); /* Time between repeats in usec */
180 lsx_debug("Time between repeats (usec): %u", repgap);
182 lsx_readdw(ft, &listlen); /* Length of samples list */
183 lsx_debug("Number of bytes in samples list: %u", listlen);
185 if (ft->signal.rate != 0 && ft->signal.rate != 8000)
186 lsx_report("PRC only supports 8 kHz; overriding.");
187 ft->signal.rate = 8000;
189 if (ft->signal.channels != 1 && ft->signal.channels != 0)
190 lsx_report("PRC only supports 1 channel; overriding.");
191 ft->signal.channels = 1;
193 p->data_start = lsx_tell(ft);
194 ft->signal.length = p->nsamp / ft->signal.channels;
196 if (ft->encoding.encoding == SOX_ENCODING_ALAW) {
197 ft->encoding.bits_per_sample = 8;
198 if (lsx_rawstartread(ft))
199 return SOX_EOF;
200 } else if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM) {
201 p->frame_samp = 0;
202 if (lsx_adpcm_ima_start(ft, &p->adpcm))
203 return SOX_EOF;
206 return (SOX_SUCCESS);
209 /* Read a variable-length encoded count */
210 /* Ignore return code of lsx_readb, as it doesn't really matter if EOF
211 is delayed until the caller. */
212 static unsigned read_cardinal(sox_format_t * ft)
214 unsigned a;
215 uint8_t byte;
217 if (lsx_readb(ft, &byte) == SOX_EOF)
218 return (unsigned)SOX_EOF;
219 lsx_debug_more("Cardinal byte 1: %x", byte);
220 a = byte;
221 if (!(a & 1))
222 a >>= 1;
223 else {
224 if (lsx_readb(ft, &byte) == SOX_EOF)
225 return (unsigned)SOX_EOF;
226 lsx_debug_more("Cardinal byte 2: %x", byte);
227 a |= byte << 8;
228 if (!(a & 2))
229 a >>= 2;
230 else if (!(a & 4)) {
231 if (lsx_readb(ft, &byte) == SOX_EOF)
232 return (unsigned)SOX_EOF;
233 lsx_debug_more("Cardinal byte 3: %x", byte);
234 a |= byte << 16;
235 if (lsx_readb(ft, &byte) == SOX_EOF)
236 return (unsigned)SOX_EOF;
237 lsx_debug_more("Cardinal byte 4: %x", byte);
238 a |= byte << 24;
239 a >>= 3;
243 return a;
246 static size_t read_samples(sox_format_t * ft, sox_sample_t *buf, size_t samp)
248 priv_t * p = (priv_t *)ft->priv;
250 lsx_debug_more("length now = %d", p->nsamp);
252 if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM) {
253 size_t nsamp, read;
255 if (p->frame_samp == 0) {
256 unsigned framelen = read_cardinal(ft);
257 uint32_t trash;
259 if (framelen == (unsigned)SOX_EOF)
260 return 0;
262 lsx_debug_more("frame length %d", framelen);
263 p->frame_samp = framelen;
265 /* Discard length of compressed data */
266 lsx_debug_more("compressed length %d", read_cardinal(ft));
267 /* Discard length of BListL */
268 lsx_readdw(ft, &trash);
269 lsx_debug_more("list length %d", trash);
271 /* Reset CODEC for start of frame */
272 lsx_adpcm_reset(&p->adpcm, ft->encoding.encoding);
274 nsamp = min(p->frame_samp, samp);
275 p->nsamp += nsamp;
276 read = lsx_adpcm_read(ft, &p->adpcm, buf, nsamp);
277 p->frame_samp -= read;
278 lsx_debug_more("samples left in this frame: %d", p->frame_samp);
279 return read;
280 } else {
281 p->nsamp += samp;
282 return lsx_rawread(ft, buf, samp);
286 static int stopread(sox_format_t * ft)
288 priv_t * p = (priv_t *)ft->priv;
290 if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM)
291 return lsx_adpcm_stopread(ft, &p->adpcm);
292 else
293 return SOX_SUCCESS;
296 /* When writing, the header is supposed to contain the number of
297 data bytes written, unless it is written to a pipe.
298 Since we don't know how many bytes will follow until we're done,
299 we first write the header with an unspecified number of bytes,
300 and at the end we rewind the file and write the header again
301 with the right size. This only works if the file is seekable;
302 if it is not, the unspecified size remains in the header
303 (this is illegal). */
305 static int startwrite(sox_format_t * ft)
307 priv_t * p = (priv_t *)ft->priv;
309 if (ft->encoding.encoding == SOX_ENCODING_ALAW) {
310 if (lsx_rawstartwrite(ft))
311 return SOX_EOF;
312 } else if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM) {
313 if (lsx_adpcm_ima_start(ft, &p->adpcm))
314 return SOX_EOF;
317 p->nsamp = 0;
318 p->nbytes = 0;
319 if (p->repeats == 0)
320 p->repeats = 1;
322 prcwriteheader(ft);
324 p->data_start = lsx_tell(ft);
326 return SOX_SUCCESS;
329 static void write_cardinal(sox_format_t * ft, unsigned a)
331 uint8_t byte;
333 if (a < 0x80) {
334 byte = a << 1;
335 lsx_debug_more("Cardinal byte 1: %x", byte);
336 lsx_writeb(ft, byte);
337 } else if (a < 0x8000) {
338 byte = (a << 2) | 1;
339 lsx_debug_more("Cardinal byte 1: %x", byte);
340 lsx_writeb(ft, byte);
341 byte = a >> 6;
342 lsx_debug_more("Cardinal byte 2: %x", byte);
343 lsx_writeb(ft, byte);
344 } else {
345 byte = (a << 3) | 3;
346 lsx_debug_more("Cardinal byte 1: %x", byte);
347 lsx_writeb(ft, byte);
348 byte = a >> 5;
349 lsx_debug_more("Cardinal byte 2: %x", byte);
350 lsx_writeb(ft, byte);
351 byte = a >> 13;
352 lsx_debug_more("Cardinal byte 3: %x", byte);
353 lsx_writeb(ft, byte);
354 byte = a >> 21;
355 lsx_debug_more("Cardinal byte 4: %x", byte);
356 lsx_writeb(ft, byte);
360 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t nsamp)
362 priv_t * p = (priv_t *)ft->priv;
363 /* Psion Record seems not to be able to handle frames > 800 samples */
364 size_t written = 0;
365 lsx_debug_more("length now = %d", p->nsamp);
366 if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM) {
367 while (written < nsamp) {
368 size_t written1, samp = min(nsamp - written, 800);
370 write_cardinal(ft, (unsigned) samp);
371 /* Write compressed length */
372 write_cardinal(ft, (unsigned) ((samp / 2) + (samp % 2) + 4));
373 /* Write length again (seems to be a BListL) */
374 lsx_debug_more("list length %lu", (unsigned long)samp);
375 lsx_writedw(ft, (unsigned) samp);
376 lsx_adpcm_reset(&p->adpcm, ft->encoding.encoding);
377 written1 = lsx_adpcm_write(ft, &p->adpcm, buf + written, samp);
378 if (written1 != samp)
379 break;
380 lsx_adpcm_flush(ft, &p->adpcm);
381 written += written1;
383 } else
384 written = lsx_rawwrite(ft, buf, nsamp);
385 p->nsamp += written;
386 return written;
389 static int stopwrite(sox_format_t * ft)
391 priv_t * p = (priv_t *)ft->priv;
393 p->nbytes = lsx_tell(ft) - p->data_start;
395 if (!ft->seekable) {
396 lsx_warn("Header will have invalid file length since file is not seekable");
397 return SOX_SUCCESS;
400 if (lsx_seeki(ft, (off_t)0, 0) != 0) {
401 lsx_fail_errno(ft,errno,"Can't rewind output file to rewrite Psion header.");
402 return(SOX_EOF);
404 prcwriteheader(ft);
405 return SOX_SUCCESS;
408 static void prcwriteheader(sox_format_t * ft)
410 priv_t * p = (priv_t *)ft->priv;
412 lsx_writebuf(ft, prc_header, sizeof(prc_header));
413 lsx_writes(ft, "\x2arecord.app");
415 lsx_debug("Number of samples: %d",p->nsamp);
416 lsx_writedw(ft, p->nsamp);
418 if (ft->encoding.encoding == SOX_ENCODING_ALAW)
419 lsx_writedw(ft, 0);
420 else
421 lsx_writedw(ft, 0x100001a1); /* ADPCM */
423 lsx_writew(ft, 0); /* Number of repeats */
424 lsx_writeb(ft, 3); /* Volume: use default value of Record.app */
425 lsx_writeb(ft, 0); /* Unused and seems always zero */
426 lsx_writedw(ft, 0); /* Time between repeats in usec */
428 lsx_debug("Number of bytes: %d", p->nbytes);
429 lsx_writedw(ft, p->nbytes); /* Number of bytes of data */
432 LSX_FORMAT_HANDLER(prc)
434 static char const * const names[] = {"prc", NULL};
435 static sox_rate_t const write_rates[] = {8000, 0};
436 static unsigned const write_encodings[] = {
437 SOX_ENCODING_ALAW, 8, 0,
438 SOX_ENCODING_IMA_ADPCM, 4, 0,
440 static sox_format_handler_t const handler = {
441 SOX_LIB_VERSION_CODE,
442 "Psion Record; used in EPOC devices (Series 5, Revo and similar)",
443 names, SOX_FILE_LIT_END | SOX_FILE_MONO,
444 startread, read_samples, stopread,
445 startwrite, write_samples, stopwrite,
446 seek, write_encodings, write_rates, sizeof(priv_t)
448 return &handler;