2015-03-15 16:52:55 -04:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2015-05-09 18:04:13 +02:00
|
|
|
*
|
2015-03-15 16:52:55 -04:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2015-05-09 18:04:13 +02:00
|
|
|
*
|
2015-03-15 16:52:55 -04:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2015-05-05 07:00:24 +02:00
|
|
|
#include "sherlock/sherlock.h"
|
2015-03-15 21:25:07 -04:00
|
|
|
#include "sherlock/sound.h"
|
2015-05-01 18:21:13 -10:00
|
|
|
#include "common/config-manager.h"
|
2015-05-11 01:04:04 +02:00
|
|
|
#include "audio/audiostream.h"
|
|
|
|
#include "common/algorithm.h"
|
|
|
|
#include "audio/mixer.h"
|
|
|
|
#include "audio/decoders/raw.h"
|
2015-03-15 16:52:55 -04:00
|
|
|
|
|
|
|
namespace Sherlock {
|
|
|
|
|
2015-05-11 01:04:04 +02:00
|
|
|
Sound::Sound(SherlockEngine *vm, Audio::Mixer *mixer): _vm(vm), _mixer(mixer) {
|
2015-05-01 18:21:13 -10:00
|
|
|
_digitized = false;
|
|
|
|
_music = false;
|
|
|
|
_voices = 0;
|
2015-03-16 22:42:19 -04:00
|
|
|
_playingEpilogue = false;
|
2015-03-21 20:25:15 -04:00
|
|
|
_diskSoundPlaying = false;
|
2015-05-05 07:00:24 +02:00
|
|
|
_soundPlaying = false;
|
|
|
|
_soundIsOn = &_soundPlaying;
|
2015-05-12 21:34:45 +02:00
|
|
|
_curPriority = 0;
|
2015-05-05 07:00:24 +02:00
|
|
|
|
|
|
|
_soundOn = true;
|
|
|
|
_musicOn = true;
|
|
|
|
_speechOn = true;
|
2015-05-11 01:04:04 +02:00
|
|
|
|
|
|
|
_vm->_res->addToCache("MUSIC.LIB");
|
2015-05-13 08:24:48 +02:00
|
|
|
_vm->_res->addToCache("TITLE.SND");
|
|
|
|
_vm->_res->addToCache("EPILOGUE.SND");
|
2015-05-11 01:04:04 +02:00
|
|
|
_vm->_res->addToCache("SND.SND");
|
2015-05-01 18:21:13 -10:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves sound-related settings
|
|
|
|
*/
|
|
|
|
void Sound::syncSoundSettings() {
|
|
|
|
_digitized = !ConfMan.getBool("mute");
|
|
|
|
_music = !ConfMan.getBool("mute") && !ConfMan.getBool("music_mute");
|
|
|
|
_voices = !ConfMan.getBool("mute") && !ConfMan.getBool("speech_mute") ? 1 : 0;
|
2015-03-15 21:25:07 -04:00
|
|
|
}
|
2015-03-15 16:52:55 -04:00
|
|
|
|
2015-03-20 19:44:32 -04:00
|
|
|
void Sound::loadSound(const Common::String &name, int priority) {
|
2015-05-12 21:34:45 +02:00
|
|
|
// No implementation required in ScummVM
|
2015-03-20 19:44:32 -04:00
|
|
|
}
|
|
|
|
|
2015-05-11 01:04:04 +02:00
|
|
|
char Sound::decodeSample(char sample, byte& prediction, int& step) {
|
|
|
|
char diff = ((sample & 0x07) << step);
|
|
|
|
|
|
|
|
if (sample & 0x08) {
|
|
|
|
if (prediction > diff)
|
|
|
|
prediction = prediction - ((sample & 0x07) << step);
|
|
|
|
else
|
|
|
|
prediction = 0;
|
|
|
|
} else {
|
|
|
|
if (prediction < 0xff - diff)
|
|
|
|
prediction = prediction + ((sample&0x07) << step);
|
|
|
|
else
|
|
|
|
prediction = 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((sample & 0x07) >= 5 && step < 3) {
|
|
|
|
step ++;
|
|
|
|
} else if ((sample & 0x07) == 0 && step > 0) {
|
|
|
|
step --;
|
|
|
|
}
|
|
|
|
|
|
|
|
return prediction;
|
|
|
|
}
|
|
|
|
|
2015-05-12 21:34:45 +02:00
|
|
|
bool Sound::playSound(const Common::String &name, WaitType waitType, int priority) {
|
|
|
|
stopSound();
|
2015-05-11 01:04:04 +02:00
|
|
|
|
|
|
|
Common::String filename = name;
|
|
|
|
if (!filename.contains('.'))
|
|
|
|
filename += ".SND";
|
2015-05-13 08:24:48 +02:00
|
|
|
|
2015-05-13 22:54:03 +02:00
|
|
|
Common::SeekableReadStream *stream = _vm->_res->load(filename);
|
2015-05-13 08:24:48 +02:00
|
|
|
|
|
|
|
if (!stream)
|
2015-05-13 22:54:03 +02:00
|
|
|
error("Unable to find sound file '%s'", filename.c_str());
|
2015-05-11 01:04:04 +02:00
|
|
|
|
|
|
|
stream->skip(2);
|
|
|
|
int size = stream->readUint32BE();
|
|
|
|
int rate = stream->readUint16BE();
|
|
|
|
byte *data = (byte *)malloc(size);
|
|
|
|
byte *ptr = data;
|
|
|
|
stream->read(ptr, size);
|
|
|
|
|
|
|
|
byte *decoded = (byte *)malloc((size - 1) * 2);
|
|
|
|
|
2015-05-12 21:34:45 +02:00
|
|
|
// +127 to eliminate the pop when the sound starts (signed vs unsigned PCM). Still does not help with the pop at the end
|
2015-05-11 01:04:04 +02:00
|
|
|
byte prediction = (ptr[0] & 0x0f) + 127;
|
|
|
|
int step = 0;
|
|
|
|
int counter = 0;
|
|
|
|
|
|
|
|
for(int i = 1; i < size; i++) {
|
|
|
|
decoded[counter++] = decodeSample((ptr[i]>>4)&0x0f, prediction, step);
|
|
|
|
decoded[counter++] = decodeSample((ptr[i]>>0)&0x0f, prediction, step);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(data);
|
|
|
|
|
2015-05-13 22:54:03 +02:00
|
|
|
#if 0
|
|
|
|
// Debug : used to dump files
|
|
|
|
Common::DumpFile outFile;
|
|
|
|
outFile.open(filename);
|
|
|
|
outFile.write(decoded, (size - 2) * 2);
|
|
|
|
outFile.flush();
|
|
|
|
outFile.close();
|
|
|
|
#endif
|
|
|
|
|
2015-05-11 01:04:04 +02:00
|
|
|
Audio::AudioStream *audioStream = Audio::makeRawStream(decoded, (size - 2) * 2, rate, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
|
|
|
|
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_effectsHandle, audioStream, -1, Audio::Mixer::kMaxChannelVolume);
|
|
|
|
_soundPlaying = true;
|
2015-05-12 21:34:45 +02:00
|
|
|
_curPriority = priority;
|
2015-05-11 01:04:04 +02:00
|
|
|
|
|
|
|
if (waitType == WAIT_RETURN_IMMEDIATELY) {
|
|
|
|
_diskSoundPlaying = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool retval = true;
|
|
|
|
do {
|
|
|
|
_vm->_events->pollEvents();
|
|
|
|
g_system->delayMillis(10);
|
|
|
|
if ((waitType == WAIT_KBD_OR_FINISH) && _vm->_events->kbHit()) {
|
|
|
|
retval = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (!_vm->shouldQuit() && _mixer->isSoundHandleActive(_effectsHandle));
|
|
|
|
|
|
|
|
_soundPlaying = false;
|
|
|
|
_mixer->stopHandle(_effectsHandle);
|
|
|
|
|
|
|
|
return retval;
|
2015-03-15 21:25:07 -04:00
|
|
|
}
|
2015-03-15 16:52:55 -04:00
|
|
|
|
2015-05-12 21:34:45 +02:00
|
|
|
void Sound::playLoadedSound(int bufNum, WaitType waitType) {
|
|
|
|
if (_mixer->isSoundHandleActive(_effectsHandle) && (_curPriority > _vm->_scene->_sounds[bufNum]._priority))
|
|
|
|
return;
|
2015-03-15 21:25:07 -04:00
|
|
|
|
2015-05-12 21:34:45 +02:00
|
|
|
stopSound();
|
|
|
|
playSound(_vm->_scene->_sounds[bufNum]._name, waitType, _vm->_scene->_sounds[bufNum]._priority);
|
2015-03-21 18:18:12 -04:00
|
|
|
|
2015-05-12 21:34:45 +02:00
|
|
|
return;
|
2015-03-15 21:25:07 -04:00
|
|
|
}
|
|
|
|
|
2015-03-22 09:51:37 -04:00
|
|
|
void Sound::freeLoadedSounds() {
|
2015-05-12 21:34:45 +02:00
|
|
|
// As sounds are played with DisposeAfterUse::YES, stopping the sounds also
|
|
|
|
// frees them
|
|
|
|
stopSound();
|
2015-03-15 21:25:07 -04:00
|
|
|
}
|
|
|
|
|
2015-03-16 22:42:19 -04:00
|
|
|
void Sound::stopSound() {
|
2015-05-12 21:34:45 +02:00
|
|
|
_mixer->stopHandle(_effectsHandle);
|
2015-03-16 22:42:19 -04:00
|
|
|
}
|
|
|
|
|
2015-03-15 21:25:07 -04:00
|
|
|
void Sound::playMusic(const Common::String &name) {
|
|
|
|
// TODO
|
2015-05-11 01:04:04 +02:00
|
|
|
warning("Sound::playMusic %s", name.c_str());
|
|
|
|
Common::SeekableReadStream *stream = _vm->_res->load(name, "MUSIC.LIB");
|
|
|
|
|
|
|
|
byte *data = new byte[stream->size()];
|
|
|
|
byte *ptr = data;
|
|
|
|
stream->read(ptr, stream->size());
|
|
|
|
Common::DumpFile outFile;
|
|
|
|
outFile.open(name + ".RAW");
|
|
|
|
outFile.write(data, stream->size());
|
|
|
|
outFile.flush();
|
|
|
|
outFile.close();
|
|
|
|
delete[] data;
|
|
|
|
|
|
|
|
stopMusic();
|
|
|
|
startSong();
|
2015-03-15 21:25:07 -04:00
|
|
|
}
|
2015-03-15 16:52:55 -04:00
|
|
|
|
2015-03-15 21:25:07 -04:00
|
|
|
void Sound::stopMusic() {
|
|
|
|
// TODO
|
2015-05-05 07:00:24 +02:00
|
|
|
warning("TODO: Sound::stopMusic");
|
2015-03-15 21:25:07 -04:00
|
|
|
}
|
|
|
|
|
2015-03-18 22:32:41 -04:00
|
|
|
int Sound::loadSong(int songNumber) {
|
|
|
|
// TODO
|
2015-05-05 07:00:24 +02:00
|
|
|
warning("TODO: Sound::loadSong");
|
2015-03-18 22:32:41 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sound::startSong() {
|
|
|
|
// TODO
|
2015-05-05 07:00:24 +02:00
|
|
|
warning("TODO: Sound::startSong");
|
2015-03-18 22:32:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sound::freeSong() {
|
|
|
|
// TODO
|
2015-05-05 07:00:24 +02:00
|
|
|
warning("TODO: Sound::freeSong");
|
2015-03-18 22:32:41 -04:00
|
|
|
}
|
|
|
|
|
2015-04-10 23:35:26 -05:00
|
|
|
void Sound::stopSndFuncPtr(int v1, int v2) {
|
|
|
|
// TODO
|
2015-05-05 07:00:24 +02:00
|
|
|
warning("TODO: Sound::stopSndFuncPtr");
|
2015-04-10 23:35:26 -05:00
|
|
|
}
|
|
|
|
|
2015-04-21 01:12:16 -05:00
|
|
|
void Sound::waitTimerRoland(uint time) {
|
|
|
|
// TODO
|
2015-05-05 07:00:24 +02:00
|
|
|
warning("TODO: Sound::waitTimerRoland");
|
2015-04-21 01:12:16 -05:00
|
|
|
}
|
2015-03-15 21:25:07 -04:00
|
|
|
|
2015-05-01 17:27:59 -10:00
|
|
|
void Sound::freeDigiSound() {
|
|
|
|
delete[] _digiBuf;
|
|
|
|
_digiBuf = nullptr;
|
|
|
|
_diskSoundPlaying = false;
|
2015-05-05 07:00:24 +02:00
|
|
|
_soundPlaying = false;
|
2015-05-01 17:27:59 -10:00
|
|
|
}
|
|
|
|
|
2015-03-15 21:25:07 -04:00
|
|
|
} // End of namespace Sherlock
|