CHEWY: Fix cutscene volume and panning

This commit fixes the initial volume and panning for SFX and music in
cutscenes. These were set on the sound handle of the previous SFX or music
track played, which would not affect the new sound.
This commit is contained in:
Coen Rampen 2022-07-03 21:00:17 +02:00
parent 0fe9e930b3
commit c76d13a34c
8 changed files with 85 additions and 54 deletions

View file

@ -607,6 +607,13 @@ void Atdsys::print_aad(int16 scrX, int16 scrY) {
if (g_engine->_sound->speechEnabled() && if (g_engine->_sound->speechEnabled() &&
_aadv._strHeader->_vocNr - ATDS_VOC_OFFSET != -1) { _aadv._strHeader->_vocNr - ATDS_VOC_OFFSET != -1) {
if (_atdsv._vocNr != _aadv._strHeader->_vocNr - ATDS_VOC_OFFSET) { if (_atdsv._vocNr != _aadv._strHeader->_vocNr - ATDS_VOC_OFFSET) {
// TODO Panning does not work properly during cutscenes.
// The x position seems to be fixed, probably that of the
// character in the (now invisible) game screen, so all
// cutscene voices are panned to the same position. I don't
// know if the x position of the character speaking in the
// cutscene is available somehow; otherwise panning must be
// disabled (fixed center) during cutscenes.
_atdsv._vocNr = _aadv._strHeader->_vocNr - ATDS_VOC_OFFSET; _atdsv._vocNr = _aadv._strHeader->_vocNr - ATDS_VOC_OFFSET;
const int16 vocx = _G(moveState)[personId].Xypos[0] - const int16 vocx = _G(moveState)[personId].Xypos[0] -
_G(gameState).scrollx + _G(spieler_mi)[personId].HotX; _G(gameState).scrollx + _G(spieler_mi)[personId].HotX;

View file

@ -100,12 +100,12 @@ void Options::execute(TafInfo *ti) {
8 + ti->correction[(SCHNULL_BAND << 1) + 1], 0); 8 + ti->correction[(SCHNULL_BAND << 1) + 1], 0);
} }
const int soundVolume = MAX(1, g_engine->_sound->getSoundVolume() * 32 / Audio::Mixer::kMaxMixerVolume); const int soundVolume = MAX(1, g_engine->_sound->getUserSoundVolume() * 32 / Audio::Mixer::kMaxMixerVolume);
_G(out)->pop_box(32 - 2, 104 - 12, 42 + 4, 136 + 2, 192, 183, 182); _G(out)->pop_box(32 - 2, 104 - 12, 42 + 4, 136 + 2, 192, 183, 182);
_G(out)->printxy(32 + 3, 104 - 10, 15, 300, 0, "S"); _G(out)->printxy(32 + 3, 104 - 10, 15, 300, 0, "S");
_G(out)->boxFill(33, 136 - soundVolume, 42, 136, 15); _G(out)->boxFill(33, 136 - soundVolume, 42, 136, 15);
const int musicVolume = MAX(1, g_engine->_sound->getMusicVolume() * 32 / Audio::Mixer::kMaxMixerVolume); const int musicVolume = MAX(1, g_engine->_sound->getUserMusicVolume() * 32 / Audio::Mixer::kMaxMixerVolume);
_G(out)->pop_box(52 - 2, 104 - 12, 62 + 4, 136 + 2, 192, 183, 182); _G(out)->pop_box(52 - 2, 104 - 12, 62 + 4, 136 + 2, 192, 183, 182);
_G(out)->printxy(52 + 3, 104 - 10, 31, 300, 0, "M"); _G(out)->printxy(52 + 3, 104 - 10, 31, 300, 0, "M");
_G(out)->boxFill(53, 136 - musicVolume, 62, 136, 31); _G(out)->boxFill(53, 136 - musicVolume, 62, 136, 31);
@ -193,11 +193,11 @@ void Options::execute(TafInfo *ti) {
key = Common::KEYCODE_ESCAPE; key = Common::KEYCODE_ESCAPE;
break; break;
case 7: // S volume gauge case 7: // S volume gauge
g_engine->_sound->setSoundVolume(MIN(32, 136 - g_events->_mousePos.y) * Audio::Mixer::kMaxMixerVolume / 32); g_engine->_sound->setUserSoundVolume(MIN(32, 136 - g_events->_mousePos.y) * Audio::Mixer::kMaxMixerVolume / 32);
g_engine->syncSoundSettings(); g_engine->syncSoundSettings();
break; break;
case 8: // M volume gauge case 8: // M volume gauge
g_engine->_sound->setMusicVolume(MIN(32, 136 - g_events->_mousePos.y) * Audio::Mixer::kMaxMixerVolume / 32); g_engine->_sound->setUserMusicVolume(MIN(32, 136 - g_events->_mousePos.y) * Audio::Mixer::kMaxMixerVolume / 32);
g_engine->syncSoundSettings(); g_engine->syncSoundSettings();
break; break;

View file

@ -754,9 +754,9 @@ void flic_cut(int16 nr) {
break; break;
case FCUT_112: case FCUT_112:
g_engine->_sound->setMusicVolume(32 * Audio::Mixer::kMaxChannelVolume / 120); g_engine->_sound->setActiveMusicVolume(32);
g_engine->_video->playVideo(nr); g_engine->_video->playVideo(nr);
g_engine->_sound->setMusicVolume(5 * Audio::Mixer::kMaxChannelVolume / 120); g_engine->_sound->setActiveMusicVolume(5);
break; break;
case FCUT_133: case FCUT_133:

View file

@ -66,11 +66,8 @@ void Sound::playSound(uint8 *data, uint32 size, uint channel, bool loop, uint16
dispose), dispose),
loop ? 0 : 1); loop ? 0 : 1);
assert(volume >= 0 && volume < 64);
assert(balance >= 0 && balance < 128);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle[channel], stream, -1, _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle[channel], stream, -1,
volume * Audio::Mixer::kMaxChannelVolume / 63, MIN(127, (balance - 63) * 2)); convertVolume(volume), convertBalance(balance));
} }
void Sound::pauseSound(uint channel) { void Sound::pauseSound(uint channel) {
@ -98,37 +95,24 @@ bool Sound::isSoundActive(uint channel) const {
return _mixer->isSoundHandleActive(_soundHandle[channel]); return _mixer->isSoundHandleActive(_soundHandle[channel]);
} }
void Sound::setSoundVolume(uint volume) { void Sound::setUserSoundVolume(uint volume) {
_userSoundVolume = volume; _userSoundVolume = volume;
if (soundEnabled()) if (soundEnabled())
ConfMan.setInt("sfx_volume", volume); ConfMan.setInt("sfx_volume", volume);
} }
int Sound::getSoundVolume() const { int Sound::getUserSoundVolume() const {
return _userSoundVolume; return _userSoundVolume;
} }
void Sound::pushVolume() {
/* _soundVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType);
_speechVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType);
_musicVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);*/
}
void Sound::popVolume() {
/* assert(_soundVolume >= 0 && _speechVolume >= 0 && _musicVolume >= 0);
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _soundVolume);
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _speechVolume);
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, _musicVolume);*/
}
void Sound::setSoundChannelVolume(uint channel, uint volume) { void Sound::setSoundChannelVolume(uint channel, uint volume) {
assert(channel < MAX_SOUND_EFFECTS); assert(channel < MAX_SOUND_EFFECTS);
_mixer->setChannelVolume(_soundHandle[channel], volume); _mixer->setChannelVolume(_soundHandle[channel], convertVolume(volume));
} }
void Sound::setSoundChannelBalance(uint channel, int8 balance) { void Sound::setSoundChannelBalance(uint channel, int8 balance) {
assert(channel < MAX_SOUND_EFFECTS); assert(channel < MAX_SOUND_EFFECTS);
_mixer->setChannelBalance(_soundHandle[channel], balance); _mixer->setChannelBalance(_soundHandle[channel], convertBalance(balance));
} }
void Sound::playMusic(int16 num, bool loop) { void Sound::playMusic(int16 num, bool loop) {
@ -142,11 +126,11 @@ void Sound::playMusic(int16 num, bool loop) {
delete[] data; delete[] data;
} }
void Sound::playMusic(uint8 *data, uint32 size) { void Sound::playMusic(uint8 *data, uint32 size, uint8 volume) {
TMFStream *stream = new TMFStream(new Common::MemoryReadStream(data, size), 0); TMFStream *stream = new TMFStream(new Common::MemoryReadStream(data, size), 0);
_curMusic = -1; _curMusic = -1;
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, stream); _mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, stream, -1, convertVolume(volume));
} }
void Sound::pauseMusic() { void Sound::pauseMusic() {
@ -166,16 +150,21 @@ bool Sound::isMusicActive() const {
return _mixer->isSoundHandleActive(_musicHandle); return _mixer->isSoundHandleActive(_musicHandle);
} }
void Sound::setMusicVolume(uint volume) { void Sound::setUserMusicVolume(uint volume) {
_userMusicVolume = volume; _userMusicVolume = volume;
if (musicEnabled()) if (musicEnabled())
ConfMan.setInt("music_volume", volume); ConfMan.setInt("music_volume", volume);
} }
int Sound::getMusicVolume() const { int Sound::getUserMusicVolume() const {
return _userMusicVolume; return _userMusicVolume;
} }
void Sound::setActiveMusicVolume(uint8 volume) {
if (isMusicActive())
_mixer->setChannelVolume(_musicHandle, convertVolume(volume));
}
void Sound::playSpeech(int num, bool waitForFinish, uint16 balance) { void Sound::playSpeech(int num, bool waitForFinish, uint16 balance) {
if (isSpeechActive()) if (isSpeechActive())
stopSpeech(); stopSpeech();
@ -194,10 +183,8 @@ void Sound::playSpeech(int num, bool waitForFinish, uint16 balance) {
new Common::MemorySeekableReadWriteStream(data, size, DisposeAfterUse::YES), new Common::MemorySeekableReadWriteStream(data, size, DisposeAfterUse::YES),
DisposeAfterUse::YES); DisposeAfterUse::YES);
assert(balance >= 0 && balance < 128);
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream,
-1 , 255, MIN(127, (balance - 63) * 2)); -1, Audio::Mixer::kMaxChannelVolume, convertBalance(balance));
if (waitForFinish) { if (waitForFinish) {
// Wait for speech to finish // Wait for speech to finish
@ -237,7 +224,7 @@ void Sound::waitForSpeechToFinish() {
void Sound::setSpeechBalance(uint16 balance) { void Sound::setSpeechBalance(uint16 balance) {
if (isSpeechActive()) { if (isSpeechActive()) {
_mixer->setChannelBalance(_speechHandle, balance); _mixer->setChannelBalance(_speechHandle, convertBalance(balance));
} }
} }
@ -346,4 +333,14 @@ void Sound::playRoomMusic(int16 roomNum) {
} }
} }
uint8 Sound::convertVolume(uint16 volume) {
assert(volume >= 0 && volume < 64);
return volume * Audio::Mixer::kMaxChannelVolume / 63;
}
int8 Sound::convertBalance(uint16 balance) {
assert(balance >= 0 && balance < 128);
return MIN(127, (balance - 63) * 2);
}
} // namespace Chewy } // namespace Chewy

