ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

scx.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "audio/audiostream.h"
00024 #include "audio/decoders/xa.h"
00025 #include "common/memstream.h"
00026 #include "common/textconsole.h"
00027 #include "common/stream.h"
00028 #include "engines/grim/emi/sound/codecs/scx.h"
00029 
00030 namespace Grim {
00031 
00032 SCXStream::SCXStream(Common::SeekableReadStream *stream, const Audio::Timestamp *start, DisposeAfterUse::Flag disposeAfterUse) {
00033     static const uint32 stereoChannelNames[SCX_MAX_CHANNELS] = { MKTAG('L', 'E', 'F', 'T'), MKTAG('R', 'G', 'H', 'T') };
00034 
00035     stream->readUint32BE(); // 'SCRX'
00036     stream->readUint32LE();
00037 
00038     _blockSize = stream->readUint16LE();
00039     /* totalBlockSize = */ stream->readUint16LE();
00040 
00041     if (_blockSize & 0xf)
00042         error("Bad SCX block size %04x", _blockSize);
00043 
00044     // Base our channel count based off the block size
00045     _channels = (_blockSize == 0) ? 1 : 2;
00046 
00047     stream->skip(12);
00048 
00049     uint32 channelSize[SCX_MAX_CHANNELS];
00050     for (int i = 0; i < _channels; i++) {
00051         uint32 tag = stream->readUint32BE();
00052 
00053         if (isStereo()) {
00054             if (tag != stereoChannelNames[i])
00055                 error("Bad stereo channel tag found '%s'", tag2str(tag));
00056         } else if (tag != MKTAG('M', 'O', 'N', 'O'))
00057             error("Bad mono channel tag found '%s'", tag2str(tag));
00058 
00059         channelSize[i] = stream->readUint32LE();
00060     }
00061 
00062     stream->seek(0x80);
00063 
00064     uint32 leftRate = 0, rightRate = 0;
00065     for (int i = 0; i < _channels; i++) {
00066         if (stream->readUint32BE() != MKTAG('V', 'A', 'G', 'p'))
00067             error("Bad VAG header");
00068 
00069         /* uint32 version = */ stream->readUint32BE();
00070         stream->readUint32BE();
00071         stream->readUint32BE();
00072 
00073         if (i == 0)
00074             leftRate = stream->readUint32BE();
00075         else
00076             rightRate = stream->readUint32BE();
00077 
00078         stream->skip(12); // skip useless info
00079         stream->skip(16); // skip name
00080         stream->skip(16); // skip zeroes
00081     }
00082 
00083     if (isStereo() && leftRate != rightRate)
00084         error("Mismatching SCX rates");
00085 
00086     _rate = leftRate;
00087 
00088     if (isStereo()) {
00089         // TODO: Make XAStream allow for appending data (similar to how ScummVM
00090         // handles AAC/QDM2. For now, we de-interleave the XA ADPCM data and then
00091         // re-interleave in readBuffer().
00092         // Of course, in doing something that does better streaming, it would
00093         // screw up the XA loop points. So, I'm not really sure what is best atm.
00094         byte *leftOut = (byte*)malloc(channelSize[0]);
00095         byte *rightOut = (byte*)malloc(channelSize[1]);
00096         Common::MemoryWriteStream *leftStream = new Common::MemoryWriteStream(leftOut, channelSize[0]);
00097         Common::MemoryWriteStream *rightStream = new Common::MemoryWriteStream(rightOut, channelSize[1]);
00098         byte *buf = new byte[_blockSize];
00099 
00100         while (stream->pos() < stream->size()) {
00101             stream->read(buf, _blockSize);
00102             leftStream->write(buf, _blockSize);
00103             stream->read(buf, _blockSize);
00104             rightStream->write(buf, _blockSize);
00105         }
00106 
00107         _fileStreams[0] = new Common::MemoryReadStream(leftOut, channelSize[0], DisposeAfterUse::YES);
00108         _fileStreams[1] = new Common::MemoryReadStream(rightOut, channelSize[1], DisposeAfterUse::YES);
00109 
00110         _xaStreams[0] = Audio::makeXAStream(_fileStreams[0], _rate);
00111         _xaStreams[1] = Audio::makeXAStream(_fileStreams[1], _rate);
00112 
00113         delete[] buf;
00114         delete leftStream;
00115         delete rightStream;
00116     } else {
00117         _fileStreams[0] = stream->readStream(channelSize[0]);
00118         _fileStreams[1] = nullptr;
00119         _xaStreams[0] = Audio::makeXAStream(_fileStreams[0], _rate);
00120         _xaStreams[1] = nullptr;
00121     }
00122 
00123     if (start) {
00124         // Read data from the sound stream until we hit the desired start position.
00125         // We do this instead of seeking so the loop point gets set up properly.
00126         int samples = (int)((int64)start->msecs() * _rate / 1000);
00127         int16 temp[1024];
00128         while (samples > 0) {
00129             samples -= _xaStreams[0]->readBuffer(temp, samples < 1024 ? samples : 1024);
00130             if (_xaStreams[1]) {
00131                 _xaStreams[1]->readBuffer(temp, samples < 1024 ? samples : 1024);
00132             }
00133         }
00134     }
00135 
00136     if (disposeAfterUse == DisposeAfterUse::YES)
00137         delete stream;
00138 }
00139 
00140 SCXStream::~SCXStream() {
00141     for (int i = 0; i < SCX_MAX_CHANNELS; i++)
00142         delete _xaStreams[i];
00143 }
00144 
00145 int SCXStream::readBuffer(int16 *buffer, const int numSamples) {
00146     if (isStereo()) {
00147         // Needs to be divisible by the channel count
00148         assert((numSamples % 2) == 0);
00149 
00150         // TODO: As per above, this probably should do more actual streaming
00151 
00152         // Decode enough data from each channel
00153         int samplesPerChannel = numSamples / 2;
00154         int16 *leftSamples = new int16[samplesPerChannel];
00155         int16 *rightSamples = new int16[samplesPerChannel];
00156 
00157         int samplesDecodedLeft = _xaStreams[0]->readBuffer(leftSamples, samplesPerChannel);
00158         int samplesDecodedRight = _xaStreams[1]->readBuffer(rightSamples, samplesPerChannel);
00159         assert(samplesDecodedLeft == samplesDecodedRight);
00160 
00161         // Now re-interleave the data
00162         int samplesDecoded = 0;
00163         int16 *leftSrc = leftSamples, *rightSrc = rightSamples;
00164         for (; samplesDecoded < samplesDecodedLeft * 2; samplesDecoded += 2) {
00165             *buffer++ = *leftSrc++;
00166             *buffer++ = *rightSrc++;
00167         }
00168 
00169         delete[] leftSamples;
00170         delete[] rightSamples;
00171         return samplesDecoded;
00172     }
00173 
00174     // Just read from the stream directly for mono
00175     return _xaStreams[0]->readBuffer(buffer, numSamples);
00176 }
00177 
00178 bool SCXStream::rewind() {
00179     if (!_xaStreams[0]->rewind())
00180         return false;
00181 
00182     return !isStereo() || _xaStreams[1]->rewind();
00183 }
00184 
00185 Audio::Timestamp SCXStream::getPos() const {
00186     int32 pos = _fileStreams[0]->pos();
00187 
00188     // Each XA ADPCM block of 16 bytes decompresses to 28 samples.
00189     int32 samples = pos * 28 / 16;
00190     uint32 msecs = (uint32)((int64)samples * 1000 / _rate);
00191 
00192     return Audio::Timestamp(msecs);
00193 }
00194 
00195 SCXStream *makeSCXStream(Common::SeekableReadStream *stream, const Audio::Timestamp *start, DisposeAfterUse::Flag disposeAfterUse) {
00196     if (stream->readUint32BE() != MKTAG('S', 'C', 'R', 'X')) {
00197         delete stream;
00198         return nullptr;
00199     }
00200 
00201     stream->seek(0);
00202     return new SCXStream(stream, start, disposeAfterUse);
00203 }
00204 
00205 } // End of namespace Grim


Generated on Sat May 18 2019 05:01:19 for ResidualVM by doxygen 1.7.1
curved edge   curved edge