[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