AUDIO: Fix QDM2 sound in QuickTime files
This commit is contained in:
parent
c46aa548d6
commit
46aabed3f5
3 changed files with 66 additions and 59 deletions
|
@ -28,7 +28,9 @@
|
||||||
#ifdef AUDIO_QDM2_H
|
#ifdef AUDIO_QDM2_H
|
||||||
|
|
||||||
#include "audio/audiostream.h"
|
#include "audio/audiostream.h"
|
||||||
|
#include "audio/decoders/codec.h"
|
||||||
#include "audio/decoders/qdm2data.h"
|
#include "audio/decoders/qdm2data.h"
|
||||||
|
#include "audio/decoders/raw.h"
|
||||||
|
|
||||||
#include "common/array.h"
|
#include "common/array.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
|
@ -150,19 +152,14 @@ struct RDFTContext {
|
||||||
FFTContext fft;
|
FFTContext fft;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QDM2Stream : public AudioStream {
|
class QDM2Stream : public Codec {
|
||||||
public:
|
public:
|
||||||
QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData);
|
QDM2Stream(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData);
|
||||||
~QDM2Stream();
|
~QDM2Stream();
|
||||||
|
|
||||||
bool isStereo() const { return _channels == 2; }
|
AudioStream *decodeFrame(Common::SeekableReadStream &stream);
|
||||||
bool endOfData() const { return _stream->pos() >= _stream->size() && _outputSamples.size() == 0 && _subPacket == 0; }
|
|
||||||
int getRate() const { return _sampleRate; }
|
|
||||||
int readBuffer(int16 *buffer, const int numSamples);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Common::SeekableReadStream *_stream;
|
|
||||||
|
|
||||||
// Parameters from codec header, do not change during playback
|
// Parameters from codec header, do not change during playback
|
||||||
uint8 _channels;
|
uint8 _channels;
|
||||||
uint16 _sampleRate;
|
uint16 _sampleRate;
|
||||||
|
@ -204,7 +201,6 @@ private:
|
||||||
// I/O data
|
// I/O data
|
||||||
uint8 *_compressedData;
|
uint8 *_compressedData;
|
||||||
float _outputBuffer[1024];
|
float _outputBuffer[1024];
|
||||||
Common::Array<int16> _outputSamples;
|
|
||||||
|
|
||||||
// Synthesis filter
|
// Synthesis filter
|
||||||
int16 ff_mpa_synth_window[512];
|
int16 ff_mpa_synth_window[512];
|
||||||
|
@ -285,7 +281,7 @@ private:
|
||||||
void qdm2_fft_tone_synthesizer(uint8 sub_packet);
|
void qdm2_fft_tone_synthesizer(uint8 sub_packet);
|
||||||
void qdm2_calculate_fft(int channel);
|
void qdm2_calculate_fft(int channel);
|
||||||
void qdm2_synthesis_filter(uint8 index);
|
void qdm2_synthesis_filter(uint8 index);
|
||||||
int qdm2_decodeFrame(Common::SeekableReadStream *in);
|
bool qdm2_decodeFrame(Common::SeekableReadStream &in, QueuingAudioStream *audioStream);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fix compilation for non C99-compliant compilers, like MSVC
|
// Fix compilation for non C99-compliant compilers, like MSVC
|
||||||
|
@ -1711,7 +1707,7 @@ void QDM2Stream::initVlc(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData) {
|
QDM2Stream::QDM2Stream(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
|
||||||
uint32 tmp;
|
uint32 tmp;
|
||||||
int32 tmp_s;
|
int32 tmp_s;
|
||||||
int tmp_val;
|
int tmp_val;
|
||||||
|
@ -1719,7 +1715,6 @@ QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadS
|
||||||
|
|
||||||
debug(1, "QDM2Stream::QDM2Stream() Call");
|
debug(1, "QDM2Stream::QDM2Stream() Call");
|
||||||
|
|
||||||
_stream = stream;
|
|
||||||
_compressedData = NULL;
|
_compressedData = NULL;
|
||||||
_subPacket = 0;
|
_subPacket = 0;
|
||||||
_superBlockStart = 0;
|
_superBlockStart = 0;
|
||||||
|
@ -1906,11 +1901,13 @@ QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadS
|
||||||
initNoiseSamples();
|
initNoiseSamples();
|
||||||
|
|
||||||
_compressedData = new uint8[_packetSize];
|
_compressedData = new uint8[_packetSize];
|
||||||
|
|
||||||
|
if (disposeExtraData == DisposeAfterUse::YES)
|
||||||
|
delete extraData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDM2Stream::~QDM2Stream() {
|
QDM2Stream::~QDM2Stream() {
|
||||||
delete[] _compressedData;
|
delete[] _compressedData;
|
||||||
delete _stream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qdm2_get_vlc(GetBitContext *gb, VLC *vlc, int flag, int depth) {
|
static int qdm2_get_vlc(GetBitContext *gb, VLC *vlc, int flag, int depth) {
|
||||||
|
@ -3158,30 +3155,30 @@ void QDM2Stream::qdm2_synthesis_filter(uint8 index)
|
||||||
_outputBuffer[_channels * i + ch] += (float)(samples[_channels * sub_sampling * i + ch] >> (sizeof(int16)*8-16));
|
_outputBuffer[_channels * i + ch] += (float)(samples[_channels * sub_sampling * i + ch] >> (sizeof(int16)*8-16));
|
||||||
}
|
}
|
||||||
|
|
||||||
int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) {
|
bool QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream &in, QueuingAudioStream *audioStream) {
|
||||||
debug(1, "QDM2Stream::qdm2_decodeFrame in->pos(): %d in->size(): %d", in->pos(), in->size());
|
debug(1, "QDM2Stream::qdm2_decodeFrame in.pos(): %d in.size(): %d", in.pos(), in.size());
|
||||||
int ch, i;
|
int ch, i;
|
||||||
const int frame_size = (_sFrameSize * _channels);
|
const int frame_size = (_sFrameSize * _channels);
|
||||||
|
|
||||||
// If we're in any packet but the first, seek back to the first
|
// If we're in any packet but the first, seek back to the first
|
||||||
if (_subPacket == 0)
|
if (_subPacket == 0)
|
||||||
_superBlockStart = in->pos();
|
_superBlockStart = in.pos();
|
||||||
else
|
else
|
||||||
in->seek(_superBlockStart);
|
in.seek(_superBlockStart);
|
||||||
|
|
||||||
// select input buffer
|
// select input buffer
|
||||||
if (in->eos() || in->pos() >= in->size()) {
|
if (in.eos() || in.pos() >= in.size()) {
|
||||||
debug(1, "QDM2Stream::qdm2_decodeFrame End of Input Stream");
|
debug(1, "QDM2Stream::qdm2_decodeFrame End of Input Stream");
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((in->size() - in->pos()) < _packetSize) {
|
if ((in.size() - in.pos()) < _packetSize) {
|
||||||
debug(1, "QDM2Stream::qdm2_decodeFrame Insufficient Packet Data in Input Stream Found: %d Need: %d", in->size() - in->pos(), _packetSize);
|
debug(1, "QDM2Stream::qdm2_decodeFrame Insufficient Packet Data in Input Stream Found: %d Need: %d", in.size() - in.pos(), _packetSize);
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in->eos()) {
|
if (!in.eos()) {
|
||||||
in->read(_compressedData, _packetSize);
|
in.read(_compressedData, _packetSize);
|
||||||
debug(1, "QDM2Stream::qdm2_decodeFrame constructed input data");
|
debug(1, "QDM2Stream::qdm2_decodeFrame constructed input data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3190,7 +3187,7 @@ int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) {
|
||||||
memset(&_outputBuffer[frame_size], 0, frame_size * sizeof(float));
|
memset(&_outputBuffer[frame_size], 0, frame_size * sizeof(float));
|
||||||
debug(1, "QDM2Stream::qdm2_decodeFrame cleared outputBuffer");
|
debug(1, "QDM2Stream::qdm2_decodeFrame cleared outputBuffer");
|
||||||
|
|
||||||
if (!in->eos()) {
|
if (!in.eos()) {
|
||||||
// decode block of QDM2 compressed data
|
// decode block of QDM2 compressed data
|
||||||
debug(1, "QDM2Stream::qdm2_decodeFrame decode block of QDM2 compressed data");
|
debug(1, "QDM2Stream::qdm2_decodeFrame decode block of QDM2 compressed data");
|
||||||
if (_subPacket == 0) {
|
if (_subPacket == 0) {
|
||||||
|
@ -3218,7 +3215,7 @@ int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) {
|
||||||
|
|
||||||
if (!_hasErrors && _subPacketListC[0].packet != NULL) {
|
if (!_hasErrors && _subPacketListC[0].packet != NULL) {
|
||||||
error("QDM2 : has errors, and C list is not empty");
|
error("QDM2 : has errors, and C list is not empty");
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3236,6 +3233,12 @@ int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) {
|
||||||
debug(1, "QDM2Stream::qdm2_decodeFrame clip and convert output float[] to 16bit signed samples");
|
debug(1, "QDM2Stream::qdm2_decodeFrame clip and convert output float[] to 16bit signed samples");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frame_size == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Prepare a buffer for queuing
|
||||||
|
uint16 *outputBuffer = (uint16 *)malloc(frame_size * 2);
|
||||||
|
|
||||||
for (i = 0; i < frame_size; i++) {
|
for (i = 0; i < frame_size; i++) {
|
||||||
int value = (int)_outputBuffer[i];
|
int value = (int)_outputBuffer[i];
|
||||||
|
|
||||||
|
@ -3244,34 +3247,35 @@ int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) {
|
||||||
else if (value < -SOFTCLIP_THRESHOLD)
|
else if (value < -SOFTCLIP_THRESHOLD)
|
||||||
value = (value < -HARDCLIP_THRESHOLD) ? -32767 : -_softclipTable[-value - SOFTCLIP_THRESHOLD];
|
value = (value < -HARDCLIP_THRESHOLD) ? -32767 : -_softclipTable[-value - SOFTCLIP_THRESHOLD];
|
||||||
|
|
||||||
_outputSamples.push_back(value);
|
outputBuffer[i] = value;
|
||||||
}
|
|
||||||
return frame_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QDM2Stream::readBuffer(int16 *buffer, const int numSamples) {
|
|
||||||
debug(1, "QDM2Stream::readBuffer numSamples: %d", numSamples);
|
|
||||||
int32 decodedSamples = _outputSamples.size();
|
|
||||||
int32 i;
|
|
||||||
|
|
||||||
while (decodedSamples < numSamples) {
|
|
||||||
i = qdm2_decodeFrame(_stream);
|
|
||||||
if (i == 0)
|
|
||||||
break; // Out Of Decode Frames...
|
|
||||||
decodedSamples += i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decodedSamples > numSamples)
|
// Queue the translated buffer to our stream
|
||||||
decodedSamples = numSamples;
|
byte flags = FLAG_16BITS;
|
||||||
|
|
||||||
for (i = 0; i < decodedSamples; i++)
|
if (_channels == 2)
|
||||||
buffer[i] = _outputSamples.remove_at(0);
|
flags |= FLAG_STEREO;
|
||||||
|
|
||||||
return decodedSamples;
|
#ifdef SCUMM_LITTLE_ENDIAN
|
||||||
|
flags |= FLAG_LITTLE_ENDIAN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
audioStream->queueBuffer((byte *)outputBuffer, frame_size * 2, DisposeAfterUse::YES, flags);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData) {
|
AudioStream *QDM2Stream::decodeFrame(Common::SeekableReadStream &stream) {
|
||||||
return new QDM2Stream(stream, extraData);
|
QueuingAudioStream *audioStream = makeQueuingAudioStream(_sampleRate, _channels == 2);
|
||||||
|
|
||||||
|
while (qdm2_decodeFrame(stream, audioStream))
|
||||||
|
;
|
||||||
|
|
||||||
|
return audioStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
Codec *makeQDM2Decoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
|
||||||
|
return new QDM2Stream(extraData, disposeExtraData);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // End of namespace Audio
|
} // End of namespace Audio
|
||||||
|
|
|
@ -26,22 +26,25 @@
|
||||||
#ifndef AUDIO_QDM2_H
|
#ifndef AUDIO_QDM2_H
|
||||||
#define AUDIO_QDM2_H
|
#define AUDIO_QDM2_H
|
||||||
|
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
class SeekableReadStream;
|
class SeekableReadStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Audio {
|
namespace Audio {
|
||||||
|
|
||||||
class AudioStream;
|
class Codec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new AudioStream from the QDM2 data in the given stream.
|
* Create a new Codec from the QDM2 data in the given stream.
|
||||||
*
|
*
|
||||||
* @param stream the SeekableReadStream from which to read the FLAC data
|
* @param extraData the QuickTime extra data stream
|
||||||
* @param extraData the QuickTime extra data stream
|
* @param disposeExtraData the QuickTime extra data stream
|
||||||
* @return a new AudioStream, or NULL, if an error occurred
|
* @return a new Codec, or NULL, if an error occurred
|
||||||
*/
|
*/
|
||||||
AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData);
|
Codec *makeQDM2Decoder(Common::SeekableReadStream *extraData,
|
||||||
|
DisposeAfterUse::Flag disposeExtraData = DisposeAfterUse::NO);
|
||||||
|
|
||||||
} // End of namespace Audio
|
} // End of namespace Audio
|
||||||
|
|
||||||
|
|
|
@ -347,11 +347,6 @@ AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::S
|
||||||
} else if (_codecTag == MKTAG('i', 'm', 'a', '4')) {
|
} else if (_codecTag == MKTAG('i', 'm', 'a', '4')) {
|
||||||
// Riven uses this codec (as do some Myst ME videos)
|
// Riven uses this codec (as do some Myst ME videos)
|
||||||
return makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), kADPCMApple, _sampleRate, _channels, 34);
|
return makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), kADPCMApple, _sampleRate, _channels, 34);
|
||||||
#ifdef AUDIO_QDM2_H
|
|
||||||
} else if (_codecTag == MKTAG('Q', 'D', 'M', '2')) {
|
|
||||||
// Myst ME uses this codec for many videos
|
|
||||||
return makeQDM2Stream(stream, _parentTrack->extraData);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error("Unsupported audio codec");
|
error("Unsupported audio codec");
|
||||||
|
@ -362,6 +357,11 @@ void QuickTimeAudioDecoder::AudioSampleDesc::initCodec() {
|
||||||
delete _codec; _codec = 0;
|
delete _codec; _codec = 0;
|
||||||
|
|
||||||
switch (_codecTag) {
|
switch (_codecTag) {
|
||||||
|
case MKTAG('Q', 'D', 'M', '2'):
|
||||||
|
#ifdef AUDIO_QDM2_H
|
||||||
|
_codec = makeQDM2Decoder(_parentTrack->extraData);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
case MKTAG('m', 'p', '4', 'a'):
|
case MKTAG('m', 'p', '4', 'a'):
|
||||||
#ifdef USE_FAAD
|
#ifdef USE_FAAD
|
||||||
if (_parentTrack->objectTypeMP4 == 0x40)
|
if (_parentTrack->objectTypeMP4 == 0x40)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue