DIRECTOR: Make sound fade non-blocking

This commit is contained in:
Nathanael Gentry 2020-07-22 14:07:58 -04:00
parent 344c1deb32
commit 0044225060
5 changed files with 80 additions and 32 deletions

View File

@ -2112,7 +2112,6 @@ void LB::b_sound(int nargs) {
TYPECHECK(firstArg, INT);
g_director->getSoundManager()->stopSound(firstArg.u.i);
} else if (verb.u.s->equalsIgnoreCase("fadeIn")) {
// TODO: Check for case when sound channel changes while sound is being played.
if (nargs > 2) {
TYPECHECK(secondArg, INT);
ticks = secondArg.u.i;
@ -2121,10 +2120,10 @@ void LB::b_sound(int nargs) {
}
TYPECHECK(firstArg, INT);
g_director->getSoundManager()->playFade(firstArg.u.i, true, ticks);
g_director->getSoundManager()->registerFade(firstArg.u.i, true, ticks);
g_director->getCurrentMovie()->getScore()->_activeFade = firstArg.u.i;
return;
} else if (verb.u.s->equalsIgnoreCase("fadeOut")) {
// TODO: Check for case when sound channel changes while sound is being played.
if (nargs > 2) {
TYPECHECK(secondArg, INT);
ticks = secondArg.u.i;
@ -2133,7 +2132,8 @@ void LB::b_sound(int nargs) {
}
TYPECHECK(firstArg, INT);
g_director->getSoundManager()->playFade(firstArg.u.i, false, ticks);
g_director->getSoundManager()->registerFade(firstArg.u.i, false, ticks);
g_director->getCurrentMovie()->getScore()->_activeFade = firstArg.u.i;
return;
} else if (verb.u.s->equalsIgnoreCase("playFile")) {
ARGNUMCHECK(3)

View File

@ -279,6 +279,11 @@ void Score::update() {
_waitForChannel = 0;
}
if (_activeFade) {
if (!_soundManager->fadeChannel(_activeFade))
_activeFade = 0;
}
if (g_system->getMillis() < _nextFrameTime && !debugChannelSet(-1, kDebugFast)) {
return;
}

View File

@ -121,6 +121,7 @@ public:
PlayState _playState;
uint32 _nextFrameTime;
int _waitForChannel;
int _activeFade;
Cursor *_currentCursor;
int _numChannelsDisplayed;

View File

@ -77,6 +77,7 @@ void DirectorSound::playFile(Common::String filename, uint8 soundChannel) {
AudioFileDecoder af(filename);
Audio::RewindableAudioStream *sound = af.getAudioStream(DisposeAfterUse::YES);
cancelFade(soundChannel);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_channels[soundChannel - 1].handle, sound, -1, _channels[soundChannel - 1].volume);
}
@ -94,6 +95,7 @@ void DirectorSound::playStream(Audio::AudioStream &stream, uint8 soundChannel) {
return;
}
cancelFade(soundChannel);
_mixer->stopHandle(_channels[soundChannel - 1].handle);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_channels[soundChannel - 1].handle, &stream, -1, _channels[soundChannel - 1].volume);
}
@ -133,42 +135,60 @@ void DirectorSound::playCastMember(int castId, uint8 soundChannel, bool allowRep
}
}
void DirectorSound::playFade(uint8 soundChannel, bool fadeIn, int ticks) {
void DirectorSound::registerFade(uint8 soundChannel, bool fadeIn, int ticks) {
if (soundChannel == 0 || soundChannel > _channels.size()) {
warning("Invalid sound channel %d", soundChannel);
return;
}
Audio::SoundHandle handle = _channels[soundChannel - 1].handle;
cancelFade(soundChannel);
if (!isChannelActive(soundChannel))
return;
int startVol = fadeIn ? 0 : _channels[soundChannel - 1].volume;
int targetVol = fadeIn ? _channels[soundChannel - 1].volume : 0;
float startVolume = fadeIn ? 0 : _channels[soundChannel - 1].volume;
float targetVolume = fadeIn ? _channels[soundChannel - 1].volume : 0;
int lastVolume = 0;
_channels[soundChannel - 1].fade = new FadeParams(startVol, targetVol, ticks, _vm->getMacTicks(), fadeIn);
_mixer->setChannelVolume(_channels[soundChannel - 1].handle, startVol);
}
_mixer->setChannelVolume(handle, startVolume);
int startTicks = _vm->getMacTicks();
int lapsedTicks = 0, lastTicks = 0;
while (lapsedTicks < ticks) {
lapsedTicks = _vm->getMacTicks() - startTicks;
if (lapsedTicks == lastTicks)
continue;
lastTicks = lapsedTicks;
if (fadeIn) {
lastVolume = MIN(lapsedTicks * (targetVolume / ticks), (float)Audio::Mixer::kMaxChannelVolume);
} else {
lastVolume = MAX((ticks - lapsedTicks) * (startVolume / ticks), (float)0);
}
_mixer->setChannelVolume(handle, lastVolume);
bool DirectorSound::fadeChannel(uint8 soundChannel) {
if (soundChannel == 0 || soundChannel > _channels.size()) {
warning("Invalid sound channel %d", soundChannel);
return false;
} else if (!isChannelActive(soundChannel)) {
return false;
}
_mixer->setChannelVolume(handle, targetVolume);
FadeParams *fade = _channels[soundChannel - 1].fade;
if (!fade)
return false;
fade->lapsedTicks = _vm->getMacTicks() - fade->startTicks;
if (fade->lapsedTicks > fade->totalTicks) {
cancelFade(soundChannel);
return false;
}
int fadeVol;
if (fade->fadeIn) {
fadeVol = MIN(fade->lapsedTicks * ((float)fade->targetVol / fade->totalTicks), (float)Audio::Mixer::kMaxChannelVolume);
} else {
fadeVol = MAX((fade->totalTicks - fade->lapsedTicks) * ((float)fade->startVol / fade->totalTicks), (float)0);
}
_mixer->setChannelVolume(_channels[soundChannel - 1].handle, fadeVol);
return true;
}
void DirectorSound::cancelFade(uint8 soundChannel) {
// NOTE: It is assumed that soundChannel has already been validated, which is
// why this method is private.
if (_channels[soundChannel - 1].fade) {
_mixer->setChannelVolume(_channels[soundChannel - 1].handle, _channels[soundChannel - 1].fade->targetVol);
delete _channels[soundChannel - 1].fade;
_channels[soundChannel - 1].fade = nullptr;
}
}
bool DirectorSound::isChannelActive(uint8 soundChannel) {
@ -195,6 +215,7 @@ void DirectorSound::stopSound(uint8 soundChannel) {
return;
}
cancelFade(soundChannel);
_mixer->stopHandle(_channels[soundChannel - 1].handle);
_channels[soundChannel - 1].lastPlayingCast = 0;
return;
@ -202,9 +223,12 @@ void DirectorSound::stopSound(uint8 soundChannel) {
void DirectorSound::stopSound() {
for (uint i = 0; i < _channels.size(); i++) {
cancelFade(i);
_mixer->stopHandle(_channels[i].handle);
_channels[i].lastPlayingCast = 0;
}
_mixer->stopHandle(*_scriptSound);
_mixer->stopHandle(*_pcSpeakerHandle);
}

View File

@ -34,12 +34,25 @@ namespace Audio {
namespace Director {
struct FadeParams {
int startVol;
int targetVol;
int totalTicks;
int startTicks;
int lapsedTicks;
bool fadeIn;
FadeParams(int sv, int tv, int tt, int st, bool f) :
startVol(sv), targetVol(tv), totalTicks(tt), startTicks(st), lapsedTicks(0), fadeIn(f) {}
};
struct SoundChannel {
Audio::SoundHandle handle;
int lastPlayingCast;
byte volume;
FadeParams *fade;
SoundChannel(): handle(), lastPlayingCast(0), volume(255) {}
SoundChannel(): handle(), lastPlayingCast(0), volume(255), fade(nullptr) {}
};
class DirectorSound {
@ -61,13 +74,18 @@ public:
void playMCI(Audio::AudioStream &stream, uint32 from, uint32 to);
void playStream(Audio::AudioStream &stream, uint8 soundChannel);
void playCastMember(int castId, uint8 soundChannel, bool allowRepeat = true);
void playFade(uint8 soundChannel, bool fadeIn, int ticks);
void systemBeep();
void registerFade(uint8 soundChannel, bool fadeIn, int ticks);
bool fadeChannel(uint8 soundChannel);
bool isChannelActive(uint8 soundChannel);
int lastPlayingCast(uint8 soundChannel);
void stopSound(uint8 soundChannel);
void stopSound();
private:
void cancelFade(uint8 soundChannel);
};
class AudioDecoder {