EMI: Refactor EMISound to get rid of the fixed number of channels.

This commit is contained in:
Joni Vähämäki 2014-08-06 17:57:04 +03:00
parent e08141392e
commit b5e4aa4826
2 changed files with 108 additions and 155 deletions

View File

@ -42,8 +42,6 @@
#include "engines/grim/emi/sound/vimatrack.h" #include "engines/grim/emi/sound/vimatrack.h"
#include "engines/grim/movie/codecs/vima.h" #include "engines/grim/movie/codecs/vima.h"
#define NUM_CHANNELS 32
namespace Grim { namespace Grim {
EMISound *g_emiSound = nullptr; EMISound *g_emiSound = nullptr;
@ -192,12 +190,8 @@ void EMISound::timerHandler(void *refCon) {
} }
EMISound::EMISound(int fps) { EMISound::EMISound(int fps) {
_channels = new SoundTrack*[NUM_CHANNELS];
for (int i = 0; i < NUM_CHANNELS; i++) {
_channels[i] = nullptr;
}
_curMusicState = -1; _curMusicState = -1;
_musicChannel = -1; _musicTrack = nullptr;
_curTrackId = 0; _curTrackId = 0;
_callbackFps = fps; _callbackFps = fps;
vimaInit(imuseDestTable); vimaInit(imuseDestTable);
@ -207,39 +201,28 @@ EMISound::EMISound(int fps) {
EMISound::~EMISound() { EMISound::~EMISound() {
g_system->getTimerManager()->removeTimerProc(timerHandler); g_system->getTimerManager()->removeTimerProc(timerHandler);
freeAllChannels(); freePlayingSounds();
freeLoadedSounds(); freeLoadedSounds();
delete[] _channels; delete _musicTrack;
if (g_grim->getGamePlatform() != Common::kPlatformPS2) { if (g_grim->getGamePlatform() != Common::kPlatformPS2) {
delete[] _musicTable; delete[] _musicTable;
} }
} }
int32 EMISound::getFreeChannel() { EMISound::TrackList::iterator EMISound::getPlayingTrackByName(const Common::String &name) {
for (int i = 0; i < NUM_CHANNELS; i++) { for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
if (_channels[i] == nullptr) if ((*it)->getSoundName() == name) {
return i; return it;
} }
return -1; }
return _playingTracks.end();
} }
int32 EMISound::getChannelByName(const Common::String &name) { void EMISound::freePlayingSounds() {
for (int i = 0; i < NUM_CHANNELS; i++) { for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
if (_channels[i] && _channels[i]->getSoundName() == name) delete (*it);
return i;
}
return -1;
}
void EMISound::freeChannel(int32 channel) {
delete _channels[channel];
_channels[channel] = nullptr;
}
void EMISound::freeAllChannels() {
for (int i = 0; i < NUM_CHANNELS; i++) {
freeChannel(i);
} }
_playingTracks.clear();
} }
void EMISound::freeLoadedSounds() { void EMISound::freeLoadedSounds() {
@ -259,70 +242,64 @@ bool EMISound::startSfx(const Common::String &soundName, int volume, int pan) {
bool EMISound::startSound(const Common::String &soundName, Audio::Mixer::SoundType soundType, int volume, int pan) { bool EMISound::startSound(const Common::String &soundName, Audio::Mixer::SoundType soundType, int volume, int pan) {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
int channel = getFreeChannel();
if (channel == -1) {
warning("Failed to start sound %s. Out of free sound channels", soundName.c_str());
return false;
}
SoundTrack *track = initTrack(soundName, soundType); SoundTrack *track = initTrack(soundName, soundType);
_channels[channel] = track;
if (track) { if (track) {
track->setBalance(pan); track->setBalance(pan);
track->setVolume(volume); track->setVolume(volume);
track->play(); track->play();
_playingTracks.push_back(track);
return true; return true;
} }
freeChannel(channel);
return false; return false;
} }
bool EMISound::getSoundStatus(const Common::String &soundName) { bool EMISound::getSoundStatus(const Common::String &soundName) {
int32 channel = getChannelByName(soundName); TrackList::iterator it = getPlayingTrackByName(soundName);
if (channel == -1) // We have no such sound. if (it == _playingTracks.end()) // We have no such sound.
return false; return false;
return _channels[channel]->isPlaying(); return (*it)->isPlaying();
} }
void EMISound::stopSound(const Common::String &soundName) { void EMISound::stopSound(const Common::String &soundName) {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
int32 channel = getChannelByName(soundName); TrackList::iterator it = getPlayingTrackByName(soundName);
if (channel == -1) { if (it == _playingTracks.end()) {
warning("Sound track '%s' could not be found to stop", soundName.c_str()); warning("Sound track '%s' could not be found to stop", soundName.c_str());
} else { } else {
freeChannel(channel); delete (*it);
_playingTracks.erase(it);
} }
} }
int32 EMISound::getPosIn16msTicks(const Common::String &soundName) { int32 EMISound::getPosIn16msTicks(const Common::String &soundName) {
int32 channel = getChannelByName(soundName); TrackList::iterator it = getPlayingTrackByName(soundName);
if (channel == -1) { if (it == _playingTracks.end()) {
warning("Sound track '%s' could not be found to get ticks", soundName.c_str()); warning("Sound track '%s' could not be found to get ticks", soundName.c_str());
return 0; return 0;
} else { } else {
return _channels[channel]->getPos().msecs() / 16; return (*it)->getPos().msecs() / 16;
} }
} }
void EMISound::setVolume(const Common::String &soundName, int volume) { void EMISound::setVolume(const Common::String &soundName, int volume) {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
int32 channel = getChannelByName(soundName); TrackList::iterator it = getPlayingTrackByName(soundName);
if (channel == -1) { if (it == _playingTracks.end()) {
warning("Sound track '%s' could not be found to set volume", soundName.c_str()); warning("Sound track '%s' could not be found to set volume", soundName.c_str());
} else { } else {
_channels[channel]->setVolume(volume); (*it)->setVolume(volume);
} }
} }
void EMISound::setPan(const Common::String &soundName, int pan) { void EMISound::setPan(const Common::String &soundName, int pan) {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
int32 channel = getChannelByName(soundName); TrackList::iterator it = getPlayingTrackByName(soundName);
if (channel == -1) { if (it == _playingTracks.end()) {
warning("Sound track '%s' could not be found to set pan", soundName.c_str()); warning("Sound track '%s' could not be found to set pan", soundName.c_str());
} else { } else {
_channels[channel]->setBalance(pan * 2 - 127); (*it)->setBalance(pan * 2 - 127);
} }
} }
@ -449,8 +426,8 @@ SoundTrack *EMISound::initTrack(const Common::String &soundName, Audio::Mixer::S
bool EMISound::stateHasLooped(int stateId) { bool EMISound::stateHasLooped(int stateId) {
if (stateId == _curMusicState) { if (stateId == _curMusicState) {
if (_musicChannel != -1 && _channels[_musicChannel] != nullptr) { if (_curMusicState != 0 && _musicTrack) {
return _channels[_musicChannel]->hasLooped(); return _musicTrack->hasLooped();
} }
} else { } else {
warning("EMISound::stateHasLooped called for a different music state than the current one"); warning("EMISound::stateHasLooped called for a different music state than the current one");
@ -460,8 +437,8 @@ bool EMISound::stateHasLooped(int stateId) {
bool EMISound::stateHasEnded(int stateId) { bool EMISound::stateHasEnded(int stateId) {
if (stateId == _curMusicState) { if (stateId == _curMusicState) {
if (_musicChannel != -1 && _channels[_musicChannel] != nullptr) { if (_curMusicState != 0 && _musicTrack) {
return !_channels[_musicChannel]->isPlaying(); return !_musicTrack->isPlaying();
} }
} }
return true; return true;
@ -476,12 +453,11 @@ void EMISound::setMusicState(int stateId) {
int sync = _musicTable[stateId]._sync; int sync = _musicTable[stateId]._sync;
Audio::Timestamp musicPos; Audio::Timestamp musicPos;
int prevSync = -1; int prevSync = -1;
if (_musicChannel != -1 && _channels[_musicChannel]) { if (_musicTrack) {
SoundTrack *music = _channels[_musicChannel]; if (_musicTrack->isPlaying()) {
if (music->isPlaying()) { musicPos = _musicTrack->getPos();
musicPos = music->getPos(); prevSync = _musicTrack->getSync();
prevSync = music->getSync(); if (sync == prevSync && soundName == _musicTrack->getSoundName()) {
if (sync == prevSync && soundName == music->getSoundName()) {
// If the previous music track is the same track as the new one, we'll just // If the previous music track is the same track as the new one, we'll just
// keep playing the previous track. This happens in the PS2 version where they // keep playing the previous track. This happens in the PS2 version where they
// removed some of the music variations, but kept the states associated with // removed some of the music variations, but kept the states associated with
@ -489,14 +465,15 @@ void EMISound::setMusicState(int stateId) {
_curMusicState = stateId; _curMusicState = stateId;
return; return;
} }
music->fadeOut(); _musicTrack->fadeOut();
_musicChannel = -1; _playingTracks.push_back(_musicTrack);
_musicTrack = nullptr;
} }
} }
bool fadeMusicIn = false; bool fadeMusicIn = false;
for (int i = 0; i < NUM_CHANNELS; ++i) { for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
if (_channels[i] && _channels[i]->isPlaying() && _channels[i]->getSoundType() == Audio::Mixer::kMusicSoundType) { if ((*it)->isPlaying() && (*it)->getSoundType() == Audio::Mixer::kMusicSoundType) {
fadeMusicIn = true; fadeMusicIn = true;
break; break;
} }
@ -524,12 +501,6 @@ void EMISound::setMusicState(int stateId) {
} }
_curMusicState = stateId; _curMusicState = stateId;
_musicChannel = getFreeChannel();
if (_musicChannel == -1) {
warning("Setting music state %d failed. Out of free sound channels", stateId);
return;
}
Audio::Timestamp *start = nullptr; Audio::Timestamp *start = nullptr;
if (prevSync != 0 && sync != 0 && prevSync == sync) if (prevSync != 0 && sync != 0 && prevSync == sync)
start = &musicPos; start = &musicPos;
@ -537,30 +508,22 @@ void EMISound::setMusicState(int stateId) {
Debug::debug(Debug::Sound, "Loading music: %s", soundName.c_str()); Debug::debug(Debug::Sound, "Loading music: %s", soundName.c_str());
SoundTrack *music = initTrack(soundName, Audio::Mixer::kMusicSoundType, start); SoundTrack *music = initTrack(soundName, Audio::Mixer::kMusicSoundType, start);
if (music) { if (music) {
_channels[_musicChannel] = music;
music->play(); music->play();
music->setSync(sync); music->setSync(sync);
if (fadeMusicIn) { if (fadeMusicIn) {
music->setFade(0.0f); music->setFade(0.0f);
music->fadeIn(); music->fadeIn();
} }
} else { _musicTrack = music;
freeChannel(_musicChannel);
_musicChannel = -1;
} }
} }
uint32 EMISound::getMsPos(int stateId) { uint32 EMISound::getMsPos(int stateId) {
if (_musicChannel == -1) { if (!_musicTrack) {
Debug::debug(Debug::Sound, "EMISound::getMsPos: No active music channel");
return 0;
}
SoundTrack *music = _channels[_musicChannel];
if (!music) {
Debug::debug(Debug::Sound, "EMISound::getMsPos: Music track is null", stateId); Debug::debug(Debug::Sound, "EMISound::getMsPos: Music track is null", stateId);
return 0; return 0;
} }
return music->getPos().msecs(); return _musicTrack->getPos().msecs();
} }
MusicEntry *initMusicTableDemo(const Common::String &filename) { MusicEntry *initMusicTableDemo(const Common::String &filename) {
@ -689,10 +652,10 @@ void EMISound::selectMusicSet(int setId) {
} }
// Immediately switch all currently active music tracks to the new quality. // Immediately switch all currently active music tracks to the new quality.
for (uint32 i = 0; i < NUM_CHANNELS; ++i) { for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
SoundTrack *track = _channels[i]; SoundTrack *track = (*it);
if (track && track->getSoundType() == Audio::Mixer::kMusicSoundType) { if (track && track->getSoundType() == Audio::Mixer::kMusicSoundType) {
_channels[i] = restartTrack(track); (*it) = restartTrack(track);
delete track; delete track;
} }
} }
@ -725,12 +688,11 @@ SoundTrack *EMISound::restartTrack(SoundTrack *track) {
void EMISound::pushStateToStack() { void EMISound::pushStateToStack() {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
if (_musicChannel != -1 && _channels[_musicChannel]) { if (_musicTrack) {
_channels[_musicChannel]->fadeOut(); _musicTrack->fadeOut();
StackEntry entry = { _curMusicState, _channels[_musicChannel] }; StackEntry entry = { _curMusicState, _musicTrack };
_stateStack.push(entry); _stateStack.push(entry);
_channels[_musicChannel] = nullptr; _musicTrack = nullptr;
_musicChannel = -1;
} else { } else {
StackEntry entry = { _curMusicState, nullptr }; StackEntry entry = { _curMusicState, nullptr };
_stateStack.push(entry); _stateStack.push(entry);
@ -740,21 +702,16 @@ void EMISound::pushStateToStack() {
void EMISound::popStateFromStack() { void EMISound::popStateFromStack() {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
if (_musicChannel != -1 && _channels[_musicChannel]) { if (_musicTrack) {
_channels[_musicChannel]->fadeOut(); _musicTrack->fadeOut();
_playingTracks.push_back(_musicTrack);
} }
//even pop state from stack if music isn't set //even pop state from stack if music isn't set
StackEntry entry = _stateStack.pop(); StackEntry entry = _stateStack.pop();
SoundTrack *track = entry._track; SoundTrack *track = entry._track;
_musicChannel = getFreeChannel(); _musicTrack = track;
if (_musicChannel == -1) {
warning("Setting music state %d from stack failed. Out of free sound channels", entry._state);
return;
}
_channels[_musicChannel] = track;
_curMusicState = entry._state; _curMusicState = entry._state;
if (track) { if (track) {
@ -776,10 +733,8 @@ void EMISound::flushStack() {
void EMISound::pause(bool paused) { void EMISound::pause(bool paused) {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
for (int i = 0; i < NUM_CHANNELS; i++) { for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
SoundTrack *track = _channels[i]; SoundTrack *track = (*it);
if (track == nullptr)
continue;
if (paused && track->isPaused()) if (paused && track->isPaused())
continue; continue;
@ -787,7 +742,7 @@ void EMISound::pause(bool paused) {
continue; continue;
// Do not pause music. // Do not pause music.
if (i == _musicChannel) if (track == _musicTrack)
continue; continue;
track->pause(); track->pause();
@ -797,6 +752,10 @@ void EMISound::pause(bool paused) {
void EMISound::callback() { void EMISound::callback() {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
if (_musicTrack) {
updateTrack(_musicTrack);
}
for (uint i = 0; i < _stateStack.size(); ++i) { for (uint i = 0; i < _stateStack.size(); ++i) {
SoundTrack *track = _stateStack[i]._track; SoundTrack *track = _stateStack[i]._track;
if (track == nullptr || track->isPaused() || !track->isPlaying()) if (track == nullptr || track->isPaused() || !track->isPlaying())
@ -808,9 +767,9 @@ void EMISound::callback() {
} }
} }
for (int i = 0; i < NUM_CHANNELS; i++) { for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
SoundTrack *track = _channels[i]; SoundTrack *track = (*it);
if (track == nullptr || track->isPaused() || !track->isPlaying()) if (track->isPaused() || !track->isPlaying())
continue; continue;
updateTrack(track); updateTrack(track);
@ -841,13 +800,11 @@ void EMISound::updateTrack(SoundTrack *track) {
void EMISound::flushTracks() { void EMISound::flushTracks() {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
for (int i = 0; i < NUM_CHANNELS; i++) { for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
SoundTrack *track = _channels[i]; SoundTrack *track = (*it);
if (track == nullptr)
continue;
if (!track->isPlaying()) { if (!track->isPlaying()) {
freeChannel(i); delete track;
it = _playingTracks.erase(it);
} }
} }
} }
@ -857,14 +814,15 @@ void EMISound::restoreState(SaveGame *savedState) {
// Clear any current music // Clear any current music
flushStack(); flushStack();
setMusicState(0); setMusicState(0);
freeAllChannels(); freePlayingSounds();
freeLoadedSounds(); freeLoadedSounds();
delete _musicTrack;
_musicTrack = nullptr;
// Actually load: // Actually load:
savedState->beginSection('SOUN'); savedState->beginSection('SOUN');
_musicPrefix = savedState->readString(); _musicPrefix = savedState->readString();
if (savedState->saveMinorVersion() >= 21) { if (savedState->saveMinorVersion() >= 21) {
_curMusicState = savedState->readLESint32(); _curMusicState = savedState->readLESint32();
_musicChannel = savedState->readLESint32();
} }
// Stack: // Stack:
@ -890,39 +848,35 @@ void EMISound::restoreState(SaveGame *savedState) {
_stateStack.push(entry); _stateStack.push(entry);
} }
// Music:
if (savedState->saveMinorVersion() < 21) { if (savedState->saveMinorVersion() < 21) {
// Old savegame format stored the music channel separately.
uint32 hasActiveTrack = savedState->readLEUint32(); uint32 hasActiveTrack = savedState->readLEUint32();
if (hasActiveTrack) { if (hasActiveTrack) {
Common::String soundName = savedState->readString(); Common::String soundName = savedState->readString();
_musicChannel = getFreeChannel(); _musicTrack = initTrack(soundName, Audio::Mixer::kMusicSoundType);
if (_musicChannel == -1) { if (_musicTrack) {
warning("Failed to start sound %s. Out of free sound channels", soundName.c_str()); _musicTrack->play();
} else { } else {
_channels[_musicChannel] = initTrack(soundName, Audio::Mixer::kMusicSoundType);
if (_channels[_musicChannel]) {
_channels[_musicChannel]->play();
}
else {
error("Couldn't reopen %s", soundName.c_str()); error("Couldn't reopen %s", soundName.c_str());
} }
} }
} else if (savedState->saveMinorVersion() >= 21) {
bool musicActive = savedState->readBool();
if (musicActive) {
_musicTrack = restoreTrack(savedState);
} }
} }
// Channels: // Effects and voices:
uint32 numChannels = savedState->readLEUint32(); uint32 numTracks = savedState->readLEUint32();
if (numChannels > NUM_CHANNELS) { for (uint32 i = 0; i < numTracks; i++) {
error("Save game made with more channels than we have now: %d > %d", numChannels, NUM_CHANNELS); bool channelIsActive = true;
} if (savedState->saveMinorVersion() < 21) {
for (uint32 i = 0; i < numChannels; i++) {
bool channelIsActive;
if (savedState->saveMinorVersion() >= 21)
channelIsActive = savedState->readBool();
else
channelIsActive = (savedState->readLESint32() != 0); channelIsActive = (savedState->readLESint32() != 0);
}
if (channelIsActive) { if (channelIsActive) {
_channels[i] = restoreTrack(savedState); SoundTrack *track = restoreTrack(savedState);
_playingTracks.push_back(track);
} }
} }
@ -944,7 +898,6 @@ void EMISound::saveState(SaveGame *savedState) {
savedState->beginSection('SOUN'); savedState->beginSection('SOUN');
savedState->writeString(_musicPrefix); savedState->writeString(_musicPrefix);
savedState->writeLESint32(_curMusicState); savedState->writeLESint32(_curMusicState);
savedState->writeLESint32(_musicChannel);
// Stack: // Stack:
uint32 stackSize = _stateStack.size(); uint32 stackSize = _stateStack.size();
@ -959,16 +912,16 @@ void EMISound::saveState(SaveGame *savedState) {
} }
} }
// Channels: // Music:
uint32 numChannels = NUM_CHANNELS; savedState->writeBool(_musicTrack != nullptr);
savedState->writeLEUint32(numChannels); if (_musicTrack) {
for (uint32 i = 0; i < numChannels; i++) { saveTrack(_musicTrack, savedState);
if (!_channels[i]) {
savedState->writeBool(false); // The track is inactive.
} else {
savedState->writeBool(true);
saveTrack(_channels[i], savedState);
} }
// Effects and voices:
savedState->writeLEUint32(_playingTracks.size());
for (TrackList::iterator it = _playingTracks.begin(); it != _playingTracks.end(); ++it) {
saveTrack((*it), savedState);
} }
// Preloaded sounds: // Preloaded sounds:

View File

@ -95,8 +95,9 @@ private:
SoundTrack *_track; SoundTrack *_track;
}; };
SoundTrack **_channels; typedef Common::List<SoundTrack *> TrackList;
int32 _musicChannel; TrackList _playingTracks;
SoundTrack *_musicTrack;
MusicEntry *_musicTable; MusicEntry *_musicTable;
Common::String _musicPrefix; Common::String _musicPrefix;
Common::Stack<StackEntry> _stateStack; Common::Stack<StackEntry> _stateStack;
@ -113,14 +114,13 @@ private:
static void timerHandler(void *refConf); static void timerHandler(void *refConf);
void removeItem(SoundTrack *item); void removeItem(SoundTrack *item);
int32 getFreeChannel(); TrackList::iterator getPlayingTrackByName(const Common::String &name);
int32 getChannelByName(const Common::String &name);
void freeChannel(int32 channel); void freeChannel(int32 channel);
void initMusicTable(); void initMusicTable();
void callback(); void callback();
void updateTrack(SoundTrack *track); void updateTrack(SoundTrack *track);
void freeAllChannels(); void freePlayingSounds();
void freeLoadedSounds(); void freeLoadedSounds();
SoundTrack *initTrack(const Common::String &soundName, Audio::Mixer::SoundType soundType, const Audio::Timestamp *start = nullptr) const; SoundTrack *initTrack(const Common::String &soundName, Audio::Mixer::SoundType soundType, const Audio::Timestamp *start = nullptr) const;
SoundTrack *restartTrack(SoundTrack *track); SoundTrack *restartTrack(SoundTrack *track);