mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 13:50:13 +00:00
NANCY: Sound manager improvements
The original engine stores sound information in at least four different types of structs, all of which can now be loaded and played back. Also implemented sound types, which will be configurable from the ScummVM interface, added the correct number of sound channels, and implemented sound fx in a couple of action record types.
This commit is contained in:
parent
c73ea1b0cb
commit
e4890a18fc
@ -66,6 +66,10 @@ void ActionManager::handleInput(NancyInput &input) {
|
||||
shouldTrigger = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldTrigger) {
|
||||
_engine->sound->playSound(17); // Hardcoded by original engine
|
||||
}
|
||||
} else {
|
||||
shouldTrigger = true;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/state/scene.h"
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/util.h"
|
||||
|
||||
#include "common/file.h"
|
||||
@ -85,19 +85,9 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
|
||||
UI::Textbox::assembleTextLine(rawText, text, 1500);
|
||||
delete[] rawText;
|
||||
|
||||
stream.read(name, 10);
|
||||
soundName = Common::String(name);
|
||||
soundChannelID = stream.readUint16LE();
|
||||
sound.read(stream, SoundManager::SoundDescription::kNormal);
|
||||
|
||||
stream.skip(8);
|
||||
|
||||
numRepeats = stream.readUint16LE();
|
||||
|
||||
stream.skip(4);
|
||||
|
||||
volume = stream.readUint16LE();
|
||||
|
||||
stream.skip(0x29);
|
||||
stream.skip(0x23);
|
||||
conditionalResponseCharacterID = stream.readByte();
|
||||
goodbyeResponseCharacterID = stream.readByte();
|
||||
numSceneChanges = stream.readByte();
|
||||
@ -180,8 +170,8 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
|
||||
case kBegin:
|
||||
init();
|
||||
registerGraphics();
|
||||
engine->sound->loadSound(soundName, soundChannelID, numRepeats, volume);
|
||||
engine->sound->pauseSound(soundChannelID, false);
|
||||
engine->sound->loadSound(sound);
|
||||
engine->sound->playSound(sound.channelID);
|
||||
state = kRun;
|
||||
// fall through
|
||||
case kRun:
|
||||
@ -205,7 +195,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!engine->sound->isSoundPlaying(soundChannelID)) {
|
||||
if (!engine->sound->isSoundPlaying(sound.channelID)) {
|
||||
if (responses.size() == 0) {
|
||||
// NPC has finished talking with no responses available, auto-advance to next scene
|
||||
state = kActionTrigger;
|
||||
@ -221,8 +211,11 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
|
||||
if (pickedResponse != -1) {
|
||||
// Player has picked response, play sound file and change state
|
||||
sceneChange = responses[pickedResponse].sceneChange;
|
||||
engine->sound->loadSound(responses[pickedResponse].soundName, soundChannelID, numRepeats, volume);
|
||||
engine->sound->pauseSound(soundChannelID, false);
|
||||
SoundManager::SoundDescription responseSound = sound;
|
||||
responseSound.name = responses[pickedResponse].soundName;
|
||||
// TODO this is probably not correct
|
||||
engine->sound->loadSound(responseSound);
|
||||
engine->sound->playSound(responseSound.channelID);
|
||||
state = kActionTrigger;
|
||||
}
|
||||
}
|
||||
@ -259,7 +252,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
|
||||
engine->scene->setEventFlag(responses[pickedResponse].flagDesc.label, responses[pickedResponse].flagDesc.flag);
|
||||
}
|
||||
|
||||
if (!engine->sound->isSoundPlaying(soundChannelID)) {
|
||||
if (!engine->sound->isSoundPlaying(sound.channelID)) {
|
||||
if (shouldPopScene) {
|
||||
// Exit dialogue
|
||||
engine->scene->popScene();
|
||||
@ -326,6 +319,7 @@ void PlayPrimaryVideoChan0::addGoodbye(NancyEngine *engine) {
|
||||
newResponse.text = file.readString();
|
||||
// response is picked randomly
|
||||
newResponse.sceneChange.sceneID = res.sceneIDs[engine->_rnd->getRandomNumber(3)];
|
||||
newResponse.sceneChange.doNotStartSound = true;
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ public:
|
||||
PlayPrimaryVideoChan0(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
|
||||
virtual ~PlayPrimaryVideoChan0();
|
||||
|
||||
virtual void init()override;
|
||||
virtual void init() override;
|
||||
virtual void updateGraphics() override;
|
||||
|
||||
virtual uint16 readData(Common::SeekableReadStream &stream) override;
|
||||
@ -80,10 +80,7 @@ public:
|
||||
// _screenPosition 0x2D
|
||||
Common::String text; // 0x3D
|
||||
|
||||
Common::String soundName; // 0x619, TODO make a proper soundDesc struct
|
||||
uint16 soundChannelID; // 0x623
|
||||
uint16 numRepeats; // 0x62D
|
||||
uint16 volume; // 0x633
|
||||
SoundManager::SoundDescription sound; // 0x619
|
||||
|
||||
byte conditionalResponseCharacterID; // 0x65E
|
||||
byte goodbyeResponseCharacterID; // 0x65F
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/graphics.h"
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/input.h"
|
||||
#include "engines/nancy/resource.h"
|
||||
#include "engines/nancy/util.h"
|
||||
@ -493,7 +493,7 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
|
||||
break;
|
||||
}
|
||||
case kActionTrigger:
|
||||
// TODO play sound
|
||||
engine->sound->playSound(24); // Hardcoded by original engine
|
||||
engine->scene->addItemToInventory(objectID);
|
||||
setVisible(false);
|
||||
hasHotspot = false;
|
||||
@ -503,15 +503,7 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
|
||||
}
|
||||
|
||||
uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
|
||||
char str[10];
|
||||
stream.read(str, 10);
|
||||
filename = Common::String(str);
|
||||
id = stream.readSint16LE();
|
||||
stream.skip(4);
|
||||
numLoops = stream.readUint16LE();
|
||||
stream.skip(4);
|
||||
volume = stream.readUint16LE();
|
||||
stream.skip(6);
|
||||
sound.read(stream, SoundManager::SoundDescription::kDIGI);
|
||||
SceneChange::readData(stream);
|
||||
return 0x2B;
|
||||
}
|
||||
@ -519,12 +511,12 @@ uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
|
||||
void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
|
||||
switch (state) {
|
||||
case kBegin:
|
||||
engine->sound->loadSound(filename, id, numLoops, volume);
|
||||
engine->sound->pauseSound(id, false);
|
||||
engine->sound->loadSound(sound);
|
||||
engine->sound->playSound(sound.channelID);
|
||||
state = kRun;
|
||||
break;
|
||||
case kRun:
|
||||
if (!engine->sound->isSoundPlaying(id)) {
|
||||
if (!engine->sound->isSoundPlaying(sound.channelID)) {
|
||||
state = kActionTrigger;
|
||||
}
|
||||
break;
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include "engines/nancy/commontypes.h"
|
||||
#include "engines/nancy/renderobject.h"
|
||||
|
||||
#include "engines/nancy/sound.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/array.h"
|
||||
#include "common/str.h"
|
||||
@ -349,10 +351,7 @@ public:
|
||||
virtual void execute(Nancy::NancyEngine *engine) override;
|
||||
// TODO subclass into Play and Stop (?)
|
||||
|
||||
Common::String filename;
|
||||
int16 id = -1; // 0xA
|
||||
uint16 numLoops = 0; // 0x10
|
||||
uint16 volume = 0; // 0x16, maximum is 65?
|
||||
SoundManager::SoundDescription sound;
|
||||
// ...
|
||||
// SceneChange elements at 0x1E
|
||||
};
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/util.h"
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/cursor.h"
|
||||
#include "engines/nancy/input.h"
|
||||
#include "engines/nancy/graphics.h"
|
||||
@ -212,13 +212,7 @@ uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
|
||||
}
|
||||
|
||||
triggerFlags.readData(stream);
|
||||
stream.read(name, 10);
|
||||
soundName = Common::String(name);
|
||||
soundChannel = stream.readUint16LE();
|
||||
stream.skip(0xE);
|
||||
soundVolume = stream.readUint16LE();
|
||||
|
||||
stream.skip(6);
|
||||
sound.read(stream, SoundManager::SoundDescription::kNormal);
|
||||
SceneChange::readData(stream);
|
||||
|
||||
uint16 numVideoDescs = stream.readUint16LE();
|
||||
@ -276,8 +270,8 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
|
||||
case kBegin:
|
||||
init();
|
||||
registerGraphics();
|
||||
if (soundName != "NO SOUND") {
|
||||
engine->sound->loadSound(soundName, soundChannel, 1, soundVolume);
|
||||
if (sound.name != "NO SOUND") {
|
||||
engine->sound->loadSound(sound);
|
||||
}
|
||||
state = kRun;
|
||||
// fall through
|
||||
@ -300,8 +294,8 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
|
||||
_screenPosition = videoDescs[activeFrame].destRect;
|
||||
|
||||
// Start sound if any
|
||||
if (soundName != "NO SOUND") {
|
||||
engine->sound->pauseSound(soundChannel, false);
|
||||
if (sound.name != "NO SOUND") {
|
||||
engine->sound->playSound(sound.channelID);
|
||||
}
|
||||
|
||||
setVisible(true);
|
||||
|
@ -104,9 +104,7 @@ public:
|
||||
FlagAtFrame frameFlags[15]; // 0x26
|
||||
EventFlagsDesc triggerFlags; // 0x80
|
||||
|
||||
Common::String soundName; // 0xA8
|
||||
uint16 soundChannel = 0; // 0xB2
|
||||
uint16 soundVolume = 0; // 0xC2
|
||||
SoundManager::SoundDescription sound; // 0xA8
|
||||
|
||||
// SceneChange data at 0xCA
|
||||
Common::Array<SecondaryVideoDesc> videoDescs; // 0xD4
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/resource.h"
|
||||
#include "engines/nancy/video.h"
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/iff.h"
|
||||
#include "engines/nancy/state/scene.h"
|
||||
|
||||
@ -270,7 +270,7 @@ bool NancyConsole::Cmd_playAudio(int argc, const char **argv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Audio::AudioStream *stream = makeHISStream(f, DisposeAfterUse::YES);
|
||||
Audio::AudioStream *stream = SoundManager::makeHISStream(f, DisposeAfterUse::YES);
|
||||
|
||||
if (!stream) {
|
||||
debugPrintf("Failed to load '%s.his'\n", argv[1]);
|
||||
|
@ -14,8 +14,7 @@ MODULE_OBJS = \
|
||||
ui/viewport.o \
|
||||
state/logo.o \
|
||||
state/map.o \
|
||||
state/scene.o\
|
||||
audio.o \
|
||||
state/scene.o \
|
||||
console.o \
|
||||
cursor.o \
|
||||
decompress.o \
|
||||
@ -27,6 +26,7 @@ MODULE_OBJS = \
|
||||
nancy.o \
|
||||
renderobject.o \
|
||||
resource.o \
|
||||
sound.o \
|
||||
video.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
|
@ -26,9 +26,9 @@
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/resource.h"
|
||||
#include "engines/nancy/iff.h"
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/input.h"
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/state/map.h"
|
||||
#include "engines/nancy/graphics.h"
|
||||
#include "engines/nancy/cursor.h"
|
||||
@ -248,6 +248,16 @@ Common::SeekableReadStream *NancyEngine::getBootChunkStream(const Common::String
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
void NancyEngine::stopAndUnloadSpecificSounds() {
|
||||
// TODO missing if
|
||||
|
||||
sound->stopSound(logo->MSNDchannelID);
|
||||
|
||||
for (uint i = 0; i < 10; ++i) {
|
||||
sound->stopSound(i);
|
||||
}
|
||||
}
|
||||
|
||||
void NancyEngine::clearBootChunks() {
|
||||
for (auto const& i : _bootChunks) {
|
||||
delete i._value;
|
||||
|
@ -124,6 +124,9 @@ public:
|
||||
|
||||
// Chunks found in BOOT get extracted and cached at startup, this function lets other classes access them
|
||||
Common::SeekableReadStream *getBootChunkStream(const Common::String &name);
|
||||
|
||||
// Used for state switching
|
||||
void stopAndUnloadSpecificSounds();
|
||||
|
||||
void setGameState(GameState state);
|
||||
GameState getGameState() const { return _gameFlow.minGameState; }
|
||||
|
@ -20,7 +20,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/nancy.h"
|
||||
|
||||
#include "common/system.h"
|
||||
@ -122,7 +122,7 @@ bool readHISHeader(Common::SeekableReadStream *stream, SoundType &type, uint16 &
|
||||
return true;
|
||||
}
|
||||
|
||||
Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
Audio::SeekableAudioStream *SoundManager::makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
char buf[22];
|
||||
|
||||
stream->read(buf, 22);
|
||||
@ -176,52 +176,159 @@ Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, Di
|
||||
return Audio::makeVorbisStream(subStream, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
void SoundManager::SoundDescription::read(Common::SeekableReadStream &stream, Type type) {
|
||||
char buf[10];
|
||||
|
||||
stream.read(buf, 10);
|
||||
name = buf;
|
||||
if (type == SoundDescription::kScene) {
|
||||
stream.skip(4);
|
||||
}
|
||||
channelID = stream.readUint16LE();
|
||||
|
||||
// The difference between these is a couple members found at the same position
|
||||
// whose purpose I don't understand, so for now just skip them
|
||||
switch (type) {
|
||||
case kNormal:
|
||||
stream.skip(8);
|
||||
break;
|
||||
case kMenu:
|
||||
stream.skip(6);
|
||||
break;
|
||||
case kScene:
|
||||
// fall through
|
||||
case kDIGI:
|
||||
stream.skip(4);
|
||||
break;
|
||||
}
|
||||
|
||||
numLoops = stream.readUint16LE();
|
||||
if (stream.readUint16LE() != 0) { // loop indefinitely
|
||||
numLoops = 0;
|
||||
}
|
||||
stream.skip(2);
|
||||
volume = stream.readUint16LE();
|
||||
stream.skip(6);
|
||||
}
|
||||
|
||||
SoundManager::SoundManager(NancyEngine *engine) :
|
||||
_engine(engine) {
|
||||
_mixer = _engine->_system->getMixer();
|
||||
|
||||
initSoundChannels();
|
||||
}
|
||||
|
||||
|
||||
void SoundManager::loadSound(Common::String &name, int16 id, uint16 numLoops, uint16 volume) {
|
||||
if (_mixer->isSoundHandleActive(handles[id])) {
|
||||
_mixer->stopHandle(handles[id]);
|
||||
}
|
||||
Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(name + ".his");
|
||||
if (mSnd) {
|
||||
Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
|
||||
if (aStr) {
|
||||
Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, numLoops);
|
||||
_engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handles[id], aStrLoop, -1, volume * 255 / 100);
|
||||
_engine->_system->getMixer()->pauseHandle(handles[id], true);
|
||||
names[id] = name;
|
||||
}
|
||||
}
|
||||
SoundManager::~SoundManager() {
|
||||
stopAllSounds();
|
||||
}
|
||||
|
||||
void SoundManager::pauseSound(int16 id, bool pause) {
|
||||
if (id < 0 || id > 20)
|
||||
uint16 SoundManager::loadSound(const SoundDescription &description) {
|
||||
if (_mixer->isSoundHandleActive(_channels[description.channelID].handle)) {
|
||||
_mixer->stopHandle(_channels[description.channelID].handle);
|
||||
}
|
||||
|
||||
delete _channels[description.channelID].stream;
|
||||
_channels[description.channelID].stream = nullptr;
|
||||
|
||||
_channels[description.channelID].name = description.name;
|
||||
_channels[description.channelID].numLoops = description.numLoops;
|
||||
_channels[description.channelID].volume = description.volume;
|
||||
|
||||
Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(description.name + ".his");
|
||||
if (file) {
|
||||
_channels[description.channelID].stream = makeHISStream(file, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
return description.channelID;
|
||||
}
|
||||
|
||||
void SoundManager::playSound(uint16 channelID) {
|
||||
if (channelID > 32 || _channels[channelID].stream == 0)
|
||||
return;
|
||||
|
||||
_engine->_system->getMixer()->pauseHandle(handles[id], pause);
|
||||
_channels[channelID].stream->seek(0);
|
||||
|
||||
_mixer->playStream( _channels[channelID].type,
|
||||
&_channels[channelID].handle,
|
||||
Audio::makeLoopingAudioStream(_channels[channelID].stream, _channels[channelID].numLoops),
|
||||
channelID,
|
||||
_channels[channelID].volume * 255 / 100,
|
||||
0, DisposeAfterUse::NO);
|
||||
}
|
||||
|
||||
void SoundManager::stopSound(int16 id) {
|
||||
if (isSoundPlaying(id)) {
|
||||
_mixer->stopHandle(handles[id]);
|
||||
void SoundManager::pauseSound(uint16 channelID, bool pause) {
|
||||
if (channelID > 32)
|
||||
return;
|
||||
|
||||
if (isSoundPlaying(channelID)) {
|
||||
_engine->_system->getMixer()->pauseHandle(_channels[channelID].handle, pause);
|
||||
}
|
||||
names[id] = Common::String();
|
||||
}
|
||||
|
||||
bool SoundManager::isSoundPlaying(int16 id) {
|
||||
if (id >= 0 && id < 20) {
|
||||
return _mixer->isSoundHandleActive(handles[id]);
|
||||
void SoundManager::stopSound(uint16 channelID) {
|
||||
if (channelID > 32)
|
||||
return;
|
||||
|
||||
if (isSoundPlaying(channelID)) {
|
||||
_mixer->stopHandle(_channels[channelID].handle);
|
||||
}
|
||||
return false;
|
||||
_channels[channelID].name = Common::String();
|
||||
delete _channels[channelID].stream;
|
||||
_channels[channelID].stream = nullptr;
|
||||
}
|
||||
|
||||
bool SoundManager::isSoundPlaying(uint16 channelID) {
|
||||
if (channelID > 32)
|
||||
return false;
|
||||
|
||||
return _mixer->isSoundHandleActive(_channels[channelID].handle);
|
||||
}
|
||||
|
||||
// Returns whether the exception was skipped
|
||||
void SoundManager::stopAllSounds() {
|
||||
_mixer->stopAll();
|
||||
for (uint i = 0; i < 32; ++i) {
|
||||
stopSound(i);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::initSoundChannels() {
|
||||
// Original engine hardcoded these and so do we
|
||||
_channels[7].type = Audio::Mixer::kSpeechSoundType;
|
||||
_channels[8].type = Audio::Mixer::kSpeechSoundType;
|
||||
_channels[30].type = Audio::Mixer::kSpeechSoundType;
|
||||
|
||||
_channels[0].type = Audio::Mixer::kMusicSoundType;
|
||||
_channels[1].type = Audio::Mixer::kMusicSoundType;
|
||||
_channels[2].type = Audio::Mixer::kMusicSoundType;
|
||||
_channels[27].type = Audio::Mixer::kMusicSoundType;
|
||||
_channels[28].type = Audio::Mixer::kMusicSoundType;
|
||||
_channels[29].type = Audio::Mixer::kMusicSoundType;
|
||||
_channels[19].type = Audio::Mixer::kMusicSoundType;
|
||||
|
||||
_channels[3].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[4].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[5].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[6].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[20].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[21].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[25].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[26].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[24].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[23].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[22].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[31].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[18].type = Audio::Mixer::kSFXSoundType;
|
||||
_channels[17].type = Audio::Mixer::kSFXSoundType;
|
||||
|
||||
_channels[9].type = Audio::Mixer::kPlainSoundType;
|
||||
_channels[10].type = Audio::Mixer::kPlainSoundType;
|
||||
_channels[11].type = Audio::Mixer::kPlainSoundType;
|
||||
_channels[12].type = Audio::Mixer::kPlainSoundType;
|
||||
_channels[13].type = Audio::Mixer::kPlainSoundType;
|
||||
_channels[14].type = Audio::Mixer::kPlainSoundType;
|
||||
_channels[15].type = Audio::Mixer::kPlainSoundType;
|
||||
_channels[16].type = Audio::Mixer::kPlainSoundType;
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace Nancy
|
@ -20,8 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NANCY_AUDIO_H
|
||||
#define NANCY_AUDIO_H
|
||||
#ifndef NANCY_SOUND_H
|
||||
#define NANCY_SOUND_H
|
||||
|
||||
#include "common/types.h"
|
||||
#include "common/str.h"
|
||||
@ -40,28 +40,53 @@ namespace Nancy {
|
||||
|
||||
class NancyEngine;
|
||||
|
||||
Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
class SoundManager {
|
||||
public:
|
||||
enum SoundLoopType { kLoop = 0, kOneShot = 1 };
|
||||
SoundManager(NancyEngine *engine);
|
||||
~SoundManager() =default;
|
||||
// Combines four different structs in one
|
||||
struct SoundDescription {
|
||||
enum Type { kNormal, kMenu, kDIGI, kScene };
|
||||
|
||||
void loadSound(Common::String &name, int16 id, uint16 numLoops = 0, uint16 volume = 60);
|
||||
void pauseSound(int16 id, bool pause);
|
||||
void stopSound(int16 id);
|
||||
bool isSoundPlaying(int16 id);
|
||||
Common::String name;
|
||||
uint16 channelID;
|
||||
uint16 numLoops;
|
||||
uint16 volume;
|
||||
|
||||
void read(Common::SeekableReadStream &stream, Type type);
|
||||
};
|
||||
|
||||
SoundManager(NancyEngine *engine);
|
||||
~SoundManager();
|
||||
|
||||
// Load a sound into a channel without starting it
|
||||
uint16 loadSound(const SoundDescription &description);
|
||||
|
||||
void playSound(uint16 channelID);
|
||||
void pauseSound(uint16 channelID, bool pause);
|
||||
bool isSoundPlaying(uint16 channelID);
|
||||
|
||||
// Stop playing a sound and unload it from the channel
|
||||
void stopSound(uint16 channelID);
|
||||
void stopAllSounds();
|
||||
|
||||
private:
|
||||
static Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
protected:
|
||||
struct Channel {
|
||||
Common::String name;
|
||||
Audio::Mixer::SoundType type;
|
||||
uint16 numLoops = 0;
|
||||
uint volume = 0;
|
||||
Audio::SeekableAudioStream *stream = nullptr;
|
||||
Audio::SoundHandle handle;
|
||||
};
|
||||
|
||||
void initSoundChannels();
|
||||
NancyEngine *_engine;
|
||||
Audio::Mixer *_mixer;
|
||||
|
||||
Audio::SoundHandle handles[20];
|
||||
Common::String names[20];
|
||||
Channel _channels[32];
|
||||
};
|
||||
|
||||
} // End of namespace Nancy
|
||||
|
||||
#endif // NANCY_AUDIO_H
|
||||
#endif // NANCY_SOUND_H
|
@ -24,7 +24,7 @@
|
||||
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/resource.h"
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/input.h"
|
||||
|
||||
#include "common/error.h"
|
||||
@ -66,13 +66,24 @@ void Logo::init() {
|
||||
}
|
||||
|
||||
void Logo::startSound() {
|
||||
Common::SeekableReadStream *msnd = _engine->getBootChunkStream("MSND");
|
||||
char name[10];
|
||||
msnd->seek(0);
|
||||
msnd->read(name, 10);
|
||||
Common::String sname(name);
|
||||
_engine->sound->loadSound(sname, 0);
|
||||
_engine->sound->pauseSound(0, false);
|
||||
SoundManager::SoundDescription desc;
|
||||
desc.read(*_engine->getBootChunkStream("MSND"), SoundManager::SoundDescription::kMenu);
|
||||
_engine->sound->loadSound(desc);
|
||||
MSNDchannelID = desc.channelID;
|
||||
desc.read(*_engine->getBootChunkStream("BUOK"), SoundManager::SoundDescription::kNormal);
|
||||
_engine->sound->loadSound(desc);
|
||||
desc.read(*_engine->getBootChunkStream("BUDE"), SoundManager::SoundDescription::kNormal);
|
||||
_engine->sound->loadSound(desc);
|
||||
desc.read(*_engine->getBootChunkStream("BULS"), SoundManager::SoundDescription::kNormal);
|
||||
_engine->sound->loadSound(desc);
|
||||
desc.read(*_engine->getBootChunkStream("GLOB"), SoundManager::SoundDescription::kNormal);
|
||||
_engine->sound->loadSound(desc);
|
||||
desc.read(*_engine->getBootChunkStream("CURT"), SoundManager::SoundDescription::kNormal);
|
||||
_engine->sound->loadSound(desc);
|
||||
desc.read(*_engine->getBootChunkStream("CANT"), SoundManager::SoundDescription::kNormal);
|
||||
_engine->sound->loadSound(desc);
|
||||
|
||||
_engine->sound->playSound(MSNDchannelID);
|
||||
|
||||
_startTicks = _engine->_system->getMillis();
|
||||
_state = kRun;
|
||||
|
@ -46,6 +46,8 @@ public:
|
||||
|
||||
void process();
|
||||
|
||||
uint MSNDchannelID; // This definitely shouldn't be here
|
||||
|
||||
private:
|
||||
void init();
|
||||
void startSound();
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "engines/nancy/state/scene.h"
|
||||
|
||||
#include "engines/nancy/resource.h"
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/input.h"
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/util.h"
|
||||
@ -73,13 +73,10 @@ void Map::init() {
|
||||
|
||||
// Load the audio
|
||||
chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
|
||||
chunk->read(name, 10);
|
||||
n = Common::String(name);
|
||||
uint16 channel = chunk->readUint16LE();
|
||||
chunk->skip(0xA);
|
||||
uint16 volume = chunk->readUint16LE();
|
||||
_engine->sound->loadSound(n, channel, 0, volume);
|
||||
_engine->sound->pauseSound(channel, false);
|
||||
SoundManager::SoundDescription sound;
|
||||
sound.read(*chunk, SoundManager::SoundDescription::kMenu);
|
||||
_engine->sound->loadSound(sound);
|
||||
_engine->sound->playSound(sound.channelID);
|
||||
|
||||
for (uint i = 0; i < 4; ++i) {
|
||||
chunk->seek(0x162 + i * 16, SEEK_SET);
|
||||
@ -133,6 +130,7 @@ void Map::run() {
|
||||
// TODO handle map button as well
|
||||
|
||||
if (input.input & NancyInput::kLeftMouseButtonUp) {
|
||||
stopSound();
|
||||
_engine->setGameState(NancyEngine::kScene);
|
||||
_engine->scene->changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
|
||||
_state = kInit;
|
||||
@ -142,6 +140,14 @@ void Map::run() {
|
||||
}
|
||||
}
|
||||
|
||||
void Map::stopSound() {
|
||||
Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
|
||||
SoundManager::SoundDescription sound;
|
||||
chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
|
||||
sound.read(*chunk, SoundManager::SoundDescription::kMenu);
|
||||
_engine->sound->stopSound(sound.channelID);
|
||||
}
|
||||
|
||||
void Map::registerGraphics() {
|
||||
_viewport.registerGraphics();
|
||||
_label.registerGraphics();
|
||||
|
@ -89,6 +89,7 @@ private:
|
||||
|
||||
void init();
|
||||
void run();
|
||||
void stopSound();
|
||||
|
||||
void registerGraphics();
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "engines/nancy/iff.h"
|
||||
#include "engines/nancy/action/actionmanager.h"
|
||||
#include "engines/nancy/input.h"
|
||||
#include "engines/nancy/audio.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/graphics.h"
|
||||
#include "engines/nancy/cursor.h"
|
||||
#include "engines/nancy/time.h"
|
||||
@ -55,9 +55,9 @@ void Scene::process() {
|
||||
case kStartSound:
|
||||
_state = kRun;
|
||||
if (!_sceneState._doNotStartSound) {
|
||||
_engine->sound->stopAllSounds();
|
||||
_engine->sound->loadSound(_sceneState.summary.audioFile, _sceneState.summary.audioID, 0, _sceneState.summary.audioVolume);
|
||||
_engine->sound->pauseSound(_sceneState.summary.audioID, false);
|
||||
_engine->stopAndUnloadSpecificSounds();
|
||||
_engine->sound->loadSound(_sceneState.summary.sound);
|
||||
_engine->sound->playSound(_sceneState.summary.sound.channelID);
|
||||
}
|
||||
// fall through
|
||||
case kRun:
|
||||
@ -85,6 +85,20 @@ void Scene::popScene() {
|
||||
_sceneState.isScenePushed = false;
|
||||
}
|
||||
|
||||
void Scene::pauseSceneSpecificSounds() {
|
||||
// TODO missing if, same condition as the one in NancyEngine::stopAndUnloadSpecificSounds
|
||||
|
||||
for (uint i = 0; i < 10; ++i) {
|
||||
_engine->sound->pauseSound(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::unpauseSceneSpecificSounds() {
|
||||
for (uint i = 0; i < 10; ++i) {
|
||||
_engine->sound->pauseSound(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::addItemToInventory(uint16 id) {
|
||||
_flags.items[id] = kTrue;
|
||||
if (_flags.heldItem == id) {
|
||||
@ -245,6 +259,7 @@ void Scene::run() {
|
||||
}
|
||||
|
||||
registerGraphics();
|
||||
unpauseSceneSpecificSounds();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -305,12 +320,7 @@ void Scene::readSceneSummary(Common::SeekableReadStream &stream) {
|
||||
stream.seek(3, SEEK_CUR);
|
||||
_sceneState.summary.videoFormat = stream.readUint16LE();
|
||||
|
||||
stream.read(buf, 10);
|
||||
buf[9] = 0;
|
||||
_sceneState.summary.audioFile = Common::String(buf);
|
||||
_sceneState.summary.audioID = stream.readSint16LE();
|
||||
stream.skip(0xE);
|
||||
_sceneState.summary.audioVolume = stream.readUint16LE();
|
||||
_sceneState.summary.sound.read(stream, SoundManager::SoundDescription::kScene);
|
||||
|
||||
stream.seek(0x72);
|
||||
_sceneState.summary.verticalScrollDelta = stream.readUint16LE();
|
||||
@ -332,6 +342,7 @@ bool Scene::changeGameState() {
|
||||
_timers.pushedPlayTime = _engine->getTotalPlayTime();
|
||||
_engine->setGameState(_gameStateRequested);
|
||||
_gameStateRequested = NancyEngine::kScene;
|
||||
pauseSceneSpecificSounds();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "engines/nancy/time.h"
|
||||
#include "engines/nancy/commontypes.h"
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
@ -64,19 +65,18 @@ class Scene {
|
||||
friend class Nancy::NancyConsole;
|
||||
public:
|
||||
struct SceneSummary { // SSUM
|
||||
Common::String description; // 0x00
|
||||
Common::String videoFile; // 0x32
|
||||
Common::String description; // 0x00
|
||||
Common::String videoFile; // 0x32
|
||||
//
|
||||
uint16 videoFormat; // 0x3E, value is 1 or 2
|
||||
Common::String audioFile; // 0x40
|
||||
int16 audioID; // 0x4A
|
||||
uint16 audioVolume; // 0x5A
|
||||
uint16 videoFormat; // 0x3E, value is 1 or 2
|
||||
Common::String audioFile;
|
||||
SoundManager::SoundDescription sound; // 0x40
|
||||
//
|
||||
uint16 verticalScrollDelta; // 0x72
|
||||
uint16 horizontalEdgeSize; // 0x74
|
||||
uint16 verticalEdgeSize; // 0x76
|
||||
Time slowMoveTimeDelta; // 0x78
|
||||
Time fastMoveTimeDelta; // 0x7A
|
||||
uint16 verticalScrollDelta; // 0x72
|
||||
uint16 horizontalEdgeSize; // 0x74
|
||||
uint16 verticalEdgeSize; // 0x76
|
||||
Time slowMoveTimeDelta; // 0x78
|
||||
Time fastMoveTimeDelta; // 0x7A
|
||||
// byte unknown7C enum with 4 values
|
||||
//
|
||||
};
|
||||
@ -97,6 +97,9 @@ public:
|
||||
void pushScene();
|
||||
void popScene();
|
||||
|
||||
void pauseSceneSpecificSounds();
|
||||
void unpauseSceneSpecificSounds();
|
||||
|
||||
void addItemToInventory(uint16 id);
|
||||
void removeItemFromInventory(uint16 id, bool pickUp = true);
|
||||
int16 getHeldItem() { return _flags.heldItem; }
|
||||
|
Loading…
Reference in New Issue
Block a user