Files
scummvm-cursorfix/engines/director/lingo/xlibs/v/voyagerxsound.cpp
2026-02-02 04:50:13 +01:00

389 lines
13 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 "common/system.h"
#include "director/director.h"
#include "director/movie.h"
#include "director/score.h"
#include "director/window.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/v/voyagerxsound.h"
/**************************************************
*
* USED IN:
* Puppet Motel
*
**************************************************/
/*
-- Voyager Company XSound External Factory. 27October1995
--XSound
I mNew --Creates a new instance of XSound
X mDispose --Disposes of XSound instance
X init --Initializes the XSound library
III open, numchan, monostereo --Opens the specified sound channel
X close --Stops playback and closes all channels
XI bufsize, kbytes --Sets buffer size to kbytes
IS exists, name --Determines if AIFF file exists on disk
II status, chan --Determines if the specified channel is busy
XS path, pathname --Sets path used when opening sound files
SS duration, name --Returns duration in secs for specified file
SI curtime, chan --Returns current file location in seconds
V playfile, chan, name, tstart, tend --Plays specified AIFF file
IIS loadfile, chan, name -- preloads the specified AIFF file
XI unloadfile, chan -- unloads a previously loaded file
V playsnd, chan, name, tstart, tend --Plays specified WAVE file
V extplayfile, chan, name, lstart, lend --Plays specified looped AIFF
XI stop, chan --Stops playback on specified channel
XII volume, chan, vol --Sets volume for specified channel
XIII leftrightvol, chan, lvol, rvol --Sets volume of left and right
V fade, chan, endvol, duration, autostop --Fades channel over duration
XII frequency, chan, percent --Transposes pitch by percent
XII pan, chan, panvalue --Sets the panning of a channel
ISS startrecord, name, duration --Records new sound file
X stoprecord --Stops any recording in process
XS recordpath, path --Sets default record path
*/
namespace Director {
const char *VoyagerXSoundXObj::xlibName = "XSound";
const XlibFileDesc VoyagerXSoundXObj::fileNames[] = {
{ "XSound", "puppetmotel" },
{ "XSound32", "puppetmotel" },
{ nullptr, nullptr },
};
static MethodProto xlibMethods[] = {
{ "new", VoyagerXSoundXObj::m_new, 0, 0, 400 },
{ "dispose", VoyagerXSoundXObj::m_dispose, 0, 0, 400 },
{ "init", VoyagerXSoundXObj::m_init, 0, 0, 400 },
{ "open", VoyagerXSoundXObj::m_open, 2, 2, 400 },
{ "close", VoyagerXSoundXObj::m_close, 0, 0, 400 },
{ "bufsize", VoyagerXSoundXObj::m_bufsize, 1, 1, 400 },
{ "exists", VoyagerXSoundXObj::m_exists, 1, 1, 400 },
{ "status", VoyagerXSoundXObj::m_status, 1, 1, 400 },
{ "path", VoyagerXSoundXObj::m_path, 1, 1, 400 },
{ "duration", VoyagerXSoundXObj::m_duration, 1, 1, 400 },
{ "curtime", VoyagerXSoundXObj::m_curtime, 1, 1, 400 },
{ "playfile", VoyagerXSoundXObj::m_playfile, 0, 0, 400 },
{ "loadfile", VoyagerXSoundXObj::m_loadfile, 2, 2, 400 },
{ "unloadfile", VoyagerXSoundXObj::m_unloadfile, 1, 1, 400 },
{ "playsnd", VoyagerXSoundXObj::m_playsnd, 0, 0, 400 },
{ "extplayfile", VoyagerXSoundXObj::m_extplayfile, 0, 0, 400 },
{ "stop", VoyagerXSoundXObj::m_stop, 1, 1, 400 },
{ "volume", VoyagerXSoundXObj::m_volume, 2, 2, 400 },
{ "leftrightvol", VoyagerXSoundXObj::m_leftrightvol, 3, 3, 400 },
{ "fade", VoyagerXSoundXObj::m_fade, 0, 0, 400 },
{ "frequency", VoyagerXSoundXObj::m_frequency, 2, 2, 400 },
{ "pan", VoyagerXSoundXObj::m_pan, 2, 2, 400 },
{ "startrecord", VoyagerXSoundXObj::m_startrecord, 2, 2, 400 },
{ "stoprecord", VoyagerXSoundXObj::m_stoprecord, 0, 0, 400 },
{ "recordpath", VoyagerXSoundXObj::m_recordpath, 1, 1, 400 },
{ nullptr, nullptr, 0, 0, 0 }
};
static BuiltinProto xlibBuiltins[] = {
{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
};
VoyagerXSoundXObject::VoyagerXSoundXObject(ObjectType ObjectType) :Object<VoyagerXSoundXObject>("XSound") {
_objType = ObjectType;
_soundManager = g_director->getCurrentWindow()->getSoundManager();
}
VoyagerXSoundXObject::~VoyagerXSoundXObject() {
close();
}
int VoyagerXSoundXObject::open(int numChan, int monoStereo) {
if (!_channels.contains(numChan)) {
_channels[numChan] = new VoyagerChannel();
_channels[numChan]->channelID = numChan + 1000;
}
return 1;
}
void VoyagerXSoundXObject::close() {
for (auto &it : _channels)
delete it._value;
_channels.clear();
}
int VoyagerXSoundXObject::status(int chan) {
if (_channels.contains(chan)) {
return _soundManager->isChannelActive(_channels[chan]->channelID) ? 1 : 0;
}
return 0;
}
int VoyagerXSoundXObject::playfile(int chan, Common::String &path, int tstart, int tend) {
if (!_channels.contains(chan)) {
open(chan, 2);
}
_soundManager->playFile(path, _channels[chan]->channelID);
return 1;
}
int VoyagerXSoundXObject::fade(int chan, int endvol, int duration, bool autostop) {
if (!_channels.contains(chan)) {
return 0;
}
int channelID = _channels[chan]->channelID;
_soundManager->registerFade(channelID, _soundManager->getChannelVolume(channelID), endvol, duration*60, autostop);
Window *window = g_director->getCurrentWindow();
Score *score = window->getCurrentMovie()->getScore();
score->_activeFade = true;
return 1;
}
void VoyagerXSoundXObject::stop(int chan) {
if (!_channels.contains(chan)) {
return;
}
int channelID = _channels[chan]->channelID;
_soundManager->stopSound(channelID);
}
void VoyagerXSoundXObject::volume(int chan, int vol) {
if (!_channels.contains(chan)) {
return;
}
int channelID = _channels[chan]->channelID;
_soundManager->setChannelVolume(channelID, vol);
}
void VoyagerXSoundXObject::leftrightvol(int chan, uint8 lvol, uint8 rvol) {
if (!_channels.contains(chan)) {
return;
}
int channelID = _channels[chan]->channelID;
_soundManager->setChannelFaderL(channelID, lvol);
_soundManager->setChannelFaderR(channelID, lvol);
}
void VoyagerXSoundXObject::frequency(int chan, int percent) {
if (!_channels.contains(chan)) {
return;
}
int channelID = _channels[chan]->channelID;
_soundManager->setChannelPitchShift(channelID, percent);
}
void VoyagerXSoundXObject::pan(int chan, int percent) {
if (!_channels.contains(chan)) {
return;
}
percent = MAX(MIN(100, percent), -100);
int channelID = _channels[chan]->channelID;
_soundManager->setChannelBalance(channelID, (int8)percent);
}
void VoyagerXSoundXObj::open(ObjectType type, const Common::Path &path) {
VoyagerXSoundXObject::initMethods(xlibMethods);
VoyagerXSoundXObject *xobj = new VoyagerXSoundXObject(type);
if (type == kXtraObj)
g_lingo->_openXtras.push_back(xlibName);
g_lingo->exposeXObject(xlibName, xobj);
g_lingo->initBuiltIns(xlibBuiltins);
}
void VoyagerXSoundXObj::close(ObjectType type) {
VoyagerXSoundXObject::cleanupMethods();
g_lingo->_globalvars[xlibName] = Datum();
}
void VoyagerXSoundXObj::m_new(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_new", nargs);
g_lingo->dropStack(nargs);
g_lingo->push(g_lingo->_state->me);
}
// For some reason the game code calls all of these with ARGC, so always return something
XOBJSTUBNR(VoyagerXSoundXObj::m_dispose)
XOBJSTUBNR(VoyagerXSoundXObj::m_init)
void VoyagerXSoundXObj::m_open(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_open", nargs);
ARGNUMCHECK(2);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
Datum monoStereo = g_lingo->pop();
Datum numChan = g_lingo->pop();
int result = me->open(numChan.asInt(), monoStereo.asInt());
g_lingo->push(result);
}
void VoyagerXSoundXObj::m_close(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_close", nargs);
ARGNUMCHECK(0);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
me->close();
g_lingo->push(0);
}
XOBJSTUB(VoyagerXSoundXObj::m_bufsize, 0)
XOBJSTUB(VoyagerXSoundXObj::m_exists, 0)
void VoyagerXSoundXObj::m_status(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_status", nargs);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
ARGNUMCHECK(1);
Datum chan = g_lingo->pop();
g_lingo->push(me->status(chan.asInt()));
}
XOBJSTUB(VoyagerXSoundXObj::m_path, 0)
XOBJSTUB(VoyagerXSoundXObj::m_duration, "")
XOBJSTUB(VoyagerXSoundXObj::m_curtime, "")
void VoyagerXSoundXObj::m_playfile(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_playfile", nargs);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
if (nargs < 2) {
warning("VoyagerXSoundXObj::m_playfile: expected at least 2 args");
g_lingo->dropStack(nargs);
g_lingo->push(0);
return;
} else if (nargs > 4) {
g_lingo->dropStack(nargs - 4);
nargs = 4;
}
Datum tend(-1);
if (nargs == 4) {
tend = g_lingo->pop();
nargs--;
}
Datum tstart(-1);
if (nargs == 3) {
tstart = g_lingo->pop();
nargs--;
}
Common::String path = g_lingo->pop().asString();
Datum chan = g_lingo->pop();
int result = me->playfile(chan.asInt(), path, tstart.asInt(), tend.asInt());
g_lingo->push(result);
}
XOBJSTUB(VoyagerXSoundXObj::m_loadfile, 0)
XOBJSTUB(VoyagerXSoundXObj::m_unloadfile, 0)
void VoyagerXSoundXObj::m_playsnd(int nargs) {
m_playfile(nargs);
}
XOBJSTUB(VoyagerXSoundXObj::m_extplayfile, 0)
void VoyagerXSoundXObj::m_stop(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_stop", nargs);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
ARGNUMCHECK(1);
int chan = g_lingo->pop().asInt();
me->stop(chan);
g_lingo->push(Datum(1));
}
void VoyagerXSoundXObj::m_volume(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_volume", nargs);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
ARGNUMCHECK(2);
int vol = g_lingo->pop().asInt();
int chan = g_lingo->pop().asInt();
me->volume(chan, vol);
g_lingo->push(Datum(1));
}
void VoyagerXSoundXObj::m_leftrightvol(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_pan", nargs);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
ARGNUMCHECK(3);
int rvol = g_lingo->pop().asInt();
int lvol = g_lingo->pop().asInt();
int chan = g_lingo->pop().asInt();
me->leftrightvol(chan, (uint8)lvol, (uint8)rvol);
g_lingo->push(Datum(1));
}
void VoyagerXSoundXObj::m_fade(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_fade", nargs);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
if (nargs < 2) {
warning("VoyagerXSoundXObj::m_fade: expected at least 2 args");
g_lingo->dropStack(nargs);
g_lingo->push(Datum());
return;
}
if (nargs > 4) {
warning("VoyagerXSoundXObj: dropping %d extra args", nargs - 4);
g_lingo->dropStack(nargs - 4);
nargs = 4;
}
bool autoStop = false;
int duration = 0;
if (nargs == 4) {
autoStop = (bool)g_lingo->pop().asInt();
nargs--;
}
if (nargs == 3) {
duration = g_lingo->pop().asInt();
nargs--;
}
int endVol = g_lingo->pop().asInt();
int chan = g_lingo->pop().asInt();
g_lingo->push(Datum(me->fade(chan, endVol, duration, autoStop)));
}
void VoyagerXSoundXObj::m_frequency(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_frequency", nargs);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
ARGNUMCHECK(2);
int percent = g_lingo->pop().asInt();
int chan = g_lingo->pop().asInt();
me->frequency(chan, percent);
g_lingo->push(Datum(1));
}
void VoyagerXSoundXObj::m_pan(int nargs) {
g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_pan", nargs);
VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
ARGNUMCHECK(2);
int percent = g_lingo->pop().asInt();
int chan = g_lingo->pop().asInt();
me->pan(chan, percent);
g_lingo->push(Datum(1));
}
XOBJSTUB(VoyagerXSoundXObj::m_startrecord, 0)
XOBJSTUB(VoyagerXSoundXObj::m_stoprecord, 0)
XOBJSTUB(VoyagerXSoundXObj::m_recordpath, 0)
}