/* 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 . * */ #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("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(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(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(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(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(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(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(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(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(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(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) }