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
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 ******************************************************************/
61 uint32_t nsamp
, nbytes
;
64 off_t data_start
; /* for seeking */
66 unsigned frame_samp
; /* samples left to read in current frame */
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
);
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] = {
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
93 The longs are in (ID, offset) pairs, each pair identifying a
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)
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
)];
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");
136 lsx_fail_errno(ft
,SOX_EHDR
,"Not a Psion Record file");
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
));
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
);
154 lsx_readdw(ft
, &len
);
156 lsx_debug("Number of samples: %d", len
);
158 lsx_readdw(ft
, &encoding
);
159 lsx_debug("Encoding of samples: %x", encoding
);
161 ft
->encoding
.encoding
= SOX_ENCODING_ALAW
;
162 else if (encoding
== 0x100001a1)
163 ft
->encoding
.encoding
= SOX_ENCODING_IMA_ADPCM
;
165 lsx_fail_errno(ft
, SOX_EHDR
, "Unrecognised encoding");
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
))
200 } else if (ft
->encoding
.encoding
== SOX_ENCODING_IMA_ADPCM
) {
202 if (lsx_adpcm_ima_start(ft
, &p
->adpcm
))
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
)
217 if (lsx_readb(ft
, &byte
) == SOX_EOF
)
218 return (unsigned)SOX_EOF
;
219 lsx_debug_more("Cardinal byte 1: %x", byte
);
224 if (lsx_readb(ft
, &byte
) == SOX_EOF
)
225 return (unsigned)SOX_EOF
;
226 lsx_debug_more("Cardinal byte 2: %x", byte
);
231 if (lsx_readb(ft
, &byte
) == SOX_EOF
)
232 return (unsigned)SOX_EOF
;
233 lsx_debug_more("Cardinal byte 3: %x", byte
);
235 if (lsx_readb(ft
, &byte
) == SOX_EOF
)
236 return (unsigned)SOX_EOF
;
237 lsx_debug_more("Cardinal byte 4: %x", byte
);
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
) {
255 if (p
->frame_samp
== 0) {
256 unsigned framelen
= read_cardinal(ft
);
259 if (framelen
== (unsigned)SOX_EOF
)
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
);
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
);
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
);
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
))
312 } else if (ft
->encoding
.encoding
== SOX_ENCODING_IMA_ADPCM
) {
313 if (lsx_adpcm_ima_start(ft
, &p
->adpcm
))
324 p
->data_start
= lsx_tell(ft
);
329 static void write_cardinal(sox_format_t
* ft
, unsigned a
)
335 lsx_debug_more("Cardinal byte 1: %x", byte
);
336 lsx_writeb(ft
, byte
);
337 } else if (a
< 0x8000) {
339 lsx_debug_more("Cardinal byte 1: %x", byte
);
340 lsx_writeb(ft
, byte
);
342 lsx_debug_more("Cardinal byte 2: %x", byte
);
343 lsx_writeb(ft
, byte
);
346 lsx_debug_more("Cardinal byte 1: %x", byte
);
347 lsx_writeb(ft
, byte
);
349 lsx_debug_more("Cardinal byte 2: %x", byte
);
350 lsx_writeb(ft
, byte
);
352 lsx_debug_more("Cardinal byte 3: %x", byte
);
353 lsx_writeb(ft
, byte
);
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 */
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
)
380 lsx_adpcm_flush(ft
, &p
->adpcm
);
384 written
= lsx_rawwrite(ft
, buf
, nsamp
);
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
;
396 lsx_warn("Header will have invalid file length since file is not seekable");
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.");
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
)
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
)