diff options
author | johndoe123 | 2012-09-22 00:55:40 +0000 |
---|---|---|
committer | Willem Jan Palenstijn | 2013-05-08 20:43:42 +0200 |
commit | fc0e40db304aa489d4117299fcce1f80ba0b6379 (patch) | |
tree | e8f25ec91d06c4150e17a661c2285c1f411b89eb /engines/neverhood/sound.cpp | |
parent | 0bb70c39f084dc122d213b07b1ef2d946608fe88 (diff) | |
download | scummvm-rg350-fc0e40db304aa489d4117299fcce1f80ba0b6379.tar.gz scummvm-rg350-fc0e40db304aa489d4117299fcce1f80ba0b6379.tar.bz2 scummvm-rg350-fc0e40db304aa489d4117299fcce1f80ba0b6379.zip |
NEVERHOOD: Implement the actual audio code
Sounds and music play now in some scenes
(I didn't change the comments to code in all modules yet)
- Fix calcHash to ignore non-alphanumeric characters, this fixes at least
one animation glitch (when inserting tapes into the player)
- Move SoundResource to sound.cpp
Diffstat (limited to 'engines/neverhood/sound.cpp')
-rw-r--r-- | engines/neverhood/sound.cpp | 463 |
1 files changed, 421 insertions, 42 deletions
diff --git a/engines/neverhood/sound.cpp b/engines/neverhood/sound.cpp index 91a23bfc7b..bd2f223998 100644 --- a/engines/neverhood/sound.cpp +++ b/engines/neverhood/sound.cpp @@ -20,36 +20,101 @@ * */ +#include "common/memstream.h" #include "graphics/palette.h" #include "neverhood/sound.h" +#include "neverhood/resourceman.h" namespace Neverhood { // TODO Put more stuff into the constructors/destructors of the item structs // TODO Some parts are quite bad here, but my priority is to get sound working at all +SoundResource::SoundResource(NeverhoodEngine *vm) + : _vm(vm), _soundIndex(-1) { +} + +SoundResource::~SoundResource() { + unload(); +} + +bool SoundResource::isPlaying() { + return _soundIndex >= 0 && + _vm->_audioResourceMan->isSoundPlaying(_soundIndex); +} + +void SoundResource::load(uint32 fileHash) { + unload(); + _soundIndex = _vm->_audioResourceMan->addSound(fileHash); + _vm->_audioResourceMan->loadSound(_soundIndex); +} + +void SoundResource::unload() { + if (_soundIndex >= 0) { + _vm->_audioResourceMan->removeSound(_soundIndex); + _soundIndex = -1; + } +} + +void SoundResource::play(uint32 fileHash) { + load(fileHash); + play(); +} + +void SoundResource::play() { + if (_soundIndex >= 0) + _vm->_audioResourceMan->playSound(_soundIndex, false); +} + +void SoundResource::stop() { + if (_soundIndex >= 0) + _vm->_audioResourceMan->stopSound(_soundIndex); +} + +void SoundResource::setVolume(int16 volume) { + if (_soundIndex >= 0) + _vm->_audioResourceMan->setSoundVolume(_soundIndex, volume); +} + +void SoundResource::setPan(int16 pan) { + if (_soundIndex >= 0) + _vm->_audioResourceMan->setSoundPan(_soundIndex, pan); +} + MusicResource::MusicResource(NeverhoodEngine *vm) - : _vm(vm) { + : _vm(vm), _musicIndex(-1) { } bool MusicResource::isPlaying() { - return false; + return _musicIndex >= 0 && + _vm->_audioResourceMan->isMusicPlaying(_musicIndex); } void MusicResource::load(uint32 fileHash) { - // TODO + unload(); + _musicIndex = _vm->_audioResourceMan->loadMusic(fileHash); } void MusicResource::unload() { - // TODO + if (_musicIndex >= 0) { + _vm->_audioResourceMan->unloadMusic(_musicIndex); + _musicIndex = -1; + } } void MusicResource::play(int16 fadeVolumeStep) { - // TODO + if (_musicIndex >= 0) + _vm->_audioResourceMan->playMusic(_musicIndex, fadeVolumeStep); } void MusicResource::stop(int16 fadeVolumeStep) { - // TODO + if (_musicIndex >= 0) + _vm->_audioResourceMan->stopMusic(_musicIndex, fadeVolumeStep); +} + +void MusicResource::setVolume(int16 volume) { + if (_musicIndex >= 0) + _vm->_audioResourceMan->setMusicVolume(_musicIndex, volume); } MusicItem::MusicItem() @@ -80,6 +145,8 @@ SoundItem::~SoundItem() { delete _soundResource; } +// SoundMan + SoundMan::SoundMan(NeverhoodEngine *vm) : _vm(vm), _soundIndex1(-1), _soundIndex2(-1), _soundIndex3(-1) { @@ -107,8 +174,8 @@ void SoundMan::deleteMusic(uint32 musicFileHash) { if (musicItem) { delete musicItem; for (uint i = 0; i < _musicItems.size(); ++i) - if (_musicItems[i]->_musicFileHash == musicFileHash) { - _musicItems.remove_at(i); + if (_musicItems[i] == musicItem) { + _musicItems[i] = NULL; break; } } @@ -149,8 +216,8 @@ void SoundMan::deleteSound(uint32 soundFileHash) { if (soundItem) { delete soundItem; for (uint i = 0; i < _soundItems.size(); ++i) - if (_soundItems[i]->_soundFileHash == soundFileHash) { - _soundItems.remove_at(i); + if (_soundItems[i] == soundItem) { + _soundItems[i] = NULL; break; } } @@ -213,35 +280,41 @@ void SoundMan::update() { for (uint i = 0; i < _soundItems.size(); ++i) { SoundItem *soundItem = _soundItems[i]; - if (soundItem->_playOnceAfterCountdown) { - if (soundItem->_currCountdown == 0) { - soundItem->_currCountdown = soundItem->_initialCountdown; - } else if (--soundItem->_currCountdown == 0) { - soundItem->_soundResource->play(); + if (soundItem) { + if (soundItem->_playOnceAfterCountdown) { + if (soundItem->_currCountdown == 0) { + soundItem->_currCountdown = soundItem->_initialCountdown; + } else if (--soundItem->_currCountdown == 0) { + soundItem->_soundResource->play(); + } + } else if (soundItem->_playOnceAfterRandomCountdown) { + if (soundItem->_currCountdown == 0) { + if (soundItem->_minCountdown > 0 && soundItem->_maxCountdown > 0 && soundItem->_minCountdown < soundItem->_maxCountdown) + soundItem->_currCountdown = _vm->_rnd->getRandomNumberRng(soundItem->_minCountdown, soundItem->_maxCountdown); + } else if (--soundItem->_currCountdown == 0) { + soundItem->_soundResource->play(); + } + } else if (soundItem->_playLooping && !soundItem->_soundResource->isPlaying()) { + soundItem->_soundResource->play(); // TODO Looping parameter? } - } else if (soundItem->_playOnceAfterRandomCountdown) { - if (soundItem->_currCountdown == 0) { - if (soundItem->_minCountdown > 0 && soundItem->_maxCountdown > 0 && soundItem->_minCountdown < soundItem->_maxCountdown) - soundItem->_currCountdown = _vm->_rnd->getRandomNumberRng(soundItem->_minCountdown, soundItem->_maxCountdown); - } else if (--soundItem->_currCountdown == 0) { - soundItem->_soundResource->play(); - } - } else if (soundItem->_playLooping && !soundItem->_soundResource->isPlaying()) { - soundItem->_soundResource->play(); // TODO Looping parameter? } } for (uint i = 0; i < _musicItems.size(); ++i) { MusicItem *musicItem = _musicItems[i]; - if (musicItem->_countdown) { - --musicItem->_countdown; - } else if (musicItem->_play && !musicItem->_musicResource->isPlaying()) { - musicItem->_musicResource->play(musicItem->_fadeVolumeStep); - musicItem->_fadeVolumeStep = 0; - } else if (musicItem->_stop) { - musicItem->_musicResource->stop(musicItem->_fadeVolumeStep); - musicItem->_fadeVolumeStep = 0; - musicItem->_stop = false; + if (musicItem) { + if (musicItem->_countdown) { + --musicItem->_countdown; + } else if (musicItem->_play && !musicItem->_musicResource->isPlaying()) { + debug("SoundMan: play music %08X (fade %d)", musicItem->_musicFileHash, musicItem->_fadeVolumeStep); + musicItem->_musicResource->play(musicItem->_fadeVolumeStep); + musicItem->_fadeVolumeStep = 0; + } else if (musicItem->_stop) { + debug("SoundMan: stop music %08X (fade %d)", musicItem->_musicFileHash, musicItem->_fadeVolumeStep); + musicItem->_musicResource->stop(musicItem->_fadeVolumeStep); + musicItem->_fadeVolumeStep = 0; + musicItem->_stop = false; + } } } @@ -253,11 +326,11 @@ void SoundMan::deleteGroup(uint32 nameHash) { } void SoundMan::deleteMusicGroup(uint32 nameHash) { - for (int index = _musicItems.size() - 1; index >= 0; --index) { + for (uint index = 0; index < _musicItems.size(); ++index) { MusicItem *musicItem = _musicItems[index]; - if (musicItem->_nameHash == nameHash) { + if (musicItem && musicItem->_nameHash == nameHash) { delete musicItem; - _musicItems.remove_at(index); + _musicItems[index] = NULL; } } } @@ -276,11 +349,11 @@ void SoundMan::deleteSoundGroup(uint32 nameHash) { _soundIndex2 = -1; } - for (int index = _soundItems.size() - 1; index >= 0; --index) { + for (uint index = 0; index < _soundItems.size(); ++index) { soundItem = _soundItems[index]; - if (soundItem->_nameHash == nameHash) { + if (soundItem && soundItem->_nameHash == nameHash) { delete soundItem; - _soundItems.remove_at(index); + _soundItems[index] = NULL; } } @@ -359,21 +432,327 @@ void SoundMan::setSoundThreePlayFlag(bool playOnceAfterCountdown) { MusicItem *SoundMan::getMusicItemByHash(uint32 musicFileHash) { for (uint i = 0; i < _musicItems.size(); ++i) - if (_musicItems[i]->_musicFileHash == musicFileHash) + if (_musicItems[i] && _musicItems[i]->_musicFileHash == musicFileHash) return _musicItems[i]; return NULL; } SoundItem *SoundMan::getSoundItemByHash(uint32 soundFileHash) { for (uint i = 0; i < _soundItems.size(); ++i) - if (_soundItems[i]->_soundFileHash == soundFileHash) + if (_soundItems[i] && _soundItems[i]->_soundFileHash == soundFileHash) return _soundItems[i]; return NULL; } +int16 SoundMan::addMusicItem(MusicItem *musicItem) { + return 0; // TODO +} + +int16 SoundMan::addSoundItem(SoundItem *soundItem) { + for (uint i = 0; i < _soundItems.size(); ++i) + if (!_soundItems[i]) { + _soundItems[i] = soundItem; + return i; + } + int16 soundIndex = _soundItems.size(); + _soundItems.push_back(soundItem); + return soundIndex; +} + void SoundMan::deleteSoundByIndex(int index) { delete _soundItems[index]; - _soundItems.remove_at(index); + _soundItems[index] = NULL; +} + +// NeverhoodAudioStream + +NeverhoodAudioStream::NeverhoodAudioStream(int rate, byte shiftValue, bool isLooping, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream) + : _rate(rate), _shiftValue(shiftValue), _isLooping(isLooping), _isStereo(false), _stream(stream, disposeStream), _endOfData(false), _buffer(0), + _isCompressed(_shiftValue != 0xFF), _prevValue(0) { + // Setup our buffer for readBuffer + _buffer = new byte[kSampleBufferLength * (_isCompressed ? 1 : 2)]; + assert(_buffer); +} + +NeverhoodAudioStream::~NeverhoodAudioStream() { + delete[] _buffer; +} + +int NeverhoodAudioStream::readBuffer(int16 *buffer, const int numSamples) { + int samplesLeft = numSamples; + + while (samplesLeft > 0 && !_endOfData) { + + const int maxSamples = MIN<int>(kSampleBufferLength, samplesLeft); + const int bytesToRead = maxSamples * (_isCompressed ? 1 : 2); + int bytesRead = _stream->read(_buffer, bytesToRead); + int samplesRead = bytesRead / (_isCompressed ? 1 : 2); + + samplesLeft -= samplesRead; + + const byte *src = _buffer; + if (_isCompressed) { + while (samplesRead--) { + _prevValue += (int8)(*src++); + *buffer++ = _prevValue << _shiftValue; + } + } else { + memcpy(buffer, _buffer, bytesRead); + buffer += bytesRead; + } + + if (bytesRead < bytesToRead || _stream->pos() >= _stream->size() || _stream->err() || _stream->eos()) { + if (_isLooping) + _stream->seek(0); + else + _endOfData = true; + } + + } + + return numSamples - samplesLeft; +} + +AudioResourceMan::AudioResourceMan(NeverhoodEngine *vm) + : _vm(vm) { +} + +AudioResourceMan::~AudioResourceMan() { +} + +int16 AudioResourceMan::addSound(uint32 fileHash) { + AudioResourceManSoundItem *soundItem = new AudioResourceManSoundItem(); + soundItem->_resourceHandle = _vm->_res->useResource(fileHash); + soundItem->_fileHash = fileHash; + soundItem->_data = NULL; + soundItem->_isLoaded = false; + soundItem->_isPlaying = false; + soundItem->_volume = 100; + soundItem->_panning = 50; + + for (uint i = 0; i < _soundItems.size(); ++i) + if (!_soundItems[i]) { + _soundItems[i] = soundItem; + return i; + } + + int16 soundIndex = (int16)_soundItems.size(); + _soundItems.push_back(soundItem); + return soundIndex; +} + +void AudioResourceMan::removeSound(int16 soundIndex) { + AudioResourceManSoundItem *soundItem = _soundItems[soundIndex]; + if (soundItem->_data) { + _vm->_res->unloadResource(soundItem->_resourceHandle); + soundItem->_data = NULL; + } + if (soundItem->_resourceHandle != 1) { + _vm->_res->unuseResource(soundItem->_resourceHandle); + soundItem->_resourceHandle = -1; + } + if (_vm->_mixer->isSoundHandleActive(soundItem->_soundHandle)) + _vm->_mixer->stopHandle(soundItem->_soundHandle); + delete soundItem; + _soundItems[soundIndex] = NULL; +} + +void AudioResourceMan::loadSound(int16 soundIndex) { + AudioResourceManSoundItem *soundItem = _soundItems[soundIndex]; + if (!soundItem->_data) { + // TODO Check if it's a sound resource + soundItem->_data = _vm->_res->loadResource(soundItem->_resourceHandle); + } +} + +void AudioResourceMan::unloadSound(int16 soundIndex) { + AudioResourceManSoundItem *soundItem = _soundItems[soundIndex]; + if (soundItem->_data) { + _vm->_res->unloadResource(soundItem->_resourceHandle); + soundItem->_data = NULL; + } +} + +void AudioResourceMan::setSoundVolume(int16 soundIndex, int16 volume) { + AudioResourceManSoundItem *soundItem = _soundItems[soundIndex]; + soundItem->_volume = MIN<int16>(volume, 100); + if (soundItem->_isPlaying && _vm->_mixer->isSoundHandleActive(soundItem->_soundHandle)) + _vm->_mixer->setChannelVolume(soundItem->_soundHandle, VOLUME(soundItem->_volume)); +} + +void AudioResourceMan::setSoundPan(int16 soundIndex, int16 pan) { + AudioResourceManSoundItem *soundItem = _soundItems[soundIndex]; + soundItem->_panning = MIN<int16>(pan, 100); + if (soundItem->_isPlaying && _vm->_mixer->isSoundHandleActive(soundItem->_soundHandle)) + _vm->_mixer->setChannelVolume(soundItem->_soundHandle, PANNING(soundItem->_panning)); +} + +void AudioResourceMan::playSound(int16 soundIndex, bool looping) { + AudioResourceManSoundItem *soundItem = _soundItems[soundIndex]; + if (!soundItem->_data) + loadSound(soundIndex); + + uint32 soundSize = _vm->_res->getResourceSize(soundItem->_resourceHandle); + Common::MemoryReadStream *stream = new Common::MemoryReadStream(soundItem->_data, soundSize, DisposeAfterUse::NO); + byte *shiftValue = _vm->_res->getResourceExtData(soundItem->_resourceHandle); + NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, false, DisposeAfterUse::YES, stream); + + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &soundItem->_soundHandle, + audioStream, -1, VOLUME(soundItem->_volume), PANNING(soundItem->_panning)); + + debug("playing sound %08X", soundItem->_fileHash); + + soundItem->_isPlaying = true; + +} + +void AudioResourceMan::stopSound(int16 soundIndex) { + AudioResourceManSoundItem *soundItem = _soundItems[soundIndex]; + if (_vm->_mixer->isSoundHandleActive(soundItem->_soundHandle)) + _vm->_mixer->stopHandle(soundItem->_soundHandle); + soundItem->_isPlaying = false; +} + +bool AudioResourceMan::isSoundPlaying(int16 soundIndex) { + AudioResourceManSoundItem *soundItem = _soundItems[soundIndex]; + return soundItem->_isPlaying; +} + +int16 AudioResourceMan::loadMusic(uint32 fileHash) { + + AudioResourceManMusicItem *musicItem; + + for (uint i = 0; i < _musicItems.size(); ++i) { + musicItem = _musicItems[i]; + if (musicItem && musicItem->_fileHash == fileHash && musicItem->_remove) { + musicItem->_remove = false; + musicItem->_isFadingOut = false; + musicItem->_isFadingIn = true; + return i; + } + } + + musicItem = new AudioResourceManMusicItem(); + musicItem->_fileHash = fileHash; + musicItem->_isPlaying = false; + musicItem->_remove = false; + musicItem->_volume = 100; + musicItem->_panning = 50; + musicItem->_start = false; + musicItem->_isFadingIn = false; + musicItem->_isFadingOut = false; + + for (uint i = 0; i < _musicItems.size(); ++i) { + if (!_musicItems[i]) { + _musicItems[i] = musicItem; + return i; + } + } + + int16 musicIndex = _musicItems.size(); + _musicItems.push_back(musicItem); + return musicIndex; + +} + +void AudioResourceMan::unloadMusic(int16 musicIndex) { + AudioResourceManMusicItem *musicItem = _musicItems[musicIndex]; + if (musicItem->_isFadingOut) { + musicItem->_remove = true; + } else { + if (_vm->_mixer->isSoundHandleActive(musicItem->_soundHandle)) + _vm->_mixer->stopHandle(musicItem->_soundHandle); + musicItem->_isPlaying = false; + _musicItems[musicIndex] = NULL; + } +} + +void AudioResourceMan::setMusicVolume(int16 musicIndex, int16 volume) { + AudioResourceManMusicItem *musicItem = _musicItems[musicIndex]; + musicItem->_volume = MIN<int16>(volume, 100); + if (musicItem->_isPlaying && _vm->_mixer->isSoundHandleActive(musicItem->_soundHandle)) + _vm->_mixer->setChannelVolume(musicItem->_soundHandle, VOLUME(musicItem->_volume)); +} + +void AudioResourceMan::playMusic(int16 musicIndex, int16 fadeVolumeStep) { + AudioResourceManMusicItem *musicItem = _musicItems[musicIndex]; + if (!musicItem->_isPlaying) { + musicItem->_isFadingIn = false; + musicItem->_isFadingOut = false; + if (fadeVolumeStep != 0) { + musicItem->_isFadingIn = true; + musicItem->_fadeVolume = 0; + musicItem->_fadeVolumeStep = fadeVolumeStep; + } + musicItem->_start = true; + } +} + +void AudioResourceMan::stopMusic(int16 musicIndex, int16 fadeVolumeStep) { + AudioResourceManMusicItem *musicItem = _musicItems[musicIndex]; + if (_vm->_mixer->isSoundHandleActive(musicItem->_soundHandle)) { + if (fadeVolumeStep != 0) { + if (musicItem->_isFadingIn) + musicItem->_isFadingIn = false; + else + musicItem->_fadeVolume = musicItem->_volume; + musicItem->_isFadingOut = true; + musicItem->_fadeVolumeStep = fadeVolumeStep; + } else { + _vm->_mixer->stopHandle(musicItem->_soundHandle); + } + musicItem->_isPlaying = false; + } +} + +bool AudioResourceMan::isMusicPlaying(int16 musicIndex) { + AudioResourceManMusicItem *musicItem = _musicItems[musicIndex]; + return musicItem->_isPlaying; +} + +void AudioResourceMan::updateMusicItem(int16 musicIndex) { + AudioResourceManMusicItem *musicItem = _musicItems[musicIndex]; + + if (musicItem->_start && !_vm->_mixer->isSoundHandleActive(musicItem->_soundHandle)) { + Common::SeekableReadStream *stream = _vm->_res->createStream(musicItem->_fileHash); + byte *shiftValue = _vm->_res->getResourceExtDataByHash(musicItem->_fileHash); + NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, true, DisposeAfterUse::YES, stream); + _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &musicItem->_soundHandle, + audioStream, -1, VOLUME(musicItem->_isFadingIn ? musicItem->_fadeVolume : musicItem->_volume), + PANNING(musicItem->_panning)); + musicItem->_start = false; + musicItem->_isPlaying = true; + } + + if (_vm->_mixer->isSoundHandleActive(musicItem->_soundHandle)) { + if (musicItem->_isFadingIn) { + musicItem->_fadeVolume += musicItem->_fadeVolumeStep; + if (musicItem->_fadeVolume >= musicItem->_volume) { + musicItem->_fadeVolume = musicItem->_volume; + musicItem->_isFadingIn = false; + } + _vm->_mixer->setChannelVolume(musicItem->_soundHandle, VOLUME(musicItem->_fadeVolume)); + } + if (musicItem->_isFadingOut) { + musicItem->_fadeVolume -= musicItem->_fadeVolumeStep; + if (musicItem->_fadeVolume < 0) + musicItem->_fadeVolume = 0; + _vm->_mixer->setChannelVolume(musicItem->_soundHandle, VOLUME(musicItem->_fadeVolume)); + if (musicItem->_fadeVolume == 0) { + musicItem->_isFadingOut = false; + stopMusic(musicIndex, 0); + if (musicItem->_remove) + unloadMusic(musicIndex); + } + } + } + +} + +void AudioResourceMan::update() { + for (uint i = 0; i < _musicItems.size(); ++i) + if (_musicItems[i]) + updateMusicItem(i); } } // End of namespace Neverhood |