Add an extension to support MSADPCM buffer formats

This commit is contained in:
Chris Robinson
2014-03-04 22:44:30 -08:00
parent f3a87d72b2
commit a1c440bd09
5 changed files with 344 additions and 30 deletions
+3 -1
View File
@@ -445,6 +445,8 @@ static const ALCenums enumeration[] = {
DECL(AL_FORMAT_STEREO_DOUBLE_EXT),
DECL(AL_FORMAT_MONO_IMA4),
DECL(AL_FORMAT_STEREO_IMA4),
DECL(AL_FORMAT_MONO_MSADPCM_SOFT),
DECL(AL_FORMAT_STEREO_MSADPCM_SOFT),
DECL(AL_FORMAT_QUAD8_LOKI),
DECL(AL_FORMAT_QUAD16_LOKI),
DECL(AL_FORMAT_QUAD8),
@@ -710,7 +712,7 @@ static const ALchar alExtList[] =
"AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model "
"AL_LOKI_quadriphonic AL_SOFTX_block_alignment AL_SOFT_buffer_samples "
"AL_SOFT_buffer_sub_data AL_SOFT_deferred_updates AL_SOFT_direct_channels "
"AL_SOFT_loop_points AL_SOFT_source_latency";
"AL_SOFT_loop_points AL_SOFTX_MSADPCM AL_SOFT_source_latency";
static volatile ALCenum LastNullDeviceError = ALC_NO_ERROR;
+1
View File
@@ -22,6 +22,7 @@ enum UserFmtType {
UserFmtMulaw,
UserFmtAlaw,
UserFmtIMA4,
UserFmtMSADPCM,
};
enum UserFmtChannels {
UserFmtMono = AL_MONO_SOFT,
+6
View File
@@ -246,6 +246,12 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
#define AL_PACK_BLOCK_ALIGNMENT_SOFT 0x200D
#endif
#ifndef AL_SOFT_MSADPCM
#define AL_SOFT_MSADPCM 1
#define AL_FORMAT_MONO_MSADPCM_SOFT 0x1302
#define AL_FORMAT_STEREO_MSADPCM_SOFT 0x1303
#endif
#ifdef IN_IDE_PARSER
/* KDevelop's parser doesn't recognize the C99-standard restrict keyword, but
+311 -29
View File
@@ -81,6 +81,25 @@ static const int IMA4Index_adjust[16] = {
-1,-1,-1,-1, 2, 4, 6, 8
};
/* MSADPCM Adaption table */
static const int MSADPCMAdaption[16] = {
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
/* MSADPCM Adaption Coefficient tables */
static const int MSADPCMAdaptionCoeff[7][2] = {
{ 256, 0 },
{ 512, -256 },
{ 0, 0 },
{ 192, 64 },
{ 240, 0 },
{ 460, -208 },
{ 392, -232 }
};
/* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a
* signed 16-bit sample */
static const ALshort muLawDecompressionTable[256] = {
@@ -409,6 +428,29 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
if(err != AL_NO_ERROR)
SET_ERROR_AND_GOTO(context, err, done);
break;
case UserFmtMSADPCM:
framesize = (align-2)/2 + 7;
framesize *= ChannelsFromUserFmt(srcchannels);
if((size%framesize) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
newformat = AL_FORMAT_MONO16;
switch(srcchannels)
{
case UserFmtMono: newformat = AL_FORMAT_MONO16; break;
case UserFmtStereo: newformat = AL_FORMAT_STEREO16; break;
case UserFmtRear: newformat = AL_FORMAT_REAR16; break;
case UserFmtQuad: newformat = AL_FORMAT_QUAD16; break;
case UserFmtX51: newformat = AL_FORMAT_51CHN16; break;
case UserFmtX61: newformat = AL_FORMAT_61CHN16; break;
case UserFmtX71: newformat = AL_FORMAT_71CHN16; break;
}
err = LoadData(albuf, freq, newformat, size/framesize*align,
srcchannels, srctype, data, align, AL_TRUE);
if(err != AL_NO_ERROR)
SET_ERROR_AND_GOTO(context, err, done);
break;
}
done:
@@ -461,6 +503,11 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons
byte_align = (albuf->OriginalAlign-1)/2 + 4;
byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels);
}
else if(albuf->OriginalType == UserFmtMSADPCM)
{
byte_align = (albuf->OriginalAlign-2)/2 + 7;
byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels);
}
else
{
byte_align = albuf->OriginalAlign;
@@ -1069,6 +1116,7 @@ done:
typedef ALubyte ALmulaw;
typedef ALubyte ALalaw;
typedef ALubyte ALima4;
typedef ALubyte ALmsadpcm;
typedef struct {
ALbyte b[3];
} ALbyte3;
@@ -1247,6 +1295,118 @@ static void EncodeIMA4Block(ALima4 *dst, const ALshort *src, ALint *sample, ALin
}
static void DecodeMSADPCMBlock(ALshort *dst, const ALmsadpcm *src, ALint numchans, ALsizei align)
{
ALubyte blockpred[MAX_INPUT_CHANNELS];
ALint delta[MAX_INPUT_CHANNELS];
ALshort samples[MAX_INPUT_CHANNELS][2];
ALint i, j;
for(i = 0;i < numchans;i++)
{
blockpred[i] = *(src++);
blockpred[i] = minu(blockpred[i], 6);
}
for(i = 0;i < numchans;i++)
{
delta[i] = *(src++);
delta[i] |= *(src++) << 8;
}
for(i = 0;i < numchans;i++)
{
samples[i][0] = *(src++);
samples[i][0] |= *(src++) << 8;
samples[i][0] = (samples[i][0]^0x8000) - 0x8000;
}
for(i = 0;i < numchans;i++)
{
samples[i][1] = *(src++);
samples[i][1] |= *(src++) << 8;
samples[i][1] = (samples[i][1]^0x8000) - 0x8000;
}
/* Second sample is written first. */
for(i = 0;i < numchans;i++)
*(dst++) = samples[i][1];
for(i = 0;i < numchans;i++)
*(dst++) = samples[i][0];
for(j = 2;j < align;j++)
{
for(i = 0;i < numchans;i++)
{
const ALint num = (j*numchans) + i;
ALint nibble, pred;
/* Read the nibble and sign-expand it. */
if(!(num&1))
nibble = (*src>>4)&0x0f;
else
nibble = (*(src++))&0x0f;
nibble = (nibble^0x08) - 0x08;
pred = (samples[i][0]*MSADPCMAdaptionCoeff[blockpred[i]][0] +
samples[i][1]*MSADPCMAdaptionCoeff[blockpred[i]][1]) / 256;
pred += nibble * delta[i];
pred = clampi(pred, -32768, 32767);
samples[i][1] = samples[i][0];
samples[i][0] = pred;
delta[i] = (MSADPCMAdaption[nibble&0x0f] * delta[i]) / 256;
delta[i] = maxu(16, delta[i]);
*(dst++) = pred;
}
}
}
/* FIXME: Stubbed. Just inserts silence. */
static void EncodeMSADPCMBlock(ALmsadpcm *dst, const ALshort* UNUSED(src), ALint* UNUSED(sample), ALint* UNUSED(index), ALint numchans, ALsizei align)
{
ALint i, j;
/* Block predictor */
for(i = 0;i < numchans;i++)
*(dst++) = 2;
/* Initial delta */
for(i = 0;i < numchans;i++)
{
*(dst++) = 16;
*(dst++) = 0;
}
/* Initial sample 1 */
for(i = 0;i < numchans;i++)
{
*(dst++) = 0;
*(dst++) = 0;
}
/* Initial sample 2 */
for(i = 0;i < numchans;i++)
{
*(dst++) = 0;
*(dst++) = 0;
}
for(j = 2;j < align;j++)
{
for(i = 0;i < numchans;i++)
{
const ALint num = (j*numchans) + i;
ALubyte nibble = 0;
if(!(num&1))
*dst = nibble << 4;
else
{
*dst |= nibble;
dst++;
}
}
}
}
static inline ALint DecodeByte3(ALbyte3 val)
{
if(IS_LITTLE_ENDIAN)
@@ -1665,7 +1825,7 @@ DECL_TEMPLATE2(ALubyte3)
static void Convert_##T##_ALima4(T *dst, const ALima4 *src, ALuint numchans, \
ALuint len, ALuint align) \
{ \
ALsizei byte_align = (align-1)/2 + 4; \
ALsizei byte_align = ((align-1)/2 + 4) * numchans; \
ALuint i, j, k; \
ALshort *tmp; \
\
@@ -1673,7 +1833,7 @@ static void Convert_##T##_ALima4(T *dst, const ALima4 *src, ALuint numchans, \
for(i = 0;i < len;i += align) \
{ \
DecodeIMA4Block(tmp, src, numchans, align); \
src += byte_align*numchans; \
src += byte_align; \
\
for(j = 0;j < align;j++) \
{ \
@@ -1704,7 +1864,7 @@ static void Convert_ALima4_##T(ALima4 *dst, const T *src, ALuint numchans, \
{ \
ALint sample[MaxChannels] = {0,0,0,0,0,0,0,0}; \
ALint index[MaxChannels] = {0,0,0,0,0,0,0,0}; \
ALsizei byte_align = (align-1)/2 + 4; \
ALsizei byte_align = ((align-1)/2 + 4) * numchans; \
ALshort *tmp; \
ALuint i, j; \
\
@@ -1714,7 +1874,7 @@ static void Convert_ALima4_##T(ALima4 *dst, const T *src, ALuint numchans, \
for(j = 0;j < align*numchans;j++) \
tmp[j] = Conv_ALshort_##T(*(src++)); \
EncodeIMA4Block(dst, tmp, sample, index, numchans, align); \
dst += byte_align*numchans; \
dst += byte_align; \
} \
}
@@ -1728,19 +1888,118 @@ DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
static void Convert_ALima4_ALima4(ALima4* UNUSED(dst), const ALima4* UNUSED(src),
ALuint UNUSED(numchans), ALuint UNUSED(len),
ALuint UNUSED(align))
{
/* We don't store IMA4 samples internally, so IMA4-to-IMA4 conversions
* should never happen. */
ERR("Unexpected IMA4-to-IMA4 conversion!\n");
}
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
#define DECL_TEMPLATE(T) \
static void Convert_##T##_ALmsadpcm(T *dst, const ALmsadpcm *src, \
ALuint numchans, ALuint len, \
ALuint align) \
{ \
ALsizei byte_align = ((align-2)/2 + 7) * numchans; \
ALuint i, j, k; \
ALshort *tmp; \
\
tmp = alloca(align*numchans); \
for(i = 0;i < len;i += align) \
{ \
DecodeMSADPCMBlock(tmp, src, numchans, align); \
src += byte_align; \
\
for(j = 0;j < align;j++) \
{ \
for(k = 0;k < numchans;k++) \
*(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]); \
} \
} \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
#define DECL_TEMPLATE(T) \
static void Convert_ALmsadpcm_##T(ALmsadpcm *dst, const T *src, \
ALuint numchans, ALuint len, ALuint align) \
{ \
ALint sample[MaxChannels] = {0,0,0,0,0,0,0,0}; \
ALint index[MaxChannels] = {0,0,0,0,0,0,0,0}; \
ALsizei byte_align = ((align-2)/2 + 7) * numchans; \
ALshort *tmp; \
ALuint i, j; \
\
ERR("MSADPCM encoding not currently supported!\n"); \
\
tmp = alloca(align*numchans); \
for(i = 0;i < len;i += align) \
{ \
for(j = 0;j < align*numchans;j++) \
tmp[j] = Conv_ALshort_##T(*(src++)); \
EncodeMSADPCMBlock(dst, tmp, sample, index, numchans, align); \
dst += byte_align; \
} \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
/* NOTE: We don't store compressed samples internally, so these conversions
* should never happen. */
static void Convert_ALima4_ALima4(ALima4* UNUSED(dst), const ALima4* UNUSED(src),
ALuint UNUSED(numchans), ALuint UNUSED(len),
ALuint UNUSED(align))
{
ERR("Unexpected IMA4-to-IMA4 conversion!\n");
}
static void Convert_ALmsadpcm_ALmsadpcm(ALmsadpcm* UNUSED(dst), const ALmsadpcm* UNUSED(src),
ALuint UNUSED(numchans), ALuint UNUSED(len),
ALuint UNUSED(align))
{
ERR("Unexpected MSADPCM-to-MSADPCM conversion!\n");
}
static void Convert_ALmsadpcm_ALima4(ALmsadpcm* UNUSED(dst), const ALima4* UNUSED(src),
ALuint UNUSED(numchans), ALuint UNUSED(len),
ALuint UNUSED(align))
{
ERR("Unexpected IMA4-to-MSADPCM conversion!\n");
}
static void Convert_ALima4_ALmsadpcm(ALima4* UNUSED(dst), const ALmsadpcm* UNUSED(src),
ALuint UNUSED(numchans), ALuint UNUSED(len),
ALuint UNUSED(align))
{
ERR("Unexpected MSADPCM-to-IMA4 conversion!\n");
}
#define DECL_TEMPLATE(T) \
static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType, \
ALsizei numchans, ALsizei len, ALsizei align) \
@@ -1780,6 +2039,9 @@ static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType, \
case UserFmtIMA4: \
Convert_##T##_ALima4(dst, src, numchans, len, align); \
break; \
case UserFmtMSADPCM: \
Convert_##T##_ALmsadpcm(dst, src, numchans, len, align); \
break; \
case UserFmtByte3: \
Convert_##T##_ALbyte3(dst, src, numchans, len, align); \
break; \
@@ -1800,6 +2062,7 @@ DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALima4)
DECL_TEMPLATE(ALmsadpcm)
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
@@ -1843,6 +2106,9 @@ static void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src
case UserFmtIMA4:
Convert_ALima4(dst, src, srcType, numchans, len, align);
break;
case UserFmtMSADPCM:
Convert_ALmsadpcm(dst, src, srcType, numchans, len, align);
break;
case UserFmtByte3:
Convert_ALbyte3(dst, src, srcType, numchans, len, align);
break;
@@ -1909,6 +2175,12 @@ static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei f
ALBuf->OriginalSize = frames / align * byte_align;
ALBuf->OriginalAlign = align;
}
else if(SrcType == UserFmtMSADPCM)
{
ALsizei byte_align = ((align-2)/2 + 7) * ChannelsFromUserFmt(SrcChannels);
ALBuf->OriginalSize = frames / align * byte_align;
ALBuf->OriginalAlign = align;
}
else
{
ALBuf->OriginalSize = frames * FrameSizeFromUserFmt(SrcChannels, SrcType);
@@ -1954,6 +2226,7 @@ ALuint BytesFromUserFmt(enum UserFmtType type)
case UserFmtMulaw: return sizeof(ALubyte);
case UserFmtAlaw: return sizeof(ALubyte);
case UserFmtIMA4: break; /* not handled here */
case UserFmtMSADPCM: break; /* not handled here */
}
return 0;
}
@@ -1979,21 +2252,23 @@ static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans,
enum UserFmtChannels channels;
enum UserFmtType type;
} list[] = {
{ AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
{ AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
{ AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat },
{ AL_FORMAT_MONO_DOUBLE_EXT, UserFmtMono, UserFmtDouble },
{ AL_FORMAT_MONO_IMA4, UserFmtMono, UserFmtIMA4 },
{ AL_FORMAT_MONO_MULAW, UserFmtMono, UserFmtMulaw },
{ AL_FORMAT_MONO_ALAW_EXT, UserFmtMono, UserFmtAlaw },
{ AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
{ AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
{ AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat },
{ AL_FORMAT_MONO_DOUBLE_EXT, UserFmtMono, UserFmtDouble },
{ AL_FORMAT_MONO_IMA4, UserFmtMono, UserFmtIMA4 },
{ AL_FORMAT_MONO_MSADPCM_SOFT, UserFmtMono, UserFmtMSADPCM },
{ AL_FORMAT_MONO_MULAW, UserFmtMono, UserFmtMulaw },
{ AL_FORMAT_MONO_ALAW_EXT, UserFmtMono, UserFmtAlaw },
{ AL_FORMAT_STEREO8, UserFmtStereo, UserFmtUByte },
{ AL_FORMAT_STEREO16, UserFmtStereo, UserFmtShort },
{ AL_FORMAT_STEREO_FLOAT32, UserFmtStereo, UserFmtFloat },
{ AL_FORMAT_STEREO_DOUBLE_EXT, UserFmtStereo, UserFmtDouble },
{ AL_FORMAT_STEREO_IMA4, UserFmtStereo, UserFmtIMA4 },
{ AL_FORMAT_STEREO_MULAW, UserFmtStereo, UserFmtMulaw },
{ AL_FORMAT_STEREO_ALAW_EXT, UserFmtStereo, UserFmtAlaw },
{ AL_FORMAT_STEREO8, UserFmtStereo, UserFmtUByte },
{ AL_FORMAT_STEREO16, UserFmtStereo, UserFmtShort },
{ AL_FORMAT_STEREO_FLOAT32, UserFmtStereo, UserFmtFloat },
{ AL_FORMAT_STEREO_DOUBLE_EXT, UserFmtStereo, UserFmtDouble },
{ AL_FORMAT_STEREO_IMA4, UserFmtStereo, UserFmtIMA4 },
{ AL_FORMAT_STEREO_MSADPCM_SOFT, UserFmtStereo, UserFmtMSADPCM },
{ AL_FORMAT_STEREO_MULAW, UserFmtStereo, UserFmtMulaw },
{ AL_FORMAT_STEREO_ALAW_EXT, UserFmtStereo, UserFmtAlaw },
{ AL_FORMAT_REAR8, UserFmtRear, UserFmtUByte },
{ AL_FORMAT_REAR16, UserFmtRear, UserFmtShort },
@@ -2130,6 +2405,8 @@ static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align)
*/
*align = 65;
}
else if(type == UserFmtMSADPCM)
*align = 64;
else
*align = 1;
return AL_TRUE;
@@ -2138,9 +2415,14 @@ static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align)
if(type == UserFmtIMA4)
{
/* IMA4 block alignment must be a multiple of 8, plus 1. */
if(((*align)&7) != 1)
return AL_FALSE;
return AL_TRUE;
return ((*align)&7) == 1;
}
if(type == UserFmtMSADPCM)
{
/* MSADPCM block alignment must be a multiple of 8. */
/* FIXME: Too strict? Might only require align*channels to be a
* multiple of 2. */
return ((*align)&7) == 0;
}
return AL_TRUE;
+23
View File
@@ -2551,6 +2551,23 @@ static ALvoid GetSourceOffsets(const ALsource *Source, ALenum name, ALdouble *of
FrameBlockSize * BlockSize);
}
}
else if(Buffer->OriginalType == UserFmtMSADPCM)
{
ALsizei align = (Buffer->OriginalAlign-2)/2 + 7;
ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels);
ALuint FrameBlockSize = Buffer->OriginalAlign;
/* Round down to nearest ADPCM block */
offset[0] = (ALdouble)(readPos / FrameBlockSize * BlockSize);
if(Source->state != AL_PLAYING)
offset[1] = offset[0];
else
{
/* Round up to nearest ADPCM block */
offset[1] = (ALdouble)((writePos+FrameBlockSize-1) /
FrameBlockSize * BlockSize);
}
}
else
{
ALuint FrameSize = FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType);
@@ -2655,6 +2672,12 @@ static ALint GetSampleOffset(ALsource *Source)
Offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels);
Offset *= Buffer->OriginalAlign;
}
else if(Buffer->OriginalType == UserFmtMSADPCM)
{
ALsizei align = (Buffer->OriginalAlign-2)/2 + 7;
Offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels);
Offset *= Buffer->OriginalAlign;
}
else
Offset /= FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType);
break;