mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-13 21:31:53 +00:00
1513 lines
36 KiB
C++
1513 lines
36 KiB
C++
/* ResidualVM - A 3D game interpreter
|
|
*
|
|
* ResidualVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
// Based on xoreos' WMA code which is in turn
|
|
// Largely based on the WMA implementation found in FFmpeg.
|
|
|
|
#include "common/util.h"
|
|
#include "common/math.h"
|
|
#include "common/sinewindows.h"
|
|
#include "common/error.h"
|
|
#include "common/memstream.h"
|
|
#include "common/mdct.h"
|
|
#include "common/huffman.h"
|
|
|
|
#include "audio/audiostream.h"
|
|
|
|
#include "audio/decoders/util.h"
|
|
#include "audio/decoders/raw.h"
|
|
#include "audio/decoders/wma.h"
|
|
#include "audio/decoders/wmadata.h"
|
|
|
|
namespace Audio {
|
|
|
|
static inline void butterflyFloats(float *v1, float *v2, int len) {
|
|
while (len-- > 0) {
|
|
float t = *v1 - *v2;
|
|
|
|
*v1++ += *v2;
|
|
*v2++ = t;
|
|
}
|
|
}
|
|
|
|
static inline void vectorFMulAdd(float *dst, const float *src0,
|
|
const float *src1, const float *src2, int len) {
|
|
while (len-- > 0)
|
|
*dst++ = *src0++ * *src1++ + *src2++;
|
|
}
|
|
|
|
static inline void vectorFMulReverse(float *dst, const float *src0,
|
|
const float *src1, int len) {
|
|
src1 += len - 1;
|
|
|
|
while (len-- > 0)
|
|
*dst++ = *src0++ * *src1--;
|
|
}
|
|
|
|
|
|
WMACodec::WMACodec(int version, uint32 sampleRate, uint8 channels,
|
|
uint32 bitRate, uint32 blockAlign, Common::SeekableReadStream *extraData) :
|
|
_version(version), _sampleRate(sampleRate), _channels(channels),
|
|
_bitRate(bitRate), _blockAlign(blockAlign), _audioFlags(0),
|
|
_resetBlockLengths(true), _curFrame(0), _frameLen(0), _frameLenBits(0),
|
|
_blockSizeCount(0), _framePos(0), _curBlock(0), _blockLen(0), _blockLenBits(0),
|
|
_nextBlockLenBits(0), _prevBlockLenBits(0), _byteOffsetBits(0),
|
|
_hgainHuffman(0), _expHuffman(0), _lastSuperframeLen(0), _lastBitoffset(0) {
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
_coefHuffman[i] = 0;
|
|
|
|
_coefHuffmanRunTable [i] = 0;
|
|
_coefHuffmanLevelTable[i] = 0;
|
|
_coefHuffmanIntTable [i] = 0;
|
|
}
|
|
|
|
if ((_version != 1) && (_version != 2))
|
|
error("WMACodec::init(): Unsupported WMA version %d", _version);
|
|
|
|
if ((_sampleRate == 0) || (_sampleRate > 50000))
|
|
error("WMACodec::init(): Invalid sample rate %d", _sampleRate);
|
|
if ((_channels == 0) || (_channels > kChannelsMax))
|
|
error("WMACodec::init(): Unsupported number of channels %d",
|
|
_channels);
|
|
|
|
_audioFlags = FLAG_16BITS;
|
|
|
|
#ifdef SCUMM_LITTLE_ENDIAN
|
|
_audioFlags |= FLAG_LITTLE_ENDIAN;
|
|
#endif
|
|
|
|
if (_channels == 2) {
|
|
_audioFlags |= FLAG_STEREO;
|
|
}
|
|
|
|
init(extraData);
|
|
}
|
|
|
|
WMACodec::~WMACodec() {
|
|
delete _expHuffman;
|
|
delete _hgainHuffman;
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
delete[] _coefHuffmanRunTable [i];
|
|
delete[] _coefHuffmanLevelTable[i];
|
|
delete[] _coefHuffmanIntTable [i];
|
|
|
|
delete _coefHuffman[i];
|
|
}
|
|
|
|
for (Common::Array<Common::MDCT *>::iterator m = _mdct.begin(); m != _mdct.end(); ++m)
|
|
delete *m;
|
|
}
|
|
|
|
void WMACodec::init(Common::SeekableReadStream *extraData) {
|
|
// Flags
|
|
uint16 flags = getFlags(extraData);
|
|
evalFlags(flags, extraData);
|
|
|
|
// Frame length
|
|
_frameLenBits = getFrameBitLength();
|
|
_frameLen = 1 << _frameLenBits;
|
|
|
|
// Number of MDCT block sizes
|
|
_blockSizeCount = getBlockSizeCount(flags);
|
|
|
|
float bps = ((float) _bitRate) / ((float) (_channels * _sampleRate));
|
|
|
|
_byteOffsetBits = Common::intLog2((int) (bps * _frameLen / 8.0 + 0.05)) + 2;
|
|
|
|
// Compute high frequency value and choose if noise coding should be activated
|
|
float highFreq;
|
|
_useNoiseCoding = useNoiseCoding(highFreq, bps);
|
|
|
|
// Compute the scale factor band sizes for each MDCT block size
|
|
evalMDCTScales(highFreq);
|
|
|
|
// Init the noise generator
|
|
initNoise();
|
|
|
|
// Init the coefficient huffman codes
|
|
initCoefHuffman(bps);
|
|
|
|
// Init MDCTs
|
|
initMDCT();
|
|
|
|
// Init exponent codes
|
|
initExponents();
|
|
|
|
// Clear the sample output buffers
|
|
memset(_output , 0, sizeof(_output));
|
|
memset(_frameOut, 0, sizeof(_frameOut));
|
|
}
|
|
|
|
uint16 WMACodec::getFlags(Common::SeekableReadStream *extraData) {
|
|
if ((_version == 1) && extraData && (extraData->size() >= 4)) {
|
|
extraData->seek(2);
|
|
return extraData->readUint16LE();
|
|
}
|
|
|
|
if ((_version == 2) && extraData && (extraData->size() >= 6)) {
|
|
extraData->seek(4);
|
|
return extraData->readUint16LE();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void WMACodec::evalFlags(uint16 flags, Common::SeekableReadStream *extraData) {
|
|
_useExpHuffman = (flags & 0x0001) != 0;
|
|
_useBitReservoir = (flags & 0x0002) != 0;
|
|
_useVariableBlockLen = (flags & 0x0004) != 0;
|
|
|
|
if ((_version == 2) && extraData && (extraData->size() >= 8)) {
|
|
extraData->seek(4);
|
|
if ((extraData->readUint16LE() == 0x000D) && _useVariableBlockLen) {
|
|
// Apparently, this fixes ffmpeg "issue1503"
|
|
|
|
_useVariableBlockLen = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
int WMACodec::getFrameBitLength() {
|
|
if (_sampleRate <= 16000)
|
|
return 9;
|
|
|
|
if ((_sampleRate <= 22050) || (_sampleRate <= 32000 && _version == 1))
|
|
return 10;
|
|
|
|
if (_sampleRate <= 48000)
|
|
return 11;
|
|
|
|
if (_sampleRate <= 96000)
|
|
return 12;
|
|
|
|
return 13;
|
|
}
|
|
|
|
int WMACodec::getBlockSizeCount(uint16 flags) {
|
|
if (!_useVariableBlockLen)
|
|
return 1;
|
|
|
|
int count = ((flags >> 3) & 3) + 1;
|
|
|
|
if ((_bitRate / _channels) >= 32000)
|
|
count += 2;
|
|
|
|
const int maxCount = _frameLenBits - kBlockBitsMin;
|
|
|
|
return MIN(count, maxCount) + 1;
|
|
}
|
|
|
|
uint32 WMACodec::getNormalizedSampleRate() {
|
|
// Sample rates are only normalized in WMAv2
|
|
if (_version != 2)
|
|
return _sampleRate;
|
|
|
|
if (_sampleRate>= 44100)
|
|
return 44100;
|
|
|
|
if (_sampleRate >= 22050)
|
|
return 22050;
|
|
|
|
if (_sampleRate >= 16000)
|
|
return 16000;
|
|
|
|
if (_sampleRate >= 11025)
|
|
return 11025;
|
|
|
|
if (_sampleRate >= 8000)
|
|
return 8000;
|
|
|
|
return _sampleRate;
|
|
}
|
|
|
|
bool WMACodec::useNoiseCoding(float &highFreq, float &bps) {
|
|
highFreq = _sampleRate * 0.5;
|
|
|
|
uint32 rateNormalized = getNormalizedSampleRate();
|
|
|
|
float bpsOrig = bps;
|
|
if (_channels == 2)
|
|
bps = bpsOrig * 1.6;
|
|
|
|
if (rateNormalized == 44100) {
|
|
if (bps >= 0.61)
|
|
return false;
|
|
|
|
highFreq = highFreq * 0.4;
|
|
return true;
|
|
}
|
|
|
|
if (rateNormalized == 22050) {
|
|
if (bps >= 1.16)
|
|
return false;
|
|
|
|
if (bps >= 0.72)
|
|
highFreq = highFreq * 0.7;
|
|
else
|
|
highFreq = highFreq * 0.6;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (rateNormalized == 16000) {
|
|
if (bpsOrig > 0.5)
|
|
highFreq = highFreq * 0.5;
|
|
else
|
|
highFreq = highFreq * 0.3;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (rateNormalized == 11025) {
|
|
highFreq = highFreq * 0.7;
|
|
return true;
|
|
}
|
|
|
|
if (rateNormalized == 8000) {
|
|
if (bpsOrig > 0.75)
|
|
return false;
|
|
|
|
if (bpsOrig <= 0.625)
|
|
highFreq = highFreq * 0.5;
|
|
else
|
|
highFreq = highFreq * 0.65;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
if (bpsOrig >= 0.8)
|
|
highFreq = highFreq * 0.75;
|
|
else if (bpsOrig >= 0.6)
|
|
highFreq = highFreq * 0.6;
|
|
else
|
|
highFreq = highFreq * 0.5;
|
|
|
|
return true;
|
|
}
|
|
|
|
void WMACodec::evalMDCTScales(float highFreq) {
|
|
if (_version == 1)
|
|
_coefsStart = 3;
|
|
else
|
|
_coefsStart = 0;
|
|
|
|
for (int k = 0; k < _blockSizeCount; k++) {
|
|
int blockLen = _frameLen >> k;
|
|
|
|
if (_version == 1) {
|
|
int i, lpos = 0;
|
|
|
|
for (i = 0; i < 25; i++) {
|
|
int a = wmaCriticalFreqs[i];
|
|
int b = _sampleRate;
|
|
int pos = ((blockLen * 2 * a) + (b >> 1)) / b;
|
|
|
|
if (pos > blockLen)
|
|
pos = blockLen;
|
|
|
|
_exponentBands[0][i] = pos - lpos;
|
|
if (pos >= blockLen) {
|
|
i++;
|
|
break;
|
|
}
|
|
lpos = pos;
|
|
}
|
|
|
|
_exponentSizes[0] = i;
|
|
|
|
} else {
|
|
// Hardcoded tables
|
|
const uint8 *table = 0;
|
|
|
|
int t = _frameLenBits - kBlockBitsMin - k;
|
|
if (t < 3) {
|
|
if (_sampleRate >= 44100)
|
|
table = exponentBand44100[t];
|
|
else if (_sampleRate >= 32000)
|
|
table = exponentBand32000[t];
|
|
else if (_sampleRate >= 22050)
|
|
table = exponentBand22050[t];
|
|
}
|
|
|
|
if (table) {
|
|
int n = *table++;
|
|
|
|
for (int i = 0; i < n; i++)
|
|
_exponentBands[k][i] = table[i];
|
|
|
|
_exponentSizes[k] = n;
|
|
|
|
} else {
|
|
int j = 0, lpos = 0;
|
|
|
|
for (int i = 0; i < 25; i++) {
|
|
int a = wmaCriticalFreqs[i];
|
|
int b = _sampleRate;
|
|
int pos = ((blockLen * 2 * a) + (b << 1)) / (4 * b);
|
|
|
|
pos <<= 2;
|
|
if (pos > blockLen)
|
|
pos = blockLen;
|
|
|
|
if (pos > lpos)
|
|
_exponentBands[k][j++] = pos - lpos;
|
|
|
|
if (pos >= blockLen)
|
|
break;
|
|
|
|
lpos = pos;
|
|
}
|
|
|
|
_exponentSizes[k] = j;
|
|
}
|
|
|
|
}
|
|
|
|
// Max number of coefs
|
|
_coefsEnd[k] = (_frameLen - ((_frameLen * 9) / 100)) >> k;
|
|
|
|
// High freq computation
|
|
_highBandStart[k] = (int)((blockLen * 2 * highFreq) / _sampleRate + 0.5);
|
|
|
|
int n = _exponentSizes[k];
|
|
int j = 0;
|
|
int pos = 0;
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
int start, end;
|
|
|
|
start = pos;
|
|
pos += _exponentBands[k][i];
|
|
end = pos;
|
|
|
|
if (start < _highBandStart[k])
|
|
start = _highBandStart[k];
|
|
|
|
if (end > _coefsEnd[k])
|
|
end = _coefsEnd[k];
|
|
|
|
if (end > start)
|
|
_exponentHighBands[k][j++] = end - start;
|
|
|
|
}
|
|
|
|
_exponentHighSizes[k] = j;
|
|
}
|
|
}
|
|
|
|
void WMACodec::initNoise() {
|
|
if (!_useNoiseCoding)
|
|
return;
|
|
|
|
_noiseMult = _useExpHuffman ? 0.02 : 0.04;
|
|
_noiseIndex = 0;
|
|
|
|
uint seed = 1;
|
|
float norm = (1.0 / (float)(1LL << 31)) * sqrt(3.0f) * _noiseMult;
|
|
|
|
for (int i = 0; i < kNoiseTabSize; i++) {
|
|
seed = seed * 314159 + 1;
|
|
|
|
_noiseTable[i] = (float)((int)seed) * norm;
|
|
}
|
|
|
|
_hgainHuffman = new HuffmanDecoder(0, ARRAYSIZE(hgainHuffCodes),
|
|
hgainHuffCodes, hgainHuffBits);
|
|
}
|
|
|
|
void WMACodec::initCoefHuffman(float bps) {
|
|
// Choose the parameter table
|
|
int coefHuffTable = 2;
|
|
if (_sampleRate >= 32000) {
|
|
if (bps < 0.72) {
|
|
coefHuffTable = 0;
|
|
} else if (bps < 1.16) {
|
|
coefHuffTable = 1;
|
|
}
|
|
}
|
|
|
|
_coefHuffmanParam[0] = &coefHuffmanParam[coefHuffTable * 2 ];
|
|
_coefHuffmanParam[1] = &coefHuffmanParam[coefHuffTable * 2 + 1];
|
|
|
|
_coefHuffman[0] = initCoefHuffman(_coefHuffmanRunTable[0], _coefHuffmanLevelTable[0],
|
|
_coefHuffmanIntTable[0], *_coefHuffmanParam[0]);
|
|
_coefHuffman[1] = initCoefHuffman(_coefHuffmanRunTable[1], _coefHuffmanLevelTable[1],
|
|
_coefHuffmanIntTable[1], *_coefHuffmanParam[1]);
|
|
}
|
|
|
|
void WMACodec::initMDCT() {
|
|
_mdct.reserve(_blockSizeCount);
|
|
for (int i = 0; i < _blockSizeCount; i++)
|
|
_mdct.push_back(new Common::MDCT(_frameLenBits - i + 1, true, 1.0));
|
|
|
|
// Init MDCT windows (simple sine window)
|
|
_mdctWindow.reserve(_blockSizeCount);
|
|
for (int i = 0; i < _blockSizeCount; i++)
|
|
_mdctWindow.push_back(Common::getSineWindow(_frameLenBits - i));
|
|
}
|
|
|
|
void WMACodec::initExponents() {
|
|
if (_useExpHuffman)
|
|
_expHuffman = new HuffmanDecoder(0, ARRAYSIZE(scaleHuffCodes),
|
|
scaleHuffCodes, scaleHuffBits);
|
|
else
|
|
initLSPToCurve();
|
|
}
|
|
|
|
WMACodec::HuffmanDecoder *WMACodec::initCoefHuffman(uint16 *&runTable, float *&levelTable,
|
|
uint16 *&intTable, const WMACoefHuffmanParam ¶ms) {
|
|
|
|
HuffmanDecoder *huffman =
|
|
new HuffmanDecoder(0, params.n, params.huffCodes, params.huffBits);
|
|
|
|
runTable = new uint16[params.n];
|
|
levelTable = new float[params.n];
|
|
intTable = new uint16[params.n];
|
|
|
|
uint16 *iLevelTable = new uint16[params.n];
|
|
|
|
int i = 2;
|
|
int level = 1;
|
|
int k = 0;
|
|
|
|
while (i < params.n) {
|
|
intTable[k] = i;
|
|
|
|
int l = params.levels[k++];
|
|
|
|
for (int j = 0; j < l; j++) {
|
|
runTable [i] = j;
|
|
iLevelTable[i] = level;
|
|
levelTable [i] = level;
|
|
|
|
i++;
|
|
}
|
|
|
|
level++;
|
|
}
|
|
|
|
delete[] iLevelTable;
|
|
|
|
return huffman;
|
|
}
|
|
|
|
void WMACodec::initLSPToCurve() {
|
|
float wdel = (float)M_PI / _frameLen;
|
|
|
|
for (int i = 0; i < _frameLen; i++)
|
|
_lspCosTable[i] = 2.0f * cosf(wdel * i);
|
|
|
|
// Tables for x^-0.25 computation
|
|
for (int i = 0; i < 256; i++) {
|
|
int e = i - 126;
|
|
|
|
_lspPowETable[i] = powf(2.0, e * -0.25);
|
|
}
|
|
|
|
// NOTE: These two tables are needed to avoid two operations in pow_m1_4
|
|
float b = 1.0;
|
|
for (int i = (1 << kLSPPowBits) - 1; i >= 0; i--) {
|
|
int m = (1 << kLSPPowBits) + i;
|
|
float a = (float) m * (0.5 / (1 << kLSPPowBits));
|
|
|
|
a = pow(a, -0.25f);
|
|
|
|
_lspPowMTable1[i] = 2 * a - b;
|
|
_lspPowMTable2[i] = b - a;
|
|
|
|
b = a;
|
|
}
|
|
}
|
|
|
|
AudioStream *WMACodec::decodeFrame(Common::SeekableReadStream &data) {
|
|
Common::SeekableReadStream *stream = decodeSuperFrame(data);
|
|
if (!stream)
|
|
return 0;
|
|
|
|
return makeRawStream(stream, _sampleRate, _audioFlags, DisposeAfterUse::YES);
|
|
}
|
|
|
|
Common::SeekableReadStream *WMACodec::decodeSuperFrame(Common::SeekableReadStream &data) {
|
|
uint32 size = data.size();
|
|
if (size < _blockAlign) {
|
|
warning("WMACodec::decodeSuperFrame(): size < _blockAlign");
|
|
return 0;
|
|
}
|
|
|
|
if (_blockAlign)
|
|
size = _blockAlign;
|
|
|
|
Common::BitStream8MSB bits(data);
|
|
|
|
int outputDataSize = 0;
|
|
int16 *outputData = 0;
|
|
|
|
_curFrame = 0;
|
|
|
|
if (_useBitReservoir) {
|
|
// This superframe consists of more than just one frame
|
|
|
|
bits.skip(4); // Super frame index
|
|
|
|
// Number of frames in this superframe
|
|
int newFrameCount = bits.getBits(4) - 1;
|
|
if (newFrameCount < 0) {
|
|
warning("WMACodec::decodeSuperFrame(): newFrameCount == %d", newFrameCount);
|
|
|
|
_resetBlockLengths = true;
|
|
_lastSuperframeLen = 0;
|
|
_lastBitoffset = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Number of frames in this superframe + overhang from the last superframe
|
|
int frameCount = newFrameCount;
|
|
if (_lastSuperframeLen > 0)
|
|
frameCount++;
|
|
|
|
// PCM output data
|
|
outputDataSize = frameCount * _channels * _frameLen;
|
|
outputData = new int16[outputDataSize];
|
|
|
|
memset(outputData, 0, outputDataSize * 2);
|
|
|
|
// Number of bits data that completes the last superframe's overhang.
|
|
int bitOffset = bits.getBits(_byteOffsetBits + 3);
|
|
|
|
if (_lastSuperframeLen > 0) {
|
|
// We have overhang data from the last superframe. Paste the
|
|
// complementary data from this superframe at the end and
|
|
// decode it as another frame.
|
|
|
|
byte *lastSuperframeEnd = _lastSuperframe + _lastSuperframeLen;
|
|
|
|
while (bitOffset > 7) { // Full bytes
|
|
*lastSuperframeEnd++ = bits.getBits(8);
|
|
|
|
bitOffset -= 8;
|
|
_lastSuperframeLen += 1;
|
|
}
|
|
|
|
if (bitOffset > 0) { // Remaining bits
|
|
*lastSuperframeEnd++ = bits.getBits(bitOffset) << (8 - bitOffset);
|
|
|
|
bitOffset = 0;
|
|
_lastSuperframeLen += 1;
|
|
}
|
|
|
|
Common::MemoryReadStream lastSuperframe(_lastSuperframe, _lastSuperframeLen);
|
|
Common::BitStream8MSB lastBits(lastSuperframe);
|
|
|
|
lastBits.skip(_lastBitoffset);
|
|
|
|
decodeFrame(lastBits, outputData);
|
|
|
|
_curFrame++;
|
|
}
|
|
|
|
// Skip any complementary data we haven't used
|
|
bits.skip(bitOffset);
|
|
|
|
// New superframe = New block lengths
|
|
_resetBlockLengths = true;
|
|
|
|
// Decode the frames
|
|
for (int i = 0; i < newFrameCount; i++, _curFrame++)
|
|
if (!decodeFrame(bits, outputData))
|
|
return 0;
|
|
|
|
// Check if we've got new overhang data
|
|
int remainingBits = bits.size() - bits.pos();
|
|
if (remainingBits > 0) {
|
|
// We do: Save it
|
|
|
|
_lastSuperframeLen = remainingBits >> 3;
|
|
_lastBitoffset = 8 - (remainingBits - (_lastSuperframeLen << 3));
|
|
|
|
if (_lastBitoffset > 0)
|
|
_lastSuperframeLen++;
|
|
|
|
data.seek(data.size() - _lastSuperframeLen);
|
|
data.read(_lastSuperframe, _lastSuperframeLen);
|
|
} else {
|
|
// We don't
|
|
|
|
_lastSuperframeLen = 0;
|
|
_lastBitoffset = 0;
|
|
}
|
|
|
|
} else {
|
|
// This superframe has only one frame
|
|
|
|
// PCM output data
|
|
outputDataSize = _channels * _frameLen;
|
|
outputData = new int16[outputDataSize];
|
|
|
|
memset(outputData, 0, outputDataSize * 2);
|
|
|
|
// Decode the frame
|
|
if (!decodeFrame(bits, outputData)) {
|
|
delete[] outputData;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// And return our PCM output data as a stream, if available
|
|
|
|
if (!outputData)
|
|
return 0;
|
|
|
|
return new Common::MemoryReadStream((byte *) outputData, outputDataSize * 2, DisposeAfterUse::YES);
|
|
}
|
|
|
|
bool WMACodec::decodeFrame(Common::BitStream8MSB &bits, int16 *outputData) {
|
|
_framePos = 0;
|
|
_curBlock = 0;
|
|
|
|
// Decode all blocks
|
|
int finished = 0;
|
|
while (finished == 0)
|
|
finished = decodeBlock(bits);
|
|
|
|
// Check for error
|
|
if (finished < 0)
|
|
return false;
|
|
|
|
// Convert output into interleaved PCM data
|
|
|
|
const float *floatOut[kChannelsMax];
|
|
for (int i = 0; i < kChannelsMax; i++)
|
|
floatOut[i] = _frameOut[i];
|
|
|
|
int16 *pcmOut = outputData + _curFrame * _channels * _frameLen;
|
|
|
|
floatToInt16Interleave(pcmOut, floatOut, _frameLen, _channels);
|
|
|
|
// Prepare for the next frame
|
|
for (int i = 0; i < _channels; i++)
|
|
memmove(&_frameOut[i][0], &_frameOut[i][_frameLen], _frameLen * sizeof(float));
|
|
|
|
return true;
|
|
}
|
|
|
|
int WMACodec::decodeBlock(Common::BitStream8MSB &bits) {
|
|
// Computer new block length
|
|
if (!evalBlockLength(bits))
|
|
return -1;
|
|
|
|
// Block size
|
|
|
|
int bSize = _frameLenBits - _blockLenBits;
|
|
assert((bSize >= 0) && (bSize < _blockSizeCount));
|
|
|
|
// MS Stereo?
|
|
|
|
bool msStereo = false;
|
|
if (_channels == 2)
|
|
msStereo = bits.getBit();
|
|
|
|
// Which channels are encoded?
|
|
|
|
bool hasChannels = false;
|
|
bool hasChannel[kChannelsMax];
|
|
for (int i = 0; i < kChannelsMax; i++)
|
|
hasChannel[i] = false;
|
|
|
|
for (int i = 0; i < _channels; i++) {
|
|
hasChannel[i] = bits.getBit();
|
|
if (hasChannel[i])
|
|
hasChannels = true;
|
|
}
|
|
|
|
// Decode channels
|
|
|
|
if (hasChannels)
|
|
if (!decodeChannels(bits, bSize, msStereo, hasChannel))
|
|
return -1;
|
|
|
|
// Calculate IMDCTs
|
|
|
|
if (!calculateIMDCT(bSize, msStereo, hasChannel))
|
|
return -1;
|
|
|
|
// Update block number
|
|
|
|
_curBlock += 1;
|
|
_framePos += _blockLen;
|
|
|
|
// Finished
|
|
if (_framePos >= _frameLen)
|
|
return 1;
|
|
|
|
// Need more blocks
|
|
return 0;
|
|
}
|
|
|
|
bool WMACodec::decodeChannels(Common::BitStream8MSB &bits, int bSize,
|
|
bool msStereo, bool *hasChannel) {
|
|
|
|
int totalGain = readTotalGain(bits);
|
|
int coefBitCount = totalGainToBits(totalGain);
|
|
|
|
int coefCount[kChannelsMax];
|
|
calculateCoefCount(coefCount, bSize);
|
|
|
|
if (!decodeNoise(bits, bSize, hasChannel, coefCount))
|
|
return false;
|
|
|
|
if (!decodeExponents(bits, bSize, hasChannel))
|
|
return false;
|
|
|
|
if (!decodeSpectralCoef(bits, msStereo, hasChannel, coefCount, coefBitCount))
|
|
return false;
|
|
|
|
float mdctNorm = getNormalizedMDCTLength();
|
|
|
|
calculateMDCTCoefficients(bSize, hasChannel, coefCount, totalGain, mdctNorm);
|
|
|
|
if (msStereo && hasChannel[1]) {
|
|
// Nominal case for ms stereo: we do it before MDCT
|
|
// No need to optimize this case because it should almost never happen
|
|
|
|
if (!hasChannel[0]) {
|
|
memset(_coefs[0], 0, sizeof(float) * _blockLen);
|
|
hasChannel[0] = true;
|
|
}
|
|
|
|
butterflyFloats(_coefs[0], _coefs[1], _blockLen);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WMACodec::calculateIMDCT(int bSize, bool msStereo, bool *hasChannel) {
|
|
Common::MDCT &mdct = *_mdct[bSize];
|
|
|
|
for (int i = 0; i < _channels; i++) {
|
|
int n4 = _blockLen / 2;
|
|
|
|
if (hasChannel[i])
|
|
mdct.calcIMDCT(_output, _coefs[i]);
|
|
else if (!(msStereo && (i == 1)))
|
|
memset(_output, 0, sizeof(_output));
|
|
|
|
// Multiply by the window and add in the frame
|
|
int index = (_frameLen / 2) + _framePos - n4;
|
|
window(&_frameOut[i][index]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WMACodec::evalBlockLength(Common::BitStream8MSB &bits) {
|
|
if (_useVariableBlockLen) {
|
|
// Variable block lengths
|
|
|
|
int n = Common::intLog2(_blockSizeCount - 1) + 1;
|
|
|
|
if (_resetBlockLengths) {
|
|
// Completely new block lengths
|
|
|
|
_resetBlockLengths = false;
|
|
|
|
const int prev = bits.getBits(n);
|
|
const int prevBits = _frameLenBits - prev;
|
|
if (prev >= _blockSizeCount) {
|
|
warning("WMACodec::evalBlockLength(): _prevBlockLenBits %d out of range", prevBits);
|
|
return false;
|
|
}
|
|
|
|
_prevBlockLenBits = prevBits;
|
|
|
|
const int cur = bits.getBits(n);
|
|
const int curBits = _frameLenBits - cur;
|
|
if (cur >= _blockSizeCount) {
|
|
warning("WMACodec::evalBlockLength(): _blockLenBits %d out of range", curBits);
|
|
return false;
|
|
}
|
|
|
|
_blockLenBits = curBits;
|
|
|
|
} else {
|
|
// Update block lengths
|
|
|
|
_prevBlockLenBits = _blockLenBits;
|
|
_blockLenBits = _nextBlockLenBits;
|
|
}
|
|
|
|
const int next = bits.getBits(n);
|
|
const int nextBits = _frameLenBits - next;
|
|
if (next >= _blockSizeCount) {
|
|
warning("WMACodec::evalBlockLength(): _nextBlockLenBits %d out of range", nextBits);
|
|
return false;
|
|
}
|
|
|
|
_nextBlockLenBits = nextBits;
|
|
|
|
} else {
|
|
// Fixed block length
|
|
|
|
_nextBlockLenBits = _frameLenBits;
|
|
_prevBlockLenBits = _frameLenBits;
|
|
_blockLenBits = _frameLenBits;
|
|
}
|
|
|
|
// Sanity checks
|
|
|
|
if (_frameLenBits - _blockLenBits >= _blockSizeCount) {
|
|
warning("WMACodec::evalBlockLength(): _blockLenBits not initialized to a valid value");
|
|
return false;
|
|
}
|
|
|
|
_blockLen = 1 << _blockLenBits;
|
|
if ((_framePos + _blockLen) > _frameLen) {
|
|
warning("WMACodec::evalBlockLength(): frame length overflow");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WMACodec::calculateCoefCount(int *coefCount, int bSize) const {
|
|
const int coefN = _coefsEnd[bSize] - _coefsStart;
|
|
|
|
for (int i = 0; i < _channels; i++)
|
|
coefCount[i] = coefN;
|
|
}
|
|
|
|
bool WMACodec::decodeNoise(Common::BitStream8MSB &bits, int bSize,
|
|
bool *hasChannel, int *coefCount) {
|
|
if (!_useNoiseCoding)
|
|
return true;
|
|
|
|
for (int i = 0; i < _channels; i++) {
|
|
if (!hasChannel[i])
|
|
continue;
|
|
|
|
const int n = _exponentHighSizes[bSize];
|
|
for (int j = 0; j < n; j++) {
|
|
bool a = bits.getBit() != 0;
|
|
_highBandCoded[i][j] = a;
|
|
|
|
// With noise coding, the coefficients are not transmitted
|
|
if (a)
|
|
coefCount[i] -= _exponentHighBands[bSize][j];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < _channels; i++) {
|
|
if (!hasChannel[i])
|
|
continue;
|
|
|
|
const int n = _exponentHighSizes[bSize];
|
|
int val = (int) 0x80000000;
|
|
|
|
for (int j = 0; j < n; j++) {
|
|
if (!_highBandCoded[i][j])
|
|
continue;
|
|
|
|
if (val != (int) 0x80000000) {
|
|
int code = _hgainHuffman->getSymbol(bits);
|
|
if (code < 0) {
|
|
warning("WMACodec::decodeNoise(): HGain Huffman invalid");
|
|
return false;
|
|
}
|
|
|
|
val += code - 18;
|
|
|
|
} else
|
|
val = bits.getBits(7) - 19;
|
|
|
|
_highBandValues[i][j] = val;
|
|
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WMACodec::decodeExponents(Common::BitStream8MSB &bits, int bSize, bool *hasChannel) {
|
|
// Exponents can be reused in short blocks
|
|
if (!((_blockLenBits == _frameLenBits) || bits.getBit()))
|
|
return true;
|
|
|
|
for (int i = 0; i < _channels; i++) {
|
|
if (!hasChannel[i])
|
|
continue;
|
|
|
|
if (_useExpHuffman) {
|
|
if (!decodeExpHuffman(bits, i))
|
|
return false;
|
|
} else {
|
|
if (!decodeExpLSP(bits, i))
|
|
return false;
|
|
}
|
|
|
|
_exponentsBSize[i] = bSize;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WMACodec::decodeSpectralCoef(Common::BitStream8MSB &bits, bool msStereo, bool *hasChannel,
|
|
int *coefCount, int coefBitCount) {
|
|
// Simple RLE encoding
|
|
|
|
for (int i = 0; i < _channels; i++) {
|
|
if (hasChannel[i]) {
|
|
// Special Huffman tables are used for MS stereo
|
|
// because there is potentially less energy there.
|
|
const int tindex = ((i == 1) && msStereo);
|
|
|
|
float *ptr = &_coefs1[i][0];
|
|
memset(ptr, 0, _blockLen * sizeof(float));
|
|
|
|
if (!decodeRunLevel(bits, *_coefHuffman[tindex],
|
|
_coefHuffmanLevelTable[tindex], _coefHuffmanRunTable[tindex],
|
|
0, ptr, 0, coefCount[i], _blockLen, _frameLenBits, coefBitCount))
|
|
return false;
|
|
}
|
|
|
|
if ((_version == 1) && (_channels >= 2))
|
|
bits.skip(-bits.pos() & 7);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
float WMACodec::getNormalizedMDCTLength() const {
|
|
const int n4 = _blockLen / 2;
|
|
|
|
float mdctNorm = 1.0 / (float) n4;
|
|
if (_version == 1)
|
|
mdctNorm *= sqrt((float) n4);
|
|
|
|
return mdctNorm;
|
|
}
|
|
|
|
void WMACodec::calculateMDCTCoefficients(int bSize, bool *hasChannel,
|
|
int *coefCount, int totalGain, float mdctNorm) {
|
|
|
|
for (int i = 0; i < _channels; i++) {
|
|
if (!hasChannel[i])
|
|
continue;
|
|
|
|
float *coefs = _coefs[i];
|
|
const float *coefs1 = _coefs1[i];
|
|
const float *exponents = _exponents[i];
|
|
|
|
const int eSize = _exponentsBSize[i];
|
|
|
|
const float mult = (pow(10, totalGain * 0.05) / _maxExponent[i]) * mdctNorm;
|
|
|
|
if (_useNoiseCoding) {
|
|
|
|
// Very low freqs: noise
|
|
for (int j = 0; j < _coefsStart; j++) {
|
|
*coefs++ = _noiseTable[_noiseIndex] * exponents[(j << bSize) >> eSize] * mult;
|
|
|
|
_noiseIndex = (_noiseIndex + 1) & (kNoiseTabSize - 1);
|
|
}
|
|
|
|
// Compute power of high bands
|
|
float expPower[kHighBandSizeMax];
|
|
|
|
const int n1 = _exponentHighSizes[bSize];
|
|
exponents = _exponents[i] + ((_highBandStart[bSize] << bSize) >> eSize);
|
|
|
|
int lastHighBand = 0;
|
|
for (int k = 0; k < n1; k++) {
|
|
const int n = _exponentHighBands[_frameLenBits - _blockLenBits][k];
|
|
|
|
if (_highBandCoded[i][k]) {
|
|
float e2 = 0;
|
|
|
|
for (int j = 0; j < n; j++) {
|
|
const float v = exponents[(j << bSize) >> eSize];
|
|
|
|
e2 += v * v;
|
|
}
|
|
|
|
expPower[k] = e2 / n;
|
|
lastHighBand = k;
|
|
}
|
|
|
|
exponents += (n << bSize) >> eSize;
|
|
}
|
|
|
|
// Main freqs and high freqs
|
|
exponents = _exponents[i] + ((_coefsStart << bSize) >> eSize);
|
|
|
|
for (int k = -1; k < n1; k++) {
|
|
|
|
int n;
|
|
if (k < 0)
|
|
n = _highBandStart[bSize] - _coefsStart;
|
|
else
|
|
n = _exponentHighBands[_frameLenBits - _blockLenBits][k];
|
|
|
|
if (k >= 0 && _highBandCoded[i][k]) {
|
|
// Use noise with specified power
|
|
|
|
float mult1 = sqrt(expPower[k] / expPower[lastHighBand]);
|
|
|
|
mult1 *= pow(10, _highBandValues[i][k] * 0.05);
|
|
mult1 /= _maxExponent[i] * _noiseMult;
|
|
mult1 *= mdctNorm;
|
|
|
|
for (int j = 0; j < n; j++) {
|
|
float noise = _noiseTable[_noiseIndex];
|
|
|
|
_noiseIndex = (_noiseIndex + 1) & (kNoiseTabSize - 1);
|
|
*coefs++ = noise * exponents[(j << bSize) >> eSize] * mult1;
|
|
}
|
|
|
|
exponents += (n << bSize) >> eSize;
|
|
|
|
} else {
|
|
// Coded values + small noise
|
|
|
|
for (int j = 0; j < n; j++) {
|
|
float noise = _noiseTable[_noiseIndex];
|
|
|
|
_noiseIndex = (_noiseIndex + 1) & (kNoiseTabSize - 1);
|
|
*coefs++ = ((*coefs1++) + noise) * exponents[(j << bSize) >> eSize] * mult;
|
|
}
|
|
|
|
exponents += (n << bSize) >> eSize;
|
|
}
|
|
|
|
}
|
|
|
|
// Very high freqs: Noise
|
|
const int n = _blockLen - _coefsEnd[bSize];
|
|
const float mult1 = mult * exponents[(-(1 << bSize)) >> eSize];
|
|
|
|
for (int j = 0; j < n; j++) {
|
|
*coefs++ = _noiseTable[_noiseIndex] * mult1;
|
|
_noiseIndex = (_noiseIndex + 1) & (kNoiseTabSize - 1);
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int j = 0; j < _coefsStart; j++)
|
|
*coefs++ = 0.0;
|
|
|
|
for (int j = 0;j < coefCount[i]; j++) {
|
|
*coefs = coefs1[j] * exponents[(j << bSize) >> eSize] * mult;
|
|
coefs++;
|
|
}
|
|
|
|
int n = _blockLen - _coefsEnd[bSize];
|
|
for (int j = 0; j < n; j++)
|
|
*coefs++ = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static const float powTab[] = {
|
|
1.7782794100389e-04, 2.0535250264571e-04,
|
|
2.3713737056617e-04, 2.7384196342644e-04,
|
|
3.1622776601684e-04, 3.6517412725484e-04,
|
|
4.2169650342858e-04, 4.8696752516586e-04,
|
|
5.6234132519035e-04, 6.4938163157621e-04,
|
|
7.4989420933246e-04, 8.6596432336006e-04,
|
|
1.0000000000000e-03, 1.1547819846895e-03,
|
|
1.3335214321633e-03, 1.5399265260595e-03,
|
|
1.7782794100389e-03, 2.0535250264571e-03,
|
|
2.3713737056617e-03, 2.7384196342644e-03,
|
|
3.1622776601684e-03, 3.6517412725484e-03,
|
|
4.2169650342858e-03, 4.8696752516586e-03,
|
|
5.6234132519035e-03, 6.4938163157621e-03,
|
|
7.4989420933246e-03, 8.6596432336006e-03,
|
|
1.0000000000000e-02, 1.1547819846895e-02,
|
|
1.3335214321633e-02, 1.5399265260595e-02,
|
|
1.7782794100389e-02, 2.0535250264571e-02,
|
|
2.3713737056617e-02, 2.7384196342644e-02,
|
|
3.1622776601684e-02, 3.6517412725484e-02,
|
|
4.2169650342858e-02, 4.8696752516586e-02,
|
|
5.6234132519035e-02, 6.4938163157621e-02,
|
|
7.4989420933246e-02, 8.6596432336007e-02,
|
|
1.0000000000000e-01, 1.1547819846895e-01,
|
|
1.3335214321633e-01, 1.5399265260595e-01,
|
|
1.7782794100389e-01, 2.0535250264571e-01,
|
|
2.3713737056617e-01, 2.7384196342644e-01,
|
|
3.1622776601684e-01, 3.6517412725484e-01,
|
|
4.2169650342858e-01, 4.8696752516586e-01,
|
|
5.6234132519035e-01, 6.4938163157621e-01,
|
|
7.4989420933246e-01, 8.6596432336007e-01,
|
|
1.0000000000000e+00, 1.1547819846895e+00,
|
|
1.3335214321633e+00, 1.5399265260595e+00,
|
|
1.7782794100389e+00, 2.0535250264571e+00,
|
|
2.3713737056617e+00, 2.7384196342644e+00,
|
|
3.1622776601684e+00, 3.6517412725484e+00,
|
|
4.2169650342858e+00, 4.8696752516586e+00,
|
|
5.6234132519035e+00, 6.4938163157621e+00,
|
|
7.4989420933246e+00, 8.6596432336007e+00,
|
|
1.0000000000000e+01, 1.1547819846895e+01,
|
|
1.3335214321633e+01, 1.5399265260595e+01,
|
|
1.7782794100389e+01, 2.0535250264571e+01,
|
|
2.3713737056617e+01, 2.7384196342644e+01,
|
|
3.1622776601684e+01, 3.6517412725484e+01,
|
|
4.2169650342858e+01, 4.8696752516586e+01,
|
|
5.6234132519035e+01, 6.4938163157621e+01,
|
|
7.4989420933246e+01, 8.6596432336007e+01,
|
|
1.0000000000000e+02, 1.1547819846895e+02,
|
|
1.3335214321633e+02, 1.5399265260595e+02,
|
|
1.7782794100389e+02, 2.0535250264571e+02,
|
|
2.3713737056617e+02, 2.7384196342644e+02,
|
|
3.1622776601684e+02, 3.6517412725484e+02,
|
|
4.2169650342858e+02, 4.8696752516586e+02,
|
|
5.6234132519035e+02, 6.4938163157621e+02,
|
|
7.4989420933246e+02, 8.6596432336007e+02,
|
|
1.0000000000000e+03, 1.1547819846895e+03,
|
|
1.3335214321633e+03, 1.5399265260595e+03,
|
|
1.7782794100389e+03, 2.0535250264571e+03,
|
|
2.3713737056617e+03, 2.7384196342644e+03,
|
|
3.1622776601684e+03, 3.6517412725484e+03,
|
|
4.2169650342858e+03, 4.8696752516586e+03,
|
|
5.6234132519035e+03, 6.4938163157621e+03,
|
|
7.4989420933246e+03, 8.6596432336007e+03,
|
|
1.0000000000000e+04, 1.1547819846895e+04,
|
|
1.3335214321633e+04, 1.5399265260595e+04,
|
|
1.7782794100389e+04, 2.0535250264571e+04,
|
|
2.3713737056617e+04, 2.7384196342644e+04,
|
|
3.1622776601684e+04, 3.6517412725484e+04,
|
|
4.2169650342858e+04, 4.8696752516586e+04,
|
|
5.6234132519035e+04, 6.4938163157621e+04,
|
|
7.4989420933246e+04, 8.6596432336007e+04,
|
|
1.0000000000000e+05, 1.1547819846895e+05,
|
|
1.3335214321633e+05, 1.5399265260595e+05,
|
|
1.7782794100389e+05, 2.0535250264571e+05,
|
|
2.3713737056617e+05, 2.7384196342644e+05,
|
|
3.1622776601684e+05, 3.6517412725484e+05,
|
|
4.2169650342858e+05, 4.8696752516586e+05,
|
|
5.6234132519035e+05, 6.4938163157621e+05,
|
|
7.4989420933246e+05, 8.6596432336007e+05,
|
|
};
|
|
|
|
bool WMACodec::decodeExpHuffman(Common::BitStream8MSB &bits, int ch) {
|
|
const float *ptab = powTab + 60;
|
|
const uint32 *iptab = (const uint32 *) ptab;
|
|
|
|
const uint16 *ptr = _exponentBands[_frameLenBits - _blockLenBits];
|
|
|
|
uint32 *q = (uint32 *) _exponents[ch];
|
|
uint32 *qEnd = q + _blockLen;
|
|
|
|
float maxScale = 0;
|
|
|
|
int lastExp;
|
|
if (_version == 1) {
|
|
|
|
lastExp = bits.getBits(5) + 10;
|
|
|
|
float v = ptab[lastExp];
|
|
uint32 iv = iptab[lastExp];
|
|
|
|
maxScale = v;
|
|
|
|
int n = *ptr++;
|
|
|
|
switch (n & 3) do {
|
|
case 0: *q++ = iv; // fall through
|
|
case 3: *q++ = iv; // fall through
|
|
case 2: *q++ = iv; // fall through
|
|
case 1: *q++ = iv;
|
|
} while ((n -= 4) > 0);
|
|
|
|
} else
|
|
lastExp = 36;
|
|
|
|
while (q < qEnd) {
|
|
int code = _expHuffman->getSymbol(bits);
|
|
if (code < 0) {
|
|
warning("WMACodec::decodeExpHuffman(): Exponent invalid");
|
|
return false;
|
|
}
|
|
|
|
// NOTE: This offset is the same as MPEG4 AAC!
|
|
lastExp += code - 60;
|
|
if ((unsigned) lastExp + 60 >= ARRAYSIZE(powTab)) {
|
|
warning("WMACodec::decodeExpHuffman(): Exponent out of range: %d", lastExp);
|
|
return false;
|
|
}
|
|
|
|
float v = ptab[lastExp];
|
|
uint32 iv = iptab[lastExp];
|
|
|
|
if (v > maxScale)
|
|
maxScale = v;
|
|
|
|
int n = *ptr++;
|
|
|
|
switch (n & 3) do {
|
|
case 0: *q++ = iv; // fall through
|
|
case 3: *q++ = iv; // fall through
|
|
case 2: *q++ = iv; // fall through
|
|
case 1: *q++ = iv;
|
|
} while ((n -= 4) > 0);
|
|
|
|
}
|
|
|
|
_maxExponent[ch] = maxScale;
|
|
|
|
return true;
|
|
}
|
|
|
|
void WMACodec::lspToCurve(float *out, float *val_max_ptr, int n, float *lsp) {
|
|
float val_max = 0;
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
float p = 0.5f;
|
|
float q = 0.5f;
|
|
float w = _lspCosTable[i];
|
|
|
|
for (int j = 1; j < kLSPCoefCount; j += 2) {
|
|
q *= w - lsp[j - 1];
|
|
p *= w - lsp[j];
|
|
}
|
|
|
|
p *= p * (2.0f - w);
|
|
q *= q * (2.0f + w);
|
|
|
|
float v = p + q;
|
|
v = pow_m1_4(v);
|
|
|
|
if (v > val_max)
|
|
val_max = v;
|
|
|
|
out[i] = v;
|
|
}
|
|
|
|
*val_max_ptr = val_max;
|
|
}
|
|
|
|
// Decode exponents coded with LSP coefficients (same idea as Vorbis)
|
|
bool WMACodec::decodeExpLSP(Common::BitStream8MSB &bits, int ch) {
|
|
float lspCoefs[kLSPCoefCount];
|
|
|
|
for (int i = 0; i < kLSPCoefCount; i++) {
|
|
int val;
|
|
|
|
if (i == 0 || i >= 8)
|
|
val = bits.getBits(3);
|
|
else
|
|
val = bits.getBits(4);
|
|
|
|
lspCoefs[i] = lspCodebook[i][val];
|
|
}
|
|
|
|
lspToCurve(_exponents[ch], &_maxExponent[ch], _blockLen, lspCoefs);
|
|
return true;
|
|
}
|
|
|
|
bool WMACodec::decodeRunLevel(Common::BitStream8MSB &bits, const HuffmanDecoder &huffman,
|
|
const float *levelTable, const uint16 *runTable, int version, float *ptr,
|
|
int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits) {
|
|
|
|
const uint32 *ilvl = (const uint32*) levelTable;
|
|
uint32 *iptr = (uint32 *) ptr;
|
|
|
|
const unsigned int coefMask = blockLen - 1;
|
|
|
|
for (; offset < numCoefs; offset++) {
|
|
const int code = huffman.getSymbol(bits);
|
|
|
|
if (code > 1) {
|
|
// Normal code
|
|
|
|
const int sign = bits.getBit() - 1;
|
|
|
|
offset += runTable[code];
|
|
|
|
iptr[offset & coefMask] = ilvl[code] ^ (sign << 31);
|
|
|
|
} else if (code == 1) {
|
|
// EOB
|
|
|
|
break;
|
|
|
|
} else {
|
|
// Escape
|
|
|
|
int level;
|
|
|
|
if (!version) {
|
|
|
|
level = bits.getBits(coefNbBits);
|
|
// NOTE: This is rather suboptimal. reading blockLenBits would be better
|
|
offset += bits.getBits(frameLenBits);
|
|
|
|
} else {
|
|
level = getLargeVal(bits);
|
|
|
|
// Escape decode
|
|
if (bits.getBit()) {
|
|
if (bits.getBit()) {
|
|
if (bits.getBit()) {
|
|
warning("WMACodec::decodeRunLevel(): Broken escape sequence");
|
|
return false;
|
|
} else
|
|
offset += bits.getBits(frameLenBits) + 4;
|
|
} else
|
|
offset += bits.getBits(2) + 1;
|
|
}
|
|
|
|
}
|
|
|
|
const int sign = bits.getBit() - 1;
|
|
|
|
ptr[offset & coefMask] = (level ^ sign) - sign;
|
|
|
|
}
|
|
}
|
|
|
|
// NOTE: EOB can be omitted
|
|
if (offset > numCoefs) {
|
|
warning("WMACodec::decodeRunLevel(): Overflow in spectral RLE, ignoring");
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** Apply MDCT window and add into output.
|
|
*
|
|
* We ensure that when the windows overlap their squared sum
|
|
* is always 1 (MDCT reconstruction rule).
|
|
*/
|
|
void WMACodec::window(float *out) const {
|
|
const float *in = _output;
|
|
|
|
// Left part
|
|
if (_blockLenBits <= _prevBlockLenBits) {
|
|
|
|
const int bSize = _frameLenBits - _blockLenBits;
|
|
|
|
vectorFMulAdd(out, in, _mdctWindow[bSize], out, _blockLen);
|
|
|
|
} else {
|
|
|
|
const int blockLen = 1 << _prevBlockLenBits;
|
|
const int n = (_blockLen - blockLen) / 2;
|
|
|
|
const int bSize = _frameLenBits - _prevBlockLenBits;
|
|
|
|
vectorFMulAdd(out + n, in + n, _mdctWindow[bSize], out + n, blockLen);
|
|
|
|
memcpy(out + n + blockLen, in + n + blockLen, n * sizeof(float));
|
|
}
|
|
|
|
out += _blockLen;
|
|
in += _blockLen;
|
|
|
|
// Right part
|
|
if (_blockLenBits <= _nextBlockLenBits) {
|
|
|
|
const int bSize = _frameLenBits - _blockLenBits;
|
|
|
|
vectorFMulReverse(out, in, _mdctWindow[bSize], _blockLen);
|
|
|
|
} else {
|
|
|
|
const int blockLen = 1 << _nextBlockLenBits;
|
|
const int n = (_blockLen - blockLen) / 2;
|
|
|
|
const int bSize = _frameLenBits - _nextBlockLenBits;
|
|
|
|
memcpy(out, in, n*sizeof(float));
|
|
|
|
vectorFMulReverse(out + n, in + n, _mdctWindow[bSize], blockLen);
|
|
|
|
memset(out + n + blockLen, 0, n * sizeof(float));
|
|
}
|
|
}
|
|
|
|
float WMACodec::pow_m1_4(float x) const {
|
|
union {
|
|
float f;
|
|
unsigned int v;
|
|
} u, t;
|
|
|
|
u.f = x;
|
|
|
|
const unsigned int e = u.v >> 23;
|
|
const unsigned int m = (u.v >> (23 - kLSPPowBits)) & ((1 << kLSPPowBits) - 1);
|
|
|
|
// Build interpolation scale: 1 <= t < 2
|
|
t.v = ((u.v << kLSPPowBits) & ((1 << 23) - 1)) | (127 << 23);
|
|
|
|
const float a = _lspPowMTable1[m];
|
|
const float b = _lspPowMTable2[m];
|
|
|
|
return _lspPowETable[e] * (a + b * t.f);
|
|
}
|
|
|
|
int WMACodec::readTotalGain(Common::BitStream8MSB &bits) {
|
|
int totalGain = 1;
|
|
|
|
int v = 127;
|
|
while (v == 127) {
|
|
v = bits.getBits(7);
|
|
|
|
totalGain += v;
|
|
}
|
|
|
|
return totalGain;
|
|
}
|
|
|
|
int WMACodec::totalGainToBits(int totalGain) {
|
|
if (totalGain < 15) return 13;
|
|
else if (totalGain < 32) return 12;
|
|
else if (totalGain < 40) return 11;
|
|
else if (totalGain < 45) return 10;
|
|
else return 9;
|
|
}
|
|
|
|
uint32 WMACodec::getLargeVal(Common::BitStream8MSB &bits) {
|
|
// Consumes up to 34 bits
|
|
|
|
int count = 8;
|
|
if (bits.getBit()) {
|
|
count += 8;
|
|
|
|
if (bits.getBit()) {
|
|
count += 8;
|
|
|
|
if (bits.getBit())
|
|
count += 7;
|
|
}
|
|
}
|
|
|
|
return bits.getBits(count);
|
|
}
|
|
|
|
} // End of namespace Audio
|