Initial support for sound code in HE games.
Split HE sound functions into separate file. svn-id: r19909
This commit is contained in:
parent
c2702891ce
commit
e01afb0bd4
16 changed files with 766 additions and 274 deletions
|
@ -1146,9 +1146,14 @@ void Actor::drawActorCostume(bool hitTestMode) {
|
|||
|
||||
if (_vm->_heversion >= 80 && _heNoTalkAnimation == 0 && _animProgress == 0) {
|
||||
if (_vm->getTalkingActor() == _number && !_vm->_string[0].no_talk_anim) {
|
||||
// Get sound var 19 of sound 1, if sound code is active.
|
||||
// Otherwise choose random animation
|
||||
setTalkCondition(_vm->_rnd.getRandomNumberRng(1, 10));
|
||||
int talkState = 0;
|
||||
|
||||
if (_vm->_sound->isSoundCodeUsed(1))
|
||||
talkState = _vm->_sound->getSoundVar(1, 19);
|
||||
if (talkState == 0)
|
||||
talkState = _vm->_rnd.getRandomNumberRng(1, 10);
|
||||
|
||||
setTalkCondition(talkState);
|
||||
} else {
|
||||
setTalkCondition(1);
|
||||
}
|
||||
|
@ -1557,8 +1562,7 @@ void Actor::setActorCostume(int c) {
|
|||
|
||||
if (_vm->_heversion >= 71 && _vm->getTalkingActor() == _number) {
|
||||
if (_vm->_heversion <= 95 || (_vm->_heversion >= 98 && _vm->VAR(_vm->VAR_SKIP_RESET_TALK_ACTOR) == 0)) {
|
||||
// TODO
|
||||
// _vm->setTalkingActor(0);
|
||||
_vm->setTalkingActor(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1086,7 +1086,8 @@ protected:
|
|||
|
||||
const OpcodeEntryV80he *_opcodesV80he;
|
||||
|
||||
int32 _heSBNGId;
|
||||
int32 _heSndResId;
|
||||
int _curSndId, _sndOffs1, _sndOffs2;
|
||||
|
||||
public:
|
||||
ScummEngine_v80he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
|
||||
|
@ -1102,11 +1103,13 @@ protected:
|
|||
|
||||
virtual void clearDrawQueues();
|
||||
|
||||
void createSound(int snd1id, int snd2id);
|
||||
|
||||
void drawLine(int x1, int y1, int x, int unk1, int unk2, int type, int id);
|
||||
void drawPixel(int x, int y, int flags);
|
||||
|
||||
/* HE version 80 script opcodes */
|
||||
void o80_loadSBNG();
|
||||
void o80_createSound();
|
||||
void o80_getFileSize();
|
||||
void o80_stringToInt();
|
||||
void o80_getSoundVar();
|
||||
|
@ -1300,7 +1303,7 @@ protected:
|
|||
void o100_resourceRoutines();
|
||||
void o100_wizImageOps();
|
||||
void o100_jumpToScript();
|
||||
void o100_loadSBNG();
|
||||
void o100_createSound();
|
||||
void o100_dim2dim2Array();
|
||||
void o100_paletteOps();
|
||||
void o100_jumpToScriptUnk();
|
||||
|
|
|
@ -42,6 +42,7 @@ MODULE_OBJS := \
|
|||
scumm/script_v6he.o \
|
||||
scumm/scumm.o \
|
||||
scumm/sound.o \
|
||||
scumm/sound_he.o \
|
||||
scumm/string.o \
|
||||
scumm/usage_bits.o \
|
||||
scumm/util.o \
|
||||
|
|
|
@ -1005,6 +1005,8 @@ bool ScummEngine::isResourceInUse(int type, int i) const {
|
|||
return _sound->isSoundInUse(i);
|
||||
case rtCharset:
|
||||
return _charset->getCurID() == i;
|
||||
case rtSpoolBuffer:
|
||||
return _sound->isSoundRunning(10000 + i);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -1348,6 +1350,9 @@ void ScummEngine::allocateArrays() {
|
|||
allocResTypeData(rtImage, MKID('AWIZ'), _numImages, "images", 1);
|
||||
allocResTypeData(rtTalkie, MKID('TLKE'), _numTalkies, "talkie", 1);
|
||||
|
||||
if (_heversion >= 70) {
|
||||
allocResTypeData(rtSpoolBuffer, MKID('NONE'), 9, "spool buffer", 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ScummEngine::dumpResource(const char *tag, int idx, const byte *ptr, int length) {
|
||||
|
@ -1577,6 +1582,8 @@ const char *resTypeFromId(int id) {
|
|||
return "Image";
|
||||
case rtTalkie:
|
||||
return "Talkie";
|
||||
case rtSpoolBuffer:
|
||||
return "SpoolBuffer";
|
||||
case rtNumTypes:
|
||||
return "NumTypes";
|
||||
default:
|
||||
|
|
|
@ -1797,4 +1797,119 @@ int ScummEngine_v72he::getSoundResourceSize(int id) {
|
|||
return size;
|
||||
}
|
||||
|
||||
void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
|
||||
debug(0, "createSound: snd1id %d snd2id %d", snd1id, snd2id);
|
||||
|
||||
byte *snd1Ptr, *snd2Ptr;
|
||||
byte *sbng1Ptr, *sbng2Ptr;
|
||||
byte *sdat1Ptr, *sdat2Ptr;
|
||||
byte *src, *dst, *tmp;
|
||||
int curOffs, len, offs, size;
|
||||
|
||||
if (snd2id == -1) {
|
||||
_sndOffs1 = 0;
|
||||
_sndOffs2 = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (snd1id != _curSndId) {
|
||||
_curSndId = snd1id;
|
||||
_sndOffs1 = 0;
|
||||
_sndOffs2 = 0;
|
||||
}
|
||||
|
||||
res.lock(rtSound, snd1id);
|
||||
res.lock(rtSound, snd2id);
|
||||
|
||||
snd1Ptr = getResourceAddress(rtSound, snd1id);
|
||||
snd2Ptr = getResourceAddress(rtSound, snd2id);
|
||||
|
||||
int i;
|
||||
int chan = -1;
|
||||
for (i = 0; i < ARRAYSIZE(_sound->_heChannel); i++) {
|
||||
if (_sound->_heChannel[i].sound == snd1id)
|
||||
chan = i;
|
||||
}
|
||||
|
||||
sbng1Ptr = heFindResource(MKID('SBNG'), snd1Ptr);
|
||||
sbng2Ptr = heFindResource(MKID('SBNG'), snd2Ptr);
|
||||
|
||||
if (sbng1Ptr != NULL && sbng2Ptr != NULL) {
|
||||
if (chan != -1 && _sound->_heChannel[chan].codeOffs > 0) {
|
||||
curOffs = _sound->_heChannel[chan].codeOffs;
|
||||
|
||||
src = snd1Ptr + curOffs;
|
||||
dst = sbng1Ptr + 8;
|
||||
size = READ_BE_UINT32(sbng1Ptr + 4);
|
||||
len = sbng1Ptr - snd1Ptr + size - curOffs;
|
||||
memcpy(dst, src, len);
|
||||
|
||||
|
||||
dst = sbng1Ptr + 8;
|
||||
while ((offs = READ_LE_UINT16(dst)) != 0)
|
||||
dst += offs;
|
||||
} else {
|
||||
dst = sbng1Ptr + 8;
|
||||
}
|
||||
|
||||
_sound->_heChannel[chan].codeOffs = sbng1Ptr - snd1Ptr + 8;
|
||||
|
||||
tmp = sbng2Ptr + 8;
|
||||
while ((offs = READ_LE_UINT16(tmp)) != 0) {
|
||||
tmp += offs;
|
||||
}
|
||||
|
||||
src = sbng2Ptr + 8;
|
||||
len = tmp - sbng2Ptr - 6;
|
||||
memcpy(dst, src, len);
|
||||
|
||||
int time;
|
||||
while (READ_LE_UINT16(dst) != 0) {
|
||||
time = READ_LE_UINT32(dst + 2);
|
||||
time += _sndOffs2;
|
||||
size = READ_LE_UINT16(dst);
|
||||
WRITE_LE_UINT32(dst + 2, time);
|
||||
dst += size;
|
||||
}
|
||||
}
|
||||
|
||||
int size1, size2;
|
||||
|
||||
sdat1Ptr = heFindResource(MKID('SDAT'), snd1Ptr);
|
||||
assert(sdat1Ptr);
|
||||
sdat2Ptr = heFindResource(MKID('SDAT'), snd2Ptr);
|
||||
assert(sdat2Ptr);
|
||||
|
||||
size1 = READ_BE_UINT32(sdat1Ptr + 4) - 8 - _sndOffs1;
|
||||
size2 = READ_BE_UINT32(sdat2Ptr + 4) - 8;
|
||||
|
||||
if (size2 < size1) {
|
||||
src = sdat2Ptr + 8;
|
||||
dst = sdat1Ptr + 8 + _sndOffs1;
|
||||
len = size2;
|
||||
|
||||
memcpy(dst, src, len);
|
||||
|
||||
_sndOffs1 += size2;
|
||||
_sndOffs2 += size2;
|
||||
} else {
|
||||
src = sdat2Ptr + 8;
|
||||
dst = sdat1Ptr + 8 + _sndOffs1;
|
||||
len = size1;
|
||||
|
||||
memcpy(dst, src, len);
|
||||
|
||||
int tmp3 = size2 - size1;
|
||||
if (tmp3 != 0) {
|
||||
// TODO: Additional copy
|
||||
}
|
||||
|
||||
_sndOffs1 += tmp3;
|
||||
_sndOffs2 += size2;
|
||||
}
|
||||
|
||||
res.unlock(rtSound, snd1id);
|
||||
res.unlock(rtSound, snd2id);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
|
|
@ -68,7 +68,7 @@ void ScummEngine_v100he::setupOpcodes() {
|
|||
OPCODE(o6_loadRoomWithEgo),
|
||||
OPCODE(o6_invalid),
|
||||
OPCODE(o72_setFilePath),
|
||||
OPCODE(o100_loadSBNG),
|
||||
OPCODE(o100_createSound),
|
||||
/* 18 */
|
||||
OPCODE(o6_cutscene),
|
||||
OPCODE(o6_pop),
|
||||
|
@ -708,28 +708,25 @@ void ScummEngine_v100he::o100_jumpToScript() {
|
|||
runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args);
|
||||
}
|
||||
|
||||
void ScummEngine_v100he::o100_loadSBNG() {
|
||||
// Loads SBNG sound resource
|
||||
void ScummEngine_v100he::o100_createSound() {
|
||||
byte subOp = fetchScriptByte();
|
||||
|
||||
switch (subOp) {
|
||||
case 0:
|
||||
_heSBNGId = pop();
|
||||
_heSndResId = pop();
|
||||
break;
|
||||
case 53:
|
||||
//loadSBNG(_heSBNGId, -1);
|
||||
createSound(_heSndResId, -1);
|
||||
break;
|
||||
case 92:
|
||||
// dummy case
|
||||
break;
|
||||
case 128:
|
||||
//loadSBNG(_heSBNGId, pop();
|
||||
pop();
|
||||
createSound(_heSndResId, pop());
|
||||
break;
|
||||
default:
|
||||
error("o100_loadSBNG: default case %d", subOp);
|
||||
error("o100_createSound: default case %d", subOp);
|
||||
}
|
||||
debug(1,"o100_loadSBNG stub (%d)",subOp);
|
||||
}
|
||||
|
||||
void ScummEngine_v100he::o100_dim2dimArray() {
|
||||
|
@ -1679,7 +1676,8 @@ void ScummEngine_v100he::o100_startSound() {
|
|||
value = pop();
|
||||
var = pop();
|
||||
_heSndSoundId = pop();
|
||||
debug(0,"o100_startSound: case 29 (snd %d, var %d, value %d)", _heSndSoundId, var, value);
|
||||
_sound->setSoundVar(_heSndSoundId, var, value);
|
||||
debug(0,"o100_startSound: case 83 (snd %d, var %d, value %d)", _heSndSoundId, var, value);
|
||||
break;
|
||||
case 92:
|
||||
debug(0, "o100_startSound (ID %d, Offset %d, Channel %d, Flags %d)", _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
|
||||
|
|
|
@ -868,9 +868,7 @@ void ScummEngine_v72he::o72_getTimer() {
|
|||
int cmd = fetchScriptByte();
|
||||
|
||||
if (cmd == 10 || cmd == 50) {
|
||||
checkRange(3, 1, timer, "o72_getTimer: Timer %d out of range(%d)");
|
||||
int diff = _system->getMillis() - _timers[timer];
|
||||
push(diff);
|
||||
push(getHETimer(timer));
|
||||
} else {
|
||||
push(0);
|
||||
}
|
||||
|
@ -881,8 +879,7 @@ void ScummEngine_v72he::o72_setTimer() {
|
|||
int cmd = fetchScriptByte();
|
||||
|
||||
if (cmd == 158 || cmd == 61) {
|
||||
checkRange(3, 1, timer, "o72_setTimer: Timer %d out of range(%d)");
|
||||
_timers[timer] = _system->getMillis();
|
||||
setHETimer(timer);
|
||||
} else {
|
||||
error("TIMER command %d?", cmd);
|
||||
}
|
||||
|
@ -890,8 +887,7 @@ void ScummEngine_v72he::o72_setTimer() {
|
|||
|
||||
void ScummEngine_v72he::o72_getSoundPosition() {
|
||||
int snd = pop();
|
||||
push(_sound->getSoundElapsedTime(snd) * 10);
|
||||
debug(1,"o72_getSoundPosition (%d)", snd);
|
||||
push(_sound->getSoundPos(snd));
|
||||
}
|
||||
|
||||
void ScummEngine_v72he::o72_startScript() {
|
||||
|
|
|
@ -436,11 +436,13 @@ void ScummEngine_v70he::o70_startSound() {
|
|||
value = pop();
|
||||
var = pop();
|
||||
_heSndSoundId = pop();
|
||||
_sound->setSoundVar(_heSndSoundId, var, value);
|
||||
debug(0,"o70_startSound: case 23 (snd %d, var %d, value %d)", _heSndSoundId, var, value);
|
||||
break;
|
||||
case 25:
|
||||
value = pop();
|
||||
_heSndSoundId = pop();
|
||||
debug(0, "o70_startSound: case 25 (ID %d, Offset 0, Channel 0, Flags 8)", _heSndSoundId);
|
||||
_sound->addSoundToQueue(_heSndSoundId, 0, 0, 8);
|
||||
case 56:
|
||||
_heSndFlags |= 16;
|
||||
|
@ -467,7 +469,7 @@ void ScummEngine_v70he::o70_startSound() {
|
|||
_heSndFlags |= 1;
|
||||
break;
|
||||
case 255:
|
||||
debug(1, "o70_startSound (ID %d, Offset %d, Channel %d, Flags %d)", _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
|
||||
debug(0, "o70_startSound (ID %d, Offset %d, Channel %d, Flags %d)", _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
|
||||
_sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
|
||||
_heSndFlags = 0;
|
||||
break;
|
||||
|
|
|
@ -129,7 +129,7 @@ void ScummEngine_v80he::setupOpcodes() {
|
|||
OPCODE(o6_writeWordVar),
|
||||
/* 44 */
|
||||
OPCODE(o6_invalid),
|
||||
OPCODE(o80_loadSBNG),
|
||||
OPCODE(o80_createSound),
|
||||
OPCODE(o80_getFileSize),
|
||||
OPCODE(o6_wordArrayWrite),
|
||||
/* 48 */
|
||||
|
@ -376,28 +376,25 @@ const char *ScummEngine_v80he::getOpcodeDesc(byte i) {
|
|||
return _opcodesV80he[i].desc;
|
||||
}
|
||||
|
||||
void ScummEngine_v80he::o80_loadSBNG() {
|
||||
// Loads SBNG sound resource
|
||||
void ScummEngine_v80he::o80_createSound() {
|
||||
byte subOp = fetchScriptByte();
|
||||
|
||||
switch (subOp) {
|
||||
case 27:
|
||||
//loadSBNG(_heSBNGId, pop();
|
||||
pop();
|
||||
createSound(_heSndResId, pop());
|
||||
break;
|
||||
case 217:
|
||||
//loadSBNG(_heSBNGId, -1);
|
||||
createSound(_heSndResId, -1);
|
||||
break;
|
||||
case 232:
|
||||
_heSBNGId = pop();
|
||||
_heSndResId = pop();
|
||||
break;
|
||||
case 255:
|
||||
// dummy case
|
||||
break;
|
||||
default:
|
||||
error("o80_loadSBNG: default case %d", subOp);
|
||||
error("o80_createSound: default case %d", subOp);
|
||||
}
|
||||
debug(1,"o80_loadSBNG stub (%d)",subOp);
|
||||
}
|
||||
|
||||
void ScummEngine_v80he::o80_getFileSize() {
|
||||
|
@ -432,14 +429,9 @@ void ScummEngine_v80he::o80_stringToInt() {
|
|||
}
|
||||
|
||||
void ScummEngine_v80he::o80_getSoundVar() {
|
||||
// Checks sound variable
|
||||
int var = pop();
|
||||
int snd = pop();
|
||||
int rnd = _rnd.getRandomNumberRng(1, 3);
|
||||
|
||||
checkRange(27, 0, var, "Illegal sound variable %d");
|
||||
push (rnd);
|
||||
debug(1,"o80_getSoundVar stub (snd %d, var %d)", snd, var);
|
||||
push(_sound->getSoundVar(snd, var));
|
||||
}
|
||||
|
||||
void ScummEngine_v80he::o80_localizeArrayToRoom() {
|
||||
|
|
|
@ -127,7 +127,7 @@ void ScummEngine_v90he::setupOpcodes() {
|
|||
OPCODE(o6_writeWordVar),
|
||||
/* 44 */
|
||||
OPCODE(o90_getObjectData),
|
||||
OPCODE(o80_loadSBNG),
|
||||
OPCODE(o80_createSound),
|
||||
OPCODE(o80_getFileSize),
|
||||
OPCODE(o6_wordArrayWrite),
|
||||
/* 48 */
|
||||
|
|
|
@ -1210,7 +1210,7 @@ ScummEngine::ScummEngine(GameDetector *detector, OSystem *syst, const ScummGameS
|
|||
_actorClipOverride.right = 640;
|
||||
|
||||
_skipDrawObject = 0;
|
||||
memset(_timers, 0, sizeof(_timers));
|
||||
memset(_heTimers, 0, sizeof(_heTimers));
|
||||
|
||||
memset(_akosQueue, 0, sizeof(_akosQueue));
|
||||
_akosQueuePos = 0;
|
||||
|
@ -1340,6 +1340,8 @@ ScummEngine::ScummEngine(GameDetector *detector, OSystem *syst, const ScummGameS
|
|||
VAR_SKIP_RESET_TALK_ACTOR = 0xFF;
|
||||
VAR_MUSIC_CHANNEL = 0xFF;
|
||||
VAR_SOUND_CHANNEL = 0xFF;
|
||||
VAR_SOUNDCODE_TMR = 0xFF;
|
||||
VAR_DEFAULT_SOUND_CHANNEL = 0xFF;
|
||||
|
||||
VAR_NUM_SCRIPT_CYCLES = 0xFF;
|
||||
VAR_SCRIPT_CYCLE = 0xFF;
|
||||
|
@ -1605,7 +1607,10 @@ ScummEngine_v72he::ScummEngine_v72he(GameDetector *detector, OSystem *syst, cons
|
|||
|
||||
ScummEngine_v80he::ScummEngine_v80he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
|
||||
: ScummEngine_v72he(detector, syst, gs, md5sum, substResFileNameIndex) {
|
||||
_heSBNGId = 0;
|
||||
_heSndResId = 0;
|
||||
_curSndId = 0;
|
||||
_sndOffs1 = 0;
|
||||
_sndOffs2 = 0;
|
||||
}
|
||||
|
||||
ScummEngine_v90he::ScummEngine_v90he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
|
||||
|
@ -2489,6 +2494,9 @@ load_game:
|
|||
_fullRedraw = true;
|
||||
}
|
||||
|
||||
if (_heversion >= 80) {
|
||||
_sound->processSoundCode();
|
||||
}
|
||||
runAllScripts();
|
||||
checkExecVerbs();
|
||||
checkAndRunSentenceScript();
|
||||
|
@ -2609,6 +2617,17 @@ load_game:
|
|||
#pragma mark --- SCUMM ---
|
||||
#pragma mark -
|
||||
|
||||
int ScummEngine::getHETimer(int timer) {
|
||||
checkRange(15, 1, timer, "getHETimer: Timer %d out of range(%d)");
|
||||
int time = _system->getMillis() - _heTimers[timer];
|
||||
return time;
|
||||
}
|
||||
|
||||
void ScummEngine::setHETimer(int timer) {
|
||||
checkRange(15, 1, timer, "setHETimer: Timer %d out of range(%d)");
|
||||
_heTimers[timer] = _system->getMillis();
|
||||
}
|
||||
|
||||
void ScummEngine::pauseGame() {
|
||||
pauseDialog();
|
||||
}
|
||||
|
|
|
@ -311,8 +311,9 @@ enum ResTypes {
|
|||
rtRoomImage = 18,
|
||||
rtImage = 19,
|
||||
rtTalkie = 20,
|
||||
rtLast = 20,
|
||||
rtNumTypes = 21
|
||||
rtSpoolBuffer = 21,
|
||||
rtLast = 21,
|
||||
rtNumTypes = 22
|
||||
};
|
||||
|
||||
class ResourceManager {
|
||||
|
@ -1074,14 +1075,19 @@ protected:
|
|||
bool testGfxOtherUsageBits(int strip, int bit);
|
||||
|
||||
public:
|
||||
uint8 *_hePalettes;
|
||||
byte _HEV7ActorPalette[256];
|
||||
byte _roomPalette[256];
|
||||
byte *_shadowPalette;
|
||||
bool _skipDrawObject;
|
||||
int _timers[4];
|
||||
int _voiceMode;
|
||||
|
||||
// HE specific
|
||||
byte _HEV7ActorPalette[256];
|
||||
uint8 *_hePalettes;
|
||||
|
||||
int _heTimers[16];
|
||||
int getHETimer(int timer);
|
||||
void setHETimer(int timer);
|
||||
|
||||
protected:
|
||||
int _shadowPaletteSize;
|
||||
byte _currentPalette[3 * 256];
|
||||
|
@ -1310,7 +1316,7 @@ public:
|
|||
byte VAR_SAVELOAD_SCRIPT; // V6/V7 (not HE)
|
||||
byte VAR_SAVELOAD_SCRIPT2; // V6/V7 (not HE)
|
||||
|
||||
// V6/V7 specific variables (actually, they are only used in FT and Sam, it seems?)
|
||||
// V6/V7 specific variables (FT & Sam & Max specific)
|
||||
byte VAR_CHARSET_MASK;
|
||||
|
||||
// V6 specific variables
|
||||
|
@ -1328,6 +1334,8 @@ public:
|
|||
byte VAR_SKIP_RESET_TALK_ACTOR;
|
||||
byte VAR_MUSIC_CHANNEL;
|
||||
byte VAR_SOUND_CHANNEL;
|
||||
byte VAR_SOUNDCODE_TMR;
|
||||
byte VAR_DEFAULT_SOUND_CHANNEL;
|
||||
|
||||
byte VAR_SCRIPT_CYCLE;
|
||||
byte VAR_NUM_SCRIPT_CYCLES;
|
||||
|
|
253
scumm/sound.cpp
253
scumm/sound.cpp
|
@ -29,6 +29,7 @@
|
|||
#include "scumm/util.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/system.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/util.h"
|
||||
|
||||
|
@ -79,6 +80,7 @@ Sound::Sound(ScummEngine *parent)
|
|||
_sfxMode(0),
|
||||
_heMusicTracks(0) {
|
||||
|
||||
memset(_heChannel, 0, sizeof(_heChannel));
|
||||
memset(_soundQue, 0, sizeof(_soundQue));
|
||||
memset(_soundQue2, 0, sizeof(_soundQue2));
|
||||
memset(_mouthSyncTimes, 0, sizeof(_mouthSyncTimes));
|
||||
|
@ -92,9 +94,16 @@ Sound::~Sound() {
|
|||
void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) {
|
||||
if (_vm->VAR_LAST_SOUND != 0xFF)
|
||||
_vm->VAR(_vm->VAR_LAST_SOUND) = sound;
|
||||
|
||||
if (heFlags & 16) {
|
||||
playHESound(sound, heOffset, heChannel, heFlags);
|
||||
return;
|
||||
}
|
||||
|
||||
// HE music resources are in separate file
|
||||
if (sound <= _vm->_numSounds)
|
||||
_vm->ensureResourceLoaded(rtSound, sound);
|
||||
|
||||
addSoundToQueue2(sound, heOffset, heChannel, heFlags);
|
||||
}
|
||||
|
||||
|
@ -140,8 +149,12 @@ void Sound::processSoundQueues() {
|
|||
heOffset = _soundQue2[_soundQue2Pos].offset;
|
||||
heChannel = _soundQue2[_soundQue2Pos].channel;
|
||||
heFlags = _soundQue2[_soundQue2Pos].flags;
|
||||
if (snd)
|
||||
playSound(snd, heOffset, heChannel, heFlags);
|
||||
if (snd) {
|
||||
if (_vm->_heversion>= 60)
|
||||
playHESound(snd, heOffset, heChannel, heFlags);
|
||||
else
|
||||
playSound(snd);
|
||||
}
|
||||
}
|
||||
|
||||
while (i < _soundQuePos) {
|
||||
|
@ -168,64 +181,7 @@ void Sound::processSoundQueues() {
|
|||
_soundQuePos = 0;
|
||||
}
|
||||
|
||||
void Sound::setOverrideFreq(int freq) {
|
||||
_overrideFreq = freq;
|
||||
}
|
||||
|
||||
void Sound::setupHEMusicFile() {
|
||||
int i, total_size;
|
||||
char buf[32], buf1[128];
|
||||
Common::File musicFile;
|
||||
|
||||
sprintf(buf, "%s.he4", _vm->getGameName());
|
||||
|
||||
if (_vm->_substResFileNameIndex > 0) {
|
||||
_vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
|
||||
strcpy(buf, buf1);
|
||||
}
|
||||
if (musicFile.open(buf) == true) {
|
||||
musicFile.seek(4, SEEK_SET);
|
||||
total_size = musicFile.readUint32BE();
|
||||
musicFile.seek(16, SEEK_SET);
|
||||
_heMusicTracks = musicFile.readUint32LE();
|
||||
debug(0, "Total music tracks %d", _heMusicTracks);
|
||||
|
||||
int musicStart = (_vm->_heversion >= 80) ? 56 : 20;
|
||||
musicFile.seek(musicStart, SEEK_SET);
|
||||
|
||||
_heMusic = (HEMusic *)malloc((_heMusicTracks + 1) * sizeof(HEMusic));
|
||||
for (i = 0; i < _heMusicTracks; i++) {
|
||||
_heMusic[i].id = musicFile.readUint32LE();
|
||||
_heMusic[i].offset = musicFile.readUint32LE();
|
||||
_heMusic[i].size = musicFile.readUint32LE();
|
||||
|
||||
if (_vm->_heversion >= 80) {
|
||||
musicFile.seek(+9, SEEK_CUR);
|
||||
} else {
|
||||
musicFile.seek(+13, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
musicFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
bool Sound::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _heMusicTracks; i++) {
|
||||
if (_heMusic[i].id == id) {
|
||||
musicOffs = _heMusic[i].offset;
|
||||
musicSize = _heMusic[i].size;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Sound::playSound(int soundID, int heOffset, int heChannel, int heFlags) {
|
||||
debug(5,"playSound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
|
||||
void Sound::playSound(int soundID) {
|
||||
byte *mallocedPtr = NULL;
|
||||
byte *ptr;
|
||||
char *sound;
|
||||
|
@ -233,56 +189,10 @@ void Sound::playSound(int soundID, int heOffset, int heChannel, int heFlags) {
|
|||
int rate;
|
||||
byte flags = Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE;
|
||||
|
||||
if (heChannel == -1) {
|
||||
heChannel = 1;
|
||||
}
|
||||
if (_vm->_heversion >= 70 && soundID > _vm->_numSounds) {
|
||||
debug(1, "playSound #%d", soundID);
|
||||
|
||||
if (soundID >= 10000) {
|
||||
// Special codes, used in pjgames
|
||||
return;
|
||||
}
|
||||
|
||||
int music_offs;
|
||||
char buf[32], buf1[128];
|
||||
Common::File musicFile;
|
||||
|
||||
sprintf(buf, "%s.he4", _vm->getGameName());
|
||||
|
||||
if (_vm->_substResFileNameIndex > 0) {
|
||||
_vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
|
||||
strcpy(buf, buf1);
|
||||
}
|
||||
if (musicFile.open(buf) == false) {
|
||||
warning("playSound: Can't open music file %s", buf);
|
||||
return;
|
||||
}
|
||||
if (!getHEMusicDetails(soundID, music_offs, size)) {
|
||||
debug(0, "playSound: musicID %d not found", soundID);
|
||||
return;
|
||||
}
|
||||
|
||||
musicFile.seek(music_offs, SEEK_SET);
|
||||
ptr = (byte *)malloc(size);
|
||||
musicFile.read(ptr, size);
|
||||
musicFile.close();
|
||||
|
||||
_vm->_mixer->stopID(_currentMusic);
|
||||
_currentMusic = soundID;
|
||||
if (_vm->_heversion == 70) {
|
||||
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], ptr, size, 11025, flags, soundID);
|
||||
return;
|
||||
}
|
||||
|
||||
// This pointer will be freed at the end of the function
|
||||
mallocedPtr = ptr;
|
||||
} else {
|
||||
debugC(DEBUG_SOUND, "playSound #%d (room %d)", soundID,
|
||||
_vm->getResourceRoomNr(rtSound, soundID));
|
||||
|
||||
ptr = _vm->getResourceAddress(rtSound, soundID);
|
||||
}
|
||||
|
||||
if (!ptr) {
|
||||
return;
|
||||
|
@ -304,7 +214,6 @@ void Sound::playSound(int soundID, int heOffset, int heChannel, int heFlags) {
|
|||
memcpy(sound, ptr, size);
|
||||
_vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
|
||||
}
|
||||
|
||||
// WORKAROUND bug # 1311447
|
||||
else if (READ_UINT32(ptr) == MKID(0x460e200d)) {
|
||||
// This sound resource occurs in the Macintosh version of Monkey Island.
|
||||
|
@ -327,91 +236,6 @@ void Sound::playSound(int soundID, int heOffset, int heChannel, int heFlags) {
|
|||
memcpy(sound, ptr, size);
|
||||
_vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
|
||||
}
|
||||
|
||||
// Support for sound in later Backyard sports games
|
||||
else if (READ_UINT32(ptr) == MKID('RIFF')) {
|
||||
uint16 type;
|
||||
int blockAlign;
|
||||
size = READ_LE_UINT32(ptr + 4);
|
||||
Common::MemoryReadStream stream(ptr, size);
|
||||
|
||||
if (!loadWAVFromStream(stream, size, rate, flags, &type, &blockAlign)) {
|
||||
error("playSound: Not a valid WAV file");
|
||||
}
|
||||
|
||||
if (type == 17) {
|
||||
AudioStream *voxStream = new ADPCMInputStream(&stream, size, kADPCMIma, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
|
||||
|
||||
sound = (char *)malloc(size * 4);
|
||||
size = voxStream->readBuffer((int16*)sound, size * 2);
|
||||
size *= 2; // 16bits.
|
||||
} else {
|
||||
// Allocate a sound buffer, copy the data into it, and play
|
||||
sound = (char *)malloc(size);
|
||||
memcpy(sound, ptr + stream.pos(), size);
|
||||
}
|
||||
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
|
||||
}
|
||||
// Support for sound in Humongous Entertainment games
|
||||
else if (READ_UINT32(ptr) == MKID('DIGI') || READ_UINT32(ptr) == MKID('TALK') || READ_UINT32(ptr) == MKID('HSHD')) {
|
||||
int priority;
|
||||
|
||||
if (READ_UINT32(ptr) == MKID('HSHD')) {
|
||||
priority = READ_LE_UINT16(ptr + 10);
|
||||
rate = READ_LE_UINT16(ptr + 14);
|
||||
ptr += READ_BE_UINT32(ptr + 4);
|
||||
} else {
|
||||
priority = READ_LE_UINT16(ptr + 18);
|
||||
rate = READ_LE_UINT16(ptr + 22);
|
||||
ptr += 8 + READ_BE_UINT32(ptr + 12);
|
||||
}
|
||||
|
||||
if (READ_UINT32(ptr) == MKID('SBNG')) {
|
||||
ptr += READ_BE_UINT32(ptr + 4);
|
||||
}
|
||||
|
||||
assert(READ_UINT32(ptr) == MKID('SDAT'));
|
||||
size = READ_BE_UINT32(ptr+4) - 8;
|
||||
if (heOffset < 0 || heOffset > size) {
|
||||
// Occurs when making fireworks in puttmoon
|
||||
debug(0, "playSound: Invalid sound offset (%d) in sound %d", heOffset, soundID);
|
||||
heOffset = 0;
|
||||
}
|
||||
size -= heOffset;
|
||||
|
||||
if (_overrideFreq) {
|
||||
// Used by the piano in Fatty Bear's Birthday Surprise
|
||||
rate = _overrideFreq;
|
||||
_overrideFreq = 0;
|
||||
}
|
||||
|
||||
if (heFlags & 1) {
|
||||
// TODO
|
||||
// flags |= Audio::Mixer::FLAG_LOOP;
|
||||
}
|
||||
|
||||
// Allocate a sound buffer, copy the data into it, and play
|
||||
sound = (char *)malloc(size);
|
||||
memcpy(sound, ptr + heOffset + 8, size);
|
||||
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
|
||||
}
|
||||
// Support for PCM music in 3DO versions of Humongous Entertainment games
|
||||
else if (READ_UINT32(ptr) == MKID('MRAW')) {
|
||||
ptr += 8 + READ_BE_UINT32(ptr+12);
|
||||
if (READ_UINT32(ptr) != MKID('SDAT'))
|
||||
return;
|
||||
|
||||
size = READ_BE_UINT32(ptr+4) - 8;
|
||||
rate = 22050;
|
||||
flags = Audio::Mixer::FLAG_AUTOFREE;
|
||||
|
||||
// Allocate a sound buffer, copy the data into it, and play
|
||||
sound = (char *)malloc(size);
|
||||
memcpy(sound, ptr + 8, size);
|
||||
_vm->_mixer->stopID(_currentMusic);
|
||||
_currentMusic = soundID;
|
||||
_vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
|
||||
}
|
||||
// Support for sampled sound effects in Monkey Island 1 and 2
|
||||
else if (READ_UINT32(ptr) == MKID('SBL ')) {
|
||||
debugC(DEBUG_SOUND, "Using SBL sound effect");
|
||||
|
@ -696,30 +520,6 @@ static int compareMP3OffsetTable(const void *a, const void *b) {
|
|||
return ((const MP3OffsetTable *)a)->org_offset - ((const MP3OffsetTable *)b)->org_offset;
|
||||
}
|
||||
|
||||
void Sound::startHETalkSound(uint32 offset) {
|
||||
byte *ptr;
|
||||
int32 size;
|
||||
|
||||
if (ConfMan.getBool("speech_mute"))
|
||||
return;
|
||||
|
||||
if (!_sfxFile->isOpen()) {
|
||||
error("startHETalkSound: Speech file is not open");
|
||||
return;
|
||||
}
|
||||
|
||||
_sfxMode |= 2;
|
||||
_vm->res.nukeResource(rtSound, 1);
|
||||
_sfxFile->seek(offset + 4, SEEK_SET);
|
||||
size = _sfxFile->readUint32BE() - 8;
|
||||
_vm->res.createResource(rtSound, 1, size);
|
||||
ptr = _vm->getResourceAddress(rtSound, 1);
|
||||
_sfxFile->read(ptr, size);
|
||||
|
||||
int channel = (_vm->VAR_SOUND_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_SOUND_CHANNEL) : 0;
|
||||
addSoundToQueue2(1, 0, channel, 0);
|
||||
}
|
||||
|
||||
void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle) {
|
||||
int num = 0, i;
|
||||
int size = 0;
|
||||
|
@ -1008,7 +808,13 @@ void Sound::stopSound(int sound) {
|
|||
|
||||
if (_vm->_heversion >= 70) {
|
||||
if ( sound >= 10000) {
|
||||
_vm->_mixer->stopHandle(_heSoundChannels[sound - 10000]);
|
||||
int chan = sound - 10000;
|
||||
_vm->_mixer->stopHandle(_heSoundChannels[chan]);
|
||||
_heChannel[chan].sound = 0;
|
||||
_heChannel[chan].priority = 0;
|
||||
_heChannel[chan].sbngBlock = 0;
|
||||
_heChannel[chan].codeOffs = 0;
|
||||
memset(_heChannel[chan].soundVars, 0, sizeof(_heChannel[chan].soundVars));
|
||||
}
|
||||
} else if (_vm->_heversion >= 60) {
|
||||
if (sound == -2) {
|
||||
|
@ -1033,6 +839,16 @@ void Sound::stopSound(int sound) {
|
|||
if (_vm->_musicEngine)
|
||||
_vm->_musicEngine->stopSound(sound);
|
||||
|
||||
for (i = 0; i < ARRAYSIZE(_heChannel); i++) {
|
||||
if (_heChannel[i].sound == sound) {
|
||||
_heChannel[i].sound = 0;
|
||||
_heChannel[i].priority = 0;
|
||||
_heChannel[i].sbngBlock = 0;
|
||||
_heChannel[i].codeOffs = 0;
|
||||
memset(_heChannel[i].soundVars, 0, sizeof(_heChannel[i].soundVars));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAYSIZE(_soundQue2); i++) {
|
||||
if (_soundQue2[i].sound == sound) {
|
||||
_soundQue2[i].sound = 0;
|
||||
|
@ -1050,6 +866,9 @@ void Sound::stopAllSounds() {
|
|||
stopCDTimer();
|
||||
}
|
||||
|
||||
// Clear sound channels for HE games
|
||||
memset(_heChannel, 0, sizeof(_heChannel));
|
||||
|
||||
// Clear the (secondary) sound queue
|
||||
_soundQue2Pos = 0;
|
||||
memset(_soundQue2, 0, sizeof(_soundQue2));
|
||||
|
|
|
@ -95,6 +95,16 @@ protected:
|
|||
HEMusic *_heMusic;
|
||||
int16 _heMusicTracks;
|
||||
|
||||
public: // Used by createSound()
|
||||
struct {
|
||||
int sound;
|
||||
int codeOffs;
|
||||
int priority;
|
||||
int sbngBlock;
|
||||
int soundVars[27];
|
||||
int timer;
|
||||
} _heChannel[9];
|
||||
|
||||
public:
|
||||
Audio::SoundHandle _talkChannelHandle; // Handle of mixer channel actor is talking on
|
||||
Audio::SoundHandle _heSoundChannels[8];
|
||||
|
@ -109,9 +119,8 @@ public:
|
|||
void addSoundToQueue2(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0);
|
||||
void processSound();
|
||||
void processSoundQueues();
|
||||
void setOverrideFreq(int freq);
|
||||
void playSound(int soundID, int heOffset, int heChannel, int heFlags);
|
||||
void startHETalkSound(uint32 offset);
|
||||
|
||||
void playSound(int soundID);
|
||||
void startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle = NULL);
|
||||
void stopTalkSound();
|
||||
bool isMouthSyncOff(uint pos);
|
||||
|
@ -134,8 +143,19 @@ public:
|
|||
void updateCD();
|
||||
int getCurrentCDSound() const { return _currentCDSound; }
|
||||
|
||||
void setupHEMusicFile();
|
||||
// HE specific
|
||||
bool getHEMusicDetails(int id, int &musicOffs, int &musicSize);
|
||||
int isSoundCodeUsed(int sound);
|
||||
int getSoundPos(int sound);
|
||||
int getSoundPriority(int sound);
|
||||
int getSoundVar(int sound, int var);
|
||||
void setSoundVar(int sound, int var, int val);
|
||||
void playHESound(int soundID, int heOffset, int heChannel, int heFlags);
|
||||
void processSoundCode();
|
||||
void processSoundOpcodes(int sound, byte *codePtr, int *soundVars);
|
||||
void setOverrideFreq(int freq);
|
||||
void setupHEMusicFile();
|
||||
void startHETalkSound(uint32 offset);
|
||||
|
||||
// Used by the save/load system:
|
||||
void saveLoadWithSerializer(Serializer *ser);
|
||||
|
|
506
scumm/sound_he.cpp
Normal file
506
scumm/sound_he.cpp
Normal file
|
@ -0,0 +1,506 @@
|
|||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2001 Ludvig Strigeus
|
||||
* Copyright (C) 2001-2005 The ScummVM project
|
||||
*
|
||||
* 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.
|
||||
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Header$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stdafx.h"
|
||||
#include "scumm/actor.h"
|
||||
#include "scumm/scumm.h"
|
||||
#include "scumm/sound.h"
|
||||
#include "scumm/util.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/system.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "sound/adpcm.h"
|
||||
#include "sound/audiocd.h"
|
||||
#include "sound/flac.h"
|
||||
#include "sound/mididrv.h"
|
||||
#include "sound/mixer.h"
|
||||
#include "sound/mp3.h"
|
||||
#include "sound/voc.h"
|
||||
#include "sound/vorbis.h"
|
||||
#include "sound/wave.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
int Sound::isSoundCodeUsed(int sound) {
|
||||
int chan = -1;
|
||||
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
|
||||
if (_heChannel[i].sound == sound)
|
||||
chan = i;
|
||||
}
|
||||
|
||||
if (chan != -1) {
|
||||
return _heChannel[chan].sbngBlock;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int Sound::getSoundPos(int sound) {
|
||||
int chan = -1;
|
||||
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
|
||||
if (_heChannel[i].sound == sound)
|
||||
chan = i;
|
||||
}
|
||||
|
||||
if (chan != -1) {
|
||||
int time = _vm->getHETimer(chan + 4) * 11025 / 1000;
|
||||
return time;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int Sound::getSoundPriority(int sound) {
|
||||
int chan = -1;
|
||||
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
|
||||
if (_heChannel[i].sound == sound)
|
||||
chan = i;
|
||||
}
|
||||
|
||||
if (chan != -1) {
|
||||
return _heChannel[chan].priority;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int Sound::getSoundVar(int sound, int var) {
|
||||
checkRange(25, 0, var, "Illegal sound variable %d");
|
||||
|
||||
int chan = -1;
|
||||
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
|
||||
if (_heChannel[i].sound == sound)
|
||||
chan = i;
|
||||
}
|
||||
|
||||
if (chan != -1) {
|
||||
//debug(1, "getSoundVar: sound %d var %d result %d\n", sound, var, _heChannel[chan].soundVars[var]);
|
||||
return _heChannel[chan].soundVars[var];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::setSoundVar(int sound, int var, int val) {
|
||||
checkRange(25, 0, var, "Illegal sound variable %d");
|
||||
|
||||
int chan = -1;
|
||||
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
|
||||
if (_heChannel[i].sound == sound)
|
||||
chan = i;
|
||||
}
|
||||
|
||||
if (chan != -1) {
|
||||
_heChannel[chan].soundVars[var] = val;
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::setOverrideFreq(int freq) {
|
||||
_overrideFreq = freq;
|
||||
}
|
||||
|
||||
void Sound::setupHEMusicFile() {
|
||||
int i, total_size;
|
||||
char buf[32], buf1[128];
|
||||
Common::File musicFile;
|
||||
|
||||
sprintf(buf, "%s.he4", _vm->getGameName());
|
||||
|
||||
if (_vm->_substResFileNameIndex > 0) {
|
||||
_vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
|
||||
strcpy(buf, buf1);
|
||||
}
|
||||
if (musicFile.open(buf) == true) {
|
||||
musicFile.seek(4, SEEK_SET);
|
||||
total_size = musicFile.readUint32BE();
|
||||
musicFile.seek(16, SEEK_SET);
|
||||
_heMusicTracks = musicFile.readUint32LE();
|
||||
debug(0, "Total music tracks %d", _heMusicTracks);
|
||||
|
||||
int musicStart = (_vm->_heversion >= 80) ? 56 : 20;
|
||||
musicFile.seek(musicStart, SEEK_SET);
|
||||
|
||||
_heMusic = (HEMusic *)malloc((_heMusicTracks + 1) * sizeof(HEMusic));
|
||||
for (i = 0; i < _heMusicTracks; i++) {
|
||||
_heMusic[i].id = musicFile.readUint32LE();
|
||||
_heMusic[i].offset = musicFile.readUint32LE();
|
||||
_heMusic[i].size = musicFile.readUint32LE();
|
||||
|
||||
if (_vm->_heversion >= 80) {
|
||||
musicFile.seek(+9, SEEK_CUR);
|
||||
} else {
|
||||
musicFile.seek(+13, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
musicFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
bool Sound::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _heMusicTracks; i++) {
|
||||
if (_heMusic[i].id == id) {
|
||||
musicOffs = _heMusic[i].offset;
|
||||
musicSize = _heMusic[i].size;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Sound::processSoundCode() {
|
||||
byte *codePtr;
|
||||
int chan, tmr, size, time;
|
||||
|
||||
for (chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
|
||||
if (_heChannel[chan].sound == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_heChannel[chan].codeOffs == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tmr = _vm->getHETimer(chan + 4) * 11025 / 1000;
|
||||
tmr += _vm->VAR(_vm->VAR_SOUNDCODE_TMR);
|
||||
if (tmr < 0)
|
||||
tmr = 0;
|
||||
|
||||
if (_heChannel[chan].sound > _vm->_numSounds) {
|
||||
codePtr = _vm->getResourceAddress(rtSpoolBuffer, chan);
|
||||
} else {
|
||||
codePtr = _vm->getResourceAddress(rtSound, _heChannel[chan].sound);
|
||||
}
|
||||
assert(codePtr);
|
||||
codePtr += _heChannel[chan].codeOffs;
|
||||
|
||||
while(1) {
|
||||
size = READ_LE_UINT16(codePtr);
|
||||
time = READ_LE_UINT32(codePtr + 2);
|
||||
|
||||
if (size == 0) {
|
||||
_heChannel[chan].codeOffs = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
debug(1, "Channel %d Timer %d Time %d", chan,tmr, time);
|
||||
if (time >= tmr)
|
||||
break;
|
||||
|
||||
processSoundOpcodes(_heChannel[chan].sound, codePtr + 6, _heChannel[chan].soundVars);
|
||||
|
||||
codePtr += size;
|
||||
_heChannel[chan].codeOffs += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
|
||||
int edi, opcode, var, val;
|
||||
|
||||
while(READ_LE_UINT16(codePtr) != 0) {
|
||||
codePtr += 2;
|
||||
opcode = READ_LE_UINT16(codePtr); codePtr += 2;
|
||||
opcode &= ~0xF000;
|
||||
opcode /= 16;
|
||||
edi = opcode;
|
||||
opcode &= ~3;
|
||||
edi &= 3;
|
||||
|
||||
debug(1, "processSoundOpcodes: sound %d opcode %d", sound, opcode);
|
||||
switch (opcode) {
|
||||
case 0: // Continue
|
||||
break;
|
||||
case 16: // Set talk state
|
||||
val = READ_LE_UINT16(codePtr); codePtr += 2;
|
||||
setSoundVar(sound, 19, val);
|
||||
break;
|
||||
case 32: // Set var
|
||||
var = READ_LE_UINT16(codePtr); codePtr += 2;
|
||||
val = READ_LE_UINT16(codePtr); codePtr += 2;
|
||||
if (edi == 2) {
|
||||
val = getSoundVar(sound, val);
|
||||
}
|
||||
setSoundVar(sound, var, val);
|
||||
break;
|
||||
case 48: // Add
|
||||
var = READ_LE_UINT16(codePtr); codePtr += 2;;
|
||||
val = READ_LE_UINT16(codePtr); codePtr += 2;;
|
||||
|
||||
val = getSoundVar(sound, var) + val;
|
||||
setSoundVar(sound, var, val);
|
||||
break;
|
||||
case 56: // Subtract
|
||||
var = READ_LE_UINT16(codePtr); codePtr += 2;;
|
||||
val = READ_LE_UINT16(codePtr); codePtr += 2;;
|
||||
|
||||
val = getSoundVar(sound, var) - val;
|
||||
setSoundVar(sound, var, val);
|
||||
break;
|
||||
case 64: // Multiple
|
||||
var = READ_LE_UINT16(codePtr); codePtr += 2;;
|
||||
val = READ_LE_UINT16(codePtr); codePtr += 2;
|
||||
if (edi == 2) {
|
||||
val = getSoundVar(sound, val);
|
||||
}
|
||||
|
||||
val = getSoundVar(sound, var) * val;
|
||||
setSoundVar(sound, var, val);
|
||||
break;
|
||||
case 80: // Divide
|
||||
var = READ_LE_UINT16(codePtr); codePtr += 2;;
|
||||
val = READ_LE_UINT16(codePtr); codePtr += 2;
|
||||
if (edi == 2) {
|
||||
val = getSoundVar(sound, val);
|
||||
}
|
||||
|
||||
val = getSoundVar(sound, var) / val;
|
||||
setSoundVar(sound, var, val);
|
||||
break;
|
||||
case 96: // Increment
|
||||
var = READ_LE_UINT16(codePtr); codePtr += 2;
|
||||
val = getSoundVar(sound, var) + 1;
|
||||
setSoundVar(sound, var, val);
|
||||
break;
|
||||
case 104: // Decrement
|
||||
var = READ_LE_UINT16(codePtr); codePtr += 2;
|
||||
val = getSoundVar(sound, var) - 1;
|
||||
setSoundVar(sound, var, val);
|
||||
break;
|
||||
default:
|
||||
error("Illegal sound %d opcode %d", sound, opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::playHESound(int soundID, int heOffset, int heChannel, int heFlags) {
|
||||
debug(0,"playHESound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
|
||||
byte *mallocedPtr = NULL;
|
||||
byte *ptr, *spoolPtr;
|
||||
char *sound;
|
||||
int size = -1;
|
||||
int rate;
|
||||
byte flags = Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE;
|
||||
|
||||
if (heChannel == -1) {
|
||||
if (_vm->_heversion >= 95 && _vm->VAR(_vm->VAR_DEFAULT_SOUND_CHANNEL) != 0)
|
||||
heChannel = _vm->VAR(_vm->VAR_DEFAULT_SOUND_CHANNEL);
|
||||
else
|
||||
heChannel = 1;
|
||||
}
|
||||
|
||||
if (soundID > _vm->_numSounds) {
|
||||
if (soundID >= 10000) {
|
||||
// Special codes, used in pjgames
|
||||
return;
|
||||
}
|
||||
|
||||
int music_offs;
|
||||
char buf[32], buf1[128];
|
||||
Common::File musicFile;
|
||||
|
||||
sprintf(buf, "%s.he4", _vm->getGameName());
|
||||
|
||||
if (_vm->_substResFileNameIndex > 0) {
|
||||
_vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
|
||||
strcpy(buf, buf1);
|
||||
}
|
||||
if (musicFile.open(buf) == false) {
|
||||
warning("playSound: Can't open music file %s", buf);
|
||||
return;
|
||||
}
|
||||
if (!getHEMusicDetails(soundID, music_offs, size)) {
|
||||
debug(0, "playSound: musicID %d not found", soundID);
|
||||
return;
|
||||
}
|
||||
|
||||
musicFile.seek(music_offs, SEEK_SET);
|
||||
|
||||
if (_vm->_heversion == 70) {
|
||||
spoolPtr = (byte *)malloc(size);
|
||||
musicFile.read(spoolPtr, size);
|
||||
} else {
|
||||
spoolPtr = _vm->res.createResource(rtSpoolBuffer, heChannel, size);
|
||||
assert(spoolPtr);
|
||||
musicFile.read(spoolPtr, size);
|
||||
}
|
||||
musicFile.close();
|
||||
|
||||
_vm->_mixer->stopID(_currentMusic);
|
||||
_currentMusic = soundID;
|
||||
if (_vm->_heversion == 70) {
|
||||
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], spoolPtr, size, 11025, flags, soundID);
|
||||
return;
|
||||
}
|
||||
|
||||
// This pointer will be freed at the end of the function
|
||||
mallocedPtr = spoolPtr;
|
||||
}
|
||||
|
||||
if (soundID > _vm->_numSounds) {
|
||||
ptr = _vm->getResourceAddress(rtSpoolBuffer, heChannel);
|
||||
} else {
|
||||
ptr = _vm->getResourceAddress(rtSound, soundID);
|
||||
}
|
||||
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Support for sound in later Backyard sports games
|
||||
if (READ_UINT32(ptr) == MKID('RIFF')) {
|
||||
uint16 type;
|
||||
int blockAlign;
|
||||
size = READ_LE_UINT32(ptr + 4);
|
||||
Common::MemoryReadStream stream(ptr, size);
|
||||
|
||||
if (!loadWAVFromStream(stream, size, rate, flags, &type, &blockAlign)) {
|
||||
error("playSound: Not a valid WAV file");
|
||||
}
|
||||
|
||||
if (type == 17) {
|
||||
AudioStream *voxStream = new ADPCMInputStream(&stream, size, kADPCMIma, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
|
||||
|
||||
sound = (char *)malloc(size * 4);
|
||||
size = voxStream->readBuffer((int16*)sound, size * 2);
|
||||
size *= 2; // 16bits.
|
||||
} else {
|
||||
// Allocate a sound buffer, copy the data into it, and play
|
||||
sound = (char *)malloc(size);
|
||||
memcpy(sound, ptr + stream.pos(), size);
|
||||
}
|
||||
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
|
||||
}
|
||||
// Support for sound in Humongous Entertainment games
|
||||
else if (READ_UINT32(ptr) == MKID('DIGI') || READ_UINT32(ptr) == MKID('TALK') || READ_UINT32(ptr) == MKID('HSHD')) {
|
||||
byte *sndPtr = ptr;
|
||||
int priority;
|
||||
|
||||
if (READ_UINT32(ptr) == MKID('HSHD')) {
|
||||
priority = READ_LE_UINT16(ptr + 10);
|
||||
rate = READ_LE_UINT16(ptr + 14);
|
||||
ptr += READ_BE_UINT32(ptr + 4);
|
||||
} else {
|
||||
priority = READ_LE_UINT16(ptr + 18);
|
||||
rate = READ_LE_UINT16(ptr + 22);
|
||||
ptr += 8 + READ_BE_UINT32(ptr + 12);
|
||||
}
|
||||
|
||||
//if (_vm->_mixer->isSoundHandleActive(_heSoundChannels[heChannel]) && _heChannel[heChannel].priority > priority)
|
||||
// return;
|
||||
|
||||
int codeOffs = -1;
|
||||
if (READ_UINT32(ptr) == MKID('SBNG')) {
|
||||
codeOffs = ptr - sndPtr + 8;
|
||||
ptr += READ_BE_UINT32(ptr + 4);
|
||||
}
|
||||
|
||||
assert(READ_UINT32(ptr) == MKID('SDAT'));
|
||||
size = READ_BE_UINT32(ptr+4) - 8;
|
||||
if (heOffset < 0 || heOffset > size) {
|
||||
// Occurs when making fireworks in puttmoon
|
||||
debug(0, "playSound: Invalid sound offset (offset %d, size %d) in sound %d", heOffset, size, soundID);
|
||||
heOffset = 0;
|
||||
}
|
||||
size -= heOffset;
|
||||
|
||||
if (_overrideFreq) {
|
||||
// Used by the piano in Fatty Bear's Birthday Surprise
|
||||
rate = _overrideFreq;
|
||||
_overrideFreq = 0;
|
||||
}
|
||||
|
||||
if (heFlags & 1) {
|
||||
flags |= Audio::Mixer::FLAG_LOOP;
|
||||
}
|
||||
|
||||
// Allocate a sound buffer, copy the data into it, and play
|
||||
sound = (char *)malloc(size);
|
||||
memcpy(sound, ptr + heOffset + 8, size);
|
||||
//_vm->_mixer->stopHandle(_heSoundChannels[heChannel]);
|
||||
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
|
||||
|
||||
_vm->setHETimer(heChannel + 4);
|
||||
_heChannel[heChannel].sound = soundID;
|
||||
_heChannel[heChannel].priority = priority;
|
||||
_heChannel[heChannel].timer = 0;
|
||||
_heChannel[heChannel].sbngBlock = (codeOffs != 0) ? 1 : 0;
|
||||
_heChannel[heChannel].codeOffs = codeOffs;
|
||||
memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars));
|
||||
}
|
||||
// Support for PCM music in 3DO versions of Humongous Entertainment games
|
||||
else if (READ_UINT32(ptr) == MKID('MRAW')) {
|
||||
ptr += 8 + READ_BE_UINT32(ptr+12);
|
||||
if (READ_UINT32(ptr) != MKID('SDAT'))
|
||||
return;
|
||||
|
||||
size = READ_BE_UINT32(ptr+4) - 8;
|
||||
rate = 22050;
|
||||
flags = Audio::Mixer::FLAG_AUTOFREE;
|
||||
|
||||
// Allocate a sound buffer, copy the data into it, and play
|
||||
sound = (char *)malloc(size);
|
||||
memcpy(sound, ptr + 8, size);
|
||||
_vm->_mixer->stopID(_currentMusic);
|
||||
_currentMusic = soundID;
|
||||
_vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
|
||||
}
|
||||
else {
|
||||
//if (_vm->_musicEngine) {
|
||||
// _vm->_musicEngine->startSound(soundID);
|
||||
//}
|
||||
}
|
||||
|
||||
free(mallocedPtr);
|
||||
}
|
||||
|
||||
void Sound::startHETalkSound(uint32 offset) {
|
||||
byte *ptr;
|
||||
int32 size;
|
||||
|
||||
if (ConfMan.getBool("speech_mute"))
|
||||
return;
|
||||
|
||||
if (!_sfxFile->isOpen()) {
|
||||
error("startHETalkSound: Speech file is not open");
|
||||
return;
|
||||
}
|
||||
|
||||
_sfxMode |= 2;
|
||||
_vm->res.nukeResource(rtSound, 1);
|
||||
_sfxFile->seek(offset + 4, SEEK_SET);
|
||||
size = _sfxFile->readUint32BE() - 8;
|
||||
_vm->res.createResource(rtSound, 1, size);
|
||||
ptr = _vm->getResourceAddress(rtSound, 1);
|
||||
_sfxFile->read(ptr, size);
|
||||
|
||||
int channel = (_vm->VAR_SOUND_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_SOUND_CHANNEL) : 0;
|
||||
addSoundToQueue2(1, 0, channel, 0);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
|
@ -298,6 +298,7 @@ void ScummEngine_v72he::setupScummVars() {
|
|||
VAR_PLATFORM = 78; // 1 is PC, 2 is Macintosh
|
||||
VAR_WINDOWS_VERSION = 79; // 31 is Windows 3.1, 40 is Windows 95+
|
||||
VAR_CURRENT_CHARSET = 80;
|
||||
VAR_SOUNDCODE_TMR = 84;
|
||||
VAR_KEY_STATE = 86;
|
||||
VAR_NUM_SOUND_CHANNELS = 88;
|
||||
if (_heversion >= 90) {
|
||||
|
@ -310,6 +311,7 @@ void ScummEngine_v72he::setupScummVars() {
|
|||
VAR_U32_VERSION = 107;
|
||||
VAR_U32_ARRAY_UNK = 116;
|
||||
VAR_WIZ_TCOLOR = 117;
|
||||
VAR_DEFAULT_SOUND_CHANNEL = 120;
|
||||
}
|
||||
if (_heversion >= 98) {
|
||||
VAR_SKIP_RESET_TALK_ACTOR = 125;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue