mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 21:59:17 +00:00
3123 lines
80 KiB
C++
3123 lines
80 KiB
C++
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "audio/fmopl.h"
|
|
#include "audio/decoders/raw.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/timer.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<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(&sfSoundServer);
|
|
}
|
|
|
|
// 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);
|
|
|
|
g_system->getTimerManager()->installTimerProc(&sfSoundServer, 1000000 / CALLBACKS_PER_SECOND, NULL, "tsageSoundUpdate");
|
|
_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 subtitles = ConfMan.hasKey("subtitles") ? ConfMan.getBool("subtitles") : true;
|
|
|
|
bool music_mute = mute;
|
|
bool voice_mute = mute;
|
|
|
|
if (!mute) {
|
|
music_mute = ConfMan.getBool("music_mute");
|
|
voice_mute = ConfMan.getBool("speech_mute");
|
|
}
|
|
|
|
// Get the new music volume
|
|
int musicVolume = music_mute ? 0 : MIN(255, ConfMan.getInt("music_volume"));
|
|
|
|
this->setMasterVol(musicVolume / 2);
|
|
|
|
// Return to Ringworld voice settings
|
|
if (g_vm->getGameID() == GType_Ringworld2) {
|
|
// If we don't have voice, then ensure that text is turned on
|
|
if (voice_mute)
|
|
subtitles = true;
|
|
|
|
R2_GLOBALS._speechSubtitles =
|
|
(voice_mute ? 0 : SPEECH_VOICE) |
|
|
(!subtitles ? 0 : SPEECH_TEXT);
|
|
}
|
|
}
|
|
|
|
Common::List<SoundDriverEntry> &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<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 = 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<Sound *>::iterator i = _playList.begin(); i != _playList.end(); ++i)
|
|
(*i)->mute(false);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
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<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() {
|
|
sfRethinkVoiceTypes();
|
|
}
|
|
|
|
void SoundManager::sfSoundServer(void *) {
|
|
Common::StackLock slock1(SoundManager::sfManager()._serverDisabledMutex);
|
|
Common::StackLock slock2(SoundManager::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) {
|
|
VoiceStructEntryType1 &vte = vtStruct->_entries[idx]._type1;
|
|
if (vte._field6 >= -1)
|
|
++vte._field6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SoundManager::isFading() {
|
|
Common::StackLock slock(sfManager()._serverSuspendedMutex);
|
|
|
|
// Loop through any active sounds to see if any are being actively faded
|
|
Common::List<Sound *>::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) {
|
|
VoiceStructEntryType0 &vte = vs->_entries[idx]._type0;
|
|
|
|
vte._sound = vte._sound2;
|
|
vte._channelNum = vte._channelNum2;
|
|
vte._priority = vte._priority2;
|
|
vte._fieldA = vte._field12;
|
|
}
|
|
} else {
|
|
vs->_field3 = vs->_numVoices;
|
|
|
|
for (uint idx = 0; idx < vs->_entries.size(); ++idx) {
|
|
VoiceStructEntryType1 &vte = vs->_entries[idx]._type1;
|
|
|
|
vte._sound = vte._sound2;
|
|
vte._channelNum = vte._channelNum2;
|
|
vte._priority = vte._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 &vte = vtStruct->_entries[idx]._type0;
|
|
vte._sound2 = vte._sound;
|
|
vte._channelNum2 = vte._channelNum;
|
|
vte._priority2 = vte._priority;
|
|
vte._field12 = vte._fieldA;
|
|
} else {
|
|
VoiceStructEntryType1 &vte = vtStruct->_entries[idx]._type1;
|
|
vte._sound2 = vte._sound;
|
|
vte._channelNum2 = vte._channelNum;
|
|
vte._priority2 = vte._priority;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
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<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");
|
|
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 = false;
|
|
|
|
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) {
|
|
VoiceStructEntryType0 &vte = vs->_entries[idx]._type0;
|
|
vte._sound3 = vte._sound;
|
|
vte._channelNum3 = vte._channelNum;
|
|
vte._priority3 = vte._priority;
|
|
vte._sound = NULL;
|
|
vte._channelNum = 0;
|
|
vte._priority = 0;
|
|
vte._fieldA = false;
|
|
vte._sound2 = NULL;
|
|
vte._channelNum2 = 0;
|
|
vte._priority2 = 0;
|
|
vte._field12 = false;
|
|
}
|
|
} else {
|
|
for (uint idx = 0; idx < vs->_entries.size(); ++idx) {
|
|
VoiceStructEntryType1 &vte = vs->_entries[idx]._type1;
|
|
vte._sound3 = vte._sound;
|
|
vte._channelNum3 = vte._channelNum;
|
|
vte._priority3 = vte._priority;
|
|
vte._sound = NULL;
|
|
vte._channelNum = 0;
|
|
vte._priority = 0;
|
|
vte._sound2 = NULL;
|
|
vte._channelNum2 = 0;
|
|
vte._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::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) {
|
|
VoiceStructEntryType1 &vte = vtStruct->_entries[idx]._type1;
|
|
if (!vte._sound2) {
|
|
vte._sound2 = sound;
|
|
vte._channelNum2 = foundIndex;
|
|
vte._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) {
|
|
VoiceStructEntryType1 &vte = vtStruct->_entries[idx]._type1;
|
|
if (vte._priority2 == maxPriority) {
|
|
vte._sound2 = NULL;
|
|
vte._channelNum2 = 0;
|
|
vte._priority2 = 0;
|
|
++numVoices;
|
|
}
|
|
}
|
|
} while (chNumVoices > numVoices);
|
|
|
|
int voicesCtr = chNumVoices;
|
|
for (uint idx = 0; (idx < vtStruct->_entries.size()) && (voicesCtr > 0); ++idx) {
|
|
VoiceStructEntryType1 &vte = vtStruct->_entries[idx]._type1;
|
|
if (!vte._sound2) {
|
|
vte._sound2 = sound;
|
|
vte._channelNum2 = foundIndex;
|
|
vte._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) {
|
|
VoiceStructEntryType0 &vte = vtStruct->_entries[entryIndex]._type0;
|
|
vte._sound2 = sound;
|
|
vte._channelNum2 = foundIndex;
|
|
vte._priority2 = foundPriority;
|
|
vte._field12 = false;
|
|
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) {
|
|
VoiceStructEntryType0 &vte = vtStruct->_entries[entryIndex]._type0;
|
|
vte._sound2 = sound;
|
|
vte._channelNum2 = foundIndex;
|
|
vte._priority2 = foundPriority;
|
|
vte._field12 = false;
|
|
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) {
|
|
foundMatch = true;
|
|
if (!vtStruct->_entries[idx]._type0._sound2) {
|
|
entryIndex = idx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (entryIndex != -1) {
|
|
VoiceStructEntryType0 &vte = vtStruct->_entries[entryIndex]._type0;
|
|
vte._sound2 = sound;
|
|
vte._channelNum2 = foundIndex;
|
|
vte._priority2 = foundPriority;
|
|
vte._field12 = false;
|
|
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 = idx;
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
if (foundPriority)
|
|
continue;
|
|
|
|
if (entryIndex != -1) {
|
|
VoiceStructEntryType0 &vte = vtStruct->_entries[entryIndex]._type0;
|
|
vte._sound2 = sound;
|
|
vte._channelNum2 = foundIndex;
|
|
vte._priority2 = foundPriority;
|
|
vte._field12 = true;
|
|
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) {
|
|
VoiceStructEntryType0 &vte = vtStruct->_entries[idx]._type0;
|
|
if (!vte._sound2) {
|
|
if (vtStruct->_entries[idx]._voiceNum > maxVoiceNum) {
|
|
maxVoiceNum = vtStruct->_entries[idx]._voiceNum;
|
|
voiceIndex = idx;
|
|
}
|
|
} else {
|
|
if (vte._priority2 > maxPriority) {
|
|
maxPriority = vte._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 = true;
|
|
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 = true;
|
|
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 = true;
|
|
vse._sound2 = NULL;
|
|
|
|
if ((total) && vse._sound) {
|
|
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 = false;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
VoiceStructEntryType0 &vteCur = vs->_entries[entryIndex]._type0;
|
|
if ((vteCur._sound3 != sound) || (vteCur._channelNum3 != channelNum)) {
|
|
// Found match
|
|
vteCur._sound = sound;
|
|
vteCur._channelNum = channelNum;
|
|
vteCur._priority = vse._priority2;
|
|
vteCur._fieldA = false;
|
|
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 = false;
|
|
|
|
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) {
|
|
VoiceStructEntryType1 &vte = vs->_entries[idx]._type1;
|
|
vte._sound = NULL;
|
|
vte._channelNum = 0;
|
|
vte._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 = 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<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) {
|
|
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() {
|
|
if (g_globals) {
|
|
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) {
|
|
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 == 0xff ? -1 : _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<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::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);
|
|
|
|
VoiceStructEntryType1 &vte = vtStruct->_entries[entryIndex]._type1;
|
|
vte._field6 = 0;
|
|
vte._field4 = v0;
|
|
vte._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];
|
|
|
|
VoiceStructEntryType1 &vte = vtStruct->_entries[entryIndex]._type1;
|
|
vte._field6 = 0;
|
|
vte._field4 = v0;
|
|
vte._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;
|
|
default:
|
|
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);
|
|
|
|
VoiceStructEntryType1 &vte = vtStruct->_entries[entryIndex]._type1;
|
|
vte._field6 = 0;
|
|
vte._field4 = *(channelData + 1);
|
|
vte._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() {
|
|
_endAction = 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 (_endAction)
|
|
_endAction->signal();
|
|
}
|
|
|
|
if (_cueValue != -1) {
|
|
if (!_sound.isPrimed()) {
|
|
_cueValue = -1;
|
|
if (_endAction) {
|
|
_endAction->signal();
|
|
_endAction = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ASound::play(int soundNum, EventHandler *endAction, int volume) {
|
|
_endAction = endAction;
|
|
_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 *endAction) {
|
|
if (endAction)
|
|
_endAction = endAction;
|
|
|
|
_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 *endAction) {
|
|
fade(0, 10, 10, true, endAction);
|
|
}
|
|
|
|
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;
|
|
_audioStream = NULL;
|
|
|
|
_resData._fileChunkSize = 0;
|
|
_resData._indexSize = 0;
|
|
_resData._chunkSize = 0;
|
|
_voiceNum = 0;
|
|
}
|
|
|
|
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) {
|
|
stop();
|
|
_voiceNum = 0;
|
|
|
|
// 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
|
|
_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, "FEED", 4))
|
|
// Reached start of next voice sample, so stop
|
|
break;
|
|
if (strncmp(header, "MORE", 4))
|
|
// Not more remaining, so break
|
|
break;
|
|
|
|
// 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);
|
|
_voiceNum = voiceNum;
|
|
_endAction = endAction;
|
|
return true;
|
|
}
|
|
|
|
// If it reaches this point, no valid voice data found
|
|
return false;
|
|
}
|
|
|
|
void PlayStream::stop() {
|
|
if (_audioStream) {
|
|
g_vm->_mixer->stopHandle(_soundHandle);
|
|
}
|
|
|
|
_audioStream = NULL;
|
|
_voiceNum = 0;
|
|
_endAction = NULL;
|
|
}
|
|
|
|
bool PlayStream::isPlaying() const {
|
|
return _audioStream != NULL && !_audioStream->endOfData();
|
|
}
|
|
|
|
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) {
|
|
if (!data)
|
|
return 0; // no valid voice data found
|
|
|
|
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 adlib_operator1_offset[] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 };
|
|
const byte adlib_operator2_offset[] = { 3, 4, 5, 11, 12, 13, 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._pData = &adlib_group_data[0];
|
|
|
|
_mixer = g_vm->_mixer;
|
|
_opl = OPL::Config::create();
|
|
assert(_opl);
|
|
_opl->init();
|
|
|
|
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;
|
|
}
|
|
|
|
_opl->start(new Common::Functor0Mem<void, AdlibSoundDriver>(this, &AdlibSoundDriver::onTimer), CALLBACKS_PER_SECOND);
|
|
}
|
|
|
|
AdlibSoundDriver::~AdlibSoundDriver() {
|
|
DEALLOCATE(_patchData);
|
|
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 = adlib_operator1_offset[channel] + 0x80;
|
|
write(portNum, (_portContents[portNum] & 0xF0) | 0xF);
|
|
|
|
portNum = adlib_operator2_offset[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) {
|
|
Common::StackLock lock(_queueMutex);
|
|
_portContents[reg] = value;
|
|
_queue.push(RegisterValue(reg, value));
|
|
}
|
|
|
|
void AdlibSoundDriver::flush() {
|
|
Common::StackLock lock(_queueMutex);
|
|
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 = adlib_operator1_offset[channelNum] + 0x40;
|
|
write(portNum, (_portContents[portNum] & 0x80) | level1);
|
|
|
|
portNum = adlib_operator2_offset[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 = adlib_operator1_offset[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 = adlib_operator2_offset[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));
|
|
}
|
|
|
|
void AdlibSoundDriver::onTimer() {
|
|
flush();
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
|
|
SoundBlasterDriver::SoundBlasterDriver(): SoundDriver() {
|
|
_minVersion = 0x102;
|
|
_maxVersion = 0x10A;
|
|
_masterVolume = 0;
|
|
|
|
_groupData._groupMask = 1;
|
|
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 + 18;
|
|
|
|
// Make a copy of the buffer
|
|
int dataSize = g_vm->_memoryManager.getSize(channelData);
|
|
dataSize -= 18;
|
|
|
|
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
|