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) 5 * 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/ 9 * 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. 14 * 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. 19 * 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 23 * 24 * Includes code for ADPCM framing based on code carrying the 25 * following copyright: 26 * 27 ******************************************************************* 28 Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The 29 Netherlands. 30 31 All Rights Reserved 32 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. 40 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 ******************************************************************/ 49 50 51 #include "sox_i.h" 52 53 #include "adpcms.h" 54 55 #include <assert.h> 56 #include <string.h> 57 #include <errno.h> 58 #include <limits.h> 59 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; 68 69 static void prcwriteheader(sox_format_t * ft); 70 71 static int seek(sox_format_t * ft, uint64_t offset) 72 { 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; 77 } 78 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. 82 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. */ 103 }; 104 105 /* Format of the Record Section (offset 0x34): 106 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 114 */ 115 116 static int prc_checkheader(sox_format_t * ft, char *head) 117 { 118 lsx_readbuf(ft, head, sizeof(prc_header)); 119 return memcmp(head, prc_header, sizeof(prc_header)) == 0; 120 } 121 122 static int startread(sox_format_t * ft) 123 { 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 */ 131 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); 138 } 139 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; 144 } 145 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; 152 } 153 154 lsx_readdw(ft, &len); 155 p->nsamp = len; 156 lsx_debug("Number of samples: %d", len); 157 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; 167 } 168 169 lsx_readw(ft, &reps); /* Number of repeats */ 170 lsx_debug("Repeats: %d", reps); 171 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); 176 177 lsx_readb(ft, &byte); /* Unused and seems always zero */ 178 179 lsx_readdw(ft, &repgap); /* Time between repeats in usec */ 180 lsx_debug("Time between repeats (usec): %u", repgap); 181 182 lsx_readdw(ft, &listlen); /* Length of samples list */ 183 lsx_debug("Number of bytes in samples list: %u", listlen); 184 185 if (ft->signal.rate != 0 && ft->signal.rate != 8000) 186 lsx_report("PRC only supports 8 kHz; overriding."); 187 ft->signal.rate = 8000; 188 189 if (ft->signal.channels != 1 && ft->signal.channels != 0) 190 lsx_report("PRC only supports 1 channel; overriding."); 191 ft->signal.channels = 1; 192 193 p->data_start = lsx_tell(ft); 194 ft->signal.length = p->nsamp / ft->signal.channels; 195 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; 204 } 205 206 return (SOX_SUCCESS); 207 } 208 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) 213 { 214 unsigned a; 215 uint8_t byte; 216 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; 240 } 241 } 242 243 return a; 244 } 245 246 static size_t read_samples(sox_format_t * ft, sox_sample_t *buf, size_t samp) 247 { 248 priv_t * p = (priv_t *)ft->priv; 249 250 lsx_debug_more("length now = %d", p->nsamp); 251 252 if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM) { 253 size_t nsamp, read; 254 255 if (p->frame_samp == 0) { 256 unsigned framelen = read_cardinal(ft); 257 uint32_t trash; 258 259 if (framelen == (unsigned)SOX_EOF) 260 return 0; 261 262 lsx_debug_more("frame length %d", framelen); 263 p->frame_samp = framelen; 264 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); 270 271 /* Reset CODEC for start of frame */ 272 lsx_adpcm_reset(&p->adpcm, ft->encoding.encoding); 273 } 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); 283 } 284 } 285 286 static int stopread(sox_format_t * ft) 287 { 288 priv_t * p = (priv_t *)ft->priv; 289 290 if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM) 291 return lsx_adpcm_stopread(ft, &p->adpcm); 292 else 293 return SOX_SUCCESS; 294 } 295 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). */ 304 305 static int startwrite(sox_format_t * ft) 306 { 307 priv_t * p = (priv_t *)ft->priv; 308 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; 315 } 316 317 p->nsamp = 0; 318 p->nbytes = 0; 319 if (p->repeats == 0) 320 p->repeats = 1; 321 322 prcwriteheader(ft); 323 324 p->data_start = lsx_tell(ft); 325 326 return SOX_SUCCESS; 327 } 328 329 static void write_cardinal(sox_format_t * ft, unsigned a) 330 { 331 uint8_t byte; 332 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); 357 } 358 } 359 360 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t nsamp) 361 { 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); 369 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; 382 } 383 } else 384 written = lsx_rawwrite(ft, buf, nsamp); 385 p->nsamp += written; 386 return written; 387 } 388 389 static int stopwrite(sox_format_t * ft) 390 { 391 priv_t * p = (priv_t *)ft->priv; 392 393 p->nbytes = lsx_tell(ft) - p->data_start; 394 395 if (!ft->seekable) { 396 lsx_warn("Header will have invalid file length since file is not seekable"); 397 return SOX_SUCCESS; 398 } 399 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); 403 } 404 prcwriteheader(ft); 405 return SOX_SUCCESS; 406 } 407 408 static void prcwriteheader(sox_format_t * ft) 409 { 410 priv_t * p = (priv_t *)ft->priv; 411 412 lsx_writebuf(ft, prc_header, sizeof(prc_header)); 413 lsx_writes(ft, "\x2arecord.app"); 414 415 lsx_debug("Number of samples: %d",p->nsamp); 416 lsx_writedw(ft, p->nsamp); 417 418 if (ft->encoding.encoding == SOX_ENCODING_ALAW) 419 lsx_writedw(ft, 0); 420 else 421 lsx_writedw(ft, 0x100001a1); /* ADPCM */ 422 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 */ 427 428 lsx_debug("Number of bytes: %d", p->nbytes); 429 lsx_writedw(ft, p->nbytes); /* Number of bytes of data */ 430 } 431 432 LSX_FORMAT_HANDLER(prc) 433 { 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, 439 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) 447 }; 448 return &handler; 449 } 450