diff options
-rw-r--r-- | sword2/driver/d_sound.cpp | 1336 | ||||
-rw-r--r-- | sword2/driver/d_sound.h | 124 | ||||
-rw-r--r-- | sword2/driver/driver96.h | 20 | ||||
-rw-r--r-- | sword2/sound.cpp | 7 |
4 files changed, 731 insertions, 756 deletions
diff --git a/sword2/driver/d_sound.cpp b/sword2/driver/d_sound.cpp index 563433eac4..0310978eed 100644 --- a/sword2/driver/d_sound.cpp +++ b/sword2/driver/d_sound.cpp @@ -39,9 +39,6 @@ namespace Sword2 { -// Fade-out takes half a second. This may need some tuning. -#define FADE_SAMPLES 11025 - static File fpMus; // Decompression macros @@ -49,12 +46,142 @@ static File fpMus; #define GetCompressedSign(n) (((n) >> 3) & 1) #define GetCompressedAmplitude(n) ((n) & 7) -static int32 musicVolTable[17] = { +static void premix_proc(void *param, int16 *data, uint len) { + ((Sound *) param)->fxServer(data, len); +} + +Sound::Sound(Sword2Engine *vm) { + _vm = vm; + _mutex = _vm->_system->create_mutex(); + + memset(_fx, 0, sizeof(_fx)); + + _soundOn = true; + + _speechStatus = false; + _speechPaused = false; + _speechMuted = false; + _speechVol = 14; + + _fxPaused = false; + _fxMuted = false; + _fxVol = 14; + + _musicVol = 16; + _musicMuted = false; + + _converter = makeRateConverter(_music[0].getRate(), _vm->_mixer->getOutputRate(), _music[0].isStereo(), false); + + _vm->_mixer->setupPremix(premix_proc, this); +} + +Sound::~Sound() { + _vm->_mixer->setupPremix(0, 0); + if (_mutex) + _vm->_system->delete_mutex(_mutex); +} + +void Sound::fxServer(int16 *data, uint len) { + Common::StackLock lock(_mutex); + + if (!_soundOn) + return; + + updateCompSampleStreaming(data, len); + + if (!_music[0]._streaming && !_music[1]._streaming && fpMus.isOpen()) + fpMus.close(); +} + +/** + * This function creates the pan table. + */ + +// FIXME: Could we use the FLAG_REVERSE_STEREO mixer flag instead? + +void Sound::buildPanTable(bool reverse) { + int i; + + for (i = 0; i < 16; i++) { + _panTable[i] = -127 + i * 8; + _panTable[i + 17] = (i + 1) * 8 - 1; + } + + _panTable[16] = 0; + + if (reverse) { + for (i = 0; i < 33; i++) + _panTable[i] = -_panTable[i]; + } +} + +// ---------------------------------------------------------------------------- +// MUSIC +// ---------------------------------------------------------------------------- + +// All music is encoded at 22050 Hz so this means fading takes 3 seconds. +#define FADE_SAMPLES 66150 + +int32 Sound::_musicVolTable[17] = { 0, 15, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255 }; -int16 MusicHandle::read() { +void MusicHandle::fadeDown(void) { + if (_fading < 0) + _fading = -_fading; + else if (_fading == 0) + _fading = FADE_SAMPLES; +} + +void MusicHandle::fadeUp(void) { + if (_fading > 0) + _fading = -_fading; + else if (_fading == 0) + _fading = -FADE_SAMPLES; +} + +int32 MusicHandle::play(const char *filename, uint32 musicId, bool looping) { + // The assumption here is that we are never playing music from two + // different files at the same time. + + if (!fpMus.isOpen()) + fpMus.open(filename); + + if (!fpMus.isOpen()) + return RDERR_INVALIDFILENAME; + + fpMus.seek((musicId + 1) * 8, SEEK_SET); + _fileStart = fpMus.readUint32LE(); + + uint32 len = fpMus.readUint32LE(); + + if (!_fileStart || !len) + return RDERR_INVALIDID; + + _fileEnd = _fileStart + len; + _filePos = _fileStart; + _streaming = true; + _firstTime = true; + _looping = looping; + fadeUp(); + return RD_OK; +} + +void MusicHandle::stop(void) { + _streaming = false; + _looping = false; + _fading = 0; +} + +int MusicHandle::readBuffer(int16 *buffer, const int numSamples) { + int samples; + for (samples = 0; samples < numSamples && !endOfData(); samples++) + *buffer++ = read(); + return samples; +} + +int16 MusicHandle::read(void) { uint8 in; uint16 delta; int16 out; @@ -89,11 +216,9 @@ int16 MusicHandle::read() { fpMus.seek(_filePos, SEEK_SET); } } else { - // Fade out at the end of the music. Is this really desirable - // behaviour? - - if (_fileEnd - _filePos <= FADE_SAMPLES) - _fading = _fileEnd - _filePos; + // Fade out at the end of the music, unless it already is. + if (_fileEnd - _filePos <= FADE_SAMPLES && _fading <= 0) + fadeDown(); } if (_fading > 0) { @@ -102,87 +227,107 @@ int16 MusicHandle::read() { _looping = false; } out = (out * _fading) / FADE_SAMPLES; + } else if (_fading < 0) { + _fading++; + out = (out * (FADE_SAMPLES + _fading)) / FADE_SAMPLES; } return out; } -bool MusicHandle::endOfData() const { +bool MusicHandle::endOfData(void) const { return (!_streaming || _filePos >= _fileEnd); } -static void premix_proc(void *param, int16 *data, uint len) { - ((Sound *) param)->fxServer(data, len); +/** + * Mutes/Unmutes the music. + * @param mute If mute is false, restore the volume to the last set master + * level. Otherwise the music is muted (volume 0). + */ + +void Sound::muteMusic(bool mute) { + _musicMuted = mute; } -Sound::Sound(Sword2Engine *vm) { - _vm = vm; - _mutex = _vm->_system->create_mutex(); +/** + * @return the music's mute state, true if mute, false if not mute + */ - _soundOn = false; - _speechStatus = false; - _fxPaused = false; - _speechPaused = false; - _speechVol = 14; - _fxVol = 14; - _speechMuted = 0; - _fxMuted = 0; - _musicVol = 16; +bool Sound::isMusicMute(void) { + return _musicMuted; +} - _musicMuted = 0; +/** + * Set the volume of any future as well as playing music. + * @param volume volume, from 0 (silent) to 16 (max) + */ - memset(_fx, 0, sizeof(_fx)); +void Sound::setMusicVolume(uint8 volume) { + if (volume > 16) + volume = 16; - _soundOn = true; + _musicVol = volume; +} - _converter = makeRateConverter(_music[0].getRate(), _vm->_mixer->getOutputRate(), _music[0].isStereo(), false); +/** + * @return the volume setting for music + */ - _vm->_mixer->setupPremix(premix_proc, this); +uint8 Sound::getMusicVolume(void) { + return _musicVol; } -Sound::~Sound() { - _vm->_mixer->setupPremix(0, 0); - if (_mutex) - _vm->_system->delete_mutex(_mutex); -} +/** + * Stops the music dead in its tracks. Any music that is currently being + * streamed is paused. + */ -// -------------------------------------------------------------------------- -// This function reverse the pan table, thus reversing the stereo. -// -------------------------------------------------------------------------- +void Sound::pauseMusic(void) { + Common::StackLock lock(_mutex); -// FIXME: We could probably use the FLAG_REVERSE_STEREO mixer flag here. + if (_soundOn) { + for (int i = 0; i < MAXMUS; i++) + _music[i]._paused = true; + } +} /** - * This function creates the pan table. + * Restarts the music from where it was stopped. */ -void Sound::buildPanTable(bool reverse) { - int i; +void Sound::unpauseMusic(void) { + Common::StackLock lock(_mutex); - for (i = 0; i < 16; i++) { - _panTable[i] = -127 + i * 8; - _panTable[i + 17] = (i + 1) * 8 - 1; + if (_soundOn) { + for (int i = 0; i < MAXMUS; i++) + _music[i]._paused = false; } +} - _panTable[16] = 0; +/** + * Fades out and stops the music. + */ - if (reverse) { - for (i = 0; i < 33; i++) - _panTable[i] = -_panTable[i]; - } +void Sound::stopMusic(void) { + Common::StackLock lock(_mutex); + + for (int i = 0; i < MAXMUS; i++) + _music[i].fadeDown(); } -// Save/Restore information about current music so that we can restore it -// after the credits. +/** + * Save/Restore information about current music so that we can restore it + * after the credits. + */ -void Sound::saveMusicState() { +void Sound::saveMusicState(void) { Common::StackLock lock(_mutex); int saveStream; - if (_music[0]._streaming && !_music[0]._fading) { + if (_music[0]._streaming && _music[0]._fading <= 0) { saveStream = 0; - } else if (_music[1]._streaming && !_music[0]._fading) { + } else if (_music[1]._streaming && _music[0]._fading <= 0) { saveStream = 1; } else { _music[2]._streaming = false; @@ -204,7 +349,7 @@ void Sound::saveMusicState() { savedMusicFilename = NULL; } -void Sound::restoreMusicState() { +void Sound::restoreMusicState(void) { Common::StackLock lock(_mutex); int restoreStream; @@ -215,15 +360,14 @@ void Sound::restoreMusicState() { // Fade out any music that happens to be playing for (int i = 0; i < MAXMUS; i++) { - if (_music[i]._streaming && !_music[i]._fading) - _music[i]._fading = FADE_SAMPLES; + if (_music[i]._streaming) + _music[i].fadeDown(); } - if (!_music[0]._streaming && !_music[0]._fading) { + if (!_music[0]._streaming) restoreStream = 0; - } else { + else restoreStream = 1; - } _music[restoreStream]._streaming = true; _music[restoreStream]._fading = 0; @@ -232,6 +376,7 @@ void Sound::restoreMusicState() { _music[restoreStream]._filePos = _music[2]._filePos; _music[restoreStream]._fileEnd = _music[2]._fileEnd; _music[restoreStream]._lastSample = _music[2]._lastSample; + _music[restoreStream].fadeUp(); if (savedMusicFilename) { if (fpMus.isOpen()) @@ -264,41 +409,274 @@ void Sound::playLeadOut(uint8 *leadOut) { } } -// -------------------------------------------------------------------------- -// This function returns the index of the sound effect with the ID passed in. -// -------------------------------------------------------------------------- +/** + * Streams music from a cluster file. + * @param filename the file name of the music cluster file + * @param musicId the id of the music to stream + * @param looping true if the music is to loop back to the start + * @return RD_OK or an error code + */ -int32 Sound::getFxIndex(int32 id) { - for (int i = 0; i < MAXFX; i++) { - if (_fx[i]._id == id) - return i; +int32 Sound::streamCompMusic(const char *filename, uint32 musicId, bool looping) { + Common::StackLock lock(_mutex); + + int32 primaryStream = -1; + int32 secondaryStream = -1; + + // If both music streams are playing, that should mean one of them is + // fading out. Pick that one, and stop it completely. + + if (_music[0]._streaming && _music[1]._streaming) { + if (_music[0]._fading > 0) + primaryStream = 0; + else + primaryStream = 1; + + _music[primaryStream].stop(); } - return MAXFX; + // Pick the available music stream. If no music is playing it doesn't + // matter which we use, so pick the first one. + + if (_music[0]._streaming || _music[1]._streaming) { + if (_music[0]._streaming) { + primaryStream = 1; + secondaryStream = 0; + } else { + primaryStream = 0; + secondaryStream = 1; + } + } else + primaryStream = 0; + + // Don't start streaming if the volume is off. + if (isMusicMute()) + return RD_OK; + + // Start other music stream fading out + if (secondaryStream != -1) + _music[secondaryStream].fadeDown(); + + return _music[primaryStream].play(filename, musicId, looping); } -int32 Sound::isFxOpen(int32 id) { - // FIXME: This seems backwards to me, but changing it breaks sound. - return getFxIndex(id) == MAXFX; +void Sound::updateCompSampleStreaming(int16 *data, uint len) { + for (int i = 0; i < MAXMUS; i++) { + if (!_music[i]._streaming || _music[i]._paused) + continue; + + st_volume_t volume = _musicMuted ? 0 : _musicVolTable[_musicVol]; + + fpMus.seek(_music[i]._filePos, SEEK_SET); + _converter->flow(_music[i], data, len, volume, volume); + } + + // DipMusic(); } -void Sound::fxServer(int16 *data, uint len) { +int32 Sound::dipMusic(void) { + // disable this func for now + return RD_OK; + +/* + int32 len; + int32 readCursor, writeCursor; + int32 dwBytes1, dwBytes2; + int16 *sample; + int32 total = 0; + int32 i; + int32 status; + LPVOID lpv1, lpv2; + HRESULT hr = DS_OK; + LPDIRECTSOUNDBUFFER dsbMusic = NULL; + + int32 currentMusicVol = musicVolTable[musicVol]; + int32 minMusicVol; + + // Find which music buffer is currently playing + for (i = 0; i<MAXMUS && !dsbMusic; i++) + { + if (musStreaming[i] && musFading[i] == 0) + dsbMusic = lpDsbMus[i]; + } + + if ((!_musicMuted) && dsbMusic && (!_speechMuted) && (musicVol>2)) + { + minMusicVol = musicVolTable[musicVol - 3]; + + if (_speechStatus) + { + IDirectSoundBuffer_GetStatus(dsbSpeech, &status); + if ((hr = IDirectSoundBuffer_GetCurrentPosition(dsbMusic, &readCursor, &writeCursor)) != DS_OK) + return hr; + + len = 44100 / 12 ;// 12th of a second + + if ((hr = IDirectSoundBuffer_Lock(dsbMusic, readCursor, len, &lpv1, &dwBytes1, &lpv2, &dwBytes2, 0)) != DS_OK) + return hr; + + for (i = 0, sample = (int16*)lpv1; sample<(int16*)((int8*)lpv1+dwBytes1); sample+= 30, i++) // 60 samples + { + if (*sample>0) + total += *sample; + else + total -= *sample; + } + + total /= i; + + total = minMusicVol + ( ( (currentMusicVol - minMusicVol) * total ) / 8000); + + if (total > currentMusicVol) + total = currentMusicVol; + + IDirectSoundBuffer_SetVolume(dsbMusic, total); + + IDirectSoundBuffer_Unlock(dsbMusic,lpv1,dwBytes1,lpv2,dwBytes2); + } + else + { + IDirectSoundBuffer_GetVolume(dsbMusic, &total); + total += 50; + if (total > currentMusicVol) + total = currentMusicVol; + + IDirectSoundBuffer_SetVolume(dsbMusic, total); + } + } + + return hr; +*/ +} + +/** + * @return the time left for the current music, in seconds. + */ + +int32 Sound::musicTimeRemaining(void) { Common::StackLock lock(_mutex); + for (int i = 0; i < MAXMUS; i++) { + if (_music[i]._streaming && _music[i]._fading <= 0) + return (_music[i]._fileEnd - _music[i]._filePos) / _music[i].getRate(); + } + + return 0; +} + +// ---------------------------------------------------------------------------- +// SPEECH +// ---------------------------------------------------------------------------- + +/** + * Mutes/Unmutes the speech. + * @param mute If mute is false, restore the volume to the last set master + * level. Otherwise the speech is muted (volume 0). + */ + +void Sound::muteSpeech(bool mute) { + _speechMuted = mute; + + if (getSpeechStatus() == RDSE_SAMPLEPLAYING) { + byte volume = mute ? 0 : 16 * _speechVol; + + _vm->_mixer->setChannelVolume(_soundHandleSpeech, volume); + } +} + +/** + * @return the speech's mute state, true if mute, false if not mute + */ + +bool Sound::isSpeechMute(void) { + return _speechMuted; +} + +/** + * Set the volume of any future as well as playing speech samples. + * @param volume volume, from 0 (silent) to 14 (max) + */ + +void Sound::setSpeechVolume(uint8 volume) { + if (volume > 14) + volume = 14; + + _speechVol = volume; + + if (_soundHandleSpeech.isActive() && !_speechMuted && getSpeechStatus() == RDSE_SAMPLEPLAYING) { + _vm->_mixer->setChannelVolume(_soundHandleSpeech, 16 * _speechVol); + } +} + +/** + * @return the volume setting for speech + */ + +uint8 Sound::getSpeechVolume(void) { + return _speechVol; +} + +/** + * Stops the speech dead in its tracks. + */ + +void Sound::pauseSpeech(void) { + if (getSpeechStatus() == RDSE_SAMPLEPLAYING) { + _speechPaused = true; + _vm->_mixer->pauseHandle(_soundHandleSpeech, true); + } +} + +/** + * Restarts the speech from where it was stopped. + */ + +void Sound::unpauseSpeech(void) { + if (_speechPaused) { + _speechPaused = false; + _vm->_mixer->pauseHandle(_soundHandleSpeech, false); + } +} + +/** + * Stops the speech from playing. + */ + +int32 Sound::stopSpeech(void) { if (!_soundOn) - return; + return RD_OK; + + if (_speechStatus) { + _vm->_mixer->stopHandle(_soundHandleSpeech); + _speechStatus = false; + return RD_OK; + } + return RDERR_SPEECHNOTPLAYING; +} - updateCompSampleStreaming(data, len); +/** + * @return Either RDSE_SAMPLEPLAYING or RDSE_SAMPLEFINISHED + */ - if (!_music[0]._streaming && !_music[1]._streaming && fpMus.isOpen()) - fpMus.close(); +int32 Sound::getSpeechStatus(void) { + if (!_soundOn || !_speechStatus) + return RDSE_SAMPLEFINISHED; + + if (_speechPaused) + return RDSE_SAMPLEPLAYING; + + if (!_soundHandleSpeech.isActive()) { + _speechStatus = false; + return RDSE_SAMPLEFINISHED; + } + return RDSE_SAMPLEPLAYING; } /** * Returns either RDSE_QUIET or RDSE_SPEAKING */ -int32 Sound::amISpeaking() { +int32 Sound::amISpeaking(void) { if (!_speechMuted && !_speechPaused && _soundHandleSpeech.isActive()) return RDSE_SPEAKING; @@ -391,324 +769,116 @@ uint32 Sound::preFetchCompSpeech(const char *filename, uint32 speechid, uint16 * */ int32 Sound::playCompSpeech(const char *filename, uint32 speechid, uint8 vol, int8 pan) { + if (_speechMuted) + return RD_OK; + uint16 *data16; uint32 bufferSize; - if (!_speechMuted) { - if (getSpeechStatus() == RDERR_SPEECHPLAYING) - return RDERR_SPEECHPLAYING; + if (getSpeechStatus() == RDERR_SPEECHPLAYING) + return RDERR_SPEECHPLAYING; - bufferSize = preFetchCompSpeech(filename, speechid, &data16); + bufferSize = preFetchCompSpeech(filename, speechid, &data16); - // We don't know exactly what went wrong here. - if (bufferSize == 0) - return RDERR_OUTOFMEMORY; + // We don't know exactly what went wrong here. + if (bufferSize == 0) + return RDERR_OUTOFMEMORY; - // Modify the volume according to the master volume + // Modify the volume according to the master volume - byte volume = _speechMuted ? 0 : vol * _speechVol; - int8 p = _panTable[pan + 16]; + byte volume = _speechMuted ? 0 : vol * _speechVol; + int8 p = _panTable[pan + 16]; - // Start the speech playing + // Start the speech playing - _speechPaused = true; + _speechPaused = true; - uint32 flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_AUTOFREE; + uint32 flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_AUTOFREE; #ifndef SCUMM_BIG_ENDIAN - flags |= SoundMixer::FLAG_LITTLE_ENDIAN; + flags |= SoundMixer::FLAG_LITTLE_ENDIAN; #endif - _vm->_mixer->playRaw(&_soundHandleSpeech, data16, bufferSize, 22050, flags, -1, volume, p); + _vm->_mixer->playRaw(&_soundHandleSpeech, data16, bufferSize, 22050, flags, -1, volume, p); - _speechStatus = true; - } + _speechStatus = true; // DipMusic(); return RD_OK; } -/** - * Stops the speech from playing. - */ - -int32 Sound::stopSpeech(void) { - if (!_soundOn) - return RD_OK; - - if (_speechStatus) { - _vm->_mixer->stopHandle(_soundHandleSpeech); - _speechStatus = false; - return RD_OK; - } - return RDERR_SPEECHNOTPLAYING; -} - -/** - * @return Either RDSE_SAMPLEPLAYING or RDSE_SAMPLEFINISHED - */ - -int32 Sound::getSpeechStatus(void) { - if (!_soundOn || !_speechStatus) - return RDSE_SAMPLEFINISHED; - - if (_speechPaused) - return RDSE_SAMPLEPLAYING; - - if (!_soundHandleSpeech.isActive()) { - _speechStatus = false; - return RDSE_SAMPLEFINISHED; - } - return RDSE_SAMPLEPLAYING; -} +// ---------------------------------------------------------------------------- +// SOUND EFFECTS +// ---------------------------------------------------------------------------- /** - * Set the volume of any future as well as playing speech samples. - * @param volume volume, from 0 (silent) to 14 (max) + * @return the index of the sound effect with the ID passed in. */ -void Sound::setSpeechVolume(uint8 volume) { - if (volume > 14) - volume = 14; - - _speechVol = volume; - - if (_soundHandleSpeech.isActive() && !_speechMuted && getSpeechStatus() == RDSE_SAMPLEPLAYING) { - _vm->_mixer->setChannelVolume(_soundHandleSpeech, 16 * _speechVol); +int32 Sound::getFxIndex(int32 id) { + for (int i = 0; i < MAXFX; i++) { + if (_fx[i]._id == id) + return i; } -} - -/** - * @return the volume setting for speech - */ -uint8 Sound::getSpeechVolume() { - return _speechVol; + return MAXFX; } /** - * Mutes/Unmutes the speech. + * Mutes/Unmutes the sound effects. * @param mute If mute is false, restore the volume to the last set master - * level. Otherwise the speech is muted (volume 0). - */ - -void Sound::muteSpeech(bool mute) { - _speechMuted = mute; - - if (getSpeechStatus() == RDSE_SAMPLEPLAYING) { - byte volume = mute ? 0 : 16 * _speechVol; - - _vm->_mixer->setChannelVolume(_soundHandleSpeech, volume); - } -} - -/** - * @return the speech's mute state, true if mute, false if not mute + * level. Otherwise the sound effects are muted (volume 0). */ -bool Sound::isSpeechMute(void) { - return _speechMuted; -} +void Sound::muteFx(bool mute) { + _fxMuted = mute; -/** - * Stops the speech dead in its tracks. - */ + // Now update the volume of any fxs playing + for (int i = 0; i < MAXFX; i++) { + if (_fx[i]._id) { + byte volume = mute ? 0 : _fx[i]._volume * _fxVol; -void Sound::pauseSpeech(void) { - if (getSpeechStatus() == RDSE_SAMPLEPLAYING) { - _speechPaused = true; - _vm->_mixer->pauseHandle(_soundHandleSpeech, true); + _vm->_mixer->setChannelVolume(_fx[i]._handle, volume); + } } } /** - * Restarts the speech from where it was stopped. + * @return the sound effects's mute state, true if mute, false if not mute */ -void Sound::unpauseSpeech(void) { - if (_speechPaused) { - _speechPaused = false; - _vm->_mixer->pauseHandle(_soundHandleSpeech, false); - } +bool Sound::isFxMute(void) { + return _fxMuted; } /** - * This function opens a sound effect ready for playing. A unique id should be - * passed in so that each effect can be referenced individually. - * @param id the unique sound id - * @param data the WAV data - * @warning Zero is not a valid id + * @return the master volume setting for sound effects */ -int32 Sound::openFx(int32 id, uint8 *data) { - int32 i, fxi; - uint32 *data32 = NULL; - WavHeader *wav; - - wav = (WavHeader *) data; - - if (_soundOn) { - // Check for a valid id. - if (id == 0) - return RDERR_INVALIDID; - - // Check that the fx is not already open - for (i = 0; i < MAXFX; i++) { - if (_fx[i]._id == id) - return RDERR_FXALREADYOPEN; - } - - // Now choose a free slot for the fx - for (fxi = 0; fxi < MAXFX; fxi++) { - if (_fx[fxi]._id == 0) - break; - } - - if (fxi == MAXFX) { - // Expire the first sound effect that isn't currently - // playing. This usually shouldn't happen since the - // game engine manually clears all sound effects (at - // least except for lead-ins and lead-outs) when moving - // between rooms. - - for (fxi = 0; fxi < MAXFX; fxi++) { - if (!_fx[fxi]._handle.isActive()) - break; - } - - // Still no dice? I give up! - - if (fxi == MAXFX) { - warning("openFx: No free sound slots"); - return RDERR_NOFREEBUFFERS; - } - } - - // Set the sample size - search for the size of the data. - i = 0; - while (i < 100) { - if (*data == 'd') { - data32 = (uint32 *) data; - if (READ_UINT32(data32) == MKID('data')) - break; - } - i++; - data++; - } - - if (!data32) - return RDERR_INVALIDWAV; - - _fx[fxi]._bufSize = READ_LE_UINT32(data32 + 1); - - // Fill the speech buffer with data - if (_fx[fxi]._buf != NULL) - free(_fx[fxi]._buf); - _fx[fxi]._buf = (uint16 *) malloc(_fx[fxi]._bufSize); - memcpy(_fx[fxi]._buf, (uint8 *) (data32 + 2), _fx[fxi]._bufSize); - _fx[fxi]._flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_LITTLE_ENDIAN; - if (FROM_LE_16(wav->channels) == 2) - _fx[fxi]._flags |= SoundMixer::FLAG_STEREO; - - _fx[fxi]._rate = FROM_LE_16(wav->samplesPerSec); - _fx[fxi]._id = id; - } - return RD_OK; +uint8 Sound::getFxVolume(void) { + return _fxVol; } /** - * This function plays a sound effect. If the effect has already been opened - * then 'data' should be NULL, and the sound effect will simply be obtained - * from the id passed in. If the effect has not been opened, then the WAV data - * should be passed in 'data'. The sound effect will be closed when it has - * finished playing. - * @param id the sound id - * @param data either NULL or the WAV data - * @param vol volume, 0 (minimum) to 16 (maximum) - * @param pan panning, -16 (full left) to 16 (full right) - * @param type either RDSE_FXSPOT or RDSE_FXLOOP - * @warning Zero is not a valid id + * Set the master volume of all sound effects. The effects still have their + * own volume setting as well as the master volume. + * @param volume volume, from 0 (silent) to 14 (max) */ -int32 Sound::playFx(int32 id, uint8 *data, uint8 vol, int8 pan, uint8 type) { - int32 i, loop; - uint32 hr; - - if (type == RDSE_FXLOOP) - loop = 1; - else - loop = 0; - - if (_soundOn) { - if (data == NULL) { - i = getFxIndex(id); - if (i == MAXFX) { - warning("playFx(%d, %d, %d, %d) - Not open", id, vol, pan, type); - return RDERR_FXNOTOPEN; - } - if (loop == 1) - _fx[i]._flags |= SoundMixer::FLAG_LOOP; - else - _fx[i]._flags &= ~SoundMixer::FLAG_LOOP; - - _fx[i]._volume = vol; - - // Start the sound effect playing +void Sound::setFxVolume(uint8 volume) { + if (volume > 14) + volume = 14; - byte volume = _fxMuted ? 0 : vol * _fxVol; - int8 p = _panTable[pan + 16]; + _fxVol = volume; - _vm->_mixer->playRaw(&_fx[i]._handle, _fx[i]._buf, _fx[i]._bufSize, _fx[i]._rate, _fx[i]._flags, -1, volume, p); - } else { - if (type == RDSE_FXLEADIN || type == RDSE_FXLEADOUT) { - if (type == RDSE_FXLEADIN) - id = -2; - else - id = -1; - - hr = openFx(id, data); - if (hr != RD_OK) - return hr; - - i = getFxIndex(id); - if (i == MAXFX) { - warning("playFx(%d, %d, %d, %d) - Not found", id, vol, pan, type); - return RDERR_FXFUCKED; - } - _fx[i]._flags &= ~SoundMixer::FLAG_LOOP; - - byte volume = _musicMuted ? 0 : musicVolTable[_musicVol]; - - _vm->_mixer->playRaw(&_fx[i]._handle, _fx[i]._buf, _fx[i]._bufSize, _fx[i]._rate, _fx[i]._flags, -1, volume, 0); - } else { - hr = openFx(id, data); - if (hr != RD_OK) { - return hr; - } - - i = getFxIndex(id); - if (i == MAXFX) { - warning("playFx(%d, %d, %d, %d) - Not found", id, vol, pan, type); - return RDERR_FXFUCKED; - } - if (loop == 1) - _fx[i]._flags |= SoundMixer::FLAG_LOOP; - else - _fx[i]._flags &= ~SoundMixer::FLAG_LOOP; - _fx[i]._volume = vol; - - // Start the sound effect playing - - byte volume = _fxMuted ? 0 : vol * _fxVol; - int8 p = _panTable[pan + 16]; - - _vm->_mixer->playRaw(&_fx[i]._handle, _fx[i]._buf, _fx[i]._bufSize, _fx[i]._rate, _fx[i]._flags, -1, volume, p); - } - } - } + if (_fxMuted) + return; - return RD_OK; + // Now update the volume of any fxs playing + for (int i = 0; i < MAXFX; i++) + if (_fx[i]._id) + _vm->_mixer->setChannelVolume(_fx[i]._handle, _fx[i]._volume * _fxVol); } /** @@ -744,419 +914,241 @@ int32 Sound::setFxIdVolume(int32 id, uint8 vol) { return RDERR_FXNOTOPEN; _fx[i]._volume = vol; + if (!_fxMuted) _vm->_mixer->setChannelVolume(_fx[i]._handle, vol * _fxVol); return RD_OK; } -/** - * This function clears all of the sound effects which are currently open or - * playing, irrespective of type. - */ -void Sound::clearAllFx(void) { - if (!_soundOn) +void Sound::pauseFx(void) { + if (_fxPaused) return; for (int i = 0; i < MAXFX; i++) { - if (_fx[i]._id && _fx[i]._id != -1 && _fx[i]._id != -2) { - _vm->_mixer->stopHandle(_fx[i]._handle); - _fx[i]._id = 0; + if (_fx[i]._id) { + _vm->_mixer->pauseHandle(_fx[i]._handle, true); + _fx[i]._paused = true; + } else _fx[i]._paused = false; - if (_fx[i]._buf != NULL) { - free(_fx[i]._buf); - _fx[i]._buf = NULL; - } - _fx[i]._bufSize = 0; - _fx[i]._flags = 0; - } - } -} - -/** - * This function closes a sound effect which has been previously opened for - * playing. Sound effects must be closed when they are finished with, otherwise - * you will run out of sound effect buffers. - * @param id the id of the sound to close - */ - -int32 Sound::closeFx(int32 id) { - int i; - - if (!_soundOn) - return RD_OK; - - i = getFxIndex(id); - - if (i == MAXFX) - return RDERR_FXNOTOPEN; - - _vm->_mixer->stopHandle(_fx[i]._handle); - _fx[i]._id = 0; - _fx[i]._paused = false; - if (_fx[i]._buf != NULL) { - free(_fx[i]._buf); - _fx[i]._buf = NULL; } - _fx[i]._bufSize = 0; - _fx[i]._flags = 0; - return RD_OK; -} - -void Sound::pauseFx(void) { - if (!_fxPaused) { - for (int i = 0; i < MAXFX; i++) { - if (_fx[i]._id) { - _vm->_mixer->pauseHandle(_fx[i]._handle, true); - _fx[i]._paused = true; - } else - _fx[i]._paused = false; - } - _fxPaused = true; - } + _fxPaused = true; } void Sound::pauseFxForSequence(void) { - if (!_fxPaused) { - for (int i = 0; i < MAXFX; i++) { - if (_fx[i]._id && _fx[i]._id != -2) { - _vm->_mixer->pauseHandle(_fx[i]._handle, true); - _fx[i]._paused = true; - } else { - _fx[i]._paused = false; - } - } - _fxPaused = true; - } -} + if (_fxPaused) + return; -void Sound::unpauseFx(void) { - if (_fxPaused) { - for (int i = 0; i < MAXFX; i++) { - if (_fx[i]._paused && _fx[i]._id) { - _vm->_mixer->pauseHandle(_fx[i]._handle, false); - } - } - _fxPaused = false; + for (int i = 0; i < MAXFX; i++) { + if (_fx[i]._id && _fx[i]._id != -2) { + _vm->_mixer->pauseHandle(_fx[i]._handle, true); + _fx[i]._paused = true; + } else + _fx[i]._paused = false; } -} -/** - * @return the master volume setting for sound effects - */ - -uint8 Sound::getFxVolume() { - return _fxVol; + _fxPaused = true; } -/** - * Set the master volume of all sound effects. The effects still have their - * own volume setting as well as the master volume. - * @param volume volume, from 0 (silent) to 14 (max) - */ - -void Sound::setFxVolume(uint8 volume) { - if (volume > 14) - volume = 14; +void Sound::unpauseFx(void) { + if (!_fxPaused) + return; - _fxVol = volume; + for (int i = 0; i < MAXFX; i++) + if (_fx[i]._paused && _fx[i]._id) + _vm->_mixer->pauseHandle(_fx[i]._handle, false); - // Now update the volume of any fxs playing - for (int i = 0; i < MAXFX; i++) { - if (_fx[i]._id && !_fxMuted) - _vm->_mixer->setChannelVolume(_fx[i]._handle, _fx[i]._volume * _fxVol); - } + _fxPaused = false; } -/** - * Mutes/Unmutes the sound effects. - * @param mute If mute is false, restore the volume to the last set master - * level. Otherwise the sound effects are muted (volume 0). - */ - -void Sound::muteFx(bool mute) { - _fxMuted = mute; - - // Now update the volume of any fxs playing - for (int i = 0; i < MAXFX; i++) { - if (_fx[i]._id) { - byte volume = mute ? 0 : _fx[i]._volume * _fxVol; - - _vm->_mixer->setChannelVolume(_fx[i]._handle, volume); - } - } -} +bool Sound::isFxPlaying(int32 id) { + int i; -/** - * @return the sound effects's mute state, true if mute, false if not mute - */ + i = getFxIndex(id); + if (i == MAXFX) + return false; -bool Sound::isFxMute(void) { - return _fxMuted; + return _fx[i]._handle.isActive(); } /** - * Streams music from a cluster file. - * @param filename the file name of the music cluster file - * @param musicId the id of the music to stream - * @param looping true if the music is to loop back to the start - * @return RD_OK or an error code + * This function opens a sound effect ready for playing. A unique id should be + * passed in so that each effect can be referenced individually. + * @param id the unique sound id + * @param data the WAV data + * @warning Zero is not a valid id */ -int32 Sound::streamCompMusic(const char *filename, uint32 musicId, bool looping) { - Common::StackLock lock(_mutex); - - uint32 len; - int32 primaryStream = -1; - int32 secondaryStream = -1; - - // If both music streams are playing, that should mean one of them is - // fading out. Pick that one. - - if (_music[0]._streaming && _music[1]._streaming) { - if (_music[0]._fading) - primaryStream = 0; - else - primaryStream = 1; - - _music[primaryStream]._fading = 0; - _music[primaryStream]._streaming = false; - } - - // Pick the available music stream. If no music is playing it doesn't - // matter which we use, so pick the first one. - - if (_music[0]._streaming || _music[1]._streaming) { - if (_music[0]._streaming) { - primaryStream = 1; - secondaryStream = 0; - } else { - primaryStream = 0; - secondaryStream = 1; - } - } else - primaryStream = 0; - - // Save looping info and tune id - _music[primaryStream]._looping = looping; - _music[primaryStream]._id = musicId; - - // Don't start streaming if the volume is off. - if (isMusicMute()) +int32 Sound::openFx(int32 id, uint8 *data) { + if (!_soundOn) return RD_OK; - // The assumption here is that we are never playing music from two - // different files at the same time. - - if (!fpMus.isOpen()) - fpMus.open(filename); - - if (!fpMus.isOpen()) - return RDERR_INVALIDFILENAME; - - // Start other music stream fading out - if (secondaryStream != -1 && !_music[secondaryStream]._fading) - _music[secondaryStream]._fading = FADE_SAMPLES; - - fpMus.seek((musicId + 1) * 8, SEEK_SET); - _music[primaryStream]._fileStart = fpMus.readUint32LE(); - len = fpMus.readUint32LE(); - - if (!_music[primaryStream]._fileStart || !len) + if (id == 0) return RDERR_INVALIDID; - _music[primaryStream]._fileEnd = _music[primaryStream]._fileStart + len; - _music[primaryStream]._filePos = _music[primaryStream]._fileStart; - _music[primaryStream]._streaming = true; - _music[primaryStream]._firstTime = true; - - return RD_OK; -} - -void Sound::updateCompSampleStreaming(int16 *data, uint len) { - for (int i = 0; i < MAXMUS; i++) { - if (!_music[i]._streaming || _music[i]._paused) - continue; - - st_volume_t volume = _musicMuted ? 0 : musicVolTable[_musicVol]; + if (getFxIndex(id) != MAXFX) + return RDERR_FXALREADYOPEN; - fpMus.seek(_music[i]._filePos, SEEK_SET); - _converter->flow(_music[i], data, len, volume, volume); - } + // Find a free slot + int32 fxi = getFxIndex(0); - // DipMusic(); -} + if (fxi == MAXFX) { + warning("openFx: Running out of sound slots"); -int32 Sound::dipMusic() { - // disable this func for now - return RD_OK; + // There isn't any free sound handle available. This usually + // shouldn't happen, but if it does we expire the first sound + // effect that isn't currently playing. -/* - int32 len; - int32 readCursor, writeCursor; - int32 dwBytes1, dwBytes2; - int16 *sample; - int32 total = 0; - int32 i; - int32 status; - LPVOID lpv1, lpv2; - HRESULT hr = DS_OK; - LPDIRECTSOUNDBUFFER dsbMusic = NULL; + for (fxi = 0; fxi < MAXFX; fxi++) + if (!_fx[fxi]._handle.isActive()) + break; - int32 currentMusicVol = musicVolTable[musicVol]; - int32 minMusicVol; + // Still no dice? I give up! - // Find which music buffer is currently playing - for (i = 0; i<MAXMUS && !dsbMusic; i++) - { - if (musStreaming[i] && musFading[i] == 0) - dsbMusic = lpDsbMus[i]; + if (fxi == MAXFX) { + warning("openFx: No free sound slots"); + return RDERR_NOFREEBUFFERS; + } } - if ((!_musicMuted) && dsbMusic && (!_speechMuted) && (musicVol>2)) - { - minMusicVol = musicVolTable[musicVol - 3]; + if (READ_UINT32(data) != MKID('RIFF') || READ_UINT32(data + 8) != MKID('WAVE') || READ_UINT32(data + 12) != MKID('fmt ')) { + warning("openFx: Not a valid WAV file"); + return RDERR_INVALIDWAV; + } - if (_speechStatus) - { - IDirectSoundBuffer_GetStatus(dsbSpeech, &status); - if ((hr = IDirectSoundBuffer_GetCurrentPosition(dsbMusic, &readCursor, &writeCursor)) != DS_OK) - return hr; + _fx[fxi]._id = id; + _fx[fxi]._flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_LITTLE_ENDIAN; - len = 44100 / 12 ;// 12th of a second + if (READ_LE_UINT16(data + 22) == 2) + _fx[fxi]._flags |= SoundMixer::FLAG_STEREO; - if ((hr = IDirectSoundBuffer_Lock(dsbMusic, readCursor, len, &lpv1, &dwBytes1, &lpv2, &dwBytes2, 0)) != DS_OK) - return hr; + _fx[fxi]._rate = READ_LE_UINT16(data + 24); - for (i = 0, sample = (int16*)lpv1; sample<(int16*)((int8*)lpv1+dwBytes1); sample+= 30, i++) // 60 samples - { - if (*sample>0) - total += *sample; - else - total -= *sample; - } - - total /= i; - - total = minMusicVol + ( ( (currentMusicVol - minMusicVol) * total ) / 8000); + data += READ_UINT32(data + 16) + 20; - if (total > currentMusicVol) - total = currentMusicVol; + if (READ_UINT32(data) != MKID('data')) { + warning("openFx: WAV file has no 'data' chunk"); + return RDERR_INVALIDWAV; + } - IDirectSoundBuffer_SetVolume(dsbMusic, total); + _fx[fxi]._bufSize = READ_LE_UINT32(data + 4); - IDirectSoundBuffer_Unlock(dsbMusic,lpv1,dwBytes1,lpv2,dwBytes2); - } - else - { - IDirectSoundBuffer_GetVolume(dsbMusic, &total); - total += 50; - if (total > currentMusicVol) - total = currentMusicVol; + // Fill the speech buffer with data + free(_fx[fxi]._buf); + _fx[fxi]._buf = (uint16 *) malloc(_fx[fxi]._bufSize); + memcpy(_fx[fxi]._buf, data + 8, _fx[fxi]._bufSize); - IDirectSoundBuffer_SetVolume(dsbMusic, total); - } - } - - return hr; -*/ + return RD_OK; } /** - * @return the time left for the current music, in seconds. + * This function closes a sound effect which has been previously opened for + * playing. Sound effects must be closed when they are finished with, otherwise + * you will run out of sound effect buffers. + * @param id the id of the sound to close */ -int32 Sound::musicTimeRemaining() { - Common::StackLock lock(_mutex); - - for (int i = 0; i < MAXMUS; i++) { - if (_music[i]._streaming && !_music[i]._fading) - return (_music[i]._fileEnd - _music[i]._filePos) / 22050; - } +int32 Sound::closeFx(int32 id) { + int i; - return 0; -} + if (!_soundOn) + return RD_OK; -/** - * Fades out and stops the music. - */ + i = getFxIndex(id); -void Sound::stopMusic(void) { - Common::StackLock lock(_mutex); + if (i == MAXFX) + return RDERR_FXNOTOPEN; - for (int i = 0; i < MAXMUS; i++) { - if (_music[i]._streaming) - _music[i]._fading = FADE_SAMPLES; - else - _music[i]._looping = false; - } + stopFxHandle(i); + return RD_OK; } /** - * Stops the music dead in its tracks. Any music that is currently being - * streamed is paued. + * This function plays a sound effect. If the effect has already been opened + * then 'data' should be NULL, and the sound effect will simply be obtained + * from the id passed in. If the effect has not been opened, then the WAV data + * should be passed in 'data'. The sound effect will be closed when it has + * finished playing. + * @param id the sound id + * @param data either NULL or the WAV data + * @param vol volume, 0 (minimum) to 16 (maximum) + * @param pan panning, -16 (full left) to 16 (full right) + * @param type either RDSE_FXSPOT or RDSE_FXLOOP + * @warning Zero is not a valid id */ -void Sound::pauseMusic(void) { - Common::StackLock lock(_mutex); +int32 Sound::playFx(int32 id, uint8 *data, uint8 vol, int8 pan, uint8 type) { + if (!_soundOn) + return RD_OK; - if (_soundOn) { - for (int i = 0; i < MAXMUS; i++) - _music[i]._paused = _music[i]._streaming; - } -} + byte volume = _fxMuted ? 0 : vol * _fxVol; + int8 p = _panTable[pan + 16]; + int32 i, hr; -/** - * Restarts the music from where it was stopped. - */ + if (data) { + // All lead-ins and lead-outs I've heard are music, so we use + // the music volume setting for them. -void Sound::unpauseMusic(void) { - Common::StackLock lock(_mutex); + if (type == RDSE_FXLEADIN || type == RDSE_FXLEADOUT) { + id = (type == RDSE_FXLEADIN) ? -2 : -1; + volume = _musicMuted ? 0 : _musicVolTable[_musicVol]; + } - if (_soundOn) { - for (int i = 0; i < MAXMUS; i++) - _music[i]._paused = false; + hr = openFx(id, data); + if (hr != RD_OK) + return hr; } -} -/** - * Set the volume of any future as well as playing music. - * @param volume volume, from 0 (silent) to 16 (max) - */ + i = getFxIndex(id); -void Sound::setMusicVolume(uint8 volume) { - if (volume > 16) - volume = 16; + if (i == MAXFX) { + if (data) { + warning("playFx(%d, %d, %d, %d) - Not found", id, vol, pan, type); + return RDERR_FXFUCKED; + } else { + warning("playFx(%d, %d, %d, %d) - Not open", id, vol, pan, type); + return RDERR_FXNOTOPEN; + } + } - _musicVol = volume; -} + if (type == RDSE_FXLOOP) + _fx[i]._flags |= SoundMixer::FLAG_LOOP; + else + _fx[i]._flags &= ~SoundMixer::FLAG_LOOP; -/** - * @return the volume setting for music - */ + _fx[i]._volume = vol; -uint8 Sound::getMusicVolume() { - return _musicVol; -} + _vm->_mixer->playRaw(&_fx[i]._handle, _fx[i]._buf, _fx[i]._bufSize, _fx[i]._rate, _fx[i]._flags, -1, volume, p); -/** - * Mutes/Unmutes the music. - * @param mute If mute is false, restore the volume to the last set master - * level. Otherwise the music is muted (volume 0). - */ + return RD_OK; +} -void Sound::muteMusic(bool mute) { - _musicMuted = mute; +void Sound::stopFxHandle(int i) { + if (_fx[i]._id) { + _vm->_mixer->stopHandle(_fx[i]._handle); + free(_fx[i]._buf); + _fx[i]._id = 0; + _fx[i]._paused = false; + _fx[i]._flags = 0; + _fx[i]._bufSize = 0; + _fx[i]._buf = NULL; + } } /** - * @return the music's mute state, true if mute, false if not mute + * This function clears all of the sound effects which are currently open or + * playing, irrespective of type. */ -bool Sound::isMusicMute(void) { - return _musicMuted; +void Sound::clearAllFx(void) { + if (!_soundOn) + return; + + for (int i = 0; i < MAXFX; i++) + if (_fx[i]._id && _fx[i]._id != -1 && _fx[i]._id != -2) + stopFxHandle(i); } } // End of namespace Sword2 diff --git a/sword2/driver/d_sound.h b/sword2/driver/d_sound.h index 56580fc561..2e2899e921 100644 --- a/sword2/driver/d_sound.h +++ b/sword2/driver/d_sound.h @@ -46,7 +46,6 @@ struct FxHandle { class MusicHandle : public AudioInputStream { public: - uint32 _id; bool _firstTime; bool _streaming; bool _paused; @@ -57,26 +56,22 @@ public: int32 _fileEnd; uint16 _lastSample; - bool isStereo() const { return false; } - int getRate() const { return 22050; } + bool isStereo(void) const { return false; } + int getRate(void) const { return 22050; } - virtual int readBuffer(int16 *buffer, const int numSamples) { - int samples; - for (samples = 0; samples < numSamples && !endOfData(); samples++) { - *buffer++ = read(); - } - return samples; - } - - int16 read(); - bool endOfData() const; + void fadeDown(void); + void fadeUp(void); + int32 play(const char *filename, uint32 musicId, bool looping); + void stop(void); + virtual int readBuffer(int16 *buffer, const int numSamples); + int16 read(void); + bool endOfData(void) const; // This stream never 'ends' - bool endOfStream() const { return false; } + bool endOfStream(void) const { return false; } - MusicHandle() : _firstTime(false), - _streaming(false), _paused(false), _looping(false), - _fading(0), _fileStart(0), _filePos(0), _fileEnd(0), - _lastSample(0) {} + MusicHandle() : _firstTime(false), _streaming(false), _paused(false), + _looping(false), _fading(0), _fileStart(0), + _filePos(0), _fileEnd(0), _lastSample(0) {} }; class Sound { @@ -84,75 +79,80 @@ private: Sword2Engine *_vm; OSystem::MutexRef _mutex; - RateConverter *_converter; int32 _panTable[33]; + bool _soundOn; - FxHandle _fx[MAXFX]; + static int32 _musicVolTable[17]; MusicHandle _music[MAXMUS + 1]; + char *savedMusicFilename; + RateConverter *_converter; + bool _musicMuted; + uint8 _musicVol; - bool _soundOn; + void updateCompSampleStreaming(int16 *data, uint len); + int32 dipMusic(void); + + PlayingSoundHandle _soundHandleSpeech; bool _speechStatus; bool _speechPaused; - bool _fxPaused; - bool _musicMuted; bool _speechMuted; - bool _fxMuted; - uint8 _musicVol; uint8 _speechVol; - uint8 _fxVol; - PlayingSoundHandle _soundHandleSpeech; + FxHandle _fx[MAXFX]; + bool _fxPaused; + bool _fxMuted; + uint8 _fxVol; int32 getFxIndex(int32 id); - int32 dipMusic(); - - void updateCompSampleStreaming(int16 *data, uint len); - - char *savedMusicFilename; + void stopFxHandle(int i); public: Sound(Sword2Engine *vm); ~Sound(); + void fxServer(int16 *data, uint len); - int32 playCompSpeech(const char *filename, uint32 speechid, uint8 vol, int8 pan); - uint32 preFetchCompSpeech(const char *filename, uint32 speechid, uint16 **buf); - int32 amISpeaking(); - int32 stopSpeech(void); - int32 getSpeechStatus(void); - void pauseSpeech(void); - void unpauseSpeech(void); - int32 openFx(int32 id, uint8 *data); - int32 playFx(int32 id, uint8 *data, uint8 vol, int8 pan, uint8 type); - int32 closeFx(int32 id); - void clearAllFx(void); - void pauseFx(void); - void pauseFxForSequence(void); - void unpauseFx(void); + void buildPanTable(bool reverse); + + void muteMusic(bool mute); + bool isMusicMute(void); + void setMusicVolume(uint8 vol); + uint8 getMusicVolume(void); void pauseMusic(void); void unpauseMusic(void); - int32 streamCompMusic(const char *filename, uint32 musicId, bool looping); - void saveMusicState(); - void restoreMusicState(); + void stopMusic(void); + void saveMusicState(void); + void restoreMusicState(void); void playLeadOut(uint8 *leadOut); - int32 musicTimeRemaining(); - void buildPanTable(bool reverse); - uint8 getFxVolume(void); - uint8 getSpeechVolume(void); - uint8 getMusicVolume(void); - bool isMusicMute(void); - bool isFxMute(void); + int32 streamCompMusic(const char *filename, uint32 musicId, bool looping); + int32 musicTimeRemaining(void); + + void muteSpeech(bool mute); bool isSpeechMute(void); - void stopMusic(void); - void setFxVolume(uint8 vol); void setSpeechVolume(uint8 vol); - void setMusicVolume(uint8 vol); - void muteMusic(bool mute); + uint8 getSpeechVolume(void); + void pauseSpeech(void); + void unpauseSpeech(void); + int32 stopSpeech(void); + int32 getSpeechStatus(void); + int32 amISpeaking(void); + uint32 preFetchCompSpeech(const char *filename, uint32 speechid, uint16 **buf); + int32 playCompSpeech(const char *filename, uint32 speechid, uint8 vol, int8 pan); + void muteFx(bool mute); - void muteSpeech(bool mute); - int32 isFxOpen(int32 id); + bool isFxMute(void); + uint8 getFxVolume(void); + void setFxVolume(uint8 vol); int32 setFxIdVolumePan(int32 id, uint8 vol, int8 pan); int32 setFxIdVolume(int32 id, uint8 vol); + void pauseFx(void); + void pauseFxForSequence(void); + void unpauseFx(void); + bool isFxPlaying(int32 id); + int32 openFx(int32 id, uint8 *data); + int32 closeFx(int32 id); + int32 playFx(int32 id, uint8 *data, uint8 vol, int8 pan, uint8 type); + void clearAllFx(void); }; } // End of namespace Sword2 diff --git a/sword2/driver/driver96.h b/sword2/driver/driver96.h index 8c836ccecf..2af4808e49 100644 --- a/sword2/driver/driver96.h +++ b/sword2/driver/driver96.h @@ -218,26 +218,6 @@ struct SpriteInfo { uint8 *colourTable; // pointer to 16-byte colour table, only applicable to 16-col compression type }; -// This is the format of a .WAV file. Somewhere after this header is the -// string 'DATA' followed by an int32 size which is the size of the data. -// Following the size of the data is the data itself. - -struct WavHeader { - uint32 riff; - uint32 fileLength; - uint32 wavID; - uint32 format; - uint32 formatLen; - uint16 formatTag; - uint16 channels; - uint16 samplesPerSec; - uint16 avgBytesPerSec; - uint16 blockAlign; - uint16 unknown1; - uint16 unknown2; - uint16 bitsPerSample; -}; - // This is the structure which is passed to the sequence player. It includes // the smack to play, and any text lines which are to be displayed over the top // of the sequence. diff --git a/sword2/sound.cpp b/sword2/sound.cpp index 22061969d6..cf93f153f1 100644 --- a/sword2/sound.cpp +++ b/sword2/sound.cpp @@ -67,8 +67,10 @@ void Sword2Engine::processFxQueue(void) { break; case FX_SPOT2: // Once the Fx has finished remove it from the queue. - if (_sound->isFxOpen(i + 1)) + if (!_sound->isFxPlaying(i + 1)) { _fxQueue[i].resource = 0; + _sound->closeFx(i + 1); + } break; } } @@ -178,6 +180,7 @@ int32 Logic::fnPlayFx(int32 *params) { break; default: strcpy(type, "INVALID"); + break; } debug(0, "SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", _vm->fetchObjectName(params[0]), params[3], params[4], params[2], type); @@ -244,7 +247,7 @@ int32 Logic::fnPlayFx(int32 *params) { } if (_vm->_fxQueue[j].type == FX_LOOP) { - // play now, rather than in Process_fx_queue where it was + // play now, rather than in processFxQueue where it was // getting played again & again! _vm->triggerFx(j); } |