Torque3D/Engine/lib/libsndfile/src/voc.c
marauder2k7 700bf32a2a Update minimum requirements and libs
This updates the minimum required cmake version and the libs that have updates for this.
Ogg updated to master as of 20052025
Libsndfile updated to master as of 20052025
Opus minimum cmake version changed
vorbis minimum cmake version changed
2025-05-20 10:09:42 +01:00

883 lines
26 KiB
C

/*
** Copyright (C) 2001-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation; either version 2.1 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* RANT:
** The VOC file format is the most brain damaged format I have yet had to deal
** with. No one programmer could have bee stupid enough to put this together.
** Instead it looks like a series of manic, dyslexic assembly language programmers
** hacked it to fit their needs.
** Utterly woeful.
*/
#include "sfconfig.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sndfile.h"
#include "sfendian.h"
#include "common.h"
/*------------------------------------------------------------------------------
* Typedefs for file chunks.
*/
#define VOC_MAX_SECTIONS 200
enum
{ VOC_TERMINATOR = 0,
VOC_SOUND_DATA = 1,
VOC_SOUND_CONTINUE = 2,
VOC_SILENCE = 3,
VOC_MARKER = 4,
VOC_ASCII = 5,
VOC_REPEAT = 6,
VOC_END_REPEAT = 7,
VOC_EXTENDED = 8,
VOC_EXTENDED_II = 9
} ;
typedef struct
{ int samples ;
int offset ; /* Offset of zero => silence. */
} SND_DATA_BLOCKS ;
typedef struct
{ unsigned int sections, section_types ;
int samplerate, channels, bitwidth ;
SND_DATA_BLOCKS blocks [VOC_MAX_SECTIONS] ;
} VOC_DATA ;
/*------------------------------------------------------------------------------
* Private static functions.
*/
static int voc_close (SF_PRIVATE *psf) ;
static int voc_write_header (SF_PRIVATE *psf, int calc_length) ;
static int voc_read_header (SF_PRIVATE *psf) ;
static const char* voc_encoding2str (int encoding) ;
#if 0
/* These functions would be required for files with more than one VOC_SOUND_DATA
** segment. Not sure whether to bother implementing this.
*/
static int voc_multi_init (SF_PRIVATE *psf, VOC_DATA *pvoc) ;
static int voc_multi_read_uc2s (SF_PRIVATE *psf, short *ptr, int len) ;
static int voc_multi_read_les2s (SF_PRIVATE *psf, short *ptr, int len) ;
static int voc_multi_read_uc2i (SF_PRIVATE *psf, int *ptr, int len) ;
static int voc_multi_read_les2i (SF_PRIVATE *psf, int *ptr, int len) ;
static int voc_multi_read_uc2f (SF_PRIVATE *psf, float *ptr, int len) ;
static int voc_multi_read_les2f (SF_PRIVATE *psf, float *ptr, int len) ;
static int voc_multi_read_uc2d (SF_PRIVATE *psf, double *ptr, int len) ;
static int voc_multi_read_les2d (SF_PRIVATE *psf, double *ptr, int len) ;
#endif
/*------------------------------------------------------------------------------
** Public function.
*/
int
voc_open (SF_PRIVATE *psf)
{ int subformat, error = 0 ;
if (psf->is_pipe)
return SFE_VOC_NO_PIPE ;
if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
{ if ((error = voc_read_header (psf)))
return error ;
} ;
subformat = SF_CODEC (psf->sf.format) ;
if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
{ if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_VOC)
return SFE_BAD_OPEN_FORMAT ;
psf->endian = SF_ENDIAN_LITTLE ;
if ((error = voc_write_header (psf, SF_FALSE)))
return error ;
psf->write_header = voc_write_header ;
} ;
psf->blockwidth = psf->bytewidth * psf->sf.channels ;
psf->container_close = voc_close ;
switch (subformat)
{ case SF_FORMAT_PCM_U8 :
case SF_FORMAT_PCM_16 :
error = pcm_init (psf) ;
break ;
case SF_FORMAT_ALAW :
error = alaw_init (psf) ;
break ;
case SF_FORMAT_ULAW :
error = ulaw_init (psf) ;
break ;
default : return SFE_UNIMPLEMENTED ;
} ;
return error ;
} /* voc_open */
/*------------------------------------------------------------------------------
*/
static int
voc_read_header (SF_PRIVATE *psf)
{ VOC_DATA *pvoc ;
char creative [20] ;
unsigned char block_type, rate_byte ;
short version, checksum, encoding, dataoffset ;
int offset ;
/* Set position to start of file to begin reading header. */
offset = psf_binheader_readf (psf, "pb", 0, creative, SIGNED_SIZEOF (creative)) ;
if (creative [sizeof (creative) - 1] != 0x1A)
return SFE_VOC_NO_CREATIVE ;
/* Terminate the string. */
creative [sizeof (creative) - 1] = 0 ;
if (strcmp ("Creative Voice File", creative))
return SFE_VOC_NO_CREATIVE ;
psf_log_printf (psf, "%s\n", creative) ;
offset += psf_binheader_readf (psf, "e222", &dataoffset, &version, &checksum) ;
psf->dataoffset = dataoffset ;
psf_log_printf (psf, "dataoffset : %d\n"
"version : 0x%X\n"
"checksum : 0x%X\n", psf->dataoffset, version, checksum) ;
if (version != 0x010A && version != 0x0114)
return SFE_VOC_BAD_VERSION ;
if (! (psf->codec_data = malloc (sizeof (VOC_DATA))))
return SFE_MALLOC_FAILED ;
pvoc = (VOC_DATA*) psf->codec_data ;
memset (pvoc, 0, sizeof (VOC_DATA)) ;
/* Set the default encoding now. */
psf->sf.format = SF_FORMAT_VOC ; /* Major format */
encoding = SF_FORMAT_PCM_U8 ; /* Minor format */
psf->endian = SF_ENDIAN_LITTLE ;
while (1)
{ char header [256] ;
unsigned size ;
short count ;
block_type = 0 ;
offset += psf_binheader_readf (psf, "1", &block_type) ;
switch (block_type)
{ case VOC_ASCII :
offset += psf_binheader_readf (psf, "e3", &size) ;
psf_log_printf (psf, " ASCII : %d\n", size) ;
if (size < sizeof (header) - 1)
{ offset += psf_binheader_readf (psf, "b", header, size) ;
header [size] = 0 ;
psf_log_printf (psf, " text : %s\n", header) ;
continue ;
}
offset += psf_binheader_readf (psf, "j", size) ;
continue ;
case VOC_REPEAT :
offset += psf_binheader_readf (psf, "e32", &size, &count) ;
psf_log_printf (psf, " Repeat : %d\n", count) ;
continue ;
case VOC_SOUND_DATA :
case VOC_EXTENDED :
case VOC_EXTENDED_II :
break ;
default : psf_log_printf (psf, "*** Weird block marker (%d)\n", block_type) ;
} ;
break ;
} ;
if (block_type == VOC_SOUND_DATA)
{ unsigned char compression ;
int size ;
offset += psf_binheader_readf (psf, "e311", &size, &rate_byte, &compression) ;
psf->sf.samplerate = 1000000 / (256 - (rate_byte & 0xFF)) ;
psf_log_printf (psf, " Sound Data : %d\n sr : %d => %dHz\n comp : %d\n",
size, rate_byte, psf->sf.samplerate, compression) ;
if (offset + size - 1 > psf->filelength)
{ psf_log_printf (psf, "Seems to be a truncated file.\n") ;
psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ;
return SFE_VOC_BAD_SECTIONS ;
}
else if (psf->filelength - offset - size > 4)
{ psf_log_printf (psf, "Seems to be a multi-segment file (#1).\n") ;
psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ;
return SFE_VOC_BAD_SECTIONS ;
} ;
psf->dataoffset = offset ;
psf->dataend = psf->filelength - 1 ;
psf->sf.channels = 1 ;
psf->bytewidth = 1 ;
psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ;
return 0 ;
} ;
if (block_type == VOC_EXTENDED)
{ unsigned char pack, stereo, compression ;
unsigned short rate_short ;
int size ;
offset += psf_binheader_readf (psf, "e3211", &size, &rate_short, &pack, &stereo) ;
psf_log_printf (psf, " Extended : %d\n", size) ;
if (size == 4)
psf_log_printf (psf, " size : 4\n") ;
else
psf_log_printf (psf, " size : %d (should be 4)\n", size) ;
psf_log_printf (psf, " pack : %d\n"
" stereo : %s\n", pack, (stereo ? "yes" : "no")) ;
if (stereo)
{ psf->sf.channels = 2 ;
psf->sf.samplerate = 128000000 / (65536 - rate_short) ;
}
else
{ psf->sf.channels = 1 ;
psf->sf.samplerate = 256000000 / (65536 - rate_short) ;
} ;
psf_log_printf (psf, " sr : %d => %dHz\n", (rate_short & 0xFFFF), psf->sf.samplerate) ;
offset += psf_binheader_readf (psf, "1", &block_type) ;
if (block_type != VOC_SOUND_DATA)
{ psf_log_printf (psf, "*** Expecting VOC_SOUND_DATA section.\n") ;
return SFE_VOC_BAD_FORMAT ;
} ;
offset += psf_binheader_readf (psf, "e311", &size, &rate_byte, &compression) ;
psf_log_printf (psf, " Sound Data : %d\n"
" sr : %d\n"
" comp : %d\n", size, rate_byte, compression) ;
if (offset + size - 1 > psf->filelength)
{ psf_log_printf (psf, "Seems to be a truncated file.\n") ;
psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ;
return SFE_VOC_BAD_SECTIONS ;
}
else if (offset + size - 1 < psf->filelength)
{ psf_log_printf (psf, "Seems to be a multi-segment file (#2).\n") ;
psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ;
return SFE_VOC_BAD_SECTIONS ;
} ;
psf->dataoffset = offset ;
psf->dataend = psf->filelength - 1 ;
psf->bytewidth = 1 ;
psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ;
return 0 ;
}
if (block_type == VOC_EXTENDED_II)
{ unsigned char bitwidth, channels ;
int size, fourbytes ;
offset += psf_binheader_readf (psf, "e341124", &size, &psf->sf.samplerate,
&bitwidth, &channels, &encoding, &fourbytes) ;
if (size * 2 == psf->filelength - 39)
{ int temp_size = psf->filelength - 31 ;
psf_log_printf (psf, " Extended II : %d (SoX bug: should be %d)\n", size, temp_size) ;
size = temp_size ;
}
else
psf_log_printf (psf, " Extended II : %d\n", size) ;
psf_log_printf (psf, " sample rate : %d\n"
" bit width : %d\n"
" channels : %d\n", psf->sf.samplerate, bitwidth, channels) ;
if (bitwidth == 16 && encoding == 0)
{ encoding = 4 ;
psf_log_printf (psf, " encoding : 0 (SoX bug: should be 4 for 16 bit signed PCM)\n") ;
}
else
psf_log_printf (psf, " encoding : %d => %s\n", encoding, voc_encoding2str (encoding)) ;
psf_log_printf (psf, " fourbytes : %X\n", fourbytes) ;
psf->sf.channels = channels ;
psf->dataoffset = offset ;
psf->dataend = psf->filelength - 1 ;
if (size + 31 == psf->filelength + 1)
{ /* Hack for reading files produced using
** sf_command (SFC_UPDATE_HEADER_NOW).
*/
psf_log_printf (psf, "Missing zero byte at end of file.\n") ;
size = psf->filelength - 30 ;
psf->dataend = 0 ;
}
else if (size + 31 > psf->filelength)
{ psf_log_printf (psf, "Seems to be a truncated file.\n") ;
size = psf->filelength - 31 ;
}
else if (size + 31 < psf->filelength)
psf_log_printf (psf, "Seems to be a multi-segment file (#3).\n") ;
switch (encoding)
{ case 0 :
psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ;
psf->bytewidth = 1 ;
break ;
case 4 :
psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_16 ;
psf->bytewidth = 2 ;
break ;
case 6 :
psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_ALAW ;
psf->bytewidth = 1 ;
break ;
case 7 :
psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_ULAW ;
psf->bytewidth = 1 ;
break ;
default : /* Unknown */
return SFE_VOC_BAD_FORMAT ;
break ;
} ;
} ;
return 0 ;
} /* voc_read_header */
/*====================================================================================
*/
static int
voc_write_header (SF_PRIVATE *psf, int calc_length)
{ sf_count_t current ;
int rate_const, subformat ;
current = psf_ftell (psf) ;
if (calc_length)
{ psf->filelength = psf_get_filelen (psf) ;
psf->datalength = psf->filelength - psf->dataoffset ;
if (psf->dataend)
psf->datalength -= psf->filelength - psf->dataend ;
psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
} ;
subformat = SF_CODEC (psf->sf.format) ;
/* Reset the current header length to zero. */
psf->header.ptr [0] = 0 ;
psf->header.indx = 0 ;
psf_fseek (psf, 0, SEEK_SET) ;
/* VOC marker and 0x1A byte. */
psf_binheader_writef (psf, "eb1", BHWv ("Creative Voice File"), BHWz (19), BHW1 (0x1A)) ;
/* Data offset, version and other. */
psf_binheader_writef (psf, "e222", BHW2 (26), BHW2 (0x0114), BHW2 (0x111F)) ;
/* Use same logic as SOX.
** If the file is mono 8 bit data, use VOC_SOUND_DATA.
** If the file is mono 16 bit data, use VOC_EXTENED.
** Otherwise use VOC_EXTENED_2.
*/
if (subformat == SF_FORMAT_PCM_U8 && psf->sf.channels == 1)
{ /* samplerate = 1000000 / (256 - rate_const) ; */
rate_const = 256 - 1000000 / psf->sf.samplerate ;
/* First type marker, length, rate_const and compression */
psf_binheader_writef (psf, "e1311", BHW1 (VOC_SOUND_DATA), BHW3 ((int) (psf->datalength + 1)), BHW1 (rate_const), BHW1 (0)) ;
}
else if (subformat == SF_FORMAT_PCM_U8 && psf->sf.channels == 2)
{ /* sample_rate = 128000000 / (65536 - rate_short) ; */
rate_const = 65536 - 128000000 / psf->sf.samplerate ;
/* First write the VOC_EXTENDED section
** marker, length, rate_const and compression
*/
psf_binheader_writef (psf, "e13211", BHW1 (VOC_EXTENDED), BHW3 (4), BHW2 (rate_const), BHW1 (0), BHW1 (1)) ;
/* samplerate = 1000000 / (256 - rate_const) ; */
rate_const = 256 - 1000000 / psf->sf.samplerate ;
/* Now write the VOC_SOUND_DATA section
** marker, length, rate_const and compression
*/
psf_binheader_writef (psf, "e1311", BHW1 (VOC_SOUND_DATA), BHW3 ((int) (psf->datalength + 1)), BHW1 (rate_const), BHW1 (0)) ;
}
else
{ int length ;
if (psf->sf.channels < 1 || psf->sf.channels > 2)
return SFE_CHANNEL_COUNT ;
switch (subformat)
{ case SF_FORMAT_PCM_U8 :
psf->bytewidth = 1 ;
length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ;
/* Marker, length, sample rate, bitwidth, stereo flag, encoding and fourt zero bytes. */
psf_binheader_writef (psf, "e1341124", BHW1 (VOC_EXTENDED_II), BHW3 (length), BHW4 (psf->sf.samplerate), BHW1 (16), BHW1 (psf->sf.channels), BHW2 (4), BHW4 (0)) ;
break ;
case SF_FORMAT_PCM_16 :
psf->bytewidth = 2 ;
length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ;
/* Marker, length, sample rate, bitwidth, stereo flag, encoding and fourt zero bytes. */
psf_binheader_writef (psf, "e1341124", BHW1 (VOC_EXTENDED_II), BHW3 (length), BHW4 (psf->sf.samplerate), BHW1 (16), BHW1 (psf->sf.channels), BHW2 (4), BHW4 (0)) ;
break ;
case SF_FORMAT_ALAW :
psf->bytewidth = 1 ;
length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ;
psf_binheader_writef (psf, "e1341124", BHW1 (VOC_EXTENDED_II), BHW3 (length), BHW4 (psf->sf.samplerate), BHW1 (8), BHW1 (psf->sf.channels), BHW2 (6), BHW4 (0)) ;
break ;
case SF_FORMAT_ULAW :
psf->bytewidth = 1 ;
length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ;
psf_binheader_writef (psf, "e1341124", BHW1 (VOC_EXTENDED_II), BHW3 (length), BHW4 (psf->sf.samplerate), BHW1 (8), BHW1 (psf->sf.channels), BHW2 (7), BHW4 (0)) ;
break ;
default : return SFE_UNIMPLEMENTED ;
} ;
} ;
psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
if (psf->error)
return psf->error ;
psf->dataoffset = psf->header.indx ;
if (current > 0)
psf_fseek (psf, current, SEEK_SET) ;
return psf->error ;
} /* voc_write_header */
static int
voc_close (SF_PRIVATE *psf)
{
if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
{ /* Now we know for certain the length of the file we can re-write
** correct values for the FORM, 8SVX and BODY chunks.
*/
unsigned char byte = VOC_TERMINATOR ;
psf_fseek (psf, 0, SEEK_END) ;
/* Write terminator */
psf_fwrite (&byte, 1, 1, psf) ;
voc_write_header (psf, SF_TRUE) ;
} ;
return 0 ;
} /* voc_close */
static const char*
voc_encoding2str (int encoding)
{
switch (encoding)
{ case 0 : return "8 bit unsigned PCM" ;
case 4 : return "16 bit signed PCM" ;
case 6 : return "A-law" ;
case 7 : return "u-law" ;
default : break ;
}
return "*** Unknown ***" ;
} /* voc_encoding2str */
/*====================================================================================
*/
#if 0
static int
voc_multi_init (SF_PRIVATE *psf, VOC_DATA *pvoc)
{
psf->sf.frames = 0 ;
if (pvoc->bitwidth == 8)
{ psf->read_short = voc_multi_read_uc2s ;
psf->read_int = voc_multi_read_uc2i ;
psf->read_float = voc_multi_read_uc2f ;
psf->read_double = voc_multi_read_uc2d ;
return 0 ;
} ;
if (pvoc->bitwidth == 16)
{ psf->read_short = voc_multi_read_les2s ;
psf->read_int = voc_multi_read_les2i ;
psf->read_float = voc_multi_read_les2f ;
psf->read_double = voc_multi_read_les2d ;
return 0 ;
} ;
psf_log_printf (psf, "Error : bitwith != 8 && bitwidth != 16.\n") ;
return SFE_UNIMPLEMENTED ;
} /* voc_multi_read_int */
/*------------------------------------------------------------------------------------
*/
static int
voc_multi_read_uc2s (SF_PRIVATE *psf, short *ptr, int len)
{
return 0 ;
} /* voc_multi_read_uc2s */
static int
voc_multi_read_les2s (SF_PRIVATE *psf, short *ptr, int len)
{
return 0 ;
} /* voc_multi_read_les2s */
static int
voc_multi_read_uc2i (SF_PRIVATE *psf, int *ptr, int len)
{
return 0 ;
} /* voc_multi_read_uc2i */
static int
voc_multi_read_les2i (SF_PRIVATE *psf, int *ptr, int len)
{
return 0 ;
} /* voc_multi_read_les2i */
static int
voc_multi_read_uc2f (SF_PRIVATE *psf, float *ptr, int len)
{
return 0 ;
} /* voc_multi_read_uc2f */
static int
voc_multi_read_les2f (SF_PRIVATE *psf, float *ptr, int len)
{
return 0 ;
} /* voc_multi_read_les2f */
static int
voc_multi_read_uc2d (SF_PRIVATE *psf, double *ptr, int len)
{
return 0 ;
} /* voc_multi_read_uc2d */
static int
voc_multi_read_les2d (SF_PRIVATE *psf, double *ptr, int len)
{
return 0 ;
} /* voc_multi_read_les2d */
#endif
/*------------------------------------------------------------------------------------
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 1'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:
---------------------
00 Sample Rate
01 Compression Type
02+ Voice Data
**Silence Info Format:
----------------------------
00-01 Length of silence - 1
02 Sample Rate
***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]
---------------------------------------------------------------------------------
Addendum submitted by Votis Kokavessis:
After some experimenting with .VOC files I found out that there is a Data Block
Type 9, which is not covered in the VOC.TXT file. Here is what I was able to discover
about this block type:
TYPE: 09
SIZE: 12 + length of data
INFO: 12 (twelve) bytes
INFO STRUCTURE:
Bytes 0-1: (Word) Sample Rate (e.g. 44100)
Bytes 2-3: zero (could be that bytes 0-3 are a DWord for Sample Rate)
Byte 4: Sample Size in bits (e.g. 16)
Byte 5: Number of channels (e.g. 1 for mono, 2 for stereo)
Byte 6: Unknown (equal to 4 in all files I examined)
Bytes 7-11: zero
-------------------------------------------------------------------------------------*/
/*=====================================================================================
**=====================================================================================
**=====================================================================================
**=====================================================================================
*/
/*------------------------------------------------------------------------
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 precede 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
------------------------------------------------------------------------*/