ZVISION: Boost volume for MPEG cutscenes

The high-resolution videos play back at much lower volume than the
original ones. This adds hard-coded values for how much to amplify
each cutscene. It's all done by ear, and it does introduce some
clipping, but I think it should be acceptable.

Of course, it could also be a problem with the audio decoder, so
this may be the wrong approach entirely.
This commit is contained in:
Torbjörn Andersson 2019-01-18 19:19:25 +01:00 committed by Filippos Karapetis
parent 82a1859ad1
commit 9785d5007a
5 changed files with 116 additions and 16 deletions

View file

@ -24,6 +24,7 @@
#include "common/ptr.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "audio/audiostream.h"
#include "audio/decoders/ac3.h"
@ -37,7 +38,7 @@ namespace Audio {
class AC3Stream : public PacketizedAudioStream {
public:
AC3Stream();
AC3Stream(double decibel);
~AC3Stream();
bool init(Common::SeekableReadStream &firstPacket);
@ -62,9 +63,14 @@ private:
byte *_inBufPtr;
int _flags;
int _sampleRate;
double _audioGain;
};
AC3Stream::AC3Stream() : _a52State(0), _frameSize(0), _inBufPtr(0), _flags(0), _sampleRate(0) {
AC3Stream::AC3Stream(double decibel = 0.0) : _a52State(0), _frameSize(0), _inBufPtr(0), _flags(0), _sampleRate(0) {
if (decibel != 0.0)
_audioGain = pow(2, decibel / 6);
else
_audioGain = 1.0;
}
AC3Stream::~AC3Stream() {
@ -153,7 +159,7 @@ void AC3Stream::queuePacket(Common::SeekableReadStream *data) {
} else {
// TODO: Eventually support more than just stereo max
int flags = A52_STEREO | A52_ADJUST_LEVEL;
sample_t level = 32767;
sample_t level = 32767 * _audioGain;
if (a52_frame(_a52State, _inBuf, &flags, &level, 0) != 0)
error("Frame fail");
@ -165,8 +171,8 @@ void AC3Stream::queuePacket(Common::SeekableReadStream *data) {
if (a52_block(_a52State) == 0) {
sample_t *samples = a52_samples(_a52State);
for (int j = 0; j < 256; j++) {
*outputPtr++ = (int16)samples[j];
*outputPtr++ = (int16)samples[j + 256];
*outputPtr++ = (int16)CLIP<sample_t>(samples[j], -32768, 32767);
*outputPtr++ = (int16)CLIP<sample_t>(samples[j + 256], -32768, 32767);
}
outputLength += 1024;
@ -189,8 +195,8 @@ void AC3Stream::queuePacket(Common::SeekableReadStream *data) {
}
}
PacketizedAudioStream *makeAC3Stream(Common::SeekableReadStream &firstPacket) {
Common::ScopedPtr<AC3Stream> stream(new AC3Stream());
PacketizedAudioStream *makeAC3Stream(Common::SeekableReadStream &firstPacket, double decibel) {
Common::ScopedPtr<AC3Stream> stream(new AC3Stream(decibel));
if (!stream->init(firstPacket))
return 0;

View file

@ -41,7 +41,7 @@ class PacketizedAudioStream;
* @param firstPacket The stream containing the first packet of data
* @return A new PacketizedAudioStream, or NULL on error
*/
PacketizedAudioStream *makeAC3Stream(Common::SeekableReadStream &firstPacket);
PacketizedAudioStream *makeAC3Stream(Common::SeekableReadStream &firstPacket, double decibel = 0.0);
} // End of namespace Audio

View file

@ -49,8 +49,99 @@ Video::VideoDecoder *ZVision::loadAnimation(const Common::String &fileName) {
else if (tmpFileName.hasSuffix(".avi"))
animation = new ZorkAVIDecoder();
#if defined(USE_MPEG2) && defined(USE_A52)
else if (tmpFileName.hasSuffix(".vob"))
animation = new Video::MPEGPSDecoder();
else if (tmpFileName.hasSuffix(".vob")) {
// For some reason, we get much lower volume in the hi-res
// videos than in the low-res ones. So we artificially boost
// the volume here. This is an approximation, but I've tried
// to match the old volumes reasonably well.
//
// Some of these will cause audio clipping. Hopefully not
// enough to be noticeable.
double amplification = 0.0;
if (tmpFileName == "em00d011.vob") {
// The finale.
amplification = 10.0;
} else if (tmpFileName == "em00d021.vob") {
// Jack's escape and arrival at Flathead Mesa.
amplification = 9.0;
} else if (tmpFileName == "em00d032.vob") {
// The Grand Inquisitor's speech.
amplification = 11.0;
} else if (tmpFileName == "em00d122.vob") {
// Jack orders you to the radio tower.
amplification = 17.0;
} else if (tmpFileName == "em3ed012.vob") {
// The Grand Inquisitor gets the Coconut of Quendor.
amplification = 12.0;
} else if (tmpFileName == "g000d101.vob") {
// Griff gets captured.
amplification = 11.0;
} else if (tmpFileName == "g000d111.vob") {
// Brog gets totemized. The music seems to be mixed
// much softer in this than in the low-resolution
// version.
amplification = 12.0;
} else if (tmpFileName == "g000d122.vob") {
// Lucy gets captured.
amplification = 14.0;
} else if (tmpFileName == "g000d302.vob") {
// The Grand Inquisitor visits Jack in his cell.
amplification = 13.0;
} else if (tmpFileName == "g000d312.vob") {
// You get captured.
amplification = 14.0;
} else if (tmpFileName == "g000d411.vob") {
// Propaganda On Parade. No need to make it as loud as
// the low-resolution version.
amplification = 11.0;
} else if (tmpFileName == "pe1ed012.vob") {
// Jack lets you in with the lantern.
amplification = 14.0;
} else if (tmpFileName.hasPrefix("pe1ed")) {
// Jack answers the door. Several different ways.
amplification = 17.0;
} else if (tmpFileName == "pe5ed052.vob") {
// You get killed by the guards
amplification = 12.0;
} else if (tmpFileName == "pe6ed012.vob") {
// Jack gets captured by the guards
amplification = 17.0;
} else if (tmpFileName == "pp1ed022.vob") {
// Jack examines the lantern
amplification = 10.0;
} else if (tmpFileName == "qb1ed012.vob") {
// Lucy gets invited to the back room
amplification = 17.0;
} else if (tmpFileName.hasPrefix("qe1ed")) {
// Floyd answers the door. Several different ways.
amplification = 17.0;
} else if (tmpFileName == "qs1ed011.vob") {
// Jack explains the rules of the game.
amplification = 16.0;
} else if (tmpFileName == "qs1ed021.vob") {
// Jack loses the game.
amplification = 14.0;
} else if (tmpFileName == "uc1gd012.vob") {
// Y'Gael appears.
amplification = 12.0;
} else if (tmpFileName == "ue1ud012.vob") {
// Jack gets totemized... or what?
amplification = 12.0;
} else if (tmpFileName == "ue2qd012.vob") {
// Jack agrees to totemization.
amplification = 10.0;
} else if (tmpFileName == "g000d981.vob") {
// The Enterprise logo. Has no low-res version. Its
// volume is louder than the other logo animations.
amplification = 6.2;
} else if (tmpFileName.hasPrefix("g000d")) {
// The Dolby Digital and Activision logos. They have no
// low-res versions, but I've used the low-resolution
// Activision logo (slightly different) as reference.
amplification = 8.5;
}
animation = new Video::MPEGPSDecoder(amplification);
}
#endif
else
error("Unknown suffix for animation %s", fileName.c_str());

View file

@ -50,7 +50,8 @@ enum {
kStartCodePrivateStream2 = 0x1BF
};
MPEGPSDecoder::MPEGPSDecoder() {
MPEGPSDecoder::MPEGPSDecoder(double decibel) {
_decibel = decibel;
_demuxer = new MPEGPSDemuxer();
}
@ -104,7 +105,7 @@ MPEGPSDecoder::MPEGStream *MPEGPSDecoder::getStream(uint32 startCode, Common::Se
#ifdef USE_A52
handled = true;
AC3AudioTrack *ac3Track = new AC3AudioTrack(*packet, getSoundType());
AC3AudioTrack *ac3Track = new AC3AudioTrack(*packet, _decibel, getSoundType());
stream = ac3Track;
_streamMap[startCode] = ac3Track;
addTrack(ac3Track);
@ -704,9 +705,9 @@ Audio::AudioStream *MPEGPSDecoder::MPEGAudioTrack::getAudioStream() const {
#ifdef USE_A52
MPEGPSDecoder::AC3AudioTrack::AC3AudioTrack(Common::SeekableReadStream &firstPacket, Audio::Mixer::SoundType soundType) :
MPEGPSDecoder::AC3AudioTrack::AC3AudioTrack(Common::SeekableReadStream &firstPacket, double decibel, Audio::Mixer::SoundType soundType) :
AudioTrack(soundType) {
_audStream = Audio::makeAC3Stream(firstPacket);
_audStream = Audio::makeAC3Stream(firstPacket, decibel);
if (!_audStream)
error("Could not create AC-3 stream");
}

View file

@ -54,7 +54,7 @@ namespace Video {
*/
class MPEGPSDecoder : public VideoDecoder {
public:
MPEGPSDecoder();
MPEGPSDecoder(double decibel = 0.0);
virtual ~MPEGPSDecoder();
bool loadStream(Common::SeekableReadStream *stream);
@ -166,7 +166,7 @@ private:
#ifdef USE_A52
class AC3AudioTrack : public AudioTrack, public MPEGStream {
public:
AC3AudioTrack(Common::SeekableReadStream &firstPacket, Audio::Mixer::SoundType soundType);
AC3AudioTrack(Common::SeekableReadStream &firstPacket, double decibel, Audio::Mixer::SoundType soundType);
~AC3AudioTrack();
bool sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts);
@ -199,6 +199,8 @@ private:
// A map from stream types to stream handlers
typedef Common::HashMap<int, MPEGStream *> StreamMap;
StreamMap _streamMap;
double _decibel;
};
} // End of namespace Video