View file

@ -50,21 +50,26 @@ public:
void stopSound(uint channel = 0); void stopSound(uint channel = 0);
void stopAllSounds(); void stopAllSounds();
bool isSoundActive(uint channel) const; bool isSoundActive(uint channel) const;
void setSoundVolume(uint volume); void setUserSoundVolume(uint volume);
int getSoundVolume() const; int getUserSoundVolume() const;
// Sets the volume of the sound effect curently active on the specified
// channel. Does not affect the next sound effect played on the channel.
void setSoundChannelVolume(uint channel, uint volume); void setSoundChannelVolume(uint channel, uint volume);
// Sets the balance of the sound effect curently active on the specified
// channel. Does not affect the next sound effect played on the channel.
void setSoundChannelBalance(uint channel, int8 balance); void setSoundChannelBalance(uint channel, int8 balance);
void pushVolume();
void popVolume();
void playMusic(int16 num, bool loop = false); void playMusic(int16 num, bool loop = false);
void playMusic(uint8 *data, uint32 size); void playMusic(uint8 *data, uint32 size, uint8 volume = 63);
void pauseMusic(); void pauseMusic();
void resumeMusic(); void resumeMusic();
void stopMusic(); void stopMusic();
bool isMusicActive() const; bool isMusicActive() const;
void setMusicVolume(uint volume); void setUserMusicVolume(uint volume);
int getMusicVolume() const; int getUserMusicVolume() const;
// Sets the volume of the currently active music track. Does not affect the
// next music track played.
void setActiveMusicVolume(uint8 volume);
void playRoomMusic(int16 roomNum); void playRoomMusic(int16 roomNum);
void playSpeech(int num, bool waitForFinish, uint16 balance = 63); void playSpeech(int num, bool waitForFinish, uint16 balance = 63);
@ -72,6 +77,8 @@ public:
void resumeSpeech(); void resumeSpeech();
void stopSpeech(); void stopSpeech();
bool isSpeechActive() const; bool isSpeechActive() const;
// Sets the balance of the currently playing speech sample. Does not affect
// the next speech sample played.
void setSpeechBalance(uint16 balance = 63); void setSpeechBalance(uint16 balance = 63);
void stopAll(); void stopAll();
@ -96,6 +103,13 @@ public:
void syncSoundSettings(); void syncSoundSettings();
private: private:
// Converts volume from the scale used by the game data (0 - 63) to the
// ScummVM mixer channel scale (0 - 255).
uint8 convertVolume(uint16 volume);
// Converts balance from the scale used by the game data (0 - 127) to the
// ScummVM mixer channel scale (-127 - 127).
int8 convertBalance(uint16 balance);
Audio::Mixer *_mixer; Audio::Mixer *_mixer;
Audio::SoundHandle _soundHandle[MAX_SOUND_EFFECTS]; Audio::SoundHandle _soundHandle[MAX_SOUND_EFFECTS];
Audio::SoundHandle _musicHandle; Audio::SoundHandle _musicHandle;

