mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
397 lines
14 KiB
C++
397 lines
14 KiB
C++
/* 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "twp/twp.h"
|
|
#include "twp/detection.h"
|
|
#include "twp/object.h"
|
|
#include "twp/sqgame.h"
|
|
#include "twp/squtil.h"
|
|
|
|
namespace Twp {
|
|
|
|
class SoundTrigger : public Trigger {
|
|
public:
|
|
SoundTrigger(const Common::Array<Common::SharedPtr<SoundDefinition> > sounds, int objId) : _sounds(sounds), _objId(objId) {}
|
|
virtual ~SoundTrigger() {}
|
|
|
|
virtual void trig() override {
|
|
int i = g_twp->getRandomSource().getRandomNumber(_sounds.size() - 1);
|
|
g_twp->_audio->play(_sounds[i], Audio::Mixer::SoundType::kPlainSoundType, 0, 0.f, 1.f, _objId);
|
|
}
|
|
|
|
private:
|
|
const Common::Array<Common::SharedPtr<SoundDefinition> > _sounds;
|
|
int _objId;
|
|
};
|
|
|
|
// Plays a sound at the specified actor's location.
|
|
// If no sound is given, then it will turn off the trigger.
|
|
// If a list of multiple sounds or an array are given, will randomly choose between the sound files.
|
|
// The triggerNumber says which trigger in the animation JSON file should be used as a trigger to play the sound.
|
|
static SQInteger actorSound(HSQUIRRELVM v) {
|
|
Common::SharedPtr<Object> obj = sqobj(v, 2);
|
|
if (!obj)
|
|
return sq_throwerror(v, "failed to get actor or object");
|
|
SQInteger trigNum = 0;
|
|
if (SQ_FAILED(sqget(v, 3, trigNum)))
|
|
return sq_throwerror(v, "failed to get trigger number");
|
|
SQInteger numSounds = sq_gettop(v) - 3;
|
|
if (numSounds != 0) {
|
|
SQInteger tmp = 0;
|
|
if ((numSounds == 1) && (SQ_SUCCEEDED(sqget(v, 4, tmp))) && (tmp == 0)) {
|
|
obj->_triggers.erase(trigNum);
|
|
} else {
|
|
Common::Array<Common::SharedPtr<SoundDefinition> > sounds;
|
|
if (sq_gettype(v, 4) == OT_ARRAY) {
|
|
if (SQ_FAILED(sqgetarray(v, 4, sounds)))
|
|
return sq_throwerror(v, "failed to get sounds");
|
|
} else {
|
|
sounds.resize(numSounds);
|
|
for (int i = 0; i < numSounds; i++) {
|
|
sounds[i] = sqsounddef(v, 4 + i);
|
|
}
|
|
}
|
|
|
|
Common::SharedPtr<Trigger> trigger(new SoundTrigger(Common::move(sounds), obj->getId()));
|
|
obj->_triggers[trigNum] = trigger;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Defines a sound and binds it to an id.
|
|
// The defineSound(file) calls should be done at boot and do not load the file.
|
|
// Its main use is to keep strings from being created and referenced during game play and providing a way to globally change a sound.
|
|
// .. code-block:: Squirrel
|
|
// clock_tick <- defineSound("clockTick.wav")
|
|
static SQInteger defineSound(HSQUIRRELVM v) {
|
|
Common::String filename;
|
|
if (SQ_FAILED(sqget(v, 2, filename)))
|
|
return sq_throwerror(v, "failed to get filename");
|
|
Common::SharedPtr<SoundDefinition> sound(new SoundDefinition(filename));
|
|
g_twp->_audio->_soundDefs.push_back(sound);
|
|
debugC(kDebugSndScript, "defineSound(%s)-> %d", filename.c_str(), sound->getId());
|
|
sqpush(v, sound->getId());
|
|
return 1;
|
|
}
|
|
|
|
// Fades a sound out over a specified fade out duration (in seconds).
|
|
// .. code-block:: Squirrel
|
|
// fadeOutSound(soundElevatorMusic, 0.5)
|
|
static SQInteger fadeOutSound(HSQUIRRELVM v) {
|
|
SQInteger sound = 0;
|
|
if (SQ_FAILED(sqget(v, 2, sound)))
|
|
return sq_throwerror(v, "failed to get sound");
|
|
float t;
|
|
if (SQ_FAILED(sqget(v, 3, t)))
|
|
return sq_throwerror(v, "failed to get fadeOut time");
|
|
g_twp->_audio->fadeOut(sound, t);
|
|
return 0;
|
|
}
|
|
|
|
// Returns `TRUE` if sound is currently playing.
|
|
// Where sound can be a channel (an integer from 1-32), a sound id (as obtained when sound was created with playSound), an actual sound (ie one that has been defined using defineSound).
|
|
// .. code-block:: Squirrel
|
|
// if (isSoundPlaying(soundElevatorMusic)) { ...}
|
|
static SQInteger isSoundPlaying(HSQUIRRELVM v) {
|
|
SQInteger soundId;
|
|
if (SQ_FAILED(sqget(v, 2, soundId)))
|
|
return sq_throwerror(v, "failed to get sound");
|
|
sqpush(v, g_twp->_audio->playing(soundId));
|
|
return 1;
|
|
}
|
|
|
|
static SQInteger playObjectSound(HSQUIRRELVM v) {
|
|
SQInteger nArgs = sq_gettop(v);
|
|
Common::SharedPtr<SoundDefinition> soundDef = sqsounddef(v, 2);
|
|
if (!soundDef)
|
|
return sq_throwerror(v, "failed to get sound");
|
|
|
|
Common::SharedPtr<Object> obj = sqobj(v, 3);
|
|
if (!obj)
|
|
return sq_throwerror(v, "failed to get actor or object");
|
|
SQInteger loopTimes = 1;
|
|
float fadeInTime = 0.0f;
|
|
if ((nArgs >= 4) && SQ_FAILED(sqget(v, 4, loopTimes)))
|
|
return sq_throwerror(v, "failed to get loopTimes");
|
|
if ((nArgs >= 5) && SQ_FAILED(sqget(v, 5, fadeInTime)))
|
|
return sq_throwerror(v, "failed to get fadeInTime");
|
|
|
|
if (obj->_sound) {
|
|
g_twp->_audio->stop(obj->_sound);
|
|
}
|
|
|
|
int soundId = g_twp->_audio->play(soundDef, Audio::Mixer::SoundType::kPlainSoundType, loopTimes, fadeInTime, 1.f, obj->getId());
|
|
obj->_sound = soundId;
|
|
sqpush(v, soundId);
|
|
return 1;
|
|
}
|
|
|
|
// Plays a sound that has been loaded with defineSound(file).
|
|
// Classifies the audio as "sound" (not "music").
|
|
// Returns a sound ID that can be used to reference the sound later on.
|
|
// .. code-block:: Squirrel
|
|
// playSound(clock_tick)
|
|
// objectState(quickiePalFlickerLight, ON)
|
|
// _flourescentSoundID = playSound(soundFlourescentOn)
|
|
static SQInteger playSound(HSQUIRRELVM v) {
|
|
Common::SharedPtr<SoundDefinition> sound = sqsounddef(v, 2);
|
|
if (!sound)
|
|
return sq_throwerror(v, "failed to get sound");
|
|
int soundId = g_twp->_audio->play(sound, Audio::Mixer::SoundType::kPlainSoundType);
|
|
sqpush(v, soundId);
|
|
return 1;
|
|
}
|
|
|
|
// Starts playing sound at the specified volume, where volume is a float between 0 and 1.
|
|
// Not for use in adjusting the volume of a sound that is already playing.
|
|
// Returns a sound ID which can be used when turning off the sound or otherwise manipulating it.
|
|
// .. code-block:: Squirrel
|
|
// script runAway(bunnyActor) {
|
|
// local soundVolume = 1.0
|
|
// for (local soundVolume = 1.0; x > 0; x -= 0.25) {
|
|
// playSoundVolume(soundHop, soundVolume)
|
|
// objectOffsetTo(bunnyActor, -10, 0, 0.5)
|
|
// breaktime(1.0)
|
|
// }
|
|
// }
|
|
static SQInteger playSoundVolume(HSQUIRRELVM v) {
|
|
Common::SharedPtr<SoundDefinition> sound = sqsounddef(v, 2);
|
|
if (!sound)
|
|
return sq_throwerror(v, "failed to get sound");
|
|
int soundId = g_twp->_audio->play(sound, Audio::Mixer::SoundType::kPlainSoundType);
|
|
sqpush(v, soundId);
|
|
return 1;
|
|
}
|
|
|
|
static SQInteger loadSound(HSQUIRRELVM v) {
|
|
Common::SharedPtr<SoundDefinition> sound = sqsounddef(v, 2);
|
|
if (!sound)
|
|
return sq_throwerror(v, "failed to get sound");
|
|
sound->load();
|
|
return 0;
|
|
}
|
|
|
|
// Loops music.
|
|
// If loopTimes is not defined or is -1, will loop infinitely.
|
|
// For the first loop, it will fade the sound in for fadeInTime seconds, if specified.
|
|
// See also loopSound, which classifies the audio as being "sound" not "music".
|
|
// This is important if we allow separate volume control adjustment.
|
|
// .. code-block:: Squirrel
|
|
// enter = function()
|
|
// {
|
|
// print("Enter StartScreen")
|
|
// exCommand(EX_BUTTON_HOVER_SOUND, soundClockTick)
|
|
// _music = loopMusic(musicTempA)
|
|
// }
|
|
static SQInteger loopMusic(HSQUIRRELVM v) {
|
|
SQInteger loopTimes = -1;
|
|
float fadeInTime = 0.f;
|
|
SQInteger numArgs = sq_gettop(v);
|
|
Common::SharedPtr<SoundDefinition> sound(sqsounddef(v, 2));
|
|
if (!sound)
|
|
return sq_throwerror(v, "failed to get music");
|
|
if ((numArgs >= 3) && SQ_FAILED(sqget(v, 3, loopTimes)))
|
|
return sq_throwerror(v, "failed to get loopTimes");
|
|
if ((numArgs >= 4) && SQ_FAILED(sqget(v, 4, fadeInTime)))
|
|
return sq_throwerror(v, "failed to get fadeInTime");
|
|
int soundId = g_twp->_audio->play(sound, Audio::Mixer::kMusicSoundType, loopTimes, fadeInTime);
|
|
sqpush(v, soundId);
|
|
return 1;
|
|
}
|
|
|
|
static SQInteger loopObjectSound(HSQUIRRELVM v) {
|
|
SQInteger loopTimes = -1;
|
|
float fadeInTime = 0.f;
|
|
SQInteger numArgs = sq_gettop(v);
|
|
Common::SharedPtr<SoundDefinition> sound = sqsounddef(v, 2);
|
|
if (!sound)
|
|
return sq_throwerror(v, "failed to get music");
|
|
Common::SharedPtr<Object> obj = sqobj(v, 3);
|
|
if (!obj)
|
|
return sq_throwerror(v, "failed to get object");
|
|
if (numArgs == 4) {
|
|
if (SQ_FAILED(sqget(v, 4, loopTimes))) {
|
|
return sq_throwerror(v, "failed to get loopTimes");
|
|
}
|
|
}
|
|
if (numArgs == 5) {
|
|
if (SQ_FAILED(sqget(v, 5, fadeInTime))) {
|
|
return sq_throwerror(v, "failed to get fadeInTime");
|
|
}
|
|
}
|
|
int soundId = g_twp->_audio->play(sound, Audio::Mixer::kPlainSoundType, loopTimes, fadeInTime, 1.f, obj->getId());
|
|
sqpush(v, soundId);
|
|
return 1;
|
|
}
|
|
|
|
// Loops a sound a specified number of times (loopTimes).
|
|
// If loopTimes = -1 or not set, then it loops the sound forever.
|
|
// You can fade in the sound for the first loop by setting the fadeInTime duration (in seconds).
|
|
// If fadeInTime is 0 or not set, it will immediately be at full volume.
|
|
// Returns a sound ID which can be used when turning off the sound or otherwise manipulating it.
|
|
// See also loopMusic.
|
|
// .. code-block:: Squirrel
|
|
// local _muzac = loopSound(soundElevatorMusic, -1, 1.0)
|
|
//
|
|
// script daveCooking() {
|
|
// loopSound(soundSizzleLoop)
|
|
// ...
|
|
// }
|
|
//
|
|
// if (Bank.bankTelephone.inUse) {
|
|
// breaktime(0.5)
|
|
// loopSound(soundPhoneBusy, 3)
|
|
// }
|
|
static SQInteger loopSound(HSQUIRRELVM v) {
|
|
SQInteger loopTimes = -1;
|
|
float fadeInTime = 0.f;
|
|
SQInteger numArgs = sq_gettop(v);
|
|
Common::SharedPtr<SoundDefinition> sound = sqsounddef(v, 2);
|
|
if (!sound)
|
|
return sq_throwerror(v, "failed to get music");
|
|
if (numArgs == 3) {
|
|
if (SQ_FAILED(sqget(v, 3, loopTimes))) {
|
|
return sq_throwerror(v, "failed to get loopTimes");
|
|
}
|
|
}
|
|
if (numArgs == 4) {
|
|
if (SQ_FAILED(sqget(v, 4, fadeInTime))) {
|
|
return sq_throwerror(v, "failed to get fadeInTime");
|
|
}
|
|
}
|
|
int soundId = g_twp->_audio->play(sound, Audio::Mixer::kPlainSoundType, loopTimes, fadeInTime);
|
|
debugC(kDebugSndScript, "loopSound %s: %d", sound->getName().c_str(), soundId);
|
|
sqpush(v, soundId);
|
|
return 1;
|
|
}
|
|
|
|
static SQInteger soundVolume(HSQUIRRELVM v, Audio::Mixer::SoundType soundType) {
|
|
float volume = 0.f;
|
|
if (sq_gettop(v) == 2) {
|
|
if (SQ_FAILED(sqget(v, 2, volume))) {
|
|
return sq_throwerror(v, "failed to get volume");
|
|
}
|
|
int vol = volume * Audio::Mixer::kMaxMixerVolume;
|
|
g_twp->_mixer->setVolumeForSoundType(soundType, vol);
|
|
return 0;
|
|
}
|
|
volume = (float)g_twp->_mixer->getVolumeForSoundType(soundType) / Audio::Mixer::kMaxMixerVolume;
|
|
sqpush(v, volume);
|
|
return 1;
|
|
}
|
|
|
|
static SQInteger masterSoundVolume(HSQUIRRELVM v) {
|
|
float volume = 0.f;
|
|
if (sq_gettop(v) == 2) {
|
|
if (SQ_FAILED(sqget(v, 2, volume))) {
|
|
return sq_throwerror(v, "failed to get volume");
|
|
}
|
|
g_twp->_audio->setMasterVolume(volume);
|
|
return 0;
|
|
}
|
|
volume = g_twp->_audio->getMasterVolume();
|
|
sqpush(v, volume);
|
|
return 1;
|
|
}
|
|
|
|
static SQInteger musicMixVolume(HSQUIRRELVM v) {
|
|
return soundVolume(v, Audio::Mixer::SoundType::kMusicSoundType);
|
|
}
|
|
|
|
static SQInteger playMusic(HSQUIRRELVM v) {
|
|
Common::SharedPtr<SoundDefinition> soundDef = sqsounddef(v, 2);
|
|
if (!soundDef)
|
|
return sq_throwerror(v, "failed to get music");
|
|
int soundId = g_twp->_audio->play(soundDef, Audio::Mixer::SoundType::kMusicSoundType);
|
|
sqpush(v, soundId);
|
|
return 1;
|
|
}
|
|
|
|
static SQInteger soundMixVolume(HSQUIRRELVM v) {
|
|
return soundVolume(v, Audio::Mixer::SoundType::kPlainSoundType);
|
|
}
|
|
|
|
// Sets the volume (float from 0 to 1) of an already playing sound.
|
|
// Can be used for a channel (integer 1-32), soundId (as obtained when starting the sound playing) or an actual sound (defined by defineSound).
|
|
// If _sound is not yet playing, then nothing will happen (if sound is subsequently set to play it will be at full volume).
|
|
// .. code-block:: Squirrel
|
|
// local _tronSoundTID = loopObjectSound(soundTronRattle_Loop, quickieToilet, -1, 0.25)
|
|
// soundVolume(_tronSoundTID, 0.2)
|
|
// shakeObject(quickieToilet, 0.25)
|
|
// jiggleObject(quickieToilet, 0.25)
|
|
// breaktime(0.2)
|
|
static SQInteger soundVolume(HSQUIRRELVM v) {
|
|
SQInteger soundId;
|
|
if (SQ_FAILED(sqget(v, 2, soundId)))
|
|
return sq_throwerror(v, "failed to get sound");
|
|
float volume = 1.0f;
|
|
if (SQ_FAILED(sqget(v, 3, volume)))
|
|
return sq_throwerror(v, "failed to get volume");
|
|
g_twp->_audio->setVolume(soundId, volume);
|
|
return 0;
|
|
}
|
|
|
|
static SQInteger stopAllSounds(HSQUIRRELVM) {
|
|
g_twp->_mixer->stopAll();
|
|
return 0;
|
|
}
|
|
|
|
// Immediately stops the indicated sound.
|
|
// Abruptly. Silently. No fades. It's dead.
|
|
// Can be used for a channel (integer 1-32), _soundId (as obtained when starting the sound playing) or an actual sound (defined by defineSound).
|
|
// If using a defined sound, will stop any sound that is named that, eg all cricket sounds (soundCrickets, soundCrickets).
|
|
// .. code-block:: Squirrel
|
|
// stopSound(soundElevatorMusic)
|
|
static SQInteger stopSound(HSQUIRRELVM v) {
|
|
SQInteger soundId;
|
|
if (SQ_FAILED(sqget(v, 2, soundId)))
|
|
return sq_throwerror(v, "failed to get sound");
|
|
g_twp->_audio->stop(soundId);
|
|
return 0;
|
|
}
|
|
|
|
static SQInteger talkieMixVolume(HSQUIRRELVM v) {
|
|
return soundVolume(v, Audio::Mixer::SoundType::kSpeechSoundType);
|
|
}
|
|
|
|
void sqgame_register_soundlib(HSQUIRRELVM v) {
|
|
regFunc(v, actorSound, "actorSound");
|
|
regFunc(v, defineSound, "defineSound");
|
|
regFunc(v, fadeOutSound, "fadeOutSound");
|
|
regFunc(v, isSoundPlaying, "isSoundPlaying");
|
|
regFunc(v, loadSound, "loadSound");
|
|
regFunc(v, loopMusic, "loopMusic");
|
|
regFunc(v, loopObjectSound, "loopObjectSound");
|
|
regFunc(v, loopSound, "loopSound");
|
|
regFunc(v, masterSoundVolume, "masterSoundVolume");
|
|
regFunc(v, musicMixVolume, "musicMixVolume");
|
|
regFunc(v, playMusic, "playMusic");
|
|
regFunc(v, playObjectSound, "playObjectSound");
|
|
regFunc(v, playSound, "playSound");
|
|
regFunc(v, playSoundVolume, "playSoundVolume");
|
|
regFunc(v, soundMixVolume, "soundMixVolume");
|
|
regFunc(v, soundVolume, "soundVolume");
|
|
regFunc(v, stopAllSounds, "stopAllSounds");
|
|
regFunc(v, stopSound, "stopSound");
|
|
regFunc(v, talkieMixVolume, "talkieMixVolume");
|
|
}
|
|
} // namespace Twp
|