aboutsummaryrefslogtreecommitdiff
path: root/engines/tsage/sound.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/tsage/sound.cpp')
-rw-r--r--engines/tsage/sound.cpp2987
1 files changed, 2979 insertions, 8 deletions
diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp
index defec1cebd..01e770a77a 100644
--- a/engines/tsage/sound.cpp
+++ b/engines/tsage/sound.cpp
@@ -20,38 +20,3009 @@
*
*/
+#include "common/config-manager.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);
+
+ for (Common::List<Sound *>::iterator i = _soundList.begin(); i != _soundList.end(); ) {
+ Sound *s = *i;
+ ++i;
+ s->stop();
+ }
+ for (Common::List<SoundDriver *>::iterator i = _installedDrivers.begin(); i != _installedDrivers.end(); ) {
+ SoundDriver *driver = *i;
+ ++i;
+ delete driver;
+ }
+ _sfTerminate();
+
+ g_system->getTimerManager()->removeTimerProc(_sfUpdateCallback);
+ }
+
+ _soundManager = NULL;
+}
+
void SoundManager::postInit() {
- _saver->addSaveNotifier(&SoundManager::saveNotifier);
- _saver->addLoadNotifier(&SoundManager::loadNotifier);
- _saver->addListener(this);
+ if (!__sndmgrReady) {
+ _saver->addSaveNotifier(&SoundManager::saveNotifier);
+ _saver->addLoadNotifier(&SoundManager::loadNotifier);
+ _saver->addListener(this);
+
+ // Install a timer for handling sound manager updates at 60Hz
+ g_system->getTimerManager()->installTimerProc(_sfUpdateCallback, 1000000 / GAME_FRAME_RATE, NULL);
+
+ __sndmgrReady = true;
+ }
+}
+
+/**
+ * Loops through all the loaded sounds, and stops any that have been flagged for stopping
+ */
+void SoundManager::dispatch() {
+ Common::List<Sound *>::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<SoundDriverEntry> &SoundManager::buildDriverList(bool detectFlag) {
+ assert(__sndmgrReady);
+ _availableDrivers.clear();
+
+ // Build up a list of available drivers. Currently we only implement an 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);
+
+ _driversDetected = true;
+ return _availableDrivers;
+}
+
+void SoundManager::installConfigDrivers() {
+ installDriver(ADLIB_DRIVER_NUM);
+}
+
+Common::List<SoundDriverEntry> &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<Sound *>::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 = _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<Sound *>::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) {
+ assert(driverNum == ADLIB_DRIVER_NUM);
+ return new AdlibSoundDriver();
+}
+
+/**
+ * Uninstall the specified driver
+ */
+void SoundManager::unInstallDriver(int driverNum) {
+ Common::List<SoundDriver *>::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<Sound *>::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<SoundDriver *>::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() {
+ Common::StackLock slock1(sfManager()._serverDisabledMutex);
+ Common::StackLock slock2(sfManager()._serverSuspendedMutex);
+
+ 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<Sound *>::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<SoundDriver *>::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<Sound *>::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;
+ }
+ }
+ }
+}
+
+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) {
- _globals->_soundManager.saveNotifierProc(postFlag);
+ _soundManager->saveNotifierProc(postFlag);
}
void SoundManager::saveNotifierProc(bool postFlag) {
- warning("TODO: SoundManager::saveNotifierProc");
+ // Nothing needs to be done when saving the game
}
void SoundManager::loadNotifier(bool postFlag) {
- _globals->_soundManager.loadNotifierProc(postFlag);
+ _soundManager->loadNotifierProc(postFlag);
}
void SoundManager::loadNotifierProc(bool postFlag) {
- warning("TODO: SoundManager::loadNotifierProc");
+ if (!postFlag) {
+ // Stop any currently playing sounds
+ if (__sndmgrReady) {
+ Common::StackLock slock(_serverDisabledMutex);
+
+ for (Common::List<Sound *>::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<Sound *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) {
+ Sound *s = *i;
+ s->orientAfterRestore();
+ }
+ }
}
void SoundManager::listenerSynchronize(Serializer &s) {
s.validate("SoundManager");
- warning("TODO: SoundManager listenerSynchronize");
+ 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<SoundDriver *>::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<SoundDriver *>::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<Sound *>::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::set_to(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<SoundDriver *>::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<SoundDriver *>::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 = READ_LE_UINT32(driver->_groupOffset);
+
+ _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, _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<Sound *>::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<Sound *>::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::set_to(_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));
+}
+
+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() {
+ _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 = _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] = _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(_globals->_soundManager._serverSuspendedMutex);
+
+ if (flag)
+ ++_pausedCount;
+ else if (_pausedCount > 0)
+ --_pausedCount;
+
+ _soundManager->rethinkVoiceTypes();
+}
+
+void Sound::mute(bool flag) {
+ Common::StackLock slock(_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(_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(_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)
+ _soProc42(vtStruct, channelNum, chVoiceType, v);
+ else
+ _soProc32(vtStruct, 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<byte>(_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::_soProc32(VoiceTypeStruct *vtStruct, 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->proc32(vtStruct->_entries[entryIndex]._voiceNum, _chProgram[channelNum], v0, v1);
+ }
+}
+
+void Sound::_soProc42(VoiceTypeStruct *vtStruct, 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);
+
+ vtStruct->_entries[entryIndex]._type1._field6 = 0;
+ vtStruct->_entries[entryIndex]._type1._field4 = v0;
+ vtStruct->_entries[entryIndex]._type1._field5 = 0;
+
+ driver->proc32(vtStruct->_entries[entryIndex]._voiceNum, -1, v0, 0x7F);
+ driver->proc42(vtStruct->_entries[entryIndex]._voiceNum, voiceType, 0);
+ }
+ 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;
+
+ driver->proc32(vtStruct->_entries[entryIndex]._voiceNum, -1, *(channelData + 1), 0x7f);
+ }
+ } else {
+
+ }
+ }
+ }
+ }
+}
+
+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 (_globals)
+ _globals->_sounds.push_back(this);
+}
+
+ASound::~ASound() {
+ if (_globals)
+ _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, Action *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, Action *action) {
+ if (action)
+ _action = action;
+
+ _sound.fade(fadeDest, fadeSteps, fadeTicks, stopAfterFadeFlag);
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+SoundDriver::SoundDriver() {
+ _driverResID = 0;
+ _minVersion = _maxVersion = 0;
+ _groupMask = 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+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 = _vm->_mixer;
+ _sampleRate = _mixer->getOutputRate();
+ _opl = makeAdLibOPL(_sampleRate);
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+ Common::set_to(_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::set_to(_pitchBlend, _pitchBlend + ADLIB_CHANNEL_COUNT, 0x2000);
+ memset(_v4409E, 0, ADLIB_CHANNEL_COUNT * sizeof(int));
+ _patchData = NULL;
+}
+
+AdlibSoundDriver::~AdlibSoundDriver() {
+ DEALLOCATE(_patchData);
+ _mixer->stopHandle(_soundHandle);
+ OPLDestroy(_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::proc32(int channel, int program, 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();
+ OPLWriteReg(_opl, 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) {
+ update(buffer, numSamples);
+ return numSamples;
+}
+
+void AdlibSoundDriver::update(int16 *buf, int len) {
+ static int samplesLeft = 0;
+ while (len != 0) {
+ int count = samplesLeft;
+ if (count > len) {
+ count = len;
+ }
+ samplesLeft -= count;
+ len -= count;
+ YM3812UpdateOne(_opl, buf, count);
+ if (samplesLeft == 0) {
+ flush();
+ samplesLeft = _sampleRate / 50;
+ }
+ buf += count;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+const byte adlibFx_group_data[] = { 3, 1, 1, 0, 0xff };
+
+
+AdlibFxSoundDriver::AdlibFxSoundDriver(): SoundDriver() {
+ _minVersion = 0x102;
+ _maxVersion = 0x10A;
+ _masterVolume = 0;
+
+ _groupData.groupMask = 9;
+ _groupData.v1 = 0x3E;
+ _groupData.v2 = 0;
+ _groupData.pData = &adlib_group_data[0];
+
+ _mixer = _vm->_mixer;
+ _sampleRate = _mixer->getOutputRate();
+ _opl = makeAdLibOPL(_sampleRate);
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+/*
+ Common::set_to(_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::set_to(_pitchBlend, _pitchBlend + ADLIB_CHANNEL_COUNT, 0x2000);
+ memset(_v4409E, 0, ADLIB_CHANNEL_COUNT * sizeof(int));
+ _patchData = NULL;
+*/
+}
+
+AdlibFxSoundDriver::~AdlibFxSoundDriver() {
+ _mixer->stopHandle(_soundHandle);
+ OPLDestroy(_opl);
+}
+
+bool AdlibFxSoundDriver::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 AdlibFxSoundDriver::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 AdlibFxSoundDriver::reset() {
+ write(1, 0x20);
+ write(1, 0x20);
+
+ return true;
+}
+
+const GroupData *AdlibFxSoundDriver::getGroupData() {
+ return &_groupData;
+}
+
+int AdlibFxSoundDriver::setMasterVolume(int volume) {
+ int oldVolume = _masterVolume;
+ _masterVolume = volume;
+
+ for (int channelNum = 0; channelNum < ADLIB_CHANNEL_COUNT; ++channelNum)
+ updateChannelVolume(channelNum);
+
+ return oldVolume;
+}
+
+void AdlibFxSoundDriver::proc32(int channel, int program, 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 AdlibFxSoundDriver::updateVoice(int channel) {
+ if (_channelVoiced[channel])
+ clearVoice(channel);
+}
+
+void AdlibFxSoundDriver::proc38(int channel, int cmd, int value) {
+ if (cmd == 7) {
+ // Set channel volume
+ _channelVolume[channel] = value;
+ updateChannelVolume(channel);
+ }
+}
+
+void AdlibFxSoundDriver::setPitch(int channel, int pitchBlend) {
+ _pitchBlend[channel] = pitchBlend;
+ setFrequency(channel);
+}
+
+void AdlibFxSoundDriver::write(byte reg, byte value) {
+ _portContents[reg] = value;
+ _queue.push(RegisterValue(reg, value));
+}
+
+void AdlibFxSoundDriver::flush() {
+ Common::StackLock slock(SoundManager::sfManager()._serverDisabledMutex);
+
+ while (!_queue.empty()) {
+ RegisterValue v = _queue.pop();
+ OPLWriteReg(_opl, v._regNum, v._value);
+ }
+}
+
+void AdlibFxSoundDriver::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 AdlibFxSoundDriver::setVoice(int channel) {
+ int portNum = 0xB0 + channel;
+ write(portNum, _portContents[portNum] | 0x20);
+ _channelVoiced[channel] = true;
+}
+
+void AdlibFxSoundDriver::clearVoice(int channel) {
+ write(0xB0 + channel, _portContents[0xB0 + channel] & ~0x20);
+ _channelVoiced[channel] = false;
+}
+
+void AdlibFxSoundDriver::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 AdlibFxSoundDriver::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 AdlibFxSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
+ update(buffer, numSamples);
+ return numSamples;
+}
+
+void AdlibFxSoundDriver::update(int16 *buf, int len) {
+ static int samplesLeft = 0;
+ while (len != 0) {
+ int count = samplesLeft;
+ if (count > len) {
+ count = len;
+ }
+ samplesLeft -= count;
+ len -= count;
+ YM3812UpdateOne(_opl, buf, count);
+ if (samplesLeft == 0) {
+ flush();
+ samplesLeft = _sampleRate / 50;
+ }
+ buf += count;
+ }
}
} // End of namespace tSage