View file

@ -83,6 +83,10 @@ CfoDecoder::CfoVideoTrack::CfoVideoTrack(Common::SeekableReadStream *stream, uin
_musicData = nullptr; _musicData = nullptr;
_musicSize = 0; _musicSize = 0;
Common::fill(_sfxBalances, _sfxBalances + ARRAYSIZE(_sfxBalances), 63);
_sfxGlobalVolume = 63;
_musicVolume = 63;
} }
CfoDecoder::CfoVideoTrack::~CfoVideoTrack() { CfoDecoder::CfoVideoTrack::~CfoVideoTrack() {
@ -219,7 +223,7 @@ void CfoDecoder::CfoVideoTrack::handleCustomFrame() {
break; break;
case kChunkPlayMusic: case kChunkPlayMusic:
// Used in videos 0, 18, 34, 71 // Used in videos 0, 18, 34, 71
_sound->playMusic(_musicData, _musicSize); _sound->playMusic(_musicData, _musicSize, _musicVolume);
break; break;
case kChunkPlaySeq: case kChunkPlaySeq:
error("Unused chunk kChunkPlaySeq found"); error("Unused chunk kChunkPlaySeq found");
@ -246,8 +250,10 @@ void CfoDecoder::CfoVideoTrack::handleCustomFrame() {
} while (_sound->isMusicActive() && musicLoops < 100); } while (_sound->isMusicActive() && musicLoops < 100);
break; break;
case kChunkSetMusicVolume: case kChunkSetMusicVolume:
volume = _fileStream->readUint16LE() * Audio::Mixer::kMaxChannelVolume / 63; volume = _fileStream->readUint16LE();
_sound->setMusicVolume(volume);
_musicVolume = volume;
_sound->setActiveMusicVolume(volume);
break; break;
case kChunkSetLoopMode: case kChunkSetLoopMode:
error("Unused chunk kChunkSetLoopMode found"); error("Unused chunk kChunkSetLoopMode found");
@ -262,18 +268,21 @@ void CfoDecoder::CfoVideoTrack::handleCustomFrame() {
repeat = _fileStream->readUint16LE(); repeat = _fileStream->readUint16LE();
assert(number < MAX_SOUND_EFFECTS); assert(number < MAX_SOUND_EFFECTS);
//_sound->setSoundVolume(volume); _sound->playSound(_soundEffects[number], _soundEffectSize[number], channel, repeat,
_sound->playSound(_soundEffects[number], _soundEffectSize[number], channel, repeat, volume, 63, DisposeAfterUse::NO); volume * _sfxGlobalVolume / 63, _sfxBalances[channel], DisposeAfterUse::NO);
break; break;
case kChunkSetSoundVolume: case kChunkSetSoundVolume:
volume = _fileStream->readUint16LE() * Audio::Mixer::kMaxChannelVolume / 63; volume = _fileStream->readUint16LE();
_sound->setSoundVolume(volume); assert(volume >= 0 && volume < 64);
_sfxGlobalVolume = volume;
// This is only used once in the credits video, before any sounds
// are played, so no need to update volume of active sounds.
break; break;
case kChunkSetChannelVolume: case kChunkSetChannelVolume:
channel = _fileStream->readUint16LE(); channel = _fileStream->readUint16LE();
volume = _fileStream->readUint16LE() * Audio::Mixer::kMaxChannelVolume / 63; volume = _fileStream->readUint16LE();
_sound->setSoundChannelVolume(channel, volume); _sound->setSoundChannelVolume(channel, volume * _sfxGlobalVolume / 63);
break; break;
case kChunkFreeSoundEffect: case kChunkFreeSoundEffect:
number = _fileStream->readUint16LE(); number = _fileStream->readUint16LE();
@ -293,7 +302,9 @@ void CfoDecoder::CfoVideoTrack::handleCustomFrame() {
break; break;
case kChunkSetBalance: case kChunkSetBalance:
channel = _fileStream->readUint16LE(); channel = _fileStream->readUint16LE();
balance = (_fileStream->readUint16LE() * 2) - 127; balance = _fileStream->readUint16LE();
_sfxBalances[channel] = balance;
_sound->setSoundChannelBalance(channel, balance); _sound->setSoundChannelBalance(channel, balance);
break; break;
case kChunkSetSpeed: case kChunkSetSpeed:

View file

@ -62,6 +62,10 @@ private:
uint32 _soundEffectSize[MAX_SOUND_EFFECTS]; uint32 _soundEffectSize[MAX_SOUND_EFFECTS];
uint8 *_musicData; uint8 *_musicData;
uint32 _musicSize; uint32 _musicSize;
uint8 _sfxBalances[MAX_SOUND_EFFECTS];
uint8 _sfxGlobalVolume;
uint8 _musicVolume;
}; };
}; };

View file

@ -36,7 +36,6 @@ bool VideoPlayer::playVideo(uint num, bool stopMusic) {
CfoDecoder *cfoDecoder = new CfoDecoder(g_engine->_sound); CfoDecoder *cfoDecoder = new CfoDecoder(g_engine->_sound);
VideoResource *videoResource = new VideoResource("cut.tap"); VideoResource *videoResource = new VideoResource("cut.tap");
Common::SeekableReadStream *videoStream = videoResource->getVideoStream(num); Common::SeekableReadStream *videoStream = videoResource->getVideoStream(num);
g_engine->_sound->pushVolume();
_playCount = 0; _playCount = 0;
if (stopMusic) { if (stopMusic) {
@ -102,7 +101,6 @@ bool VideoPlayer::playVideo(uint num, bool stopMusic) {
g_system->getPaletteManager()->setPalette(curPalette, 0, 256); g_system->getPaletteManager()->setPalette(curPalette, 0, 256);
_G(cur)->showCursor(); _G(cur)->showCursor();
g_engine->_sound->popVolume();
delete videoResource; delete videoResource;
delete cfoDecoder; delete cfoDecoder;