Way to encode 8-bit per sample audio into 2-bits per sample

1.8k views Asked by At

If ADPCM can store 16-bit per sample audio into 4-bit per sample, is there a way to store 8-bit per sample audio into 2-bit per sample?

3

There are 3 answers

4
Oak Bytes On

As far I know, ADPCM compression standard needs 4-bits per sample even if the original uncompressed audio has 8-bit audio samples. Hence there is NO way to encode audio using 2-bit per sample with ADPCM.

EDIT: I am specifically referring to G.726, which is one of the widely supported speech compression standard in WAV. Personally, I am not aware of freely available G.727 codec. FFMPEG is one of the libraries with extensive support for audio codecs. You can see the audio codec list supported by them at https://www.ffmpeg.org/general.html#Audio-Codecs. In the list, I do see support for other ADPCM formats, which may be worth exploring.

0
joeforker On

The G.726 standard supercedes G.721 and G.723 into a single standard, and adds 2-bit ADPCM to the 3- 4- and 5-bit modes from older standards. These are all very simple and fast to encode/decode. There appears to be no file format for the 2-bit version, but there is a widely re-used open source Sun library to encode/decode the formats; SpanDSP is just one library that includes the Sun code. These take 16-bit samples as input but it is trivial to convert 8-bit to 16-bit.

If you want to hear the 2-bit mode you may have to write your own converter that calls into the library.

There's also ADPCM specifications from long ago like "ADPCM Creative Technology" that support low bit rates and sample sizes.

See also the Sox documentation about various old compression schemes.

The number of bits per sample is not strictly related to the dynamic range or number of bits in the output. For example, the https://en.wikipedia.org/wiki/Direct_Stream_Digital format used in Super Audio CD achieves high quality with only 1 bit per sample, but at a 2.8224 MHz sample rate.

0
Vladimir Poslavskiy On

8 bits per sample can be encoded directly or in a compressed form using G.711. For example, if we have an 8-bit mu-law sample, it is actually a 16-bit sample. In this case, we need to 'trim' half of the sample to obtain 8 bits.

Now, let's talk about ADPCM. I came across your question and decided to implement and test this codec. For the step table, I simply used a Fibonacci sequence.

static int stepsizeTable2[11] = {
    1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144
};

/* Encoder */

int valpred = 0;
int index = 0;
int step = stepsizeTable2[index];

for (auto sample_u8 : mmap) {
    /* G726-16 */
    /* Step 1: compute the difference from the previous sample and record the sign */
    int val = r_shift(G711::ulaw2linear(sample_u8), 8);
    int diff = val - valpred;
    bool sign = diff < 0;
    if (sign)
        diff = -diff;
    enc.put(sign);
    
    /* Step 2: compare the difference to the step size and choose the magnitude bit */
    /* Step 2.1: Assemble the value, update the index, and step values */
    int vpdiff = step >> 1;
    if (diff > step) {
        vpdiff += step;
        index += 2;
        enc.put(1);
    } else {
        index -= 1;
        enc.put(0);
    }
    index = std::clamp(index, 0, 10);
    step = stepsizeTable2[index];
    
    /* Step 3: update the previous sample value and clamp */
    if (sign)
        vpdiff = -vpdiff;
    valpred = std::clamp(valpred + vpdiff, -128, 127);
    
    if (trace_opt)
        std::cout << val << " -> " << valpred << " step: " << step << std::endl;
}

/* Decoder */

int valpred = 0;
int index = 0;
int step = stepsizeTable2[index];

for (auto &w : rw_mmap) {
    /* G726-16 */
    /* Step 1: get the delta value */
    bool sign = dec.get();
    int delta = dec.get();
    index = std::clamp(index + (delta ? 2 : -1), 0, 10);
    int vpdiff = step >> 1;
    if (delta)
        vpdiff += step;
    if (sign)
        vpdiff = -vpdiff;
    
    /* Step 6: update the step value */
    step = stepsizeTable2[index];
    valpred = std::clamp(valpred + vpdiff, -128, 127);
    w = G711::linear2ulaw(valpred << 8);
}