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

imuse.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/textconsole.h"
00024 #include "common/timer.h"
00025 
00026 #include "engines/grim/savegame.h"
00027 #include "engines/grim/debug.h"
00028 
00029 #include "engines/grim/imuse/imuse.h"
00030 #include "engines/grim/movie/codecs/vima.h"
00031 
00032 #include "audio/audiostream.h"
00033 #include "audio/mixer.h"
00034 #include "audio/decoders/raw.h"
00035 
00036 namespace Grim {
00037 
00038 Imuse *g_imuse = nullptr;
00039 
00040 extern uint16 imuseDestTable[];
00041 extern ImuseTable grimStateMusicTable[];
00042 extern ImuseTable grimSeqMusicTable[];
00043 extern ImuseTable grimDemoStateMusicTable[];
00044 extern ImuseTable grimDemoSeqMusicTable[];
00045 
00046 void Imuse::timerHandler(void *refCon) {
00047     Imuse *imuse = (Imuse *)refCon;
00048     imuse->callback();
00049 }
00050 
00051 Imuse::Imuse(int fps, bool demo) {
00052     _demo = demo;
00053     _pause = false;
00054     _sound = new ImuseSndMgr(_demo);
00055     assert(_sound);
00056     _callbackFps = fps;
00057     resetState();
00058     for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
00059         _track[l] = new Track;
00060         assert(_track[l]);
00061         memset(_track[l], 0, sizeof(Track));
00062         _track[l]->trackId = l;
00063     }
00064     vimaInit(imuseDestTable);
00065     if (_demo) {
00066         _stateMusicTable = grimDemoStateMusicTable;
00067         _seqMusicTable = grimDemoSeqMusicTable;
00068     } else {
00069         _stateMusicTable = grimStateMusicTable;
00070         _seqMusicTable = grimSeqMusicTable;
00071     }
00072     g_system->getTimerManager()->installTimerProc(timerHandler, 1000000 / _callbackFps, this, "imuseCallback");
00073 }
00074 
00075 Imuse::~Imuse() {
00076     g_system->getTimerManager()->removeTimerProc(timerHandler);
00077     stopAllSounds();
00078     for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
00079         delete _track[l];
00080     }
00081     delete _sound;
00082 }
00083 
00084 void Imuse::resetState() {
00085     _curMusicState = 0;
00086     _curMusicSeq = 0;
00087     memset(_attributes, 0, sizeof(_attributes));
00088 }
00089 
00090 void Imuse::restoreState(SaveGame *savedState) {
00091     Common::StackLock lock(_mutex);
00092 
00093     savedState->beginSection('IMUS');
00094     _curMusicState = savedState->readLESint32();
00095     _curMusicSeq = savedState->readLESint32();
00096     for (int r = 0; r < 185; r++) {
00097         _attributes[r] = savedState->readLESint32();
00098     }
00099 
00100     for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
00101         Track *track = _track[l];
00102         memset(track, 0, sizeof(Track));
00103         track->trackId = l;
00104         track->pan = savedState->readLESint32();
00105         track->panFadeDest = savedState->readLESint32();
00106         track->panFadeDelay = savedState->readLESint32();
00107         track->panFadeUsed = savedState->readBool();
00108         track->vol = savedState->readLESint32();
00109         track->volFadeDest = savedState->readLESint32();
00110         track->volFadeDelay = savedState->readLESint32();
00111         track->volFadeUsed = savedState->readBool();
00112         savedState->read(track->soundName, 32);
00113         track->used = savedState->readBool();
00114         track->toBeRemoved = savedState->readBool();
00115         track->priority = savedState->readLESint32();
00116         track->regionOffset = savedState->readLESint32();
00117         track->dataOffset = savedState->readLESint32();
00118         track->curRegion = savedState->readLESint32();
00119         track->curHookId = savedState->readLESint32();
00120         track->volGroupId = savedState->readLESint32();
00121         track->feedSize = savedState->readLESint32();
00122         track->mixerFlags = savedState->readLESint32();
00123 
00124         if (!track->used)
00125             continue;
00126 
00127         if (track->toBeRemoved || track->curRegion == -1) {
00128             track->used = false;
00129             continue;
00130         }
00131 
00132         track->soundDesc = _sound->openSound(track->soundName, track->volGroupId);
00133         if (!track->soundDesc) {
00134             warning("Imuse::restoreState: Can't open sound so will not be resumed");
00135             track->used = false;
00136             continue;
00137         }
00138 
00139         int channels = _sound->getChannels(track->soundDesc);
00140         int freq = _sound->getFreq(track->soundDesc);
00141         track->mixerFlags = kFlag16Bits;
00142         if (channels == 2)
00143             track->mixerFlags |= kFlagStereo | kFlagReverseStereo;
00144 
00145         track->stream = Audio::makeQueuingAudioStream(freq, (track->mixerFlags & kFlagStereo) != 0);
00146         g_system->getMixer()->playStream(track->getType(), &track->handle, track->stream, -1, track->getVol(),
00147                                             track->getPan(), DisposeAfterUse::YES, false,
00148                                             (track->mixerFlags & kFlagReverseStereo) != 0);
00149         g_system->getMixer()->pauseHandle(track->handle, true);
00150     }
00151     savedState->endSection();
00152     g_system->getMixer()->pauseAll(false);
00153 }
00154 
00155 void Imuse::saveState(SaveGame *savedState) {
00156     Common::StackLock lock(_mutex);
00157 
00158     savedState->beginSection('IMUS');
00159     savedState->writeLESint32(_curMusicState);
00160     savedState->writeLESint32(_curMusicSeq);
00161     for (int r = 0; r < 185; r++) {
00162         savedState->writeLESint32(_attributes[r]);
00163     }
00164 
00165     for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
00166         Track *track = _track[l];
00167         savedState->writeLESint32(track->pan);
00168         savedState->writeLESint32(track->panFadeDest);
00169         savedState->writeLESint32(track->panFadeDelay);
00170         savedState->writeBool(track->panFadeUsed);
00171         savedState->writeLESint32(track->vol);
00172         savedState->writeLESint32(track->volFadeDest);
00173         savedState->writeLESint32(track->volFadeDelay);
00174         savedState->writeBool(track->volFadeUsed);
00175         savedState->write(track->soundName, 32);
00176         savedState->writeBool(track->used);
00177         savedState->writeBool(track->toBeRemoved);
00178         savedState->writeLESint32(track->priority);
00179         savedState->writeLESint32(track->regionOffset);
00180         savedState->writeLESint32(track->dataOffset);
00181         savedState->writeLESint32(track->curRegion);
00182         savedState->writeLESint32(track->curHookId);
00183         savedState->writeLESint32(track->volGroupId);
00184         savedState->writeLESint32(track->feedSize);
00185         savedState->writeLESint32(track->mixerFlags);
00186     }
00187     savedState->endSection();
00188 }
00189 
00190 int32 Imuse::makeMixerFlags(int32 flags) {
00191     int32 mixerFlags = 0;
00192     if (flags & kFlagUnsigned)
00193         mixerFlags |= Audio::FLAG_UNSIGNED;
00194     if (flags & kFlag16Bits)
00195         mixerFlags |= Audio::FLAG_16BITS;
00196     if (flags & kFlagLittleEndian)
00197         mixerFlags |= Audio::FLAG_LITTLE_ENDIAN;
00198     if (flags & kFlagStereo)
00199         mixerFlags |= Audio::FLAG_STEREO;
00200     return mixerFlags;
00201 }
00202 
00203 void Imuse::callback() {
00204     Common::StackLock lock(_mutex);
00205 
00206     for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
00207         Track *track = _track[l];
00208         if (track->used) {
00209             // Ignore tracks which are about to finish. Also, if it did finish in the meantime,
00210             // mark it as unused.
00211             if (!track->stream) {
00212                 if (!track->soundDesc || !g_system->getMixer()->isSoundHandleActive(track->handle))
00213                     memset(track, 0, sizeof(Track));
00214                 continue;
00215             }
00216 
00217             if (_pause)
00218                 return;
00219 
00220             if (track->volFadeUsed) {
00221                 if (track->volFadeStep < 0) {
00222                     if (track->vol > track->volFadeDest) {
00223                         track->vol += track->volFadeStep;
00224                         //warning("fade: %d", track->vol);
00225                         if (track->vol < track->volFadeDest) {
00226                             track->vol = track->volFadeDest;
00227                             track->volFadeUsed = false;
00228                         }
00229                         if (track->vol == 0) {
00230                             // Fade out complete -> remove this track
00231                             flushTrack(track);
00232                             continue;
00233                         }
00234                     }
00235                 } else if (track->volFadeStep > 0) {
00236                     if (track->vol < track->volFadeDest) {
00237                         track->vol += track->volFadeStep;
00238                         //warning("fade: %d", track->vol);
00239                         if (track->vol > track->volFadeDest) {
00240                             track->vol = track->volFadeDest;
00241                             track->volFadeUsed = false;
00242                         }
00243                     }
00244                 }
00245             }
00246 
00247             if (track->panFadeUsed) {
00248                 if (track->panFadeStep < 0) {
00249                     if (track->pan > track->panFadeDest) {
00250                         track->pan += track->panFadeStep;
00251                         if (track->pan < track->panFadeDest) {
00252                             track->pan = track->panFadeDest;
00253                             track->panFadeUsed = false;
00254                         }
00255                     }
00256                 } else if (track->panFadeStep > 0) {
00257                     if (track->pan < track->panFadeDest) {
00258                         track->pan += track->panFadeStep;
00259                         if (track->pan > track->panFadeDest) {
00260                             track->pan = track->panFadeDest;
00261                             track->panFadeUsed = false;
00262                         }
00263                     }
00264                 }
00265             }
00266 
00267             assert(track->stream);
00268             byte *data = nullptr;
00269             int32 result = 0;
00270 
00271             if (track->curRegion == -1) {
00272                 switchToNextRegion(track);
00273                 if (!track->stream) // Seems we reached the end of the stream
00274                     continue;
00275             }
00276 
00277             int channels = _sound->getChannels(track->soundDesc);
00278             int32 mixer_size = track->feedSize / _callbackFps;
00279 
00280             if (track->stream->endOfData()) {
00281                 mixer_size *= 2;
00282             }
00283 
00284             if (channels == 1)
00285                 mixer_size &= ~1;
00286             if (channels == 2)
00287                 mixer_size &= ~3;
00288 
00289             if (mixer_size == 0)
00290                 continue;
00291 
00292             do {
00293                 result = _sound->getDataFromRegion(track->soundDesc, track->curRegion, &data, track->regionOffset, mixer_size);
00294                 if (channels == 1) {
00295                     result &= ~1;
00296                 }
00297                 if (channels == 2) {
00298                     result &= ~3;
00299                 }
00300 
00301                 if (result > mixer_size)
00302                     result = mixer_size;
00303 
00304                 if (g_system->getMixer()->isReady()) {
00305                     track->stream->queueBuffer(data, result, DisposeAfterUse::YES, makeMixerFlags(track->mixerFlags));
00306                     track->regionOffset += result;
00307                 } else
00308                     delete[] data;
00309 
00310                 if (_sound->isEndOfRegion(track->soundDesc, track->curRegion)) {
00311                     switchToNextRegion(track);
00312                     if (!track->stream)
00313                         break;
00314                 }
00315                 mixer_size -= result;
00316                 assert(mixer_size >= 0);
00317             } while (mixer_size);
00318             if (g_system->getMixer()->isReady()) {
00319                 g_system->getMixer()->setChannelVolume(track->handle, track->getVol());
00320                 g_system->getMixer()->setChannelBalance(track->handle, track->getPan());
00321             }
00322         }
00323     }
00324 }
00325 
00326 void Imuse::switchToNextRegion(Track *track) {
00327     assert(track);
00328 
00329     if (track->trackId >= MAX_IMUSE_TRACKS) {
00330         Debug::debug(Debug::Sound, "Imuse::switchToNextRegion(): fadeTrack end: soundName:%s", track->soundName);
00331         flushTrack(track);
00332         return;
00333     }
00334 
00335     int numRegions = _sound->getNumRegions(track->soundDesc);
00336 
00337     if (++track->curRegion == numRegions) {
00338         Debug::debug(Debug::Sound, "Imuse::switchToNextRegion(): end of tracks: soundName:%s", track->soundName);
00339         flushTrack(track);
00340         return;
00341     }
00342 
00343     ImuseSndMgr::SoundDesc *soundDesc = track->soundDesc;
00344     int jumpId = _sound->getJumpIdByRegionAndHookId(soundDesc, track->curRegion, track->curHookId);
00345     // It seems 128 is a special value meaning it should not force the 0 hookId,
00346     // otherwise the sound hkwine.imu when glottis drinks the wine in the barrel
00347     // in hk won't stop.
00348     if (jumpId == -1 && track->curHookId != 128)
00349         jumpId = _sound->getJumpIdByRegionAndHookId(soundDesc, track->curRegion, 0);
00350     if (jumpId != -1) {
00351         Debug::debug(Debug::Sound, "Imuse::switchToNextRegion(): JUMP: soundName:%s", track->soundName);
00352         int region = _sound->getRegionIdByJumpId(soundDesc, jumpId);
00353         assert(region != -1);
00354         int sampleHookId = _sound->getJumpHookId(soundDesc, jumpId);
00355         assert(sampleHookId != -1);
00356         int fadeDelay = (60 * _sound->getJumpFade(soundDesc, jumpId)) / 1000;
00357         if (fadeDelay) {
00358             Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay);
00359             if (fadeTrack) {
00360                 fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundDesc, fadeTrack->curRegion);
00361                 fadeTrack->regionOffset = 0;
00362                 fadeTrack->curHookId = 0;
00363             }
00364         }
00365         track->curRegion = region;
00366         if (track->curHookId == sampleHookId)
00367             track->curHookId = 0;
00368         else if (track->curHookId == 0x80)
00369             track->curHookId = 0;
00370     }
00371 
00372     Debug::debug(Debug::Sound, "Imuse::switchToNextRegion(): REGION %d: soundName:%s", (int)track->curRegion, track->soundName);
00373     track->dataOffset = _sound->getRegionOffset(soundDesc, track->curRegion);
00374     track->regionOffset = 0;
00375 }
00376 
00377 } // end of namespace Grim


Generated on Sat May 25 2019 05:00:47 for ResidualVM by doxygen 1.7.1
curved edge   curved edge