diff options
author | Chris Robinson <[email protected]> | 2014-03-04 22:44:30 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2014-03-04 22:44:30 -0800 |
commit | a1c440bd09d6b0bec36ef84dc474306ce6171b26 (patch) | |
tree | b0666578ef6110d6a956759445654c5adb054af1 /OpenAL32 | |
parent | f3a87d72b266640a706ddd3234aa4313b53cabce (diff) |
Add an extension to support MSADPCM buffer formats
Diffstat (limited to 'OpenAL32')
-rw-r--r-- | OpenAL32/Include/alBuffer.h | 1 | ||||
-rw-r--r-- | OpenAL32/Include/alMain.h | 6 | ||||
-rw-r--r-- | OpenAL32/alBuffer.c | 336 | ||||
-rw-r--r-- | OpenAL32/alSource.c | 23 |
4 files changed, 339 insertions, 27 deletions
diff --git a/OpenAL32/Include/alBuffer.h b/OpenAL32/Include/alBuffer.h index be426df2..10359d9d 100644 --- a/OpenAL32/Include/alBuffer.h +++ b/OpenAL32/Include/alBuffer.h @@ -22,6 +22,7 @@ enum UserFmtType { UserFmtMulaw, UserFmtAlaw, UserFmtIMA4, + UserFmtMSADPCM, }; enum UserFmtChannels { UserFmtMono = AL_MONO_SOFT, diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 7de52260..5f74eab0 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -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 diff --git a/OpenAL32/alBuffer.c b/OpenAL32/alBuffer.c index 058b1f49..8f19ae0a 100644 --- a/OpenAL32/alBuffer.c +++ b/OpenAL32/alBuffer.c @@ -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,46 @@ 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; \ + } \ +} + +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_##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]); \ + } \ } \ } @@ -1728,18 +1927,78 @@ 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)) { - /* 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 +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, \ @@ -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_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_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_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; diff --git a/OpenAL32/alSource.c b/OpenAL32/alSource.c index 4a46b77e..e0ae9768 100644 --- a/OpenAL32/alSource.c +++ b/OpenAL32/alSource.c @@ -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; |