home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 3
/
AACD03.BIN
/
AACD
/
Sound
/
SoX
/
Source
/
voc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-07-18
|
17KB
|
589 lines
/*
* 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.
*/
/*
* Sound Tools Sound Blaster VOC handler sources.
*/
/*------------------------------------------------------------------------
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 ***
*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.
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
------------------------------------------------------------------------*/
#include "st.h"
#include <string.h>
/* Private data for VOC file */
typedef struct vocstuff {
LONG rest; /* 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 */
LONG blockseek; /* start of current output block */
LONG samples; /* number of samples output */
int size; /* word length of data */
int channels; /* number of sound channels */
int extended; /* Has an extended block been read? */
} *vs_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
#define min(a, b) (((a) < (b)) ? (a) : (b))
void getblock();
void blockstart(P1(ft_t));
void blockstop(P1(ft_t));
void vocstartread(ft)
ft_t ft;
{
char header[20];
vs_t v = (vs_t) ft->priv;
int sbseek;
int littlendian = 1;
char *endptr;
endptr = (char *) &littlendian;
/* VOC is in Little Endian format. Swap bytes read in on */
/* Big Endian mahcines. */
if (!*endptr)
{
ft->swap = ft->swap ? 0 : 1;
}
if (! ft->seekable)
fail("VOC input file must be a file, not a pipe");
if (fread(header, 1, 20, ft->fp) != 20)
fail("unexpected EOF in VOC header");
if (strncmp(header, "Creative Voice File\032", 19))
fail("VOC file header incorrect");
sbseek = rshort(ft);
fseek(ft->fp, sbseek, 0);
v->rate = -1;
v->rest = 0;
v->extended = 0;
getblock(ft);
if (v->rate == -1)
fail("Input .voc file had no sound!");
ft->info.size = v->size;
ft->info.style = UNSIGNED;
if (v->size == WORD)
ft->info.style = SIGN2;
if (ft->info.channels == -1)
ft->info.channels = v->channels;
}
LONG vocread(ft, buf, len)
ft_t ft;
LONG *buf, len;
{
vs_t v = (vs_t) ft->priv;
int done = 0;
if (v->rest == 0)
getblock(ft);
if (v->rest == 0)
return 0;
if (v->silent) {
/* Fill in silence */
for(;v->rest && (done < len); v->rest--, done++)
*buf++ = 0x80000000L;
} else {
for(;v->rest && (done < len); v->rest--, done++) {
LONG datum;
switch(v->size)
{
case BYTE:
if ((datum = getc(ft->fp)) == EOF) {
warn("VOC input: short file");
v->rest = 0;
return done;
}
datum ^= 0x80; /* convert to signed */
*buf++ = LEFT(datum, 24);
break;
case WORD:
datum = rshort(ft);
if (feof(ft->fp))
{
warn("VOC input: short file");
v->rest = 0;
return done;
}
*buf++ = LEFT(datum, 16);
v->rest--; /* Processed 2 bytes so update */
break;
}
}
}
return done;
}
/* nothing to do */
void vocstopread(ft)
ft_t ft;
{
}
/* 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.
*
* 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.)
*/
void vocstartwrite(ft)
ft_t ft;
{
vs_t v = (vs_t) ft->priv;
int littlendian = 1;
char *endptr;
endptr = (char *) &littlendian;
/* VOC is in Little Endian format. Swap whats read */
/* in on Big Endian machines. */
if (!*endptr)
{
ft->swap = ft->swap ? 0 : 1;
}
if (! ft->seekable)
fail("Output .voc file must be a file, not a pipe");
v->samples = 0;
/* File format name and a ^Z (aborts printing under DOS) */
(void) fwrite("Creative Voice File\032\032", 1, 20, ft->fp);
wshort(ft, 26); /* size of header */
wshort(ft, 0x10a); /* major/minor version number */
wshort(ft, 0x1129); /* checksum of version number */
if (ft->info.size == BYTE)
ft->info.style = UNSIGNED;
else
ft->info.style = SIGN2;
if (ft->info.channels == -1)
ft->info.channels = 1;
}
void vocwrite(ft, buf, len)
ft_t ft;
LONG *buf, len;
{
vs_t v = (vs_t) ft->priv;
unsigned char uc;
int sw;
if (v->samples == 0) {
/* No silence packing yet. */
v->silent = 0;
blockstart(ft);
}
v->samples += len;
while(len--) {
if (ft->info.size == BYTE) {
uc = RIGHT(*buf++, 24);
uc ^= 0x80;
putc(uc, ft->fp);
} else {
sw = (int) RIGHT(*buf++, 16);
wshort(ft,sw);
}
}
}
void vocstopwrite(ft)
ft_t ft;
{
blockstop(ft);
}
/* Voc-file handlers */
/* Read next block header, save info, leave position at start of data */
void
getblock(ft)
ft_t ft;
{
vs_t v = (vs_t) ft->priv;
unsigned char uc, block;
ULONG sblen;
LONG new_rate;
int i;
v->silent = 0;
while (v->rest == 0) {
if (feof(ft->fp))
return;
block = getc(ft->fp);
if (block == VOC_TERM)
return;
if (feof(ft->fp))
return;
/*
* Size is an 24-bit value. Currently there is no util
* func to read this so do it this cross-platform way
*
*/
uc = getc(ft->fp);
sblen = uc;
uc = getc(ft->fp);
sblen |= ((LONG) uc) << 8;
uc = getc(ft->fp);
sblen |= ((LONG) uc) << 16;
switch(block) {
case VOC_DATA:
uc = getc(ft->fp);
/* When DATA block preceeded by an EXTENDED */
/* block, the DATA blocks rate value is invalid */
if (!v->extended) {
if (uc == 0)
fail("File %s: Sample rate is zero?");
if ((v->rate != -1) && (uc != v->rate))
fail("File %s: sample rate codes differ: %d != %d",
ft->filename,v->rate, uc);
v->rate = uc;
ft->info.rate = 1000000.0/(256 - v->rate);
v->channels = 1;
}
uc = getc(ft->fp);
if (uc != 0)
fail("File %s: only interpret 8-bit data!",
ft->filename);
v->extended = 0;
v->rest = sblen - 2;
v->size = BYTE;
return;
case VOC_DATA_16:
new_rate = rlong(ft);
if (new_rate == 0)
fail("File %s: Sample rate is zero?",ft->filename);
if ((v->rate != -1) && (new_rate != v->rate))
fail("File %s: sample rate codes differ: %d != %d",
ft->filename, v->rate, new_rate);
v->rate = new_rate;
ft->info.rate = new_rate;
uc = getc(ft->fp);
switch (uc)
{
case 8: v->size = BYTE; break;
case 16: v->size = WORD; break;
default: fail("Don't understand size %d", uc);
}
v->channels = getc(ft->fp);
getc(ft->fp); /* unknown */
getc(ft->fp); /* notused */
getc(ft->fp); /* notused */
getc(ft->fp); /* notused */
getc(ft->fp); /* notused */
getc(ft->fp); /* notused */
v->rest = sblen - 12;
return;
case VOC_CONT:
v->rest = sblen;
return;
case VOC_SILENCE:
{
unsigned short period;
period = rshort(ft);
uc = getc(ft->fp);
if (uc == 0)
fail("File %s: Silence sample rate is zero");
/*
* 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);
else
v->rate = uc;
v->rest = period;
v->silent = 1;
return;
}
case VOC_MARKER:
uc = getc(ft->fp);
uc = getc(ft->fp);
/* Falling! Falling! */
case VOC_TEXT:
{
int i;
/* Could add to comment in SF? */
for(i = 0; i < sblen; i++)
getc(ft->fp);
}
continue; /* get next block */
case VOC_LOOP:
case VOC_LOOPEND:
report("File %s: skipping repeat loop");
for(i = 0; i < sblen; i++)
getc(ft->fp);
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;
new_rate = rshort(ft);
if (new_rate == 0)
fail("File %s: Sample rate is zero?");
if ((v->rate != -1) && (new_rate != v->rate))
fail("File %s: sample rate codes differ: %d != %d",
ft->filename, v->rate, new_rate);
v->rate = new_rate;
uc = getc(ft->fp);
if (uc != 0)
fail("File %s: only interpret 8-bit data!",
ft->filename);
uc = getc(ft->fp);
if (uc)
ft->info.channels = 2; /* Stereo */
/* Needed number of channels before finishing
compute for rate */
ft->info.rate = (256000000L/(65536L - v->rate))/ft->info.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:
report("File %s: skipping unknown block code %d",
ft->filename, block);
for(i = 0; i < sblen; i++)
getc(ft->fp);
}
}
}
/* Start an output block. */
void blockstart(ft)
ft_t ft;
{
vs_t v = (vs_t) ft->priv;
v->blockseek = ftell(ft->fp);
if (v->silent) {
putc(VOC_SILENCE, ft->fp); /* Silence block code */
putc(0, ft->fp); /* Period length */
putc(0, ft->fp); /* Period length */
putc((int) v->rate, ft->fp); /* Rate code */
} else {
if (ft->info.size == BYTE) {
/* 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->info.channels > 1) {
putc(VOC_EXTENDED, ft->fp); /* Voice Extended block code */
putc(4, ft->fp); /* block length = 4 */
putc(0, ft->fp); /* block length = 4 */
putc(0, ft->fp); /* block length = 4 */
v->rate = 65536L - (256000000.0/(2*(float)ft->info.rate));
wshort(ft,v->rate); /* Rate code */
putc(0, ft->fp); /* File is not packed */
putc(1, ft->fp); /* samples are in stereo */
}
putc(VOC_DATA, ft->fp); /* Voice Data block code */
putc(0, ft->fp); /* block length (for now) */
putc(0, ft->fp); /* block length (for now) */
putc(0, ft->fp); /* block length (for now) */
v->rate = 256 - (1000000.0/(float)ft->info.rate);
putc((int) v->rate, ft->fp);/* Rate code */
putc(0, ft->fp); /* 8-bit raw data */
} else {
putc(VOC_DATA_16, ft->fp); /* Voice Data block code */
putc(0, ft->fp); /* block length (for now) */
putc(0, ft->fp); /* block length (for now) */
putc(0, ft->fp); /* block length (for now) */
v->rate = ft->info.rate;
wlong(ft, v->rate); /* Rate code */
putc(16, ft->fp); /* Sample Size */
putc(ft->info.channels, ft->fp); /* Sample Size */
putc(0, ft->fp); /* Unknown */
putc(0, ft->fp); /* Unused */
putc(0, ft->fp); /* Unused */
putc(0, ft->fp); /* Unused */
putc(0, ft->fp); /* Unused */
putc(0, ft->fp); /* Unused */
}
}
}
/* End the current data or silence block. */
void blockstop(ft)
ft_t ft;
{
vs_t v = (vs_t) ft->priv;
LONG datum;
putc(0, ft->fp); /* End of file block code */
fseek(ft->fp, v->blockseek, 0); /* seek back to block length */
fseek(ft->fp, 1, 1); /* seek forward one */
if (v->silent) {
wshort(ft, v->samples);
} else {
if (ft->info.size == BYTE) {
if (ft->info.channels > 1) {
fseek(ft->fp, 8, 1); /* forward 7 + 1 for new block header */
}
}
v->samples += 2; /* adjustment: SBDK pp. 3-5 */
datum = (v->samples) & 0xff;
putc((int)datum, ft->fp); /* low byte of length */
datum = (v->samples >> 8) & 0xff;
putc((int)datum, ft->fp); /* middle byte of length */
datum = (v->samples >> 16) & 0xff;
putc((int)datum, ft->fp); /* high byte of length */
}
}