| /* libSoX Sound Blaster VOC handler sources. |
| * Copyright 1991 Lance Norskog And Sundry Contributors |
| * |
| * This source code is freely redistributable and may be used for |
| * any purpose. This copyright notice must be maintained. |
| * Lance Norskog And Sundry Contributors are not responsible for |
| * the consequences of using this software. |
| * |
| * September 8, 1993 |
| * Copyright 1993 T. Allen Grider - for changes to support block type 9 |
| * and word sized samples. Same caveats and disclaimer as above. |
| * |
| * February 22, 1996 |
| * by Chris Bagwell (cbagwell@sprynet.com) |
| * Added support for block type 8 (extended) which allows for 8-bit stereo |
| * files. Added support for saving stereo files and 16-bit files. |
| * Added VOC format info from audio format FAQ so I don't have to keep |
| * looking around for it. |
| * |
| * February 5, 2001 |
| * For sox-12-17 by Annonymous (see notes ANN) |
| * Added comments and notes for each procedure. |
| * Fixed so this now works with pipes, input does not have to |
| * be seekable anymore (in startread() ) |
| * Added support for uLAW and aLaw (aLaw not tested). |
| * Fixed support of multi-part VOC files, and files with |
| * block 9 but no audio in the block.... |
| * The following need to be tested: 16-bit, 2 channel, and aLaw. |
| * |
| * December 10, 2001 |
| * For sox-12-17-3 by Annonymous (see notes ANN) |
| * Patch for sox-12-17 merged with sox-12-17-3-pre3 code. |
| * |
| */ |
| |
| /*------------------------------------------------------------------------ |
| The following is taken from the Audio File Formats FAQ dated 2-Jan-1995 |
| and submitted by Guido van Rossum <guido@cwi.nl>. |
| -------------------------------------------------------------------------- |
| Creative Voice (VOC) file format |
| -------------------------------- |
| |
| From: galt@dsd.es.com |
| |
| (byte numbers are hex!) |
| |
| HEADER (bytes 00-19) |
| Series of DATA BLOCKS (bytes 1A+) [Must end w/ Terminator Block] |
| |
| - --------------------------------------------------------------- |
| |
| HEADER: |
| ------- |
| byte # Description |
| ------ ------------------------------------------ |
| 00-12 "Creative Voice File" |
| 13 1A (eof to abort printing of file) |
| 14-15 Offset of first datablock in .voc file (std 1A 00 |
| in Intel Notation) |
| 16-17 Version number (minor,major) (VOC-HDR puts 0A 01) |
| 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11) |
| |
| - --------------------------------------------------------------- |
| |
| DATA BLOCK: |
| ----------- |
| |
| Data Block: TYPE(1-byte), SIZE(3-bytes), INFO(0+ bytes) |
| NOTE: Terminator Block is an exception -- it has only the TYPE byte. |
| |
| TYPE Description Size (3-byte int) Info |
| ---- ----------- ----------------- ----------------------- |
| 00 Terminator (NONE) (NONE) |
| 01 Sound data 2+length of data * |
| 02 Sound continue length of data Voice Data |
| 03 Silence 3 ** |
| 04 Marker 2 Marker# (2 bytes) |
| 05 ASCII length of string null terminated string |
| 06 Repeat 2 Count# (2 bytes) |
| 07 End repeat 0 (NONE) |
| 08 Extended 4 *** |
| 09 New Header 16 see below |
| |
| |
| *Sound Info Format: **Silence Info Format: |
| --------------------- ---------------------------- |
| 00 Sample Rate 00-01 Length of silence - 1 |
| 01 Compression Type 02 Sample Rate |
| 02+ Voice Data |
| |
| ***Extended Info Format: |
| --------------------- |
| 00-01 Time Constant: Mono: 65536 - (256000000/sample_rate) |
| Stereo: 65536 - (25600000/(2*sample_rate)) |
| 02 Pack |
| 03 Mode: 0 = mono |
| 1 = stereo |
| |
| |
| Marker# -- Driver keeps the most recent marker in a status byte |
| Count# -- Number of repetitions + 1 |
| Count# may be 1 to FFFE for 0 - FFFD repetitions |
| or FFFF for endless repetitions |
| Sample Rate -- SR byte = 256-(1000000/sample_rate) |
| Length of silence -- in units of sampling cycle |
| Compression Type -- of voice data |
| 8-bits = 0 |
| 4-bits = 1 |
| 2.6-bits = 2 |
| 2-bits = 3 |
| Multi DAC = 3+(# of channels) [interesting-- |
| this isn't in the developer's manual] |
| |
| Detailed description of new data blocks (VOC files version 1.20 and above): |
| |
| (Source is fax from Barry Boone at Creative Labs, 405/742-6622) |
| |
| BLOCK 8 - digitized sound attribute extension, must preceed block 1. |
| Used to define stereo, 8 bit audio |
| BYTE bBlockID; // = 8 |
| BYTE nBlockLen[3]; // 3 byte length |
| WORD wTimeConstant; // time constant = same as block 1 |
| BYTE bPackMethod; // same as in block 1 |
| BYTE bVoiceMode; // 0-mono, 1-stereo |
| |
| Data is stored left, right |
| |
| BLOCK 9 - data block that supersedes blocks 1 and 8. |
| Used for stereo, 16 bit (and uLaw, aLaw). |
| |
| BYTE bBlockID; // = 9 |
| BYTE nBlockLen[3]; // length 12 plus length of sound |
| DWORD dwSamplesPerSec; // samples per second, not time const. |
| BYTE bBitsPerSample; // e.g., 8 or 16 |
| BYTE bChannels; // 1 for mono, 2 for stereo |
| WORD wFormat; // see below |
| BYTE reserved[4]; // pad to make block w/o data |
| // have a size of 16 bytes |
| |
| Valid values of wFormat are: |
| |
| 0x0000 8-bit unsigned PCM |
| 0x0001 Creative 8-bit to 4-bit ADPCM |
| 0x0002 Creative 8-bit to 3-bit ADPCM |
| 0x0003 Creative 8-bit to 2-bit ADPCM |
| 0x0004 16-bit signed PCM |
| 0x0006 CCITT a-Law |
| 0x0007 CCITT u-Law |
| 0x02000 Creative 16-bit to 4-bit ADPCM |
| |
| Data is stored left, right |
| |
| ANN: Multi-byte quantities are in Intel byte order (Little Endian). |
| |
| ------------------------------------------------------------------------*/ |
| |
| #include "third_party/sox/src/src/sox_i.h" |
| #include "third_party/sox/src/src/g711.h" |
| #include "third_party/sox/src/src/adpcms.h" |
| #include <assert.h> |
| #include <string.h> |
| |
| /* Private data for VOC file */ |
| typedef struct { |
| long block_remaining; /* bytes remaining in current block */ |
| long rate; /* rate code (byte) of this chunk */ |
| int silent; /* sound or silence? */ |
| long srate; /* rate code (byte) of silence */ |
| size_t blockseek; /* start of current output block */ |
| long samples; /* number of samples output */ |
| uint16_t format; /* VOC audio format */ |
| int size; /* word length of data */ |
| unsigned char channels; /* number of sound channels */ |
| long total_size; /* total size of all audio in file */ |
| int extended; /* Has an extended block been read? */ |
| adpcm_t adpcm; |
| } priv_t; |
| |
| #define VOC_TERM 0 |
| #define VOC_DATA 1 |
| #define VOC_CONT 2 |
| #define VOC_SILENCE 3 |
| #define VOC_MARKER 4 |
| #define VOC_TEXT 5 |
| #define VOC_LOOP 6 |
| #define VOC_LOOPEND 7 |
| #define VOC_EXTENDED 8 |
| #define VOC_DATA_16 9 |
| |
| /* ANN: Format encoding types */ |
| #define VOC_FMT_LIN8U 0 /* 8 bit unsigned linear PCM */ |
| #define VOC_FMT_CRLADPCM4 1 /* Creative 8-bit to 4-bit ADPCM */ |
| #define VOC_FMT_CRLADPCM3 2 /* Creative 8-bit to 3-bit ADPCM */ |
| #define VOC_FMT_CRLADPCM2 3 /* Creative 8-bit to 2-bit ADPCM */ |
| #define VOC_FMT_LIN16 4 /* 16-bit signed PCM */ |
| #define VOC_FMT_ALAW 6 /* CCITT a-Law 8-bit PCM */ |
| #define VOC_FMT_MU255 7 /* CCITT u-Law 8-bit PCM */ |
| #define VOC_FMT_CRLADPCM4A 0x200 /* Creative 16-bit to 4-bit ADPCM */ |
| |
| /* Prototypes for internal functions */ |
| static int getblock(sox_format_t *); |
| static void blockstart(sox_format_t *); |
| |
| /* Conversion macros (from raw.c) */ |
| #define SOX_ALAW_BYTE_TO_SAMPLE(d) ((sox_sample_t)(sox_alaw2linear16(d)) << 16) |
| #define SOX_ULAW_BYTE_TO_SAMPLE(d) ((sox_sample_t)(sox_ulaw2linear16(d)) << 16) |
| |
| /* public VOC functions for SOX */ |
| |
| /*----------------------------------------------------------------- |
| * startread() -- start reading a VOC file |
| *-----------------------------------------------------------------*/ |
| static int startread(sox_format_t * ft) |
| { |
| char header[20]; |
| priv_t * v = (priv_t *) ft->priv; |
| unsigned short sbseek; |
| int rc; |
| int ii; /* for getting rid of lseek */ |
| unsigned char uc; |
| |
| if (lsx_readbuf(ft, header, (size_t)20) != 20) { |
| lsx_fail_errno(ft, SOX_EHDR, "unexpected EOF in VOC header"); |
| return (SOX_EOF); |
| } |
| if (strncmp(header, "Creative Voice File\032", (size_t)19)) { |
| lsx_fail_errno(ft, SOX_EHDR, "VOC file header incorrect"); |
| return (SOX_EOF); |
| } |
| |
| /* read the offset to data, from start of file */ |
| /* after this read we have read 20 bytes of header + 2 */ |
| lsx_readw(ft, &sbseek); |
| |
| /* ANN: read to skip the header, instead of lseek */ |
| /* this should allow use with pipes.... */ |
| for (ii = 22; ii < sbseek; ii++) |
| lsx_readb(ft, &uc); |
| |
| v->rate = -1; |
| v->block_remaining = 0; |
| v->total_size = 0; /* ANN added */ |
| v->extended = 0; |
| |
| /* read until we get the format information.... */ |
| rc = getblock(ft); |
| if (rc) |
| return rc; |
| |
| /* get rate of data */ |
| if (v->rate == -1) { |
| lsx_fail_errno(ft, SOX_EOF, "Input .voc file had no sound!"); |
| return (SOX_EOF); |
| } |
| |
| /* setup word length of data */ |
| |
| /* ANN: Check VOC format and map to the proper libSoX format value */ |
| switch (v->format) { |
| case VOC_FMT_LIN8U: /* 0 8 bit unsigned linear PCM */ |
| ft->encoding.encoding = SOX_ENCODING_UNSIGNED; |
| v->size = 8; |
| break; |
| case VOC_FMT_CRLADPCM4: /* 1 Creative 8-bit to 4-bit ADPCM */ |
| ft->encoding.encoding = SOX_ENCODING_CL_ADPCM; |
| v->size = 4; |
| break; |
| case VOC_FMT_CRLADPCM3: /* 2 Creative 8-bit to 3-bit ADPCM */ |
| ft->encoding.encoding = SOX_ENCODING_CL_ADPCM; |
| v->size = 3; |
| break; |
| case VOC_FMT_CRLADPCM2: /* 3 Creative 8-bit to 2-bit ADPCM */ |
| ft->encoding.encoding = SOX_ENCODING_CL_ADPCM; |
| v->size = 2; |
| break; |
| case VOC_FMT_LIN16: /* 4 16-bit signed PCM */ |
| ft->encoding.encoding = SOX_ENCODING_SIGN2; |
| v->size = 16; |
| break; |
| case VOC_FMT_ALAW: /* 6 CCITT a-Law 8-bit PCM */ |
| ft->encoding.encoding = SOX_ENCODING_ALAW; |
| v->size = 8; |
| break; |
| case VOC_FMT_MU255: /* 7 CCITT u-Law 8-bit PCM */ |
| ft->encoding.encoding = SOX_ENCODING_ULAW; |
| v->size = 8; |
| break; |
| case VOC_FMT_CRLADPCM4A: /*0x200 Creative 16-bit to 4-bit ADPCM */ |
| ft->encoding.encoding = SOX_ENCODING_CL_ADPCM16; |
| v->size = 4; |
| break; |
| default: |
| lsx_fail("Unknown VOC format %d", v->format); |
| break; |
| } |
| ft->encoding.bits_per_sample = v->size; |
| |
| /* setup number of channels */ |
| if (ft->signal.channels == 0) |
| ft->signal.channels = v->channels; |
| |
| return (SOX_SUCCESS); |
| } |
| |
| /*----------------------------------------------------------------- |
| * read() -- read data from a VOC file |
| * ANN: Major changes here to support multi-part files and files |
| * that do not have audio in block 9's. |
| *-----------------------------------------------------------------*/ |
| static size_t read_samples(sox_format_t * ft, sox_sample_t * buf, |
| size_t len) |
| { |
| priv_t * v = (priv_t *) ft->priv; |
| size_t done = 0; |
| int rc = 0; |
| int16_t sw; |
| unsigned char uc; |
| |
| if (v->block_remaining == 0) { /* handle getting another cont. buffer */ |
| rc = getblock(ft); |
| if (rc) |
| return 0; |
| } |
| |
| if (v->block_remaining == 0) /* if no more data, return 0, i.e., done */ |
| return 0; |
| |
| if (v->silent) { |
| for (; v->block_remaining && (done < len); v->block_remaining--, done++) |
| *buf++ = 0; /* Fill in silence */ |
| } else { /* not silence; read len samples of audio from the file */ |
| size_t per = max(1, 9 / v->size); |
| |
| for (; (done + per <= len); done += per) { |
| if (v->block_remaining == 0) { /* IF no more in this block, get another */ |
| while (v->block_remaining == 0) { /* until have either EOF or a block with data */ |
| rc = getblock(ft); |
| if (rc) |
| break; |
| } |
| if (rc) /* IF EOF, break out, no more data, next will return 0 */ |
| break; |
| } |
| |
| /* Read the data in the file */ |
| if (v->size <= 4) { |
| if (!v->adpcm.setup.sign) { |
| SOX_SAMPLE_LOCALS; |
| if (lsx_readb(ft, &uc) == SOX_EOF) { |
| lsx_warn("VOC input: short file"); |
| v->block_remaining = 0; |
| return done; |
| } |
| *buf = SOX_UNSIGNED_8BIT_TO_SAMPLE(uc,); |
| lsx_adpcm_init(&v->adpcm, 6 - v->size, SOX_SAMPLE_TO_SIGNED_16BIT(*buf, ft->clips)); |
| ++buf; |
| --v->block_remaining; |
| ++done; |
| } |
| if (lsx_readb(ft, &uc) == SOX_EOF) { |
| lsx_warn("VOC input: short file"); |
| v->block_remaining = 0; |
| return done; |
| } |
| switch (v->size) { |
| case 2: |
| if (v->format == VOC_FMT_CRLADPCM2) { |
| int u = uc; |
| |
| *buf++ = |
| SOX_SIGNED_16BIT_TO_SAMPLE(lsx_adpcm_decode (u >> 6, &v->adpcm),); |
| *buf++ = |
| SOX_SIGNED_16BIT_TO_SAMPLE(lsx_adpcm_decode (u >> 4, &v->adpcm),); |
| *buf++ = |
| SOX_SIGNED_16BIT_TO_SAMPLE(lsx_adpcm_decode (u >> 2, &v->adpcm),); |
| *buf++ = |
| SOX_SIGNED_16BIT_TO_SAMPLE(lsx_adpcm_decode (u , &v->adpcm),); |
| } |
| break; |
| case 3: |
| if (v->format == VOC_FMT_CRLADPCM3) { |
| int u = uc; |
| |
| *buf++ = |
| SOX_SIGNED_16BIT_TO_SAMPLE(lsx_adpcm_decode (u >> 5, &v->adpcm),); |
| *buf++ = |
| SOX_SIGNED_16BIT_TO_SAMPLE(lsx_adpcm_decode (u >> 2, &v->adpcm),); |
| *buf++ = /* A bit from nowhere! */ |
| SOX_SIGNED_16BIT_TO_SAMPLE(lsx_adpcm_decode (u << 1, &v->adpcm),); |
| } |
| break; |
| case 4: |
| if (v->format == VOC_FMT_CRLADPCM4) { |
| int u = uc; |
| |
| *buf++ = |
| SOX_SIGNED_16BIT_TO_SAMPLE(lsx_adpcm_decode (u >> 4, &v->adpcm),); |
| *buf++ = |
| SOX_SIGNED_16BIT_TO_SAMPLE(lsx_adpcm_decode (u , &v->adpcm),); |
| } |
| break; |
| } |
| } else |
| switch (v->size) { |
| case 8: |
| if (lsx_readb(ft, &uc) == SOX_EOF) { |
| lsx_warn("VOC input: short file"); |
| v->block_remaining = 0; |
| return done; |
| } |
| if (v->format == VOC_FMT_MU255) { |
| *buf++ = SOX_ULAW_BYTE_TO_SAMPLE(uc); |
| } else if (v->format == VOC_FMT_ALAW) { |
| *buf++ = SOX_ALAW_BYTE_TO_SAMPLE(uc); |
| } else { |
| *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(uc,); |
| } |
| break; |
| case 16: |
| lsx_readsw(ft, &sw); |
| if (lsx_eof(ft)) { |
| lsx_warn("VOC input: short file"); |
| v->block_remaining = 0; |
| return done; |
| } |
| *buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(sw,); |
| v->block_remaining--; /* Processed 2 bytes so update */ |
| break; |
| } |
| /* decrement count of processed bytes */ |
| v->block_remaining--; |
| } |
| } |
| v->total_size += done; |
| return done; |
| } |
| |
| |
| /* When saving samples in VOC format the following outline is followed: |
| * If an 8-bit mono sample then use a VOC_DATA header. |
| * If an 8-bit stereo sample then use a VOC_EXTENDED header followed |
| * by a VOC_DATA header. |
| * If a 16-bit sample (either stereo or mono) then save with a |
| * VOC_DATA_16 header. |
| * |
| * ANN: Not supported: uLaw and aLaw output VOC files.... |
| * |
| * This approach will cause the output to be an its most basic format |
| * which will work with the oldest software (eg. an 8-bit mono sample |
| * will be able to be played with a really old SB VOC player.) |
| */ |
| static int startwrite(sox_format_t * ft) |
| { |
| priv_t * v = (priv_t *) ft->priv; |
| |
| if (!ft->seekable) { |
| lsx_fail_errno(ft, SOX_EOF, |
| "Output .voc file must be a file, not a pipe"); |
| return (SOX_EOF); |
| } |
| |
| v->samples = 0; |
| |
| /* File format name and a ^Z (aborts printing under DOS) */ |
| lsx_writes(ft, "Creative Voice File\032"); |
| lsx_writew(ft, 26); /* size of header */ |
| lsx_writew(ft, 0x10a); /* major/minor version number */ |
| lsx_writew(ft, 0x1129); /* checksum of version number */ |
| |
| return (SOX_SUCCESS); |
| } |
| |
| /*----------------------------------------------------------------- |
| * write() -- write a VOC file |
| *-----------------------------------------------------------------*/ |
| static size_t write_samples(sox_format_t * ft, const sox_sample_t * buf, |
| size_t len) |
| { |
| priv_t * v = (priv_t *) ft->priv; |
| unsigned char uc; |
| int16_t sw; |
| size_t done = 0; |
| |
| if (len && v->samples == 0) { |
| /* No silence packing yet. */ |
| v->silent = 0; |
| blockstart(ft); |
| } |
| v->samples += len; |
| while (done < len) { |
| SOX_SAMPLE_LOCALS; |
| if (ft->encoding.bits_per_sample == 8) { |
| uc = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips); |
| lsx_writeb(ft, uc); |
| } else { |
| sw = (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips); |
| lsx_writesw(ft, sw); |
| } |
| done++; |
| } |
| return done; |
| } |
| |
| /*----------------------------------------------------------------- |
| * blockstop() -- stop an output block |
| * End the current data or silence block. |
| *-----------------------------------------------------------------*/ |
| static void blockstop(sox_format_t * ft) |
| { |
| priv_t * v = (priv_t *) ft->priv; |
| sox_sample_t datum; |
| |
| lsx_writeb(ft, 0); /* End of file block code */ |
| lsx_seeki(ft, (off_t) v->blockseek, 0); /* seek back to block length */ |
| lsx_seeki(ft, (off_t)1, 1); /* seek forward one */ |
| if (v->silent) { |
| lsx_writesw(ft, (signed)v->samples); |
| } else { |
| if (ft->encoding.bits_per_sample == 8) { |
| if (ft->signal.channels > 1) { |
| lsx_seeki(ft, (off_t)8, 1); /* forward 7 + 1 for new block header */ |
| } |
| } |
| v->samples += 2; /* adjustment: SBDK pp. 3-5 */ |
| datum = (v->samples * (ft->encoding.bits_per_sample >> 3)) & 0xff; |
| lsx_writesb(ft, datum); /* low byte of length */ |
| datum = ((v->samples * (ft->encoding.bits_per_sample >> 3)) >> 8) & 0xff; |
| lsx_writesb(ft, datum); /* middle byte of length */ |
| datum = ((v->samples * (ft->encoding.bits_per_sample >> 3)) >> 16) & 0xff; |
| lsx_writesb(ft, datum); /* high byte of length */ |
| } |
| } |
| |
| /*----------------------------------------------------------------- |
| * stopwrite() -- stop writing a VOC file |
| *-----------------------------------------------------------------*/ |
| static int stopwrite(sox_format_t * ft) |
| { |
| blockstop(ft); |
| return (SOX_SUCCESS); |
| } |
| |
| /*----------------------------------------------------------------- |
| * Voc-file handlers (static, private to this module) |
| *-----------------------------------------------------------------*/ |
| |
| /*----------------------------------------------------------------- |
| * getblock() -- Read next block header, save info, |
| * leave position at start of dat |
| *-----------------------------------------------------------------*/ |
| static int getblock(sox_format_t * ft) |
| { |
| priv_t * v = (priv_t *) ft->priv; |
| unsigned char uc, block; |
| sox_uint24_t sblen; |
| uint16_t new_rate_16; |
| uint32_t new_rate_32; |
| |
| v->silent = 0; |
| /* DO while we have no audio to read */ |
| while (v->block_remaining == 0) { |
| if (lsx_eof(ft)) |
| return SOX_EOF; |
| |
| if (lsx_readb(ft, &block) == SOX_EOF) |
| return SOX_EOF; |
| |
| if (block == VOC_TERM) |
| return SOX_EOF; |
| |
| if (lsx_eof(ft)) |
| return SOX_EOF; |
| |
| lsx_read3(ft, &sblen); |
| |
| /* Based on VOC block type, process the block */ |
| /* audio may be in one or multiple blocks */ |
| switch (block) { |
| case VOC_DATA: |
| lsx_readb(ft, &uc); |
| /* When DATA block preceeded by an EXTENDED */ |
| /* block, the DATA blocks rate value is invalid */ |
| if (!v->extended) { |
| if (uc == 0) { |
| lsx_fail_errno(ft, SOX_EFMT, "Sample rate is zero?"); |
| return (SOX_EOF); |
| } |
| if ((v->rate != -1) && (uc != v->rate)) { |
| lsx_fail_errno(ft, SOX_EFMT, |
| "sample rate codes differ: %ld != %d", v->rate, |
| uc); |
| return (SOX_EOF); |
| } |
| v->rate = uc; |
| ft->signal.rate = 1000000.0 / (256 - v->rate); |
| v->channels = 1; |
| } |
| lsx_readb(ft, &uc); |
| v->format = uc; |
| v->extended = 0; |
| v->block_remaining = sblen - 2; |
| return (SOX_SUCCESS); |
| case VOC_DATA_16: |
| lsx_readdw(ft, &new_rate_32); |
| if (new_rate_32 == 0) { |
| lsx_fail_errno(ft, SOX_EFMT, "Sample rate is zero?"); |
| return (SOX_EOF); |
| } |
| if ((v->rate != -1) && ((long) new_rate_32 != v->rate)) { |
| lsx_fail_errno(ft, SOX_EFMT, "sample rate codes differ: %ld != %d", |
| v->rate, new_rate_32); |
| return (SOX_EOF); |
| } |
| v->rate = new_rate_32; |
| ft->signal.rate = new_rate_32; |
| lsx_readb(ft, &uc); |
| v->size = uc; |
| lsx_readb(ft, &(v->channels)); |
| lsx_readw(ft, &(v->format)); /* ANN: added format */ |
| lsx_skipbytes(ft, (size_t) 4); |
| v->block_remaining = sblen - 12; |
| return (SOX_SUCCESS); |
| case VOC_CONT: |
| v->block_remaining = sblen; |
| return (SOX_SUCCESS); |
| case VOC_SILENCE: |
| { |
| unsigned short period; |
| |
| lsx_readw(ft, &period); |
| lsx_readb(ft, &uc); |
| if (uc == 0) { |
| lsx_fail_errno(ft, SOX_EFMT, "Silence sample rate is zero"); |
| return (SOX_EOF); |
| } |
| /* |
| * Some silence-packed files have gratuitously |
| * different sample rate codes in silence. |
| * Adjust period. |
| */ |
| if ((v->rate != -1) && (uc != v->rate)) |
| period = (period * (256. - uc)) / (256 - v->rate) + .5; |
| else |
| v->rate = uc; |
| v->block_remaining = period; |
| v->silent = 1; |
| return (SOX_SUCCESS); |
| } |
| case VOC_MARKER: |
| lsx_readb(ft, &uc); |
| lsx_readb(ft, &uc); |
| /* Falling! Falling! */ |
| case VOC_TEXT: |
| { |
| uint32_t i = sblen; |
| int8_t c /*, line_buf[80]; |
| * int len = 0 */ ; |
| |
| lsx_warn("VOC TEXT"); |
| while (i--) { |
| lsx_readsb(ft, &c); |
| /* FIXME: this needs to be tested but I couldn't |
| * find a voc file with a VOC_TEXT chunk :( |
| if (c != '\0' && c != '\r') |
| line_buf[len++] = c; |
| if (len && (c == '\0' || c == '\r' || |
| i == 0 || len == sizeof(line_buf) - 1)) |
| { |
| lsx_report("%s", line_buf); |
| line_buf[len] = '\0'; |
| len = 0; |
| } |
| */ |
| } |
| } |
| continue; /* get next block */ |
| case VOC_LOOP: |
| case VOC_LOOPEND: |
| lsx_debug("skipping repeat loop"); |
| lsx_skipbytes(ft, (size_t) sblen); |
| break; |
| case VOC_EXTENDED: |
| /* An Extended block is followed by a data block */ |
| /* Set this byte so we know to use the rate */ |
| /* value from the extended block and not the */ |
| /* data block. */ |
| v->extended = 1; |
| lsx_readw(ft, &new_rate_16); |
| if (new_rate_16 == 0) { |
| lsx_fail_errno(ft, SOX_EFMT, "Sample rate is zero?"); |
| return (SOX_EOF); |
| } |
| if ((v->rate != -1) && (new_rate_16 != v->rate)) { |
| lsx_fail_errno(ft, SOX_EFMT, "sample rate codes differ: %ld != %d", |
| v->rate, new_rate_16); |
| return (SOX_EOF); |
| } |
| v->rate = new_rate_16; |
| lsx_readb(ft, &uc); /* bits_per_sample */ |
| lsx_readb(ft, &uc); |
| ft->signal.channels = uc? 2 : 1; /* Stereo */ |
| /* Needed number of channels before finishing |
| * compute for rate */ |
| ft->signal.rate = (256e6 / (65536 - v->rate)) / ft->signal.channels; |
| /* An extended block must be followed by a data */ |
| /* block to be valid so loop back to top so it */ |
| /* can be grabed. */ |
| continue; |
| default: |
| lsx_debug("skipping unknown block code %d", block); |
| lsx_skipbytes(ft, (size_t) sblen); |
| } |
| } |
| return SOX_SUCCESS; |
| } |
| |
| /*----------------------------------------------------------------- |
| * vlockstart() -- start an output block |
| *-----------------------------------------------------------------*/ |
| static void blockstart(sox_format_t * ft) |
| { |
| priv_t * v = (priv_t *) ft->priv; |
| |
| v->blockseek = lsx_tell(ft); |
| if (v->silent) { |
| lsx_writeb(ft, VOC_SILENCE); /* Silence block code */ |
| lsx_writeb(ft, 0); /* Period length */ |
| lsx_writeb(ft, 0); /* Period length */ |
| lsx_writesb(ft, (signed)v->rate); /* Rate code */ |
| } else { |
| if (ft->encoding.bits_per_sample == 8) { |
| /* 8-bit sample section. By always setting the correct */ |
| /* rate value in the DATA block (even when its preceeded */ |
| /* by an EXTENDED block) old software can still play stereo */ |
| /* files in mono by just skipping over the EXTENDED block. */ |
| /* Prehaps the rate should be doubled though to make up for */ |
| /* double amount of samples for a given time???? */ |
| if (ft->signal.channels > 1) { |
| lsx_writeb(ft, VOC_EXTENDED); /* Voice Extended block code */ |
| lsx_writeb(ft, 4); /* block length = 4 */ |
| lsx_writeb(ft, 0); /* block length = 4 */ |
| lsx_writeb(ft, 0); /* block length = 4 */ |
| v->rate = 65536 - (256000000.0 / (2 * ft->signal.rate)) + .5; |
| lsx_writesw(ft, (signed) v->rate); /* Rate code */ |
| lsx_writeb(ft, 0); /* File is not packed */ |
| lsx_writeb(ft, 1); /* samples are in stereo */ |
| } |
| lsx_writeb(ft, VOC_DATA); /* Voice Data block code */ |
| lsx_writeb(ft, 0); /* block length (for now) */ |
| lsx_writeb(ft, 0); /* block length (for now) */ |
| lsx_writeb(ft, 0); /* block length (for now) */ |
| v->rate = 256 - (1000000.0 / ft->signal.rate) + .5; |
| lsx_writesb(ft, (signed) v->rate); /* Rate code */ |
| lsx_writeb(ft, 0); /* 8-bit raw data */ |
| } else { |
| lsx_writeb(ft, VOC_DATA_16); /* Voice Data block code */ |
| lsx_writeb(ft, 0); /* block length (for now) */ |
| lsx_writeb(ft, 0); /* block length (for now) */ |
| lsx_writeb(ft, 0); /* block length (for now) */ |
| v->rate = ft->signal.rate + .5; |
| lsx_writedw(ft, (unsigned) v->rate); /* Rate code */ |
| lsx_writeb(ft, 16); /* Sample Size */ |
| lsx_writeb(ft, ft->signal.channels); /* Sample Size */ |
| lsx_writew(ft, 0x0004); /* Encoding */ |
| lsx_writeb(ft, 0); /* Unused */ |
| lsx_writeb(ft, 0); /* Unused */ |
| lsx_writeb(ft, 0); /* Unused */ |
| lsx_writeb(ft, 0); /* Unused */ |
| } |
| } |
| } |
| |
| LSX_FORMAT_HANDLER(voc) |
| { |
| static char const *const names[] = { "voc", NULL }; |
| static unsigned const write_encodings[] = { |
| SOX_ENCODING_SIGN2, 16, 0, |
| SOX_ENCODING_UNSIGNED, 8, 0, |
| 0 |
| }; |
| static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE, |
| "Creative Technology Sound Blaster format", |
| names, SOX_FILE_LIT_END | SOX_FILE_MONO | SOX_FILE_STEREO, |
| startread, read_samples, NULL, |
| startwrite, write_samples, stopwrite, |
| NULL, write_encodings, NULL, sizeof(priv_t) |
| }; |
| return &handler; |
| } |