ref: e32b9fd834d00654bf973fa435aa8dce624edf90
dir: /src/vox.c/
/************************************************************************
* SOX *
* *
* AUDIO FILE PROCESSING UTILITY *
* *
* Project : SOX *
* File : vox.c *
* Version : V12.17.4 *
* *
* Version History : V12.17.4 - Tony Seebregts *
* 5 May 2004 *
* 1. Original *
* *
* Description : SOX file format handler for Dialogic/Oki ADPCM VOX *
* files. *
* *
* Notes : 1. Based on the vox/devox code samples at: *
* *
* http://www.cis.ksu.edu/~tim/vox *
* *
* 2. Coded from SOX skeleton code supplied with SOX source. *
* *
* 3. Tested under: *
* - Windows 2000 SP3/Visual C++ V6.0 *
* - Windows 2000 SP3/Digital Mars V7.51 *
* *
************************************************************************/
/************************************************************************
* July 5, 1991 *
* *
* 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. *
* *
************************************************************************/
#include "st_i.h"
typedef struct voxstuff { struct { short last; /* ADPCM codec state */
short index;
} state;
struct { uint8_t byte; /* write store */
uint8_t flag;
} store;
st_fileinfo_t file;
} *vox_t;
static short STEPSIZE[49] = { 16, 17, 19, 21, 23, 25, 28,
31, 34, 37, 41, 45, 50, 55,
60, 66, 73, 80, 88, 97, 107,
118, 130, 143, 157, 173, 190, 209,
230, 253, 279, 307, 337, 371, 408,
449, 494, 544, 598, 658, 724, 796,
876, 963, 1060,1166,1282,1411,1552
};
static short STEPADJUST[8] = { -1,-1,-1,-1,2,4,6,8 };
static uint8_t envox (short, vox_t);
static short devox (uint8_t,vox_t);
/******************************************************************************
* Function : st_voxstartread
* Description: Initialises the file parameters and ADPCM codec state.
* Parameters : ft - file info structure
* Returns : int - ST_SUCCESS
* ST_EOF
* Exceptions :
* Notes : 1. VOX file format is 4-bit OKI ADPCM that decodes to
* to 12 bit signed linear PCM.
* 2. Dialogic only supports 6kHz, 8kHz and 11 kHz sampling
* rates but the codecs allows any user specified rate.
******************************************************************************/
static int st_voxstartread (ft_t ft)
{ vox_t state = (vox_t) ft->priv;
/* ... setup file info */
state->file.buf = (char *)malloc(ST_BUFSIZ);
if (!state->file.buf)
{ st_fail_errno (ft,ST_ENOMEM,"Unable to allocate internal buffer memory");
return(ST_EOF);
}
state->file.size = ST_BUFSIZ;
state->file.count = 0;
state->file.pos = 0;
state->file.eof = 0;
ft->info.size = ST_SIZE_WORD;
ft->info.encoding = ST_ENCODING_OKI_ADPCM;
ft->info.channels = 1;
/* ... initialise CODEC state */
state->state.last = 0;
state->state.index = 0;
state->store.byte = 0;
state->store.flag = 0;
return (ST_SUCCESS);
}
/******************************************************************************
* Function : st_voxread
* Description: Fills an internal buffer from the VOX file, converts the
* OKI ADPCM 4-bit samples to 12-bit signed PCM and then scales
* the samples to full range 16 bit PCM.
* Parameters : ft - file info structure
* buffer - output buffer
* length - size of output buffer
* Returns : int - number of samples returned in buffer
* Exceptions :
* Notes :
******************************************************************************/
static st_size_t st_voxread (ft_t ft,st_sample_t *buffer,st_size_t length)
{ vox_t state = (vox_t) ft->priv;
int count = 0;
int N;
uint8_t byte;
short word;
/* ... round length down to nearest even number */
N = length/2;
N *=2;
/* ... loop until buffer full or EOF */
while (count < N)
{ /* ... refill buffer */
if (state->file.pos >= state->file.count)
{ state->file.count = st_readbuf (ft,state->file.buf,1,state->file.size);
state->file.pos = 0;
if (state->file.count == 0)
break;
}
/* ... decode two nybbles stored as a byte */
byte = state->file.buf[state->file.pos++];
word = devox ((uint8_t) ((byte >> 4) & 0x0F),state);
*buffer++ = ST_SIGNED_WORD_TO_SAMPLE (word * 16,);
word = devox ((uint8_t) (byte & 0x0F),state);
*buffer++ = ST_SIGNED_WORD_TO_SAMPLE (word * 16,);
count += 2;
}
return count;
}
/******************************************************************************
* Function : st_voxstopread
* Description: Frees the internal buffer allocated in st_voxstartread.
* Parameters : ft - file info structure
* Returns : int - ST_SUCCESS
* Exceptions :
* Notes :
******************************************************************************/
static int st_voxstopread (ft_t ft)
{ vox_t state = (vox_t) ft->priv;
free (state->file.buf);
return (ST_SUCCESS);
}
/******************************************************************************
* Function : st_voxstartwrite
* Description: Initialises the file parameters and ADPCM codec state.
* Parameters : ft - file info structure
* Returns : int - ST_SUCCESS
* ST_EOF
* Exceptions :
* Notes : 1. VOX file format is 4-bit OKI ADPCM that decodes to
* to 12 bit signed linear PCM.
* 2. Dialogic only supports 6kHz, 8kHz and 11 kHz sampling
* rates but the codecs allows any user specified rate.
******************************************************************************/
static int st_voxstartwrite (ft_t ft)
{ vox_t state = (vox_t) ft->priv;
/* ... setup file info */
state->file.buf = (char *)malloc(ST_BUFSIZ);
if (!state->file.buf)
{ st_fail_errno (ft,ST_ENOMEM,"Unable to allocate internal buffer memory");
return(ST_EOF);
}
state->file.size = ST_BUFSIZ;
state->file.count = 0;
state->file.pos = 0;
state->file.eof = 0;
ft->info.size = ST_SIZE_WORD;
ft->info.encoding = ST_ENCODING_OKI_ADPCM;
ft->info.channels = 1;
/* ... initialise CODEC state */
state->state.last = 0;
state->state.index = 0;
state->store.byte = 0;
state->store.flag = 0;
return (ST_SUCCESS);
}
/******************************************************************************
* Function : st_voxwrite
* Description: Converts the supplied buffer to 12 bit linear PCM and encodes
* to OKI ADPCM 4-bit samples (packed a two nibbles per byte).
* Parameters : ft - file info structure
* buffer - output buffer
* length - size of output buffer
* Returns : int - ST_SUCCESS
* ST_EOF
* Exceptions :
* Notes :
******************************************************************************/
static st_size_t st_voxwrite (ft_t ft,const st_sample_t *buffer,st_size_t length)
{ vox_t state = (vox_t) ft->priv;
st_size_t count = 0;
uint8_t byte = state->store.byte;
uint8_t flag = state->store.flag;
short word;
while (count < length)
{ word = ST_SAMPLE_TO_SIGNED_WORD (*buffer++, ft->clippedCount);
word /= 16;
byte <<= 4;
byte |= envox (word,state) & 0x0F;
flag++;
flag %= 2;
if (flag == 0)
{ state->file.buf[state->file.count++] = byte;
if (state->file.count >= state->file.size)
{ st_writebuf (ft,state->file.buf,1,state->file.count);
state->file.count = 0;
}
}
count++;
}
/* ... keep last byte across calls */
state->store.byte = byte;
state->store.flag = flag;
return (count);
}
/******************************************************************************
* Function : st_voxstopwrite
* Description: Flushes any leftover samples and frees the internal buffer
* allocated in st_voxstartwrite.
* Parameters : ft - file info structure
* Returns : int - ST_SUCCESS
* Exceptions :
* Notes :
******************************************************************************/
static int st_voxstopwrite (ft_t ft)
{ vox_t state = (vox_t) ft->priv;
uint8_t byte = state->store.byte;
uint8_t flag = state->store.flag;
/* ... flush remaining samples */
if (flag != 0)
{ byte <<= 4;
byte |= envox (0,state) & 0x0F;
state->file.buf[state->file.count++] = byte;
}
if (state->file.count > 0)
st_writebuf (ft,state->file.buf,1,state->file.count);
free (state->file.buf);
return (ST_SUCCESS);
}
/******************************************************************************
* Function : envox
* Description: Internal utility routine to encode 12 bit signed PCM to
* OKI ADPCM code
* Parameters : sample - 12 bit linear PCM sample
* state - CODEC state
* Returns : uint8_t - ADPCM nibble (in low order nibble)
* Exceptions :
* Notes :
******************************************************************************/
static uint8_t envox (short sample,vox_t state)
{ uint8_t code;
short dn;
short ss;
ss = STEPSIZE[state->state.index];
code = 0x00;
if ((dn = sample - state->state.last) < 0)
{ code = 0x08;
dn = -dn;
}
if (dn >= ss)
{ code = code | 0x04;
dn -= ss;
}
if (dn >= ss/2)
{ code = code | 0x02;
dn -= ss/2;
}
if (dn >= ss/4)
{ code = code | 0x01;
}
/* ... use decoder to set the estimate of last sample and
adjust the step index */
state->state.last = devox (code,state);
return (code);
}
/******************************************************************************
* Function : devox
* Description: Internal utility routine to decode OKI ADPCM 4-bit samples to
* 12-bit signed PCM.
* Parameters : code - ADPCM code (nibble)
* state - CODEC state
* Returns : short - 12 bit signed PCM sample
* Exceptions :
* Notes :
******************************************************************************/
static short devox (uint8_t code,vox_t state)
{ short dn;
short ss;
short sample;
ss = STEPSIZE[state->state.index];
dn = ss/8;
if (code & 0x01)
dn += ss/4;
if (code & 0x02)
dn += ss/2;
if (code & 0x04)
dn += ss;
if (code & 0x08)
dn = -dn;
sample = state->state.last + dn;
/* ... clip to 12 bits */
if (sample > 2047)
sample = 2047;
if (sample < -2048)
sample = -2048;
/* ... adjust step size */
state->state.last = sample;
state->state.index += STEPADJUST[code & 0x07];
if (state->state.index < 0)
state->state.index = 0;
if (state->state.index > 48)
state->state.index = 48;
/* ... done */
return (sample);
}
static const char *voxnames[] = {
"vox",
NULL
};
static st_format_t st_vox_format = {
voxnames,
NULL,
0,
st_voxstartread,
st_voxread,
st_voxstopread,
st_voxstartwrite,
st_voxwrite,
st_voxstopwrite,
st_format_nothing_seek
};
const st_format_t *st_vox_format_fn(void)
{
return &st_vox_format;
}