/* 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "audio/decoders/raw.h" #include "common/config-manager.h" #include "audio/decoders/raw.h" #include "audio/audiostream.h" #include "tsage/core.h" #include "tsage/globals.h" #include "tsage/debugger.h" #include "tsage/graphics.h" #include "tsage/tsage.h" namespace TsAGE { static SoundManager *_soundManager = NULL; /*--------------------------------------------------------------------------*/ SoundManager::SoundManager() { _soundManager = this; _sndmgrReady = false; _ourSndResVersion = 0x102; _ourDrvResVersion = 0x10A; for (int i = 0; i < SOUND_ARR_SIZE; ++i) _voiceTypeStructPtrs[i] = NULL; _groupsAvail = 0; _newVolume = _masterVol = 127; _driversDetected = false; _needToRethink = false; _soTimeIndexFlag = false; } SoundManager::~SoundManager() { if (_sndmgrReady) { Common::StackLock slock(_serverDisabledMutex); g_vm->_mixer->stopAll(); for (Common::List::iterator i = _soundList.begin(); i != _soundList.end(); ) { Sound *s = *i; ++i; s->stop(); } for (Common::List::iterator i = _installedDrivers.begin(); i != _installedDrivers.end(); ) { SoundDriver *driver = *i; ++i; delete driver; } sfTerminate(); // g_system->getTimerManager()->removeTimerProc(_sfUpdateCallback); } // Free any allocated voice type structures for (int idx = 0; idx < SOUND_ARR_SIZE; ++idx) { if (sfManager()._voiceTypeStructPtrs[idx]) { delete sfManager()._voiceTypeStructPtrs[idx]; sfManager()._voiceTypeStructPtrs[idx] = NULL; } } _soundManager = NULL; } void SoundManager::postInit() { if (!_sndmgrReady) { g_saver->addSaveNotifier(&SoundManager::saveNotifier); g_saver->addLoadNotifier(&SoundManager::loadNotifier); g_saver->addListener(this); // I originally separated the sound manager update method into a separate thread, since // it handles updates for both music and Fx. However, since Adlib updates also get done in a // thread, and doesn't get too far ahead, I've left it to the AdlibSoundDriver class to // call the update method, rather than having it be called separately // g_system->getTimerManager()->installTimerProc(_sfUpdateCallback, 1000000 / SOUND_FREQUENCY, NULL, "tsageSoundUpdate"); _sndmgrReady = true; } } /** * Loops through all the loaded sounds, and stops any that have been flagged for stopping */ void SoundManager::dispatch() { Common::List::iterator i = _soundList.begin(); while (i != _soundList.end()) { Sound *sound = *i; ++i; // If the sound is flagged for stopping, then stop it if (sound->_stoppedAsynchronously) { sound->stop(); } } } void SoundManager::syncSounds() { bool mute = false; if (ConfMan.hasKey("mute")) mute = ConfMan.getBool("mute"); bool music_mute = mute; if (!mute) { music_mute = ConfMan.getBool("music_mute"); } // Get the new music volume int musicVolume = music_mute ? 0 : MIN(255, ConfMan.getInt("music_volume")); this->setMasterVol(musicVolume / 2); } void SoundManager::update() { sfSoundServer(); } Common::List &SoundManager::buildDriverList(bool detectFlag) { assert(_sndmgrReady); _availableDrivers.clear(); // Build up a list of available drivers. Currently we only implement an Adlib music // and SoundBlaster FX driver // Adlib driver SoundDriverEntry sd; sd._driverNum = ADLIB_DRIVER_NUM; sd._status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED; sd._field2 = 0; sd._field6 = 15000; sd._shortDescription = "Adlib or SoundBlaster"; sd._longDescription = "3812fm"; _availableDrivers.push_back(sd); // SoundBlaster entry SoundDriverEntry sdFx; sdFx._driverNum = SBLASTER_DRIVER_NUM; sdFx._status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED; sdFx._field2 = 0; sdFx._field6 = 15000; sdFx._shortDescription = "SndBlast"; sdFx._longDescription = "SoundBlaster"; _availableDrivers.push_back(sdFx); _driversDetected = true; return _availableDrivers; } void SoundManager::installConfigDrivers() { installDriver(ADLIB_DRIVER_NUM); installDriver(SBLASTER_DRIVER_NUM); } Common::List &SoundManager::getDriverList(bool detectFlag) { if (detectFlag) return _availableDrivers; else return buildDriverList(false); } void SoundManager::dumpDriverList() { _availableDrivers.clear(); } /** * Install the specified driver number */ void SoundManager::installDriver(int driverNum) { // If driver is already installed, no need to install it if (isInstalled(driverNum)) return; // Instantiate the sound driver SoundDriver *driver = instantiateDriver(driverNum); if (!driver) return; assert((_ourDrvResVersion >= driver->_minVersion) && (_ourDrvResVersion <= driver->_maxVersion)); // Mute any loaded sounds Common::StackLock slock(_serverDisabledMutex); for (Common::List::iterator i = _playList.begin(); i != _playList.end(); ++i) (*i)->mute(true); // Install the driver if (!sfInstallDriver(driver)) error("Sound driver initialization failed"); switch (driverNum) { case ROLAND_DRIVER_NUM: case ADLIB_DRIVER_NUM: { // Handle loading bank infomation byte *bankData = g_resourceManager->getResource(RES_BANK, driverNum, 0, true); if (bankData) { // Install the patch bank data sfInstallPatchBank(driver, bankData); DEALLOCATE(bankData); } else { // Could not locate patch bank data, so unload the driver sfUnInstallDriver(driver); // Unmute currently active sounds for (Common::List::iterator i = _playList.begin(); i != _playList.end(); ++i) (*i)->mute(false); } break; } } } /** * Instantiate a driver class for the specified driver number */ SoundDriver *SoundManager::instantiateDriver(int driverNum) { switch (driverNum) { case ADLIB_DRIVER_NUM: return new AdlibSoundDriver(); case SBLASTER_DRIVER_NUM: return new SoundBlasterDriver(); default: error("Unknown sound driver - %d", driverNum); } } /** * Uninstall the specified driver */ void SoundManager::unInstallDriver(int driverNum) { Common::List::const_iterator i; for (i = _installedDrivers.begin(); i != _installedDrivers.end(); ++i) { if ((*i)->_driverResID == driverNum) { // Found driver to remove // Mute any loaded sounds Common::StackLock slock(_serverDisabledMutex); Common::List::iterator j; for (j = _playList.begin(); j != _playList.end(); ++j) (*j)->mute(true); // Uninstall the driver sfUnInstallDriver(*i); // Re-orient all the loaded sounds for (j = _soundList.begin(); j != _soundList.end(); ++j) (*j)->orientAfterDriverChange(); // Unmute currently active sounds for (j = _playList.begin(); j != _playList.end(); ++j) (*j)->mute(false); } } } /** * Returns true if a specified driver number is currently installed */ bool SoundManager::isInstalled(int driverNum) const { Common::List::const_iterator i; for (i = _installedDrivers.begin(); i != _installedDrivers.end(); ++i) { if ((*i)->_driverResID == driverNum) return true; } return false; } void SoundManager::setMasterVol(int volume) { _newVolume = volume; } int SoundManager::getMasterVol() const { return _masterVol; } void SoundManager::loadSound(int soundNum, bool showErrors) { // This method preloaded the data associated with a given sound, so is now redundant } void SoundManager::unloadSound(int soundNum) { // This method signalled the resource manager to unload the data for a sound, and is now redundant } int SoundManager::determineGroup(const byte *soundData) { return sfDetermineGroup(soundData); } void SoundManager::checkResVersion(const byte *soundData) { int maxVersion = READ_LE_UINT16(soundData + 4); int minVersion = READ_LE_UINT16(soundData + 6); if (_soundManager->_ourSndResVersion < minVersion) error("Attempt to play/prime sound resource that is too new"); if (_soundManager->_ourSndResVersion > maxVersion) error("Attempt to play/prime sound resource that is too old"); } int SoundManager::extractPriority(const byte *soundData) { return READ_LE_UINT16(soundData + 12); } int SoundManager::extractLoop(const byte *soundData) { return READ_LE_UINT16(soundData + 14); } void SoundManager::extractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum) { sfExtractTrackInfo(trackInfo, soundData, groupNum); } void SoundManager::addToSoundList(Sound *sound) { if (!contains(_soundList, sound)) _soundList.push_back(sound); } void SoundManager::removeFromSoundList(Sound *sound) { _soundList.remove(sound); } void SoundManager::addToPlayList(Sound *sound) { sfAddToPlayList(sound); } void SoundManager::removeFromPlayList(Sound *sound) { if (_soundManager) sfRemoveFromPlayList(sound); } bool SoundManager::isOnPlayList(Sound *sound) { return sfIsOnPlayList(sound); } void SoundManager::updateSoundVol(Sound *sound) { sfUpdateVolume(sound); } void SoundManager::updateSoundPri(Sound *sound) { sfUpdatePriority(sound); } void SoundManager::updateSoundLoop(Sound *sound) { sfUpdateLoop(sound); } void SoundManager::rethinkVoiceTypes() { Common::StackLock slock(sfManager()._serverSuspendedMutex); sfRethinkVoiceTypes(); } void SoundManager::sfSoundServer() { if (sfManager()._needToRethink) { sfRethinkVoiceTypes(); sfManager()._needToRethink = false; } else { sfDereferenceAll(); } // If the master volume has changed, update it if (sfManager()._newVolume != sfManager()._masterVol) sfSetMasterVol(sfManager()._newVolume); // If a time index has been set for any sound, fast forward to it SynchronizedList::iterator i; for (i = sfManager()._playList.begin(); i != sfManager()._playList.end(); ++i) { Sound *s = *i; if (s->_newTimeIndex != 0) { s->mute(true); s->soSetTimeIndex(s->_newTimeIndex); s->mute(false); s->_newTimeIndex = 0; } } // Handle any fading if necessary sfProcessFading(); // Poll all sound drivers in case they need it for (Common::List::iterator j = sfManager()._installedDrivers.begin(); j != sfManager()._installedDrivers.end(); ++j) { (*j)->poll(); } } void SoundManager::sfProcessFading() { // Loop through processing active sounds bool removeFlag = false; Common::List::iterator i = sfManager()._playList.begin(); while (i != sfManager()._playList.end()) { Sound *s = *i; ++i; if (!s->_pausedCount) removeFlag = s->soServiceTracks(); if (removeFlag) { sfDoRemoveFromPlayList(s); s->_stoppedAsynchronously = true; sfManager()._needToRethink = true; } if (s->_fadeDest != -1) { if (s->_fadeCounter != 0) --s->_fadeCounter; else { if (s->_volume >= s->_fadeDest) { s->_volume = ((s->_volume - s->_fadeDest) > s->_fadeSteps) ? s->_volume - s->_fadeSteps : s->_fadeDest; } else { s->_volume = ((s->_fadeDest - s->_volume) > s->_fadeSteps) ? s->_volume + s->_fadeSteps : s->_fadeDest; } sfDoUpdateVolume(s); if (s->_volume != s->_fadeDest) s->_fadeCounter = s->_fadeTicks; else { s->_fadeDest = -1; if (s->_stopAfterFadeFlag) { sfDoRemoveFromPlayList(s); s->_stoppedAsynchronously = true; sfManager()._needToRethink = true; } } } } } // Loop through the voiceType list for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { VoiceTypeStruct *vtStruct = sfManager()._voiceTypeStructPtrs[voiceIndex]; if (!vtStruct) continue; if (vtStruct->_voiceType == VOICETYPE_1) { for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { if (vtStruct->_entries[idx]._type1._field6 >= -1) ++vtStruct->_entries[idx]._type1._field6; } } } } bool SoundManager::isFading() { Common::StackLock slock(sfManager()._serverSuspendedMutex); // Loop through any active sounds to see if any are being actively faded Common::List::iterator i = sfManager()._playList.begin(); while (i != sfManager()._playList.end()) { Sound *s = *i; ++i; if (s->_fadeDest != -1) return true; } return false; } void SoundManager::sfUpdateVoiceStructs() { for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { VoiceTypeStruct *vs = sfManager()._voiceTypeStructPtrs[voiceIndex]; if (!vs) continue; if (vs->_voiceType == VOICETYPE_0) { for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntry &vse = vs->_entries[idx]; vse._type0._sound = vse._type0._sound2; vse._type0._channelNum = vse._type0._channelNum2; vse._type0._priority = vse._type0._priority2; vse._type0._fieldA = vse._type0._field12; } } else { vs->_field3 = vs->_numVoices; for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntry &vse = vs->_entries[idx]; vse._type1._sound = vse._type1._sound2; vse._type1._channelNum = vse._type1._channelNum2; vse._type1._priority = vse._type1._priority2; } } } } void SoundManager::sfUpdateVoiceStructs2() { for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { VoiceTypeStruct *vtStruct = sfManager()._voiceTypeStructPtrs[voiceIndex]; if (!vtStruct) continue; for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { if (vtStruct->_voiceType == VOICETYPE_0) { VoiceStructEntryType0 &vse = vtStruct->_entries[idx]._type0; vse._sound2 = vse._sound; vse._channelNum2 = vse._channelNum; vse._priority2 = vse._priority; vse._field12 = vse._fieldA; } else { VoiceStructEntryType1 &vse = vtStruct->_entries[idx]._type1; vse._sound2 = vse._sound; vse._channelNum2 = vse._channelNum; vse._priority2 = vse._priority; } } } } void SoundManager::sfUpdateCallback(void *ref) { ((SoundManager *)ref)->update(); } /*--------------------------------------------------------------------------*/ void SoundManager::saveNotifier(bool postFlag) { _soundManager->saveNotifierProc(postFlag); } void SoundManager::saveNotifierProc(bool postFlag) { // Nothing needs to be done when saving the game } void SoundManager::loadNotifier(bool postFlag) { _soundManager->loadNotifierProc(postFlag); } void SoundManager::loadNotifierProc(bool postFlag) { if (!postFlag) { // Stop any currently playing sounds if (_sndmgrReady) { Common::StackLock slock(_serverDisabledMutex); for (Common::List::iterator i = _soundList.begin(); i != _soundList.end(); ) { Sound *s = *i; ++i; s->stop(); } } } else { // Savegame is now loaded, so iterate over the sound list to prime any sounds as necessary for (Common::List::iterator i = _soundList.begin(); i != _soundList.end(); ++i) { Sound *s = *i; s->orientAfterRestore(); } } } void SoundManager::listenerSynchronize(Serializer &s) { s.validate("SoundManager"); assert(_sndmgrReady && _driversDetected); if (s.getVersion() < 6) return; Common::StackLock slock(_serverDisabledMutex); _playList.synchronize(s); _soundList.synchronize(s); } /*--------------------------------------------------------------------------*/ SoundManager &SoundManager::sfManager() { assert(_soundManager); return *_soundManager; } int SoundManager::sfDetermineGroup(const byte *soundData) { const byte *p = soundData + READ_LE_UINT16(soundData + 8); uint32 v; while ((v = READ_LE_UINT32(p)) != 0) { if ((v & _soundManager->_groupsAvail) == v) return v; p += 6 + (READ_LE_UINT16(p + 4) * 4); } return 0; } void SoundManager::sfAddToPlayList(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); sfDoAddToPlayList(sound); sound->_stoppedAsynchronously = false; sfRethinkVoiceTypes(); } void SoundManager::sfRemoveFromPlayList(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); if (sfDoRemoveFromPlayList(sound)) sfRethinkVoiceTypes(); } bool SoundManager::sfIsOnPlayList(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); bool result = contains(_soundManager->_playList, sound); return result; } void SoundManager::sfRethinkSoundDrivers() { // Free any existing entries int idx; for (idx = 0; idx < SOUND_ARR_SIZE; ++idx) { if (sfManager()._voiceTypeStructPtrs[idx]) { delete sfManager()._voiceTypeStructPtrs[idx]; sfManager()._voiceTypeStructPtrs[idx] = NULL; } } for (idx = 0; idx < SOUND_ARR_SIZE; ++idx) { byte flag = 0xff; int total = 0; // Loop through the sound drivers for (Common::List::iterator i = sfManager()._installedDrivers.begin(); i != sfManager()._installedDrivers.end(); ++i) { // Process the group data for each sound driver SoundDriver *driver = *i; const byte *groupData = driver->_groupOffset->_pData; while (*groupData != 0xff) { byte byteVal = *groupData++; if (byteVal == idx) { byte byteVal2 = *groupData++; if (flag == 0xff) flag = byteVal2; else { assert(flag == byteVal2); } if (!flag) { while (*groupData++ != 0xff) ++total; } else { total += *groupData; groupData += 2; } } else if (*groupData++ == 0) { while (*groupData != 0xff) ++groupData; ++groupData; } else { groupData += 2; } } } if (total) { VoiceTypeStruct *vs = new VoiceTypeStruct(); sfManager()._voiceTypeStructPtrs[idx] = vs; if (!flag) { vs->_voiceType = VOICETYPE_0; } else { vs->_voiceType = VOICETYPE_1; } vs->_total = vs->_numVoices = total; vs->_field3 = 0; for (Common::List::iterator i = sfManager()._installedDrivers.begin(); i != sfManager()._installedDrivers.end(); ++i) { // Process the group data for each sound driver SoundDriver *driver = *i; const byte *groupData = driver->_groupOffset->_pData; while (*groupData != 0xff) { byte byteVal = *groupData++; if (byteVal == idx) { ++groupData; if (!flag) { while ((byteVal = *groupData++) != 0xff) { VoiceStructEntry ve; memset(&ve, 0, sizeof(VoiceStructEntry)); ve._field1 = (byteVal & 0x80) ? 0 : 1; ve._driver = driver; ve._type0._sound = NULL; ve._type0._channelNum = 0; ve._type0._priority = 0; ve._type0._fieldA = 0; vs->_entries.push_back(ve); } } else { byteVal = *groupData; groupData += 2; for (int entryIndez = 0; entryIndez < byteVal; ++entryIndez) { VoiceStructEntry ve; memset(&ve, 0, sizeof(VoiceStructEntry)); ve._voiceNum = entryIndez; ve._driver = driver; ve._type1._field4 = -1; ve._type1._field5 = 0; ve._type1._field6 = 0; ve._type1._sound = NULL; ve._type1._channelNum = 0; ve._type1._priority = 0; vs->_entries.push_back(ve); } } } else { if (*groupData++ != 0) { while (*groupData != 0xff) ++groupData; } else { groupData += 2; } } } } } } } void SoundManager::sfRethinkVoiceTypes() { sfDereferenceAll(); // Pre-processing for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { VoiceTypeStruct *vs = sfManager()._voiceTypeStructPtrs[voiceIndex]; if (!vs) continue; if (vs->_voiceType == VOICETYPE_0) { for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntry &vse = vs->_entries[idx]; vse._type0._sound3 = vse._type0._sound; vse._type0._channelNum3 = vse._type0._channelNum; vse._type0._priority3 = vse._type0._priority; vse._type0._field1A = vse._type0._fieldA; vse._type0._sound = NULL; vse._type0._channelNum = 0; vse._type0._priority = 0; vse._type0._fieldA = 0; vse._type0._sound2 = NULL; vse._type0._channelNum2 = 0; vse._type0._priority2 = 0; vse._type0._field12 = 0; } } else { for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntry &vse = vs->_entries[idx]; vse._type1._sound3 = vse._type1._sound; vse._type1._channelNum3 = vse._type1._channelNum; vse._type1._priority3 = vse._type1._priority; vse._type1._sound = NULL; vse._type1._channelNum = 0; vse._type1._priority = 0; vse._type1._sound2 = NULL; vse._type1._channelNum2 = 0; vse._type1._priority2 = 0; } // Reset the number of voices available vs->_numVoices = vs->_total; } } // Main processing loop int priorityOffset = 0; for (Common::List::iterator i = sfManager()._playList.begin(); i != sfManager()._playList.end(); ++i, priorityOffset += 16) { Sound *sound = *i; if ((sound->_mutedCount != 0) || (sound->_pausedCount != 0)) continue; sfUpdateVoiceStructs(); Common::fill(sound->_chWork, sound->_chWork + SOUND_ARR_SIZE, false); for (;;) { // Scan for sub priority int foundIndex = -1, foundPriority = 0; for (int idx = 0; idx < SOUND_ARR_SIZE; ++idx) { if (!(sound->_chFlags[idx] & 0x8000) & !sound->_chWork[idx]) { int subPriority = sound->_chSubPriority[idx]; if (subPriority) subPriority = 16 - subPriority + priorityOffset; if (foundIndex != -1) { if (subPriority < foundPriority) { foundIndex = idx; foundPriority = subPriority; } } else { foundIndex = idx; foundPriority = subPriority; } } } if (foundIndex == -1) break; int chNumVoices = sound->_chNumVoices[foundIndex]; sound->_chWork[foundIndex] = true; VoiceTypeStruct *vtStruct = sfManager()._voiceTypeStructPtrs[sound->_chVoiceType[foundIndex]]; if (!vtStruct) { if (foundPriority) continue; sfUpdateVoiceStructs2(); break; } if (vtStruct->_voiceType != VOICETYPE_0) { // Type 1 int numVoices = vtStruct->_numVoices; if (numVoices >= chNumVoices) { int channelCount = chNumVoices, idx = 0; while (channelCount > 0) { if (!vtStruct->_entries[idx]._type1._sound2) { vtStruct->_entries[idx]._type1._sound2 = sound; vtStruct->_entries[idx]._type1._channelNum2 = foundIndex; vtStruct->_entries[idx]._type1._priority2 = foundPriority; --channelCount; } ++idx; } vtStruct->_numVoices -= chNumVoices; continue; } else if (!foundPriority) { do { int maxPriority = 0; for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) maxPriority = MAX(maxPriority, vtStruct->_entries[idx]._type1._priority2); if (!maxPriority) { sfUpdateVoiceStructs2(); break; } for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { if (vtStruct->_entries[idx]._type1._priority2 == maxPriority) { vtStruct->_entries[idx]._type1._sound2 = NULL; vtStruct->_entries[idx]._type1._channelNum2 = 0; vtStruct->_entries[idx]._type1._priority2 = 0; ++numVoices; } } } while (chNumVoices > numVoices); int voicesCtr = chNumVoices; for (uint idx = 0; (idx < vtStruct->_entries.size()) && (voicesCtr > 0); ++idx) { if (!vtStruct->_entries[idx]._type1._sound2) { vtStruct->_entries[idx]._type1._sound2 = sound; vtStruct->_entries[idx]._type1._channelNum2 = foundIndex; vtStruct->_entries[idx]._type1._priority2 = foundPriority; --voicesCtr; } } numVoices -= chNumVoices; vtStruct->_numVoices = numVoices; continue; } else if (!numVoices) { break; } continue; } else { // Type 0 if (sound->_isEmpty) { uint idx = 0; while ((idx < vtStruct->_entries.size()) && (vtStruct->_entries[idx]._voiceNum == foundIndex)) ++idx; if (idx == vtStruct->_entries.size()) continue; } int flagsVal = sound->_chFlags[foundIndex] & 3; if (flagsVal != 1) { // All modes except mode 1 (loc_23EDF) int entryIndex = -1, maxVoiceNum = 0; for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { if (!vtStruct->_entries[idx]._type0._sound2 && (vtStruct->_entries[idx]._field1 != 0) && (vtStruct->_entries[idx]._voiceNum > maxVoiceNum)) { maxVoiceNum = vtStruct->_entries[idx]._voiceNum; entryIndex = idx; } } if (entryIndex != -1) { vtStruct->_entries[entryIndex]._type0._sound2 = sound; vtStruct->_entries[entryIndex]._type0._channelNum2 = foundIndex; vtStruct->_entries[entryIndex]._type0._priority2 = foundPriority; vtStruct->_entries[entryIndex]._type0._field12 = 0; continue; } if (foundPriority != 0) continue; int maxPriority = 0; entryIndex = -1; for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { if ((vtStruct->_entries[idx]._field1 != 0) && (vtStruct->_entries[idx]._type0._priority2 > maxPriority)) { maxPriority = vtStruct->_entries[idx]._type0._priority2; entryIndex = idx; } } if (entryIndex != -1) { vtStruct->_entries[entryIndex]._type0._sound2 = sound; vtStruct->_entries[entryIndex]._type0._channelNum2 = foundIndex; vtStruct->_entries[entryIndex]._type0._priority2 = foundPriority; vtStruct->_entries[entryIndex]._type0._field12 = 0; continue; } sfUpdateVoiceStructs2(); break; } else { // Channel mode 1 handling (loc_23FAC) bool foundMatch = false; int entryIndex = -1; for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { if (vtStruct->_entries[idx]._voiceNum == foundIndex) { foundIndex = true; if (!vtStruct->_entries[idx]._type0._sound2) { entryIndex = idx; break; } } } if (entryIndex != -1) { vtStruct->_entries[entryIndex]._type0._sound2 = sound; vtStruct->_entries[entryIndex]._type0._channelNum2 = foundIndex; vtStruct->_entries[entryIndex]._type0._priority2 = foundPriority; vtStruct->_entries[entryIndex]._type0._field12 = 0; continue; } if (!foundMatch) { if (foundPriority) continue; if (entryIndex == -1) { sfUpdateVoiceStructs2(); break; } } // Find the entry with the highest priority int maxPriority = 0; foundMatch = false; entryIndex = -1; for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { if (vtStruct->_entries[idx]._voiceNum != foundIndex) continue; if (!vtStruct->_entries[idx]._type0._field12) { foundMatch = true; break; } if (vtStruct->_entries[idx]._type0._priority2 > maxPriority) { maxPriority = vtStruct->_entries[idx]._type0._priority2; entryIndex = -1; } } if (!foundMatch) { if (foundPriority) continue; if (entryIndex != -1) { vtStruct->_entries[entryIndex]._type0._sound2 = sound; vtStruct->_entries[entryIndex]._type0._channelNum2 = foundIndex; vtStruct->_entries[entryIndex]._type0._priority2 = foundPriority; vtStruct->_entries[entryIndex]._type0._field12 = 1; continue; } sfUpdateVoiceStructs2(); break; } // Found a match (loc_24061) maxPriority = 0; int maxVoiceNum = 0; int priorityIndex = -1, voiceIndex = -1; for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { if (vtStruct->_entries[idx]._field1) { if (!vtStruct->_entries[idx]._type0._sound2) { if (vtStruct->_entries[idx]._voiceNum > maxVoiceNum) { maxVoiceNum = vtStruct->_entries[idx]._voiceNum; voiceIndex = idx; } } else { if (vtStruct->_entries[idx]._type0._priority2 > maxPriority) { maxPriority = vtStruct->_entries[idx]._type0._priority2; priorityIndex = idx; } } } } if (voiceIndex != -1) { VoiceStructEntryType0 &vteSrc = vtStruct->_entries[foundIndex]._type0; VoiceStructEntryType0 &vteDest = vtStruct->_entries[voiceIndex]._type0; vteDest._sound2 = vteSrc._sound2; vteDest._channelNum2 = vteSrc._channelNum2; vteDest._priority2 = vteSrc._priority2; vteSrc._sound2 = sound; vteSrc._channelNum2 = foundIndex; vteSrc._priority2 = foundPriority; vteSrc._field12 = 1; continue; } if (!foundPriority) continue; if (priorityIndex == -1) { sfUpdateVoiceStructs2(); break; } VoiceStructEntryType0 &vteSrc = vtStruct->_entries[foundIndex]._type0; VoiceStructEntryType0 &vteDest = vtStruct->_entries[priorityIndex]._type0; if (priorityIndex != foundIndex) { vteDest._sound2 = vteSrc._sound2; vteDest._channelNum2 = vteSrc._channelNum2; vteDest._priority2 = vteSrc._priority2; vteDest._field12 = vteSrc._field12; } vteSrc._sound2 = sound; vteSrc._channelNum2 = foundIndex; vteSrc._priority2 = foundPriority; vteSrc._field12 = 1; continue; } } } } // Post-processing for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { VoiceTypeStruct *vs = sfManager()._voiceTypeStructPtrs[voiceIndex]; if (!vs) continue; if (vs->_voiceType == VOICETYPE_0) { // Type 0 for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntryType0 &vse = vs->_entries[idx]._type0; SoundDriver *driver = vs->_entries[idx]._driver; assert(driver); if (vse._field12) { int total = 0; vse._sound = vse._sound2; if (vse._sound3 != vse._sound) ++total; vse._channelNum = vse._channelNum2; if (vse._channelNum3 != vse._channelNum) ++total; vse._priority = vse._priority2; vse._fieldA = 1; vse._sound2 = NULL; if (total) { driver->proc24(vse._channelNum, idx, vse._sound, 123, 0); driver->proc24(vse._channelNum, idx, vse._sound, 1, vse._sound->_chModulation[vse._channelNum]); driver->proc24(vse._channelNum, idx, vse._sound, 7, vse._sound->_chVolume[vse._channelNum] * vse._sound->_volume / 127); driver->proc24(vse._channelNum, idx, vse._sound, 10, vse._sound->_chPan[vse._channelNum]); driver->proc24(vse._channelNum, idx, vse._sound, 64, vse._sound->_chDamper[vse._channelNum]); driver->setProgram(vse._channelNum, vse._sound->_chProgram[vse._channelNum]); driver->setPitchBlend(vse._channelNum, vse._sound->_chPitchBlend[vse._channelNum]); vse._sound3 = NULL; } } else { vse._sound = NULL; vse._channelNum = 0; vse._priority = 0; vse._fieldA = 0; } } for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntryType0 &vse = vs->_entries[idx]._type0; Sound *sound = vse._sound2; int channelNum = vse._channelNum2; if (!sound) continue; for (uint entryIndex = 0; entryIndex < vs->_entries.size(); ++entryIndex) { if ((vs->_entries[entryIndex]._type0._sound3 != sound) || (vs->_entries[entryIndex]._type0._channelNum3 != channelNum)) { // Found match vs->_entries[entryIndex]._type0._sound = sound; vs->_entries[entryIndex]._type0._channelNum = channelNum; vs->_entries[entryIndex]._type0._priority = vse._priority2; vs->_entries[entryIndex]._type0._fieldA = 0; vse._sound2 = NULL; break; } } } for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntryType0 &vse = vs->_entries[idx]._type0; Sound *sound = vse._sound2; if (!sound) continue; int voiceNum = 0, foundIndex = -1; for (uint entryIndex = 0; entryIndex < vs->_entries.size(); ++entryIndex) { if ((vs->_entries[entryIndex]._field1) && !vs->_entries[entryIndex]._type0._sound) { int tempVoice = vs->_entries[entryIndex]._voiceNum; if (voiceNum <= tempVoice) { voiceNum = tempVoice; foundIndex = entryIndex; } } } assert(foundIndex != -1); VoiceStructEntryType0 &vseFound = vs->_entries[foundIndex]._type0; vseFound._sound = vse._sound2; vseFound._channelNum = vse._channelNum2; vseFound._priority = vse._priority2; vseFound._fieldA = 0; SoundDriver *driver = vs->_entries[foundIndex]._driver; assert(driver); driver->proc24(vseFound._channelNum, voiceIndex, vseFound._sound, 123, 0); driver->proc24(vseFound._channelNum, voiceIndex, vseFound._sound, 1, vseFound._sound->_chModulation[vseFound._channelNum]); driver->proc24(vseFound._channelNum, voiceIndex, vseFound._sound, 7, vseFound._sound->_chVolume[vseFound._channelNum] * vseFound._sound->_volume / 127); driver->proc24(vseFound._channelNum, voiceIndex, vseFound._sound, 10, vseFound._sound->_chPan[vseFound._channelNum]); driver->setProgram(vseFound._channelNum, vseFound._sound->_chProgram[vseFound._channelNum]); driver->setPitchBlend(vseFound._channelNum, vseFound._sound->_chPitchBlend[vseFound._channelNum]); } // Final loop for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntryType0 &vse = vs->_entries[idx]._type0; if (!vse._sound && (vse._sound3)) { SoundDriver *driver = vs->_entries[idx]._driver; assert(driver); driver->proc24(vs->_entries[idx]._voiceNum, voiceIndex, vse._sound3, 123, 0); } } } else { // Type 1 for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntry &vse = vs->_entries[idx]; vse._type1._sound = NULL; vse._type1._channelNum = 0; vse._type1._priority = 0; } for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntryType1 &vse = vs->_entries[idx]._type1; Sound *sound = vse._sound2; int channelNum = vse._channelNum2; if (!sound) continue; for (uint entryIndex = 0; entryIndex < vs->_entries.size(); ++entryIndex) { VoiceStructEntryType1 &vse2 = vs->_entries[entryIndex]._type1; if (!vse2._sound && (vse2._sound3 == sound) && (vse2._channelNum3 == channelNum)) { vse2._sound = sound; vse2._channelNum = channelNum; vse2._priority = vse._priority2; vse._sound2 = NULL; break; } } } uint idx2 = 0; for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntryType1 &vse = vs->_entries[idx]._type1; Sound *sound = vse._sound2; if (!sound) continue; while (vs->_entries[idx2]._type1._sound) ++idx2; VoiceStructEntryType1 &vse2 = vs->_entries[idx2]._type1; vse2._sound = vse._sound2; vse2._channelNum = vse._channelNum2; vse2._priority = vse._priority2; vse2._field4 = -1; vse2._field5 = 0; vse2._field6 = 0; SoundDriver *driver = vs->_entries[idx2]._driver; assert(driver); driver->updateVoice(vs->_entries[idx2]._voiceNum); driver->proc38(vs->_entries[idx2]._voiceNum, 1, vse2._sound->_chModulation[vse2._channelNum]); driver->proc38(vs->_entries[idx2]._voiceNum, 7, vse2._sound->_chVolume[vse2._channelNum] * vse2._sound->_volume / 127); driver->proc38(vs->_entries[idx2]._voiceNum, 10, vse2._sound->_chPan[vse2._channelNum]); driver->setPitch(vs->_entries[idx2]._voiceNum, vse2._sound->_chPitchBlend[vse2._channelNum]); } for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntryType1 &vse = vs->_entries[idx]._type1; if (!vse._sound && (vse._sound3)) { vse._field4 = -1; vse._field5 = 0; vse._field6 = 0; SoundDriver *driver = vs->_entries[idx]._driver; assert(driver); driver->updateVoice(vs->_entries[idx]._voiceNum); } } } } } void SoundManager::sfUpdateVolume(Sound *sound) { sfDereferenceAll(); sfDoUpdateVolume(sound); } void SoundManager::sfDereferenceAll() { // Orignal used handles for both the driver list and voiceTypeStructPtrs list. This method then refreshed // pointer lists based on the handles. Since in ScummVM we're just using pointers directly, this // method doesn't need any implementation } void SoundManager::sfUpdatePriority(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); int tempPriority = (sound->_fixedPriority == 255) ? sound->_sndResPriority : sound->_priority; if (sound->_priority != tempPriority) { sound->_priority = tempPriority; if (sfDoRemoveFromPlayList(sound)) { sfDoAddToPlayList(sound); sfRethinkVoiceTypes(); } } } void SoundManager::sfUpdateLoop(Sound *sound) { if (sound->_fixedLoop) sound->_loop = sound->_sndResLoop; else sound->_loop = sound->_fixedLoop; } void SoundManager::sfSetMasterVol(int volume) { if (volume > 127) volume = 127; if (volume != _soundManager->_masterVol) { _soundManager->_masterVol = volume; for (Common::List::iterator i = _soundManager->_installedDrivers.begin(); i != _soundManager->_installedDrivers.end(); ++i) { (*i)->setMasterVolume(volume); } } } void SoundManager::sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum) { trackInfo->_numTracks = 0; const byte *p = soundData + READ_LE_UINT16(soundData + 8); uint32 v; while ((v = READ_LE_UINT32(p)) != 0) { if ((v == 0x80000000) || (v == (uint)groupNum)) { // Found group to process int count = READ_LE_UINT16(p + 4); p += 6; for (int idx = 0; idx < count; ++idx) { if (trackInfo->_numTracks == 16) { trackInfo->_numTracks = -1; return; } trackInfo->_chunks[trackInfo->_numTracks] = READ_LE_UINT16(p); trackInfo->_voiceTypes[trackInfo->_numTracks] = READ_LE_UINT16(p + 2); ++trackInfo->_numTracks; p += 4; } } else { // Not correct group, so move to next one p += 6 + (READ_LE_UINT16(p + 4) * 4); } } } void SoundManager::sfTerminate() { } void SoundManager::sfExtractGroupMask() { uint32 mask = 0; for (Common::List::iterator i = sfManager()._installedDrivers.begin(); i != sfManager()._installedDrivers.end(); ++i) mask |= (*i)->_groupMask; _soundManager->_groupsAvail = mask; } bool SoundManager::sfInstallDriver(SoundDriver *driver) { if (!driver->open()) return false; sfManager()._installedDrivers.push_back(driver); driver->_groupOffset = driver->getGroupData(); driver->_groupMask = driver->_groupOffset->_groupMask; sfExtractGroupMask(); sfRethinkSoundDrivers(); driver->setMasterVolume(sfManager()._masterVol); return true; } void SoundManager::sfUnInstallDriver(SoundDriver *driver) { sfManager()._installedDrivers.remove(driver); delete driver; sfExtractGroupMask(); sfRethinkSoundDrivers(); } void SoundManager::sfInstallPatchBank(SoundDriver *driver, const byte *bankData) { driver->installPatch(bankData, g_vm->_memoryManager.getSize(bankData)); } /** * Adds the specified sound in the playing sound list, inserting in order of priority */ void SoundManager::sfDoAddToPlayList(Sound *sound) { Common::StackLock slock2(sfManager()._serverSuspendedMutex); Common::List::iterator i = sfManager()._playList.begin(); while ((i != sfManager()._playList.end()) && (sound->_priority > (*i)->_priority)) ++i; sfManager()._playList.insert(i, sound); } /** * Removes the specified sound from the play list */ bool SoundManager::sfDoRemoveFromPlayList(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); bool result = false; for (Common::List::iterator i = sfManager()._playList.begin(); i != sfManager()._playList.end(); ++i) { if (*i == sound) { result = true; sfManager()._playList.erase(i); break; } } return result; } void SoundManager::sfDoUpdateVolume(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { VoiceTypeStruct *vs = sfManager()._voiceTypeStructPtrs[voiceIndex]; if (!vs) continue; for (uint idx = 0; idx < vs->_entries.size(); ++idx) { VoiceStructEntry &vse = vs->_entries[idx]; SoundDriver *driver = vse._driver; if (vs->_voiceType == VOICETYPE_0) { if (vse._type0._sound) { int vol = sound->_volume * sound->_chVolume[vse._type0._channelNum] / 127; driver->proc24(voiceIndex, vse._voiceNum, sound, 7, vol); } } else { if (vse._type1._sound) { int vol = sound->_volume * sound->_chVolume[vse._type1._channelNum] / 127; driver->proc38(vse._voiceNum, 7, vol); } } } } } /*--------------------------------------------------------------------------*/ Sound::Sound() { _stoppedAsynchronously = false; _soundResID = 0; _group = 0; _sndResPriority = 0; _fixedPriority = -1; _sndResLoop = 1; _fixedLoop = -1; _priority = 0; _volume = 127; _loop = 0; _pausedCount = 0; _mutedCount = 0; _hold = 0xff; _cueValue = -1; _fadeDest = -1; _fadeSteps = 0; _fadeTicks = 0; _fadeCounter = 0; _stopAfterFadeFlag = false; _timer = 0; _newTimeIndex = 0; _loopTimer = 0; _trackInfo._numTracks = 0; _primed = false; _isEmpty = false; _remoteReceiver = NULL; memset(_chProgram, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_chModulation, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_chVolume, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_chPan, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_chDamper, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_chPitchBlend, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_chVoiceType, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_chNumVoices, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_chSubPriority, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_chFlags, 0, SOUND_ARR_SIZE * sizeof(int)); Common::fill(_chWork, _chWork + SOUND_ARR_SIZE, false); memset(_channelData, 0, SOUND_ARR_SIZE * sizeof(byte *)); memset(_trkChannel, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_trkState, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_trkLoopState, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_trkIndex, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_trkLoopIndex, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_trkRest, 0, SOUND_ARR_SIZE * sizeof(int)); memset(_trkLoopRest, 0, SOUND_ARR_SIZE * sizeof(int)); for (int i = 0; i < 16; i++) { _chWork[i] = false; _trackInfo._chunks[i] = 0; _trackInfo._voiceTypes[i] = 0; } } Sound::~Sound() { stop(); } void Sound::synchronize(Serializer &s) { if (s.getVersion() < 6) return; assert(!_remoteReceiver); s.syncAsSint16LE(_soundResID); s.syncAsByte(_primed); s.syncAsByte(_stoppedAsynchronously); s.syncAsSint16LE(_group); s.syncAsSint16LE(_sndResPriority); s.syncAsSint16LE(_fixedPriority); s.syncAsSint16LE(_sndResLoop); s.syncAsSint16LE(_fixedLoop); s.syncAsSint16LE(_priority); s.syncAsSint16LE(_volume); s.syncAsSint16LE(_loop); s.syncAsSint16LE(_pausedCount); s.syncAsSint16LE(_mutedCount); s.syncAsSint16LE(_hold); s.syncAsSint16LE(_cueValue); s.syncAsSint16LE(_fadeDest); s.syncAsSint16LE(_fadeSteps); s.syncAsUint32LE(_fadeTicks); s.syncAsUint32LE(_fadeCounter); s.syncAsByte(_stopAfterFadeFlag); s.syncAsUint32LE(_timer); s.syncAsSint16LE(_loopTimer); } void Sound::play(int soundNum) { prime(soundNum); _soundManager->addToPlayList(this); } void Sound::stop() { g_globals->_soundManager.removeFromPlayList(this); _unPrime(); } void Sound::prime(int soundResID) { if (_soundResID != -1) { stop(); _prime(soundResID, false); } } void Sound::unPrime() { stop(); } void Sound::_prime(int soundResID, bool dontQueue) { if (_primed) unPrime(); _soundResID = soundResID; if (_soundResID != -1) { // Sound number specified _isEmpty = false; _remoteReceiver = NULL; byte *soundData = g_resourceManager->getResource(RES_SOUND, soundResID, 0); _soundManager->checkResVersion(soundData); _group = _soundManager->determineGroup(soundData); _sndResPriority = _soundManager->extractPriority(soundData); _sndResLoop = _soundManager->extractLoop(soundData); _soundManager->extractTrackInfo(&_trackInfo, soundData, _group); for (int idx = 0; idx < _trackInfo._numTracks; ++idx) { _channelData[idx] = g_resourceManager->getResource(RES_SOUND, soundResID, _trackInfo._chunks[idx]); } DEALLOCATE(soundData); } else { // No sound specified _isEmpty = true; _group = 0; _sndResPriority = 0; _sndResLoop = 0; _trackInfo._numTracks = 0; _channelData[0] = ALLOCATE(200); _remoteReceiver = ALLOCATE(200); } soPrimeSound(dontQueue); if (!dontQueue) _soundManager->addToSoundList(this); _primed = true; } void Sound::_unPrime() { if (_primed) { if (_isEmpty) { DEALLOCATE(_channelData[0]); DEALLOCATE(_remoteReceiver); _remoteReceiver = NULL; } else { for (int idx = 0; idx < _trackInfo._numTracks; ++idx) { DEALLOCATE(_channelData[idx]); } } _trackInfo._numTracks = 0; if (_soundManager) _soundManager->removeFromSoundList(this); _primed = false; _stoppedAsynchronously = false; } } void Sound::orientAfterDriverChange() { if (!_isEmpty) { int timeIndex = getTimeIndex(); for (int idx = 0; idx < _trackInfo._numTracks; ++idx) DEALLOCATE(_channelData[idx]); _trackInfo._numTracks = 0; _primed = false; _prime(_soundResID, true); setTimeIndex(timeIndex); } } void Sound::orientAfterRestore() { if (!_isEmpty) { int timeIndex = getTimeIndex(); _primed = false; _prime(_soundResID, true); setTimeIndex(timeIndex); } } void Sound::go() { if (!_primed) error("Attempt to execute Sound::go() on an unprimed Sound"); _soundManager->addToPlayList(this); } void Sound::halt(void) { _soundManager->removeFromPlayList(this); } int Sound::getSoundNum() const { return _soundResID; } bool Sound::isPlaying() { return _soundManager->isOnPlayList(this); } bool Sound::isPrimed() const { return _primed; } bool Sound::isPaused() const { return _pausedCount != 0; } bool Sound::isMuted() const { return _mutedCount != 0; } void Sound::pause(bool flag) { Common::StackLock slock(g_globals->_soundManager._serverSuspendedMutex); if (flag) ++_pausedCount; else if (_pausedCount > 0) --_pausedCount; _soundManager->rethinkVoiceTypes(); } void Sound::mute(bool flag) { Common::StackLock slock(g_globals->_soundManager._serverSuspendedMutex); if (flag) ++_mutedCount; else if (_mutedCount > 0) --_mutedCount; _soundManager->rethinkVoiceTypes(); } void Sound::fade(int fadeDest, int fadeSteps, int fadeTicks, bool stopAfterFadeFlag) { Common::StackLock slock(g_globals->_soundManager._serverSuspendedMutex); if (fadeDest > 127) fadeDest = 127; if (fadeTicks > 127) fadeTicks = 127; if (fadeSteps > 255) fadeSteps = 255; _fadeDest = fadeDest; _fadeTicks = fadeTicks; _fadeSteps = fadeSteps; _fadeCounter = 0; _stopAfterFadeFlag = stopAfterFadeFlag; } void Sound::setTimeIndex(uint32 timeIndex) { if (_primed) _newTimeIndex = timeIndex; } uint32 Sound::getTimeIndex() const { return _timer; } int Sound::getCueValue() const { return _cueValue; } void Sound::setCueValue(int cueValue) { _cueValue = cueValue; } void Sound::setVol(int volume) { if (volume > 127) volume = 127; if (_volume != volume) { _volume = volume; if (isPlaying()) _soundManager->updateSoundVol(this); } } int Sound::getVol() const { return _volume; } void Sound::setPri(int priority) { if (priority > 127) priority = 127; _fixedPriority = priority; _soundManager->updateSoundPri(this); } void Sound::setLoop(int flag) { _fixedLoop = flag; _soundManager->updateSoundLoop(this); } int Sound::getPri() const { return _priority; } int Sound::getLoop() { return _loop; } void Sound::holdAt(int amount) { if (amount > 127) amount = 127; _hold = amount; } void Sound::release() { _hold = -1; } void Sound::soPrimeSound(bool dontQueue) { if (!dontQueue) { _priority = (_fixedPriority != -1) ? _fixedPriority : _sndResPriority; _loop = !_fixedLoop ? _fixedLoop : _sndResLoop; _pausedCount = 0; _mutedCount = 0; _hold = -1; _cueValue = -1; _fadeDest = -1; _fadeSteps = 0; _fadeTicks = 0; _fadeCounter = 0; _stopAfterFadeFlag = false; } _timer = 0; _newTimeIndex = 0; _loopTimer = 0; soPrimeChannelData(); } void Sound::soSetTimeIndex(uint timeIndex) { Common::StackLock slock(g_globals->_soundManager._serverSuspendedMutex); if (timeIndex != _timer) { _soundManager->_soTimeIndexFlag = true; _timer = 0; _loopTimer = 0; soPrimeChannelData(); while (timeIndex > 0) { if (soServiceTracks()) { SoundManager::sfDoRemoveFromPlayList(this); _stoppedAsynchronously = true; _soundManager->_needToRethink = true; break; } --timeIndex; } _soundManager->_soTimeIndexFlag = false; } } bool Sound::soServiceTracks() { if (_isEmpty) { soRemoteReceive(); return false; } bool flag = true; for (int trackCtr = 0; trackCtr < _trackInfo._numTracks; ++trackCtr) { int mode = *_channelData[trackCtr]; if (mode == 0) { soServiceTrackType0(trackCtr, _channelData[trackCtr]); } else if (mode == 1) { soServiceTrackType1(trackCtr, _channelData[trackCtr]); } else { error("Unknown sound mode encountered"); } if (_trkState[trackCtr]) flag = false; } ++_timer; if (!flag) return false; else if ((_loop > 0) && (--_loop == 0)) return true; else { for (int trackCtr = 0; trackCtr < _trackInfo._numTracks; ++trackCtr) { _trkState[trackCtr] = _trkLoopState[trackCtr]; _trkRest[trackCtr] = _trkLoopRest[trackCtr]; _trkIndex[trackCtr] = _trkLoopIndex[trackCtr]; } _timer = _loopTimer; return false; } } void Sound::soPrimeChannelData() { if (_isEmpty) { for (int idx = 0; idx < 16; ++idx) { _chProgram[idx] = 0; _chModulation[idx] = 0; _chVolume[idx] = 127; _chPan[idx] = 64; _chDamper[idx] = 0; _chVoiceType[idx] = VOICETYPE_0; _chNumVoices[idx] = 0; _chSubPriority[idx] = 0; _chPitchBlend[idx] = 0x2000; _chFlags[idx] = 1; } _trkChannel[0] = 0; _trkState[0] = 1; _trkLoopState[0] = 1; _trkIndex[0] = 0; _trkLoopIndex[0] = 0; } else { for (int idx = 0; idx < SOUND_ARR_SIZE; ++idx) _chFlags[idx] = 0x8000; for (int idx = 0; idx < _trackInfo._numTracks; ++idx) { byte *d = _channelData[idx]; int mode = *d; int channelNum = (int8)*(d + 1); _trkChannel[idx] = channelNum; assert((channelNum >= -1) && (channelNum < 16)); if (channelNum >= 0) { _chProgram[channelNum] = *(d + 10); _chModulation[channelNum] = 0; _chVolume[channelNum] = *(d + 11); _chPan[channelNum] = *(d + 12); _chDamper[channelNum] = 0; _chVoiceType[channelNum] = _trackInfo._voiceTypes[idx]; _chNumVoices[channelNum] = *(d + 6); _chSubPriority[channelNum] = *(d + 7); _chPitchBlend[channelNum] = 0x2000; _chFlags[channelNum] = READ_LE_UINT16(d + 8); } if (mode == 0) { _trkState[idx] = 1; _trkLoopState[idx] = 1; _trkIndex[idx] = 14; _trkLoopIndex[idx] = 14; _trkRest[idx] = 0; _trkLoopRest[idx] = 0; } else if (mode == 1) { _trkState[idx] = 1; _trkLoopState[idx] = 1; _trkIndex[idx] = 0; _trkLoopIndex[idx] = 0; _trkRest[idx] = 0; _trkLoopRest[idx] = 0; } else { error("Unknown sound mode encountered"); } } } } void Sound::soRemoteReceive() { error("soRemoteReceive not implemented"); } void Sound::soServiceTrackType0(int trackIndex, const byte *channelData) { if (_trkRest[trackIndex]) { --_trkRest[trackIndex]; return; } if (!_trkState[trackIndex]) return; int channelNum = _trkChannel[trackIndex]; assert((channelNum >= -1) && (channelNum < SOUND_ARR_SIZE)); int chFlags = (channelNum == -1) ? 0 : _chFlags[channelNum]; int voiceNum = -1; SoundDriver *driver = NULL; VoiceTypeStruct *vtStruct; VoiceType voiceType = VOICETYPE_0, chVoiceType = VOICETYPE_0; if ((channelNum == -1) || _soundManager->_soTimeIndexFlag) { vtStruct = NULL; voiceType = VOICETYPE_0; } else { chVoiceType = (VoiceType)_chVoiceType[channelNum]; vtStruct = _soundManager->_voiceTypeStructPtrs[(int)chVoiceType]; if (vtStruct) { voiceType = vtStruct->_voiceType; if (voiceType == VOICETYPE_0) { for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { if (!vtStruct->_entries[idx]._type0._sound && (vtStruct->_entries[idx]._type0._channelNum != channelNum)) { voiceNum = vtStruct->_entries[idx]._voiceNum; driver = vtStruct->_entries[idx]._driver; break; } } } } } const byte *pData = channelData + _trkIndex[trackIndex]; for (;;) { byte v = *pData++; if (!(v & 0x80)) { // Area #1 if (!_soundManager->_soTimeIndexFlag) { // Only do processing if fast forwarding to a given time index if (channelNum != -1) { if (voiceType == VOICETYPE_1) { soUpdateDamper(vtStruct, channelNum, chVoiceType, v); } else if (voiceNum != -1) { assert(driver); driver->proc18(voiceNum, chVoiceType); } } } } else if (!(v & 0x40)) { // Area #2 if (!_soundManager->_soTimeIndexFlag) { // Only do processing if fast forwarding to a given time index byte b = *pData++; v <<= 1; if (b & 0x80) v |= 1; b &= 0x7f; if (channelNum != -1) { if (voiceType != VOICETYPE_0) { if (chFlags & 0x10) soPlaySound2(vtStruct, channelData, channelNum, chVoiceType, v); else soPlaySound(vtStruct, channelData, channelNum, chVoiceType, v, b); } else if (voiceNum != -1) { assert(driver); driver->proc20(voiceNum, chVoiceType); } } } else { ++pData; } } else if (!(v & 0x20)) { // Area #3 v &= 0x1f; // Gather up an extended number int trkRest = v; while ((*pData & 0xE0) == 0xC0) { byte b = *pData++; trkRest = (trkRest << 5) | (b & 0x1f); } _trkRest[trackIndex] = trkRest - 1; _trkIndex[trackIndex] = pData - channelData; return; } else if (!(v & 0x10)) { // Area #4 v = (v & 0xf) << 1; byte b = *pData++; if (b & 0x80) v |= 1; b &= 0x7f; assert(v < 4); int cmdList[32] = { 1, 7, 10, 64 }; int cmdVal = cmdList[v]; if (channelNum == -1) { if (soDoUpdateTracks(cmdVal, b)) return; } else { soDoTrackCommand(_trkChannel[trackIndex], cmdVal, b); if (!_soundManager->_soTimeIndexFlag) { if (cmdVal == 7) b = static_cast(_volume * (int)b / 127); if (voiceType != VOICETYPE_0) { soProc38(vtStruct, channelNum, chVoiceType, cmdVal, b); } else if (voiceNum != -1) { assert(driver); driver->proc24(voiceNum, chVoiceType, this, cmdVal, b); } } } } else if (!(v & 0x8)) { // Area #5 if (!_soundManager->_soTimeIndexFlag) { // Only do processing if fast forwarding to a given time index int cx = READ_LE_UINT16(pData); pData += 2; if (channelNum != -1) { assert(driver); driver->proc22(voiceNum, chVoiceType, cx); } } else { pData += 2; } } else if (!(v & 0x4)) { // Area #6 int cmd = *pData++; int value = *pData++; if (channelNum != -1) { soDoTrackCommand(_trkChannel[trackIndex], cmd, value); if (!_soundManager->_soTimeIndexFlag) { if (voiceType != VOICETYPE_0) { soProc38(vtStruct, channelNum, chVoiceType, cmd, value); } else if (voiceNum != -1) { assert(driver); driver->proc24(voiceNum, chVoiceType, this, cmd, value); } } } else if (soDoUpdateTracks(cmd, value)) { return; } } else if (!(v & 0x2)) { // Area #7 if (!_soundManager->_soTimeIndexFlag) { int pitchBlend = READ_BE_UINT16(pData); pData += 2; if (channelNum != -1) { int channel = _trkChannel[trackIndex]; _chPitchBlend[channel] = pitchBlend; if (voiceType != VOICETYPE_0) { soProc40(vtStruct, channelNum, pitchBlend); } else if (voiceNum != -1) { assert(driver); driver->setPitchBlend(channel, pitchBlend); } } } else { pData += 2; } } else if (!(v & 0x1)) { // Area #8 int program = *pData++; if (channelNum != -1) { int channel = _trkChannel[trackIndex]; _chProgram[channel] = program; if (!_soundManager->_soTimeIndexFlag) { if ((voiceType == VOICETYPE_0) && (voiceNum != -1)) { assert(driver); driver->setProgram(voiceNum, program); } } } else { soSetTrackPos(trackIndex, pData - channelData, program); } } else { // Area #9 byte b = *pData++; if (b & 0x80) { _trkState[trackIndex] = 0; _trkIndex[trackIndex] = pData - channelData; return; } if (!_soundManager->_soTimeIndexFlag) { if ((channelNum != -1) && (voiceType == VOICETYPE_0) && (voiceNum != -1)) { assert(driver); driver->setVolume1(voiceNum, chVoiceType, 0, b); } } } } } void Sound::soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0) { bool hasDamper = _chDamper[channelNum] != 0; for (uint idx = 0; idx < voiceType->_entries.size(); ++idx) { VoiceStructEntryType1 &vte = voiceType->_entries[idx]._type1; if ((vte._field4 == v0) && (vte._channelNum == channelNum) && (vte._sound == this)) { if (hasDamper) vte._field5 = 1; else { SoundDriver *driver = voiceType->_entries[idx]._driver; assert(driver); vte._field4 = -1; vte._field5 = 0; driver->updateVoice(voiceType->_entries[idx]._voiceNum); } return; } } } void Sound::soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1) { int entryIndex = soFindSound(vtStruct, channelNum); if (entryIndex != -1) { SoundDriver *driver = vtStruct->_entries[entryIndex]._driver; assert(driver); vtStruct->_entries[entryIndex]._type1._field6 = 0; vtStruct->_entries[entryIndex]._type1._field4 = v0; vtStruct->_entries[entryIndex]._type1._field5 = 0; driver->playSound(channelData, 0, _chProgram[channelNum], vtStruct->_entries[entryIndex]._voiceNum, v0, v1); } } void Sound::soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0) { for (int trackCtr = 0; trackCtr < _trackInfo._numTracks; ++trackCtr) { const byte *instrument = _channelData[trackCtr]; if ((*(instrument + 13) == v0) && (*instrument == 1)) { int entryIndex = soFindSound(vtStruct, channelNum); if (entryIndex != -1) { SoundDriver *driver = vtStruct->_entries[entryIndex]._driver; assert(driver); byte *trackData = _channelData[trackCtr]; vtStruct->_entries[entryIndex]._type1._field6 = 0; vtStruct->_entries[entryIndex]._type1._field4 = v0; vtStruct->_entries[entryIndex]._type1._field5 = 0; int v1, v2; driver->playSound(trackData, 14, -1, vtStruct->_entries[entryIndex]._voiceNum, v0, 0x7F); driver->proc42(vtStruct->_entries[entryIndex]._voiceNum, voiceType, 0, &v1, &v2); } break; } } } void Sound::soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value) { if (cmd == 64) { if (value == 0) { for (uint entryIndex = 0; entryIndex < vtStruct->_entries.size(); ++entryIndex) { VoiceStructEntryType1 &vte = vtStruct->_entries[entryIndex]._type1; if ((vte._sound == this) && (vte._channelNum == channelNum) && (vte._field5 != 0)) { SoundDriver *driver = vtStruct->_entries[entryIndex]._driver; assert(driver); vte._field4 = -1; vte._field5 = 0; driver->updateVoice(vtStruct->_entries[entryIndex]._voiceNum); } } } } else if (cmd == 75) { _soundManager->_needToRethink = true; } else { for (uint entryIndex = 0; entryIndex < vtStruct->_entries.size(); ++entryIndex) { VoiceStructEntry &vte = vtStruct->_entries[entryIndex]; if ((vte._type1._sound == this) && (vte._type1._channelNum == channelNum)) { SoundDriver *driver = vte._driver; assert(driver); driver->proc38(vte._voiceNum, cmd, value); } } } } void Sound::soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend) { for (uint entryIndex = 0; entryIndex < vtStruct->_entries.size(); ++entryIndex) { VoiceStructEntryType1 &vte = vtStruct->_entries[entryIndex]._type1; if ((vte._sound == this) && (vte._channelNum == channelNum)) { SoundDriver *driver = vtStruct->_entries[entryIndex]._driver; assert(driver); driver->setPitch(vtStruct->_entries[entryIndex]._voiceNum, pitchBlend); } } } void Sound::soDoTrackCommand(int channelNum, int command, int value) { switch (command) { case 1: _chModulation[channelNum] = value; break; case 7: _chVolume[channelNum] = value; break; case 10: _chPan[channelNum] = value; break; case 64: _chDamper[channelNum] = value; break; case 75: _chNumVoices[channelNum] = value; break; } } bool Sound::soDoUpdateTracks(int command, int value) { if ((command == 76) || (_hold != value)) return false; for (int trackIndex = 0; trackIndex < _trackInfo._numTracks; ++trackIndex) { _trkState[trackIndex] = _trkLoopState[trackIndex]; _trkRest[trackIndex] = _trkLoopRest[trackIndex]; _trkIndex[trackIndex] = _trkLoopIndex[trackIndex]; } _timer = _loopTimer; return true; } void Sound::soSetTrackPos(int trackIndex, int trackPos, int cueValue) { _trkIndex[trackIndex] = trackPos; if (cueValue == 127) { if (!_soundManager->_soTimeIndexFlag) _cueValue = cueValue; } else { for (int idx = 0; idx < _trackInfo._numTracks; ++idx) { _trkLoopState[idx] = _trkState[idx]; _trkLoopRest[idx] = _trkRest[idx]; _trkLoopIndex[idx] = _trkIndex[idx]; } _loopTimer = _timer; } } void Sound::soServiceTrackType1(int trackIndex, const byte *channelData) { if (_soundManager->_soTimeIndexFlag || !_trkState[trackIndex]) return; int channel = _trkChannel[trackIndex]; if (channel == -1) _trkState[trackIndex] = 0; else { int voiceType = _chVoiceType[channel]; VoiceTypeStruct *vtStruct = _soundManager->_voiceTypeStructPtrs[voiceType]; if (!vtStruct) _trkState[trackIndex] = 0; else { if (vtStruct->_voiceType != VOICETYPE_0) { if (_trkState[trackIndex] == 1) { int entryIndex = soFindSound(vtStruct, *(channelData + 1)); if (entryIndex != -1) { SoundDriver *driver = vtStruct->_entries[entryIndex]._driver; assert(driver); vtStruct->_entries[entryIndex]._type1._field6 = 0; vtStruct->_entries[entryIndex]._type1._field4 = *(channelData + 1); vtStruct->_entries[entryIndex]._type1._field5 = 0; int v1, v2; driver->playSound(channelData, 14, -1, vtStruct->_entries[entryIndex]._voiceNum, *(channelData + 1), 0x7f); driver->proc42(vtStruct->_entries[entryIndex]._voiceNum, *(channelData + 1), _loop ? 1 : 0, &v1, &v2); _trkState[trackIndex] = 2; } } else { for (uint entryIndex = 0; entryIndex < vtStruct->_entries.size(); ++entryIndex) { VoiceStructEntry &vte = vtStruct->_entries[entryIndex]; VoiceStructEntryType1 &vse = vte._type1; if ((vse._sound == this) && (vse._channelNum == channel) && (vse._field4 == *(channelData + 1))) { SoundDriver *driver = vte._driver; int isEnded, resetTimer; driver->proc42(vte._voiceNum, vtStruct->_total, _loop ? 1 : 0, &isEnded, &resetTimer); if (isEnded) { _trkState[trackIndex] = 0; } else if (resetTimer) { _timer = 0; } return; } } _trkState[trackIndex] = 0; } } else { _trkState[trackIndex] = 0; } } } } int Sound::soFindSound(VoiceTypeStruct *vtStruct, int channelNum) { int entryIndex = -1, entry2Index = -1; int v6 = 0, v8 = 0; for (uint idx = 0; idx < vtStruct->_entries.size(); ++idx) { VoiceStructEntryType1 &vte = vtStruct->_entries[idx]._type1; if ((vte._channelNum == channelNum) && (vte._sound == this)) { int v = vte._field6; if (vte._field4 != -1) { if (v8 <= v) { v8 = v; entry2Index = idx; } } else { if (v6 <= v) { v6 = v; entryIndex = idx; } } } } if (entryIndex != -1) return entryIndex; else if ((entryIndex == -1) && (entry2Index == -1)) return -1; else { SoundDriver *driver = vtStruct->_entries[entry2Index]._driver; assert(driver); driver->updateVoice(vtStruct->_entries[entry2Index]._voiceNum); return entry2Index; } } /*--------------------------------------------------------------------------*/ ASound::ASound(): EventHandler() { _action = NULL; _cueValue = -1; if (g_globals) g_globals->_sounds.push_back(this); } ASound::~ASound() { if (g_globals) g_globals->_sounds.remove(this); } void ASound::synchronize(Serializer &s) { EventHandler::synchronize(s); SYNC_POINTER(_action); s.syncAsByte(_cueValue); } void ASound::dispatch() { EventHandler::dispatch(); int cueValue = _sound.getCueValue(); if (cueValue != -1) { _cueValue = cueValue; _sound.setCueValue(-1); if (_action) _action->signal(); } if (_cueValue != -1) { if (!_sound.isPrimed()) { _cueValue = -1; if (_action) { _action->signal(); _action = NULL; } } } } void ASound::play(int soundNum, EventHandler *action, int volume) { _action = action; _cueValue = 0; setVol(volume); _sound.play(soundNum); } void ASound::stop() { _sound.stop(); _action = NULL; } void ASound::prime(int soundResID, Action *action) { _action = action; _cueValue = 0; _sound.prime(soundResID); } void ASound::unPrime() { _sound.unPrime(); _action = NULL; } void ASound::fade(int fadeDest, int fadeSteps, int fadeTicks, bool stopAfterFadeFlag, EventHandler *action) { if (action) _action = action; _sound.fade(fadeDest, fadeSteps, fadeTicks, stopAfterFadeFlag); } void ASound::fadeSound(int soundNum) { play(soundNum, NULL, 0); fade(127, 5, 1, false, NULL); } /*--------------------------------------------------------------------------*/ ASoundExt::ASoundExt(): ASound() { _soundNum = 0; } void ASoundExt::synchronize(Serializer &s) { ASound::synchronize(s); s.syncAsSint16LE(_soundNum); } void ASoundExt::signal() { if (_soundNum != 0) { fadeSound(_soundNum); } } void ASoundExt::fadeOut2(EventHandler *action) { fade(0, 10, 10, true, action); } void ASoundExt::changeSound(int soundNum) { if (isPlaying()) { _soundNum = soundNum; fadeOut2(this); } else { fadeSound(soundNum); } } /*--------------------------------------------------------------------------*/ void PlayStream::ResFileData::load(Common::SeekableReadStream &stream) { // Validate that it's the correct data file char header[4]; stream.read(&header[0], 4); if (strncmp(header, "SPAM", 4)) error("Invalid voice resource data"); _fileChunkSize = stream.readUint32LE(); stream.skip(2); _indexSize = stream.readUint16LE(); _chunkSize = stream.readUint16LE(); stream.skip(18); } PlayStream::PlayStream(): EventHandler() { _index = NULL; _endAction = NULL; } PlayStream::~PlayStream() { remove(); } bool PlayStream::setFile(const Common::String &filename) { remove(); // Open the resource file for access if (!_file.open(filename)) return false; // Load header _resData.load(_file); // Load the index _index = new uint16[_resData._indexSize / 2]; for (uint i = 0; i < (_resData._indexSize / 2); ++i) _index[i] = _file.readUint16LE(); return true; } bool PlayStream::play(int voiceNum, EventHandler *endAction) { uint32 offset = getFileOffset(_index, _resData._fileChunkSize, voiceNum); if (offset) { _voiceNum = 0; if (_sound.isPlaying()) _sound.stop(); // Move to the offset for the start of the voice _file.seek(offset); // Read in the header and validate it char header[4]; _file.read(&header[0], 4); if (strncmp(header, "FEED", 4)) error("Invalid stream data"); // Get basic details of first sound chunk uint chunkSize = _file.readUint16LE() - 16; _file.skip(4); int rate = _file.readUint16LE(); _file.skip(4); // Create the stream Audio::QueuingAudioStream *audioStream = Audio::makeQueuingAudioStream(rate, false); // Load in the first chunk byte *data = (byte *)malloc(chunkSize); _file.read(data, chunkSize); audioStream->queueBuffer(data, chunkSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); // If necessary, load further chunks of the voice in while (chunkSize == (_resData._chunkSize - 16)) { // Ensure the next chunk has the 'MORE' header _file.read(&header[0], 4); if (strncmp(header, "MORE", 4)) error("Invalid stream data"); // Get the size of the chunk chunkSize = _file.readUint16LE() - 16; _file.skip(10); // Read in the data for this next chunk and queue it data = (byte *)malloc(chunkSize); _file.read(data, chunkSize); audioStream->queueBuffer(data, chunkSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); } g_vm->_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, audioStream, DisposeAfterUse::YES); return true; } // If it reaches this point, no valid voice data found return false; } void PlayStream::stop() { g_vm->_mixer->stopHandle(_soundHandle); _voiceNum = 0; _endAction = NULL; } bool PlayStream::isPlaying() const { return _voiceNum != 0 && g_vm->_mixer->isSoundHandleActive(_soundHandle); } void PlayStream::remove() { stop(); _file.close(); // Free index delete[] _index; _index = NULL; _endAction = NULL; _voiceNum = 0; } void PlayStream::dispatch() { if (_voiceNum != 0 && !isPlaying()) { // If voice has finished playing, reset fields EventHandler *endAction = _endAction; _endAction = NULL; _voiceNum = 0; if (endAction) // Signal given end action endAction->signal(); } } uint32 PlayStream::getFileOffset(const uint16 *data, int count, int voiceNum) { assert(data); int bitsIndex = voiceNum & 7; int byteIndex = voiceNum >> 3; int shiftAmount = bitsIndex * 2; int bitMask = 3 << shiftAmount; int v = (data[byteIndex] & bitMask) >> shiftAmount; uint32 offset = 0; if (!v) return 0; // Loop to figure out offsets from index words skipped over for (int i = 0; i < (voiceNum >> 3); ++i) { for (int bit = 0; bit < 16; bit += 2) offset += ((data[i] >> bit) & 3) * count; } // Bit index loop for (int i = 0; i < bitsIndex; ++i) offset += ((data[byteIndex] >> (i * 2)) & 3) * count; return offset; } /*--------------------------------------------------------------------------*/ SoundDriver::SoundDriver() { _driverResID = 0; _minVersion = _maxVersion = 0; _groupMask = 0; _groupOffset = NULL; } /*--------------------------------------------------------------------------*/ const byte adlib_group_data[] = { 1, 1, 9, 1, 0xff }; const byte v440B0[9] = { 0, 1, 2, 6, 7, 8, 12, 13, 14 }; const byte v440B9[9] = { 3, 4, 5, 9, 10, 11, 15, 16, 17 }; const byte v440C2[18] = { 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21 }; const byte v44134[64] = { 0, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 47, 48, 49, 50, 50, 51, 52, 52, 53, 54, 54, 55, 56, 56, 57, 57, 58, 58, 59, 59, 59, 60, 60, 60, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63 }; const int v440D4[48] = { 343, 348, 353, 358, 363, 369, 374, 379, 385, 391, 396, 402, 408, 414, 420, 426, 432, 438, 445, 451, 458, 465, 471, 478, 485, 492, 499, 507, 514, 521, 529, 537, 544, 552, 560, 569, 577, 585, 594, 602, 611, 620, 629, 638, 647, 657, 666, 676 }; AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() { _minVersion = 0x102; _maxVersion = 0x10A; _masterVolume = 0; _groupData._groupMask = 9; _groupData._v1 = 0x46; _groupData._v2 = 0; _groupData._pData = &adlib_group_data[0]; _mixer = g_vm->_mixer; _sampleRate = _mixer->getOutputRate(); _opl = OPL::Config::create(); assert(_opl); _opl->init(_sampleRate); _samplesTillCallback = 0; _samplesTillCallbackRemainder = 0; _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND; _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND; _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); Common::fill(_channelVoiced, _channelVoiced + ADLIB_CHANNEL_COUNT, false); memset(_channelVolume, 0, ADLIB_CHANNEL_COUNT * sizeof(int)); memset(_v4405E, 0, ADLIB_CHANNEL_COUNT * sizeof(int)); memset(_v44067, 0, ADLIB_CHANNEL_COUNT * sizeof(int)); memset(_v44070, 0, ADLIB_CHANNEL_COUNT * sizeof(int)); memset(_v44079, 0, ADLIB_CHANNEL_COUNT * sizeof(int)); memset(_v44082, 0, ADLIB_CHANNEL_COUNT * sizeof(int)); _v44082[ADLIB_CHANNEL_COUNT] = 0x90; Common::fill(_pitchBlend, _pitchBlend + ADLIB_CHANNEL_COUNT, 0x2000); memset(_v4409E, 0, ADLIB_CHANNEL_COUNT * sizeof(int)); _patchData = NULL; for (int i = 0; i < 256; i++) _portContents[i] = 0; for (int i = 0; i < 9; i++) { _channelVoiced[i] = false; _pitchBlend[i] = 0; } } AdlibSoundDriver::~AdlibSoundDriver() { DEALLOCATE(_patchData); _mixer->stopHandle(_soundHandle); delete _opl; } bool AdlibSoundDriver::open() { write(1, 0x20); if (!reset()) return false; write(8, 0); for (int idx = 0x20; idx < 0xF6; ++idx) write(idx, 0); write(0xBD, 0); return true; } void AdlibSoundDriver::close() { for (int idx = 0xB0; idx < 0xB8; ++idx) write(idx, _portContents[idx] & 0xDF); for (int idx = 0x40; idx < 0x55; ++idx) write(idx, 0x3F); reset(); } bool AdlibSoundDriver::reset() { write(1, 0x20); write(1, 0x20); return true; } const GroupData *AdlibSoundDriver::getGroupData() { return &_groupData; } void AdlibSoundDriver::installPatch(const byte *data, int size) { byte *patchData = ALLOCATE(size); Common::copy(data, data + size, patchData); _patchData = patchData; } int AdlibSoundDriver::setMasterVolume(int volume) { int oldVolume = _masterVolume; _masterVolume = volume; for (int channelNum = 0; channelNum < ADLIB_CHANNEL_COUNT; ++channelNum) updateChannelVolume(channelNum); return oldVolume; } void AdlibSoundDriver::playSound(const byte *channelData, int dataOffset, int program, int channel, int v0, int v1) { if (program == -1) return; int offset = READ_LE_UINT16(_patchData + program * 2); if (offset) { const byte *dataP = _patchData + offset; int id; for (offset = 2, id = 0; id != READ_LE_UINT16(dataP); offset += 30, ++id) { if ((dataP[offset] <= v0) && (dataP[offset + 1] >= v0)) { if (dataP[offset + 2] != 0xff) v0 = dataP[offset + 2]; _v4409E[channel] = dataP + offset - _patchData; // Set sustain/release int portNum = v440C2[v440B0[channel]] + 0x80; write(portNum, (_portContents[portNum] & 0xF0) | 0xF); portNum = v440C2[v440B9[channel]] + 0x80; write(portNum, (_portContents[portNum] & 0xF0) | 0xF); if (_channelVoiced[channel]) clearVoice(channel); _v44067[channel] = v0; _v4405E[channel] = v1; updateChannel(channel); setFrequency(channel); updateChannelVolume(channel); setVoice(channel); break; } } } } void AdlibSoundDriver::updateVoice(int channel) { if (_channelVoiced[channel]) clearVoice(channel); } void AdlibSoundDriver::proc38(int channel, int cmd, int value) { if (cmd == 7) { // Set channel volume _channelVolume[channel] = value; updateChannelVolume(channel); } } void AdlibSoundDriver::setPitch(int channel, int pitchBlend) { _pitchBlend[channel] = pitchBlend; setFrequency(channel); } void AdlibSoundDriver::write(byte reg, byte value) { _portContents[reg] = value; _queue.push(RegisterValue(reg, value)); } void AdlibSoundDriver::flush() { Common::StackLock slock(SoundManager::sfManager()._serverDisabledMutex); while (!_queue.empty()) { RegisterValue v = _queue.pop(); _opl->writeReg(v._regNum, v._value); } } void AdlibSoundDriver::updateChannelVolume(int channelNum) { int volume = (_masterVolume * _channelVolume[channelNum] / 127 * _v4405E[channelNum] / 127) / 2; int level2 = 63 - v44134[volume * _v44079[channelNum] / 63]; int level1 = !_v44082[channelNum] ? 63 - _v44070[channelNum] : 63 - v44134[volume * _v44070[channelNum] / 63]; int portNum = v440C2[v440B0[channelNum]] + 0x40; write(portNum, (_portContents[portNum] & 0x80) | level1); portNum = v440C2[v440B9[channelNum]] + 0x40; write(portNum, (_portContents[portNum] & 0x80) | level2); } void AdlibSoundDriver::setVoice(int channel) { int portNum = 0xB0 + channel; write(portNum, _portContents[portNum] | 0x20); _channelVoiced[channel] = true; } void AdlibSoundDriver::clearVoice(int channel) { write(0xB0 + channel, _portContents[0xB0 + channel] & ~0x20); _channelVoiced[channel] = false; } void AdlibSoundDriver::updateChannel(int channel) { const byte *dataP = _patchData + _v4409E[channel]; int portOffset = v440C2[v440B0[channel]]; int portNum = portOffset + 0x20; int portValue = 0; if (*(dataP + 4)) portValue |= 0x80; if (*(dataP + 5)) portValue |= 0x40; if (*(dataP + 8)) portValue |= 0x20; if (*(dataP + 6)) portValue |= 0x10; portValue |= *(dataP + 7); write(portNum, portValue); portValue = (_portContents[0x40 + portOffset] & 0x3F) | (*(dataP + 9) << 6); write(0x40 + portOffset, portValue); _v44070[channel] = 63 - *(dataP + 10); write(0x60 + portOffset, *(dataP + 12) | (*(dataP + 11) << 4)); write(0x80 + portOffset, *(dataP + 14) | (*(dataP + 13) << 4)); write(0xE0 + portOffset, (_portContents[0xE0 + portOffset] & 0xFC) | *(dataP + 15)); portOffset = v440C2[v440B9[channel]]; portNum = portOffset + 0x20; portValue = 0; if (*(dataP + 17)) portValue |= 0x80; if (*(dataP + 18)) portValue |= 0x40; if (*(dataP + 21)) portValue |= 0x20; if (*(dataP + 19)) portValue |= 0x10; portValue |= *(dataP + 20); write(portNum, portValue); write(0x40 + portOffset, (_portContents[0x40 + portOffset] & 0x3f) | (*(dataP + 22) << 6)); _v44079[channel] = 0x3F - *(dataP + 23); write(0x60 + portOffset, *(dataP + 25) | (*(dataP + 24) << 4)); write(0x80 + portOffset, *(dataP + 27) | (*(dataP + 26) << 4)); write(0xE0 + portOffset, (_portContents[0xE0 + portOffset] & 0xFC) | *(dataP + 28)); write(0xC0 + channel, (_portContents[0xC0 + channel] & 0xF0) | (*(dataP + 16) << 1) | *(dataP + 3)); _v44082[channel] = *(dataP + 3); } void AdlibSoundDriver::setFrequency(int channel) { int offset, ch; int v = _pitchBlend[channel]; if (v == 0x2000) { offset = 0; ch = _v44067[channel]; } else if (v > 0x2000) { ch = _v44067[channel]; v -= 0x2000; if (v == 0x1fff) v = 0x2000; offset = (v / 170) & 3; ch += (v / 170) >> 2; if (ch >= 128) ch = 127; } else { ch = _v44067[channel]; int tempVal = (0x2000 - v) / 170; int tempVal2 = 4 - (tempVal & 3); if (tempVal2 == 4) offset = 0; else { offset = tempVal2; --ch; } ch -= tempVal >> 2; if (ch < 0) ch = 0; } int var2 = ch / 12; if (var2) --var2; int dataWord = v440D4[((ch % 12) << 2) + offset]; write(0xA0 + channel, dataWord & 0xff); write(0xB0 + channel, (_portContents[0xB0 + channel] & 0xE0) | ((dataWord >> 8) & 3) | (var2 << 2)); } int AdlibSoundDriver::readBuffer(int16 *buffer, const int numSamples) { Common::StackLock slock1(SoundManager::sfManager()._serverDisabledMutex); Common::StackLock slock2(SoundManager::sfManager()._serverSuspendedMutex); int32 samplesLeft = numSamples; memset(buffer, 0, sizeof(int16) * numSamples); while (samplesLeft) { if (!_samplesTillCallback) { SoundManager::sfUpdateCallback(NULL); flush(); _samplesTillCallback = _samplesPerCallback; _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) { _samplesTillCallback++; _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND; } } int32 render = MIN(samplesLeft, _samplesTillCallback); samplesLeft -= render; _samplesTillCallback -= render; _opl->readBuffer(buffer, render); buffer += render; } return numSamples; } /*--------------------------------------------------------------------------*/ SoundBlasterDriver::SoundBlasterDriver(): SoundDriver() { _minVersion = 0x102; _maxVersion = 0x10A; _masterVolume = 0; _groupData._groupMask = 1; _groupData._v1 = 0x3E; _groupData._v2 = 0; static byte const group_data[] = { 3, 1, 1, 0, 0xff }; _groupData._pData = group_data; _mixer = g_vm->_mixer; _sampleRate = _mixer->getOutputRate(); _audioStream = NULL; _channelData = NULL; _channelVolume = 0; } SoundBlasterDriver::~SoundBlasterDriver() { _mixer->stopHandle(_soundHandle); } bool SoundBlasterDriver::open() { return true; } void SoundBlasterDriver::close() { } bool SoundBlasterDriver::reset() { return true; } const GroupData *SoundBlasterDriver::getGroupData() { return &_groupData; } int SoundBlasterDriver::setMasterVolume(int volume) { int oldVolume = _masterVolume; _masterVolume = volume; return oldVolume; } void SoundBlasterDriver::playSound(const byte *channelData, int dataOffset, int program, int channel, int v0, int v1) { if (program != -1) return; assert(channel == 0); // If sound data has been previously set, then release it if (_channelData) updateVoice(channel); // Set the new channel data _channelData = channelData + dataOffset; // Make a copy of the buffer int dataSize = g_vm->_memoryManager.getSize(channelData); byte *soundData = (byte *)malloc(dataSize - dataOffset); Common::copy(_channelData, _channelData + (dataSize - dataOffset), soundData); _audioStream = Audio::makeQueuingAudioStream(11025, false); _audioStream->queueBuffer(soundData, dataSize - dataOffset, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); // Start the new sound if (!_mixer->isSoundHandleActive(_soundHandle)) _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _audioStream); } void SoundBlasterDriver::updateVoice(int channel) { // Stop the playing voice if (_mixer->isSoundHandleActive(_soundHandle)) _mixer->stopHandle(_soundHandle); _audioStream = NULL; _channelData = NULL; } void SoundBlasterDriver::proc38(int channel, int cmd, int value) { if (cmd == 7) { // Set channel volume _channelVolume = value; _mixer->setChannelVolume(_soundHandle, (byte)MIN(255, value * 2)); } } void SoundBlasterDriver::proc42(int channel, int cmd, int value, int *v1, int *v2) { // TODO: v2 is used for flagging a reset of the timer. I'm not sure if it's needed *v1 = 0; *v2 = 0; // Note: Checking whether a playing Fx sound had finished was originally done in another // method in the sample playing code. But since we're using the ScummVM audio soundsystem, // it's easier simply to do the check right here if (_audioStream && (_audioStream->numQueuedStreams() == 0)) { updateVoice(channel); } if (!_channelData) // Flag that sound isn't playing *v1 = 1; } } // End of namespace TsAGE