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

vimatrack.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 "common/stream.h"
00024 #include "common/mutex.h"
00025 #include "common/textconsole.h"
00026 #include "audio/audiostream.h"
00027 #include "audio/mixer.h"
00028 #include "audio/decoders/raw.h"
00029 #include "engines/grim/debug.h"
00030 #include "engines/grim/resource.h"
00031 #include "engines/grim/imuse/imuse_mcmp_mgr.h"
00032 #include "engines/grim/emi/sound/vimatrack.h"
00033 
00034 namespace Grim {
00035 
00036 struct Region {
00037     int32 offset;       // offset of region
00038     int32 length;       // lenght of region
00039 };
00040 
00041 struct SoundDesc {
00042     uint16 freq;        // frequency
00043     byte channels;      // stereo or mono
00044     byte bits;          // 8, 12, 16
00045     int numRegions;     // number of Regions
00046     Region *region;
00047     bool endFlag;
00048     bool inUse;
00049     char name[32];
00050     McmpMgr *mcmpMgr;
00051     int type;
00052     int volGroupId;
00053     bool mcmpData;
00054     uint32 headerSize;
00055     Common::SeekableReadStream *inStream;
00056 };
00057 
00058 bool VimaTrack::isPlaying() {
00059     // FIXME: Actually clean up the data better
00060     // (we don't currently handle the case where it isn't asked for through isPlaying, or deleted explicitly).
00061     if (!_handle)
00062         return false;
00063 
00064     if (g_system->getMixer()->isSoundHandleActive(*_handle)) {
00065         if (_stream->endOfData()) {
00066             g_system->getMixer()->stopHandle(*_handle);
00067             return false;
00068         } else {
00069             return true;
00070         }
00071     }
00072     return false;
00073 }
00074 
00075 bool VimaTrack::openSound(const Common::String &filename, const Common::String &voiceName, const Audio::Timestamp *start) {
00076     Common::SeekableReadStream *file = g_resourceloader->openNewStreamFile(filename);
00077     if (!file) {
00078         Debug::debug(Debug::Sound, "Stream for %s not open", voiceName.c_str());
00079         return false;
00080     }
00081     _soundName = voiceName;
00082     _mcmp = new McmpMgr();
00083     _desc = new SoundDesc();
00084     _desc->inStream = file;
00085     _desc->mcmpData = true;
00086     _desc->mcmpMgr = _mcmp;
00087     int headerSize = 0;
00088 
00089     if (_mcmp->openSound(voiceName.c_str(), file, headerSize)) {
00090         parseSoundHeader(_desc, headerSize);
00091 
00092         _stream = Audio::makeQueuingAudioStream(_desc->freq, (false));
00093 
00094         playTrack(start);
00095         return true;
00096     } else {
00097         return false;
00098     }
00099 }
00100 void VimaTrack::parseSoundHeader(SoundDesc *sound, int &headerSize) {
00101     Common::SeekableReadStream *data = sound->inStream;
00102 
00103     uint32 tag = data->readUint32BE();
00104     if (tag == MKTAG('R','I','F','F')) {
00105         sound->endFlag = false;
00106         sound->region = new Region[1];
00107         sound->numRegions = 1;
00108         sound->region[0].offset = 0;
00109         data->seek(18, SEEK_CUR);
00110         sound->channels = data->readByte();
00111         data->readByte();
00112         sound->freq = data->readUint32LE();
00113         data->seek(6, SEEK_CUR);
00114         sound->bits = data->readByte();
00115         data->seek(5, SEEK_CUR);
00116         sound->region[0].length = data->readUint32LE();
00117         headerSize = 44;
00118     } else {
00119         assert(tag != MKTAG('i','M','U','S'));
00120         error("VimaTrack::parseSoundHeader() Unknown sound format");
00121     }
00122 }
00123 
00124 int32 VimaTrack::getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size) {
00125     //assert(checkForProperHandle(sound));
00126     assert(buf && offset >= 0 && size >= 0);
00127     assert(region >= 0 && region < sound->numRegions);
00128 
00129     int32 region_offset = sound->region[region].offset;
00130     int32 region_length = sound->region[region].length;
00131 
00132     if (offset + size > region_length) {
00133         size = region_length - offset;
00134         sound->endFlag = true;
00135     } else {
00136         sound->endFlag = false;
00137     }
00138 
00139     if (sound->mcmpData) {
00140         size = sound->mcmpMgr->decompressSample(region_offset + offset, size, buf);
00141     } else {
00142         *buf = new byte[size];
00143         sound->inStream->seek(region_offset + offset + sound->headerSize, SEEK_SET);
00144         sound->inStream->read(*buf, size);
00145     }
00146 
00147     return size;
00148 }
00149 void VimaTrack::playTrack(const Audio::Timestamp *start) {
00150     //Common::StackLock lock(_mutex);
00151     if (!_stream) {
00152         error("Stream not loaded");
00153     }
00154     byte *data = nullptr;
00155     int32 result = 0;
00156 
00157     int32 curRegion = -1;
00158     int32 regionOffset = 0;
00159     int32 mixerFlags = Audio::FLAG_16BITS;
00160 
00161     curRegion++;
00162 
00163     int channels = _desc->channels;
00164 
00165     //int32 mixer_size = track->feedSize / _callbackFps;
00166     int32 mixer_size = _desc->freq * channels * 2;
00167 
00168     if (start) {
00169         regionOffset = (start->msecs() * mixer_size) / 1000;
00170         regionOffset = (regionOffset / 2) * 2; // Ensure that the offset is divisible by 2.
00171         while (regionOffset > _desc->region[curRegion].length) {
00172             regionOffset -= _desc->region[curRegion].length;
00173             ++curRegion;
00174         }
00175 
00176         if (curRegion > _desc->numRegions - 1)
00177             return;
00178     }
00179 
00180     if (_stream->endOfData()) { // FIXME: Currently we just allocate a bunch here, try to find the correct size instead.
00181         mixer_size *= 8;
00182     }
00183 
00184     if (channels == 1)
00185         mixer_size &= ~1;
00186     if (channels == 2)
00187         mixer_size &= ~3;
00188 
00189     if (mixer_size == 0)
00190         return;
00191 
00192     do {
00193         result = getDataFromRegion(_desc, curRegion, &data, regionOffset, mixer_size);
00194         if (channels == 1) {
00195             result &= ~1;
00196         }
00197         if (channels == 2) {
00198             result &= ~3;
00199         }
00200 
00201         if (result > mixer_size)
00202             result = mixer_size;
00203 
00204         if (g_system->getMixer()->isReady()) {
00205             ((Audio::QueuingAudioStream *)_stream)->queueBuffer(data, result, DisposeAfterUse::YES, mixerFlags);
00206             regionOffset += result;
00207         } else
00208             delete[] data;
00209 
00210         if (curRegion >= 0 && curRegion < _desc->numRegions - 1) {
00211             curRegion++;
00212             regionOffset = 0;
00213 
00214             if (!_stream) {
00215                 return;
00216             }
00217         }
00218         mixer_size -= result;
00219         assert(mixer_size >= 0);
00220     } while (mixer_size && !_desc->endFlag);
00221     if (g_system->getMixer()->isReady()) {
00222         //g_system->getMixer()->setChannelVolume(track->handle, track->getVol());
00223         //g_system->getMixer()->setChannelBalance(track->handle, track->getPan());
00224     }
00225 }
00226 
00227 Audio::Timestamp VimaTrack::getPos() {
00228     // FIXME: Return actual stream position.
00229     return g_system->getMixer()->getSoundElapsedTime(*_handle);
00230 }
00231 
00232 VimaTrack::VimaTrack() {
00233     _soundType = Audio::Mixer::kSpeechSoundType;
00234     _handle = new Audio::SoundHandle();
00235     _file = nullptr;
00236     _mcmp = nullptr;
00237     _desc = nullptr;
00238 }
00239 
00240 VimaTrack::~VimaTrack() {
00241     stop();
00242 
00243     delete _mcmp;
00244 
00245     if (_desc) {
00246         delete[] _desc->region;
00247         delete _desc->inStream;
00248     }
00249 
00250     if (_handle) {
00251         g_system->getMixer()->stopHandle(*_handle);
00252         delete _handle;
00253     }
00254     delete _desc;
00255 }
00256 
00257 } // end of namespace Grim


Generated on Sat Aug 17 2019 05:00:37 for ResidualVM by doxygen 1.7.1
curved edge   curved edge