mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-21 19:51:49 +00:00
AUDIO: Fix remaining AAC bugs by decoding with the same AAC context
This introduces a new Audio::Codec class, based on DrMcCoy's solution for WMA in eos.
This commit is contained in:
parent
0d02cc0ef5
commit
c46aa548d6
@ -28,74 +28,34 @@
|
||||
#ifdef USE_FAAD
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/codec.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include <neaacdec.h>
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AACStream : public AudioStream {
|
||||
class AACDecoder : public Codec {
|
||||
public:
|
||||
AACStream(Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeStream,
|
||||
Common::SeekableReadStream *extraData,
|
||||
AACDecoder(Common::SeekableReadStream *extraData,
|
||||
DisposeAfterUse::Flag disposeExtraData);
|
||||
~AACStream();
|
||||
~AACDecoder();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
bool endOfData() const { return _inBufferPos >= _inBufferSize && !_remainingSamples; }
|
||||
bool isStereo() const { return _channels == 2; }
|
||||
int getRate() const { return _rate; }
|
||||
AudioStream *decodeFrame(Common::SeekableReadStream &stream);
|
||||
|
||||
private:
|
||||
NeAACDecHandle _handle;
|
||||
byte _channels;
|
||||
unsigned long _rate;
|
||||
|
||||
byte *_inBuffer;
|
||||
uint32 _inBufferSize;
|
||||
uint32 _inBufferPos;
|
||||
|
||||
int16 *_remainingSamples;
|
||||
uint32 _remainingSamplesSize;
|
||||
uint32 _remainingSamplesPos;
|
||||
|
||||
void init(Common::SeekableReadStream *extraData);
|
||||
};
|
||||
|
||||
AACStream::AACStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeStream,
|
||||
Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
|
||||
|
||||
_remainingSamples = 0;
|
||||
_inBufferPos = 0;
|
||||
|
||||
init(extraData);
|
||||
|
||||
// Copy all the data to a pointer so it can be passed through
|
||||
// (At least MPEG-4 chunks shouldn't be large)
|
||||
_inBufferSize = stream->size();
|
||||
_inBuffer = new byte[_inBufferSize];
|
||||
stream->read(_inBuffer, _inBufferSize);
|
||||
|
||||
if (disposeStream == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
|
||||
if (disposeExtraData == DisposeAfterUse::YES)
|
||||
delete extraData;
|
||||
}
|
||||
|
||||
AACStream::~AACStream() {
|
||||
NeAACDecClose(_handle);
|
||||
delete[] _inBuffer;
|
||||
delete[] _remainingSamples;
|
||||
}
|
||||
|
||||
void AACStream::init(Common::SeekableReadStream *extraData) {
|
||||
AACDecoder::AACDecoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
|
||||
// Open the library
|
||||
_handle = NeAACDecOpen();
|
||||
|
||||
@ -117,59 +77,55 @@ void AACStream::init(Common::SeekableReadStream *extraData) {
|
||||
|
||||
if (err < 0)
|
||||
error("Could not initialize AAC decoder: %s", NeAACDecGetErrorMessage(err));
|
||||
|
||||
if (disposeExtraData == DisposeAfterUse::YES)
|
||||
delete extraData;
|
||||
}
|
||||
|
||||
int AACStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
AACDecoder::~AACDecoder() {
|
||||
NeAACDecClose(_handle);
|
||||
}
|
||||
|
||||
assert((numSamples % _channels) == 0);
|
||||
AudioStream *AACDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
// read everything into a buffer
|
||||
uint32 inBufferPos = 0;
|
||||
uint32 inBufferSize = stream.size();
|
||||
byte *inBuffer = new byte[inBufferSize];
|
||||
stream.read(inBuffer, inBufferSize);
|
||||
|
||||
// Dip into our remaining samples pool if it's available
|
||||
if (_remainingSamples) {
|
||||
samples = MIN<int>(numSamples, _remainingSamplesSize - _remainingSamplesPos);
|
||||
|
||||
memcpy(buffer, _remainingSamples + _remainingSamplesPos, samples * 2);
|
||||
_remainingSamplesPos += samples;
|
||||
|
||||
if (_remainingSamplesPos == _remainingSamplesSize) {
|
||||
delete[] _remainingSamples;
|
||||
_remainingSamples = 0;
|
||||
}
|
||||
}
|
||||
QueuingAudioStream *audioStream = makeQueuingAudioStream(_rate, _channels == 2);
|
||||
|
||||
// Decode until we have enough samples (or there's no more left)
|
||||
while (samples < numSamples && !endOfData()) {
|
||||
while (inBufferPos < inBufferSize) {
|
||||
NeAACDecFrameInfo frameInfo;
|
||||
uint16 *decodedSamples = (uint16 *)NeAACDecDecode(_handle, &frameInfo, _inBuffer + _inBufferPos, _inBufferSize - _inBufferPos);
|
||||
void *decodedSamples = NeAACDecDecode(_handle, &frameInfo, inBuffer + inBufferPos, inBufferSize - inBufferPos);
|
||||
|
||||
if (frameInfo.error != 0)
|
||||
error("Failed to decode AAC frame: %s", NeAACDecGetErrorMessage(frameInfo.error));
|
||||
|
||||
int decodedSampleSize = frameInfo.samples;
|
||||
int copySamples = (decodedSampleSize > (numSamples - samples)) ? (numSamples - samples) : decodedSampleSize;
|
||||
byte *buffer = (byte *)malloc(frameInfo.samples * 2);
|
||||
memcpy(buffer, decodedSamples, frameInfo.samples * 2);
|
||||
|
||||
memcpy(buffer + samples, decodedSamples, copySamples * 2);
|
||||
samples += copySamples;
|
||||
byte flags = FLAG_16BITS;
|
||||
|
||||
// Copy leftover samples for use in a later readBuffer() call
|
||||
if (copySamples != decodedSampleSize) {
|
||||
_remainingSamplesSize = decodedSampleSize - copySamples;
|
||||
_remainingSamples = new int16[_remainingSamplesSize];
|
||||
_remainingSamplesPos = 0;
|
||||
memcpy(_remainingSamples, decodedSamples + copySamples, _remainingSamplesSize * 2);
|
||||
}
|
||||
if (_channels == 2)
|
||||
flags |= FLAG_STEREO;
|
||||
|
||||
_inBufferPos += frameInfo.bytesconsumed;
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
flags |= FLAG_LITTLE_ENDIAN;
|
||||
#endif
|
||||
|
||||
audioStream->queueBuffer(buffer, frameInfo.samples * 2, DisposeAfterUse::YES, flags);
|
||||
|
||||
inBufferPos += frameInfo.bytesconsumed;
|
||||
}
|
||||
|
||||
return samples;
|
||||
return audioStream;
|
||||
}
|
||||
|
||||
// Factory function
|
||||
AudioStream *makeAACStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeStream,
|
||||
Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
|
||||
|
||||
return new AACStream(stream, disposeStream, extraData, disposeExtraData);
|
||||
Codec *makeAACDecoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
|
||||
return new AACDecoder(extraData, disposeExtraData);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
@ -43,23 +43,19 @@ namespace Common {
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AudioStream;
|
||||
class Codec;
|
||||
|
||||
/**
|
||||
* Create a new AudioStream from the AAC data of an MPEG-4 file in the given stream.
|
||||
* Create a new Codec for decoding AAC data of an MPEG-4 file in the given stream.
|
||||
*
|
||||
* @note This should *only* be called by our QuickTime/MPEG-4 decoder since it relies
|
||||
* on the MPEG-4 extra data. If you want to decode a file using AAC, go use
|
||||
* makeQuickTimeStream() instead!
|
||||
* @param stream the SeekableReadStream from which to read the AAC data
|
||||
* @param disposeStream whether to delete the stream after use
|
||||
* @param extraData the SeekableReadStream from which to read the AAC extra data
|
||||
* @param disposeExtraData whether to delete the extra data stream after use
|
||||
* @return a new AudioStream, or NULL, if an error occurred
|
||||
* @return a new Codec, or NULL, if an error occurred
|
||||
*/
|
||||
AudioStream *makeAACStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeStream,
|
||||
Codec *makeAACDecoder(
|
||||
Common::SeekableReadStream *extraData,
|
||||
DisposeAfterUse::Flag disposeExtraData = DisposeAfterUse::NO);
|
||||
|
||||
|
47
audio/decoders/codec.h
Normal file
47
audio/decoders/codec.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_DECODERS_CODEC_H
|
||||
#define AUDIO_DECODERS_CODEC_H
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AudioStream;
|
||||
|
||||
class Codec {
|
||||
public:
|
||||
Codec() {}
|
||||
virtual ~Codec() {}
|
||||
|
||||
virtual AudioStream *decodeFrame(Common::SeekableReadStream &data) = 0;
|
||||
};
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
@ -30,6 +30,7 @@
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/codec.h"
|
||||
#include "audio/decoders/quicktime.h"
|
||||
#include "audio/decoders/quicktime_intern.h"
|
||||
|
||||
@ -86,6 +87,9 @@ void QuickTimeAudioDecoder::init() {
|
||||
// Make sure the bits per sample transfers to the sample size
|
||||
if (entry->getCodecTag() == MKTAG('r', 'a', 'w', ' ') || entry->getCodecTag() == MKTAG('t', 'w', 'o', 's'))
|
||||
_tracks[_audioTrackIndex]->sampleSize = (entry->_bitsPerSample / 8) * entry->_channels;
|
||||
|
||||
// Initialize the codec (if necessary)
|
||||
entry->initCodec();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -217,6 +221,9 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) {
|
||||
Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_tracks[_audioTrackIndex]->sampleDescs[0];
|
||||
_audStream = Audio::makeQueuingAudioStream(entry->_sampleRate, entry->_channels == 2);
|
||||
|
||||
// Reinitialize the codec
|
||||
entry->initCodec();
|
||||
|
||||
// First, we need to track down what audio sample we need
|
||||
Audio::Timestamp curAudioTime = where.convertToFramerate(_tracks[_audioTrackIndex]->timeScale);
|
||||
uint32 sample = curAudioTime.totalNumberOfFrames();
|
||||
@ -266,6 +273,11 @@ QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc(Common::QuickTimeParser:
|
||||
_samplesPerFrame = 0;
|
||||
_bytesPerFrame = 0;
|
||||
_bitsPerSample = 0;
|
||||
_codec = 0;
|
||||
}
|
||||
|
||||
QuickTimeAudioDecoder::AudioSampleDesc::~AudioSampleDesc() {
|
||||
delete _codec;
|
||||
}
|
||||
|
||||
bool QuickTimeAudioDecoder::AudioSampleDesc::isAudioCodecSupported() const {
|
||||
@ -313,7 +325,12 @@ AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::S
|
||||
if (!stream)
|
||||
return 0;
|
||||
|
||||
if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ')) {
|
||||
if (_codec) {
|
||||
// If we've loaded a codec, make sure we use first
|
||||
AudioStream *audioStream = _codec->decodeFrame(*stream);
|
||||
delete stream;
|
||||
return audioStream;
|
||||
} else if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ')) {
|
||||
// Fortunately, most of the audio used in Myst videos is raw...
|
||||
uint16 flags = 0;
|
||||
if (_codecTag == MKTAG('r', 'a', 'w', ' '))
|
||||
@ -330,12 +347,6 @@ AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::S
|
||||
} else if (_codecTag == MKTAG('i', 'm', 'a', '4')) {
|
||||
// Riven uses this codec (as do some Myst ME videos)
|
||||
return makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), kADPCMApple, _sampleRate, _channels, 34);
|
||||
} else if (_codecTag == MKTAG('m', 'p', '4', 'a')) {
|
||||
// The 7th Guest iOS uses an MPEG-4 codec
|
||||
#ifdef USE_FAAD
|
||||
if (_parentTrack->objectTypeMP4 == 0x40)
|
||||
return makeAACStream(stream, DisposeAfterUse::YES, _parentTrack->extraData);
|
||||
#endif
|
||||
#ifdef AUDIO_QDM2_H
|
||||
} else if (_codecTag == MKTAG('Q', 'D', 'M', '2')) {
|
||||
// Myst ME uses this codec for many videos
|
||||
@ -344,10 +355,24 @@ AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::S
|
||||
}
|
||||
|
||||
error("Unsupported audio codec");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void QuickTimeAudioDecoder::AudioSampleDesc::initCodec() {
|
||||
delete _codec; _codec = 0;
|
||||
|
||||
switch (_codecTag) {
|
||||
case MKTAG('m', 'p', '4', 'a'):
|
||||
#ifdef USE_FAAD
|
||||
if (_parentTrack->objectTypeMP4 == 0x40)
|
||||
_codec = makeAACDecoder(_parentTrack->extraData);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around QuickTimeAudioDecoder that implements the RewindableAudioStream API
|
||||
*/
|
||||
|
@ -45,6 +45,7 @@ namespace Common {
|
||||
namespace Audio {
|
||||
|
||||
class AudioStream;
|
||||
class Codec;
|
||||
class QueuingAudioStream;
|
||||
|
||||
class QuickTimeAudioDecoder : public Common::QuickTimeParser {
|
||||
@ -68,10 +69,12 @@ protected:
|
||||
class AudioSampleDesc : public Common::QuickTimeParser::SampleDesc {
|
||||
public:
|
||||
AudioSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
|
||||
~AudioSampleDesc();
|
||||
|
||||
bool isAudioCodecSupported() const;
|
||||
uint32 getAudioChunkSampleCount(uint chunk) const;
|
||||
AudioStream *createAudioStream(Common::SeekableReadStream *stream) const;
|
||||
void initCodec();
|
||||
|
||||
// TODO: Make private in the long run
|
||||
uint16 _bitsPerSample;
|
||||
@ -79,6 +82,8 @@ protected:
|
||||
uint32 _sampleRate;
|
||||
uint32 _samplesPerFrame;
|
||||
uint32 _bytesPerFrame;
|
||||
|
||||
Codec *_codec;
|
||||
};
|
||||
|
||||
// Common::QuickTimeParser API
|
||||
|
Loading…
x
Reference in New Issue
Block a user