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

emisound.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 "gui/error.h"
00024 
00025 #include "common/stream.h"
00026 #include "common/mutex.h"
00027 #include "common/timer.h"
00028 #include "audio/audiostream.h"
00029 #include "audio/decoders/raw.h"
00030 #include "audio/mixer.h"
00031 #include "engines/grim/debug.h"
00032 #include "engines/grim/sound.h"
00033 #include "engines/grim/grim.h"
00034 #include "engines/grim/resource.h"
00035 #include "engines/grim/savegame.h"
00036 #include "engines/grim/textsplit.h"
00037 #include "engines/grim/emi/sound/emisound.h"
00038 #include "engines/grim/emi/sound/track.h"
00039 #include "engines/grim/emi/sound/aifftrack.h"
00040 #include "engines/grim/emi/sound/mp3track.h"
00041 #include "engines/grim/emi/sound/scxtrack.h"
00042 #include "engines/grim/emi/sound/vimatrack.h"
00043 #include "engines/grim/movie/codecs/vima.h"
00044 
00045 namespace Grim {
00046 
00047 EMISound *g_emiSound = nullptr;
00048 
00049 extern uint16 imuseDestTable[];
00050 
00051 MusicEntry emiPS2MusicTable[] = {
00052     { 0, 0, 0, 127, 0, "", "", "" },
00053     { 0, 0, 1, 127, 1, "state", "", "1115.scx" },
00054     { 0, 0, 2, 127, 2, "state", "", "1170.scx" },
00055     { 0, 0, 2, 127, 3, "state", "", "1170.scx" },
00056     { 0, 0, 2, 127, 4, "state", "", "1170.scx" },
00057     { 0, 0, 3, 127, 5, "state", "", "1165.scx" },
00058     { 0, 0, 4, 127, 6, "state", "", "1145.scx" },
00059     { 0, 0, 4, 127, 7, "state", "", "1145.scx" },
00060     { 0, 0, 1, 127, 8, "state", "", "1115.scx" },
00061     { 0, 0, 1, 127, 9, "state", "", "1115.scx" },
00062     { 0, 0, 0, 127, 10, "episode", "", "7200.scx" },
00063     { 0, 0, 0, 127, 11, "episode", "", "1210.scx" },
00064     { 0, 0, 0, 127, 12, "state", "", "1180.scx" },
00065     { 0, 0, 0, 127, 13, "state", "", "1110.scx" },
00066     { 0, 0, 1, 127, 14, "state", "", "1115.scx" },
00067     { 0, 0, 0, 127, 15, "state", "", "1105.scx" },
00068     { 0, 0, 4, 127, 16, "state", "", "1145.scx" },
00069     { 0, 0, 0, 127, 17, "state", "", "1150.scx" },
00070     { 0, 0, 0, 127, 18, "state", "", "1100.scx" },
00071     { 0, 0, 5, 127, 19, "state", "", "1120.scx" },
00072     { 0, 0, 5, 127, 20, "state", "", "1120.scx" },
00073     { 0, 0, 5, 127, 21, "state", "", "1120.scx" },
00074     { 0, 0, 3, 127, 22, "state", "", "1165.scx" },
00075     { 0, 0, 0, 127, 23, "state", "", "1155.scx" },
00076     { 0, 0, 0, 127, 24, "state", "", "1160.scx" },
00077     { 0, 0, 0, 127, 25, "state", "", "1140.scx" },
00078     { 0, 0, 0, 127, 26, "state", "", "1140.scx" },
00079     { 0, 0, 2, 127, 27, "state", "", "1170.scx" },
00080     { 0, 0, 2, 127, 28, "state", "", "1175.scx" },
00081     { 0, 0, 0, 127, 29, "episode", "", "1205.scx" },
00082     { 0, 0, 0, 127, 30, "state", "", "1000.scx" },
00083     { 0, 0, 0, 127, 31, "state", "", "1185.scx" },
00084     { 0, 0, 0, 127, 32, "state", "", "2127.scx" },
00085     { 0, 0, 0, 127, 33, "state", "", "2119.scx" },
00086     { 0, 0, 0, 127, 34, "episode", "", "2208.scx" },
00087     { 0, 0, 0, 127, 35, "state", "", "2195.scx" },
00088     { 0, 0, 0, 127, 36, "state", "", "2190.scx" },
00089     { 0, 0, 0, 127, 37, "state", "", "2185.scx" },
00090     { 0, 0, 1, 127, 38, "state", "", "2175.scx" },
00091     { 0, 0, 0, 127, 39, "state", "", "2170.scx" },
00092     { 0, 0, 0, 127, 40, "state", "", "2165.scx" },
00093     { 0, 0, 0, 127, 41, "state", "", "2160.scx" },
00094     { 0, 0, 0, 127, 42, "state", "", "2155.scx" },
00095     { 0, 0, 0, 127, 43, "state", "", "2120.scx" },
00096     { 0, 0, 0, 127, 44, "state", "", "2150.scx" },
00097     { 0, 0, 0, 127, 45, "state", "", "2145.scx" },
00098     { 0, 0, 2, 127, 46, "state", "", "2105.scx" },
00099     { 0, 0, 0, 127, 47, "state", "", "2115.scx" },
00100     { 0, 0, 0, 127, 48, "state", "", "2125.scx" },
00101     { 0, 0, 0, 127, 49, "state", "", "2130.scx" },
00102     { 0, 0, 0, 127, 50, "state", "", "2100.scx" },
00103     { 0, 0, 0, 127, 51, "state", "", "2140.scx" },
00104     { 0, 0, 0, 127, 52, "episode", "", "2200.scx" },
00105     { 0, 0, 0, 127, 53, "state", "", "2116.scx" },
00106     { 0, 0, 0, 127, 54, "episode", "", "2207.scx" },
00107     { 0, 0, 0, 127, 55, "state", "", "2107.scx" },
00108     { 0, 0, 0, 127, 56, "episode", "", "2215.scx" },
00109     { 0, 0, 0, 127, 57, "episode", "", "2220.scx" },
00110     { 0, 0, 0, 127, 58, "episode", "", "2225.scx" },
00111     { 0, 0, 0, 127, 59, "episode", "", "2210.scx" },
00112     { 0, 0, 0, 127, 60, "state", "", "2135.scx" },
00113     { 0, 0, 2, 127, 61, "state", "", "2105.scx" },
00114     { 0, 0, 0, 127, 62, "state", "", "2108.scx" },
00115     { 0, 0, 0, 127, 63, "state", "", "2117.scx" },
00116     { 0, 0, 0, 127, 64, "state", "", "2118.scx" },
00117     { 0, 0, 1, 127, 65, "state", "", "2175.scx" },
00118     { 0, 0, 0, 127, 66, "state", "", "4120.scx" },
00119     { 0, 0, 1, 127, 67, "state", "", "3100.scx" },
00120     { 0, 0, 0, 127, 68, "state", "", "4115.scx" },
00121     { 0, 0, 2, 127, 69, "state", "", "4100.scx" },
00122     { 0, 0, 0, 127, 70, "state", "", "3150.scx" },
00123     { 0, 0, 0, 127, 71, "state", "", "3145.scx" },
00124     { 0, 0, 0, 127, 72, "state", "", "4110.scx" },
00125     { 0, 0, 0, 127, 73, "state", "", "3140.scx" },
00126     { 0, 0, 3, 127, 74, "state", "", "3135.scx" },
00127     { 0, 0, 3, 127, 75, "state", "", "3120.scx" },
00128     { 0, 0, 4, 127, 76, "state", "", "3130.scx" },
00129     { 0, 0, 4, 127, 77, "state", "", "3115.scx" },
00130     { 0, 0, 1, 127, 78, "state", "", "3100.scx" },
00131     { 0, 0, 5, 127, 79, "state", "", "3125.scx" },
00132     { 0, 0, 5, 127, 80, "state", "", "3110.scx" },
00133     { 0, 0, 6, 127, 81, "state", "", "3105.scx" },
00134     { 0, 0, 0, 127, 82, "episode", "", "3210.scx" },
00135     { 0, 0, 0, 127, 83, "episode", "", "3200.scx" },
00136     { 0, 0, 0, 127, 84, "episode", "", "3205.scx" },
00137     { 0, 0, 0, 127, 85, "state", "", "3147.scx" },
00138     { 0, 0, 0, 127, 86, "episode", "", "4215.scx" },
00139     { 0, 0, 0, 127, 87, "state", "", "4105.scx" },
00140     { 0, 0, 6, 127, 88, "state", "", "3106.scx" },
00141     { 0, 0, 6, 127, 89, "state", "", "3107.scx" },
00142     { 0, 0, 2, 127, 90, "state", "", "4100.scx" },
00143     { 0, 0, 1, 127, 91, "state", "", "5145.scx" },
00144     { 0, 0, 2, 127, 92, "state", "", "5140.scx" },
00145     { 0, 0, 2, 127, 93, "state", "", "5140.scx" },
00146     { 0, 0, 3, 127, 94, "state", "", "5135.scx" },
00147     { 0, 0, 3, 127, 95, "state", "", "5135.scx" },
00148     { 0, 0, 3, 127, 96, "state", "", "5135.scx" },
00149     { 0, 0, 0, 127, 97, "state", "", "5170.scx" },
00150     { 0, 0, 0, 127, 98, "episode", "", "5205.scx" },
00151     { 0, 0, 0, 127, 99, "state", "", "5120.scx" },
00152     { 0, 0, 0, 127, 100, "episode", "", "5215.scx" },
00153     { 0, 0, 0, 127, 101, "episode", "", "5230.scx" },
00154     { 0, 0, 0, 127, 102, "episode", "", "5225.scx" },
00155     { 0, 0, 0, 127, 103, "state", "", "5117.scx" },
00156     { 0, 0, 0, 127, 104, "state", "", "5115.scx" },
00157     { 0, 0, 0, 127, 105, "episode", "", "5220.scx" },
00158     { 0, 0, 0, 127, 106, "state", "", "6105.scx" },
00159     { 0, 0, 0, 127, 107, "state", "", "6100.scx" },
00160     { 0, 0, 0, 127, 108, "state", "", "5165.scx" },
00161     { 0, 0, 0, 127, 109, "state", "", "5160.scx" },
00162     { 0, 0, 0, 127, 110, "episode", "", "5200.scx" },
00163     { 0, 0, 2, 127, 111, "state", "", "5140.scx" },
00164     { 0, 0, 3, 127, 112, "state", "", "5135.scx" },
00165     { 0, 0, 0, 127, 113, "state", "", "5155.scx" },
00166     { 0, 0, 0, 127, 114, "state", "", "5150.scx" },
00167     { 0, 0, 0, 127, 115, "state", "", "5130.scx" },
00168     { 0, 0, 0, 127, 116, "state", "", "5125.scx" },
00169     { 0, 0, 0, 127, 117, "state", "", "5110.scx" },
00170     { 0, 0, 1, 127, 118, "state", "", "5105.scx" },
00171     { 0, 0, 0, 127, 119, "state", "", "5100.scx" },
00172     { 0, 0, 0, 127, 120, "state", "", "6110.scx" },
00173     { 0, 0, 0, 127, 121, "state", "", "5106.scx" },
00174     { 0, 0, 0, 127, 122, "episode", "", "7210.scx" },
00175     { 0, 0, 0, 127, 123, "episode", "", "1200.scx" },
00176     { 0, 0, 0, 127, 124, "state", "", "1195.scx" },
00177     { 0, 0, 0, 127, 125, "episode", "", "1215.scx" }
00178 };
00179 
00180 void EMISound::timerHandler(void *refCon) {
00181     EMISound *emiSound = (EMISound *)refCon;
00182     emiSound->callback();
00183 }
00184 
00185 EMISound::EMISound(int fps) {
00186     _curMusicState = -1;
00187     _numMusicStates = 0;
00188     _musicTrack = nullptr;
00189     _curTrackId = 0;
00190     _callbackFps = fps;
00191     vimaInit(imuseDestTable);
00192     initMusicTable();
00193     g_system->getTimerManager()->installTimerProc(timerHandler, 1000000 / _callbackFps, this, "emiSoundCallback");
00194 }
00195 
00196 EMISound::~EMISound() {
00197     g_system->getTimerManager()->removeTimerProc(timerHandler);
00198     freePlayingSounds();
00199     freeLoadedSounds();
00200     delete _musicTrack;
00201     if (g_grim->getGamePlatform() != Common::kPlatformPS2) {
00202         delete[] _musicTable;
00203     }
00204 }
00205 
00206 EMISound::TrackList::iterator EMISound::getPlayingTrackByName(const Common::String &name) {
00207     for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
00208         if ((*it)->getSoundName() == name) {
00209             return it;
00210         }
00211     }
00212     return _playingTracks.end();
00213 }
00214 
00215 void EMISound::freePlayingSounds() {
00216     for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
00217         delete (*it);
00218     }
00219     _playingTracks.clear();
00220 }
00221 
00222 void EMISound::freeLoadedSounds() {
00223     for (TrackMap::iterator it = _preloadedTrackMap.begin(); it != _preloadedTrackMap.end(); ++it) {
00224         delete it->_value;
00225     }
00226     _preloadedTrackMap.clear();
00227 }
00228 
00229 bool EMISound::startVoice(const Common::String &soundName, int volume, int pan) {
00230     return startSound(soundName, Audio::Mixer::kSpeechSoundType, volume, pan);
00231 }
00232 
00233 bool EMISound::startSfx(const Common::String &soundName, int volume, int pan) {
00234     return startSound(soundName, Audio::Mixer::kSFXSoundType, volume, pan);
00235 }
00236 
00237 bool EMISound::startSfxFrom(const Common::String &soundName, const Math::Vector3d &pos, int volume) {
00238     return startSoundFrom(soundName, Audio::Mixer::kSFXSoundType, pos, volume);
00239 }
00240 
00241 bool EMISound::startSound(const Common::String &soundName, Audio::Mixer::SoundType soundType, int volume, int pan) {
00242     Common::StackLock lock(_mutex);
00243     SoundTrack *track = initTrack(soundName, soundType);
00244     if (track) {
00245         track->setBalance(pan * 2 - 127);
00246         track->setVolume(volume);
00247         track->play();
00248         _playingTracks.push_back(track);
00249         return true;
00250     }
00251     return false;
00252 }
00253 
00254 bool EMISound::startSoundFrom(const Common::String &soundName, Audio::Mixer::SoundType soundType, const Math::Vector3d &pos, int volume) {
00255     Common::StackLock lock(_mutex);
00256     SoundTrack *track = initTrack(soundName, soundType);
00257     if (track) {
00258         track->setVolume(volume);
00259         track->setPosition(true, pos);
00260         track->play();
00261         _playingTracks.push_back(track);
00262         return true;
00263     }
00264     return false;
00265 }
00266 
00267 bool EMISound::getSoundStatus(const Common::String &soundName) {
00268     TrackList::iterator it = getPlayingTrackByName(soundName);
00269 
00270     if (it == _playingTracks.end())  // We have no such sound.
00271         return false;
00272 
00273     return (*it)->isPlaying();
00274 }
00275 
00276 void EMISound::stopSound(const Common::String &soundName) {
00277     Common::StackLock lock(_mutex);
00278     TrackList::iterator it = getPlayingTrackByName(soundName);
00279     if (it == _playingTracks.end()) {
00280         warning("Sound track '%s' could not be found to stop", soundName.c_str());
00281     } else {
00282         delete (*it);
00283         _playingTracks.erase(it);
00284     }
00285 }
00286 
00287 int32 EMISound::getPosIn16msTicks(const Common::String &soundName) {
00288     TrackList::iterator it = getPlayingTrackByName(soundName);
00289     if (it == _playingTracks.end()) {
00290         warning("Sound track '%s' could not be found to get ticks", soundName.c_str());
00291         return 0;
00292     } else {
00293         return (*it)->getPos().msecs() / 16;
00294     }
00295 }
00296 
00297 void EMISound::setVolume(const Common::String &soundName, int volume) {
00298     Common::StackLock lock(_mutex);
00299     TrackList::iterator it = getPlayingTrackByName(soundName);
00300     if (it == _playingTracks.end()) {
00301         warning("Sound track '%s' could not be found to set volume", soundName.c_str());
00302     } else {
00303         (*it)->setVolume(volume);
00304     }
00305 }
00306 
00307 void EMISound::setPan(const Common::String &soundName, int pan) {
00308     Common::StackLock lock(_mutex);
00309     TrackList::iterator it = getPlayingTrackByName(soundName);
00310     if (it == _playingTracks.end()) {
00311         warning("Sound track '%s' could not be found to set pan", soundName.c_str());
00312     } else {
00313         (*it)->setBalance(pan * 2 - 127);
00314     }
00315 }
00316 
00317 bool EMISound::loadSfx(const Common::String &soundName, int &id) {
00318     Common::StackLock lock(_mutex);
00319     SoundTrack *track = initTrack(soundName, Audio::Mixer::kSFXSoundType);
00320     if (track) {
00321         id = _curTrackId++;
00322         _preloadedTrackMap[id] = track;
00323         return true;
00324     } else {
00325         return false;
00326     }
00327 }
00328 
00329 void EMISound::playLoadedSound(int id, bool looping) {
00330     Common::StackLock lock(_mutex);
00331     TrackMap::iterator it = _preloadedTrackMap.find(id);
00332     if (it != _preloadedTrackMap.end()) {
00333         it->_value->setLooping(looping);
00334         it->_value->setPosition(false);
00335         it->_value->play();
00336     } else {
00337         warning("EMISound::playLoadedSound called with invalid sound id");
00338     }
00339 }
00340 
00341 void EMISound::playLoadedSoundFrom(int id, const Math::Vector3d &pos, bool looping) {
00342     Common::StackLock lock(_mutex);
00343     TrackMap::iterator it = _preloadedTrackMap.find(id);
00344     if (it != _preloadedTrackMap.end()) {
00345         it->_value->setLooping(looping);
00346         it->_value->setPosition(true, pos);
00347         it->_value->play();
00348     }
00349     else {
00350         warning("EMISound::playLoadedSoundFrom called with invalid sound id");
00351     }
00352 }
00353 
00354 void EMISound::setLoadedSoundLooping(int id, bool looping) {
00355     Common::StackLock lock(_mutex);
00356     TrackMap::iterator it = _preloadedTrackMap.find(id);
00357     if (it != _preloadedTrackMap.end()) {
00358         it->_value->setLooping(looping);
00359     } else {
00360         warning("EMISound::setLoadedSoundLooping called with invalid sound id");
00361     }
00362 }
00363 
00364 void EMISound::stopLoadedSound(int id) {
00365     Common::StackLock lock(_mutex);
00366     TrackMap::iterator it = _preloadedTrackMap.find(id);
00367     if (it != _preloadedTrackMap.end()) {
00368         it->_value->stop();
00369     } else {
00370         warning("EMISound::stopLoadedSound called with invalid sound id");
00371     }
00372 }
00373 
00374 void EMISound::freeLoadedSound(int id) {
00375     Common::StackLock lock(_mutex);
00376     TrackMap::iterator it = _preloadedTrackMap.find(id);
00377     if (it != _preloadedTrackMap.end()) {
00378         delete it->_value;
00379         _preloadedTrackMap.erase(it);
00380     } else {
00381         warning("EMISound::freeLoadedSound called with invalid sound id");
00382     }
00383 }
00384 
00385 void EMISound::setLoadedSoundVolume(int id, int volume) {
00386     Common::StackLock lock(_mutex);
00387     TrackMap::iterator it = _preloadedTrackMap.find(id);
00388     if (it != _preloadedTrackMap.end()) {
00389         it->_value->setVolume(volume);
00390     } else {
00391         warning("EMISound::setLoadedSoundVolume called with invalid sound id");
00392     }
00393 }
00394 
00395 void EMISound::setLoadedSoundPan(int id, int pan) {
00396     Common::StackLock lock(_mutex);
00397     TrackMap::iterator it = _preloadedTrackMap.find(id);
00398     if (it != _preloadedTrackMap.end()) {
00399         it->_value->setBalance(pan * 2 - 127);
00400     } else {
00401         warning("EMISound::setLoadedSoundPan called with invalid sound id");
00402     }
00403 }
00404 
00405 void EMISound::setLoadedSoundPosition(int id, const Math::Vector3d &pos) {
00406     Common::StackLock lock(_mutex);
00407     TrackMap::iterator it = _preloadedTrackMap.find(id);
00408     if (it != _preloadedTrackMap.end()) {
00409         it->_value->setPosition(true, pos);
00410     } else {
00411         warning("EMISound::setLoadedSoundPosition called with invalid sound id");
00412     }
00413 }
00414 
00415 bool EMISound::getLoadedSoundStatus(int id) {
00416     Common::StackLock lock(_mutex);
00417     TrackMap::iterator it = _preloadedTrackMap.find(id);
00418     if (it != _preloadedTrackMap.end()) {
00419         return it->_value->isPlaying();
00420     }
00421     warning("EMISound::getLoadedSoundStatus called with invalid sound id");
00422     return false;
00423 }
00424 
00425 int EMISound::getLoadedSoundVolume(int id) {
00426     Common::StackLock lock(_mutex);
00427     TrackMap::iterator it = _preloadedTrackMap.find(id);
00428     if (it != _preloadedTrackMap.end()) {
00429         return it->_value->getVolume();
00430     }
00431     warning("EMISound::getLoadedSoundVolume called with invalid sound id");
00432     return false;
00433 }
00434 
00435 SoundTrack *EMISound::initTrack(const Common::String &soundName, Audio::Mixer::SoundType soundType, const Audio::Timestamp *start) const {
00436     SoundTrack *track;
00437     Common::String soundNameLower(soundName);
00438     soundNameLower.toLowercase();
00439     if (soundNameLower.hasSuffix(".scx")) {
00440         track = new SCXTrack(soundType);
00441     } else if (soundNameLower.hasSuffix(".m4b") || soundNameLower.hasSuffix(".lab")) {
00442         track = new MP3Track(soundType);
00443     } else if (soundNameLower.hasSuffix(".aif")) {
00444         track = new AIFFTrack(soundType);
00445     } else {
00446         track = new VimaTrack();
00447     }
00448 
00449     Common::String filename;
00450     if (soundType == Audio::Mixer::kMusicSoundType) {
00451         filename = _musicPrefix + soundName;
00452     } else {
00453         filename = soundName;
00454     }
00455 
00456     if (track->openSound(filename, soundName, start)) {
00457         return track;
00458     }
00459     return nullptr;
00460 }
00461 
00462 bool EMISound::stateHasLooped(int stateId) {
00463     if (stateId == _curMusicState) {
00464         if (_curMusicState != 0 && _musicTrack) {
00465             return _musicTrack->hasLooped();
00466         }
00467     } else {
00468         warning("EMISound::stateHasLooped called for a different music state than the current one");
00469     }
00470     return false;
00471 }
00472 
00473 bool EMISound::stateHasEnded(int stateId) {
00474     if (stateId == _curMusicState) {
00475         if (_curMusicState != 0 && _musicTrack) {
00476             return !_musicTrack->isPlaying();
00477         }
00478     }
00479     return true;
00480 }
00481 
00482 void EMISound::setMusicState(int stateId) {
00483     Common::StackLock lock(_mutex);
00484     // The demo calls ImSetState with state id 1000, which exceeds the number of states in the
00485     // music table.
00486     if (stateId >= _numMusicStates)
00487         stateId = 0;
00488     if (stateId == _curMusicState)
00489         return;
00490 
00491     if (_musicTable == nullptr) {
00492         Debug::debug(Debug::Sound, "No music table loaded");
00493         return;
00494     }
00495 
00496     Common::String soundName = _musicTable[stateId]._filename;
00497     int sync = _musicTable[stateId]._sync;
00498     Audio::Timestamp musicPos;
00499     int prevSync = -1;
00500     if (_musicTrack) {
00501         if (_musicTrack->isPlaying()) {
00502             musicPos = _musicTrack->getPos();
00503             prevSync = _musicTrack->getSync();
00504             if (sync == prevSync && soundName == _musicTrack->getSoundName()) {
00505                 // If the previous music track is the same track as the new one, we'll just
00506                 // keep playing the previous track. This happens in the PS2 version where they
00507                 // removed some of the music variations, but kept the states associated with
00508                 // those.
00509                 _curMusicState = stateId;
00510                 return;
00511             }
00512             _musicTrack->fadeOut();
00513             _playingTracks.push_back(_musicTrack);
00514             _musicTrack = nullptr;
00515         }
00516     }
00517 
00518     bool fadeMusicIn = false;
00519     for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
00520         if ((*it)->isPlaying() && (*it)->getSoundType() == Audio::Mixer::kMusicSoundType) {
00521             fadeMusicIn = true;
00522             break;
00523         }
00524     }
00525     if (!fadeMusicIn) {
00526         for (uint i = 0; i < _stateStack.size(); ++i) {
00527             if (_stateStack[i]._track && _stateStack[i]._track->isPlaying() && !_stateStack[i]._track->isPaused()) {
00528                 fadeMusicIn = true;
00529                 break;
00530             }
00531         }
00532     }
00533 
00534     if (stateId == 0) {
00535         _curMusicState = 0;
00536         return;
00537     }
00538     if (_musicTable[stateId]._id != stateId) {
00539         Debug::debug(Debug::Sound, "Attempted to play track #%d, not found in music table!", stateId);
00540         return;
00541     }
00542     _curMusicState = stateId;
00543 
00544     Audio::Timestamp *start = nullptr;
00545     if (prevSync != 0 && sync != 0 && prevSync == sync)
00546         start = &musicPos;
00547 
00548     Debug::debug(Debug::Sound, "Loading music: %s", soundName.c_str());
00549     SoundTrack *music = initTrack(soundName, Audio::Mixer::kMusicSoundType, start);
00550     if (music) {
00551         music->play();
00552         music->setSync(sync);
00553         if (fadeMusicIn) {
00554             music->setFade(0.0f);
00555             music->fadeIn();
00556         }
00557         _musicTrack = music;
00558     }
00559 }
00560 
00561 uint32 EMISound::getMsPos(int stateId) {
00562     if (!_musicTrack) {
00563         Debug::debug(Debug::Sound, "EMISound::getMsPos: Music track is null", stateId);
00564         return 0;
00565     }
00566     return _musicTrack->getPos().msecs();
00567 }
00568 
00569 MusicEntry *EMISound::initMusicTableDemo(const Common::String &filename) {
00570     Common::SeekableReadStream *data = g_resourceloader->openNewStreamFile(filename);
00571 
00572     if (!data)
00573         error("Couldn't open %s", filename.c_str());
00574     // FIXME, for now we use a fixed-size table, as I haven't looked at the retail-data yet.
00575     _numMusicStates = 15;
00576     MusicEntry *musicTable = new MusicEntry[_numMusicStates];
00577     for (int i = 0; i < 15; ++i) {
00578         musicTable->_x = 0;
00579         musicTable->_y = 0;
00580         musicTable->_sync = 0;
00581         musicTable->_trim = 0;
00582         musicTable->_id = i;
00583     }
00584 
00585     TextSplitter *ts = new TextSplitter(filename, data);
00586     int id, x, y, sync;
00587     char musicfilename[64];
00588     char name[64];
00589     while (!ts->isEof()) {
00590         while (!ts->checkString("*/")) {
00591             while (!ts->checkString(".cuebutton"))
00592                 ts->nextLine();
00593 
00594             ts->scanString(".cuebutton id %d x %d y %d sync %d \"%[^\"]64s", 5, &id, &x, &y, &sync, name);
00595             ts->scanString(".playfile \"%[^\"]64s", 1, musicfilename);
00596             musicTable[id]._id = id;
00597             musicTable[id]._x = x;
00598             musicTable[id]._y = y;
00599             musicTable[id]._sync = sync;
00600             musicTable[id]._name = name;
00601             musicTable[id]._filename = musicfilename;
00602         }
00603         ts->nextLine();
00604     }
00605     delete ts;
00606     delete data;
00607     return musicTable;
00608 }
00609 
00610 void EMISound::initMusicTableRetail(MusicEntry *musicTable, const Common::String filename) {
00611     Common::SeekableReadStream *data = g_resourceloader->openNewStreamFile(filename);
00612 
00613     // Remember to check, in case we forgot to copy over those files from the CDs.
00614     if (!data) {
00615         warning("Couldn't open %s", filename.c_str());
00616         return;
00617     }
00618     
00619     TextSplitter *ts = new TextSplitter(filename, data);
00620     int id, x, y, sync, trim;
00621     char musicfilename[64];
00622     char type[16];
00623     // Every block is followed by 3 lines of commenting/uncommenting, except the last.
00624     while (!ts->isEof()) {
00625         while (!ts->checkString("*/")) {
00626             while (!ts->checkString(".cuebutton"))
00627                 ts->nextLine();
00628 
00629             ts->scanString(".cuebutton id %d x %d y %d sync %d type %16s", 5, &id, &x, &y, &sync, type);
00630             ts->scanString(".playfile trim %d \"%[^\"]64s", 2, &trim, musicfilename);
00631             if (musicfilename[1] == '\\')
00632                 musicfilename[1] = '/';
00633             musicTable[id]._id = id;
00634             musicTable[id]._x = x;
00635             musicTable[id]._y = y;
00636             musicTable[id]._sync = sync;
00637             musicTable[id]._type = type;
00638             musicTable[id]._name = "";
00639             musicTable[id]._trim = trim;
00640             musicTable[id]._filename = musicfilename;
00641         }
00642         ts->nextLine();
00643     }
00644     delete ts;
00645     delete data;
00646 }
00647 
00648 void tableLoadErrorDialog() {
00649     const char *errorMessage = nullptr;
00650     errorMessage =  "ERROR: Not enough music tracks found!\n"
00651     "Escape from Monkey Island has two versions of FullMonkeyMap.imt,\n"
00652     "you need to copy both files from both CDs to Textures/, and rename\n"
00653     "them as follows to get music-support in-game: \n"
00654     "CD 1: \"FullMonkeyMap.imt\" -> \"FullMonkeyMap1.imt\"\n"
00655     "CD 2: \"FullMonkeyMap.imt\" -> \"FullMonkeyMap2.imt\"\n"
00656     "\n"
00657     "Alternatively, a Steam or GOG copy has a combined FullMonkeyMap.int";
00658     GUI::displayErrorDialog(errorMessage);
00659 }
00660 
00661 void EMISound::initMusicTable() {
00662     if (g_grim->getGameFlags() & ADGF_DEMO) {
00663         _musicTable = initMusicTableDemo("Music/FullMonkeyMap.imt");
00664         _musicPrefix = "Music/";
00665     } else if (g_grim->getGamePlatform() == Common::kPlatformPS2) {
00666         _musicTable = emiPS2MusicTable;
00667         _numMusicStates = ARRAYSIZE(emiPS2MusicTable);
00668         _musicPrefix = "";
00669     } else {
00670         MusicEntry *musicTable = new MusicEntry[126];
00671         for (int i = 0; i < 126; ++i) {
00672             musicTable[i]._x = 0;
00673             musicTable[i]._y = 0;
00674             musicTable[i]._sync = 0;
00675             musicTable[i]._trim = 0;
00676             musicTable[i]._id = i;
00677         }
00678 
00679         initMusicTableRetail(musicTable, "Textures/FullMonkeyMap1.imt");
00680         initMusicTableRetail(musicTable, "Textures/FullMonkeyMap2.imt");
00681         initMusicTableRetail(musicTable, "Textures/FullMonkeyMap.imt");
00682 
00683         /* There seem to be 69+60 music tracks, for a total of 125 unique tracks. */
00684         int numTracks = 0;
00685         for (int i = 0; i < 126; i++) {
00686             if (!musicTable[i]._filename.empty()) {
00687                 numTracks++;
00688             }
00689         }
00690 
00691         warning("Found %d music tracks, expected at least 100", numTracks);
00692         if (numTracks < 100) {
00693             delete[] musicTable;
00694             _numMusicStates = 0;
00695             _musicTable = nullptr;
00696             tableLoadErrorDialog();
00697         } else {
00698             _numMusicStates = 126;
00699             _musicTable = musicTable;
00700             _musicPrefix = "Textures/spago/"; // Default to high-quality music.
00701         }
00702     }
00703 }
00704 
00705 void EMISound::selectMusicSet(int setId) {
00706     if (g_grim->getGamePlatform() == Common::kPlatformPS2) {
00707         assert(setId == 0);
00708         _musicPrefix = "";
00709         return;
00710     }
00711     if (setId == 0) {
00712         _musicPrefix = "Textures/spago/";
00713     } else if (setId == 1) {
00714         _musicPrefix = "Textures/mego/";
00715     } else {
00716         error("EMISound::selectMusicSet - Unknown setId %d", setId);
00717     }
00718 
00719     // Immediately switch all currently active music tracks to the new quality.
00720     for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
00721         SoundTrack *track = (*it);
00722         if (track && track->getSoundType() == Audio::Mixer::kMusicSoundType) {
00723             (*it) = restartTrack(track);
00724             delete track;
00725         }
00726     }
00727     for (uint32 i = 0; i < _stateStack.size(); ++i) {
00728         SoundTrack *track = _stateStack[i]._track;
00729         if (track) {
00730             _stateStack[i]._track = restartTrack(track);
00731             delete track;
00732         }
00733     }
00734 }
00735 
00736 SoundTrack *EMISound::restartTrack(SoundTrack *track) {
00737     Audio::Timestamp pos = track->getPos();
00738     SoundTrack *newTrack = initTrack(track->getSoundName(), track->getSoundType(), &pos);
00739     if (newTrack) {
00740         newTrack->setVolume(track->getVolume());
00741         newTrack->setBalance(track->getBalance());
00742         newTrack->setFadeMode(track->getFadeMode());
00743         newTrack->setFade(track->getFade());
00744         if (track->isPlaying()) {
00745             newTrack->play();
00746         }
00747         if (track->isPaused()) {
00748             newTrack->pause();
00749         }
00750     }
00751     return newTrack;
00752 }
00753 
00754 void EMISound::pushStateToStack() {
00755     Common::StackLock lock(_mutex);
00756     if (_musicTrack) {
00757         _musicTrack->fadeOut();
00758         StackEntry entry = { _curMusicState, _musicTrack };
00759         _stateStack.push(entry);
00760         _musicTrack = nullptr;
00761     } else {
00762         StackEntry entry = { _curMusicState, nullptr };
00763         _stateStack.push(entry);
00764     }
00765     _curMusicState = 0;
00766 }
00767 
00768 void EMISound::popStateFromStack() {
00769     Common::StackLock lock(_mutex);
00770     if (_musicTrack) {
00771         _musicTrack->fadeOut();
00772         _playingTracks.push_back(_musicTrack);
00773     }
00774 
00775     //even pop state from stack if music isn't set
00776     StackEntry entry = _stateStack.pop();
00777     SoundTrack *track = entry._track;
00778 
00779     _musicTrack = track;
00780     _curMusicState = entry._state;
00781 
00782     if (track) {
00783         if (track->isPaused()) {
00784             track->pause();
00785         }
00786         track->fadeIn();
00787     }
00788 }
00789 
00790 void EMISound::flushStack() {
00791     Common::StackLock lock(_mutex);
00792     while (!_stateStack.empty()) {
00793         SoundTrack *temp = _stateStack.pop()._track;
00794         delete temp;
00795     }
00796 }
00797 
00798 void EMISound::pause(bool paused) {
00799     Common::StackLock lock(_mutex);
00800 
00801     for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
00802         SoundTrack *track = (*it);
00803         if (paused == track->isPaused())
00804             continue;
00805 
00806         // Do not pause music.
00807         if (track == _musicTrack)
00808             continue;
00809 
00810         track->pause();
00811     }
00812 
00813     for (TrackMap::iterator it = _preloadedTrackMap.begin(); it != _preloadedTrackMap.end(); ++it) {
00814         SoundTrack *track = (*it)._value;
00815         if (!track->isPlaying() || paused == track->isPaused())
00816             continue;
00817 
00818         track->pause();
00819     }
00820 }
00821 
00822 void EMISound::callback() {
00823     Common::StackLock lock(_mutex);
00824 
00825     if (_musicTrack) {
00826         updateTrack(_musicTrack);
00827     }
00828 
00829     for (uint i = 0; i < _stateStack.size(); ++i) {
00830         SoundTrack *track = _stateStack[i]._track;
00831         if (track == nullptr || track->isPaused() || !track->isPlaying())
00832             continue;
00833 
00834         updateTrack(track);
00835         if (track->getFadeMode() == SoundTrack::FadeOut && track->getFade() == 0.0f) {
00836             track->pause();
00837         }
00838     }
00839 
00840     for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
00841         SoundTrack *track = (*it);
00842         if (track->isPaused() || !track->isPlaying())
00843             continue;
00844 
00845         updateTrack(track);
00846         if (track->getFadeMode() == SoundTrack::FadeOut && track->getFade() == 0.0f) {
00847             track->stop();
00848         }
00849     }
00850 }
00851 
00852 void EMISound::updateTrack(SoundTrack *track) {
00853     if (track->getFadeMode() != SoundTrack::FadeNone) {
00854         float fadeStep = 0.5f / _callbackFps;
00855         float fade = track->getFade();
00856         if (track->getFadeMode() == SoundTrack::FadeIn) {
00857             fade += fadeStep;
00858             if (fade > 1.0f)
00859                 fade = 1.0f;
00860             track->setFade(fade);
00861         }
00862         else {
00863             fade -= fadeStep;
00864             if (fade < 0.0f)
00865                 fade = 0.0f;
00866             track->setFade(fade);
00867         }
00868     }
00869 }
00870 
00871 void EMISound::flushTracks() {
00872     Common::StackLock lock(_mutex);
00873     for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
00874         SoundTrack *track = (*it);
00875         if (!track->isPlaying()) {
00876             delete track;
00877             it = _playingTracks.erase(it);
00878         }
00879     }
00880 }
00881 
00882 void EMISound::restoreState(SaveGame *savedState) {
00883     Common::StackLock lock(_mutex);
00884     // Clear any current music
00885     flushStack();
00886     setMusicState(0);
00887     freePlayingSounds();
00888     freeLoadedSounds();
00889     delete _musicTrack;
00890     _musicTrack = nullptr;
00891     // Actually load:
00892     savedState->beginSection('SOUN');
00893     _musicPrefix = savedState->readString();
00894     if (savedState->saveMinorVersion() >= 21) {
00895         _curMusicState = savedState->readLESint32();
00896     }
00897 
00898     // Stack:
00899     uint32 stackSize = savedState->readLEUint32();
00900     for (uint32 i = 0; i < stackSize; i++) {
00901         SoundTrack *track = nullptr;
00902         int state = 0;
00903         if (savedState->saveMinorVersion() >= 21) {
00904             state = savedState->readLESint32();
00905             bool hasTrack = savedState->readBool();
00906             if (hasTrack) {
00907                 track = restoreTrack(savedState);
00908             }
00909         } else {
00910             Common::String soundName = savedState->readString();
00911             track = initTrack(soundName, Audio::Mixer::kMusicSoundType);
00912             if (track) {
00913                 track->play();
00914                 track->pause();
00915             }
00916         }
00917         StackEntry entry = { state, track };
00918         _stateStack.push(entry);
00919     }
00920 
00921     // Music:
00922     if (savedState->saveMinorVersion() < 21) {
00923         uint32 hasActiveTrack = savedState->readLEUint32();
00924         if (hasActiveTrack) {
00925             Common::String soundName = savedState->readString();
00926             _musicTrack = initTrack(soundName, Audio::Mixer::kMusicSoundType);
00927             if (_musicTrack) {
00928                 _musicTrack->play();
00929             } else {
00930                 error("Couldn't reopen %s", soundName.c_str());
00931             }
00932         }
00933     } else if (savedState->saveMinorVersion() >= 21) {
00934         bool musicActive = savedState->readBool();
00935         if (musicActive) {
00936             _musicTrack = restoreTrack(savedState);
00937         }
00938     }
00939 
00940     // Effects and voices:
00941     uint32 numTracks = savedState->readLEUint32();
00942     for (uint32 i = 0; i < numTracks; i++) {
00943         bool channelIsActive = true;
00944         if (savedState->saveMinorVersion() < 21) {
00945             channelIsActive = (savedState->readLESint32() != 0);
00946         }
00947         if (channelIsActive) {
00948             SoundTrack *track = restoreTrack(savedState);
00949             _playingTracks.push_back(track);
00950         }
00951     }
00952 
00953     // Preloaded sounds:
00954     if (savedState->saveMinorVersion() >= 21) {
00955         _curTrackId = savedState->readLESint32();
00956         uint32 numLoaded = savedState->readLEUint32();
00957         for (uint32 i = 0; i < numLoaded; ++i) {
00958             int id = savedState->readLESint32();
00959             _preloadedTrackMap[id] = restoreTrack(savedState);
00960         }
00961     }
00962 
00963     savedState->endSection();
00964 }
00965 
00966 void EMISound::saveState(SaveGame *savedState) {
00967     Common::StackLock lock(_mutex);
00968     savedState->beginSection('SOUN');
00969     savedState->writeString(_musicPrefix);
00970     savedState->writeLESint32(_curMusicState);
00971 
00972     // Stack:
00973     uint32 stackSize = _stateStack.size();
00974     savedState->writeLEUint32(stackSize);
00975     for (uint32 i = 0; i < stackSize; i++) {
00976         savedState->writeLESint32(_stateStack[i]._state);
00977         if (!_stateStack[i]._track) {
00978             savedState->writeBool(false);
00979         } else {
00980             savedState->writeBool(true);
00981             saveTrack(_stateStack[i]._track, savedState);
00982         }
00983     }
00984 
00985     // Music:
00986     savedState->writeBool(_musicTrack != nullptr);
00987     if (_musicTrack) {
00988         saveTrack(_musicTrack, savedState);
00989     }
00990 
00991     // Effects and voices:
00992     savedState->writeLEUint32(_playingTracks.size());
00993     for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
00994         saveTrack((*it), savedState);
00995     }
00996 
00997     // Preloaded sounds:
00998     savedState->writeLESint32(_curTrackId);
00999     uint32 numLoaded = _preloadedTrackMap.size();
01000     savedState->writeLEUint32(numLoaded);
01001     for (TrackMap::iterator it = _preloadedTrackMap.begin(); it != _preloadedTrackMap.end(); ++it) {
01002         savedState->writeLESint32(it->_key);
01003         saveTrack(it->_value, savedState);
01004     }
01005 
01006     savedState->endSection();
01007 }
01008 
01009 void EMISound::saveTrack(SoundTrack *track, SaveGame *savedState) {
01010     savedState->writeString(track->getSoundName());
01011     savedState->writeLEUint32(track->getVolume());
01012     savedState->writeLEUint32(track->getBalance());
01013     savedState->writeLEUint32(track->getPos().msecs());
01014     savedState->writeBool(track->isPlaying());
01015     savedState->writeBool(track->isPaused());
01016     savedState->writeLESint32((int)track->getSoundType());
01017     savedState->writeLESint32((int)track->getFadeMode());
01018     savedState->writeFloat(track->getFade());
01019     savedState->writeLESint32(track->getSync());
01020     savedState->writeBool(track->isLooping());
01021     savedState->writeBool(track->isPositioned());
01022     savedState->writeVector3d(track->getWorldPos());
01023 }
01024 
01025 SoundTrack *EMISound::restoreTrack(SaveGame *savedState) {
01026     Common::String soundName = savedState->readString();
01027     int volume = savedState->readLESint32();
01028     int balance = savedState->readLESint32();
01029     Audio::Timestamp pos(savedState->readLESint32());
01030     bool playing = savedState->readBool();
01031     if (savedState->saveMinorVersion() < 21) {
01032         SoundTrack *track = initTrack(soundName, Audio::Mixer::kSpeechSoundType);
01033         if (track)
01034             track->play();
01035         return track;
01036     }
01037     bool paused = savedState->readBool();
01038     Audio::Mixer::SoundType soundType = (Audio::Mixer::SoundType)savedState->readLESint32();
01039     SoundTrack::FadeMode fadeMode = (SoundTrack::FadeMode)savedState->readLESint32();
01040     float fade = savedState->readFloat();
01041     int sync = savedState->readLESint32();
01042     bool looping = savedState->saveMinorVersion() >= 21 ? savedState->readBool() : false;
01043     bool positioned = false;
01044     Math::Vector3d worldPos;
01045     if (savedState->saveMinorVersion() >= 23) {
01046         positioned = savedState->readBool();
01047         worldPos = savedState->readVector3d();
01048     }
01049 
01050     SoundTrack *track = initTrack(soundName, soundType, &pos);
01051     track->setVolume(volume);
01052     track->setBalance(balance);
01053     track->setPosition(positioned, worldPos);
01054     track->setLooping(looping);
01055     track->setFadeMode(fadeMode);
01056     track->setFade(fade);
01057     track->setSync(sync);
01058     if (playing)
01059         track->play();
01060     if (paused)
01061         track->pause();
01062     return track;
01063 }
01064 
01065 void EMISound::updateSoundPositions() {
01066     Common::StackLock lock(_mutex);
01067 
01068     for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
01069         SoundTrack *track = (*it);
01070         track->updatePosition();
01071     }
01072 
01073     for (TrackMap::iterator it = _preloadedTrackMap.begin(); it != _preloadedTrackMap.end(); ++it) {
01074         SoundTrack *track = (*it)._value;
01075         track->updatePosition();
01076     }
01077 }
01078 
01079 } // end of namespace Grim


Generated on Sat Apr 4 2020 05:00:29 for ResidualVM by doxygen 1.7.1
curved edge   curved edge