/* 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 3 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, see . * */ #include "common/inttypes.h" #include "common/ptr.h" #include "common/stream.h" #include "common/textconsole.h" #include "common/util.h" #include "audio/decoders/adpcm_intern.h" #include "audio/decoders/apc.h" #include "audio/decoders/raw.h" namespace Audio { class APCStreamImpl : public APCStream { public: // These parameters are completely optional and only used in HNM videos to make sure data is consistent APCStreamImpl(uint32 sampleRate = uint(-1), byte stereo = byte(-1)); bool init(Common::SeekableReadStream &header) override; // AudioStream API int readBuffer(int16 *buffer, const int numSamples) override { return _audStream->readBuffer(buffer, numSamples); } bool isStereo() const override { return _audStream->isStereo(); } int getRate() const override { return _audStream->getRate(); } bool endOfData() const override { return _audStream->endOfData(); } bool endOfStream() const override { return _audStream->endOfStream(); } // PacketizedAudioStream API void queuePacket(Common::SeekableReadStream *data) override; void finish() override { _audStream->finish(); } private: struct Status { int32 last; uint32 stepIndex; int16 step; }; FORCEINLINE static int16 decodeIMA(byte code, Status *status); Common::ScopedPtr _audStream; byte _stereo; uint32 _sampleRate; Status _status[2]; }; APCStreamImpl::APCStreamImpl(uint32 sampleRate, byte stereo) : _sampleRate(sampleRate), _stereo(stereo) { memset(_status, 0, sizeof(_status)); if (_sampleRate != uint32(-1) && _stereo != byte(-1)) { _audStream.reset(makeQueuingAudioStream(_sampleRate, _stereo)); } } bool APCStreamImpl::init(Common::SeekableReadStream &header) { // Header size if (header.size() < 32) { return false; } // Read magic and version at once byte magicVersion[12]; if (header.read(magicVersion, sizeof(magicVersion)) != sizeof(magicVersion)) { return false; } if (memcmp(magicVersion, "CRYO_APC1.20", sizeof(magicVersion))) { return false; } //uint32 num_samples = header.readUint32LE(); header.skip(4); uint32 samplerate = header.readUint32LE(); if (_sampleRate != uint32(-1) && _sampleRate != samplerate) { error("Samplerate mismatch"); return false; } _sampleRate = samplerate; uint32 leftSample = header.readSint32LE(); uint32 rightSample = header.readSint32LE(); uint32 audioFlags = header.readUint32LE(); byte stereo = (audioFlags & 1); if (_stereo != byte(-1) && _stereo != stereo) { error("Channels mismatch"); return false; } _stereo = stereo; _status[0].last = leftSample; _status[1].last = rightSample; _status[0].stepIndex = _status[1].stepIndex = 0; _status[0].step = _status[1].step = Ima_ADPCMStream::_imaTable[0]; if (!_audStream) { _audStream.reset(makeQueuingAudioStream(_sampleRate, _stereo)); } return true; } void APCStreamImpl::queuePacket(Common::SeekableReadStream *data) { Common::ScopedPtr packet(data); uint32 size = packet->size() - packet->pos(); if (size == 0) { return; } // From 4-bits samples to 16-bits int16 *outputBuffer = (int16 *)malloc(size * 4); int16 *outputPtr = outputBuffer; int channelOffset = (_stereo ? 1 : 0); for (uint32 counter = size; counter > 0; counter--) { byte word = packet->readByte(); *(outputPtr++) = decodeIMA((word >> 4) & 0x0f, _status); *(outputPtr++) = decodeIMA((word >> 0) & 0x0f, _status + channelOffset); } byte flags = FLAG_16BITS; if (_stereo) { flags |= FLAG_STEREO; } #ifdef SCUMM_LITTLE_ENDIAN flags |= Audio::FLAG_LITTLE_ENDIAN; #endif _audStream->queueBuffer((byte *)outputBuffer, size * 4, DisposeAfterUse::YES, flags); } int16 APCStreamImpl::decodeIMA(byte code, Status *status) { int32 E = (2 * (code & 0x7) + 1) * status->step / 8; int32 diff = (code & 0x08) ? -E : E; // In Cryo APC data is only truncated and not CLIPed as expected int16 samp = (status->last + diff); int32 index = status->stepIndex + Ima_ADPCMStream::_stepAdjustTable[code]; index = CLIP(index, 0, ARRAYSIZE(Ima_ADPCMStream::_imaTable) - 1); status->last = samp; status->stepIndex = index; status->step = Ima_ADPCMStream::_imaTable[index]; return samp; } PacketizedAudioStream *makeAPCStream(Common::SeekableReadStream &header) { Common::ScopedPtr stream(new APCStreamImpl()); if (!stream->init(header)) { return nullptr; } return stream.release(); } APCStream *makeAPCStream(uint sampleRate, bool stereo) { assert((sampleRate % 11025) == 0); return new APCStreamImpl(sampleRate, stereo ? 1 : 0); } } // End of namespace Audio