diff --git a/audio/decoders/g711.cpp b/audio/decoders/g711.cpp new file mode 100644 index 00000000000..b7f8cd80fb8 --- /dev/null +++ b/audio/decoders/g711.cpp @@ -0,0 +1,128 @@ +/* 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. + * + */ + +#include "audio/decoders/g711.h" + +#include "audio/audiostream.h" +#include "common/stream.h" +#include "common/util.h" + +/* from g711.c by SUN microsystems (unrestricted use) */ + +#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ +#define BIAS (0x84) /* Bias for linear code. */ + +namespace Audio { + +/** + * Logarithmic PCM (G.711) + * https://en.wikipedia.org/wiki/G.711 + * https://wiki.multimedia.cx/index.php/PCM#Logarithmic_PCM + */ +class G711AudioStream : public SeekableAudioStream { + Common::DisposablePtr _stream; + const int _rate; + const int _channels; + +protected: + virtual int16 decodeSample(uint8 val) = 0; + +public: + G711AudioStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) : + _stream(stream, disposeAfterUse), + _rate(rate), + _channels(channels) { + } + + virtual int readBuffer(int16 *buffer, const int numSamples) override { + int samples; + + for (samples = 0; samples < numSamples; samples++) { + uint8 val = _stream->readByte(); + if (endOfData()) + break; + buffer[samples] = decodeSample(val); + } + + return samples; + } + + virtual bool isStereo() const override { return (_channels == 2); } + virtual int getRate() const override { return _rate; } + virtual bool endOfData() const override { return _stream->eos(); } + virtual bool seek(const Timestamp &where) override { + const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames(); + return _stream->seek(seekSample, SEEK_SET); + } + virtual Timestamp getLength() const override { + return Timestamp(0, _stream->size() / _channels, _rate); + } +}; + +class G711ALawStream : public G711AudioStream { + virtual int16 decodeSample(uint8 val) override { + val ^= 0x55; + + int t = val & QUANT_MASK; + int seg = ((unsigned)val & SEG_MASK) >> SEG_SHIFT; + if (seg) + t = (t + t + 1 + 32) << (seg + 2); + else + t = (t + t + 1) << 3; + + return (val & SIGN_BIT) ? t : -t; + } + +public: + G711ALawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) : + G711AudioStream(stream, disposeAfterUse, rate, channels) { + } +}; + +SeekableAudioStream *makeALawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) { + return new G711ALawStream(stream, disposeAfterUse, rate, channels); +} + +class G711MuLawStream : public G711AudioStream { + virtual int16 decodeSample(uint8 val) override { + val = ~val; + + int t = ((val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)val & SEG_MASK) >> SEG_SHIFT; + + return (val & SIGN_BIT) ? (BIAS - t) : (t - BIAS); + } + +public: + G711MuLawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) : + G711AudioStream(stream, disposeAfterUse, rate, channels) { + } +}; + +SeekableAudioStream *makeMuLawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) { + return new G711MuLawStream(stream, disposeAfterUse, rate, channels); +} + +} // End of namespace Audio diff --git a/audio/decoders/g711.h b/audio/decoders/g711.h new file mode 100644 index 00000000000..3b9e0a3efa9 --- /dev/null +++ b/audio/decoders/g711.h @@ -0,0 +1,77 @@ +/* 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. + * + */ + +/** + * @file + * Sound decoder used in engines: + * - trecision + */ + +#ifndef AUDIO_G711_H +#define AUDIO_G711_H + +#include "common/scummsys.h" +#include "common/types.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Audio { + +class SeekableAudioStream; + +/** + * Takes an input stream containing G711 A-law compressed sound data and creates + * a SeekableAudioStream from that. + * + * @param stream the SeekableReadStream from which to read the PCM data + * @param disposeAfterUse whether to delete the stream after use + * @param rate the sampling rate + * @param channels the number of channels + * @return a new SeekableAudioStream, or NULL, if an error occurred + */ +SeekableAudioStream *makeALawStream( + Common::SeekableReadStream *stream, + DisposeAfterUse::Flag disposeAfterUse, + int rate, + int channels); + +/** + * Takes an input stream containing G711 μ-law compressed sound data and creates + * a SeekableAudioStream from that. + * + * @param stream the SeekableReadStream from which to read the PCM data + * @param disposeAfterUse whether to delete the stream after use + * @param rate the sampling rate + * @param channels the number of channels + * @return a new SeekableAudioStream, or NULL, if an error occurred + */ +SeekableAudioStream *makeMuLawStream( + Common::SeekableReadStream *stream, + DisposeAfterUse::Flag disposeAfterUse, + int rate, + int channels); + +} // End of namespace Audio + +#endif diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 9b045b20ca3..a3473ff74cf 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -35,6 +35,7 @@ #include "audio/decoders/adpcm.h" #include "audio/decoders/qdm2.h" #include "audio/decoders/raw.h" +#include "audio/decoders/g711.h" namespace Audio { @@ -642,6 +643,10 @@ 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('a', 'l', 'a', 'w')) { + return makeALawStream(stream, DisposeAfterUse::YES, _sampleRate, _channels); + } else if (_codecTag == MKTAG('u', 'l', 'a', 'w')) { + return makeMuLawStream(stream, DisposeAfterUse::YES, _sampleRate, _channels); } error("Unsupported audio codec"); diff --git a/audio/decoders/wave.cpp b/audio/decoders/wave.cpp index 67e0b31d831..f5201c26db6 100644 --- a/audio/decoders/wave.cpp +++ b/audio/decoders/wave.cpp @@ -31,6 +31,7 @@ #include "audio/decoders/adpcm.h" #include "audio/decoders/mp3.h" #include "audio/decoders/raw.h" +#include "audio/decoders/g711.h" namespace Audio { @@ -78,8 +79,8 @@ bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, // values for it: // 1 -> uncompressed PCM // 17 -> IMA ADPCM compressed WAVE - // See for a more complete - // list of common WAVE compression formats... + // See + // for a more complete list of common WAVE compression formats... uint16 type = stream.readUint16LE(); // == 1 for PCM data uint16 numChannels = stream.readUint16LE(); // 1 for mono, 2 for stereo uint32 samplesPerSec = stream.readUint32LE(); // in Hz @@ -107,31 +108,32 @@ bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, debug(" bitsPerSample: %d", bitsPerSample); #endif + switch (type) { + case kWaveFormatPCM: + case kWaveFormatMSADPCM: + case kWaveFormatALawPCM: + case kWaveFormatMuLawPCM: + case kWaveFormatMSIMAADPCM: #ifdef USE_MAD + case kWaveFormatMP3: + #endif + break; + default: + warning("getWavInfo: unsupported format (type %d)", type); + return false; + } + if (type == kWaveFormatMP3) { bitsPerSample = 8; - } else { - #endif - if (type != kWaveFormatPCM && type != kWaveFormatMSADPCM && type != kWaveFormatMSIMAADPCM) { - #ifdef USE_MAD - warning("getWavInfo: only PCM, MS ADPCM, MP3, or IMA ADPCM data is supported (type %d)", type); - #else - warning("getWavInfo: only PCM, MS ADPCM, or IMA ADPCM data is supported (type %d)", type); - #endif - - return false; - } - - if (blockAlign != numChannels * bitsPerSample / 8 && type != kWaveFormatMSADPCM) { + } else if (type != kWaveFormatMSADPCM) { + if (blockAlign != numChannels * bitsPerSample / 8) { debug(0, "getWavInfo: blockAlign is invalid"); } - if (avgBytesPerSec != samplesPerSec * blockAlign && type != kWaveFormatMSADPCM) { + if (avgBytesPerSec != samplesPerSec * blockAlign) { debug(0, "getWavInfo: avgBytesPerSec is invalid"); } - #ifdef USE_MAD } - #endif // Prepare the return values. rate = samplesPerSec; @@ -216,6 +218,10 @@ SeekableAudioStream *makeWAVStream(Common::SeekableReadStream *stream, DisposeAf case kWaveFormatMP3: return makeMP3Stream(dataStream, DisposeAfterUse::YES); #endif + case kWaveFormatALawPCM: + return makeALawStream(dataStream, DisposeAfterUse::YES, rate, channels); + case kWaveFormatMuLawPCM: + return makeMuLawStream(dataStream, DisposeAfterUse::YES, rate, channels); case kWaveFormatPCM: return makeRawStream(dataStream, rate, flags); } diff --git a/audio/decoders/wave_types.h b/audio/decoders/wave_types.h index c600b02bc15..a9f051795ad 100644 --- a/audio/decoders/wave_types.h +++ b/audio/decoders/wave_types.h @@ -30,6 +30,8 @@ enum WaveCompressionType { kWaveFormatNone = 0x0000, kWaveFormatPCM = 0x0001, kWaveFormatMSADPCM = 0x0002, + kWaveFormatALawPCM = 0x0006, + kWaveFormatMuLawPCM = 0x0007, kWaveFormatMSIMAADPCM = 0x0011, kWaveFormatMP3 = 0x0055, kWaveFormatDK3 = 0x0062, // rogue format number diff --git a/audio/module.mk b/audio/module.mk index d79a0cd246a..127bed6151c 100644 --- a/audio/module.mk +++ b/audio/module.mk @@ -25,6 +25,7 @@ MODULE_OBJS := \ decoders/aiff.o \ decoders/asf.o \ decoders/flac.o \ + decoders/g711.o \ decoders/iff_sound.o \ decoders/mac_snd.o \ decoders/mp3.o \