[sdiy] Continuously Variable Slope Delta (CVSD)
Jason Champion
jchampion at dsci-usa.com
Mon Nov 27 20:55:12 CET 2006
I'm working on some C code for encoding/decoding CVSD. I seem to be
close, but not quite right. All the information I've found assumes it's
being done in hardware rather than software, so I may have made some
assumptions I shouldn't have. As it is, the code runs, but the encoded
audio is too static-filled to be intelligible. I know CVSD doesn't
sound very good, but at 44kbps I ought to at least be able to make out
something.
Any pointers, tips, documentation, or observations would be
appreciated. If you have or know of a better way to do this I'd be
happy to listen.
Thank You,
Jason Champion
Here's what I have so far:
#define CVSD_SIGNIFICANT_BITS 3
#define DELTA_MIN 1.0f
#define STEP 0.35f
#define ALPHA 0.98758f /* aplha = e^(-1/(fs*TC) TC=5ms */
#define ACC_ALPHA 0.93068f /* aplha = e^(-1/(fs*TC) TC=0.87ms */
struct CVSDStatic
{
int init;
int shiftRegister;
int index;
float delta;
float Acc;
float LPx[CVSD_SIGNIFICANT_BITS];
float LPy[CVSD_SIGNIFICANT_BITS];
};
void ConvertPCMtoCVSD ( CVSDConversionData *PCMBlock, CVSDConversionData
*CVSDBits, CVSDStatic *ds )
{
if( PCMBlock == 0 || CVSDBits == 0 || ds == 0 )
{
return;
}
int j;
unsigned int BitCount;
short sample;
float sum;
short *ptr;
int saveIndex;
int temp_bit = 0; /* is the current bit */
static float *BPa = (float *)BPal;
static float *BPb = (float *)BPbl;
if (ds->init)
{
ds->init = 0;
ds->shiftRegister = 1;
ds->index = 0;
ds->delta = DELTA_MIN;
ds->Acc = 0.0;
ds->LPx[0] = 0;
ds->LPx[1] = 0;
ds->LPx[2] = 0;
ds->LPy[0] = 0;
ds->LPy[1] = 0;
ds->LPy[2] = 0;
}
// Match number of CVSD samples (bits) to number of PCM samples
(shorts).
CVSDBits->samples = PCMBlock->samples;
// Length does not equate to samples. CVSD generally has one bit
per sample
// while PCM has 8 or 16 bits per sample.
CVSDBits->bitLength = PCMBlock->samples;
// Here we add a bit to CVSD bits if we somehow have a partial
sample in the PCM block.
// Instead of doing this we should throw an error.
if ( PCMBlock->bitLength % 16 >= 1 )
{
++(CVSDBits->bitLength);
}
// We use shorts because we're dealing with 16-bit PCM data
// If we wanted to be fancy, we could allow both 8 and 16 bit. Since we
// use 16-bit internally, we won't bother.
ptr = (short *)&PCMBlock->data;
// Iterate through each sample in the PCM block.
for(BitCount = 0; BitCount < PCMBlock->samples; BitCount++)
{
sample = (*ptr++);
// perform bandpass filter with 50 Hz and 4 Khz 3 dB points
// perform output lowpass filter operation
saveIndex = ds->index;
BPx[ds->index] = (float)sample;
sum = BPb[0] * BPx[ds->index];
for (j = 1; j < FILTER_N_1; j++)
{
ds->index = (ds->index + 1) % FILTER_N_1;
sum += (BPb[j] * BPx[ds->index]) - (BPa[j] * BPy[ds->index]);
}
BPy[saveIndex] = sum;
// Compare Accumulator estimate with output of filter
if(ds->Acc <= sum)
{
temp_bit = 1;
}
else
{
temp_bit = 0;
}
// Discard the oldest bit and tack on the new bit, masking to
keep only the three most recent bits.
ds->shiftRegister = ((ds->shiftRegister << 1) | temp_bit) & 0x7;
// Check to see if we have had 3 bits alike and if so, increase
the delta by step
if ((ds->shiftRegister == 7) || (ds->shiftRegister == 0))
{
ds->delta = (ALPHA * ds->delta) + STEP;
}
// Else bits are different so no change to step size
else
{
ds->delta = (ALPHA * ds->delta);
}
// Update the accumulator so we can check errors.
if(temp_bit == 1)
{
ds->Acc = ( ACC_ALPHA * ds->Acc ) + ( ds->delta + DELTA_MIN );
}
else
{
ds->Acc = ( ACC_ALPHA * ds->Acc ) - ( ds->delta + DELTA_MIN );
}
SetBit((CVSDBits->samples), CVSDBits->data, BitCount, temp_bit);
}
}
More information about the Synth-diy
mailing list