gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Audio > New ADPCM-ish format

#176495 - Ruben - Sun Aug 07, 2011 6:05 pm

Hi all.
Just thought I'd post this as it's quite nifty.

Just today, I finished perfecting a new delta format that stores its samples as 4-bit data with indices.

The whole idea of the format is that the entire file is split into seven-sample 'blocks'. Each block contains a 4-bit index followed by 7x 4-bit samples. The index is a reference to a set of 16 delta tables and the four bit 'samples' are a reference to the table data.

Decoding (C/C++):
Code:
//! read packed data [7 samples + index]
u32 pckDat = *src++;

//! since data is packed as I1234567,
//! find the index
u32 tabIndex = pckDat>>28;

//! since the whole point of this format
//! is speed, the index is shifted off
pckDat = pckDat << 4;

//! now decode the seven samples
for(i=0;i<7;i++) {
    //! find the delta
    //! NOTE: highest nibble first
    u32 ti    = pckDat>>28;
    s32 delta = stepTab[tabIndex][ti];

    //! add to original sample to find
    //! final data.
    //! since the data is 15-bit, there's
    //! no need to clip and overflow is ok
    smpDat += delta;

    //! skip to next sample.
    //! again, since the point of this
    //! format is speed, the data is
    //! shifted off
    pckDat = pckDat<<4;

    //! out of samples?
    if(!pckDat) pckDat = *src++;
}


Decoding (ARM ASM):
Code:
@ r0: sample
@ r1: source data
@ r2: step tables
@ r3: packed data
@ r4: current step table
@ r5: mask
@ r6: count

prepare:
    ldd   r0, =0
    ldr   r1, =sourceData
    ldr   r2, =stepTab
    ldr   r3, [r1], #0x04         @ load samples + index
    mov   r4, r3, lsr #0x1C       @ find index...
    add   r4, r2, r4, lsl #0x01+4 @ find table source
                                  @ [SHL 1 for 16-bit data,
                                  @  SHL 4 as 16 entries]
    mov   r3, r3, lsl #0x04       @ shift off index
    mvn   r5, #0x01               @ mask = 0xFFFE
    mov   r6, #0x07               @ count = 7

decode:
    and   r7, r5, r3, lsr #0x1C-1 @ find table offset
    ldrsh r7, [r4, r7]            @ find delta

    add   r0, r0, r7              @ find final sample

    movs  r3, r3, lsl #0x04       @ shift out data...
    ldreq r3, [r1], #0x04         @ load new data when out of data

    subs  r6, r6, #0x01           @ more samples?
    bne   decode                  @   yes, keep decoding


What you may have noticed is that samples are loaded when the packed data = 0. You may be asking, "What if the final sample's index is zero?" Well, that's the thing: the final sample index *CAN'T* be zero. The conversion program should take care of that.

Another nifty thing about this format is that since it's differential PCM, linear interpolation is a LOT easier as you have to read the delta anyway so you can pre-load it (it's what I do).

Sound demo (NOTE: taken from my FFII-DS port, so there may be extra baggage and will NOT work on a real DS as it's using ROM/FS. You may also have to set DeSmuME to synchronous sound mode): MediaFire

Conversion program source (NOTE: made for my own DS library, so you'll have to make changes to use it): MediaFire