[openal] AL_SOFT_MSADPCM
Chris Robinson
chris.kcat at gmail.com
Thu Jul 3 10:48:26 EDT 2014
Due to the fact that there is no official documentation for the MSADPCM
format, I decided to throw something together for the extension spec
(OpenGL's S3TC compression extension spec specifies how S3TC formats
work, so I don't see the harm in doing the same here for MSADPCM).
The extension functionality is pretty much set in stone, unless someone
thinks there's some glaring omission. Otherwise, I'd be interested in
knowing whether there's any inaccuracies in the description of the format.
-------------- next part --------------
Name
AL_SOFT_MSADPCM
Contributors
Chris Robinson
Contact
Chris Robinson (chris.kcat 'at' gmail.com)
Status
In progress
Dependencies
This extension is for OpenAL 1.1.
This extension interacts with AL_SOFT_block_alignment.
Overview
This extension adds support for MSADPCM compressed sample formats.
Issues
None.
New Procedures and Functions
None.
New Tokens
Accepted by the <format> parameter of alBufferData:
AL_FORMAT_MONO_MSADPCM_SOFT 0x1302
AL_FORMAT_STEREO_MSADPCM_SOFT 0x1303
Additions to Specification
MSADPCM Sample Formats
MSADPCM data, like IMA4, is expressed as one or more blocks of data. By
default a block length of 64 sample frames (or 38 bytes per channel) is
used, however this can be changed using the AL_SOFT_block_alignment
extension. Each block starts with a header that provides initial
information for the decoding process, which is then used in conjunction
with the following nibbles (4 bits) to generate samples.
The header for a mono MSADPCM block is 7 bytes, and can be described as:
predictor : uint8
delta : int16 (little-endian)
history0 : int16 (little-endian)
history1 : int16 (little-endian)
The predictor is in the range of [0...6], and should be clamped as needed.
The value is used as an index into two lookup tables that are used in the
decoding process. The two tables are defined as:
const int coeff0[7] = { 256, 512, 0, 192, 240, 460, 392 };
const int coeff1[7] = { 0, -256, 0, 64, 0, -208, -232 };
An adaption table is also used, to modify the delta after decoding a
sample. It is defined as:
const int adapt[16] = { 230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230 };
The history values represent the last two decoded samples, with history0
being the newest and history1 the oldest. Additionally, they are the first
two output values, as signed 16-bit samples, with history1 being the first
and history0 being the second.
The remaining bytes of each block are each composed of two nibbles, with
each nibble being used to generate one sample. The top nibble of a byte is
used first to decode a sample, followed by the bottom for the next.
Decoding each nibble into a signed 16-bit sample is done like:
/* Use a 32-bit temporary in case it overflows the 16-bit range. */
int sample;
/* Form an initial prediction using the history and coefficients. */
sample = (history0*coeff0[predictor] + history1*coeff1[predictor]) / 256;
/* Add a correction using a sign-extended nibble scaled by the delta. */
sample += ((nibble >= 8) ? nibble-16 : nibble) * delta;
/* Clamp the result to a valid 16-bit range. */
if(sample > 32767)
sample = 32767;
if(sample < -32768)
sample = -32768;
/* Update the history with the new sample. */
history1 = history0;
history0 = sample;
/* Update the delta for the next sample. */
delta = adapt[nibble] * delta / 256;
/* Make sure the delta is no less than 16. */
if(delta < 16)
delta = 16;
The decoded 16-bit value is now stored in 'sample'.
For a stereo MSADPCM block, each header is 14 bytes, and each field is
interleaved with the left channel followed by the right:
left-predictor : uint8
right-predictor : uint8
left-delta : int16 (little-endian)
right-delta : int16 (little-endian)
left-history0 : int16 (little-endian)
right-history0 : int16 (little-endian)
left-history1 : int16 (little-endian)
right-history1 : int16 (little-endian)
The remaining bytes are interleaved on a per-nibble basis. Effectively,
the top nibble of each byte is used for the left channel, and the bottom
nibble is used for the right channel. The channel header fields are used
independently for decoding -- that is, the left channel does not at all
influence the samples produced for the right channel, and vice-versa.
Errors
An AL_INVALID_VALUE error is generted if alBufferData is called with a
format of AL_FORMAT_MONO_MSADPCM_SOFT or AL_FORMAT_STEREO_MSADPCM_SOFT and
the data size is not a valid multiple of the block alignment in bytes.
More information about the openal
mailing list