Initial commit
This commit is contained in:
421
engines/scumm/imuse_digi/dimuse_bndmgr.cpp
Normal file
421
engines/scumm/imuse_digi/dimuse_bndmgr.cpp
Normal file
@@ -0,0 +1,421 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "scumm/scumm.h"
|
||||
#include "scumm/util.h"
|
||||
#include "scumm/file.h"
|
||||
#include "scumm/imuse_digi/dimuse_bndmgr.h"
|
||||
#include "scumm/imuse_digi/dimuse_codecs.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
BundleDirCache::BundleDirCache(const ScummEngine *vm) : _vm(vm) {
|
||||
for (int fileId = 0; fileId < ARRAYSIZE(_bundleDirCache); fileId++) {
|
||||
_bundleDirCache[fileId].bundleTable = nullptr;
|
||||
_bundleDirCache[fileId].fileName[0] = 0;
|
||||
_bundleDirCache[fileId].numFiles = 0;
|
||||
_bundleDirCache[fileId].isCompressed = false;
|
||||
_bundleDirCache[fileId].indexTable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BundleDirCache::~BundleDirCache() {
|
||||
for (int fileId = 0; fileId < ARRAYSIZE(_bundleDirCache); fileId++) {
|
||||
free(_bundleDirCache[fileId].bundleTable);
|
||||
free(_bundleDirCache[fileId].indexTable);
|
||||
}
|
||||
}
|
||||
|
||||
BundleDirCache::AudioTable *BundleDirCache::getTable(int slot) {
|
||||
return _bundleDirCache[slot].bundleTable;
|
||||
}
|
||||
|
||||
int32 BundleDirCache::getNumFiles(int slot) {
|
||||
return _bundleDirCache[slot].numFiles;
|
||||
}
|
||||
|
||||
BundleDirCache::IndexNode *BundleDirCache::getIndexTable(int slot) {
|
||||
return _bundleDirCache[slot].indexTable;
|
||||
}
|
||||
|
||||
bool BundleDirCache::isSndDataExtComp(int slot) {
|
||||
return _bundleDirCache[slot].isCompressed;
|
||||
}
|
||||
|
||||
int BundleDirCache::matchFile(const char *filename) {
|
||||
int32 tag, offset;
|
||||
bool found = false;
|
||||
int freeSlot = -1;
|
||||
int fileId;
|
||||
|
||||
for (fileId = 0; fileId < ARRAYSIZE(_bundleDirCache); fileId++) {
|
||||
if ((_bundleDirCache[fileId].bundleTable == nullptr) && (freeSlot == -1)) {
|
||||
freeSlot = fileId;
|
||||
}
|
||||
if (scumm_stricmp(filename, _bundleDirCache[fileId].fileName) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
ScummFile file(_vm);
|
||||
|
||||
if (g_scumm->openFile(file, filename) == false) {
|
||||
error("BundleDirCache::matchFile() Can't open bundle file: %s", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (freeSlot == -1)
|
||||
error("BundleDirCache::matchFileFile() Can't find free slot for file bundle dir cache");
|
||||
|
||||
tag = file.readUint32BE();
|
||||
if (tag == MKTAG('L','B','2','3'))
|
||||
_bundleDirCache[freeSlot].isCompressed = true;
|
||||
offset = file.readUint32BE();
|
||||
|
||||
Common::strlcpy(_bundleDirCache[freeSlot].fileName, filename, sizeof(_bundleDirCache[freeSlot].fileName));
|
||||
_bundleDirCache[freeSlot].numFiles = file.readUint32BE();
|
||||
_bundleDirCache[freeSlot].bundleTable = (AudioTable *)malloc(_bundleDirCache[freeSlot].numFiles * sizeof(AudioTable));
|
||||
assert(_bundleDirCache[freeSlot].bundleTable);
|
||||
|
||||
file.seek(offset, SEEK_SET);
|
||||
|
||||
_bundleDirCache[freeSlot].indexTable =
|
||||
(IndexNode *)calloc(_bundleDirCache[freeSlot].numFiles, sizeof(IndexNode));
|
||||
assert(_bundleDirCache[freeSlot].indexTable);
|
||||
|
||||
for (int32 i = 0; i < _bundleDirCache[freeSlot].numFiles; i++) {
|
||||
char name[24], c;
|
||||
int32 z = 0;
|
||||
int32 z2;
|
||||
|
||||
if (tag == MKTAG('L','B','2','3')) {
|
||||
file.read(_bundleDirCache[freeSlot].bundleTable[i].filename, 24);
|
||||
} else {
|
||||
for (z2 = 0; z2 < 8; z2++)
|
||||
if ((c = file.readByte()) != 0)
|
||||
name[z++] = c;
|
||||
name[z++] = '.';
|
||||
for (z2 = 0; z2 < 4; z2++)
|
||||
if ((c = file.readByte()) != 0)
|
||||
name[z++] = c;
|
||||
|
||||
name[z] = '\0';
|
||||
Common::strlcpy(_bundleDirCache[freeSlot].bundleTable[i].filename, name, sizeof(_bundleDirCache[freeSlot].bundleTable[i].filename));
|
||||
}
|
||||
_bundleDirCache[freeSlot].bundleTable[i].offset = file.readUint32BE();
|
||||
_bundleDirCache[freeSlot].bundleTable[i].size = file.readUint32BE();
|
||||
Common::strlcpy(_bundleDirCache[freeSlot].indexTable[i].filename, _bundleDirCache[freeSlot].bundleTable[i].filename, sizeof(_bundleDirCache[freeSlot].indexTable[i].filename));
|
||||
_bundleDirCache[freeSlot].indexTable[i].index = i;
|
||||
}
|
||||
qsort(_bundleDirCache[freeSlot].indexTable, _bundleDirCache[freeSlot].numFiles,
|
||||
sizeof(IndexNode), (int (*)(const void *, const void *))scumm_stricmp);
|
||||
return freeSlot;
|
||||
} else {
|
||||
return fileId;
|
||||
}
|
||||
}
|
||||
|
||||
BundleMgr::BundleMgr(const ScummEngine *vm, BundleDirCache *cache) {
|
||||
_cache = cache;
|
||||
_bundleTable = nullptr;
|
||||
_compTable = nullptr;
|
||||
_numFiles = 0;
|
||||
_numCompItems = 0;
|
||||
_lastBlockDecompressedSize = 0;
|
||||
_curSampleId = -1;
|
||||
_fileBundleId = -1;
|
||||
_file = new ScummFile(vm);
|
||||
_compInputBuff = nullptr;
|
||||
}
|
||||
|
||||
BundleMgr::~BundleMgr() {
|
||||
close();
|
||||
delete _file;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *BundleMgr::getFile(const char *filename, int32 &offset, int32 &size) {
|
||||
BundleDirCache::IndexNode target;
|
||||
Common::strlcpy(target.filename, filename, sizeof(target.filename));
|
||||
BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
|
||||
sizeof(BundleDirCache::IndexNode), (int (*)(const void *, const void *))scumm_stricmp);
|
||||
if (found) {
|
||||
_file->seek(_bundleTable[found->index].offset, SEEK_SET);
|
||||
offset = _bundleTable[found->index].offset;
|
||||
size = _bundleTable[found->index].size;
|
||||
return _file;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool BundleMgr::open(const char *filename, bool &isCompressed, bool errorFlag) {
|
||||
if (_file->isOpen())
|
||||
return true;
|
||||
|
||||
if (g_scumm->openFile(*_file, filename) == false) {
|
||||
if (errorFlag) {
|
||||
error("BundleMgr::open() Can't open bundle file: %s", filename);
|
||||
} else {
|
||||
warning("BundleMgr::open() Can't open bundle file: %s", filename);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int slot = _cache->matchFile(filename);
|
||||
assert(slot != -1);
|
||||
isCompressed = _cache->isSndDataExtComp(slot);
|
||||
_numFiles = _cache->getNumFiles(slot);
|
||||
assert(_numFiles);
|
||||
_bundleTable = _cache->getTable(slot);
|
||||
_indexTable = _cache->getIndexTable(slot);
|
||||
assert(_bundleTable);
|
||||
_compTableLoaded = false;
|
||||
_isUncompressed = false;
|
||||
_outputSize = 0;
|
||||
_lastBlockDecompressedSize = 0;
|
||||
_curDecompressedFilePos = 0;
|
||||
_lastBlock = -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BundleMgr::close() {
|
||||
if (_file->isOpen()) {
|
||||
_file->close();
|
||||
_bundleTable = nullptr;
|
||||
_numFiles = 0;
|
||||
_numCompItems = 0;
|
||||
_lastBlockDecompressedSize = 0;
|
||||
_curDecompressedFilePos = 0;
|
||||
_compTableLoaded = false;
|
||||
_isUncompressed = false;
|
||||
_lastBlock = -1;
|
||||
_outputSize = 0;
|
||||
_curSampleId = -1;
|
||||
free(_compTable);
|
||||
_compTable = nullptr;
|
||||
free(_compInputBuff);
|
||||
_compInputBuff = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool BundleMgr::loadCompTable(int32 index) {
|
||||
_file->seek(_bundleTable[index].offset, SEEK_SET);
|
||||
uint32 tag = _file->readUint32BE();
|
||||
|
||||
if (tag == MKTAG('i','M','U','S')) {
|
||||
_isUncompressed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
_numCompItems = _file->readUint32BE();
|
||||
assert(_numCompItems > 0);
|
||||
_file->seek(4, SEEK_CUR);
|
||||
_lastBlockDecompressedSize = _file->readUint32BE();
|
||||
if (tag != MKTAG('C','O','M','P')) {
|
||||
debug("BundleMgr::loadCompTable() Compressed sound %d (%s:%d) invalid (%s)", index, _file->getDebugName().c_str(), _bundleTable[index].offset, tag2str(tag));
|
||||
return false;
|
||||
}
|
||||
|
||||
_compTable = (CompTable *)malloc(sizeof(CompTable) * _numCompItems);
|
||||
assert(_compTable);
|
||||
int32 maxSize = 0;
|
||||
for (int i = 0; i < _numCompItems; i++) {
|
||||
_compTable[i].offset = _file->readUint32BE();
|
||||
_compTable[i].size = _file->readUint32BE();
|
||||
_compTable[i].codec = _file->readUint32BE();
|
||||
_file->seek(4, SEEK_CUR);
|
||||
if (_compTable[i].size > maxSize)
|
||||
maxSize = _compTable[i].size;
|
||||
}
|
||||
// CMI hack: one more byte at the end of input buffer
|
||||
_compInputBuff = (byte *)malloc(maxSize + 1);
|
||||
assert(_compInputBuff);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 BundleMgr::seekFile(int32 offset, int mode) {
|
||||
// We don't actually seek the file, but instead try to find that the specified offset exists
|
||||
// within the decompressed blocks, and save that offset in _curDecompressedFilePos
|
||||
int result = 0;
|
||||
switch (mode) {
|
||||
case SEEK_END:
|
||||
if (_isUncompressed) {
|
||||
result = offset + _bundleTable[_curSampleId].size;
|
||||
} else {
|
||||
result = offset + ((_numCompItems - 1) * DIMUSE_BUN_CHUNK_SIZE) + _lastBlockDecompressedSize;
|
||||
}
|
||||
_curDecompressedFilePos = result;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
default:
|
||||
if (_isUncompressed) {
|
||||
result = offset;
|
||||
_curDecompressedFilePos = result;
|
||||
} else {
|
||||
int destBlock = offset / DIMUSE_BUN_CHUNK_SIZE + (offset % DIMUSE_BUN_CHUNK_SIZE != 0);
|
||||
if (destBlock <= _numCompItems) {
|
||||
result = offset;
|
||||
_curDecompressedFilePos = result;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int32 BundleMgr::readFile(const char *name, int32 size, byte **comp_final, bool header_outside) {
|
||||
int32 final_size = 0;
|
||||
|
||||
if (!_file->isOpen()) {
|
||||
error("BundleMgr::readFile() File is not open");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the sound in the bundle
|
||||
BundleDirCache::IndexNode target;
|
||||
strncpy(target.filename, name, sizeof(target.filename));
|
||||
target.filename[sizeof(target.filename) - 1] = '\0';
|
||||
BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
|
||||
sizeof(BundleDirCache::IndexNode), (int(*)(const void *, const void *))scumm_stricmp);
|
||||
|
||||
if (found) {
|
||||
int32 i, finalSize, outputSize;
|
||||
int skip, firstBlock, lastBlock;
|
||||
int headerSize = 0;
|
||||
|
||||
assert(0 <= found->index && found->index < _numFiles);
|
||||
|
||||
if (_file->isOpen() == false) {
|
||||
error("BundleMgr::readFile() File is not open");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_curSampleId == -1)
|
||||
_curSampleId = found->index;
|
||||
|
||||
assert(_curSampleId == found->index);
|
||||
|
||||
if (!_compTableLoaded) {
|
||||
_compTableLoaded = loadCompTable(found->index);
|
||||
if (!_compTableLoaded)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_isUncompressed) {
|
||||
_file->seek(_bundleTable[found->index].offset + _curDecompressedFilePos + headerSize, SEEK_SET);
|
||||
*comp_final = (byte *)malloc(size);
|
||||
assert(*comp_final);
|
||||
_file->read(*comp_final, size);
|
||||
_curDecompressedFilePos += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
firstBlock = (_curDecompressedFilePos + headerSize) / DIMUSE_BUN_CHUNK_SIZE;
|
||||
lastBlock = (_curDecompressedFilePos + headerSize + size - 1) / DIMUSE_BUN_CHUNK_SIZE;
|
||||
|
||||
// Clip last_block by the total number of blocks (= "comp items")
|
||||
if ((lastBlock >= _numCompItems) && (_numCompItems > 0))
|
||||
lastBlock = _numCompItems - 1;
|
||||
|
||||
int32 blocksFinalSize = DIMUSE_BUN_CHUNK_SIZE * (1 + lastBlock - firstBlock);
|
||||
*comp_final = (byte *)malloc(blocksFinalSize);
|
||||
assert(*comp_final);
|
||||
finalSize = 0;
|
||||
|
||||
skip = (_curDecompressedFilePos + headerSize) % DIMUSE_BUN_CHUNK_SIZE; // Excess length after the last block
|
||||
|
||||
for (i = firstBlock; i <= lastBlock; i++) {
|
||||
if (_lastBlock != i) {
|
||||
// CMI hack: one more zero byte at the end of input buffer
|
||||
_compInputBuff[_compTable[i].size] = 0;
|
||||
_file->seek(_bundleTable[found->index].offset + _compTable[i].offset, SEEK_SET);
|
||||
_file->read(_compInputBuff, _compTable[i].size);
|
||||
_outputSize = BundleCodecs::decompressCodec(_compTable[i].codec, _compInputBuff, _compOutputBuff, _compTable[i].size);
|
||||
|
||||
if (_outputSize > DIMUSE_BUN_CHUNK_SIZE) {
|
||||
error("_outputSize: %d", _outputSize);
|
||||
}
|
||||
_lastBlock = i;
|
||||
}
|
||||
|
||||
outputSize = _outputSize;
|
||||
|
||||
if (header_outside) {
|
||||
outputSize -= skip;
|
||||
} else {
|
||||
if ((headerSize != 0) && (skip >= headerSize))
|
||||
outputSize -= skip;
|
||||
}
|
||||
|
||||
if ((outputSize + skip) > DIMUSE_BUN_CHUNK_SIZE) // workaround
|
||||
outputSize -= (outputSize + skip) - DIMUSE_BUN_CHUNK_SIZE;
|
||||
|
||||
if (outputSize > size)
|
||||
outputSize = size;
|
||||
|
||||
assert(finalSize + outputSize <= blocksFinalSize);
|
||||
|
||||
memcpy(*comp_final + finalSize, _compOutputBuff + skip, outputSize);
|
||||
finalSize += outputSize;
|
||||
|
||||
size -= outputSize;
|
||||
assert(size >= 0);
|
||||
if (size == 0)
|
||||
break;
|
||||
|
||||
skip = 0;
|
||||
}
|
||||
_curDecompressedFilePos += finalSize;
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
debug(2, "BundleMgr::readFile() Failed finding sound %s", name);
|
||||
return final_size;
|
||||
}
|
||||
|
||||
bool BundleMgr::isExtCompBun(byte gameId) {
|
||||
bool isExtComp = false;
|
||||
if (gameId == GID_CMI) {
|
||||
bool isExtComp1 = false, isExtComp2 = false, isExtComp3 = false, isExtComp4 = false;
|
||||
this->open("voxdisk1.bun", isExtComp1); this->close();
|
||||
this->open("voxdisk2.bun", isExtComp2); this->close();
|
||||
this->open("musdisk1.bun", isExtComp3); this->close();
|
||||
this->open("musdisk2.bun", isExtComp4); this->close();
|
||||
|
||||
isExtComp = isExtComp1 | isExtComp2 | isExtComp3 | isExtComp4;
|
||||
} else {
|
||||
bool isExtComp1 = false, isExtComp2 = false;
|
||||
this->open("digvoice.bun", isExtComp1); this->close();
|
||||
this->open("digmusic.bun", isExtComp2); this->close();
|
||||
isExtComp = isExtComp1 | isExtComp2;
|
||||
}
|
||||
|
||||
return isExtComp;
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
112
engines/scumm/imuse_digi/dimuse_bndmgr.h
Normal file
112
engines/scumm/imuse_digi/dimuse_bndmgr.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SCUMM_IMUSE_DIGI_BUNDLE_MGR_H
|
||||
#define SCUMM_IMUSE_DIGI_BUNDLE_MGR_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/file.h"
|
||||
#include "scumm/imuse_digi/dimuse_defs.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class BaseScummFile;
|
||||
|
||||
class BundleDirCache {
|
||||
public:
|
||||
struct AudioTable {
|
||||
char filename[24];
|
||||
int32 offset;
|
||||
int32 size;
|
||||
};
|
||||
|
||||
struct IndexNode {
|
||||
char filename[24];
|
||||
int32 index;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct FileDirCache {
|
||||
char fileName[20];
|
||||
AudioTable *bundleTable;
|
||||
int32 numFiles;
|
||||
bool isCompressed;
|
||||
IndexNode *indexTable;
|
||||
} _bundleDirCache[4];
|
||||
|
||||
const ScummEngine *_vm;
|
||||
public:
|
||||
BundleDirCache(const ScummEngine *vm);
|
||||
~BundleDirCache();
|
||||
|
||||
int matchFile(const char *filename);
|
||||
AudioTable *getTable(int slot);
|
||||
IndexNode *getIndexTable(int slot);
|
||||
int32 getNumFiles(int slot);
|
||||
bool isSndDataExtComp(int slot);
|
||||
};
|
||||
|
||||
class BundleMgr {
|
||||
|
||||
private:
|
||||
struct CompTable {
|
||||
int32 offset;
|
||||
int32 size;
|
||||
int32 codec;
|
||||
};
|
||||
|
||||
BundleDirCache *_cache;
|
||||
BundleDirCache::AudioTable *_bundleTable;
|
||||
BundleDirCache::IndexNode *_indexTable = nullptr;
|
||||
CompTable *_compTable;
|
||||
|
||||
int _numFiles = 0;
|
||||
int _numCompItems = 0;
|
||||
int _lastBlockDecompressedSize = 0;
|
||||
int _curSampleId = 0;
|
||||
int _curDecompressedFilePos = 0;
|
||||
BaseScummFile *_file;
|
||||
bool _compTableLoaded = 0;
|
||||
bool _isUncompressed = 0;
|
||||
int _fileBundleId = 0;
|
||||
byte _compOutputBuff[0x2000] = {};
|
||||
byte *_compInputBuff = nullptr;
|
||||
int _outputSize = 0;
|
||||
int _lastBlock = 0;
|
||||
bool loadCompTable(int32 index);
|
||||
|
||||
public:
|
||||
|
||||
BundleMgr(const ScummEngine *vm, BundleDirCache *_cache);
|
||||
~BundleMgr();
|
||||
|
||||
bool open(const char *filename, bool &isCompressed, bool errorFlag = false);
|
||||
void close();
|
||||
Common::SeekableReadStream *getFile(const char *filename, int32 &offset, int32 &size);
|
||||
int32 seekFile(int32 offset, int size);
|
||||
int32 readFile(const char *name, int32 size, byte **compFinal, bool headerOutside);
|
||||
bool isExtCompBun(byte gameId);
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
||||
#endif
|
||||
272
engines/scumm/imuse_digi/dimuse_cmds.cpp
Normal file
272
engines/scumm/imuse_digi/dimuse_cmds.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
// We have some uintptr arguments as commands 28, 29 and 30 actually require pointer arguments
|
||||
// Unfortunately this makes function calls for other command a little less pretty...
|
||||
int IMuseDigital::cmdsHandleCmd(int cmd, uint8 *ptr, int a, int b, int c, int d, int e,
|
||||
int f, int g, int h, int i, int j, int k, int l, int m, int n) {
|
||||
|
||||
// Convert the character constant (single quotes '') to string
|
||||
char marker[5];
|
||||
if (!_isEarlyDiMUSE && (cmd == 17 || cmd == 18 || cmd == 19)) {
|
||||
for (int index = 0; index < 4; index++) {
|
||||
#if defined SCUMM_BIG_ENDIAN
|
||||
marker[index] = (b >> (8 * index)) & 0xff;
|
||||
#elif defined SCUMM_LITTLE_ENDIAN
|
||||
marker[3 - index] = (b >> (8 * index)) & 0xff;
|
||||
#endif
|
||||
}
|
||||
marker[4] = '\0';
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case DIMUSE_C_INIT:
|
||||
return cmdsInit();
|
||||
case DIMUSE_C_PAUSE:
|
||||
return cmdsPause();
|
||||
case DIMUSE_C_RESUME:
|
||||
return cmdsResume();
|
||||
case DIMUSE_C_SET_GRP_VOL:
|
||||
return _groupsHandler->setGroupVol(a, b);
|
||||
case DIMUSE_C_START_SND:
|
||||
cmdsStartSound(a, b);
|
||||
break;
|
||||
case DIMUSE_C_STOP_SND:
|
||||
cmdsStopSound(a);
|
||||
break;
|
||||
case DIMUSE_C_STOP_ALL_SNDS:
|
||||
cmdsStopAllSounds();
|
||||
break;
|
||||
case DIMUSE_C_GET_NEXT_SND:
|
||||
return cmdsGetNextSound(a);
|
||||
case DIMUSE_C_SET_PARAM:
|
||||
cmdsSetParam(a, b, c);
|
||||
break;
|
||||
case DIMUSE_C_GET_PARAM:
|
||||
return cmdsGetParam(a, b);
|
||||
case DIMUSE_C_FADE_PARAM:
|
||||
return _fadesHandler->fadeParam(a, b, c, d);
|
||||
case DIMUSE_C_SET_HOOK:
|
||||
return cmdsSetHook(a, b);
|
||||
case DIMUSE_C_GET_HOOK:
|
||||
return cmdsGetHook(a);
|
||||
case DIMUSE_C_SET_TRIGGER:
|
||||
return _triggersHandler->setTrigger(a, marker, c, d, e, f, g, h, i, j, k, l, m, n);
|
||||
case DIMUSE_C_CHECK_TRIGGER:
|
||||
return _triggersHandler->checkTrigger(a, marker, c);
|
||||
case DIMUSE_C_CLEAR_TRIGGER:
|
||||
return _triggersHandler->clearTrigger(a, marker, c);
|
||||
case DIMUSE_C_DEFER_CMD:
|
||||
return _triggersHandler->deferCommand(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
|
||||
case DIMUSE_C_GET_MARKER_SYNCS:
|
||||
_vm->_sound->extractSyncsFromDiMUSEMarker((char *)ptr);
|
||||
break;
|
||||
case DIMUSE_C_START_STREAM:
|
||||
return waveStartStream(a, b, c);
|
||||
case DIMUSE_C_SWITCH_STREAM:
|
||||
if (_isEarlyDiMUSE)
|
||||
return waveSwitchStream(a, b, ptr, d, e);
|
||||
else
|
||||
return waveSwitchStream(a, b, c, d, e);
|
||||
case DIMUSE_C_PROCESS_STREAMS:
|
||||
return waveProcessStreams();
|
||||
case DIMUSE_C_FEED_STREAM:
|
||||
return waveFeedStream(a, ptr, c, d);
|
||||
default:
|
||||
debug(5, "IMuseDigital::cmdsHandleCmd(): bogus/unused opcode ignored (%d).", cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsInit() {
|
||||
_cmdsRunning60HzCount = 0;
|
||||
_cmdsRunning10HzCount = 0;
|
||||
|
||||
if (_groupsHandler->init() || _fadesHandler->init() ||
|
||||
_triggersHandler->init() || waveInit()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_cmdsPauseCount = 0;
|
||||
return 48;
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsDeinit() {
|
||||
waveTerminate();
|
||||
waveOutDeinit();
|
||||
_triggersHandler->deinit();
|
||||
_fadesHandler->deinit();
|
||||
_cmdsPauseCount = 0;
|
||||
_cmdsRunning60HzCount = 0;
|
||||
_cmdsRunning10HzCount = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsTerminate() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsPause() {
|
||||
int result = 0;
|
||||
|
||||
if (_cmdsPauseCount == 0) {
|
||||
result = wavePause();
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
result = _cmdsPauseCount + 1;
|
||||
}
|
||||
_cmdsPauseCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsResume() {
|
||||
int result = 0;
|
||||
|
||||
if (_cmdsPauseCount == 1) {
|
||||
result = waveResume();
|
||||
}
|
||||
|
||||
if (_cmdsPauseCount != 0) {
|
||||
_cmdsPauseCount--;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
result = _cmdsPauseCount;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void IMuseDigital::cmdsSaveLoad(Common::Serializer &ser) {
|
||||
// Serialize in this order:
|
||||
// - Open files
|
||||
// - Fades
|
||||
// - Triggers
|
||||
// - Pass the control to waveSaveLoad and then tracksSaveLoad, which will serialize:
|
||||
// - Dispatches
|
||||
// - Tracks (with SYNCs, if the game is COMI)
|
||||
// - State and sequence info
|
||||
// - Attributes
|
||||
// - Full Throttle's music cue ID
|
||||
|
||||
_filesHandler->saveLoad(ser);
|
||||
_fadesHandler->saveLoad(ser);
|
||||
_triggersHandler->saveLoad(ser);
|
||||
waveSaveLoad(ser);
|
||||
ser.syncAsSint32LE(_curMusicState, VER(103));
|
||||
ser.syncAsSint32LE(_curMusicSeq, VER(103));
|
||||
ser.syncAsSint32LE(_nextSeqToPlay, VER(103));
|
||||
ser.syncAsByte(_radioChatterSFX, VER(103));
|
||||
ser.syncArray(_attributes, 188, Common::Serializer::Sint32LE, VER(103));
|
||||
ser.syncAsSint32LE(_curMusicCue, VER(103));
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsStartSound(int soundId, int priority) {
|
||||
uint8 *src = _filesHandler->getSoundAddrData(soundId);
|
||||
|
||||
if (src == nullptr) {
|
||||
debug(5, "IMuseDigital::cmdsStartSound(): ERROR: resource address for sound %d is NULL", soundId);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for the "Creative Voice File" header
|
||||
if (_isEarlyDiMUSE && READ_BE_UINT32(src) == MKTAG('C', 'r', 'e', 'a'))
|
||||
return waveStartSound(soundId, priority);
|
||||
|
||||
// Check for the "iMUS" header
|
||||
if (READ_BE_UINT32(src) == MKTAG('i', 'M', 'U', 'S'))
|
||||
return waveStartSound(soundId, priority);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsStopSound(int soundId) {
|
||||
int result = _filesHandler->getNextSound(soundId);
|
||||
|
||||
if (result != 2)
|
||||
return -1;
|
||||
|
||||
return waveStopSound(soundId);
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsStopAllSounds() {
|
||||
return _triggersHandler->clearAllTriggers() | waveStopAllSounds();
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsGetNextSound(int soundId) {
|
||||
return waveGetNextSound(soundId);
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsSetParam(int soundId, int subCmd, int value) {
|
||||
int result = _filesHandler->getNextSound(soundId);
|
||||
|
||||
if (result != 2)
|
||||
return -1;
|
||||
|
||||
return waveSetParam(soundId, subCmd, value);
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsGetParam(int soundId, int subCmd) {
|
||||
int result = _filesHandler->getNextSound(soundId);
|
||||
|
||||
if (subCmd != 0) {
|
||||
if (subCmd == DIMUSE_P_TRIGS_SNDS) {
|
||||
return _triggersHandler->countPendingSounds(soundId);
|
||||
}
|
||||
|
||||
if (result == 2) {
|
||||
return waveGetParam(soundId, subCmd);
|
||||
}
|
||||
|
||||
result = (subCmd == DIMUSE_P_SND_TRACK_NUM) - 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsSetHook(int soundId, int hookId) {
|
||||
int result = _filesHandler->getNextSound(soundId);
|
||||
|
||||
if (result != 2)
|
||||
return -1;
|
||||
|
||||
return waveSetHook(soundId, hookId);
|
||||
}
|
||||
|
||||
int IMuseDigital::cmdsGetHook(int soundId) {
|
||||
int result = _filesHandler->getNextSound(soundId);
|
||||
|
||||
if (result != 2)
|
||||
return -1;
|
||||
|
||||
return waveGetHook(soundId);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
643
engines/scumm/imuse_digi/dimuse_codecs.cpp
Normal file
643
engines/scumm/imuse_digi/dimuse_codecs.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/util.h"
|
||||
#include "scumm/imuse_digi/dimuse_codecs.h"
|
||||
|
||||
#include "audio/decoders/adpcm_intern.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
namespace BundleCodecs {
|
||||
|
||||
/*
|
||||
* The "IMC" codec below (see cases 13 & 15 in decompressCodec) is actually a
|
||||
* variant of the IMA codec, see also
|
||||
* <https://multimedia.cx/simpleaudio.html>
|
||||
*
|
||||
* It is somewhat different, though: the standard ADPCM codecs use a fixed
|
||||
* size for their data packets (4 bits), while the codec implemented here
|
||||
* varies the size of each "packet" between 2 and 7 bits.
|
||||
*/
|
||||
|
||||
static byte *_destImcTable = nullptr;
|
||||
static uint32 *_destImcTable2 = nullptr;
|
||||
|
||||
// This table is the "big brother" of Audio::ADPCMStream::_stepAdjustTable.
|
||||
static const byte imxOtherTable[6][64] = {
|
||||
{
|
||||
0xFF,
|
||||
4
|
||||
},
|
||||
|
||||
{
|
||||
0xFF, 0xFF,
|
||||
2, 8
|
||||
},
|
||||
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
1, 2, 4, 6
|
||||
},
|
||||
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
1, 2, 4, 6, 8, 12, 16, 32
|
||||
},
|
||||
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
1, 2, 4, 6, 8, 10, 12, 14,
|
||||
16, 18, 20, 22, 24, 26, 28, 32
|
||||
},
|
||||
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
1, 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32
|
||||
}
|
||||
};
|
||||
|
||||
void releaseImcTables() {
|
||||
free(_destImcTable);
|
||||
_destImcTable = nullptr;
|
||||
free(_destImcTable2);
|
||||
_destImcTable2 = nullptr;
|
||||
}
|
||||
|
||||
void initializeImcTables() {
|
||||
int pos;
|
||||
|
||||
if (!_destImcTable) _destImcTable = (byte *)calloc(89, sizeof(byte));
|
||||
if (!_destImcTable2) _destImcTable2 = (uint32 *)calloc(89 * 64, sizeof(uint32));
|
||||
|
||||
for (pos = 0; pos <= 88; ++pos) {
|
||||
byte put = 1;
|
||||
int32 tableValue = ((Audio::Ima_ADPCMStream::_imaTable[pos] * 4) / 7) / 2;
|
||||
while (tableValue != 0) {
|
||||
tableValue /= 2;
|
||||
put++;
|
||||
}
|
||||
if (put < 3) {
|
||||
put = 3;
|
||||
}
|
||||
if (put > 8) {
|
||||
put = 8;
|
||||
}
|
||||
_destImcTable[pos] = put - 1;
|
||||
}
|
||||
|
||||
for (int n = 0; n < 64; n++) {
|
||||
for (pos = 0; pos <= 88; ++pos) {
|
||||
int32 count = 32;
|
||||
int32 put = 0;
|
||||
int32 tableValue = Audio::Ima_ADPCMStream::_imaTable[pos];
|
||||
do {
|
||||
if ((count & n) != 0) {
|
||||
put += tableValue;
|
||||
}
|
||||
count /= 2;
|
||||
tableValue /= 2;
|
||||
} while (count != 0);
|
||||
_destImcTable2[n + pos * 64] = put;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define NextBit \
|
||||
do { \
|
||||
bit = mask & 1; \
|
||||
mask >>= 1; \
|
||||
if (!--bitsleft) { \
|
||||
mask = READ_LE_UINT16(srcptr); \
|
||||
srcptr += 2; \
|
||||
bitsleft = 16; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int32 compDecode(byte *src, byte *dst) {
|
||||
byte *result, *srcptr = src, *dstptr = dst;
|
||||
int data, size, bit, bitsleft = 16, mask = READ_LE_UINT16(srcptr);
|
||||
srcptr += 2;
|
||||
|
||||
for (;;) {
|
||||
NextBit;
|
||||
if (bit) {
|
||||
*dstptr++ = *srcptr++;
|
||||
} else {
|
||||
NextBit;
|
||||
if (!bit) {
|
||||
NextBit;
|
||||
size = bit << 1;
|
||||
NextBit;
|
||||
size = (size | bit) + 3;
|
||||
data = *srcptr++ | 0xffffff00;
|
||||
} else {
|
||||
data = *srcptr++;
|
||||
size = *srcptr++;
|
||||
|
||||
data |= 0xfffff000 + ((size & 0xf0) << 4);
|
||||
size = (size & 0x0f) + 3;
|
||||
|
||||
if (size == 3)
|
||||
if (((*srcptr++) + 1) == 1)
|
||||
return dstptr - dst;
|
||||
}
|
||||
result = dstptr + data;
|
||||
while (size--)
|
||||
*dstptr++ = *result++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef NextBit
|
||||
|
||||
int32 decompressADPCM(byte *compInput, byte *compOutput, int channels) {
|
||||
byte *src;
|
||||
|
||||
// Decoder for the IMA ADPCM variants used in COMI.
|
||||
// Contrary to regular IMA ADPCM, this codec uses a variable
|
||||
// bitsize for the encoded data.
|
||||
|
||||
const int MAX_CHANNELS = 2;
|
||||
int32 outputSamplesLeft;
|
||||
int32 destPos;
|
||||
int16 firstWord;
|
||||
byte initialTablePos[MAX_CHANNELS] = {0, 0};
|
||||
//int32 initialimcTableEntry[MAX_CHANNELS] = {7, 7};
|
||||
int32 initialOutputWord[MAX_CHANNELS] = {0, 0};
|
||||
int32 totalBitOffset, curTablePos, outputWord;
|
||||
byte *dst;
|
||||
int i;
|
||||
|
||||
// We only support mono and stereo
|
||||
assert(channels == 1 || channels == 2);
|
||||
|
||||
src = compInput;
|
||||
dst = compOutput;
|
||||
outputSamplesLeft = 0x1000;
|
||||
|
||||
// Every data packet contains 0x2000 bytes of audio data
|
||||
// when extracted. In order to encode bigger data sets,
|
||||
// one has to split the data into multiple blocks.
|
||||
//
|
||||
// Every block starts with a 2 byte word. If that word is
|
||||
// non-zero, it indicates the size of a block of raw audio
|
||||
// data (not encoded) following it. That data we simply copy
|
||||
// to the output buffer and then proceed by decoding the
|
||||
// remaining data.
|
||||
//
|
||||
// If on the other hand the word is zero, then what follows
|
||||
// are 7*channels bytes containing seed data for the decoder.
|
||||
firstWord = READ_BE_UINT16(src);
|
||||
src += 2;
|
||||
if (firstWord != 0) {
|
||||
// Copy raw data
|
||||
memcpy(dst, src, firstWord);
|
||||
dst += firstWord;
|
||||
src += firstWord;
|
||||
assert((firstWord & 1) == 0);
|
||||
outputSamplesLeft -= firstWord / 2;
|
||||
} else {
|
||||
// Read the seed values for the decoder.
|
||||
for (i = 0; i < channels; i++) {
|
||||
initialTablePos[i] = *src;
|
||||
src += 1;
|
||||
//initialimcTableEntry[i] = READ_BE_UINT32(src);
|
||||
src += 4;
|
||||
initialOutputWord[i] = READ_BE_UINT32(src);
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
||||
totalBitOffset = 0;
|
||||
// The channels are encoded separately.
|
||||
for (int chan = 0; chan < channels; chan++) {
|
||||
// Read initial state (this makes it possible for the data stream
|
||||
// to be split & spread across multiple data chunks.
|
||||
curTablePos = initialTablePos[chan];
|
||||
//imcTableEntry = initialimcTableEntry[chan];
|
||||
outputWord = initialOutputWord[chan];
|
||||
|
||||
// We need to interleave the channels in the output; we achieve
|
||||
// that by using a variables dest offset:
|
||||
destPos = chan * 2;
|
||||
|
||||
const int bound = (channels == 1)
|
||||
? outputSamplesLeft
|
||||
: ((chan == 0)
|
||||
? (outputSamplesLeft+1) / 2
|
||||
: outputSamplesLeft / 2);
|
||||
for (i = 0; i < bound; ++i) {
|
||||
// Determine the size (in bits) of the next data packet
|
||||
const int32 curTableEntryBitCount = _destImcTable[curTablePos];
|
||||
assert(2 <= curTableEntryBitCount && curTableEntryBitCount <= 7);
|
||||
|
||||
// Read the next data packet
|
||||
const byte *readPos = src + (totalBitOffset >> 3);
|
||||
const uint16 readWord = (uint16)(READ_BE_UINT16(readPos) << (totalBitOffset & 7));
|
||||
const byte packet = (byte)(readWord >> (16 - curTableEntryBitCount));
|
||||
|
||||
// Advance read position to the next data packet
|
||||
totalBitOffset += curTableEntryBitCount;
|
||||
|
||||
// Decode the data packet into a delta value for the output signal.
|
||||
const byte signBitMask = (1 << (curTableEntryBitCount - 1));
|
||||
const byte dataBitMask = (signBitMask - 1);
|
||||
const byte data = (packet & dataBitMask);
|
||||
|
||||
const int32 tmpA = (data << (7 - curTableEntryBitCount));
|
||||
const int32 imcTableEntry = Audio::Ima_ADPCMStream::_imaTable[curTablePos] >> (curTableEntryBitCount - 1);
|
||||
int32 delta = imcTableEntry + _destImcTable2[tmpA + (curTablePos * 64)];
|
||||
|
||||
// The topmost bit in the data packet tells is a sign bit
|
||||
if ((packet & signBitMask) != 0) {
|
||||
delta = -delta;
|
||||
}
|
||||
|
||||
// Accumulate the delta onto the output data
|
||||
outputWord += delta;
|
||||
|
||||
// Clip outputWord to 16 bit signed, and write it into the destination stream
|
||||
outputWord = CLIP<int32>(outputWord, -0x8000, 0x7fff);
|
||||
|
||||
// This is being written as-is (LE), without concerns regarding endianness:
|
||||
// this is because the internal DiMUSE mixer handles the data in LE format,
|
||||
// and we'll convert data to the appropriate format using the QueuingAudioStream flags
|
||||
// when flushing the final audio data to the output stream (see IMuseDigital::waveOutWrite())
|
||||
WRITE_UINT16(dst + destPos, outputWord);
|
||||
|
||||
destPos += channels << 1;
|
||||
|
||||
// Adjust the curTablePos
|
||||
curTablePos += (int8)imxOtherTable[curTableEntryBitCount - 2][data];
|
||||
curTablePos = CLIP<int32>(curTablePos, 0, ARRAYSIZE(Audio::Ima_ADPCMStream::_imaTable) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0x2000;
|
||||
}
|
||||
|
||||
int32 decompressCodec(int32 codec, byte *compInput, byte *compOutput, int32 inputSize) {
|
||||
int32 outputSize;
|
||||
int32 offset1, offset2, offset3, length, k, c, s, j, r, t, z;
|
||||
byte *src, *t_table, *p, *ptr;
|
||||
byte t_tmp1, t_tmp2;
|
||||
|
||||
switch (codec) {
|
||||
case 0:
|
||||
memcpy(compOutput, compInput, inputSize);
|
||||
outputSize = inputSize;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
outputSize = compDecode(compInput, compOutput);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
outputSize = compDecode(compInput, compOutput);
|
||||
p = compOutput;
|
||||
for (z = 1; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
break;
|
||||
|
||||
case 3:
|
||||
outputSize = compDecode(compInput, compOutput);
|
||||
p = compOutput;
|
||||
for (z = 2; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
for (z = 1; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
break;
|
||||
|
||||
case 4:
|
||||
outputSize = compDecode(compInput, compOutput);
|
||||
p = compOutput;
|
||||
for (z = 2; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
for (z = 1; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
|
||||
t_table = (byte *)calloc(outputSize, 1);
|
||||
assert(t_table);
|
||||
|
||||
src = compOutput;
|
||||
length = (outputSize << 3) / 12;
|
||||
k = 0;
|
||||
if (length > 0) {
|
||||
c = -12;
|
||||
s = 0;
|
||||
j = 0;
|
||||
do {
|
||||
ptr = src + length + (k >> 1);
|
||||
t_tmp2 = src[j];
|
||||
if (k & 1) {
|
||||
r = c >> 3;
|
||||
t_table[r + 2] = ((t_tmp2 & 0x0f) << 4) | (ptr[1] >> 4);
|
||||
t_table[r + 1] = (t_tmp2 & 0xf0) | (t_table[r + 1]);
|
||||
} else {
|
||||
r = s >> 3;
|
||||
t_table[r + 0] = ((t_tmp2 & 0x0f) << 4) | (ptr[0] & 0x0f);
|
||||
t_table[r + 1] = t_tmp2 >> 4;
|
||||
}
|
||||
s += 12;
|
||||
c += 12;
|
||||
k++;
|
||||
j++;
|
||||
} while (k < length);
|
||||
}
|
||||
offset1 = ((length - 1) * 3) >> 1;
|
||||
t_table[offset1 + 1] = (t_table[offset1 + 1]) | (src[length - 1] & 0xf0);
|
||||
memcpy(src, t_table, outputSize);
|
||||
free(t_table);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
outputSize = compDecode(compInput, compOutput);
|
||||
p = compOutput;
|
||||
for (z = 2; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
for (z = 1; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
|
||||
t_table = (byte *)malloc(outputSize);
|
||||
assert(t_table);
|
||||
|
||||
src = compOutput;
|
||||
length = (outputSize << 3) / 12;
|
||||
k = 1;
|
||||
c = 0;
|
||||
s = 12;
|
||||
t_table[0] = src[length] >> 4;
|
||||
t = length + k;
|
||||
j = 1;
|
||||
if (t > k) {
|
||||
do {
|
||||
t_tmp1 = *(src + length + (k >> 1));
|
||||
t_tmp2 = src[j - 1];
|
||||
if (k & 1) {
|
||||
r = c >> 3;
|
||||
t_table[r + 0] = (t_tmp2 & 0xf0) | t_table[r];
|
||||
t_table[r + 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 & 0x0f);
|
||||
} else {
|
||||
r = s >> 3;
|
||||
t_table[r + 0] = t_tmp2 >> 4;
|
||||
t_table[r - 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 >> 4);
|
||||
}
|
||||
s += 12;
|
||||
c += 12;
|
||||
k++;
|
||||
j++;
|
||||
} while (k < t);
|
||||
}
|
||||
memcpy(src, t_table, outputSize);
|
||||
free(t_table);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
outputSize = compDecode(compInput, compOutput);
|
||||
p = compOutput;
|
||||
for (z = 2; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
for (z = 1; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
|
||||
t_table = (byte *)malloc(outputSize);
|
||||
assert(t_table);
|
||||
|
||||
src = compOutput;
|
||||
length = (outputSize << 3) / 12;
|
||||
k = 0;
|
||||
c = 0;
|
||||
j = 0;
|
||||
s = -12;
|
||||
t_table[0] = src[outputSize - 1];
|
||||
t_table[outputSize - 1] = src[length - 1];
|
||||
t = length - 1;
|
||||
if (t > 0) {
|
||||
do {
|
||||
t_tmp1 = *(src + length + (k >> 1));
|
||||
t_tmp2 = src[j];
|
||||
if (k & 1) {
|
||||
r = s >> 3;
|
||||
t_table[r + 2] = (t_tmp2 & 0xf0) | t_table[r + 2];
|
||||
t_table[r + 3] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 >> 4);
|
||||
} else {
|
||||
r = c >> 3;
|
||||
t_table[r + 2] = t_tmp2 >> 4;
|
||||
t_table[r + 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 & 0x0f);
|
||||
}
|
||||
s += 12;
|
||||
c += 12;
|
||||
k++;
|
||||
j++;
|
||||
} while (k < t);
|
||||
}
|
||||
memcpy(src, t_table, outputSize);
|
||||
free(t_table);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
outputSize = compDecode(compInput, compOutput);
|
||||
p = compOutput;
|
||||
for (z = 2; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
for (z = 1; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
|
||||
t_table = (byte *)malloc(outputSize);
|
||||
assert(t_table);
|
||||
memcpy(t_table, p, outputSize);
|
||||
|
||||
offset1 = outputSize / 3;
|
||||
offset2 = offset1 << 1;
|
||||
offset3 = offset2;
|
||||
src = compOutput;
|
||||
|
||||
while (offset1--) {
|
||||
offset2 -= 2;
|
||||
offset3--;
|
||||
t_table[offset2 + 0] = src[offset1];
|
||||
t_table[offset2 + 1] = src[offset3];
|
||||
}
|
||||
|
||||
src = compOutput;
|
||||
length = (outputSize << 3) / 12;
|
||||
k = 0;
|
||||
if (length > 0) {
|
||||
c = -12;
|
||||
s = 0;
|
||||
do {
|
||||
j = length + (k >> 1);
|
||||
t_tmp1 = t_table[k];
|
||||
if (k & 1) {
|
||||
r = c >> 3;
|
||||
t_tmp2 = t_table[j + 1];
|
||||
src[r + 2] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4);
|
||||
src[r + 1] = (src[r + 1]) | (t_tmp1 & 0xf0);
|
||||
} else {
|
||||
r = s >> 3;
|
||||
t_tmp2 = t_table[j];
|
||||
src[r + 0] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f);
|
||||
src[r + 1] = t_tmp1 >> 4;
|
||||
}
|
||||
s += 12;
|
||||
c += 12;
|
||||
k++;
|
||||
} while (k < length);
|
||||
}
|
||||
offset1 = ((length - 1) * 3) >> 1;
|
||||
src[offset1 + 1] = (t_table[length] & 0xf0) | src[offset1 + 1];
|
||||
free(t_table);
|
||||
break;
|
||||
|
||||
case 11:
|
||||
outputSize = compDecode(compInput, compOutput);
|
||||
p = compOutput;
|
||||
for (z = 2; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
for (z = 1; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
|
||||
t_table = (byte *)malloc(outputSize);
|
||||
assert(t_table);
|
||||
memcpy(t_table, p, outputSize);
|
||||
|
||||
offset1 = outputSize / 3;
|
||||
offset2 = offset1 << 1;
|
||||
offset3 = offset2;
|
||||
src = compOutput;
|
||||
|
||||
while (offset1--) {
|
||||
offset2 -= 2;
|
||||
offset3--;
|
||||
t_table[offset2 + 0] = src[offset1];
|
||||
t_table[offset2 + 1] = src[offset3];
|
||||
}
|
||||
|
||||
src = compOutput;
|
||||
length = (outputSize << 3) / 12;
|
||||
k = 1;
|
||||
c = 0;
|
||||
s = 12;
|
||||
t_tmp1 = t_table[length] >> 4;
|
||||
src[0] = t_tmp1;
|
||||
t = length + k;
|
||||
if (t > k) {
|
||||
do {
|
||||
j = length + (k >> 1);
|
||||
t_tmp1 = t_table[k - 1];
|
||||
t_tmp2 = t_table[j];
|
||||
if (k & 1) {
|
||||
r = c >> 3;
|
||||
src[r + 0] = (src[r]) | (t_tmp1 & 0xf0);
|
||||
src[r + 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f);
|
||||
} else {
|
||||
r = s >> 3;
|
||||
src[r + 0] = t_tmp1 >> 4;
|
||||
src[r - 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4);
|
||||
}
|
||||
s += 12;
|
||||
c += 12;
|
||||
k++;
|
||||
} while (k < t);
|
||||
}
|
||||
free(t_table);
|
||||
break;
|
||||
|
||||
case 12:
|
||||
outputSize = compDecode(compInput, compOutput);
|
||||
p = compOutput;
|
||||
for (z = 2; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
for (z = 1; z < outputSize; z++)
|
||||
p[z] += p[z - 1];
|
||||
|
||||
t_table = (byte *)malloc(outputSize);
|
||||
assert(t_table);
|
||||
memcpy(t_table, p, outputSize);
|
||||
|
||||
offset1 = outputSize / 3;
|
||||
offset2 = offset1 << 1;
|
||||
offset3 = offset2;
|
||||
src = compOutput;
|
||||
|
||||
while (offset1--) {
|
||||
offset2 -= 2;
|
||||
offset3--;
|
||||
t_table[offset2 + 0] = src[offset1];
|
||||
t_table[offset2 + 1] = src[offset3];
|
||||
}
|
||||
|
||||
src = compOutput;
|
||||
length = (outputSize << 3) / 12;
|
||||
k = 0;
|
||||
c = 0;
|
||||
s = -12;
|
||||
src[0] = t_table[outputSize - 1];
|
||||
src[outputSize - 1] = t_table[length - 1];
|
||||
t = length - 1;
|
||||
if (t > 0) {
|
||||
do {
|
||||
j = length + (k >> 1);
|
||||
t_tmp1 = t_table[k];
|
||||
t_tmp2 = t_table[j];
|
||||
if (k & 1) {
|
||||
r = s >> 3;
|
||||
src[r + 2] = (src[r + 2]) | (t_tmp1 & 0xf0);
|
||||
src[r + 3] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4);
|
||||
} else {
|
||||
r = c >> 3;
|
||||
src[r + 2] = t_tmp1 >> 4;
|
||||
src[r + 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f);
|
||||
}
|
||||
s += 12;
|
||||
c += 12;
|
||||
k++;
|
||||
} while (k < t);
|
||||
}
|
||||
free(t_table);
|
||||
break;
|
||||
|
||||
case 13:
|
||||
case 15:
|
||||
outputSize = decompressADPCM(compInput, compOutput, (codec == 13) ? 1 : 2);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("BundleCodecs::decompressCodec() Unknown codec %d", (int)codec);
|
||||
outputSize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return outputSize;
|
||||
}
|
||||
|
||||
} // End of namespace BundleCodecs
|
||||
|
||||
} // End of namespace Scumm
|
||||
40
engines/scumm/imuse_digi/dimuse_codecs.h
Normal file
40
engines/scumm/imuse_digi/dimuse_codecs.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SCUMM_IMUSE_DIGI_CODECS_H
|
||||
#define SCUMM_IMUSE_DIGI_CODECS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
namespace BundleCodecs {
|
||||
|
||||
int32 decompressCodec(int32 codec, byte *compInput, byte *compOutput, int32 inputSize);
|
||||
|
||||
void initializeImcTables();
|
||||
void releaseImcTables();
|
||||
|
||||
} // End of namespace BundleCodecs
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
||||
#endif
|
||||
280
engines/scumm/imuse_digi/dimuse_defs.h
Normal file
280
engines/scumm/imuse_digi/dimuse_defs.h
Normal file
@@ -0,0 +1,280 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(SCUMM_IMUSE_DIGI_DEFS_H) && defined(ENABLE_SCUMM_7_8)
|
||||
#define SCUMM_IMUSE_DIGI_DEFS_H
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
#define DIMUSE_MAX_GROUPS 16
|
||||
#define DIMUSE_MAX_FADES 16
|
||||
#define DIMUSE_MAX_TRIGGERS 8
|
||||
#define DIMUSE_MAX_DEFERS 8
|
||||
#define DIMUSE_MAX_TRACKS 8
|
||||
#define DIMUSE_MAX_MAP_SIZE 2048
|
||||
#define DIMUSE_MAX_DISPATCHES 8
|
||||
#define DIMUSE_MAX_STREAMZONES 50
|
||||
#define DIMUSE_MAX_FADE_VOLUME 8323072
|
||||
#define DIMUSE_MAX_STREAMS 3
|
||||
|
||||
#define DIMUSE_LARGE_FADES 1
|
||||
#define DIMUSE_SMALL_FADES 4
|
||||
#define DIMUSE_LARGE_FADE_DIM 350000
|
||||
#define DIMUSE_SMALL_FADE_DIM 44100
|
||||
|
||||
#define DIMUSE_BASE_SAMPLERATE 22050
|
||||
#define DIMUSE_BASE_FEEDSIZE 512
|
||||
#define DIMUSE_NUM_WAVE_BUFS 8
|
||||
#define DIMUSE_SMUSH_SOUNDID 12345678
|
||||
#define DIMUSE_BUN_CHUNK_SIZE 0x2000
|
||||
#define DIMUSE_GROUP_SFX 1
|
||||
#define DIMUSE_GROUP_SPEECH 2
|
||||
#define DIMUSE_GROUP_MUSIC 3
|
||||
#define DIMUSE_GROUP_MUSICEFF 4
|
||||
#define DIMUSE_BUFFER_SPEECH 1
|
||||
#define DIMUSE_BUFFER_MUSIC 2
|
||||
#define DIMUSE_BUFFER_SFX 3
|
||||
|
||||
#define DIMUSE_TIMER_BASE_RATE_HZ 50
|
||||
#define DIMUSE_TIMER_BASE_RATE_USEC 20000 // 1000000 / 50Hz
|
||||
#define DIMUSE_TIMER_GAIN_RED_RATE_USEC 100000 // 1000000 / 10Hz
|
||||
#define DIMUSE_TIMER_FADES_RATE_USEC 16667 // 1000000 / 60Hz
|
||||
|
||||
// Parameters IDs
|
||||
#define DIMUSE_P_BOGUS_ID 0x0
|
||||
#define DIMUSE_P_SND_TRACK_NUM 0x100
|
||||
#define DIMUSE_P_TRIGS_SNDS 0x200
|
||||
#define DIMUSE_P_MARKER 0x300
|
||||
#define DIMUSE_P_GROUP 0x400
|
||||
#define DIMUSE_P_PRIORITY 0x500
|
||||
#define DIMUSE_P_VOLUME 0x600
|
||||
#define DIMUSE_P_PAN 0x700
|
||||
#define DIMUSE_P_DETUNE 0x800
|
||||
#define DIMUSE_P_TRANSPOSE 0x900
|
||||
#define DIMUSE_P_MAILBOX 0xA00
|
||||
#define DIMUSE_P_UNKNOWN 0xF00
|
||||
#define DIMUSE_P_SND_HAS_STREAM 0x1800
|
||||
#define DIMUSE_P_STREAM_BUFID 0x1900
|
||||
#define DIMUSE_P_SND_POS_IN_MS 0x1A00
|
||||
|
||||
// Soundkludge command IDs
|
||||
#define DIMUSE_C_KLUDGE_SET_STATE 0x1000
|
||||
#define DIMUSE_C_KLUDGE_SET_SEQUENCE 0x1001
|
||||
#define DIMUSE_C_KLUDGE_SET_CUE_POINT 0x1002
|
||||
#define DIMUSE_C_KLUDGE_SET_ATTRIBUTE 0x1003
|
||||
#define DIMUSE_C_KLUDGE_SET_SFX_VOLUME 0x2000
|
||||
#define DIMUSE_C_KLUDGE_SET_VOICE_VOLUME 0x2001
|
||||
#define DIMUSE_C_KLUDGE_SET_MUSIC_VOLUME 0x2002
|
||||
#define DIMUSE_C_KLUDGE_STOP_ALL_SNDS 10
|
||||
#define DIMUSE_C_KLUDGE_SET_PARAM 12
|
||||
#define DIMUSE_C_KLUDGE_FADE_PARAM 14
|
||||
#define DIMUSE_C_KLUDGE_START_STREAM 25
|
||||
#define DIMUSE_C_KLUDGE_SWITCH_STREAM 26
|
||||
|
||||
// Script command IDs
|
||||
#define DIMUSE_C_SCRIPT_INIT 0
|
||||
#define DIMUSE_C_SCRIPT_TERMINATE 1
|
||||
#define DIMUSE_C_SCRIPT_SAVE 2
|
||||
#define DIMUSE_C_SCRIPT_RESTORE 3
|
||||
#define DIMUSE_C_SCRIPT_REFRESH 4
|
||||
#define DIMUSE_C_SCRIPT_SET_STATE 5
|
||||
#define DIMUSE_C_SCRIPT_SET_SEQUENCE 6
|
||||
#define DIMUSE_C_SCRIPT_CUE_POINT 7
|
||||
#define DIMUSE_C_SCRIPT_SET_ATTRIBUTE 8
|
||||
|
||||
// Internal command IDs
|
||||
#define DIMUSE_C_INIT 0
|
||||
#define DIMUSE_C_PAUSE 3
|
||||
#define DIMUSE_C_RESUME 4
|
||||
#define DIMUSE_C_SET_GRP_VOL 7
|
||||
#define DIMUSE_C_START_SND 8
|
||||
#define DIMUSE_C_STOP_SND 9
|
||||
#define DIMUSE_C_STOP_ALL_SNDS 10
|
||||
#define DIMUSE_C_GET_NEXT_SND 11
|
||||
#define DIMUSE_C_SET_PARAM 12
|
||||
#define DIMUSE_C_GET_PARAM 13
|
||||
#define DIMUSE_C_FADE_PARAM 14
|
||||
#define DIMUSE_C_SET_HOOK 15
|
||||
#define DIMUSE_C_GET_HOOK 16
|
||||
#define DIMUSE_C_SET_TRIGGER 17
|
||||
#define DIMUSE_C_CHECK_TRIGGER 18
|
||||
#define DIMUSE_C_CLEAR_TRIGGER 19
|
||||
#define DIMUSE_C_DEFER_CMD 20
|
||||
#define DIMUSE_C_GET_MARKER_SYNCS 21
|
||||
#define DIMUSE_C_START_STREAM 25
|
||||
#define DIMUSE_C_SWITCH_STREAM 26
|
||||
#define DIMUSE_C_PROCESS_STREAMS 27
|
||||
#define DIMUSE_C_FEED_STREAM 29
|
||||
|
||||
// Trigger callback command ID
|
||||
#define DIMUSE_C_SCRIPT_CALLBACK 0
|
||||
|
||||
// Block IDs for the Creative Voice File format
|
||||
// used within Full Throttle and The Dig (demo)
|
||||
#define VOC_DIGI_DATA_BLOCK 1
|
||||
#define VOC_MARKER_BLOCK 4
|
||||
#define VOC_LOOP_START_BLOCK 6
|
||||
#define VOC_LOOP_END_BLOCK 7
|
||||
|
||||
struct IMuseDigiDispatch;
|
||||
struct IMuseDigiTrack;
|
||||
struct IMuseDigiStreamZone;
|
||||
|
||||
typedef struct {
|
||||
int sound;
|
||||
char text[256];
|
||||
int opcode;
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
int g;
|
||||
int h;
|
||||
int i;
|
||||
int j;
|
||||
int clearLater;
|
||||
} IMuseDigiTrigger;
|
||||
|
||||
typedef struct {
|
||||
int counter;
|
||||
int opcode;
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
int g;
|
||||
int h;
|
||||
int i;
|
||||
int j;
|
||||
} IMuseDigiDefer;
|
||||
|
||||
typedef struct {
|
||||
int status;
|
||||
int sound;
|
||||
int param;
|
||||
int currentVal;
|
||||
int counter;
|
||||
int length;
|
||||
int slope;
|
||||
int slopeMod;
|
||||
int modOvfloCounter;
|
||||
int nudge;
|
||||
} IMuseDigiFade;
|
||||
|
||||
struct IMuseDigiTrack {
|
||||
int index;
|
||||
IMuseDigiTrack *prev;
|
||||
IMuseDigiTrack *next;
|
||||
IMuseDigiDispatch *dispatchPtr;
|
||||
int soundId;
|
||||
int marker;
|
||||
int group;
|
||||
int priority;
|
||||
int vol;
|
||||
int effVol;
|
||||
int pan;
|
||||
int detune;
|
||||
int transpose;
|
||||
int pitchShift;
|
||||
int mailbox;
|
||||
int jumpHook;
|
||||
int32 syncSize_0;
|
||||
byte *syncPtr_0;
|
||||
int32 syncSize_1;
|
||||
byte *syncPtr_1;
|
||||
int32 syncSize_2;
|
||||
byte *syncPtr_2;
|
||||
int32 syncSize_3;
|
||||
byte *syncPtr_3;
|
||||
};
|
||||
|
||||
struct IMuseDigiStreamZone {
|
||||
IMuseDigiStreamZone *prev;
|
||||
IMuseDigiStreamZone *next;
|
||||
int useFlag;
|
||||
int32 offset;
|
||||
int32 size;
|
||||
int fadeFlag;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int soundId;
|
||||
int32 curOffset;
|
||||
int32 endOffset;
|
||||
int bufId;
|
||||
uint8 *buf;
|
||||
int32 bufFreeSize;
|
||||
int32 loadSize;
|
||||
int32 criticalSize;
|
||||
int32 maxRead;
|
||||
int32 loadIndex;
|
||||
int32 readIndex;
|
||||
int paused;
|
||||
int vocLoopFlag;
|
||||
int32 vocLoopTriggerOffset;
|
||||
} IMuseDigiStream;
|
||||
|
||||
typedef struct {
|
||||
uint8 *buffer;
|
||||
int32 bufSize;
|
||||
int32 loadSize;
|
||||
int32 criticalSize;
|
||||
} IMuseDigiSndBuffer;
|
||||
|
||||
struct IMuseDigiDispatch {
|
||||
IMuseDigiTrack *trackPtr;
|
||||
int wordSize;
|
||||
int sampleRate;
|
||||
int channelCount;
|
||||
int32 currentOffset;
|
||||
int32 audioRemaining;
|
||||
int32 map[DIMUSE_MAX_MAP_SIZE];
|
||||
IMuseDigiStream *streamPtr;
|
||||
int streamBufID;
|
||||
IMuseDigiStreamZone *streamZoneList;
|
||||
int streamErrFlag;
|
||||
uint8 *fadeBuf;
|
||||
int32 fadeOffset;
|
||||
int32 fadeRemaining;
|
||||
int fadeWordSize;
|
||||
int fadeSampleRate;
|
||||
int fadeChannelCount;
|
||||
int fadeSyncFlag;
|
||||
int32 fadeSyncDelta;
|
||||
int fadeVol;
|
||||
int fadeSlope;
|
||||
int32 vocLoopStartingPoint;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int bytesPerSample;
|
||||
int numChannels;
|
||||
uint8 *mixBuf;
|
||||
int mixBufSize;
|
||||
int sizeSampleKB;
|
||||
} waveOutParamsStruct;
|
||||
|
||||
} // End of namespace Scumm
|
||||
#endif
|
||||
1761
engines/scumm/imuse_digi/dimuse_dispatch.cpp
Normal file
1761
engines/scumm/imuse_digi/dimuse_dispatch.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1144
engines/scumm/imuse_digi/dimuse_engine.cpp
Normal file
1144
engines/scumm/imuse_digi/dimuse_engine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
446
engines/scumm/imuse_digi/dimuse_engine.h
Normal file
446
engines/scumm/imuse_digi/dimuse_engine.h
Normal file
@@ -0,0 +1,446 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(SCUMM_IMUSE_DIGI_H) && defined(ENABLE_SCUMM_7_8)
|
||||
#define SCUMM_IMUSE_DIGI_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "scumm/scumm_v7.h"
|
||||
#include "scumm/music.h"
|
||||
#include "scumm/sound.h"
|
||||
#include "scumm/file.h"
|
||||
#include "scumm/debugger.h"
|
||||
|
||||
#include "scumm/imuse_digi/dimuse_defs.h"
|
||||
#include "scumm/imuse_digi/dimuse_internalmixer.h"
|
||||
#include "scumm/imuse_digi/dimuse_groups.h"
|
||||
#include "scumm/imuse_digi/dimuse_fades.h"
|
||||
#include "scumm/imuse_digi/dimuse_files.h"
|
||||
#include "scumm/imuse_digi/dimuse_triggers.h"
|
||||
#include "scumm/imuse_digi/dimuse_bndmgr.h"
|
||||
#include "scumm/imuse_digi/dimuse_sndmgr.h"
|
||||
#include "scumm/imuse_digi/dimuse_tables.h"
|
||||
|
||||
#include "scumm/smush/smush_player.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
namespace Audio {
|
||||
class AudioStream;
|
||||
class Mixer;
|
||||
class QueuingAudioStream;
|
||||
}
|
||||
|
||||
namespace Scumm {
|
||||
class ScummEngine_v7;
|
||||
class SmushPlayer;
|
||||
|
||||
struct imuseDigTable;
|
||||
struct imuseComiTable;
|
||||
struct IMuseDigiDispatch;
|
||||
struct IMuseDigiTrack;
|
||||
struct IMuseDigiStreamZone;
|
||||
|
||||
class IMuseDigital : public MusicEngine {
|
||||
private:
|
||||
Common::Mutex *_mutex;
|
||||
ScummEngine_v7 *_vm;
|
||||
Audio::Mixer *_mixer;
|
||||
SmushPlayer *_splayer;
|
||||
|
||||
IMuseDigiInternalMixer *_internalMixer;
|
||||
IMuseDigiGroupsHandler *_groupsHandler;
|
||||
IMuseDigiFadesHandler *_fadesHandler;
|
||||
IMuseDigiTriggersHandler *_triggersHandler;
|
||||
IMuseDigiFilesHandler *_filesHandler;
|
||||
|
||||
int _callbackFps;
|
||||
static void timer_handler(void *refConf);
|
||||
void callback();
|
||||
|
||||
bool _isEarlyDiMUSE;
|
||||
bool _isEngineDisabled;
|
||||
bool _checkForUnderrun;
|
||||
int _underrunCooldown;
|
||||
bool _lowLatencyMode;
|
||||
|
||||
int _internalFeedSize;
|
||||
int _internalSampleRate;
|
||||
|
||||
// These three are manipulated in the waveOut functions
|
||||
uint8 *_outputAudioBuffer;
|
||||
int _outputFeedSize;
|
||||
int _outputSampleRate;
|
||||
|
||||
// Used in low latency mode only
|
||||
uint8 *_outputLowLatencyAudioBuffers[DIMUSE_MAX_TRACKS];
|
||||
|
||||
int _maxQueuedStreams; // maximum number of streams which can be queued before they are played
|
||||
int _nominalBufferCount;
|
||||
|
||||
int _currentSpeechVolume, _currentSpeechFrequency, _currentSpeechPan;
|
||||
int _curMixerMusicVolume, _curMixerSpeechVolume, _curMixerSFXVolume;
|
||||
bool _radioChatterSFX;
|
||||
|
||||
int32 _attributes[188]; // internal attributes for each music file to store and check later
|
||||
int32 _nextSeqToPlay;
|
||||
int32 _curMusicState;
|
||||
int32 _curMusicSeq;
|
||||
int32 _curMusicCue;
|
||||
|
||||
char *_audioNames; // filenames of sound SFX used in FT
|
||||
int32 _numAudioNames; // number of above filenames
|
||||
uint8 _currentVOCHeader[52]; // Header for the current sound for early DiMUSE
|
||||
|
||||
int _stopSequenceFlag;
|
||||
int _scriptInitializedFlag;
|
||||
char _emptyMarker[1];
|
||||
bool _spooledMusicEnabled;
|
||||
|
||||
int _usecPerInt; // Microseconds between each callback (will be set to 50 Hz)
|
||||
int _callbackInterruptFlag;
|
||||
void diMUSEHeartbeat();
|
||||
|
||||
void setFtMusicState(int stateId);
|
||||
void setFtMusicSequence(int seqId);
|
||||
void playFtMusic(const char *songName, int opcode, int volume);
|
||||
void setDigMusicState(int stateId);
|
||||
void setDigMusicSequence(int seqId);
|
||||
void playDigMusic(const char *songName, const imuseDigTable *table, int attribPos, bool sequence);
|
||||
void setComiMusicState(int stateId);
|
||||
void setComiMusicSequence(int seqId);
|
||||
void playComiMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence);
|
||||
void playComiDemoMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence);
|
||||
int getSoundIdByName(const char *soundName);
|
||||
bool isMusicStreamIdle();
|
||||
bool isMusicCritical();
|
||||
|
||||
// Script
|
||||
int scriptParse(int cmd, int a, int b);
|
||||
int scriptInit();
|
||||
int scriptTerminate();
|
||||
void scriptRefresh();
|
||||
void scriptSetState(int soundId);
|
||||
void scriptSetSequence(int soundId);
|
||||
void scriptSetCuePoint(int cueId);
|
||||
int scriptSetAttribute(int attrIndex, int attrVal);
|
||||
|
||||
// CMDs
|
||||
int _cmdsPauseCount;
|
||||
int _cmdsRunning60HzCount;
|
||||
int _cmdsRunning10HzCount;
|
||||
|
||||
int cmdsInit();
|
||||
int cmdsDeinit();
|
||||
int cmdsTerminate();
|
||||
int cmdsPause();
|
||||
int cmdsResume();
|
||||
void cmdsSaveLoad(Common::Serializer &ser);
|
||||
int cmdsStartSound(int soundId, int priority);
|
||||
int cmdsStopSound(int soundId);
|
||||
int cmdsStopAllSounds();
|
||||
int cmdsGetNextSound(int soundId);
|
||||
int cmdsSetParam(int soundId, int opcode, int value);
|
||||
int cmdsGetParam(int soundId, int opcode);
|
||||
int cmdsSetHook(int soundId, int hookId);
|
||||
int cmdsGetHook(int soundId);
|
||||
|
||||
// Streamer
|
||||
IMuseDigiStream _streams[DIMUSE_MAX_STREAMS];
|
||||
IMuseDigiStream *_lastStreamLoaded;
|
||||
int _streamerBailFlag;
|
||||
|
||||
int streamerInit();
|
||||
IMuseDigiStream *streamerAllocateSound(int soundId, int bufId, int32 maxRead);
|
||||
int streamerClearSoundInStream(IMuseDigiStream *streamPtr);
|
||||
int streamerProcessStreams();
|
||||
uint8 *streamerGetStreamBuffer(IMuseDigiStream *streamPtr, int size);
|
||||
uint8 *streamerGetStreamBufferAtOffset(IMuseDigiStream *streamPtr, int32 offset, int size);
|
||||
int streamerSetReadIndex(IMuseDigiStream *streamPtr, int offset);
|
||||
int streamerSetLoadIndex(IMuseDigiStream *streamPtr, int offset);
|
||||
int streamerGetFreeBufferAmount(IMuseDigiStream *streamPtr);
|
||||
int streamerSetSoundToStreamFromOffset(IMuseDigiStream *streamPtr, int soundId, int32 offset);
|
||||
void streamerQueryStream(IMuseDigiStream *streamPtr, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused);
|
||||
int streamerFeedStream(IMuseDigiStream *streamPtr, uint8 *srcBuf, int32 sizeToFeed, int paused);
|
||||
int streamerFetchData(IMuseDigiStream *streamPtr);
|
||||
void streamerSetLoopFlag(IMuseDigiStream *streamPtr, int offset);
|
||||
void streamerRemoveLoopFlag(IMuseDigiStream *streamPtr);
|
||||
|
||||
// Tracks
|
||||
IMuseDigiTrack _tracks[DIMUSE_MAX_TRACKS];
|
||||
IMuseDigiTrack *_trackList;
|
||||
|
||||
int _trackCount;
|
||||
int _tracksPauseTimer;
|
||||
int _tracksMicroSecsToFeed;
|
||||
|
||||
int tracksInit();
|
||||
void tracksPause();
|
||||
void tracksResume();
|
||||
void tracksSaveLoad(Common::Serializer &ser);
|
||||
void tracksSetGroupVol();
|
||||
void tracksCallback();
|
||||
void tracksLowLatencyCallback();
|
||||
int tracksStartSound(int soundId, int tryPriority, int group);
|
||||
int tracksStopSound(int soundId);
|
||||
int tracksStopAllSounds();
|
||||
int tracksGetNextSound(int soundId);
|
||||
int tracksQueryStream(int soundId, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused);
|
||||
int tracksFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused);
|
||||
void tracksClear(IMuseDigiTrack *trackPtr);
|
||||
int tracksSetParam(int soundId, int opcode, int value);
|
||||
int tracksGetParam(int soundId, int opcode);
|
||||
int tracksLipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height);
|
||||
int tracksSetHook(int soundId, int hookId);
|
||||
int tracksGetHook(int soundId);
|
||||
IMuseDigiTrack *tracksReserveTrack(int priority);
|
||||
void tracksDeinit();
|
||||
|
||||
// Dispatch
|
||||
IMuseDigiDispatch _dispatches[DIMUSE_MAX_DISPATCHES];
|
||||
IMuseDigiStreamZone _streamZones[DIMUSE_MAX_STREAMZONES];
|
||||
uint8 *_dispatchBuffer;
|
||||
uint8 _ftCrossfadeBuffer[30000]; // Used by FT & DIG demo
|
||||
int32 _dispatchSize;
|
||||
uint8 *_dispatchSmallFadeBufs;
|
||||
uint8 *_dispatchLargeFadeBufs;
|
||||
int32 _dispatchFadeSize;
|
||||
int _dispatchLargeFadeFlags[DIMUSE_LARGE_FADES];
|
||||
int _dispatchSmallFadeFlags[DIMUSE_SMALL_FADES];
|
||||
int _dispatchFadeStartedFlag;
|
||||
int _dispatchBufferedHookId;
|
||||
int32 _dispatchJumpFadeSize;
|
||||
int32 _dispatchCurStreamBufSize;
|
||||
int32 _dispatchCurStreamCriticalSize;
|
||||
int32 _dispatchCurStreamFreeSpace;
|
||||
int _dispatchCurStreamPaused;
|
||||
|
||||
int dispatchInit();
|
||||
IMuseDigiDispatch *dispatchGetDispatchByTrackId(int trackId);
|
||||
void dispatchSaveLoad(Common::Serializer &ser);
|
||||
int dispatchRestoreStreamZones();
|
||||
int dispatchAllocateSound(IMuseDigiTrack *trackPtr, int groupId);
|
||||
int dispatchRelease(IMuseDigiTrack *trackPtr);
|
||||
int dispatchSwitchStream(int oldSoundId, int newSoundId, int fadeLength, int exitTriggerSyncFlag, int offsetFadeSyncFlag);
|
||||
int dispatchSwitchStream(int oldSoundId, int newSoundId, uint8 *crossfadeBuffer, int crossfadeBufferSize, int vocLoopFlag);
|
||||
void dispatchProcessDispatches(IMuseDigiTrack *trackPtr, int feedSize, int sampleRate);
|
||||
void dispatchProcessDispatches(IMuseDigiTrack *trackPtr, int feedSize);
|
||||
void dispatchPredictFirstStream();
|
||||
int dispatchNavigateMap(IMuseDigiDispatch *dispatchPtr);
|
||||
int dispatchGetMap(IMuseDigiDispatch *dispatchPtr);
|
||||
int dispatchConvertMap(uint8 *rawMap, int32 *destMap);
|
||||
uint8 *dispatchGetNextMapEvent(int32 *mapPtr, int32 soundOffset, uint8 *mapEvent);
|
||||
void dispatchPredictStream(IMuseDigiDispatch *dispatchPtr);
|
||||
uint8 *dispatchCheckForJump(int32 *mapPtr, IMuseDigiStreamZone *strZnPtr, int &candidateHookId);
|
||||
void dispatchPrepareToJump(IMuseDigiDispatch *dispatchPtr, IMuseDigiStreamZone *strZnPtr, uint8 *jumpParamsFromMap, int calledFromGetNextMapEvent);
|
||||
void dispatchStreamNextZone(IMuseDigiDispatch *dispatchPtr, IMuseDigiStreamZone *strZnPtr);
|
||||
IMuseDigiStreamZone *dispatchAllocateStreamZone();
|
||||
uint8 *dispatchAllocateFade(int32 &fadeSize, const char *functionName);
|
||||
void dispatchDeallocateFade(IMuseDigiDispatch *dispatchPtr, const char *functionName);
|
||||
int dispatchGetFadeSize(IMuseDigiDispatch *dispatchPtr, int fadeLength);
|
||||
void dispatchValidateFadeSize(IMuseDigiDispatch *dispatchPtr, int32 &dispatchSize, const char *functionName);
|
||||
int dispatchUpdateFadeMixVolume(IMuseDigiDispatch *dispatchPtr, int32 remainingFade);
|
||||
int dispatchUpdateFadeSlope(IMuseDigiDispatch *dispatchPtr);
|
||||
void dispatchVOCLoopCallback(int soundId);
|
||||
int dispatchSeekToNextChunk(IMuseDigiDispatch *dispatchPtr);
|
||||
|
||||
// Wave (mainly a wrapper for Tracks functions)
|
||||
int waveInit();
|
||||
int waveTerminate();
|
||||
int wavePause();
|
||||
int waveResume();
|
||||
void waveSaveLoad(Common::Serializer &ser);
|
||||
void waveUpdateGroupVolumes();
|
||||
int waveStartSound(int soundId, int priority);
|
||||
int waveStopSound(int soundId);
|
||||
int waveStopAllSounds();
|
||||
int waveGetNextSound(int soundId);
|
||||
int waveSetParam(int soundId, int opcode, int value);
|
||||
int waveGetParam(int soundId, int opcode);
|
||||
int waveSetHook(int soundId, int hookId);
|
||||
int waveGetHook(int soundId);
|
||||
int waveStartStream(int soundId, int priority, int groupId);
|
||||
int waveSwitchStream(int oldSoundId, int newSoundId, int fadeLengthMs, int fadeSyncFlag2, int fadeSyncFlag1);
|
||||
int waveSwitchStream(int oldSoundId, int newSoundId, uint8 *crossfadeBuffer, int crossfadeBufferSize, int vocLoopFlag);
|
||||
int waveProcessStreams();
|
||||
int waveQueryStream(int soundId, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused);
|
||||
int waveFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused);
|
||||
int waveLipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height);
|
||||
|
||||
// Waveapi
|
||||
waveOutParamsStruct waveOutSettings;
|
||||
|
||||
int _waveOutSampleRate;
|
||||
int _waveOutBytesPerSample;
|
||||
int _waveOutNumChannels;
|
||||
int _waveOutZeroLevel;
|
||||
int _waveOutPreferredFeedSize;
|
||||
uint8 *_waveOutMixBuffer;
|
||||
uint8 *_waveOutOutputBuffer;
|
||||
|
||||
int _waveOutXorTrigger;
|
||||
int _waveOutWriteIndex;
|
||||
int _waveOutDisableWrite;
|
||||
|
||||
int waveOutInit(waveOutParamsStruct *waveOutSettings);
|
||||
void waveOutWrite(uint8 **audioBuffer, int &feedSize, int &sampleRate);
|
||||
int waveOutDeinit();
|
||||
void waveOutCallback();
|
||||
byte waveOutGetStreamFlags();
|
||||
|
||||
// Low latency mode
|
||||
void waveOutLowLatencyWrite(uint8 **audioBuffer, int &feedSize, int &sampleRate, int idx);
|
||||
void waveOutEmptyBuffer(int idx);
|
||||
|
||||
uint8 *_waveOutLowLatencyOutputBuffer;
|
||||
|
||||
public:
|
||||
IMuseDigital(ScummEngine_v7 *scumm, int sampleRate, Audio::Mixer *mixer, Common::Mutex *mutex, bool lowLatencyMode = false);
|
||||
~IMuseDigital() override;
|
||||
|
||||
// Wrapper functions used by the main engine
|
||||
|
||||
void startSound(int sound) override { error("IMuseDigital::startSound(int) should be never called"); }
|
||||
void setMusicVolume(int vol) override {}
|
||||
void stopSound(int sound) override;
|
||||
void stopAllSounds() override;
|
||||
int getSoundStatus(int sound) const override { return 0; }
|
||||
int isSoundRunning(int soundId); // Needed because getSoundStatus is a const function, and I needed a workaround
|
||||
int startVoice(int soundId, const char *soundName, byte speakingActorId);
|
||||
int startVoice(const char *fileName, ScummFile *file, uint32 offset, uint32 size);
|
||||
void saveLoadEarly(Common::Serializer &ser);
|
||||
void setRadioChatterSFX(bool state);
|
||||
void setAudioNames(int32 num, char *names);
|
||||
int startSfx(int soundId, int priority) ;
|
||||
void setPriority(int soundId, int priority);
|
||||
void setVolume(int soundId, int volume);
|
||||
void setPan(int soundId, int pan);
|
||||
void setFrequency(int soundId, int frequency);
|
||||
int getCurSpeechVolume() const;
|
||||
int getCurSpeechPan() const;
|
||||
int getCurSpeechFrequency() const;
|
||||
void pause(bool pause);
|
||||
void parseScriptCmds(int cmd, int soundId, int sub_cmd, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p);
|
||||
void refreshScripts();
|
||||
void flushTracks();
|
||||
void disableEngine();
|
||||
bool isEngineDisabled();
|
||||
void stopSMUSHAudio();
|
||||
void receiveAudioFromSMUSH(uint8 *srcBuf, int32 inFrameCount, int32 feedSize, int32 mixBufStartIndex, int volume, int pan, bool is11025Hz);
|
||||
void setSmushPlayer(SmushPlayer *splayer);
|
||||
void floodMusicBuffer();
|
||||
void fillStreamsWhileMusicCritical(int fillTimesAfter);
|
||||
bool queryNextSoundFile(int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused);
|
||||
int getSampleRate();
|
||||
int getFeedSize();
|
||||
|
||||
bool isFTSoundEngine(); // Used in the handlers to check if we're using the FT version of the engine
|
||||
|
||||
int32 getCurMusicPosInMs();
|
||||
int32 getCurVoiceLipSyncWidth();
|
||||
int32 getCurVoiceLipSyncHeight();
|
||||
int32 getCurMusicLipSyncWidth(int syncId);
|
||||
int32 getCurMusicLipSyncHeight(int syncId);
|
||||
void getSpeechLipSyncInfo(int32 &width, int32 &height);
|
||||
void getMusicLipSyncInfo(int syncId, int32 &width, int32 &height);
|
||||
int32 getSoundElapsedTimeInMs(int soundId);
|
||||
|
||||
// General engine functions
|
||||
int diMUSETerminate();
|
||||
int diMUSEInitialize();
|
||||
int diMUSEPause();
|
||||
int diMUSEResume();
|
||||
void diMUSESaveLoad(Common::Serializer &ser);
|
||||
int diMUSESetGroupVol(int groupId, int volume);
|
||||
int diMUSEStartSound(int soundId, int priority);
|
||||
int diMUSEStopSound(int soundId);
|
||||
int diMUSEStopAllSounds();
|
||||
int diMUSEGetNextSound(int soundId);
|
||||
int diMUSESetParam(int soundId, int paramId, int value);
|
||||
int diMUSEGetParam(int soundId, int paramId);
|
||||
int diMUSEFadeParam(int soundId, int opcode, int destValue, int fadeLength);
|
||||
int diMUSESetHook(int soundId, int hookId);
|
||||
|
||||
int diMUSESetTrigger(int soundId, int marker, int opcode,
|
||||
int d = -1, int e = -1, int f = -1, int g = -1,
|
||||
int h = -1, int i = -1, int j = -1, int k = -1,
|
||||
int l = -1, int m = -1, int n = -1);
|
||||
|
||||
int diMUSEStartStream(int soundId, int priority, int groupId);
|
||||
int diMUSESwitchStream(int oldSoundId, int newSoundId, int fadeDelay, int fadeSyncFlag2, int fadeSyncFlag1);
|
||||
int diMUSESwitchStream(int oldSoundId, int newSoundId, uint8 *crossfadeBuffer, int crossfadeBufferSize, int vocLoopFlag);
|
||||
int diMUSEProcessStreams();
|
||||
int diMUSEQueryStream(int soundId, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused);
|
||||
int diMUSEFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused);
|
||||
int diMUSELipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height);
|
||||
int diMUSEGetMusicGroupVol();
|
||||
int diMUSEGetSFXGroupVol();
|
||||
int diMUSEGetVoiceGroupVol();
|
||||
int diMUSESetMusicGroupVol(int volume);
|
||||
int diMUSESetSFXGroupVol(int volume);
|
||||
int diMUSESetVoiceGroupVol(int volume);
|
||||
void diMUSEUpdateGroupVolumes();
|
||||
int diMUSEInitializeScript();
|
||||
void diMUSERefreshScript();
|
||||
int diMUSESetState(int soundId);
|
||||
int diMUSESetSequence(int soundId);
|
||||
int diMUSESetCuePoint(int cueId);
|
||||
int diMUSESetAttribute(int attrIndex, int attrVal);
|
||||
void diMUSEEnableSpooledMusic();
|
||||
void diMUSEDisableSpooledMusic();
|
||||
|
||||
// Utils
|
||||
int addTrackToList(IMuseDigiTrack **listPtr, IMuseDigiTrack *listPtr_Item);
|
||||
int removeTrackFromList(IMuseDigiTrack **listPtr, IMuseDigiTrack *itemPtr);
|
||||
int addStreamZoneToList(IMuseDigiStreamZone **listPtr, IMuseDigiStreamZone *listPtr_Item);
|
||||
int removeStreamZoneFromList(IMuseDigiStreamZone **listPtr, IMuseDigiStreamZone *itemPtr);
|
||||
int clampNumber(int value, int minValue, int maxValue);
|
||||
int clampTuning(int value, int minValue, int maxValue);
|
||||
int checkHookId(int &trackHookId, int sampleHookId);
|
||||
int roundRobinSetBufferCount();
|
||||
void adaptBufferCount();
|
||||
|
||||
// CMDs
|
||||
int cmdsHandleCmd(int cmd, uint8 *ptr = nullptr,
|
||||
int a = -1, int b = -1, int c = -1, int d = -1, int e = -1,
|
||||
int f = -1, int g = -1, int h = -1, int i = -1, int j = -1,
|
||||
int k = -1, int l = -1, int m = -1, int n = -1);
|
||||
|
||||
// Script
|
||||
int scriptTriggerCallback(char *marker);
|
||||
|
||||
// Debugger utility functions
|
||||
void listStates();
|
||||
void listSeqs();
|
||||
void listCues();
|
||||
void listTracks();
|
||||
void listGroups();
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
||||
#endif
|
||||
178
engines/scumm/imuse_digi/dimuse_fades.cpp
Normal file
178
engines/scumm/imuse_digi/dimuse_fades.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
#include "scumm/imuse_digi/dimuse_fades.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
IMuseDigiFadesHandler::IMuseDigiFadesHandler(IMuseDigital *engine, Common::Mutex *mutex) {
|
||||
_engine = engine;
|
||||
_mutex = mutex;
|
||||
}
|
||||
|
||||
IMuseDigiFadesHandler::~IMuseDigiFadesHandler() {}
|
||||
|
||||
int IMuseDigiFadesHandler::init() {
|
||||
clearAllFades();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigiFadesHandler::fadeParam(int soundId, int opcode, int destinationValue, int fadeLength) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
if (!soundId || fadeLength < 0)
|
||||
return -5;
|
||||
|
||||
if (opcode != DIMUSE_P_PRIORITY && opcode != DIMUSE_P_VOLUME && opcode != DIMUSE_P_PAN && opcode != DIMUSE_P_DETUNE && opcode != DIMUSE_P_UNKNOWN && opcode != 17)
|
||||
return -5;
|
||||
|
||||
clearFadeStatus(soundId, opcode);
|
||||
|
||||
if (!fadeLength) {
|
||||
debug(5, "IMuseDigiFadesHandler::fadeParam(): WARNING: allocated fade with zero length for sound %d", soundId);
|
||||
if (opcode != DIMUSE_P_VOLUME || destinationValue) {
|
||||
_engine->diMUSESetParam(soundId, opcode, destinationValue);
|
||||
} else {
|
||||
_engine->diMUSEStopSound(soundId);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
|
||||
if (!_fades[l].status) {
|
||||
_fades[l].sound = soundId;
|
||||
_fades[l].param = opcode;
|
||||
_fades[l].currentVal = _engine->diMUSEGetParam(soundId, opcode);
|
||||
_fades[l].length = fadeLength;
|
||||
_fades[l].counter = fadeLength;
|
||||
_fades[l].slope = (destinationValue - _fades[l].currentVal) / fadeLength;
|
||||
_fades[l].modOvfloCounter = 0;
|
||||
_fades[l].status = 1;
|
||||
_fadesOn = 1;
|
||||
|
||||
if ((destinationValue - _fades[l].currentVal) < 0) {
|
||||
_fades[l].nudge = -1;
|
||||
_fades[l].slopeMod = (-(destinationValue - _fades[l].currentVal) % fadeLength);
|
||||
} else {
|
||||
_fades[l].nudge = 1;
|
||||
_fades[l].slopeMod = (destinationValue - _fades[l].currentVal) % fadeLength;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
debug(5, "IMuseDigiFadesHandler::fadeParam(): unable to allocate fade for sound %d", soundId);
|
||||
return -6;
|
||||
}
|
||||
|
||||
void IMuseDigiFadesHandler::clearFadeStatus(int soundId, int opcode) {
|
||||
for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
|
||||
if (_fades[l].status
|
||||
&& _fades[l].sound == soundId
|
||||
&& (_fades[l].param == opcode || opcode == -1)) {
|
||||
_fades[l].status = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigiFadesHandler::loop() {
|
||||
if (!_fadesOn)
|
||||
return;
|
||||
|
||||
_fadesOn = 0;
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
|
||||
if (_fades[l].status) {
|
||||
_fadesOn = 1;
|
||||
if (--_fades[l].counter == 0) {
|
||||
_fades[l].status = 0;
|
||||
}
|
||||
|
||||
int currentVolume = _fades[l].currentVal + _fades[l].slope;
|
||||
int currentSlopeMod = _fades[l].modOvfloCounter + _fades[l].slopeMod;
|
||||
_fades[l].modOvfloCounter += _fades[l].slopeMod;
|
||||
|
||||
if (_fades[l].length <= currentSlopeMod) {
|
||||
_fades[l].modOvfloCounter = currentSlopeMod - _fades[l].length;
|
||||
currentVolume += _fades[l].nudge;
|
||||
}
|
||||
|
||||
if (_fades[l].currentVal != currentVolume) {
|
||||
_fades[l].currentVal = currentVolume;
|
||||
|
||||
if (!(_fades[l].counter % 6)) {
|
||||
debug(5, "IMuseDigiFadesHandler::loop(): running fade for sound %d with id %d, currently at volume %d", _fades[l].sound, l, currentVolume);
|
||||
if ((_fades[l].param != DIMUSE_P_VOLUME) || currentVolume) {
|
||||
_engine->diMUSESetParam(_fades[l].sound, _fades[l].param, currentVolume);
|
||||
} else {
|
||||
_engine->diMUSEStopSound(_fades[l].sound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigiFadesHandler::deinit() {
|
||||
clearAllFades();
|
||||
}
|
||||
|
||||
void IMuseDigiFadesHandler::saveLoad(Common::Serializer &ser) {
|
||||
for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
|
||||
ser.syncAsSint32LE(_fades[l].status, VER(103));
|
||||
ser.syncAsSint32LE(_fades[l].sound, VER(103));
|
||||
ser.syncAsSint32LE(_fades[l].param, VER(103));
|
||||
ser.syncAsSint32LE(_fades[l].currentVal, VER(103));
|
||||
ser.syncAsSint32LE(_fades[l].counter, VER(103));
|
||||
ser.syncAsSint32LE(_fades[l].length, VER(103));
|
||||
ser.syncAsSint32LE(_fades[l].slope, VER(103));
|
||||
ser.syncAsSint32LE(_fades[l].slopeMod, VER(103));
|
||||
ser.syncAsSint32LE(_fades[l].modOvfloCounter, VER(103));
|
||||
ser.syncAsSint32LE(_fades[l].nudge, VER(103));
|
||||
}
|
||||
|
||||
if (ser.isLoading())
|
||||
_fadesOn = 1;
|
||||
}
|
||||
|
||||
void IMuseDigiFadesHandler::clearAllFades() {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
|
||||
_fades[l].status = 0;
|
||||
_fades[l].sound = 0;
|
||||
_fades[l].param = 0;
|
||||
_fades[l].currentVal = 0;
|
||||
_fades[l].counter = 0;
|
||||
_fades[l].length = 0;
|
||||
_fades[l].slope = 0;
|
||||
_fades[l].slopeMod = 0;
|
||||
_fades[l].modOvfloCounter = 0;
|
||||
_fades[l].nudge = 0;
|
||||
}
|
||||
|
||||
_fadesOn = 0;
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
54
engines/scumm/imuse_digi/dimuse_fades.h
Normal file
54
engines/scumm/imuse_digi/dimuse_fades.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(SCUMM_IMUSE_DIGI_FADES_H) && defined(ENABLE_SCUMM_7_8)
|
||||
#define SCUMM_IMUSE_DIGI_FADES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class IMuseDigiFadesHandler {
|
||||
|
||||
private:
|
||||
IMuseDigital *_engine;
|
||||
Common::Mutex *_mutex;
|
||||
IMuseDigiFade _fades[DIMUSE_MAX_FADES] = {};
|
||||
int _fadesOn = 0;
|
||||
|
||||
void clearAllFades();
|
||||
public:
|
||||
IMuseDigiFadesHandler(IMuseDigital *engine, Common::Mutex *mutex);
|
||||
~IMuseDigiFadesHandler();
|
||||
|
||||
int init();
|
||||
void deinit();
|
||||
void saveLoad(Common::Serializer &ser);
|
||||
int fadeParam(int soundId, int opcode, int destinationValue, int fadeLength);
|
||||
void clearFadeStatus(int soundId, int opcode);
|
||||
void loop();
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
#endif
|
||||
440
engines/scumm/imuse_digi/dimuse_files.cpp
Normal file
440
engines/scumm/imuse_digi/dimuse_files.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
#include "scumm/imuse_digi/dimuse_files.h"
|
||||
#include "scumm/file.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
IMuseDigiFilesHandler::IMuseDigiFilesHandler(IMuseDigital *engine, ScummEngine_v7 *vm) {
|
||||
_engine = engine;
|
||||
_sound = new ImuseDigiSndMgr(vm);
|
||||
assert(_sound);
|
||||
_vm = vm;
|
||||
|
||||
_ftSpeechFilename[0] = '\0';
|
||||
_ftSpeechSubFileOffset = 0;
|
||||
_ftSpeechFileSize = 0;
|
||||
_ftSpeechFileCurPos = 0;
|
||||
_ftSpeechFile = nullptr;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
IMuseDigiSndBuffer *selectedSoundBuf = &_soundBuffers[i];
|
||||
selectedSoundBuf->buffer = nullptr;
|
||||
selectedSoundBuf->bufSize = 0;
|
||||
selectedSoundBuf->loadSize = 0;
|
||||
selectedSoundBuf->criticalSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
IMuseDigiFilesHandler::~IMuseDigiFilesHandler() {
|
||||
delete _ftSpeechFile;
|
||||
delete _sound;
|
||||
}
|
||||
|
||||
void IMuseDigiFilesHandler::saveLoad(Common::Serializer &ser) {
|
||||
int curSound = 0;
|
||||
ImuseDigiSndMgr::SoundDesc *sounds = _sound->getSounds();
|
||||
|
||||
ser.syncArray(_currentSpeechFilename, 60, Common::Serializer::SByte, VER(103));
|
||||
if (ser.isSaving()) {
|
||||
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
||||
ser.syncAsSint32LE(sounds[l].soundId, VER(103));
|
||||
}
|
||||
if (_engine->isFTSoundEngine()) {
|
||||
ser.syncAsSint32LE(_ftSpeechFileCurPos, VER(103));
|
||||
ser.syncAsSint32LE(_ftSpeechFileSize, VER(103));
|
||||
ser.syncAsSint32LE(_ftSpeechSubFileOffset, VER(103));
|
||||
ser.syncArray(_ftSpeechFilename, sizeof(_ftSpeechFilename), Common::Serializer::SByte, VER(103));
|
||||
}
|
||||
}
|
||||
|
||||
if (ser.isLoading()) {
|
||||
// Close prior sounds if we're reloading (needed for edge cases like the recipe book in COMI)
|
||||
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
||||
_sound->closeSound(&sounds[l]);
|
||||
}
|
||||
|
||||
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
||||
ser.syncAsSint32LE(curSound, VER(103));
|
||||
if (curSound) {
|
||||
openSound(curSound);
|
||||
if (curSound != kTalkSoundID)
|
||||
closeSound(curSound);
|
||||
}
|
||||
}
|
||||
|
||||
if (_engine->isFTSoundEngine()) {
|
||||
ser.syncAsSint32LE(_ftSpeechFileCurPos, VER(103));
|
||||
ser.syncAsSint32LE(_ftSpeechFileSize, VER(103));
|
||||
ser.syncAsSint32LE(_ftSpeechSubFileOffset, VER(103));
|
||||
ser.syncArray(_ftSpeechFilename, sizeof(_ftSpeechFilename), Common::Serializer::SByte, VER(103));
|
||||
if (strlen(_ftSpeechFilename))
|
||||
_ftSpeechFile = _vm->_sound->restoreDiMUSESpeechFile(_ftSpeechFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8 *IMuseDigiFilesHandler::getSoundAddrData(int soundId) {
|
||||
if (_engine->isEngineDisabled())
|
||||
return nullptr;
|
||||
|
||||
// This function is always used for SFX (tracks which do not
|
||||
// have a stream pointer), hence the use of the resource address
|
||||
if (soundId != 0) {
|
||||
_vm->_res->lock(rtSound, soundId);
|
||||
byte *ptr = _vm->getResourceAddress(rtSound, soundId);
|
||||
if (!ptr) {
|
||||
_vm->_res->unlock(rtSound, soundId);
|
||||
return nullptr;
|
||||
}
|
||||
return ptr;
|
||||
|
||||
}
|
||||
debug(5, "IMuseDigiFilesHandler::getSoundAddrData(): soundId is 0 or out of range");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int IMuseDigiFilesHandler::getSoundAddrDataSize(int soundId, bool hasStream) {
|
||||
if (_engine->isEngineDisabled())
|
||||
return 0;
|
||||
|
||||
if (hasStream) {
|
||||
ImuseDigiSndMgr::SoundDesc *s = _sound->findSoundById(soundId);
|
||||
if (s) {
|
||||
if (soundId != kTalkSoundID) {
|
||||
return s->resSize;
|
||||
}
|
||||
} else if (soundId == kTalkSoundID) {
|
||||
return _ftSpeechFileSize;
|
||||
}
|
||||
} else {
|
||||
return _vm->getResourceSize(rtSound, soundId);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigiFilesHandler::getNextSound(int soundId) {
|
||||
int foundSoundId = 0;
|
||||
do {
|
||||
foundSoundId = _engine->diMUSEGetNextSound(foundSoundId);
|
||||
if (!foundSoundId)
|
||||
return -1;
|
||||
} while (foundSoundId != soundId);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int IMuseDigiFilesHandler::seek(int soundId, int32 offset, int mode, int bufId) {
|
||||
// This function and files_read() are used for sounds for which a stream is needed (speech
|
||||
// and music), therefore they will always refer to sounds in a bundle file for DIG and COMI
|
||||
// The seek'd position is in reference to the decompressed sound
|
||||
|
||||
if (_engine->isEngineDisabled())
|
||||
return 0;
|
||||
|
||||
char fileName[60] = "";
|
||||
getFilenameFromSoundId(soundId, fileName, sizeof(fileName));
|
||||
|
||||
ImuseDigiSndMgr::SoundDesc *s = _sound->findSoundById(soundId);
|
||||
if (s || (_engine->isFTSoundEngine() && soundId == kTalkSoundID)) {
|
||||
if (soundId != 0) {
|
||||
if (_engine->isFTSoundEngine()) {
|
||||
switch (mode) {
|
||||
case SEEK_END:
|
||||
if (soundId != kTalkSoundID) {
|
||||
return s->resSize;
|
||||
} else {
|
||||
return _ftSpeechFileSize;
|
||||
}
|
||||
case SEEK_SET:
|
||||
default:
|
||||
if (soundId != kTalkSoundID) {
|
||||
if (offset <= s->resSize) {
|
||||
s->resCurOffset = offset;
|
||||
return offset;
|
||||
}
|
||||
} else {
|
||||
if (offset <= _ftSpeechFileSize) {
|
||||
_ftSpeechFileCurPos = offset;
|
||||
return _ftSpeechFileCurPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// A soundId > 10000 is a SAN cutscene
|
||||
if ((_vm->_game.id == GID_DIG && !(_vm->_game.features & GF_DEMO)) && (soundId > kTalkSoundID))
|
||||
return 0;
|
||||
|
||||
return s->bundle->seekFile(offset, mode);
|
||||
}
|
||||
} else {
|
||||
debug(5, "IMuseDigiFilesHandler::seek(): soundId is 0 or out of range");
|
||||
}
|
||||
} else {
|
||||
debug(5, "IMuseDigiFilesHandler::seek(): can't find sound %d (%s); did you forget to open it?", soundId, fileName);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigiFilesHandler::read(int soundId, uint8 *buf, int32 size, int bufId) {
|
||||
// This function and files_seek() are used for sounds for which a stream is needed (speech
|
||||
// and music), therefore they will always refer to sounds in a bundle file for DIG and COMI
|
||||
|
||||
if (_engine->isEngineDisabled())
|
||||
return 0;
|
||||
|
||||
if (soundId != 0) {
|
||||
uint8 *tmpBuf = nullptr;
|
||||
int32 resultingSize;
|
||||
|
||||
// We don't have SoundDesc objects for FT & DIG demo speech files
|
||||
if (_engine->isFTSoundEngine() && soundId == kTalkSoundID) {
|
||||
_ftSpeechFile->seek(_ftSpeechSubFileOffset + _ftSpeechFileCurPos, SEEK_SET);
|
||||
resultingSize = size > _ftSpeechFileSize ? (_ftSpeechFileSize - _ftSpeechFileCurPos) : size;
|
||||
return _ftSpeechFile->read(buf, resultingSize);
|
||||
}
|
||||
|
||||
char fileName[60] = "";
|
||||
getFilenameFromSoundId(soundId, fileName, sizeof(fileName));
|
||||
|
||||
ImuseDigiSndMgr::SoundDesc *s = _sound->getSounds();
|
||||
ImuseDigiSndMgr::SoundDesc *curSnd = nullptr;
|
||||
for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
|
||||
curSnd = &s[i];
|
||||
if (curSnd->inUse) {
|
||||
if (curSnd->soundId == soundId) {
|
||||
if (_engine->isFTSoundEngine()) { // FT & DIG demo
|
||||
resultingSize = size > (curSnd->resSize - curSnd->resCurOffset) ? (curSnd->resSize - curSnd->resCurOffset) : size;
|
||||
tmpBuf = &curSnd->resPtr[curSnd->resCurOffset];
|
||||
|
||||
if (resultingSize != size)
|
||||
debug(5, "IMuseDigiFilesHandler::read(): WARNING: tried to read %d bytes, got %d instead (soundId %d (%s))", size, resultingSize, soundId, fileName);
|
||||
|
||||
memcpy(buf, tmpBuf, resultingSize); // We don't free tmpBuf: it's the resource pointer
|
||||
return resultingSize;
|
||||
} else { // DIG & COMI
|
||||
resultingSize = curSnd->bundle->readFile(fileName, size, &tmpBuf, ((_vm->_game.id == GID_CMI) && !(_vm->_game.features & GF_DEMO)));
|
||||
|
||||
if (resultingSize != size)
|
||||
debug(5, "IMuseDigiFilesHandler::read(): WARNING: tried to read %d bytes, got %d instead (soundId %d (%s))", size, resultingSize, soundId, fileName);
|
||||
|
||||
memcpy(buf, tmpBuf, resultingSize);
|
||||
free(tmpBuf);
|
||||
return resultingSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug(5, "IMuseDigiFilesHandler::read(): can't find sound %d (%s); did you forget to open it?", soundId, fileName);
|
||||
|
||||
} else {
|
||||
debug(5, "IMuseDigiFilesHandler::read(): soundId is 0 or out of range");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IMuseDigiSndBuffer *IMuseDigiFilesHandler::getBufInfo(int bufId) {
|
||||
if (bufId > 0 && bufId <= 4) {
|
||||
return &_soundBuffers[bufId];
|
||||
}
|
||||
|
||||
debug(5, "IMuseDigiFilesHandler::getBufInfo(): ERROR: invalid buffer id");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int IMuseDigiFilesHandler::openSound(int soundId) {
|
||||
if (_engine->isEngineDisabled())
|
||||
return 1;
|
||||
|
||||
ImuseDigiSndMgr::SoundDesc *s = nullptr;
|
||||
if (!_engine->isFTSoundEngine()) {
|
||||
char fileName[60] = "";
|
||||
getFilenameFromSoundId(soundId, fileName, sizeof(fileName));
|
||||
|
||||
int groupId = soundId == kTalkSoundID ? IMUSE_VOLGRP_VOICE : IMUSE_VOLGRP_MUSIC;
|
||||
s = _sound->findSoundById(soundId);
|
||||
if (!s)
|
||||
s = _sound->openSound(soundId, fileName, IMUSE_BUNDLE, groupId, -1);
|
||||
if (!s)
|
||||
s = _sound->openSound(soundId, fileName, IMUSE_BUNDLE, groupId, 1);
|
||||
if (!s)
|
||||
s = _sound->openSound(soundId, fileName, IMUSE_BUNDLE, groupId, 2);
|
||||
if (!s) {
|
||||
debug(5, "IMuseDigiFilesHandler::openSound(): can't open sound %d (%s)", soundId, fileName);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
s = _sound->findSoundById(soundId);
|
||||
if (!s)
|
||||
s = _sound->openSound(soundId, "", IMUSE_RESOURCE, -1, -1);
|
||||
if (!s)
|
||||
s = _sound->openSound(soundId, "", IMUSE_RESOURCE, -1, 1);
|
||||
if (!s)
|
||||
s = _sound->openSound(soundId, "", IMUSE_RESOURCE, -1, 2);
|
||||
if (!s) {
|
||||
debug(5, "IMuseDigiFilesHandler::openSound(): can't open sound %d", soundId);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigiFilesHandler::closeSound(int soundId) {
|
||||
if (_engine->isEngineDisabled())
|
||||
return;
|
||||
|
||||
_sound->scheduleSoundForDeallocation(soundId);
|
||||
}
|
||||
|
||||
void IMuseDigiFilesHandler::closeAllSounds() {
|
||||
ImuseDigiSndMgr::SoundDesc *s = _sound->getSounds();
|
||||
for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
|
||||
if (s[i].inUse) {
|
||||
closeSound((&s[i])->soundId);
|
||||
}
|
||||
}
|
||||
|
||||
_engine->flushTracks();
|
||||
}
|
||||
|
||||
void IMuseDigiFilesHandler::getFilenameFromSoundId(int soundId, char *fileName, size_t size) {
|
||||
if (_engine->isFTSoundEngine())
|
||||
return;
|
||||
|
||||
int i = 0;
|
||||
|
||||
if (soundId == kTalkSoundID) {
|
||||
Common::strlcpy(fileName, _currentSpeechFilename, size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_vm->_game.id == GID_CMI) {
|
||||
if (_vm->_game.features & GF_DEMO) {
|
||||
while (_comiDemoStateMusicTable[i].soundId != -1) {
|
||||
if (_comiDemoStateMusicTable[i].soundId == soundId) {
|
||||
Common::strlcpy(fileName, _comiDemoStateMusicTable[i].filename, size);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
if (soundId < 2000) {
|
||||
while (_comiStateMusicTable[i].soundId != -1) {
|
||||
if (_comiStateMusicTable[i].soundId == soundId) {
|
||||
Common::strlcpy(fileName, _comiStateMusicTable[i].filename, size);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
while (_comiSeqMusicTable[i].soundId != -1) {
|
||||
if (_comiSeqMusicTable[i].soundId == soundId) {
|
||||
Common::strlcpy(fileName, _comiSeqMusicTable[i].filename, size);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (_vm->_game.id == GID_DIG) {
|
||||
if (soundId < 2000) {
|
||||
while (_digStateMusicTable[i].soundId != -1) {
|
||||
if (_digStateMusicTable[i].soundId == soundId) {
|
||||
Common::strlcpy(fileName, _digStateMusicTable[i].filename, size);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
while (_digSeqMusicTable[i].soundId != -1) {
|
||||
if (_digSeqMusicTable[i].soundId == soundId) {
|
||||
Common::strlcpy(fileName, _digSeqMusicTable[i].filename, size);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigiFilesHandler::allocSoundBuffer(int bufId, int32 size, int32 loadSize, int32 criticalSize) {
|
||||
IMuseDigiSndBuffer *selectedSoundBuf;
|
||||
|
||||
selectedSoundBuf = &_soundBuffers[bufId];
|
||||
selectedSoundBuf->buffer = (uint8 *)malloc(size);
|
||||
selectedSoundBuf->bufSize = size;
|
||||
selectedSoundBuf->loadSize = loadSize;
|
||||
selectedSoundBuf->criticalSize = criticalSize;
|
||||
}
|
||||
|
||||
void IMuseDigiFilesHandler::deallocSoundBuffer(int bufId) {
|
||||
IMuseDigiSndBuffer *selectedSoundBuf;
|
||||
|
||||
selectedSoundBuf = &_soundBuffers[bufId];
|
||||
free(selectedSoundBuf->buffer);
|
||||
selectedSoundBuf->buffer = nullptr;
|
||||
}
|
||||
|
||||
void IMuseDigiFilesHandler::flushSounds() {
|
||||
if (_engine->isEngineDisabled())
|
||||
return;
|
||||
|
||||
ImuseDigiSndMgr::SoundDesc *s = _sound->getSounds();
|
||||
for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
|
||||
ImuseDigiSndMgr::SoundDesc *curSnd = &s[i];
|
||||
if (curSnd && curSnd->inUse) {
|
||||
if (curSnd->scheduledForDealloc)
|
||||
if (!_engine->diMUSEGetParam(curSnd->soundId, DIMUSE_P_SND_TRACK_NUM) && !_engine->diMUSEGetParam(curSnd->soundId, DIMUSE_P_TRIGS_SNDS))
|
||||
_sound->closeSound(curSnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int IMuseDigiFilesHandler::setCurrentSpeechFilename(const char *fileName) {
|
||||
Common::strlcpy(_currentSpeechFilename, fileName, sizeof(_currentSpeechFilename));
|
||||
if (openSound(kTalkSoundID))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigiFilesHandler::setCurrentFtSpeechFile(const char *fileName, ScummFile *file, uint32 offset, uint32 size) {
|
||||
Common::strlcpy(_ftSpeechFilename, fileName, sizeof(_ftSpeechFilename));
|
||||
delete _ftSpeechFile;
|
||||
_ftSpeechFile = file;
|
||||
_ftSpeechSubFileOffset = offset;
|
||||
_ftSpeechFileSize = size;
|
||||
}
|
||||
|
||||
void IMuseDigiFilesHandler::closeSoundImmediatelyById(int soundId) {
|
||||
if (_engine->isEngineDisabled())
|
||||
return;
|
||||
|
||||
_sound->closeSoundById(soundId);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
74
engines/scumm/imuse_digi/dimuse_files.h
Normal file
74
engines/scumm/imuse_digi/dimuse_files.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(SCUMM_IMUSE_DIGI_FILES_H) && defined(ENABLE_SCUMM_7_8)
|
||||
#define SCUMM_IMUSE_DIGI_FILES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "scumm/resource.h"
|
||||
#include "scumm/file.h"
|
||||
#include "scumm/imuse_digi/dimuse_bndmgr.h"
|
||||
#include "scumm/imuse_digi/dimuse_sndmgr.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class IMuseDigiFilesHandler {
|
||||
|
||||
private:
|
||||
IMuseDigital *_engine;
|
||||
ImuseDigiSndMgr *_sound;
|
||||
ScummEngine_v7 *_vm;
|
||||
Common::Mutex _mutex;
|
||||
IMuseDigiSndBuffer _soundBuffers[4];
|
||||
char _currentSpeechFilename[60] = {};
|
||||
ScummFile *_ftSpeechFile;
|
||||
char _ftSpeechFilename[160] = {};
|
||||
int _ftSpeechSubFileOffset;
|
||||
int _ftSpeechFileSize;
|
||||
int _ftSpeechFileCurPos;
|
||||
|
||||
void getFilenameFromSoundId(int soundId, char *fileName, size_t size);
|
||||
public:
|
||||
IMuseDigiFilesHandler(IMuseDigital *engine, ScummEngine_v7 *vm);
|
||||
~IMuseDigiFilesHandler();
|
||||
|
||||
uint8 *getSoundAddrData(int soundId);
|
||||
int getSoundAddrDataSize(int soundId, bool hasStream);
|
||||
int getNextSound(int soundId);
|
||||
int seek(int soundId, int32 offset, int mode, int bufId);
|
||||
int read(int soundId, uint8 *buf, int32 size, int bufId);
|
||||
IMuseDigiSndBuffer *getBufInfo(int bufId);
|
||||
int openSound(int soundId);
|
||||
void closeSound(int soundId);
|
||||
void closeAllSounds();
|
||||
void allocSoundBuffer(int bufId, int32 size, int32 loadSize, int32 criticalSize);
|
||||
void deallocSoundBuffer(int bufId);
|
||||
void flushSounds();
|
||||
int setCurrentSpeechFilename(const char *fileName);
|
||||
void setCurrentFtSpeechFile(const char *fileName, ScummFile *file, uint32 offset, uint32 size);
|
||||
void closeSoundImmediatelyById(int soundId);
|
||||
void saveLoad(Common::Serializer &ser);
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
#endif
|
||||
84
engines/scumm/imuse_digi/dimuse_groups.cpp
Normal file
84
engines/scumm/imuse_digi/dimuse_groups.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
#include "scumm/imuse_digi/dimuse_groups.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
IMuseDigiGroupsHandler::IMuseDigiGroupsHandler(IMuseDigital *engine, Common::Mutex *mutex) {
|
||||
_engine = engine;
|
||||
_mutex = mutex;
|
||||
}
|
||||
|
||||
IMuseDigiGroupsHandler::~IMuseDigiGroupsHandler() {}
|
||||
|
||||
int IMuseDigiGroupsHandler::init() {
|
||||
for (int i = 0; i < DIMUSE_MAX_GROUPS; i++) {
|
||||
_effVols[i] = 127;
|
||||
_vols[i] = 127;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigiGroupsHandler::setGroupVol(int id, int volume) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
int l;
|
||||
|
||||
if (id >= DIMUSE_MAX_GROUPS) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (volume == -1) {
|
||||
return _vols[id];
|
||||
}
|
||||
|
||||
if (volume > 127)
|
||||
return -5;
|
||||
|
||||
if (id) {
|
||||
_vols[id] = volume;
|
||||
_effVols[id] = (_vols[0] * (volume + 1)) / 128;
|
||||
} else {
|
||||
_effVols[0] = volume;
|
||||
_vols[0] = volume;
|
||||
|
||||
for (l = 1; l < DIMUSE_MAX_GROUPS; l++) {
|
||||
_effVols[l] = (volume * (_vols[l] + 1)) / 128;
|
||||
}
|
||||
}
|
||||
|
||||
_engine->diMUSEUpdateGroupVolumes();
|
||||
return _vols[id];
|
||||
}
|
||||
|
||||
int IMuseDigiGroupsHandler::getGroupVol(int id) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
if (id >= DIMUSE_MAX_GROUPS) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
return _effVols[id];
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
50
engines/scumm/imuse_digi/dimuse_groups.h
Normal file
50
engines/scumm/imuse_digi/dimuse_groups.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(SCUMM_IMUSE_DIGI_GROUPS_H) && defined(ENABLE_SCUMM_7_8)
|
||||
#define SCUMM_IMUSE_DIGI_GROUPS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "scumm/imuse_digi/dimuse_engine.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class IMuseDigiGroupsHandler {
|
||||
|
||||
private:
|
||||
IMuseDigital *_engine;
|
||||
Common::Mutex *_mutex;
|
||||
int _effVols[DIMUSE_MAX_GROUPS] = {};
|
||||
int _vols[DIMUSE_MAX_GROUPS] = {};
|
||||
public:
|
||||
IMuseDigiGroupsHandler(IMuseDigital *engine, Common::Mutex *mutex);
|
||||
~IMuseDigiGroupsHandler();
|
||||
int init();
|
||||
int setGroupVol(int id, int volume);
|
||||
int getGroupVol(int id);
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
#endif
|
||||
1391
engines/scumm/imuse_digi/dimuse_internalmixer.cpp
Normal file
1391
engines/scumm/imuse_digi/dimuse_internalmixer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
105
engines/scumm/imuse_digi/dimuse_internalmixer.h
Normal file
105
engines/scumm/imuse_digi/dimuse_internalmixer.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(SCUMM_IMUSE_DIGI_MIXER_H) && defined(ENABLE_SCUMM_7_8)
|
||||
#define SCUMM_IMUSE_DIGI_MIXER_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "scumm/imuse_digi/dimuse_engine.h"
|
||||
#include "scumm/music.h"
|
||||
#include "scumm/sound.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Audio {
|
||||
class AudioStream;
|
||||
class Mixer;
|
||||
class QueuingAudioStream;
|
||||
}
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class IMuseDigiInternalMixer {
|
||||
|
||||
private:
|
||||
int32 *_amp8Table = nullptr;
|
||||
int32 *_amp12Table = nullptr;
|
||||
int32 *_softLMID = nullptr;
|
||||
int32 *_softLTable = nullptr;
|
||||
|
||||
uint8 *_mixBuf = nullptr;
|
||||
|
||||
Audio::Mixer *_mixer;
|
||||
Audio::SoundHandle _channelHandle;
|
||||
int _mixBufSize = 0;
|
||||
int _radioChatter = 0;
|
||||
int _outWordSize = 0;
|
||||
int _outChannelCount = 0;
|
||||
int _sampleRate = 0;
|
||||
int _stereoReverseFlag = 0;
|
||||
bool _isEarlyDiMUSE = false;
|
||||
bool _lowLatencyMode = false;
|
||||
|
||||
void mixBits8Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable, bool ftIs11025Hz);
|
||||
void mixBits12Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
|
||||
void mixBits16Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
|
||||
|
||||
void mixBits8ConvertToMono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
|
||||
void mixBits12ConvertToMono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
|
||||
void mixBits16ConvertToMono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
|
||||
|
||||
void mixBits8ConvertToStereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *leftAmpTable, int32 *rightAmpTable, bool ftIs11025Hz);
|
||||
void mixBits12ConvertToStereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *leftAmpTable, int32 *rightAmpTable);
|
||||
void mixBits16ConvertToStereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *leftAmpTable, int32 *rightAmpTable);
|
||||
|
||||
void mixBits8Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
|
||||
void mixBits12Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
|
||||
void mixBits16Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
|
||||
|
||||
public:
|
||||
IMuseDigiInternalMixer(Audio::Mixer *mixer, int sampleRate, bool isEarlyDiMUSE, bool lowLatencyMode = false);
|
||||
~IMuseDigiInternalMixer();
|
||||
int init(int bytesPerSample, int numChannels, uint8 *mixBuf, int mixBufSize, int sizeSampleKB, int mixChannelsNum);
|
||||
void setRadioChatter();
|
||||
void clearRadioChatter();
|
||||
int clearMixerBuffer();
|
||||
|
||||
void mix(uint8 *srcBuf, int32 inFrameCount, int wordSize, int channelCount, int feedSize, int32 mixBufStartIndex, int volume, int pan, bool ftIs11025Hz);
|
||||
int loop(uint8 **destBuffer, int len);
|
||||
Audio::QueuingAudioStream *_stream;
|
||||
|
||||
// For low latency audio
|
||||
void setCurrentMixerBuffer(uint8 *newBuf);
|
||||
void endStream(int idx);
|
||||
Audio::QueuingAudioStream *getStream(int idx);
|
||||
|
||||
Audio::QueuingAudioStream *_separateStreams[DIMUSE_MAX_TRACKS];
|
||||
Audio::SoundHandle _separateChannelHandles[DIMUSE_MAX_TRACKS];
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
||||
#endif
|
||||
931
engines/scumm/imuse_digi/dimuse_scripts.cpp
Normal file
931
engines/scumm/imuse_digi/dimuse_scripts.cpp
Normal file
@@ -0,0 +1,931 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
#define DIG_STATE_OFFSET 11
|
||||
#define DIG_SEQ_OFFSET (DIG_STATE_OFFSET + 65)
|
||||
#define COMI_STATE_OFFSET 3
|
||||
|
||||
int IMuseDigital::scriptParse(int cmd, int a, int b) {
|
||||
if (_scriptInitializedFlag || !cmd) {
|
||||
switch (cmd) {
|
||||
case DIMUSE_C_SCRIPT_INIT:
|
||||
if (_scriptInitializedFlag) {
|
||||
debug(5, "IMuseDigital::scriptParse(): script module already initialized");
|
||||
return -1;
|
||||
} else {
|
||||
_scriptInitializedFlag = 1;
|
||||
return scriptInit();
|
||||
}
|
||||
case DIMUSE_C_SCRIPT_TERMINATE:
|
||||
_scriptInitializedFlag = 0;
|
||||
return scriptTerminate();
|
||||
case DIMUSE_C_SCRIPT_SAVE:
|
||||
case DIMUSE_C_SCRIPT_RESTORE:
|
||||
break;
|
||||
case DIMUSE_C_SCRIPT_REFRESH:
|
||||
scriptRefresh();
|
||||
return 0;
|
||||
case DIMUSE_C_SCRIPT_SET_STATE:
|
||||
scriptSetState(a);
|
||||
return 0;
|
||||
case DIMUSE_C_SCRIPT_SET_SEQUENCE:
|
||||
scriptSetSequence(a);
|
||||
return 0;
|
||||
case DIMUSE_C_SCRIPT_CUE_POINT:
|
||||
scriptSetCuePoint(a);
|
||||
return 0;
|
||||
case DIMUSE_C_SCRIPT_SET_ATTRIBUTE:
|
||||
return scriptSetAttribute(a, b);
|
||||
default:
|
||||
debug(5, "IMuseDigital::scriptParse(): unrecognized opcode (%d)", cmd);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
debug(5, "IMuseDigital::scriptParse(): script module not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int IMuseDigital::scriptInit() {
|
||||
_curMusicState = 0;
|
||||
_curMusicSeq = 0;
|
||||
_nextSeqToPlay = 0;
|
||||
_curMusicCue = 0;
|
||||
memset(_attributes, 0, sizeof(_attributes));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::scriptTerminate() {
|
||||
diMUSETerminate();
|
||||
|
||||
_curMusicState = 0;
|
||||
_curMusicSeq = 0;
|
||||
_nextSeqToPlay = 0;
|
||||
_curMusicCue = 0;
|
||||
memset(_attributes, 0, sizeof(_attributes));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigital::scriptRefresh() {
|
||||
int soundId;
|
||||
int nextSound;
|
||||
|
||||
if (_stopSequenceFlag) {
|
||||
scriptSetSequence(0);
|
||||
_stopSequenceFlag = 0;
|
||||
}
|
||||
|
||||
soundId = 0;
|
||||
|
||||
while (1) {
|
||||
nextSound = diMUSEGetNextSound(soundId);
|
||||
soundId = nextSound;
|
||||
|
||||
if (!nextSound)
|
||||
break;
|
||||
|
||||
if (diMUSEGetParam(nextSound, DIMUSE_P_SND_HAS_STREAM) && diMUSEGetParam(soundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC) {
|
||||
if (soundId)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_curMusicSeq)
|
||||
scriptSetSequence(0);
|
||||
|
||||
flushTracks();
|
||||
}
|
||||
|
||||
void IMuseDigital::scriptSetState(int soundId) {
|
||||
if (_vm->_game.id == GID_DIG && !_isEarlyDiMUSE) {
|
||||
setDigMusicState(soundId);
|
||||
} else if (_vm->_game.id == GID_CMI) {
|
||||
setComiMusicState(soundId);
|
||||
} else {
|
||||
setFtMusicState(soundId);
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::scriptSetSequence(int soundId) {
|
||||
if (_vm->_game.id == GID_DIG && !_isEarlyDiMUSE) {
|
||||
setDigMusicSequence(soundId);
|
||||
} else if (_vm->_game.id == GID_CMI) {
|
||||
setComiMusicSequence(soundId);
|
||||
} else {
|
||||
setFtMusicSequence(soundId);
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::scriptSetCuePoint(int cueId) {
|
||||
if (!_isEarlyDiMUSE)
|
||||
return;
|
||||
|
||||
if (cueId > 3)
|
||||
return;
|
||||
|
||||
debug(5, "IMuseDigital::scriptSetCuePoint(): Cue point sequence: %d", cueId);
|
||||
|
||||
if (_curMusicSeq && _curMusicCue != cueId) {
|
||||
if (cueId == 0)
|
||||
playFtMusic(nullptr, 0, 0);
|
||||
else {
|
||||
int seq = ((_curMusicSeq - 1) * 4) + cueId;
|
||||
playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].transitionType, _ftSeqMusicTable[seq].volume);
|
||||
}
|
||||
}
|
||||
|
||||
_curMusicCue = cueId;
|
||||
}
|
||||
|
||||
int IMuseDigital::scriptSetAttribute(int attrIndex, int attrVal) {
|
||||
// FT appears to set a single attribute to 1 at start-up and
|
||||
// never use it again, so we currently ignore that behavior
|
||||
if (_vm->_game.id == GID_DIG) {
|
||||
_attributes[attrIndex] = attrVal;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::scriptTriggerCallback(char *marker) {
|
||||
if (marker[0] != '_') {
|
||||
debug(5, "IMuseDigital::scriptTriggerCallback(): got marker != '_end', callback ignored");
|
||||
return -1;
|
||||
}
|
||||
_stopSequenceFlag = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Scumm::IMuseDigital::setFtMusicState(int stateId) {
|
||||
if (stateId > 48)
|
||||
return;
|
||||
|
||||
debug(5, "IMuseDigital::setFtMusicState(): State music: %s, %s", _ftStateMusicTable[stateId].name, _ftStateMusicTable[stateId].audioName);
|
||||
|
||||
if (_curMusicState == stateId)
|
||||
return;
|
||||
|
||||
if (_curMusicSeq == 0) {
|
||||
if (stateId == 0) {
|
||||
playFtMusic(nullptr, 0, 0);
|
||||
} else {
|
||||
playFtMusic(_ftStateMusicTable[stateId].audioName, _ftStateMusicTable[stateId].transitionType, _ftStateMusicTable[stateId].volume);
|
||||
}
|
||||
}
|
||||
|
||||
_curMusicState = stateId;
|
||||
}
|
||||
|
||||
void IMuseDigital::setFtMusicSequence(int seqId) {
|
||||
if (seqId > 52)
|
||||
return;
|
||||
|
||||
debug(5, "IMuseDigital::setFtMusicSequence(): Sequence music: %s", _ftSeqNames[seqId].name);
|
||||
|
||||
if (_curMusicSeq != seqId) {
|
||||
if (seqId == 0) {
|
||||
if (_curMusicState == 0) {
|
||||
playFtMusic(nullptr, 0, 0);
|
||||
} else {
|
||||
playFtMusic(_ftStateMusicTable[_curMusicState].audioName, _ftStateMusicTable[_curMusicState].transitionType, _ftStateMusicTable[_curMusicState].volume);
|
||||
}
|
||||
} else {
|
||||
int seq = (seqId - 1) * 4;
|
||||
playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].transitionType, _ftSeqMusicTable[seq].volume);
|
||||
}
|
||||
}
|
||||
|
||||
_curMusicSeq = seqId;
|
||||
_curMusicCue = 0;
|
||||
}
|
||||
|
||||
|
||||
void IMuseDigital::setDigMusicState(int stateId) {
|
||||
int l, num = -1;
|
||||
|
||||
for (l = 0; _digStateMusicTable[l].soundId != -1; l++) {
|
||||
if ((_digStateMusicTable[l].soundId == stateId)) {
|
||||
debug(5, "IMuseDigital::setDigMusicState(): Set music state: %s, %s", _digStateMusicTable[l].name, _digStateMusicTable[l].filename);
|
||||
num = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (num == -1) {
|
||||
for (l = 0; _digStateMusicMap[l].roomId != -1; l++) {
|
||||
if ((_digStateMusicMap[l].roomId == stateId)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
num = l;
|
||||
|
||||
int offset = _attributes[_digStateMusicMap[num].offset];
|
||||
if (offset == 0) {
|
||||
if (_attributes[_digStateMusicMap[num].attribPos] != 0) {
|
||||
num = _digStateMusicMap[num].stateIndex3;
|
||||
} else {
|
||||
num = _digStateMusicMap[num].stateIndex1;
|
||||
}
|
||||
} else {
|
||||
int stateIndex2 = _digStateMusicMap[num].stateIndex2;
|
||||
if (stateIndex2 == 0) {
|
||||
num = _digStateMusicMap[num].stateIndex1 + offset;
|
||||
} else {
|
||||
num = stateIndex2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug(5, "IMuseDigital::setDigMusicState(): Set music state: %s, %s", _digStateMusicTable[num].name, _digStateMusicTable[num].filename);
|
||||
|
||||
if (_curMusicState == num)
|
||||
return;
|
||||
|
||||
if (_curMusicSeq == 0) {
|
||||
if (num == 0)
|
||||
playDigMusic(nullptr, &_digStateMusicTable[0], num, false);
|
||||
else
|
||||
playDigMusic(_digStateMusicTable[num].name, &_digStateMusicTable[num], num, false);
|
||||
}
|
||||
|
||||
_curMusicState = num;
|
||||
}
|
||||
|
||||
void IMuseDigital::setDigMusicSequence(int seqId) {
|
||||
int l, num = -1;
|
||||
|
||||
if (seqId == 0)
|
||||
seqId = 2000;
|
||||
|
||||
for (l = 0; _digSeqMusicTable[l].soundId != -1; l++) {
|
||||
if ((_digSeqMusicTable[l].soundId == seqId)) {
|
||||
debug(5, "IMuseDigital::setDigMusicSequence(): Set music sequence: %s, %s", _digSeqMusicTable[l].name, _digSeqMusicTable[l].filename);
|
||||
num = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (num == -1)
|
||||
return;
|
||||
|
||||
if (_curMusicSeq == num)
|
||||
return;
|
||||
|
||||
if (num != 0) {
|
||||
if (_curMusicSeq && ((_digSeqMusicTable[_curMusicSeq].transitionType == 4)
|
||||
|| (_digSeqMusicTable[_curMusicSeq].transitionType == 6))) {
|
||||
_nextSeqToPlay = num;
|
||||
return;
|
||||
} else {
|
||||
playDigMusic(_digSeqMusicTable[num].name, &_digSeqMusicTable[num], 0, true);
|
||||
_nextSeqToPlay = 0;
|
||||
_attributes[DIG_SEQ_OFFSET + num] = 1;
|
||||
}
|
||||
} else {
|
||||
if (_nextSeqToPlay != 0) {
|
||||
playDigMusic(_digSeqMusicTable[_nextSeqToPlay].name, &_digSeqMusicTable[_nextSeqToPlay], 0, true);
|
||||
_attributes[DIG_SEQ_OFFSET + _nextSeqToPlay] = 1;
|
||||
num = _nextSeqToPlay;
|
||||
_nextSeqToPlay = 0;
|
||||
} else {
|
||||
if (_curMusicState != 0) {
|
||||
playDigMusic(_digStateMusicTable[_curMusicState].name, &_digStateMusicTable[_curMusicState], _curMusicState, true);
|
||||
} else {
|
||||
playDigMusic(nullptr, &_digStateMusicTable[0], _curMusicState, true);
|
||||
}
|
||||
|
||||
num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
_curMusicSeq = num;
|
||||
}
|
||||
|
||||
void IMuseDigital::setComiMusicState(int stateId) {
|
||||
int l, num = -1;
|
||||
|
||||
if (stateId == 0)
|
||||
stateId = 1000;
|
||||
|
||||
if ((_vm->_game.features & GF_DEMO) && stateId == 1000)
|
||||
stateId = 0;
|
||||
|
||||
if (!(_vm->_game.features & GF_DEMO)) {
|
||||
for (l = 0; _comiStateMusicTable[l].soundId != -1; l++) {
|
||||
if ((_comiStateMusicTable[l].soundId == stateId)) {
|
||||
debug(5, "IMuseDigital::setComiMusicState(): Set music state: %s, %s", _comiStateMusicTable[l].name, _comiStateMusicTable[l].filename);
|
||||
num = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (num == -1 && !(_vm->_game.features & GF_DEMO))
|
||||
return;
|
||||
|
||||
if (!(_vm->_game.features & GF_DEMO) && _curMusicState == num) {
|
||||
return;
|
||||
} else if ((_vm->_game.features & GF_DEMO) && _curMusicState == stateId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_curMusicSeq == 0) {
|
||||
if (_vm->_game.features & GF_DEMO) {
|
||||
if (_curMusicSeq == 0) {
|
||||
if (stateId == 0)
|
||||
playComiDemoMusic(nullptr, &_comiDemoStateMusicTable[0], stateId, false);
|
||||
else
|
||||
playComiDemoMusic(_comiDemoStateMusicTable[stateId].name, &_comiDemoStateMusicTable[stateId], stateId, false);
|
||||
}
|
||||
} else {
|
||||
if (num == 0)
|
||||
playComiMusic(nullptr, &_comiStateMusicTable[0], num, false);
|
||||
else
|
||||
playComiMusic(_comiStateMusicTable[num].name, &_comiStateMusicTable[num], num, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(_vm->_game.features & GF_DEMO)) {
|
||||
_curMusicState = num;
|
||||
} else {
|
||||
_curMusicState = stateId;
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::setComiMusicSequence(int seqId) {
|
||||
int l, num = -1;
|
||||
|
||||
if (seqId == 0)
|
||||
seqId = 2000;
|
||||
|
||||
for (l = 0; _comiSeqMusicTable[l].soundId != -1; l++) {
|
||||
if ((_comiSeqMusicTable[l].soundId == seqId)) {
|
||||
debug(5, "IMuseDigital::setComiMusicSequence(): Set music sequence: %s, %s", _comiSeqMusicTable[l].name, _comiSeqMusicTable[l].filename);
|
||||
num = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (num == -1)
|
||||
return;
|
||||
|
||||
if (_curMusicSeq == num)
|
||||
return;
|
||||
|
||||
if (num != 0) {
|
||||
if (_curMusicSeq && ((_comiSeqMusicTable[_curMusicSeq].transitionType == 4)
|
||||
|| (_comiSeqMusicTable[_curMusicSeq].transitionType == 6))) {
|
||||
_nextSeqToPlay = num;
|
||||
return;
|
||||
} else {
|
||||
playComiMusic(_comiSeqMusicTable[num].name, &_comiSeqMusicTable[num], 0, true);
|
||||
_nextSeqToPlay = 0;
|
||||
}
|
||||
} else {
|
||||
if (_nextSeqToPlay != 0) {
|
||||
playComiMusic(_comiSeqMusicTable[_nextSeqToPlay].name, &_comiSeqMusicTable[_nextSeqToPlay], 0, true);
|
||||
num = _nextSeqToPlay;
|
||||
_nextSeqToPlay = 0;
|
||||
} else {
|
||||
if (_curMusicState != 0) {
|
||||
playComiMusic(_comiStateMusicTable[_curMusicState].name, &_comiStateMusicTable[_curMusicState], _curMusicState, true);
|
||||
} else {
|
||||
playComiMusic(nullptr, &_comiStateMusicTable[0], _curMusicState, true);
|
||||
}
|
||||
num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
_curMusicSeq = num;
|
||||
}
|
||||
|
||||
void IMuseDigital::playFtMusic(const char *songName, int transitionType, int volume) {
|
||||
int oldSoundId = 0;
|
||||
int soundId;
|
||||
|
||||
if (!_spooledMusicEnabled)
|
||||
return;
|
||||
|
||||
// Check for any music piece which is played as a SFX (without an associated stream)
|
||||
// and fade it out
|
||||
for (int i = diMUSEGetNextSound(0); i; i = diMUSEGetNextSound(i)) {
|
||||
if (diMUSEGetParam(i, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && !diMUSEGetParam(i, DIMUSE_P_SND_HAS_STREAM))
|
||||
diMUSEFadeParam(i, DIMUSE_P_VOLUME, 0, 200);
|
||||
}
|
||||
|
||||
// Now grab the current standard music soundId: it will either be crossfaded by SwitchStream,
|
||||
// or faded out
|
||||
for (int j = diMUSEGetNextSound(0); j; j = diMUSEGetNextSound(j)) {
|
||||
if (diMUSEGetParam(j, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && diMUSEGetParam(j, DIMUSE_P_SND_HAS_STREAM))
|
||||
oldSoundId = j;
|
||||
}
|
||||
|
||||
if (songName) {
|
||||
switch (transitionType) {
|
||||
case 0:
|
||||
debug(5, "IMuseDigital::playFtMusic(): NULL transition, ignored");
|
||||
return;
|
||||
case 1:
|
||||
soundId = getSoundIdByName(songName);
|
||||
|
||||
if (_filesHandler->openSound(soundId))
|
||||
return;
|
||||
|
||||
if (!soundId) {
|
||||
debug(5, "IMuseDigital::playFtMusic(): failed to retrieve soundId for sound \"%s\"", songName);
|
||||
break;
|
||||
}
|
||||
|
||||
if (diMUSEStartSound(soundId, 126))
|
||||
debug(5, "IMuseDigital::playFtMusic(): transition 1, failed to start sound \"%s\"(%d)", songName, soundId);
|
||||
|
||||
_filesHandler->closeSound(soundId);
|
||||
diMUSESetParam(soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetParam(soundId, DIMUSE_P_VOLUME, volume);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
soundId = getSoundIdByName(songName);
|
||||
if (soundId)
|
||||
if (_filesHandler->openSound(soundId))
|
||||
return;
|
||||
|
||||
if (soundId) {
|
||||
if (oldSoundId) {
|
||||
if (oldSoundId != soundId || transitionType == 2) {
|
||||
diMUSESwitchStream(oldSoundId, soundId, _ftCrossfadeBuffer, sizeof(_ftCrossfadeBuffer), 0);
|
||||
}
|
||||
|
||||
// WORKAROUND for bug in the original: at the beginning of the game, going in
|
||||
// and out of the bar a couple of times breaks and temporarily stops the music
|
||||
// Here, we override the fade out, just like the remastered does
|
||||
if (oldSoundId == soundId && soundId == 622 && _vm->enhancementEnabled(kEnhAudioChanges)) {
|
||||
diMUSEFadeParam(soundId, DIMUSE_P_VOLUME, volume, 200);
|
||||
}
|
||||
} else if (diMUSEStartStream(soundId, 126, DIMUSE_BUFFER_MUSIC)) {
|
||||
debug(5, "IMuseDigital::playFtMusic(): failed to start the stream for \"%s\" (%d)", songName, soundId);
|
||||
}
|
||||
|
||||
_filesHandler->closeSound(soundId);
|
||||
diMUSESetParam(soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetParam(soundId, DIMUSE_P_VOLUME, volume);
|
||||
} else {
|
||||
debug(5, "IMuseDigital::playFtMusic(): failed to retrieve soundId for sound \"%s\" (%d)", songName, soundId);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 200);
|
||||
return;
|
||||
default:
|
||||
debug(5, "IMuseDigital::playFtMusic(): bogus transition type, ignored");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 200);
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::playDigMusic(const char *songName, const imuseDigTable *table, int attribPos, bool sequence) {
|
||||
int hookId = 0;
|
||||
|
||||
if (songName != nullptr) {
|
||||
if ((_attributes[DIG_SEQ_OFFSET + 38]) && (!_attributes[DIG_SEQ_OFFSET + 41])) {
|
||||
if ((attribPos == 43) || (attribPos == 44))
|
||||
hookId = 3;
|
||||
}
|
||||
|
||||
if ((_attributes[DIG_SEQ_OFFSET + 46] != 0) && (_attributes[DIG_SEQ_OFFSET + 48] == 0)) {
|
||||
if ((attribPos == 38) || (attribPos == 39))
|
||||
hookId = 3;
|
||||
}
|
||||
|
||||
if ((_attributes[DIG_SEQ_OFFSET + 53] != 0)) {
|
||||
if ((attribPos == 50) || (attribPos == 51))
|
||||
hookId = 3;
|
||||
}
|
||||
|
||||
if ((attribPos != 0) && (hookId == 0)) {
|
||||
if (table->attribPos != 0)
|
||||
attribPos = table->attribPos;
|
||||
hookId = _attributes[DIG_STATE_OFFSET + attribPos];
|
||||
if (table->hookId != 0) {
|
||||
if ((hookId != 0) && (table->hookId > 1)) {
|
||||
_attributes[DIG_STATE_OFFSET + attribPos] = 2;
|
||||
} else {
|
||||
_attributes[DIG_STATE_OFFSET + attribPos] = hookId + 1;
|
||||
if (table->hookId < hookId + 1)
|
||||
_attributes[DIG_STATE_OFFSET + attribPos] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int nextSoundId = 0;
|
||||
while (1) {
|
||||
nextSoundId = diMUSEGetNextSound(nextSoundId);
|
||||
if (!nextSoundId)
|
||||
break;
|
||||
|
||||
// If a sound is found (and its stream is active), fade it out if it's a music track
|
||||
if (diMUSEGetParam(nextSoundId, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && !diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM))
|
||||
diMUSEFadeParam(nextSoundId, DIMUSE_P_VOLUME, 0, 120);
|
||||
}
|
||||
|
||||
int oldSoundId = 0;
|
||||
nextSoundId = 0;
|
||||
while (1) {
|
||||
nextSoundId = diMUSEGetNextSound(nextSoundId);
|
||||
if (!nextSoundId)
|
||||
break;
|
||||
|
||||
if (diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM) && (diMUSEGetParam(nextSoundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC)) {
|
||||
oldSoundId = nextSoundId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!songName) {
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 120);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (table->transitionType) {
|
||||
case 0:
|
||||
debug(5, "IMuseDigital::playDigMusic(): NULL transition, ignored");
|
||||
break;
|
||||
case 1:
|
||||
if (_filesHandler->openSound(table->soundId))
|
||||
return;
|
||||
|
||||
if (table->soundId) {
|
||||
if (diMUSEStartSound(table->soundId, 126))
|
||||
debug(5, "IMuseDigital::playDigMusic(): transition 1, failed to start the sound (%d)", table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 1);
|
||||
diMUSEFadeParam(table->soundId, DIMUSE_P_VOLUME, 127, 120);
|
||||
_filesHandler->closeSound(table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
} else {
|
||||
debug(5, "IMuseDigital::playDigMusic(): transition 1, empty soundId, ignored");
|
||||
}
|
||||
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (_filesHandler->openSound(table->soundId))
|
||||
return;
|
||||
|
||||
if (table->filename[0] == 0 || table->soundId == 0) {
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
|
||||
return;
|
||||
}
|
||||
|
||||
if (table->transitionType == 4) {
|
||||
_stopSequenceFlag = 0;
|
||||
diMUSESetTrigger(table->soundId, MKTAG('_', 'e', 'n', 'd'), DIMUSE_C_SCRIPT_CALLBACK);
|
||||
}
|
||||
|
||||
if (oldSoundId) {
|
||||
if (table->transitionType == 2) {
|
||||
diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 0);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetHook(table->soundId, hookId);
|
||||
diMUSEProcessStreams();
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldSoundId != table->soundId) {
|
||||
if ((!sequence) && (table->attribPos != 0) &&
|
||||
(table->attribPos == _digStateMusicTable[_curMusicState].attribPos)) {
|
||||
diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 1);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSEProcessStreams();
|
||||
} else {
|
||||
diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 0);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetHook(table->soundId, hookId);
|
||||
diMUSEProcessStreams();
|
||||
_filesHandler->closeSound(table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (diMUSEStartStream(table->soundId, 126, DIMUSE_BUFFER_MUSIC))
|
||||
debug(5, "IMuseDigital::playDigMusic(): failed to start the stream for sound %d", table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetHook(table->soundId, hookId);
|
||||
}
|
||||
_filesHandler->closeSound(table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
|
||||
break;
|
||||
case 5:
|
||||
debug(5, "IMuseDigital::playDigMusic(): no-op transition type (5), ignored");
|
||||
break;
|
||||
case 6:
|
||||
_stopSequenceFlag = 0;
|
||||
diMUSESetTrigger(DIMUSE_SMUSH_SOUNDID + DIMUSE_BUFFER_MUSIC, MKTAG('_', 'e', 'n', 'd'), DIMUSE_C_SCRIPT_CALLBACK);
|
||||
break;
|
||||
case 7:
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
|
||||
break;
|
||||
default:
|
||||
debug(5, "IMuseDigital::playDigMusic(): bogus transition type, ignored");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::playComiDemoMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence) {
|
||||
// This is a stripped down version of playDigMusic
|
||||
int hookId = 0;
|
||||
|
||||
if (songName != nullptr) {
|
||||
if (attribPos != 0) {
|
||||
if (table->attribPos != 0)
|
||||
attribPos = table->attribPos;
|
||||
}
|
||||
}
|
||||
|
||||
int nextSoundId = 0;
|
||||
while (1) {
|
||||
nextSoundId = diMUSEGetNextSound(nextSoundId);
|
||||
if (!nextSoundId)
|
||||
break;
|
||||
|
||||
// If a sound is found (and its stream is active), fade it out if it's a music track
|
||||
if (diMUSEGetParam(nextSoundId, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && !diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM))
|
||||
diMUSEFadeParam(nextSoundId, DIMUSE_P_VOLUME, 0, 120);
|
||||
}
|
||||
|
||||
int oldSoundId = 0;
|
||||
nextSoundId = 0;
|
||||
while (1) {
|
||||
nextSoundId = diMUSEGetNextSound(nextSoundId);
|
||||
if (!nextSoundId)
|
||||
break;
|
||||
|
||||
if (diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM) && (diMUSEGetParam(nextSoundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC)) {
|
||||
oldSoundId = nextSoundId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!songName) {
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 120);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (table->transitionType) {
|
||||
case 3:
|
||||
if (_filesHandler->openSound(table->soundId))
|
||||
return;
|
||||
|
||||
if (table->filename[0] == 0 || table->soundId == 0) {
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldSoundId) {
|
||||
if (oldSoundId != table->soundId) {
|
||||
if ((!sequence) && (table->attribPos != 0) &&
|
||||
(table->attribPos == _comiDemoStateMusicTable[_curMusicState].attribPos)) {
|
||||
diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 1);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSEProcessStreams();
|
||||
} else {
|
||||
diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 0);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetHook(table->soundId, hookId);
|
||||
diMUSEProcessStreams();
|
||||
_filesHandler->closeSound(table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (diMUSEStartStream(table->soundId, 126, DIMUSE_BUFFER_MUSIC))
|
||||
debug(5, "IMuseDigital::playComiDemoMusic(): failed to start the stream for sound %d", table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetHook(table->soundId, hookId);
|
||||
}
|
||||
_filesHandler->closeSound(table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
|
||||
break;
|
||||
default:
|
||||
debug(5, "IMuseDigital::playComiDemoMusic(): bogus or unused transition type, ignored");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::playComiMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence) {
|
||||
int hookId = 0;
|
||||
int fadeDelay = 0;
|
||||
|
||||
// This flag is not set in the original,
|
||||
// but since we're not using DirectSound and this is pretty much timing related
|
||||
// please see IMuseDigital::waveOutWrite() for more context...
|
||||
Common::StackLock lock(*_mutex);
|
||||
_waveOutXorTrigger = 1;
|
||||
|
||||
if ((songName != nullptr) && (attribPos != 0)) {
|
||||
if (table->attribPos != 0)
|
||||
attribPos = table->attribPos;
|
||||
hookId = _attributes[COMI_STATE_OFFSET + attribPos];
|
||||
|
||||
if (table->hookId != 0) {
|
||||
if ((hookId != 0) && (table->hookId > 1)) {
|
||||
_attributes[COMI_STATE_OFFSET + attribPos] = 2;
|
||||
} else {
|
||||
_attributes[COMI_STATE_OFFSET + attribPos] = hookId + 1;
|
||||
if (table->hookId < hookId + 1)
|
||||
_attributes[COMI_STATE_OFFSET + attribPos] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int nextSoundId = 0;
|
||||
while (1) {
|
||||
nextSoundId = diMUSEGetNextSound(nextSoundId);
|
||||
if (!nextSoundId)
|
||||
break;
|
||||
|
||||
// If a sound is found (and its stream is active), fade it out if it's a music track
|
||||
if (diMUSEGetParam(nextSoundId, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && !diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM))
|
||||
diMUSEFadeParam(nextSoundId, DIMUSE_P_VOLUME, 0, 120);
|
||||
}
|
||||
|
||||
int oldSoundId = 0;
|
||||
nextSoundId = 0;
|
||||
while (1) {
|
||||
nextSoundId = diMUSEGetNextSound(nextSoundId);
|
||||
if (!nextSoundId)
|
||||
break;
|
||||
|
||||
if (diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM) && (diMUSEGetParam(nextSoundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC)) {
|
||||
oldSoundId = nextSoundId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!songName) {
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 120);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (table->transitionType) {
|
||||
case 0:
|
||||
debug(5, "IMuseDigital::playComiMusic(): NULL transition, ignored");
|
||||
break;
|
||||
case 1:
|
||||
if (_filesHandler->openSound(table->soundId))
|
||||
return;
|
||||
|
||||
if (table->soundId) {
|
||||
if (diMUSEStartSound(table->soundId, 126))
|
||||
debug(5, "IMuseDigital::playComiMusic(): transition 1, failed to start the sound (%d)", table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 1);
|
||||
diMUSEFadeParam(table->soundId, DIMUSE_P_VOLUME, 127, 120);
|
||||
_filesHandler->closeSound(table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
} else {
|
||||
debug(5, "IMuseDigital::playComiMusic(): transition 1, empty soundId, ignored");
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 12:
|
||||
if (_filesHandler->openSound(table->soundId))
|
||||
return;
|
||||
|
||||
if (table->filename[0] == 0 || table->soundId == 0) {
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
|
||||
break;
|
||||
}
|
||||
|
||||
if (table->transitionType == 4) {
|
||||
_stopSequenceFlag = 0;
|
||||
diMUSESetTrigger(table->soundId, MKTAG('_', 'e', 'n', 'd'), DIMUSE_C_SCRIPT_CALLBACK);
|
||||
}
|
||||
|
||||
if (oldSoundId) {
|
||||
fadeDelay = table->fadeOutDelay;
|
||||
if (!fadeDelay)
|
||||
fadeDelay = 1000;
|
||||
else
|
||||
fadeDelay = (fadeDelay * 100) / 6; // Set dimuse_table fade out time to millisecond scale
|
||||
|
||||
if (table->transitionType == 2) {
|
||||
diMUSESwitchStream(oldSoundId, table->soundId, fadeDelay, 0, 0);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetHook(table->soundId, table->hookId);
|
||||
diMUSEProcessStreams();
|
||||
} else if (oldSoundId != table->soundId) {
|
||||
if ((!sequence) && (table->attribPos != 0) &&
|
||||
(table->attribPos == _comiStateMusicTable[_curMusicState].attribPos)) {
|
||||
|
||||
debug(5, "IMuseDigital::playComiMusic(): Starting new sound (%s) with same attribute as old sound (%s)",
|
||||
table->name, _comiStateMusicTable[_curMusicState].name);
|
||||
|
||||
diMUSESwitchStream(oldSoundId, table->soundId, fadeDelay, 0, 1);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSEProcessStreams();
|
||||
} else {
|
||||
switch (table->transitionType) {
|
||||
case 12:
|
||||
diMUSESetHook(oldSoundId, table->hookId);
|
||||
diMUSESetTrigger(oldSoundId, MKTAG('e', 'x', 'i', 't'), DIMUSE_C_SWITCH_STREAM, oldSoundId, table->soundId, fadeDelay, 1, 0);
|
||||
diMUSESetTrigger(oldSoundId, MKTAG('e', 'x', 'i', 't'), DIMUSE_C_SET_PARAM, table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetTrigger(oldSoundId, MKTAG('e', 'x', 'i', 't'), DIMUSE_C_SET_PARAM, table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetTrigger(oldSoundId, MKTAG('e', 'x', 'i', 't'), DIMUSE_C_SET_HOOK, table->soundId, hookId);
|
||||
diMUSEProcessStreams();
|
||||
break;
|
||||
default:
|
||||
diMUSESwitchStream(oldSoundId, table->soundId, fadeDelay, 0, 0);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetHook(table->soundId, hookId);
|
||||
diMUSEProcessStreams();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (diMUSEStartStream(table->soundId, 126, DIMUSE_BUFFER_MUSIC))
|
||||
debug(5, "IMuseDigital::playComiMusic(): failed to start the stream for sound %d", table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
diMUSESetHook(table->soundId, hookId);
|
||||
}
|
||||
_filesHandler->closeSound(table->soundId);
|
||||
diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
|
||||
break;
|
||||
case 5:
|
||||
debug(5, "IMuseDigital::playComiMusic(): no-op transition type (5), ignored");
|
||||
break;
|
||||
case 6:
|
||||
_stopSequenceFlag = 0;
|
||||
diMUSESetTrigger(DIMUSE_SMUSH_SOUNDID + DIMUSE_BUFFER_MUSIC, MKTAG('_', 'e', 'n', 'd'), DIMUSE_C_SCRIPT_CALLBACK);
|
||||
break;
|
||||
case 7:
|
||||
if (oldSoundId)
|
||||
diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
|
||||
break;
|
||||
case 8:
|
||||
if (oldSoundId)
|
||||
diMUSESetHook(oldSoundId, table->hookId);
|
||||
break;
|
||||
case 9:
|
||||
if (oldSoundId)
|
||||
diMUSESetHook(oldSoundId, table->hookId);
|
||||
_stopSequenceFlag = 0;
|
||||
diMUSESetTrigger(oldSoundId, MKTAG('_', 'e', 'n', 'd'), DIMUSE_C_SCRIPT_CALLBACK);
|
||||
break;
|
||||
default:
|
||||
debug(5, "IMuseDigital::playComiMusic(): bogus transition type, ignored");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
285
engines/scumm/imuse_digi/dimuse_sndmgr.cpp
Normal file
285
engines/scumm/imuse_digi/dimuse_sndmgr.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
/* 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/scummsys.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/flac.h"
|
||||
#include "audio/decoders/voc.h"
|
||||
#include "audio/decoders/vorbis.h"
|
||||
#include "audio/decoders/mp3.h"
|
||||
|
||||
#include "scumm/resource.h"
|
||||
#include "scumm/scumm.h"
|
||||
#include "scumm/imuse_digi/dimuse_bndmgr.h"
|
||||
#include "scumm/imuse_digi/dimuse_codecs.h"
|
||||
#include "scumm/imuse_digi/dimuse_sndmgr.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
ImuseDigiSndMgr::ImuseDigiSndMgr(ScummEngine *scumm) {
|
||||
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
||||
memset(&_sounds[l], 0, sizeof(SoundDesc));
|
||||
}
|
||||
_vm = scumm;
|
||||
_disk = 0;
|
||||
_cacheBundleDir = new BundleDirCache(scumm);
|
||||
assert(_cacheBundleDir);
|
||||
BundleCodecs::initializeImcTables();
|
||||
}
|
||||
|
||||
ImuseDigiSndMgr::~ImuseDigiSndMgr() {
|
||||
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
||||
closeSound(&_sounds[l]);
|
||||
}
|
||||
|
||||
delete _cacheBundleDir;
|
||||
BundleCodecs::releaseImcTables();
|
||||
}
|
||||
|
||||
ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::allocSlot() {
|
||||
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
||||
if (!_sounds[l].inUse) {
|
||||
_sounds[l].inUse = true;
|
||||
_sounds[l].scheduledForDealloc = false;
|
||||
return &_sounds[l];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ImuseDigiSndMgr::openMusicBundle(SoundDesc *sound, int &disk) {
|
||||
bool result = false;
|
||||
bool compressed = false;
|
||||
|
||||
sound->bundle = new BundleMgr(_vm, _cacheBundleDir);
|
||||
assert(sound->bundle);
|
||||
if (_vm->_game.id == GID_CMI) {
|
||||
if (_vm->_game.features & GF_DEMO) {
|
||||
result = sound->bundle->open("music.bun", compressed);
|
||||
} else {
|
||||
char musicfile[20];
|
||||
if (disk == -1)
|
||||
disk = _vm->VAR(_vm->VAR_CURRENTDISK);
|
||||
Common::sprintf_s(musicfile, "musdisk%d.bun", disk);
|
||||
// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
|
||||
// _vm->_DiMUSE_v1->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
|
||||
// _vm->_DiMUSE_v1->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
|
||||
// _vm->_DiMUSE_v1->stopAllSounds();
|
||||
// sound->bundle->closeFile();
|
||||
// }
|
||||
|
||||
result = sound->bundle->open(musicfile, compressed);
|
||||
|
||||
// FIXME: Shouldn't we only set _disk if result == true?
|
||||
_disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
|
||||
}
|
||||
} else if (_vm->_game.id == GID_DIG)
|
||||
result = sound->bundle->open("digmusic.bun", compressed);
|
||||
else
|
||||
error("ImuseDigiSndMgr::openMusicBundle() Don't know which bundle file to load");
|
||||
|
||||
_vm->VAR(_vm->VAR_MUSIC_BUNDLE_LOADED) = result ? 1 : 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ImuseDigiSndMgr::openVoiceBundle(SoundDesc *sound, int &disk) {
|
||||
bool result = false;
|
||||
bool compressed = false;
|
||||
|
||||
sound->bundle = new BundleMgr(_vm, _cacheBundleDir);
|
||||
assert(sound->bundle);
|
||||
if (_vm->_game.id == GID_CMI) {
|
||||
if (_vm->_game.features & GF_DEMO) {
|
||||
result = sound->bundle->open("voice.bun", compressed);
|
||||
} else {
|
||||
char voxfile[20];
|
||||
if (disk == -1)
|
||||
disk = _vm->VAR(_vm->VAR_CURRENTDISK);
|
||||
Common::sprintf_s(voxfile, "voxdisk%d.bun", disk);
|
||||
// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
|
||||
// _vm->_DiMUSE_v1->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
|
||||
// _vm->_DiMUSE_v1->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
|
||||
// _vm->_DiMUSE_v1->stopAllSounds();
|
||||
// sound->bundle->closeFile();
|
||||
// }
|
||||
|
||||
result = sound->bundle->open(voxfile, compressed);
|
||||
|
||||
// FIXME: Shouldn't we only set _disk if result == true?
|
||||
_disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
|
||||
}
|
||||
} else if (_vm->_game.id == GID_DIG)
|
||||
result = sound->bundle->open("digvoice.bun", compressed);
|
||||
else
|
||||
error("ImuseDigiSndMgr::openVoiceBundle() Don't know which bundle file to load");
|
||||
|
||||
_vm->VAR(_vm->VAR_VOICE_BUNDLE_LOADED) = result ? 1 : 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk) {
|
||||
assert(soundId >= 0);
|
||||
assert(soundType);
|
||||
|
||||
SoundDesc *sound = allocSlot();
|
||||
if (!sound) {
|
||||
error("ImuseDigiSndMgr::openSound() can't alloc free sound slot");
|
||||
}
|
||||
|
||||
const bool header_outside = ((_vm->_game.id == GID_CMI) && !(_vm->_game.features & GF_DEMO));
|
||||
bool result = false;
|
||||
byte *ptr = nullptr;
|
||||
|
||||
switch (soundType) {
|
||||
case IMUSE_RESOURCE:
|
||||
assert(soundName[0] == 0); // Paranoia check
|
||||
|
||||
_vm->_res->lock(rtSound, soundId);
|
||||
ptr = _vm->getResourceAddress(rtSound, soundId);
|
||||
if (ptr == nullptr) {
|
||||
closeSound(sound);
|
||||
return nullptr;
|
||||
}
|
||||
sound->resPtr = ptr;
|
||||
sound->resSize = _vm->getResourceSize(rtSound, soundId) - 8;
|
||||
break;
|
||||
case IMUSE_BUNDLE:
|
||||
if (volGroupId == IMUSE_VOLGRP_VOICE)
|
||||
result = openVoiceBundle(sound, disk);
|
||||
else if (volGroupId == IMUSE_VOLGRP_MUSIC)
|
||||
result = openMusicBundle(sound, disk);
|
||||
else
|
||||
error("ImuseDigiSndMgr::openSound() Don't know how load sound: %d", soundId);
|
||||
if (!result) {
|
||||
closeSound(sound);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (soundName[0] != 0) {
|
||||
if (sound->bundle->readFile(soundName, 0x2000, &ptr, header_outside) == 0 || ptr == nullptr) {
|
||||
closeSound(sound);
|
||||
free(ptr);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
sound->resPtr = nullptr;
|
||||
break;
|
||||
default:
|
||||
error("ImuseDigiSndMgr::openSound() Unknown soundType %d (trying to load sound %d)", soundType, soundId);
|
||||
}
|
||||
|
||||
Common::strlcpy(sound->name, soundName, sizeof(sound->name));
|
||||
sound->soundId = soundId;
|
||||
|
||||
if (soundType == IMUSE_BUNDLE) {
|
||||
free(ptr);
|
||||
}
|
||||
return sound;
|
||||
}
|
||||
|
||||
void ImuseDigiSndMgr::closeSound(SoundDesc *soundDesc) {
|
||||
// Check if there's an actual sound to close...
|
||||
if (!checkForProperHandle(soundDesc))
|
||||
return;
|
||||
|
||||
if (soundDesc->resPtr) {
|
||||
bool found = false;
|
||||
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
||||
if ((_sounds[l].soundId == soundDesc->soundId) && (&_sounds[l] != soundDesc))
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
_vm->_res->unlock(rtSound, soundDesc->soundId);
|
||||
}
|
||||
|
||||
delete soundDesc->bundle;
|
||||
|
||||
memset(soundDesc, 0, sizeof(SoundDesc));
|
||||
}
|
||||
|
||||
ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::findSoundById(int soundId) {
|
||||
SoundDesc *soundDesc = nullptr;
|
||||
for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
|
||||
if (_sounds[i].soundId == soundId) {
|
||||
soundDesc = &_sounds[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return soundDesc;
|
||||
}
|
||||
|
||||
ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::getSounds() {
|
||||
return _sounds;
|
||||
}
|
||||
|
||||
void ImuseDigiSndMgr::scheduleSoundForDeallocation(int soundId) {
|
||||
SoundDesc *soundDesc = nullptr;
|
||||
for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
|
||||
if (_sounds[i].soundId == soundId) {
|
||||
soundDesc = &_sounds[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there's an actual sound to deallocate...
|
||||
if (!checkForProperHandle(soundDesc))
|
||||
return;
|
||||
|
||||
soundDesc->scheduledForDealloc = true;
|
||||
}
|
||||
|
||||
void ImuseDigiSndMgr::closeSoundById(int soundId) {
|
||||
SoundDesc *soundDesc = nullptr;
|
||||
for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
|
||||
if (_sounds[i].soundId == soundId) {
|
||||
soundDesc = &_sounds[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (soundDesc) {
|
||||
assert(checkForProperHandle(soundDesc));
|
||||
|
||||
if (soundDesc->resPtr) {
|
||||
_vm->_res->unlock(rtSound, soundDesc->soundId);
|
||||
}
|
||||
|
||||
delete soundDesc->bundle;
|
||||
|
||||
memset(soundDesc, 0, sizeof(SoundDesc));
|
||||
}
|
||||
}
|
||||
|
||||
bool ImuseDigiSndMgr::checkForProperHandle(SoundDesc *soundDesc) {
|
||||
if (!soundDesc)
|
||||
return false;
|
||||
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
||||
if (soundDesc == &_sounds[l])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
97
engines/scumm/imuse_digi/dimuse_sndmgr.h
Normal file
97
engines/scumm/imuse_digi/dimuse_sndmgr.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SCUMM_IMUSE_DIGI_SNDMGR_H
|
||||
#define SCUMM_IMUSE_DIGI_SNDMGR_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Audio {
|
||||
class SeekableAudioStream;
|
||||
}
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class ScummEngine;
|
||||
class BundleMgr;
|
||||
class BundleDirCache;
|
||||
|
||||
class ImuseDigiSndMgr {
|
||||
public:
|
||||
|
||||
#define MAX_IMUSE_SOUNDS 16
|
||||
|
||||
#define IMUSE_RESOURCE 1
|
||||
#define IMUSE_BUNDLE 2
|
||||
|
||||
#define IMUSE_VOLGRP_VOICE 1
|
||||
#define IMUSE_VOLGRP_SFX 2
|
||||
#define IMUSE_VOLGRP_MUSIC 3
|
||||
|
||||
public:
|
||||
|
||||
struct SoundDesc {
|
||||
bool inUse;
|
||||
bool scheduledForDealloc;
|
||||
|
||||
byte *resPtr;
|
||||
int resSize;
|
||||
int resCurOffset;
|
||||
char name[15];
|
||||
int16 soundId;
|
||||
BundleMgr *bundle;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
SoundDesc _sounds[MAX_IMUSE_SOUNDS];
|
||||
bool checkForProperHandle(SoundDesc *soundDesc);
|
||||
SoundDesc *allocSlot();
|
||||
|
||||
ScummEngine *_vm;
|
||||
byte _disk;
|
||||
BundleDirCache *_cacheBundleDir;
|
||||
|
||||
bool openMusicBundle(SoundDesc *sound, int &disk);
|
||||
bool openVoiceBundle(SoundDesc *sound, int &disk);
|
||||
|
||||
public:
|
||||
|
||||
ImuseDigiSndMgr(ScummEngine *scumm);
|
||||
~ImuseDigiSndMgr();
|
||||
|
||||
SoundDesc *openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk);
|
||||
void closeSound(SoundDesc *soundDesc);
|
||||
void closeSoundById(int soundId);
|
||||
SoundDesc *findSoundById(int soundId);
|
||||
SoundDesc *getSounds();
|
||||
void scheduleSoundForDeallocation(int soundId);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
||||
#endif
|
||||
386
engines/scumm/imuse_digi/dimuse_streamer.cpp
Normal file
386
engines/scumm/imuse_digi/dimuse_streamer.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
int IMuseDigital::streamerInit() {
|
||||
for (int l = 0; l < DIMUSE_MAX_STREAMS; l++) {
|
||||
_streams[l].soundId = 0;
|
||||
}
|
||||
_lastStreamLoaded = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
IMuseDigiStream *IMuseDigital::streamerAllocateSound(int soundId, int bufId, int32 maxRead) {
|
||||
IMuseDigiSndBuffer *bufInfoPtr = _filesHandler->getBufInfo(bufId);
|
||||
if (!bufInfoPtr) {
|
||||
debug(5, "IMuseDigital::streamerAlloc(): ERROR: couldn't get buffer info");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((bufInfoPtr->bufSize / 4) <= maxRead) {
|
||||
debug(5, "IMuseDigital::streamerAlloc(): ERROR: maxRead too big for buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_STREAMS; l++) {
|
||||
if (_streams[l].soundId && _streams[l].bufId == bufId) {
|
||||
debug(5, "IMuseDigital::streamerAlloc(): ERROR: stream bufId %d already in use", bufId);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_STREAMS; l++) {
|
||||
if (!_streams[l].soundId) {
|
||||
_streams[l].endOffset = _filesHandler->seek(soundId, 0, SEEK_END, bufId);
|
||||
_streams[l].curOffset = 0;
|
||||
_streams[l].soundId = soundId;
|
||||
_streams[l].bufId = bufId;
|
||||
_streams[l].buf = bufInfoPtr->buffer;
|
||||
_streams[l].bufFreeSize = bufInfoPtr->bufSize - maxRead - (_isEarlyDiMUSE ? 0 : 4);
|
||||
_streams[l].loadSize = bufInfoPtr->loadSize;
|
||||
_streams[l].criticalSize = bufInfoPtr->criticalSize;
|
||||
_streams[l].maxRead = maxRead;
|
||||
_streams[l].loadIndex = 0;
|
||||
_streams[l].readIndex = 0;
|
||||
_streams[l].paused = 0;
|
||||
_streams[l].vocLoopFlag = 0;
|
||||
_streams[l].vocLoopTriggerOffset = 0;
|
||||
return &_streams[l];
|
||||
}
|
||||
}
|
||||
debug(5, "IMuseDigital::streamerAlloc(): ERROR: no spare streams");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int IMuseDigital::streamerClearSoundInStream(IMuseDigiStream *streamPtr) {
|
||||
streamPtr->soundId = 0;
|
||||
if (_lastStreamLoaded == streamPtr) {
|
||||
_lastStreamLoaded = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::streamerProcessStreams() {
|
||||
if (!_isEarlyDiMUSE)
|
||||
dispatchPredictFirstStream();
|
||||
|
||||
IMuseDigiStream *stream1 = nullptr;
|
||||
IMuseDigiStream *stream2 = nullptr;
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_STREAMS; l++) {
|
||||
if ((_streams[l].soundId) && (!_streams[l].paused)) {
|
||||
if (stream2) {
|
||||
if (stream1) {
|
||||
debug(5, "IMuseDigital::streamerProcessStreams(): WARNING: three streams in use");
|
||||
} else {
|
||||
stream1 = &_streams[l];
|
||||
}
|
||||
} else {
|
||||
stream2 = &_streams[l];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!stream1) {
|
||||
if (stream2)
|
||||
streamerFetchData(stream2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!stream2) {
|
||||
if (stream1)
|
||||
streamerFetchData(stream1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if we've reached or surpassed criticalSize
|
||||
bool critical1 = (streamerGetFreeBufferAmount(stream1) >= stream1->criticalSize);
|
||||
bool critical2 = (streamerGetFreeBufferAmount(stream2) >= stream2->criticalSize);
|
||||
|
||||
if (!critical1) {
|
||||
if (!critical2) {
|
||||
if (stream1 == _lastStreamLoaded) {
|
||||
streamerFetchData(stream1);
|
||||
streamerFetchData(stream2);
|
||||
return 0;
|
||||
} else {
|
||||
streamerFetchData(stream2);
|
||||
streamerFetchData(stream1);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
streamerFetchData(stream1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!critical2) {
|
||||
streamerFetchData(stream2);
|
||||
return 0;
|
||||
} else {
|
||||
if (stream1 == _lastStreamLoaded) {
|
||||
streamerFetchData(stream1);
|
||||
} else {
|
||||
streamerFetchData(stream2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 *IMuseDigital::streamerGetStreamBuffer(IMuseDigiStream *streamPtr, int size) {
|
||||
if (size > streamerGetFreeBufferAmount(streamPtr) || streamPtr->maxRead < size)
|
||||
return 0;
|
||||
|
||||
if (size > streamPtr->bufFreeSize - streamPtr->readIndex) {
|
||||
memcpy(&streamPtr->buf[streamPtr->bufFreeSize],
|
||||
streamPtr->buf,
|
||||
size - streamPtr->bufFreeSize + streamPtr->readIndex + (_isEarlyDiMUSE ? 0 : 4));
|
||||
}
|
||||
|
||||
int32 readIndex_tmp = streamPtr->readIndex;
|
||||
uint8 *ptr = &streamPtr->buf[readIndex_tmp];
|
||||
streamPtr->readIndex = readIndex_tmp + size;
|
||||
if (streamPtr->bufFreeSize <= readIndex_tmp + size)
|
||||
streamPtr->readIndex = readIndex_tmp + size - streamPtr->bufFreeSize;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint8 *IMuseDigital::streamerGetStreamBufferAtOffset(IMuseDigiStream *streamPtr, int32 offset, int size) {
|
||||
if (offset + size > streamerGetFreeBufferAmount(streamPtr) || streamPtr->maxRead < size)
|
||||
return 0;
|
||||
|
||||
int32 offsetReadIndex = offset + streamPtr->readIndex;
|
||||
if (offsetReadIndex >= streamPtr->bufFreeSize) {
|
||||
offsetReadIndex -= streamPtr->bufFreeSize;
|
||||
}
|
||||
|
||||
if (size > streamPtr->bufFreeSize - offsetReadIndex) {
|
||||
memcpy(&streamPtr->buf[streamPtr->bufFreeSize], streamPtr->buf, size + offsetReadIndex - streamPtr->bufFreeSize);
|
||||
}
|
||||
|
||||
return &streamPtr->buf[offsetReadIndex];
|
||||
}
|
||||
|
||||
int IMuseDigital::streamerSetReadIndex(IMuseDigiStream *streamPtr, int offset) {
|
||||
_streamerBailFlag = 1;
|
||||
if (offset > streamerGetFreeBufferAmount(streamPtr))
|
||||
return -1;
|
||||
|
||||
streamPtr->readIndex += offset;
|
||||
if (streamPtr->readIndex >= streamPtr->bufFreeSize) {
|
||||
streamPtr->readIndex -= streamPtr->bufFreeSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::streamerSetLoadIndex(IMuseDigiStream *streamPtr, int offset) {
|
||||
_streamerBailFlag = 1;
|
||||
if (offset > streamerGetFreeBufferAmount(streamPtr))
|
||||
return -1;
|
||||
|
||||
streamPtr->loadIndex = streamPtr->readIndex + offset;
|
||||
if (streamPtr->loadIndex >= streamPtr->bufFreeSize) {
|
||||
streamPtr->loadIndex -= streamPtr->bufFreeSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::streamerGetFreeBufferAmount(IMuseDigiStream *streamPtr) {
|
||||
int32 freeBufferSize = streamPtr->loadIndex - streamPtr->readIndex;
|
||||
if (freeBufferSize < 0)
|
||||
freeBufferSize += streamPtr->bufFreeSize;
|
||||
return freeBufferSize;
|
||||
}
|
||||
|
||||
int IMuseDigital::streamerSetSoundToStreamFromOffset(IMuseDigiStream *streamPtr, int soundId, int32 offset) {
|
||||
_streamerBailFlag = 1;
|
||||
streamPtr->soundId = soundId;
|
||||
streamPtr->curOffset = offset;
|
||||
streamPtr->endOffset = _isEarlyDiMUSE ? _filesHandler->seek(streamPtr->soundId, 0, SEEK_END, streamPtr->bufId) : 0;
|
||||
streamPtr->paused = 0;
|
||||
if (_lastStreamLoaded == streamPtr) {
|
||||
_lastStreamLoaded = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigital::streamerQueryStream(IMuseDigiStream *streamPtr, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused) {
|
||||
if (!isFTSoundEngine())
|
||||
dispatchPredictFirstStream();
|
||||
|
||||
bufSize = streamPtr->bufFreeSize;
|
||||
criticalSize = (isFTSoundEngine() && streamPtr->paused) ? 0 : streamPtr->criticalSize;
|
||||
freeSpace = streamerGetFreeBufferAmount(streamPtr);
|
||||
paused = streamPtr->paused;
|
||||
}
|
||||
|
||||
int IMuseDigital::streamerFeedStream(IMuseDigiStream *streamPtr, uint8 *srcBuf, int32 sizeToFeed, int paused) {
|
||||
int32 size = streamPtr->readIndex - streamPtr->loadIndex;
|
||||
if (size <= 0)
|
||||
size += streamPtr->bufFreeSize;
|
||||
if (sizeToFeed > (size - 4)) {
|
||||
// Don't worry, judging from the intro of The Dig, this is totally expected
|
||||
// to happen, and when it does, the output matches the EXE behavior
|
||||
debug(5, "IMuseDigital::streamerFeedStream(): WARNING: buffer overflow");
|
||||
_streamerBailFlag = 1;
|
||||
|
||||
int32 newTentativeSize = sizeToFeed - (size - 4) - (sizeToFeed - (size - 4)) % 12 + 12;
|
||||
size = streamPtr->loadIndex - streamPtr->readIndex;
|
||||
if (size < 0)
|
||||
size += streamPtr->bufFreeSize;
|
||||
if (size >= newTentativeSize) {
|
||||
streamPtr->readIndex = newTentativeSize + streamPtr->readIndex;
|
||||
if (streamPtr->bufFreeSize <= streamPtr->readIndex)
|
||||
streamPtr->readIndex -= streamPtr->bufFreeSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (sizeToFeed > 0) {
|
||||
do {
|
||||
size = streamPtr->bufFreeSize - streamPtr->loadIndex;
|
||||
if (size >= sizeToFeed) {
|
||||
size = sizeToFeed;
|
||||
}
|
||||
sizeToFeed -= size;
|
||||
memcpy(&streamPtr->buf[streamPtr->loadIndex], srcBuf, size);
|
||||
srcBuf += size;
|
||||
|
||||
int val = size + streamPtr->loadIndex;
|
||||
streamPtr->curOffset += size;
|
||||
streamPtr->loadIndex += size;
|
||||
if (val >= streamPtr->bufFreeSize) {
|
||||
streamPtr->loadIndex = val - streamPtr->bufFreeSize;
|
||||
}
|
||||
} while (sizeToFeed > 0);
|
||||
}
|
||||
|
||||
streamPtr->paused = paused;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::streamerFetchData(IMuseDigiStream *streamPtr) {
|
||||
if (!_isEarlyDiMUSE && streamPtr->endOffset == 0) {
|
||||
streamPtr->endOffset = _filesHandler->seek(streamPtr->soundId, 0, SEEK_END, streamPtr->bufId);
|
||||
}
|
||||
|
||||
int32 size = streamPtr->readIndex - streamPtr->loadIndex;
|
||||
if (size <= 0)
|
||||
size += streamPtr->bufFreeSize;
|
||||
|
||||
int32 loadSize = streamPtr->loadSize;
|
||||
int32 remainingSize = streamPtr->endOffset - streamPtr->curOffset;
|
||||
|
||||
if (loadSize >= size - (_isEarlyDiMUSE ? 1 : 4)) {
|
||||
loadSize = size - (_isEarlyDiMUSE ? 1 : 4);
|
||||
}
|
||||
|
||||
if (loadSize >= remainingSize) {
|
||||
loadSize = remainingSize;
|
||||
}
|
||||
|
||||
if (remainingSize <= 0) {
|
||||
streamPtr->paused = 1;
|
||||
|
||||
if (!_isEarlyDiMUSE) {
|
||||
// Pad the buffer
|
||||
streamPtr->buf[streamPtr->loadIndex] = 127;
|
||||
streamPtr->loadIndex++;
|
||||
streamPtr->buf[streamPtr->loadIndex] = 127;
|
||||
streamPtr->loadIndex++;
|
||||
streamPtr->buf[streamPtr->loadIndex] = 127;
|
||||
streamPtr->loadIndex++;
|
||||
streamPtr->buf[streamPtr->loadIndex] = 127;
|
||||
streamPtr->loadIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
int32 actualAmount;
|
||||
int32 requestedAmount;
|
||||
|
||||
while (1) {
|
||||
if (!_isEarlyDiMUSE && loadSize <= 0)
|
||||
return 0;
|
||||
|
||||
requestedAmount = streamPtr->bufFreeSize - streamPtr->loadIndex;
|
||||
if (requestedAmount >= loadSize) {
|
||||
requestedAmount = loadSize;
|
||||
}
|
||||
|
||||
if (_filesHandler->seek(streamPtr->soundId, streamPtr->curOffset, SEEK_SET, streamPtr->bufId) != streamPtr->curOffset) {
|
||||
debug(5, "IMuseDigital::streamerFetchData(): ERROR: invalid seek in streamer (%d), pausing stream...", streamPtr->curOffset);
|
||||
streamPtr->paused = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_streamerBailFlag = 0;
|
||||
|
||||
_mutex->lock();
|
||||
actualAmount = _filesHandler->read(streamPtr->soundId, &streamPtr->buf[streamPtr->loadIndex], requestedAmount, streamPtr->bufId);
|
||||
_mutex->unlock();
|
||||
|
||||
// FT has no bailFlag
|
||||
if (!_isEarlyDiMUSE && _streamerBailFlag)
|
||||
return 0;
|
||||
|
||||
loadSize -= actualAmount;
|
||||
streamPtr->curOffset += actualAmount;
|
||||
_lastStreamLoaded = streamPtr;
|
||||
|
||||
int32 newLoadIndex = actualAmount + streamPtr->loadIndex;
|
||||
streamPtr->loadIndex = newLoadIndex;
|
||||
if (newLoadIndex >= streamPtr->bufFreeSize) {
|
||||
streamPtr->loadIndex = newLoadIndex - streamPtr->bufFreeSize;
|
||||
}
|
||||
|
||||
if (_isEarlyDiMUSE && streamPtr->vocLoopFlag) {
|
||||
if (streamPtr->vocLoopTriggerOffset <= streamPtr->curOffset) {
|
||||
dispatchVOCLoopCallback(streamPtr->soundId);
|
||||
streamPtr->vocLoopFlag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (actualAmount != requestedAmount)
|
||||
break;
|
||||
|
||||
// FT checks for negative or zero loadSize here
|
||||
if (_isEarlyDiMUSE && loadSize <= 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug(5, "IMuseDigital::streamerFetchData(): ERROR: unable to load the correct amount of data (req=%d, act=%d)", requestedAmount, actualAmount);
|
||||
_lastStreamLoaded = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigital::streamerSetLoopFlag(IMuseDigiStream *streamPtr, int offset) {
|
||||
streamPtr->vocLoopFlag = 1;
|
||||
streamPtr->vocLoopTriggerOffset = offset;
|
||||
}
|
||||
|
||||
void IMuseDigital::streamerRemoveLoopFlag(IMuseDigiStream *streamPtr) {
|
||||
streamPtr->vocLoopFlag = 0;
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
866
engines/scumm/imuse_digi/dimuse_tables.cpp
Normal file
866
engines/scumm/imuse_digi/dimuse_tables.cpp
Normal file
@@ -0,0 +1,866 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_tables.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
const imuseRoomMap _digStateMusicMap[] = {
|
||||
{0, 0, 0, 0, 0, 0 },
|
||||
{1, 0, 0, 0, 0, 0 },
|
||||
{2, 2, 0, 0, 0, 0 },
|
||||
{4, 3, 0, 0, 0, 0 },
|
||||
{5, 3, 0, 0, 0, 0 },
|
||||
{6, 3, 0, 0, 0, 0 },
|
||||
{7, 3, 0, 0, 0, 0 },
|
||||
{8, 4, 0, 0, 0, 0 },
|
||||
{9, 5, 0, 0, 0, 0 },
|
||||
{10, 4, 0, 0, 0, 0 },
|
||||
{12, 5, 0, 0, 0, 0 },
|
||||
{14, 5, 0, 0, 0, 0 },
|
||||
{15, 6, 29, 7, 0, 0 },
|
||||
{16, 8, 0, 0, 0, 0 },
|
||||
{17, 1, 0, 0, 0, 0 },
|
||||
{18, 9, 0, 0, 0, 0 },
|
||||
{19, 9, 0, 0, 0, 0 },
|
||||
{20, 6, 0, 0, 0, 0 },
|
||||
{21, 6, 0, 0, 0, 0 },
|
||||
{22, 44, 0, 0, 0, 0 },
|
||||
{23, 10, 7, 0, 0, 0 },
|
||||
{24, 26, 0, 0, 0, 0 },
|
||||
{25, 17, 0, 0, 0, 0 },
|
||||
{26, 17, 0, 0, 0, 0 },
|
||||
{27, 18, 0, 0, 0, 0 },
|
||||
{28, 1, 0, 0, 0, 0 },
|
||||
{29, 20, 0, 0, 0, 0 },
|
||||
{30, 22, 0, 0, 0, 0 },
|
||||
{31, 23, 0, 0, 0, 0 },
|
||||
{32, 22, 0, 0, 0, 0 },
|
||||
{33, 26, 0, 0, 0, 0 },
|
||||
{34, 24, 0, 0, 0, 0 },
|
||||
{35, 1, 0, 0, 0, 0 },
|
||||
{36, 1, 0, 0, 0, 0 },
|
||||
{37, 42, 0, 0, 0, 0 },
|
||||
{38, 43, 0, 0, 0, 0 },
|
||||
{39, 44, 0, 0, 0, 0 },
|
||||
{40, 1, 0, 0, 0, 0 },
|
||||
{41, 43, 0, 0, 0, 0 },
|
||||
{42, 44, 0, 0, 0, 0 },
|
||||
{43, 43, 0, 0, 0, 0 },
|
||||
{44, 45, 117,45, 114,46},
|
||||
{47, 1, 0, 0, 0, 0 },
|
||||
{48, 43, 0, 0, 0, 0 },
|
||||
{49, 44, 0, 0, 0, 0 },
|
||||
{51, 1, 0, 0, 0, 0 },
|
||||
{53, 28, 0, 0, 0, 0 },
|
||||
{54, 28, 0, 0, 0, 0 },
|
||||
{55, 29, 0, 0, 0, 0 },
|
||||
{56, 29, 0, 0, 0, 0 },
|
||||
{57, 29, 0, 0, 0, 0 },
|
||||
{58, 31, 0, 0, 0, 0 },
|
||||
{59, 1, 0, 0, 0, 0 },
|
||||
{60, 37, 0, 0, 0, 0 },
|
||||
{61, 39, 0, 0, 0, 0 },
|
||||
{62, 38, 0, 0, 0, 0 },
|
||||
{63, 39, 0, 0, 0, 0 },
|
||||
{64, 39, 0, 0, 0, 0 },
|
||||
{65, 40, 0, 0, 0, 0 },
|
||||
{67, 40, 0, 0, 0, 0 },
|
||||
{68, 39, 0, 0, 0, 0 },
|
||||
{69, 1, 0, 0, 0, 0 },
|
||||
{70, 49, 0, 0, 0, 0 },
|
||||
{73, 50, 0, 0, 0, 0 },
|
||||
{75, 51, 0, 0, 0, 0 },
|
||||
{76, 1, 0, 0, 0, 0 },
|
||||
{77, 52, 7, 0, 0, 0 },
|
||||
{78, 63, 0, 0, 0, 0 },
|
||||
{79, 1, 0, 0, 0, 0 },
|
||||
{82, 21, 0, 0, 0, 0 },
|
||||
{85, 1, 0, 0, 0, 0 },
|
||||
{86, 0, 0, 0, 0, 0 },
|
||||
{89, 33, 6, 35, 5, 34},
|
||||
{90, 16, 0, 0, 0, 0 },
|
||||
{91, 57, 0, 0, 0, 0 },
|
||||
{88, 32, 0, 0, 0, 0 },
|
||||
{92, 25, 0, 0, 0, 0 },
|
||||
{93, 0, 0, 0, 0, 0 },
|
||||
{95, 19, 0, 0, 0, 0 },
|
||||
{80, 41, 0, 0, 0, 0 },
|
||||
{81, 48, 0, 0, 0, 0 },
|
||||
{83, 27, 0, 0, 0, 0 },
|
||||
{94, 36, 0, 0, 0, 0 },
|
||||
{40, 1, 0, 0, 0, 0 },
|
||||
{96, 13, 0, 0, 0, 0 },
|
||||
{97, 14, 0, 0, 0, 0 },
|
||||
{98, 11, 0, 0, 0, 0 },
|
||||
{99, 15, 0, 0, 0, 0 },
|
||||
{100, 17, 0, 0, 0, 0 },
|
||||
{101, 38, 0, 0, 0, 0 },
|
||||
{103, 0, 0, 0, 0, 0 },
|
||||
{104, 0, 0, 0, 0, 0 },
|
||||
{11, 44, 0, 0, 0, 0 },
|
||||
{3, 47, 0, 0, 0, 0 },
|
||||
{105, 30, 128,29, 0, 0 },
|
||||
{106, 0, 0, 0, 0, 0 },
|
||||
{107, 1, 0, 0, 0, 0 },
|
||||
{108, 1, 0, 0, 0, 0 },
|
||||
{47, 1, 0, 0, 0, 0 },
|
||||
{50, 1, 0, 0, 0, 0 },
|
||||
{52, 0, 0, 0, 0, 0 },
|
||||
{71, 1, 0, 0, 0, 0 },
|
||||
{13, 1, 0, 0, 0, 0 },
|
||||
{72, 1, 0, 0, 0, 0 },
|
||||
{46, 33, 6, 35, 5, 34},
|
||||
{74, 1, 0, 0, 0, 0 },
|
||||
{84, 1, 0, 0, 0, 0 },
|
||||
{66, 1, 0, 0, 0, 0 },
|
||||
{102, 1, 0, 0, 0, 0 },
|
||||
{109, 1, 0, 0, 0, 0 },
|
||||
{110, 2, 0, 0, 0, 0 },
|
||||
{45, 1, 0, 0, 0, 0 },
|
||||
{87, 1, 0, 0, 0, 0 },
|
||||
{111, 1, 0, 0, 0, 0 },
|
||||
{-1, 1, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
const imuseDigTable _digStateMusicTable[] = {
|
||||
{0, 1000, "STATE_NULL", 0, 0, ""}, /* 00 */
|
||||
{0, 1001, "stateNoChange", 0, 0, ""}, /* 01 */
|
||||
{3, 1100, "stateAstShip", 2, 0, "ASTERO~1.IMU"}, /* 02 */
|
||||
{3, 1120, "stateAstClose", 2, 0, "ASTERO~2.IMU"}, /* 03 */
|
||||
{3, 1140, "stateAstInside", 0, 0, "ASTERO~3.IMU"}, /* 04 */
|
||||
{3, 1150, "stateAstCore", 0, 2, "ASTERO~4.IMU"}, /* 05 */
|
||||
{3, 1200, "stateCanyonClose", 0, 1, "CANYON~1.IMU"}, /* 06 */
|
||||
{3, 1205, "stateCanyonClose_m", 0, 0, "CANYON~2.IMU"}, /* 07 */
|
||||
{3, 1210, "stateCanyonOver", 0, 1, "CANYON~3.IMU"}, /* 08 */
|
||||
{3, 1220, "stateCanyonWreck", 0, 1, "CANYON~4.IMU"}, /* 09 */
|
||||
{3, 1300, "stateNexusCanyon", 10, 0, "NEXUS(~1.IMU"}, /* 10 */
|
||||
{3, 1310, "stateNexusPlan", 10, 0, "NEXUS(~1.IMU"}, /* 11 */
|
||||
{3, 1320, "stateNexusRamp", 10, 0, "NEXUS(~2.IMU"}, /* 12 */
|
||||
{3, 1330, "stateNexusMuseum", 10, 0, "NEXUS(~3.IMU"}, /* 13 */
|
||||
{3, 1340, "stateNexusMap", 10, 0, "NEXUS(~4.IMU"}, /* 14 */
|
||||
{3, 1350, "stateNexusTomb", 10, 0, "NE3706~5.IMU"}, /* 15 */
|
||||
{3, 1360, "stateNexusCath", 10, 0, "NE3305~5.IMU"}, /* 16 */
|
||||
{3, 1370, "stateNexusAirlock", 0, 0, "NE2D3A~5.IMU"}, /* 17 */
|
||||
{3, 1380, "stateNexusPowerOff", 0, 1, "NE8522~5.IMU"}, /* 18 */
|
||||
{3, 1400, "stateMuseumTramNear", 0, 1, "TRAM(M~1.IMU"}, /* 19 */
|
||||
{3, 1410, "stateMuseumTramFar", 0, 0, "TRAM(M~2.IMU"}, /* 20 */
|
||||
{3, 1420, "stateMuseumLockup", 0, 0, "MUSEUM~1.IMU"}, /* 21 */
|
||||
{3, 1433, "stateMuseumPool", 22, 1, "MUSEUM~2.IMU"}, /* 22 */
|
||||
{3, 1436, "stateMuseumSpire", 22, 2, "MUSEUM~3.IMU"}, /* 23 */
|
||||
{3, 1440, "stateMuseumMuseum", 22, 2, "MUSEUM~4.IMU"}, /* 24 */
|
||||
{3, 1450, "stateMuseumLibrary", 0, 0, "MUB575~5.IMU"}, /* 25 */
|
||||
{3, 1460, "stateMuseumCavern", 0, 0, "MUF9BE~5.IMU"}, /* 26 */
|
||||
{3, 1500, "stateTombTramNear", 0, 1, "TRAM(T~1.IMU"}, /* 27 */
|
||||
{3, 1510, "stateTombBase", 28, 2, "TOMB(A~1.IMU"}, /* 28 */
|
||||
{3, 1520, "stateTombSpire", 28, 2, "TOMB(A~2.IMU"}, /* 29 */
|
||||
{3, 1530, "stateTombCave", 28, 2, "TOMB(A~3.IMU"}, /* 30 */
|
||||
{3, 1540, "stateTombCrypt", 31, 1, "TOMB(C~1.IMU"}, /* 31 */
|
||||
{3, 1550, "stateTombGuards", 31, 1, "TOMB(C~2.IMU"}, /* 32 */
|
||||
{3, 1560, "stateTombInner", 0, 1, "TOMB(I~1.IMU"}, /* 33 */
|
||||
{3, 1570, "stateTombCreator1", 0, 0, "TOMB(C~3.IMU"}, /* 34 */
|
||||
{3, 1580, "stateTombCreator2", 0, 0, "TOMB(C~4.IMU"}, /* 35 */
|
||||
{3, 1600, "statePlanTramNear", 0, 1, "TRAM(P~1.IMU"}, /* 36 */
|
||||
{3, 1610, "statePlanTramFar", 0, 0, "TRAM(P~2.IMU"}, /* 37 */
|
||||
{3, 1620, "statePlanBase", 38, 2, "PLAN(A~1.IMU"}, /* 38 */
|
||||
{3, 1630, "statePlanSpire", 38, 2, "PLAN(A~2.IMU"}, /* 39 */
|
||||
{3, 1650, "statePlanDome", 0, 0, "PLAN(D~1.IMU"}, /* 40 */
|
||||
{3, 1700, "stateMapTramNear", 0, 1, "TRAM(M~3.IMU"}, /* 41 */
|
||||
{3, 1710, "stateMapTramFar", 0, 0, "TRAM(M~4.IMU"}, /* 42 */
|
||||
{3, 1720, "stateMapCanyon", 43, 2, "MAP(AM~1.IMU"}, /* 43 */
|
||||
{3, 1730, "stateMapExposed", 43, 2, "MAP(AM~2.IMU"}, /* 44 */
|
||||
{3, 1750, "stateMapNestEmpty", 43, 2, "MAP(AM~4.IMU"}, /* 45 */
|
||||
{3, 1760, "stateMapNestMonster", 0, 0, "MAP(MO~1.IMU"}, /* 46 */
|
||||
{3, 1770, "stateMapKlein", 0, 0, "MAP(KL~1.IMU"}, /* 47 */
|
||||
{3, 1800, "stateCathTramNear", 0, 1, "TRAM(C~1.IMU"}, /* 48 */
|
||||
{3, 1810, "stateCathTramFar", 0, 0, "TRAM(C~2.IMU"}, /* 49 */
|
||||
{3, 1820, "stateCathLab", 50, 1, "CATH(A~1.IMU"}, /* 50 */
|
||||
{3, 1830, "stateCathOutside", 50, 1, "CATH(A~2.IMU"}, /* 51 */
|
||||
{3, 1900, "stateWorldMuseum", 52, 0, "WORLD(~1.IMU"}, /* 52 */
|
||||
{3, 1901, "stateWorldPlan", 52, 0, "WORLD(~2.IMU"}, /* 53 */
|
||||
{3, 1902, "stateWorldTomb", 52, 0, "WORLD(~3.IMU"}, /* 54 */
|
||||
{3, 1903, "stateWorldMap", 52, 0, "WORLD(~4.IMU"}, /* 55 */
|
||||
{3, 1904, "stateWorldCath", 52, 0, "WO3227~5.IMU"}, /* 56 */
|
||||
{3, 1910, "stateEye1", 0, 0, "EYE1~1.IMU"}, /* 57 */
|
||||
{3, 1911, "stateEye2", 0, 0, "EYE2~1.IMU"}, /* 58 */
|
||||
{3, 1912, "stateEye3", 0, 0, "EYE3~1.IMU"}, /* 59 */
|
||||
{3, 1913, "stateEye4", 0, 0, "EYE4~1.IMU"}, /* 60 */
|
||||
{3, 1914, "stateEye5", 0, 0, "EYE5~1.IMU"}, /* 61 */
|
||||
{3, 1915, "stateEye6", 0, 0, "EYE6~1.IMU"}, /* 62 */
|
||||
{3, 1916, "stateEye7", 0, 0, "EYE7~1.IMU"}, /* 63 */
|
||||
{0, -1, "", 0, 0, ""}
|
||||
};
|
||||
|
||||
const imuseDigTable _digSeqMusicTable[] = {
|
||||
{0, 2000, "SEQ_NULL", 0, 0, ""},
|
||||
{0, 2005, "seqLogo", 0, 0, ""},
|
||||
{0, 2010, "seqIntro", 0, 0, ""},
|
||||
{6, 2020, "seqExplosion1b", 0, 0, ""},
|
||||
{3, 2030, "seqAstTunnel1a", 0, 0, "SEQ(AS~1.IMU"},
|
||||
{6, 2031, "seqAstTunnel2b", 0, 0, ""},
|
||||
{4, 2032, "seqAstTunnel3a", 0, 0, "SEQ(AS~2.IMU"},
|
||||
{5, 2040, "seqToPlanet1b", 0, 0, ""},
|
||||
{4, 2045, "seqArgBegin", 0, 0, "SEQ(AR~1.IMU"},
|
||||
{4, 2046, "seqArgEnd", 0, 0, "SEQ(AR~2.IMU"},
|
||||
{4, 2050, "seqWreckGhost", 0, 0, "SEQ(GH~1.IMU"},
|
||||
{4, 2060, "seqCanyonGhost", 0, 0, "SEQ(GH~2.IMU"},
|
||||
{0, 2070, "seqBrinkFall", 0, 0, ""},
|
||||
{4, 2080, "seqPanUpCanyon", 0, 0, "SEQ(PA~1.IMU"},
|
||||
{6, 2091, "seqAirlockTunnel1b", 0, 0, ""},
|
||||
{6, 2100, "seqTramToMu", 0, 0, ""},
|
||||
{6, 2101, "seqTramFromMu", 0, 0, ""},
|
||||
{6, 2102, "seqTramToTomb", 0, 0, ""},
|
||||
{6, 2103, "seqTramFromTomb", 0, 0, ""},
|
||||
{6, 2104, "seqTramToPlan", 0, 0, ""},
|
||||
{6, 2105, "seqTramFromPlan", 0, 0, ""},
|
||||
{6, 2106, "seqTramToMap", 0, 0, ""},
|
||||
{6, 2107, "seqTramFromMap", 0, 0, ""},
|
||||
{6, 2108, "seqTramToCath", 0, 0, ""},
|
||||
{6, 2109, "seqTramFromCath", 0, 0, ""},
|
||||
{0, 2110, "seqMuseumGhost", 0, 0, ""},
|
||||
{0, 2120, "seqSerpentAppears", 0, 0, ""},
|
||||
{0, 2130, "seqSerpentEats", 0, 0, ""},
|
||||
{6, 2140, "seqBrinkRes1b", 0, 0, ""},
|
||||
{4, 2141, "seqBrinkRes2a", 0, 0, "SEQ(BR~1.IMU"},
|
||||
{3, 2150, "seqLockupEntry", 0, 0, "SEQ(BR~1.IMU"},
|
||||
{0, 2160, "seqSerpentExplodes", 0, 0, ""},
|
||||
{4, 2170, "seqSwimUnderwater", 0, 0, "SEQ(DE~1.IMU"},
|
||||
{4, 2175, "seqWavesPlunge", 0, 0, "SEQ(PL~1.IMU"},
|
||||
{0, 2180, "seqCryptOpens", 0, 0, ""},
|
||||
{0, 2190, "seqGuardsFight", 0, 0, ""},
|
||||
{3, 2200, "seqCreatorRes1.1a", 0, 0, "SEQ(CR~1.IMU"},
|
||||
{6, 2201, "seqCreatorRes1.2b", 0, 0, ""},
|
||||
{6, 2210, "seqMaggieCapture1b", 0, 0, ""},
|
||||
{3, 2220, "seqStealCrystals", 0, 0, "SEQ(BR~1.IMU"},
|
||||
{0, 2230, "seqGetByMonster", 0, 0, ""},
|
||||
{6, 2240, "seqKillMonster1b", 0, 0, ""},
|
||||
{3, 2250, "seqCreatorRes2.1a", 0, 0, "SEQ(CR~2.IMU"},
|
||||
{6, 2251, "seqCreatorRes2.2b", 0, 0, ""},
|
||||
{4, 2252, "seqCreatorRes2.3a", 0, 0, "SEQ(CR~3.IMU"},
|
||||
{0, 2260, "seqMaggieInsists", 0, 0, ""},
|
||||
{0, 2270, "seqBrinkHelpCall", 0, 0, ""},
|
||||
{3, 2280, "seqBrinkCrevice1a", 0, 0, "SEQ(BR~2.IMU"},
|
||||
{3, 2281, "seqBrinkCrevice2a", 0, 0, "SEQ(BR~3.IMU"},
|
||||
{6, 2290, "seqCathAccess1b", 0, 0, ""},
|
||||
{4, 2291, "seqCathAccess2a", 0, 0, "SEQ(CA~1.IMU"},
|
||||
{3, 2300, "seqBrinkAtGenerator", 0, 0, "SEQ(BR~1.IMU"},
|
||||
{6, 2320, "seqFightBrink1b", 0, 0, ""},
|
||||
{6, 2340, "seqMaggieDies1b", 0, 0, ""},
|
||||
{6, 2346, "seqMaggieRes1b", 0, 0, ""},
|
||||
{4, 2347, "seqMaggieRes2a", 0, 0, "SEQ(MA~1.IMU"},
|
||||
{0, 2350, "seqCreatureFalls", 0, 0, ""},
|
||||
{5, 2360, "seqFinale1b", 0, 0, ""},
|
||||
{3, 2370, "seqFinale2a", 0, 0, "SEQ(FI~1.IMU"},
|
||||
{6, 2380, "seqFinale3b1", 0, 0, ""},
|
||||
{6, 2390, "seqFinale3b2", 0, 0, ""},
|
||||
{3, 2400, "seqFinale4a", 0, 0, "SEQ(FI~2.IMU"},
|
||||
{3, 2410, "seqFinale5a", 0, 0, "SEQ(FI~3.IMU"},
|
||||
{3, 2420, "seqFinale6a", 0, 0, "SEQ(FI~4.IMU"},
|
||||
{3, 2430, "seqFinale7a", 0, 0, "SE3D2B~5.IMU"},
|
||||
{6, 2440, "seqFinale8b", 0, 0, ""},
|
||||
{4, 2450, "seqFinale9a", 0, 0, "SE313B~5.IMU"},
|
||||
{0, -1, "", 0, 0, ""}
|
||||
};
|
||||
|
||||
const imuseComiTable _comiStateMusicTable[] = {
|
||||
{0, 1000, "STATE_NULL", 0, 0, 0, "" }, /* 00 */
|
||||
{0, 1001, "stateNoChange", 0, 0, 0, "" }, /* 01 */
|
||||
{3, 1098, "stateCredits1", 0, 0, 60, "1098-C~1.IMX"}, /* 02 */
|
||||
{3, 1099, "stateMenu", 0, 0, 60, "1099-M~1.IMX"}, /* 03 */
|
||||
{3, 1100, "stateHold1", 4, 0, 60, "1100-H~1.IMX"}, /* 04 */
|
||||
{3, 1101, "stateWaterline1", 4, 0, 60, "1101-W~1.IMX"}, /* 05 */
|
||||
{3, 1102, "stateHold2", 6, 1, 60, "1102-H~1.IMX"}, /* 06 */
|
||||
{3, 1103, "stateWaterline2", 6, 0, 60, "1103-W~1.IMX"}, /* 07 */
|
||||
{3, 1104, "stateCannon", 0, 0, 60, "1104-C~1.IMX"}, /* 08 */
|
||||
{3, 1105, "stateTreasure", 0, 0, 60, "1105-T~1.IMX"}, /* 09 */
|
||||
{3, 1200, "stateFortBase", 10, 1, 60, "1200-F~1.IMX"}, /* 10 */
|
||||
{3, 1201, "statePreFort", 10, 1, 60, "1201-P~1.IMX"}, /* 11 */
|
||||
{3, 1202, "statePreVooOut", 12, 0, 60, "1202-P~1.IMX"}, /* 12 */
|
||||
{3, 1203, "statePreVooIn", 12, 0, 60, "1203-P~1.IMX"}, /* 13 */
|
||||
{3, 1204, "statePreVooLady", 12, 0, 60, "1204-P~1.IMX"}, /* 14 */
|
||||
{3, 1205, "stateVoodooOut", 0, 0, 60, "1205-V~1.IMX"}, /* 15 */
|
||||
{3, 1210, "stateVoodooIn", 0, 0, 60, "1210-V~1.IMX"}, /* 16 */
|
||||
{12,1212, "stateVoodooInAlt", 0, 1, 42, "1210-V~1.IMX"}, /* 17 */
|
||||
{3, 1215, "stateVoodooLady", 0, 0, 60, "1215-V~1.IMX"}, /* 18 */
|
||||
{3, 1219, "statePrePlundermap", 0, 0, 60, "1219-P~1.IMX"}, /* 19 */
|
||||
{3, 1220, "statePlundermap", 0, 0, 60, "1220-P~1.IMX"}, /* 20 */
|
||||
{3, 1222, "statePreCabana", 0, 0, 60, "1222-P~1.IMX"}, /* 21 */
|
||||
{3, 1223, "stateCabana", 0, 0, 60, "1223-C~1.IMX"}, /* 22 */
|
||||
{3, 1224, "statePostCabana", 23, 0, 60, "1224-P~1.IMX"}, /* 23 */
|
||||
{3, 1225, "stateBeachClub", 23, 0, 60, "1225-B~1.IMX"}, /* 24 */
|
||||
{3, 1230, "stateCliff", 0, 0, 60, "1230-C~1.IMX"}, /* 25 */
|
||||
{3, 1232, "stateBelly", 0, 0, 48, "1232-B~1.IMX"}, /* 26 */
|
||||
{3, 1235, "stateQuicksand", 0, 0, 60, "1235-Q~1.IMX"}, /* 27 */
|
||||
{3, 1240, "stateDangerBeach", 0, 0, 48, "1240-D~1.IMX"}, /* 28 */
|
||||
{12,1241, "stateDangerBeachAlt",0, 2, 48, "1240-D~1.IMX"}, /* 29 */
|
||||
{3, 1245, "stateRowBoat", 0, 0, 60, "1245-R~1.IMX"}, /* 30 */
|
||||
{3, 1247, "stateAlongside", 0, 0, 48, "1247-A~1.IMX"}, /* 31 */
|
||||
{12,1248, "stateAlongsideAlt", 0, 1, 48, "1247-A~1.IMX"}, /* 32 */
|
||||
{3, 1250, "stateChimpBoat", 0, 0, 30, "1250-C~1.IMX"}, /* 33 */
|
||||
{3, 1255, "stateMrFossey", 0, 0, 48, "1255-M~1.IMX"}, /* 34 */
|
||||
{3, 1259, "statePreTown", 0, 0, 60, "1259-P~1.IMX"}, /* 35 */
|
||||
{3, 1260, "stateTown", 0, 0, 60, "1260-T~1.IMX"}, /* 36 */
|
||||
{3, 1264, "statePreMeadow", 0, 0, 60, "1264-P~1.IMX"}, /* 37 */
|
||||
{3, 1265, "stateMeadow", 0, 0, 60, "1265-M~1.IMX"}, /* 38 */
|
||||
{3, 1266, "stateMeadowAmb", 0, 0, 60, "1266-M~1.IMX"}, /* 39 */
|
||||
{3, 1270, "stateWardrobePre", 40, 0, 60, "1270-W~1.IMX"}, /* 40 */
|
||||
{3, 1272, "statePreShow", 40, 0, 60, "1272-P~1.IMX"}, /* 41 */
|
||||
{3, 1274, "stateWardrobeShow", 42, 0, 60, "1274-W~1.IMX"}, /* 42 */
|
||||
{3, 1276, "stateShow", 42, 0, 60, "1276-S~1.IMX"}, /* 43 */
|
||||
{3, 1277, "stateWardrobeJug", 44, 0, 60, "1277-W~1.IMX"}, /* 44 */
|
||||
{3, 1278, "stateJuggling", 44, 0, 60, "1278-J~1.IMX"}, /* 45 */
|
||||
{3, 1279, "statePostShow", 0, 0, 60, "1279-P~1.IMX"}, /* 46 */
|
||||
{3, 1280, "stateChickenShop", 0, 0, 60, "1280-C~1.IMX"}, /* 47 */
|
||||
{3, 1285, "stateBarberShop", 48, 0, 60, "1285-B~1.IMX"}, /* 48 */
|
||||
{3, 1286, "stateVanHelgen", 48, 0, 60, "1286-V~1.IMX"}, /* 49 */
|
||||
{3, 1287, "stateBill", 48, 0, 60, "1287-B~1.IMX"}, /* 50 */
|
||||
{3, 1288, "stateHaggis", 48, 0, 60, "1288-H~1.IMX"}, /* 51 */
|
||||
{3, 1289, "stateRottingham", 48, 0, 60, "1289-R~1.IMX"}, /* 52 */
|
||||
{3, 1305, "stateDeck", 0, 0, 60, "1305-D~1.IMX"}, /* 53 */
|
||||
{3, 1310, "stateCombatMap", 0, 0, 60, "1310-C~1.IMX"}, /* 54 */
|
||||
{3, 1320, "stateShipCombat", 0, 0, 60, "1320-S~1.IMX"}, /* 55 */
|
||||
{3, 1325, "stateSwordfight", 0, 0, 60, "1325-S~1.IMX"}, /* 56 */
|
||||
{3, 1327, "stateSwordRott", 0, 0, 60, "1327-S~1.IMX"}, /* 57 */
|
||||
{3, 1330, "stateTownEdge", 0, 0, 60, "1330-T~1.IMX"}, /* 58 */
|
||||
{3, 1335, "stateSwordLose", 0, 0, 60, "1335-S~1.IMX"}, /* 59 */
|
||||
{3, 1340, "stateSwordWin", 0, 0, 60, "1340-S~1.IMX"}, /* 60 */
|
||||
{3, 1345, "stateGetMap", 0, 0, 60, "1345-G~1.IMX"}, /* 61 */
|
||||
{3, 1400, "stateWreckBeach", 0, 0, 60, "1400-W~1.IMX"}, /* 62 */
|
||||
{3, 1405, "stateBloodMap", 63, 0, 60, "1405-B~1.IMX"}, /* 63 */
|
||||
{3, 1410, "stateClearing", 0, 0, 60, "1410-C~1.IMX"}, /* 64 */
|
||||
{3, 1415, "stateLighthouse", 63, 0, 60, "1415-L~1.IMX"}, /* 65 */
|
||||
{3, 1420, "stateVillage", 66, 0, 60, "1420-V~1.IMX"}, /* 66 */
|
||||
{3, 1423, "stateVolcano", 66, 0, 60, "1423-V~1.IMX"}, /* 67 */
|
||||
{3, 1425, "stateAltar", 66, 0, 60, "1425-A~1.IMX"}, /* 68 */
|
||||
{3, 1430, "stateHotelOut", 0, 0, 60, "1430-H~1.IMX"}, /* 69 */
|
||||
{3, 1435, "stateHotelBar", 70, 0, 60, "1435-H~1.IMX"}, /* 70 */
|
||||
{3, 1440, "stateHotelIn", 70, 0, 60, "1440-H~1.IMX"}, /* 71 */
|
||||
{3, 1445, "stateTarotLady", 70, 0, 60, "1445-T~1.IMX"}, /* 72 */
|
||||
{3, 1447, "stateGoodsoup", 70, 0, 60, "1447-G~1.IMX"}, /* 73 */
|
||||
{3, 1448, "stateGuestRoom", 0, 0, 60, "1448-G~1.IMX"}, /* 74 */
|
||||
{3, 1450, "stateWindmill", 63, 0, 60, "1450-W~1.IMX"}, /* 75 */
|
||||
{3, 1455, "stateCemetary", 0, 0, 60, "1455-C~1.IMX"}, /* 76 */
|
||||
{3, 1460, "stateCrypt", 77, 0, 60, "1460-C~1.IMX"}, /* 77 */
|
||||
{3, 1463, "stateGraveDigger", 77, 0, 60, "1463-G~1.IMX"}, /* 78 */
|
||||
{3, 1465, "stateMonkey1", 0, 0, 60, "1465-M~1.IMX"}, /* 79 */
|
||||
{3, 1475, "stateStanDark", 0, 0, 60, "1475-S~1.IMX"}, /* 80 */
|
||||
{3, 1477, "stateStanLight", 0, 0, 60, "1477-S~1.IMX"}, /* 81 */
|
||||
{3, 1480, "stateEggBeach", 63, 0, 60, "1480-E~1.IMX"}, /* 82 */
|
||||
{3, 1485, "stateSkullIsland", 0, 0, 60, "1485-S~1.IMX"}, /* 83 */
|
||||
{3, 1490, "stateSmugglersCave", 0, 0, 60, "1490-S~1.IMX"}, /* 84 */
|
||||
{3, 1500, "stateLeChuckTalk", 0, 0, 60, "1500-L~1.IMX"}, /* 85 */
|
||||
{3, 1505, "stateCarnival", 0, 0, 60, "1505-C~1.IMX"}, /* 86 */
|
||||
{3, 1511, "stateHang", 87, 0, 60, "1511-H~1.IMX"}, /* 87 */
|
||||
{3, 1512, "stateRum", 87, 0, 60, "1512-RUM.IMX"}, /* 88 */
|
||||
{3, 1513, "stateTorture", 87, 0, 60, "1513-T~1.IMX"}, /* 89 */
|
||||
{3, 1514, "stateSnow", 87, 0, 60, "1514-S~1.IMX"}, /* 90 */
|
||||
{3, 1515, "stateCredits", 0, 0, 60, "1515-C~1.IMX"}, /* 91 */
|
||||
{3, 1520, "stateCarnAmb", 0, 0, 60, "1520-C~1.IMX"}, /* 92 */
|
||||
{0, -1, "", 0, 0, 0, "", }
|
||||
};
|
||||
|
||||
const imuseComiTable _comiSeqMusicTable[] = {
|
||||
{0, 2000, "SEQ_NULL", 0, 0, 0, "" },
|
||||
{0, 2100, "seqINTRO", 0, 0, 0, "" },
|
||||
{3, 2105, "seqInterlude1", 0, 0, 60, "2105-I~1.IMX"},
|
||||
{8, 2110, "seqLastBoat", 0, 1, 0, "" },
|
||||
{0, 2115, "seqSINK_SHIP", 0, 0, 0, "" },
|
||||
{0, 2120, "seqCURSED_RING", 0, 0, 60, "" },
|
||||
{3, 2200, "seqInterlude2", 0, 0, 60, "2200-I~1.IMX"},
|
||||
{3, 2210, "seqKidnapped", 0, 0, 60, "2210-K~1.IMX"},
|
||||
{8, 2220, "seqSnakeVomits", 0, 1, 0, "" },
|
||||
{8, 2222, "seqPopBalloon", 0, 1, 0, "" },
|
||||
{3, 2225, "seqDropBalls", 0, 0, 60, "2225-D~1.IMX"},
|
||||
{4, 2232, "seqArriveBarber", 0, 0, 60, "2232-A~1.IMX"},
|
||||
{3, 2233, "seqAtonal", 0, 0, 60, "2233-A~1.IMX"},
|
||||
{3, 2235, "seqShaveHead1", 0, 0, 60, "2235-S~1.IMX"},
|
||||
{2, 2236, "seqShaveHead2", 0, 2, 60, "2235-S~1.IMX"},
|
||||
{3, 2245, "seqCaberLose", 0, 0, 60, "2245-C~1.IMX"},
|
||||
{3, 2250, "seqCaberWin", 0, 0, 60, "2250-C~1.IMX"},
|
||||
{3, 2255, "seqDuel1", 0, 0, 60, "2255-D~1.IMX"},
|
||||
{2, 2256, "seqDuel2", 0, 2, 60, "2255-D~1.IMX"},
|
||||
{2, 2257, "seqDuel3", 0, 3, 60, "2255-D~1.IMX"},
|
||||
{3, 2260, "seqBlowUpTree1", 0, 0, 60, "2260-B~1.IMX"},
|
||||
{2, 2261, "seqBlowUpTree2", 0, 2, 60, "2260-B~1.IMX"},
|
||||
{3, 2275, "seqMonkeys", 0, 0, 60, "2275-M~1.IMX"},
|
||||
{9, 2277, "seqAttack", 0, 1, 0, "" },
|
||||
{3, 2285, "seqSharks", 0, 0, 60, "2285-S~1.IMX"},
|
||||
{3, 2287, "seqTowelWalk", 0, 0, 60, "2287-T~1.IMX"},
|
||||
{0, 2293, "seqNICE_BOOTS", 0, 0, 0, "" },
|
||||
{0, 2295, "seqBIG_BONED", 0, 0, 0, "" },
|
||||
{3, 2300, "seqToBlood", 0, 0, 60, "2300-T~1.IMX"},
|
||||
{3, 2301, "seqInterlude3", 0, 0, 60, "2301-I~1.IMX"},
|
||||
{3, 2302, "seqRott1", 0, 0, 60, "2302-R~1.IMX"},
|
||||
{2, 2304, "seqRott2", 0, 2, 60, "2302-R~1.IMX"},
|
||||
{2, 2305, "seqRott2b", 0,21, 60, "2302-R~1.IMX"},
|
||||
{2, 2306, "seqRott3", 0, 3, 60, "2302-R~1.IMX"},
|
||||
{2, 2308, "seqRott4", 0, 4, 60, "2302-R~1.IMX"},
|
||||
{2, 2309, "seqRott5", 0, 5, 60, "2302-R~1.IMX"},
|
||||
{3, 2311, "seqVerse1", 0, 0, 60, "2311-S~1.IMX"},
|
||||
{2, 2312, "seqVerse2", 0, 2, 60, "2311-S~1.IMX"},
|
||||
{2, 2313, "seqVerse3", 0, 3, 60, "2311-S~1.IMX"},
|
||||
{2, 2314, "seqVerse4", 0, 4, 60, "2311-S~1.IMX"},
|
||||
{2, 2315, "seqVerse5", 0, 5, 60, "2311-S~1.IMX"},
|
||||
{2, 2316, "seqVerse6", 0, 6, 60, "2311-S~1.IMX"},
|
||||
{2, 2317, "seqVerse7", 0, 7, 60, "2311-S~1.IMX"},
|
||||
{2, 2318, "seqVerse8", 0, 8, 60, "2311-S~1.IMX"},
|
||||
{2, 2319, "seqSongEnd", 0, 9, 60, "2311-S~1.IMX"},
|
||||
{2, 2336, "seqRiposteLose", 0, 0, 60, "2336-R~1.IMX"},
|
||||
{2, 2337, "seqRiposteWin", 0, 0, 60, "2337-R~1.IMX"},
|
||||
{2, 2338, "seqInsultLose", 0, 0, 60, "2338-I~1.IMX"},
|
||||
{2, 2339, "seqInsultWin", 0, 0, 60, "2339-I~1.IMX"},
|
||||
{3, 2340, "seqSwordLose", 0, 0, 60, "1335-S~1.IMX"},
|
||||
{3, 2345, "seqSwordWin", 0, 0, 60, "1340-S~1.IMX"},
|
||||
{3, 2347, "seqGetMap", 0, 0, 60, "1345-G~1.IMX"},
|
||||
{3, 2400, "seqInterlude4", 0, 0, 60, "2400-I~1.IMX"},
|
||||
{0, 2405, "seqSHIPWRECK", 0, 0, 0, "" },
|
||||
{3, 2408, "seqFakeCredits", 0, 0, 60, "2408-F~1.IMX"},
|
||||
{3, 2410, "seqPassOut", 0, 0, 60, "2410-P~1.IMX"},
|
||||
{3, 2414, "seqGhostTalk", 0, 0, 60, "2414-G~1.IMX"},
|
||||
{2, 2415, "seqGhostWedding", 0, 1, 60, "2414-G~1.IMX"},
|
||||
{3, 2420, "seqEruption", 0, 0, 60, "2420-E~1.IMX"},
|
||||
{3, 2425, "seqSacrifice", 0, 0, 60, "2425-S~1.IMX"},
|
||||
{2, 2426, "seqSacrificeEnd", 0, 1, 60, "2425-S~1.IMX"},
|
||||
{3, 2430, "seqScareDigger", 0, 0, 60, "2430-S~1.IMX"},
|
||||
{3, 2445, "seqSkullArrive", 0, 0, 60, "2445-S~1.IMX"},
|
||||
{3, 2450, "seqFloat", 0, 0, 60, "2450-C~1.IMX"},
|
||||
{2, 2451, "seqFall", 0, 1, 60, "2450-C~1.IMX"},
|
||||
{2, 2452, "seqUmbrella", 0, 2, 60, "2450-C~1.IMX"},
|
||||
{3, 2460, "seqFight", 0, 0, 60, "2460-F~1.IMX"},
|
||||
{0, 2465, "seqLAVE_RIDE", 0, 0, 0, "" },
|
||||
{0, 2470, "seqMORE_SLAW", 0, 0, 0, "" },
|
||||
{0, 2475, "seqLIFT_CURSE", 0, 0, 0, "" },
|
||||
{3, 2500, "seqInterlude5", 0, 0, 60, "2500-I~1.IMX"},
|
||||
{3, 2502, "seqExitSkycar", 0, 0, 60, "2502-E~1.IMX"},
|
||||
{3, 2504, "seqGrow1", 0, 0, 60, "2504-G~1.IMX"},
|
||||
{2, 2505, "seqGrow2", 0, 1, 60, "2504-G~1.IMX"},
|
||||
{3, 2508, "seqInterlude6", 0, 0, 60, "2508-I~1.IMX"},
|
||||
{0, 2515, "seqFINALE", 0, 0, 0, "" },
|
||||
{3, 2520, "seqOut", 0, 0, 60, "2520-OUT.IMX"},
|
||||
{3, 2530, "seqZap1a", 0, 0, 60, "2530-Z~1.IMX"},
|
||||
{2, 2531, "seqZap1b", 0, 1, 60, "2530-Z~1.IMX"},
|
||||
{2, 2532, "seqZap1c", 0, 2, 60, "2530-Z~1.IMX"},
|
||||
{2, 2540, "seqZap2a", 0, 0, 60, "2540-Z~1.IMX"},
|
||||
{2, 2541, "seqZap2b", 0, 1, 60, "2540-Z~1.IMX"},
|
||||
{2, 2542, "seqZap2c", 0, 2, 60, "2540-Z~1.IMX"},
|
||||
{3, 2550, "seqZap3a", 0, 0, 60, "2550-Z~1.IMX"},
|
||||
{2, 2551, "seqZap3b", 0, 1, 60, "2550-Z~1.IMX"},
|
||||
{2, 2552, "seqZap3c", 0, 2, 60, "2550-Z~1.IMX"},
|
||||
{3, 2560, "seqZap4a", 0, 0, 60, "2560-Z~1.IMX"},
|
||||
{2, 2561, "seqZap4b", 0, 1, 60, "2560-Z~1.IMX"},
|
||||
{2, 2562, "seqZap4c", 0, 2, 60, "2560-Z~1.IMX"},
|
||||
{0, -1, "", 0, 0, 0, "" }
|
||||
};
|
||||
|
||||
const imuseComiTable _comiDemoStateMusicTable[] = {
|
||||
{0, 1000, "STATE_NULL", 0, 0, 0, "", }, /* 00 */
|
||||
{0, 1001, "stateNoChange", 0, 0, 0, "", }, /* 01 */
|
||||
{3, 1100, "stateHold1", 2, 0, 60, "in1.imx" }, /* 02 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 03 */
|
||||
{3, 1120, "stateHold2", 4, 0, 60, "in2.imx" }, /* 04 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 05 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 06 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 07 */
|
||||
{3, 1140, "stateWaterline1", 2, 0, 60, "out1.imx"}, /* 08 */
|
||||
{3, 1150, "stateWaterline2", 4, 0, 60, "out2.imx"}, /* 09 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 10 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 11 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 12 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 13 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 14 */
|
||||
{0, 1001, "empty", 0, 0, 0, "", }, /* 15 */
|
||||
{3, 1210, "stateCannon", 4, 0, 60, "gun.imx" }, /* 16 */
|
||||
{0, -1, "", 0, 0, 0, "", }
|
||||
};
|
||||
|
||||
const imuseFtStateTable _ftStateMusicTable[] = {
|
||||
{"", 0, 0, "STATE_NULL" },
|
||||
{"", 4, 127, "stateKstandOutside" },
|
||||
{"kinside", 2, 127, "stateKstandInside" },
|
||||
{"moshop", 3, 64, "stateMoesInside" },
|
||||
{"melcut", 2, 127, "stateMoesOutside" },
|
||||
{"mellover", 2, 127, "stateMellonAbove" },
|
||||
{"radloop", 3, 28, "stateTrailerOutside" },
|
||||
{"radloop", 3, 58, "stateTrailerInside" },
|
||||
{"radloop", 3, 127, "stateTodShop" },
|
||||
{"junkgate", 2, 127, "stateJunkGate" },
|
||||
{"junkover", 3, 127, "stateJunkAbove" },
|
||||
{"gastower", 2, 127, "stateGasTower" },
|
||||
{"", 4, 0, "stateTowerAlarm" },
|
||||
{"melcut", 2, 127, "stateCopsOnGround" },
|
||||
{"melcut", 2, 127, "stateCopsAround" },
|
||||
{"melcut", 2, 127, "stateMoesRuins" },
|
||||
{"melcut", 2, 127, "stateKstandNight" },
|
||||
{"trukblu2", 2, 127, "stateTruckerTalk" },
|
||||
{"stretch", 2, 127, "stateMumblyPeg" },
|
||||
{"kstand", 2, 100, "stateRanchOutside" },
|
||||
{"kinside", 2, 127, "stateRanchInside" },
|
||||
{"desert", 2, 127, "stateWreckedTruck" },
|
||||
{"opening", 2, 100, "stateGorgeVista" },
|
||||
{"caveopen", 2, 127, "stateCaveOpen" },
|
||||
{"cavecut1", 2, 127, "stateCaveOuter" },
|
||||
{"cavecut1", 1, 127, "stateCaveMiddle" },
|
||||
{"cave", 2, 127, "stateCaveInner" },
|
||||
{"corville", 2, 127, "stateCorvilleFront" },
|
||||
{"mines", 2, 127, "stateMineField" },
|
||||
{"bunyman3", 2, 127, "stateBunnyStore" },
|
||||
{"stretch", 2, 127, "stateStretchBen" },
|
||||
{"saveme", 2, 127, "stateBenPleas" },
|
||||
{"", 4, 0, "stateBenConvinces" },
|
||||
{"derby", 3, 127, "stateDemoDerby" },
|
||||
{"fire", 3, 127, "stateLightMyFire" },
|
||||
{"derby", 3, 127, "stateDerbyChase" },
|
||||
{"carparts", 2, 127, "stateVultureCarParts"},
|
||||
{"cavecut1", 2, 127, "stateVulturesInside" },
|
||||
{"mines", 2, 127, "stateFactoryRear" },
|
||||
{"croffice", 2, 127, "stateCorleyOffice" },
|
||||
{"melcut", 2, 127, "stateCorleyHall" },
|
||||
{"", 4, 0, "stateProjRoom" },
|
||||
{"", 4, 0, "stateMMRoom" },
|
||||
{"bumper", 2, 127, "stateBenOnBumper" },
|
||||
{"benump", 2, 127, "stateBenOnBack" },
|
||||
{"plane", 2, 127, "stateInCargoPlane" },
|
||||
{"saveme", 2, 127, "statePlaneControls" },
|
||||
{"", 4, 0, "stateCliffHanger1" },
|
||||
{"", 4, 0, "stateCliffHanger2" },
|
||||
{"", 0, 0, "" },
|
||||
};
|
||||
|
||||
const imuseFtNames _ftSeqNames[] = {
|
||||
{"SEQ_NULL" },
|
||||
{"seqLogo" },
|
||||
{"seqOpenFlick" },
|
||||
{"seqBartender" },
|
||||
{"seqBenWakes" },
|
||||
{"seqPhotoScram" },
|
||||
{"seqClimbChain" },
|
||||
{"seqDogChase" },
|
||||
{"seqDogSquish" },
|
||||
{"seqDogHoist" },
|
||||
{"seqCopsArrive" },
|
||||
{"seqCopsLand" },
|
||||
{"seqCopsLeave" },
|
||||
{"seqCopterFlyby" },
|
||||
{"seqCopterCrash" },
|
||||
{"seqMoGetsParts" },
|
||||
{"seqMoFixesBike" },
|
||||
{"seqFirstGoodbye" },
|
||||
{"seqCopRoadblock" },
|
||||
{"seqDivertCops" },
|
||||
{"seqMurder" },
|
||||
{"seqCorleyDies" },
|
||||
{"seqTooLateAtMoes" },
|
||||
{"seqPicture" },
|
||||
{"seqNewsReel" },
|
||||
{"seqCopsInspect" },
|
||||
{"seqHijack" },
|
||||
{"seqNestolusAtRanch" },
|
||||
{"seqRipLimo" },
|
||||
{"seqGorgeTurn" },
|
||||
{"seqStealRamp" },
|
||||
{"seqCavefishTalk" },
|
||||
{"seqArriveCorville" },
|
||||
{"seqSingleBunny" },
|
||||
{"seqBunnyArmy" },
|
||||
{"seqArriveAtMines" },
|
||||
{"seqArriveAtVultures"},
|
||||
{"seqMakePlan" },
|
||||
{"seqShowPlan" },
|
||||
{"seqDerbyStart" },
|
||||
{"seqLightBales" },
|
||||
{"seqNestolusBBQ" },
|
||||
{"seqCallSecurity" },
|
||||
{"seqFilmFail" },
|
||||
{"seqFilmBurn" },
|
||||
{"seqRipSpeech" },
|
||||
{"seqExposeRip" },
|
||||
{"seqRipEscape" },
|
||||
{"seqRareMoment" },
|
||||
{"seqFanBunnies" },
|
||||
{"seqRipDead" },
|
||||
{"seqFuneral" },
|
||||
{"seqCredits" },
|
||||
{"" }
|
||||
};
|
||||
|
||||
const imuseFtSeqTable _ftSeqMusicTable[] = {
|
||||
{"", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"opening", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"barbeat", 2, 127},
|
||||
{"barwarn", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0, },
|
||||
|
||||
{"benwakes", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"barwarn", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"swatben", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"dogattak", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"", 4, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"", 4, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"cops2", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"cops2", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"cops2", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"bunymrch", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"", 4, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"", 0, 0 },
|
||||
{"melcut", 2, 127},
|
||||
{"tada", 2, 127},
|
||||
{"", 0, 0 },
|
||||
|
||||
{"", 4, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"trucker", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"cops2", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"barwarn", 2, 127},
|
||||
{"murder", 2, 127},
|
||||
{"murder2", 2, 127},
|
||||
{"", 0, 0 },
|
||||
|
||||
{"corldie", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"barwarn", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"picture", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"ripintro", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"trucker", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"hosed", 2, 127},
|
||||
|
||||
{"ripdead", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"nesranch", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"scolding", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"desert", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"cavecut1", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"caveamb", 2, 80 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"castle", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"bunymrch", 2, 105},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"valkyrs", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"melcut", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"vultures", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"sorry", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"makeplan", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"castle", 2, 127},
|
||||
{"derby", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"fire", 3, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"saveme", 3, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"scolding", 2, 127},
|
||||
|
||||
{"cops2", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"sorry", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"sorry", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"caveamb", 2, 85 },
|
||||
{"tada", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"expose", 2, 127},
|
||||
{"", 4, 0 },
|
||||
{"", 0, 0 },
|
||||
{"mocoup", 2, 127},
|
||||
|
||||
{"ripscram", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"valkyrs", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"ripdead", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"funeral", 2, 127},
|
||||
{"", 0, 0 },
|
||||
{"moshop", 3, 64 },
|
||||
{"", 0, 0 },
|
||||
|
||||
{"bornbad", 2, 127},
|
||||
{"hammvox", 2, 127},
|
||||
{"legavox", 2, 127},
|
||||
{"chances", 2, 90 },
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
87
engines/scumm/imuse_digi/dimuse_tables.h
Normal file
87
engines/scumm/imuse_digi/dimuse_tables.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(SCUMM_IMUSE_DIGI_TABLES_H) && defined(ENABLE_SCUMM_7_8)
|
||||
#define SCUMM_IMUSE_DIGI_TABLES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
struct imuseRoomMap {
|
||||
int8 roomId;
|
||||
byte stateIndex1;
|
||||
byte offset;
|
||||
byte stateIndex2;
|
||||
byte attribPos;
|
||||
byte stateIndex3;
|
||||
};
|
||||
|
||||
struct imuseDigTable {
|
||||
byte transitionType;
|
||||
int16 soundId;
|
||||
char name[20];
|
||||
byte attribPos;
|
||||
byte hookId;
|
||||
char filename[13];
|
||||
};
|
||||
|
||||
struct imuseComiTable {
|
||||
byte transitionType;
|
||||
int16 soundId;
|
||||
char name[20];
|
||||
byte attribPos;
|
||||
byte hookId;
|
||||
int16 fadeOutDelay;
|
||||
char filename[13];
|
||||
};
|
||||
|
||||
|
||||
struct imuseFtNames {
|
||||
char name[20];
|
||||
};
|
||||
|
||||
struct imuseFtStateTable {
|
||||
char audioName[9];
|
||||
byte transitionType;
|
||||
byte volume;
|
||||
char name[21];
|
||||
};
|
||||
|
||||
struct imuseFtSeqTable {
|
||||
char audioName[9];
|
||||
byte transitionType;
|
||||
byte volume;
|
||||
};
|
||||
|
||||
extern const imuseRoomMap _digStateMusicMap[];
|
||||
extern const imuseDigTable _digStateMusicTable[];
|
||||
extern const imuseDigTable _digSeqMusicTable[];
|
||||
extern const imuseComiTable _comiStateMusicTable[];
|
||||
extern const imuseComiTable _comiSeqMusicTable[];
|
||||
extern const imuseComiTable _comiDemoStateMusicTable[];
|
||||
extern const imuseFtStateTable _ftStateMusicTable[];
|
||||
extern const imuseFtSeqTable _ftSeqMusicTable[];
|
||||
extern const imuseFtNames _ftSeqNames[];
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
||||
#endif
|
||||
821
engines/scumm/imuse_digi/dimuse_tracks.cpp
Normal file
821
engines/scumm/imuse_digi/dimuse_tracks.cpp
Normal file
@@ -0,0 +1,821 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
int IMuseDigital::tracksInit() {
|
||||
_trackCount = _lowLatencyMode ? DIMUSE_MAX_TRACKS : 6;
|
||||
_tracksPauseTimer = 0;
|
||||
_trackList = nullptr;
|
||||
|
||||
if (waveOutInit(&waveOutSettings))
|
||||
return -1;
|
||||
|
||||
if (_internalMixer->init(waveOutSettings.bytesPerSample,
|
||||
waveOutSettings.numChannels,
|
||||
waveOutSettings.mixBuf,
|
||||
waveOutSettings.mixBufSize,
|
||||
waveOutSettings.sizeSampleKB,
|
||||
_trackCount) ||
|
||||
dispatchInit() ||
|
||||
streamerInit()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int l = 0; l < _trackCount; l++) {
|
||||
_tracks[l].index = l;
|
||||
_tracks[l].prev = nullptr;
|
||||
_tracks[l].next = nullptr;
|
||||
_tracks[l].dispatchPtr = dispatchGetDispatchByTrackId(l);
|
||||
_tracks[l].dispatchPtr->trackPtr = &_tracks[l];
|
||||
_tracks[l].soundId = 0;
|
||||
_tracks[l].group = 0;
|
||||
_tracks[l].marker = 0;
|
||||
_tracks[l].priority = 0;
|
||||
_tracks[l].vol = 0;
|
||||
_tracks[l].effVol = 0;
|
||||
_tracks[l].pan = 0;
|
||||
_tracks[l].detune = 0;
|
||||
_tracks[l].transpose = 0;
|
||||
_tracks[l].pitchShift = 0;
|
||||
_tracks[l].mailbox = 0;
|
||||
_tracks[l].jumpHook = 0;
|
||||
_tracks[l].syncSize_0 = 0;
|
||||
_tracks[l].syncSize_1 = 0;
|
||||
_tracks[l].syncSize_2 = 0;
|
||||
_tracks[l].syncSize_3 = 0;
|
||||
_tracks[l].syncPtr_0 = nullptr;
|
||||
_tracks[l].syncPtr_1 = nullptr;
|
||||
_tracks[l].syncPtr_2 = nullptr;
|
||||
_tracks[l].syncPtr_3 = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigital::tracksPause() {
|
||||
_tracksPauseTimer = 1;
|
||||
}
|
||||
|
||||
void IMuseDigital::tracksResume() {
|
||||
_tracksPauseTimer = 0;
|
||||
}
|
||||
|
||||
void IMuseDigital::tracksSaveLoad(Common::Serializer &ser) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
dispatchSaveLoad(ser);
|
||||
|
||||
for (int l = 0; l < _trackCount; l++) {
|
||||
ser.syncAsSint32LE(_tracks[l].soundId, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].marker, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].group, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].priority, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].vol, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].effVol, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].pan, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].detune, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].transpose, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].pitchShift, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].mailbox, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].jumpHook, VER(103));
|
||||
|
||||
if (_vm->_game.id == GID_CMI) {
|
||||
ser.syncAsSint32LE(_tracks[l].syncSize_0, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].syncSize_1, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].syncSize_2, VER(103));
|
||||
ser.syncAsSint32LE(_tracks[l].syncSize_3, VER(103));
|
||||
|
||||
if (_tracks[l].syncSize_0) {
|
||||
if (ser.isLoading())
|
||||
_tracks[l].syncPtr_0 = (byte *)malloc(_tracks[l].syncSize_0);
|
||||
ser.syncArray(_tracks[l].syncPtr_0, _tracks[l].syncSize_0, Common::Serializer::Byte, VER(103));
|
||||
}
|
||||
|
||||
if (_tracks[l].syncSize_1) {
|
||||
if (ser.isLoading())
|
||||
_tracks[l].syncPtr_1 = (byte *)malloc(_tracks[l].syncSize_1);
|
||||
ser.syncArray(_tracks[l].syncPtr_1, _tracks[l].syncSize_1, Common::Serializer::Byte, VER(103));
|
||||
}
|
||||
|
||||
if (_tracks[l].syncSize_2) {
|
||||
if (ser.isLoading())
|
||||
_tracks[l].syncPtr_2 = (byte *)malloc(_tracks[l].syncSize_2);
|
||||
ser.syncArray(_tracks[l].syncPtr_2, _tracks[l].syncSize_2, Common::Serializer::Byte, VER(103));
|
||||
}
|
||||
|
||||
if (_tracks[l].syncSize_3) {
|
||||
if (ser.isLoading())
|
||||
_tracks[l].syncPtr_3 = (byte *)malloc(_tracks[l].syncSize_3);
|
||||
ser.syncArray(_tracks[l].syncPtr_3, _tracks[l].syncSize_3, Common::Serializer::Byte, VER(103));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ser.isLoading()) {
|
||||
for (int l = 0; l < _trackCount; l++) {
|
||||
_tracks[l].prev = nullptr;
|
||||
_tracks[l].next = nullptr;
|
||||
_tracks[l].dispatchPtr = dispatchGetDispatchByTrackId(l);
|
||||
_tracks[l].dispatchPtr->trackPtr = &_tracks[l];
|
||||
if (_tracks[l].soundId) {
|
||||
addTrackToList(&_trackList, &_tracks[l]);
|
||||
}
|
||||
}
|
||||
|
||||
dispatchRestoreStreamZones();
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::tracksSetGroupVol() {
|
||||
IMuseDigiTrack* curTrack = _trackList;
|
||||
while (curTrack) {
|
||||
curTrack->effVol = ((curTrack->vol + 1) * _groupsHandler->getGroupVol(curTrack->group)) / 128;
|
||||
curTrack = curTrack->next;
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::tracksCallback() {
|
||||
if (_tracksPauseTimer) {
|
||||
if (++_tracksPauseTimer < 3)
|
||||
return;
|
||||
_tracksPauseTimer = 3;
|
||||
}
|
||||
|
||||
// This piece of code is responsible for adaptive buffer overrun correction:
|
||||
// it checks whether a buffer underrun has occurred within our output stream
|
||||
// and then it increments the buffer count.
|
||||
//
|
||||
// This is not part of the original implementation, but it's used to yield
|
||||
// smooth audio hopefully on every device.
|
||||
if (_internalMixer->_stream->endOfData() && _checkForUnderrun) {
|
||||
debug(5, "IMuseDigital::tracksCallback(): WARNING: audio buffer underrun, adapting the buffer queue count...");
|
||||
|
||||
adaptBufferCount();
|
||||
|
||||
// Allow the routine to cooldown: i.e. wait until the engine manages to
|
||||
// refill the stream with the most recent maximum number of queueable buffers.
|
||||
_underrunCooldown = _maxQueuedStreams;
|
||||
_checkForUnderrun = false;
|
||||
}
|
||||
|
||||
// If we leave the number of queued streams unbounded, we fill the queue with streams faster than
|
||||
// we can play them: this leads to a very noticeable audio latency and desync with the graphics.
|
||||
if ((int)_internalMixer->_stream->numQueuedStreams() < _maxQueuedStreams) {
|
||||
if (!_isEarlyDiMUSE)
|
||||
dispatchPredictFirstStream();
|
||||
|
||||
waveOutWrite(&_outputAudioBuffer, _outputFeedSize, _outputSampleRate);
|
||||
|
||||
if (_outputFeedSize != 0) {
|
||||
// Let's see if we should check for buffer underruns...
|
||||
if (!_checkForUnderrun) {
|
||||
if (_underrunCooldown == 0) {
|
||||
_checkForUnderrun = true;
|
||||
} else {
|
||||
_underrunCooldown--;
|
||||
}
|
||||
}
|
||||
|
||||
_internalMixer->clearMixerBuffer();
|
||||
if (_isEarlyDiMUSE && _splayer && _splayer->isAudioCallbackEnabled()) {
|
||||
_splayer->processDispatches(_outputFeedSize);
|
||||
}
|
||||
|
||||
if (!_tracksPauseTimer) {
|
||||
IMuseDigiTrack *track = _trackList;
|
||||
|
||||
while (track) {
|
||||
IMuseDigiTrack *next = track->next;
|
||||
if (_isEarlyDiMUSE) {
|
||||
dispatchProcessDispatches(track, _outputFeedSize);
|
||||
} else {
|
||||
dispatchProcessDispatches(track, _outputFeedSize, _outputSampleRate);
|
||||
}
|
||||
track = next;
|
||||
};
|
||||
}
|
||||
|
||||
_internalMixer->loop(&_outputAudioBuffer, _outputFeedSize);
|
||||
|
||||
// The Dig tries to write a second time
|
||||
if (!_isEarlyDiMUSE && _vm->_game.id == GID_DIG) {
|
||||
waveOutWrite(&_outputAudioBuffer, _outputFeedSize, _outputSampleRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::tracksLowLatencyCallback() {
|
||||
// Why do we need a low latency mode?
|
||||
//
|
||||
// For every audio callback, this engine works by collecting all the sound
|
||||
// data for every track and by mixing it up in a single output stream.
|
||||
// This is exactly how the original executables worked, so our implementation
|
||||
// provides a very faithful recreation of that experience. And it comes with
|
||||
// a compromise that e.g. The Dig and Full Throttle didn't have to front:
|
||||
//
|
||||
// in order to provide glitchless audio, an appropriate stream queue size
|
||||
// has to be enforced: a longer queue yields a lower probability of audio glitches
|
||||
// but a higher latency, and viceversa. In our case, this depends on the audio backend
|
||||
// configuration. As such: some configurations might encounter audible latency (#13462).
|
||||
//
|
||||
// We solve this issue by offering this alternate low latency mode which, instead
|
||||
// of keeping a single stream for everything, creates (and disposes) streams on the fly
|
||||
// for every different sound. This means that whenever the new sound data is ready,
|
||||
// a new stream is initialized and played immediately, without having to wait for all
|
||||
// the other sounds to be processed and mixed in the same sample pool.
|
||||
|
||||
if (_tracksPauseTimer) {
|
||||
if (++_tracksPauseTimer < 3)
|
||||
return;
|
||||
_tracksPauseTimer = 3;
|
||||
}
|
||||
|
||||
// The callback path is heavily inspired from the original one (see tracksCallback()),
|
||||
// but it handles each track separatedly, with the exception of SMUSH audio for Full
|
||||
// Throttle: this is why we operate on two parallel paths...
|
||||
|
||||
if (!_isEarlyDiMUSE)
|
||||
dispatchPredictFirstStream();
|
||||
|
||||
IMuseDigiTrack *track = _trackList;
|
||||
|
||||
// This flag ensures that, even when no track is available,
|
||||
// FT SMUSH audio can still be played. At least, and AT MOST once :-)
|
||||
bool runSMUSHAudio = _isEarlyDiMUSE;
|
||||
|
||||
while (track || runSMUSHAudio) {
|
||||
|
||||
IMuseDigiTrack *next = track ? track->next : nullptr;
|
||||
int idx = track ? track->index : -1;
|
||||
|
||||
// We use a separate queue cardinality handling, since SMUSH audio and iMUSE audio can overlap...
|
||||
bool canQueueBufs = (int)_internalMixer->getStream(idx)->numQueuedStreams() < (_maxQueuedStreams + 1);
|
||||
bool canQueueFtSmush = _internalMixer->getStream(-1) != nullptr;
|
||||
|
||||
if (canQueueFtSmush) {
|
||||
canQueueFtSmush &= (int)_internalMixer->getStream(-1)->numQueuedStreams() < (_maxQueuedStreams + 1);
|
||||
}
|
||||
|
||||
if (canQueueBufs) {
|
||||
if (track)
|
||||
waveOutLowLatencyWrite(&_outputLowLatencyAudioBuffers[idx], _outputFeedSize, _outputSampleRate, idx);
|
||||
|
||||
// Notice how SMUSH audio for Full Throttle uses the original single-stream mode:
|
||||
// this is necessary both for code cleanliness and for correct audio sync.
|
||||
if (runSMUSHAudio && canQueueFtSmush)
|
||||
waveOutWrite(&_outputAudioBuffer, _outputFeedSize, _outputSampleRate);
|
||||
|
||||
if (_outputFeedSize != 0) {
|
||||
// FT SMUSH dispatch processing...
|
||||
if (runSMUSHAudio && canQueueFtSmush && _isEarlyDiMUSE && _splayer && _splayer->isAudioCallbackEnabled()) {
|
||||
_internalMixer->setCurrentMixerBuffer(_outputAudioBuffer);
|
||||
_internalMixer->clearMixerBuffer();
|
||||
|
||||
_splayer->processDispatches(_outputFeedSize);
|
||||
_internalMixer->loop(&_outputAudioBuffer, _outputFeedSize);
|
||||
}
|
||||
|
||||
// Ordinary audio tracks handling...
|
||||
if (track) {
|
||||
_internalMixer->setCurrentMixerBuffer(_outputLowLatencyAudioBuffers[idx]);
|
||||
_internalMixer->clearMixerBuffer();
|
||||
|
||||
if (!_tracksPauseTimer) {
|
||||
if (_isEarlyDiMUSE) {
|
||||
dispatchProcessDispatches(track, _outputFeedSize);
|
||||
} else {
|
||||
dispatchProcessDispatches(track, _outputFeedSize, _outputSampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_internalMixer->loop(&_outputLowLatencyAudioBuffers[idx], _outputFeedSize);
|
||||
|
||||
// The Dig tries to write a second time
|
||||
if (!_isEarlyDiMUSE && _vm->_game.id == GID_DIG) {
|
||||
waveOutLowLatencyWrite(&_outputLowLatencyAudioBuffers[idx], _outputFeedSize, _outputSampleRate, idx);
|
||||
}
|
||||
|
||||
// If, after processing the track dispatch, the sound is set to zero
|
||||
// it means that it has reached the end: let's notify its stream...
|
||||
if (track->soundId == 0) {
|
||||
_internalMixer->endStream(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (track)
|
||||
track = next;
|
||||
|
||||
runSMUSHAudio = false;
|
||||
}
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksStartSound(int soundId, int tryPriority, int group) {
|
||||
int priority = clampNumber(tryPriority, 0, 127);
|
||||
|
||||
debug(5, "IMuseDigital::tracksStartSound(): sound %d with priority %d and group %d", soundId, priority, group);
|
||||
IMuseDigiTrack *allocatedTrack = tracksReserveTrack(priority);
|
||||
|
||||
if (!allocatedTrack) {
|
||||
debug(5, "IMuseDigital::tracksStartSound(): ERROR: couldn't find a spare track to allocate sound %d", soundId);
|
||||
return -6;
|
||||
}
|
||||
|
||||
allocatedTrack->soundId = soundId;
|
||||
allocatedTrack->marker = 0;
|
||||
allocatedTrack->group = 0;
|
||||
allocatedTrack->priority = priority;
|
||||
allocatedTrack->vol = 127;
|
||||
allocatedTrack->effVol = _groupsHandler->getGroupVol(0);
|
||||
allocatedTrack->pan = 64;
|
||||
allocatedTrack->detune = 0;
|
||||
allocatedTrack->transpose = 0;
|
||||
allocatedTrack->pitchShift = 256;
|
||||
allocatedTrack->mailbox = 0;
|
||||
allocatedTrack->jumpHook = 0;
|
||||
allocatedTrack->syncSize_0 = 0;
|
||||
allocatedTrack->syncPtr_0 = nullptr;
|
||||
allocatedTrack->syncSize_1 = 0;
|
||||
allocatedTrack->syncPtr_1 = nullptr;
|
||||
allocatedTrack->syncSize_2 = 0;
|
||||
allocatedTrack->syncPtr_2 = nullptr;
|
||||
allocatedTrack->syncSize_3 = 0;
|
||||
allocatedTrack->syncPtr_3 = nullptr;
|
||||
|
||||
if (dispatchAllocateSound(allocatedTrack, group)) {
|
||||
debug(5, "IMuseDigital::tracksStartSound(): ERROR: dispatch couldn't start sound %d", soundId);
|
||||
allocatedTrack->soundId = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_mutex->lock();
|
||||
addTrackToList(&_trackList, allocatedTrack);
|
||||
_mutex->unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksStopSound(int soundId) {
|
||||
if (!_trackList)
|
||||
return -1;
|
||||
|
||||
IMuseDigiTrack *nextTrack = _trackList;
|
||||
IMuseDigiTrack *curTrack;
|
||||
|
||||
while (nextTrack) {
|
||||
curTrack = nextTrack;
|
||||
nextTrack = curTrack->next;
|
||||
if (curTrack->soundId == soundId) {
|
||||
tracksClear(curTrack);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksStopAllSounds() {
|
||||
Common::StackLock lock(*_mutex);
|
||||
IMuseDigiTrack *nextTrack = _trackList;
|
||||
IMuseDigiTrack *curTrack;
|
||||
|
||||
while (nextTrack) {
|
||||
curTrack = nextTrack;
|
||||
nextTrack = curTrack->next;
|
||||
tracksClear(curTrack);
|
||||
}
|
||||
|
||||
_filesHandler->closeAllSounds();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksGetNextSound(int soundId) {
|
||||
int foundSoundId = 0;
|
||||
IMuseDigiTrack *track = _trackList;
|
||||
while (track) {
|
||||
if (track->soundId > soundId) {
|
||||
if (!foundSoundId || track->soundId < foundSoundId) {
|
||||
foundSoundId = track->soundId;
|
||||
}
|
||||
}
|
||||
track = track->next;
|
||||
};
|
||||
|
||||
return foundSoundId;
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksQueryStream(int soundId, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused) {
|
||||
if (!_trackList) {
|
||||
debug(5, "IMuseDigital::tracksQueryStream(): WARNING: empty trackList, ignoring call...");
|
||||
return isFTSoundEngine() ? 0 : -1;
|
||||
}
|
||||
|
||||
IMuseDigiTrack *track = _trackList;
|
||||
if (isFTSoundEngine()) {
|
||||
IMuseDigiTrack *chosenTrack = nullptr;
|
||||
|
||||
do {
|
||||
if (track->soundId > soundId && (!chosenTrack || track->soundId < chosenTrack->soundId)) {
|
||||
if (track->dispatchPtr->streamPtr)
|
||||
chosenTrack = track;
|
||||
}
|
||||
track = track->next;
|
||||
} while (track);
|
||||
|
||||
if (!chosenTrack)
|
||||
return 0;
|
||||
streamerQueryStream(chosenTrack->dispatchPtr->streamPtr, bufSize, criticalSize, freeSpace, paused);
|
||||
return chosenTrack->soundId;
|
||||
} else {
|
||||
do {
|
||||
if (track->soundId) {
|
||||
if (soundId == track->soundId && track->dispatchPtr->streamPtr) {
|
||||
streamerQueryStream(track->dispatchPtr->streamPtr, bufSize, criticalSize, freeSpace, paused);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
track = track->next;
|
||||
} while (track);
|
||||
|
||||
debug(5, "IMuseDigital::tracksQueryStream(): WARNING: couldn't find sound %d in trackList, ignoring call...", soundId);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused) {
|
||||
if (!_trackList)
|
||||
return -1;
|
||||
|
||||
IMuseDigiTrack *track = _trackList;
|
||||
do {
|
||||
if (track->soundId != 0) {
|
||||
if (track->soundId == soundId && track->dispatchPtr->streamPtr) {
|
||||
streamerFeedStream(track->dispatchPtr->streamPtr, srcBuf, sizeToFeed, paused);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
track = track->next;
|
||||
} while (track);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void IMuseDigital::tracksClear(IMuseDigiTrack *trackPtr) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
if (_vm->_game.id == GID_CMI) {
|
||||
if (trackPtr->syncPtr_0) {
|
||||
trackPtr->syncSize_0 = 0;
|
||||
free(trackPtr->syncPtr_0);
|
||||
trackPtr->syncPtr_0 = nullptr;
|
||||
}
|
||||
|
||||
if (trackPtr->syncPtr_1) {
|
||||
trackPtr->syncSize_1 = 0;
|
||||
free(trackPtr->syncPtr_1);
|
||||
trackPtr->syncPtr_1 = nullptr;
|
||||
}
|
||||
|
||||
if (trackPtr->syncPtr_2) {
|
||||
trackPtr->syncSize_2 = 0;
|
||||
free(trackPtr->syncPtr_2);
|
||||
trackPtr->syncPtr_2 = nullptr;
|
||||
}
|
||||
|
||||
if (trackPtr->syncPtr_3) {
|
||||
trackPtr->syncSize_3 = 0;
|
||||
free(trackPtr->syncPtr_3);
|
||||
trackPtr->syncPtr_3 = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
removeTrackFromList(&_trackList, trackPtr);
|
||||
dispatchRelease(trackPtr);
|
||||
_fadesHandler->clearFadeStatus(trackPtr->soundId, -1);
|
||||
_triggersHandler->clearTrigger(trackPtr->soundId, _emptyMarker, -1);
|
||||
|
||||
// Unlock the sound, if it's loaded as a resource
|
||||
if (trackPtr->soundId < 1000 && trackPtr->soundId) {
|
||||
_vm->_res->unlock(rtSound, trackPtr->soundId);
|
||||
}
|
||||
|
||||
if (_lowLatencyMode)
|
||||
waveOutEmptyBuffer(trackPtr->index);
|
||||
|
||||
trackPtr->soundId = 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksSetParam(int soundId, int opcode, int value) {
|
||||
if (!_trackList)
|
||||
return -4;
|
||||
|
||||
IMuseDigiTrack *track = _trackList;
|
||||
while (track) {
|
||||
if (track->soundId == soundId) {
|
||||
switch (opcode) {
|
||||
case DIMUSE_P_GROUP:
|
||||
if (value >= 16)
|
||||
return -5;
|
||||
track->group = value;
|
||||
track->effVol = ((track->vol + 1) * _groupsHandler->getGroupVol(value)) / 128;
|
||||
return 0;
|
||||
case DIMUSE_P_PRIORITY:
|
||||
if (value > 127)
|
||||
return -5;
|
||||
track->priority = value;
|
||||
return 0;
|
||||
case DIMUSE_P_VOLUME:
|
||||
if (value > 127)
|
||||
return -5;
|
||||
track->vol = value;
|
||||
track->effVol = ((value + 1) * _groupsHandler->getGroupVol(track->group)) / 128;
|
||||
return 0;
|
||||
case DIMUSE_P_PAN:
|
||||
if (value > 127)
|
||||
return -5;
|
||||
track->pan = value;
|
||||
return 0;
|
||||
case DIMUSE_P_DETUNE:
|
||||
if (value < -9216 || value > 9216)
|
||||
return -5;
|
||||
track->detune = value;
|
||||
track->pitchShift = value + track->transpose * 256;
|
||||
return 0;
|
||||
case DIMUSE_P_TRANSPOSE:
|
||||
if (_vm->_game.id == GID_DIG || _vm->_game.id == GID_FT) {
|
||||
if (value < -12 || value > 12)
|
||||
return -5;
|
||||
|
||||
if (value == 0) {
|
||||
track->transpose = 0;
|
||||
} else {
|
||||
track->transpose = clampTuning(track->detune + value, -12, 12);
|
||||
}
|
||||
|
||||
track->pitchShift = track->detune + (track->transpose * 256);
|
||||
} else if (_vm->_game.id == GID_CMI) {
|
||||
if (value < 0 || value > 4095)
|
||||
return -5;
|
||||
|
||||
track->pitchShift = value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
case DIMUSE_P_MAILBOX:
|
||||
track->mailbox = value;
|
||||
return 0;
|
||||
default:
|
||||
debug(5, "IMuseDigital::tracksSetParam(): unknown opcode %d", opcode);
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
track = track->next;
|
||||
}
|
||||
|
||||
return -4;
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksGetParam(int soundId, int opcode) {
|
||||
if (!_trackList) {
|
||||
if (opcode != DIMUSE_P_SND_TRACK_NUM)
|
||||
return -4;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
IMuseDigiTrack *track = _trackList;
|
||||
int l = 0;
|
||||
do {
|
||||
if (track)
|
||||
l++;
|
||||
if (track->soundId == soundId) {
|
||||
switch (opcode) {
|
||||
case DIMUSE_P_BOGUS_ID:
|
||||
return -1;
|
||||
case DIMUSE_P_SND_TRACK_NUM:
|
||||
return l;
|
||||
case DIMUSE_P_TRIGS_SNDS:
|
||||
return -1;
|
||||
case DIMUSE_P_MARKER:
|
||||
return track->marker;
|
||||
case DIMUSE_P_GROUP:
|
||||
return track->group;
|
||||
case DIMUSE_P_PRIORITY:
|
||||
return track->priority;
|
||||
case DIMUSE_P_VOLUME:
|
||||
return track->vol;
|
||||
case DIMUSE_P_PAN:
|
||||
return track->pan;
|
||||
case DIMUSE_P_DETUNE:
|
||||
return track->detune;
|
||||
case DIMUSE_P_TRANSPOSE:
|
||||
return track->transpose;
|
||||
case DIMUSE_P_MAILBOX:
|
||||
return track->mailbox;
|
||||
case DIMUSE_P_SND_HAS_STREAM:
|
||||
return (track->dispatchPtr->streamPtr != 0);
|
||||
case DIMUSE_P_STREAM_BUFID:
|
||||
return track->dispatchPtr->streamBufID;
|
||||
case DIMUSE_P_SND_POS_IN_MS: // getCurSoundPositionInMs
|
||||
if (track->dispatchPtr->wordSize == 0)
|
||||
return 0;
|
||||
if (track->dispatchPtr->sampleRate == 0)
|
||||
return 0;
|
||||
if (track->dispatchPtr->channelCount == 0)
|
||||
return 0;
|
||||
return (track->dispatchPtr->currentOffset * 5) / (((track->dispatchPtr->wordSize / 8) * track->dispatchPtr->sampleRate * track->dispatchPtr->channelCount) / 200);
|
||||
default:
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
|
||||
track = track->next;
|
||||
} while (track);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksLipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height) {
|
||||
int32 w, h;
|
||||
|
||||
byte *syncPtr = nullptr;
|
||||
int32 syncSize = 0;
|
||||
|
||||
IMuseDigiTrack *curTrack;
|
||||
int16 val;
|
||||
|
||||
w = 0;
|
||||
h = 0;
|
||||
curTrack = _trackList;
|
||||
|
||||
if (msPos >= 0) {
|
||||
// Check for an invalid timestamp:
|
||||
// this has to be a suitable 2-bytes word...
|
||||
if (((msPos >> 4) & 0xFFFF0000) != 0) {
|
||||
return -5;
|
||||
} else {
|
||||
if (_trackList) {
|
||||
do {
|
||||
if (curTrack->soundId == soundId)
|
||||
break;
|
||||
curTrack = curTrack->next;
|
||||
} while (curTrack);
|
||||
}
|
||||
|
||||
if (curTrack) {
|
||||
if (syncId >= 0 && syncId < 4) {
|
||||
if (syncId == 0) {
|
||||
syncPtr = curTrack->syncPtr_0;
|
||||
syncSize = curTrack->syncSize_0;
|
||||
} else if (syncId == 1) {
|
||||
syncPtr = curTrack->syncPtr_1;
|
||||
syncSize = curTrack->syncSize_1;
|
||||
} else if (syncId == 2) {
|
||||
syncPtr = curTrack->syncPtr_2;
|
||||
syncSize = curTrack->syncSize_2;
|
||||
} else if (syncId == 3) {
|
||||
syncPtr = curTrack->syncPtr_3;
|
||||
syncSize = curTrack->syncSize_3;
|
||||
}
|
||||
|
||||
if (syncSize && syncPtr) {
|
||||
// SYNC data is packed in a number of 4-bytes entries, in the following order:
|
||||
// - Width and height values, packed as one byte each, next to each other;
|
||||
// - The time position of said values, packed as an unsigned word (2-bytes).
|
||||
|
||||
// Given an input timestamp (in ms), we're going to get its representation as 60Hz
|
||||
// increments by dividing it by 16, then we're going to search the SYNC data from
|
||||
// the beginning to find the first entry with a timestamp being equal or greater
|
||||
// our 60Hz timestamp.
|
||||
uint16 inputTs = msPos >> 4;
|
||||
int32 numOfEntries = (syncSize >> 2);
|
||||
uint16 *syncDataWordPtr = (uint16 *)syncPtr;
|
||||
uint16 curEntryTs = 0;
|
||||
int idx;
|
||||
for (idx = 0; idx < numOfEntries; idx++) {
|
||||
curEntryTs = READ_LE_UINT16(&syncDataWordPtr[idx * 2 + 1]);
|
||||
if (curEntryTs >= inputTs) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no relevant entry is found, or if the found entry timestamp is strictly greater
|
||||
// than ours, then we get the previous entry. If no entry was found, this will get the
|
||||
// last entry in our data block.
|
||||
if (idx == numOfEntries || curEntryTs > inputTs) {
|
||||
idx--;
|
||||
}
|
||||
|
||||
// Finally, extract width and height values and remove
|
||||
// their signs by performing AND operations with 0x7F...
|
||||
val = READ_LE_INT16(&syncDataWordPtr[idx * 2]);
|
||||
w = (val >> 8) & 0x7F;
|
||||
h = val & 0x7F;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
width = w;
|
||||
height = h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksSetHook(int soundId, int hookId) {
|
||||
if (_isEarlyDiMUSE)
|
||||
return -2;
|
||||
|
||||
if (hookId > 128)
|
||||
return -5;
|
||||
if (!_trackList)
|
||||
return -4;
|
||||
|
||||
IMuseDigiTrack *track = _trackList;
|
||||
while (track->soundId != soundId) {
|
||||
track = track->next;
|
||||
if (!track)
|
||||
return -4;
|
||||
}
|
||||
|
||||
track->jumpHook = hookId;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::tracksGetHook(int soundId) {
|
||||
if (_isEarlyDiMUSE)
|
||||
return -2;
|
||||
|
||||
if (!_trackList)
|
||||
return -4;
|
||||
|
||||
IMuseDigiTrack *track = _trackList;
|
||||
while (track->soundId != soundId) {
|
||||
track = track->next;
|
||||
if (!track)
|
||||
return -4;
|
||||
}
|
||||
|
||||
return track->jumpHook;
|
||||
}
|
||||
|
||||
IMuseDigiTrack *IMuseDigital::tracksReserveTrack(int priority) {
|
||||
IMuseDigiTrack *curTrack;
|
||||
IMuseDigiTrack *reservedTrack = nullptr;
|
||||
int minPriorityFound;
|
||||
|
||||
// Pick the track from the pool of free tracks
|
||||
for (int i = 0; i < _trackCount; i++) {
|
||||
reservedTrack = &_tracks[i];
|
||||
if (!reservedTrack->soundId) {
|
||||
return reservedTrack;
|
||||
}
|
||||
}
|
||||
|
||||
// If no free track is found, steal the lower priority one
|
||||
curTrack = _trackList;
|
||||
for (minPriorityFound = 127; curTrack; curTrack = curTrack->next) {
|
||||
if (curTrack->priority <= minPriorityFound) {
|
||||
minPriorityFound = curTrack->priority;
|
||||
reservedTrack = curTrack;
|
||||
}
|
||||
}
|
||||
|
||||
if (reservedTrack && priority >= minPriorityFound) {
|
||||
tracksClear(reservedTrack);
|
||||
}
|
||||
|
||||
return reservedTrack;
|
||||
}
|
||||
|
||||
void IMuseDigital::tracksDeinit() {
|
||||
tracksStopAllSounds();
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
348
engines/scumm/imuse_digi/dimuse_triggers.cpp
Normal file
348
engines/scumm/imuse_digi/dimuse_triggers.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
#include "scumm/imuse_digi/dimuse_triggers.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
IMuseDigiTriggersHandler::IMuseDigiTriggersHandler(IMuseDigital *engine, Common::Mutex *mutex) {
|
||||
_engine = engine;
|
||||
_mutex = mutex;
|
||||
_emptyMarker[0] = '\0';
|
||||
}
|
||||
|
||||
IMuseDigiTriggersHandler::~IMuseDigiTriggersHandler() {}
|
||||
|
||||
int IMuseDigiTriggersHandler::init() {
|
||||
return clearAllTriggers();
|
||||
}
|
||||
|
||||
int IMuseDigiTriggersHandler::deinit() {
|
||||
return clearAllTriggers();
|
||||
}
|
||||
|
||||
int IMuseDigiTriggersHandler::clearAllTriggers() {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_TRIGGERS; l++) {
|
||||
_trigs[l].sound = 0;
|
||||
memset(_trigs[l].text, 0, sizeof(_trigs[l].text));
|
||||
_trigs[l].opcode = 0;
|
||||
_trigs[l].a = 0;
|
||||
_trigs[l].b = 0;
|
||||
_trigs[l].c = 0;
|
||||
_trigs[l].d = 0;
|
||||
_trigs[l].e = 0;
|
||||
_trigs[l].f = 0;
|
||||
_trigs[l].g = 0;
|
||||
_trigs[l].h = 0;
|
||||
_trigs[l].i = 0;
|
||||
_trigs[l].j = 0;
|
||||
_trigs[l].clearLater = 0;
|
||||
}
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_DEFERS; l++) {
|
||||
_defers[l].counter = 0;
|
||||
_defers[l].opcode = 0;
|
||||
_defers[l].a = 0;
|
||||
_defers[l].b = 0;
|
||||
_defers[l].c = 0;
|
||||
_defers[l].d = 0;
|
||||
_defers[l].e = 0;
|
||||
_defers[l].f = 0;
|
||||
_defers[l].g = 0;
|
||||
_defers[l].h = 0;
|
||||
_defers[l].i = 0;
|
||||
_defers[l].j = 0;
|
||||
}
|
||||
|
||||
_defersOn = 0;
|
||||
_midProcessing = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigiTriggersHandler::saveLoad(Common::Serializer &ser) {
|
||||
for (int l = 0; l < DIMUSE_MAX_TRIGGERS; l++) {
|
||||
ser.syncAsSint32LE(_trigs[l].sound, VER(103));
|
||||
ser.syncArray(_trigs[l].text, 256, Common::Serializer::SByte, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].opcode, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].a, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].b, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].c, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].d, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].e, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].f, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].g, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].h, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].i, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].j, VER(103));
|
||||
ser.syncAsSint32LE(_trigs[l].clearLater, VER(103));
|
||||
}
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_DEFERS; l++) {
|
||||
ser.syncAsSint32LE(_defers[l].counter, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].opcode, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].a, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].b, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].c, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].d, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].e, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].f, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].g, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].h, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].i, VER(103));
|
||||
ser.syncAsSint32LE(_defers[l].j, VER(103));
|
||||
}
|
||||
|
||||
if (ser.isLoading())
|
||||
_defersOn = 1;
|
||||
}
|
||||
|
||||
int IMuseDigiTriggersHandler::setTrigger(int soundId, char *marker, int opcode, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
if (soundId == 0) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (marker == nullptr) {
|
||||
marker = _emptyMarker;
|
||||
}
|
||||
|
||||
if (strlen(marker) >= 256) {
|
||||
debug(5, "IMuseDigiTriggersHandler::setTrigger(): ERROR: attempting to set trigger with oversized marker string");
|
||||
return -5;
|
||||
}
|
||||
|
||||
for (int index = 0; index < DIMUSE_MAX_TRIGGERS; index++) {
|
||||
if (_trigs[index].sound == 0) {
|
||||
_trigs[index].sound = soundId;
|
||||
_trigs[index].clearLater = 0;
|
||||
_trigs[index].opcode = opcode;
|
||||
Common::strlcpy(_trigs[index].text, marker, sizeof(_trigs[index].text));
|
||||
_trigs[index].a = d;
|
||||
_trigs[index].b = e;
|
||||
_trigs[index].c = f;
|
||||
_trigs[index].d = g;
|
||||
_trigs[index].e = h;
|
||||
_trigs[index].f = i;
|
||||
_trigs[index].g = j;
|
||||
_trigs[index].h = k;
|
||||
_trigs[index].i = l;
|
||||
_trigs[index].j = m;
|
||||
|
||||
debug(5, "IMuseDigiTriggersHandler::setTrigger(): Successfully set trigger for soundId %d and marker '%s'", soundId, marker);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
debug(5, "IMuseDigiTriggersHandler::setTrigger(): ERROR: unable to allocate trigger \"%s\" for sound %d, every slot is full", marker, soundId);
|
||||
return -6;
|
||||
}
|
||||
|
||||
int IMuseDigiTriggersHandler::checkTrigger(int soundId, char *marker, int opcode) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
int r = 0;
|
||||
for (int l = 0; l < DIMUSE_MAX_TRIGGERS; l++) {
|
||||
if (_trigs[l].sound != 0) {
|
||||
if (soundId == -1 || _trigs[l].sound == soundId) {
|
||||
if (!strcmp(marker, _emptyMarker) || !strcmp(marker, _trigs[l].text)) {
|
||||
if (opcode == -1 || _trigs[l].opcode == opcode)
|
||||
r++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int IMuseDigiTriggersHandler::clearTrigger(int soundId, char *marker, int opcode) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_TRIGGERS; l++) {
|
||||
if ((_trigs[l].sound != 0) && (soundId == -1 || _trigs[l].sound == soundId) &&
|
||||
(!strcmp(marker, _emptyMarker) || !strcmp(marker, _trigs[l].text)) &&
|
||||
(opcode == -1 || _trigs[l].opcode == opcode)) {
|
||||
|
||||
if (_midProcessing) {
|
||||
_trigs[l].clearLater = 1;
|
||||
} else {
|
||||
_trigs[l].sound = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigiTriggersHandler::processTriggers(int soundId, char *marker) {
|
||||
char textBuffer[256];
|
||||
int r;
|
||||
|
||||
if (strlen(marker) >= 256) {
|
||||
debug(5, "IMuseDigiTriggersHandler::processTriggers(): ERROR: the input marker string is oversized");
|
||||
return;
|
||||
}
|
||||
|
||||
Common::strlcpy(_textBuffer, marker, sizeof(textBuffer));
|
||||
_midProcessing++;
|
||||
for (int l = 0; l < DIMUSE_MAX_TRIGGERS; l++) {
|
||||
if (!_trigs[l].sound || _trigs[l].sound != soundId || (_trigs[l].text[0] && strcmp(_textBuffer, _trigs[l].text))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Save the string into our local buffer for later
|
||||
r = 0;
|
||||
if (_textBuffer[0] != '\0') {
|
||||
do {
|
||||
textBuffer[r] = _textBuffer[r];
|
||||
r++;
|
||||
} while (_textBuffer[r] != '\0');
|
||||
}
|
||||
textBuffer[r] = '\0';
|
||||
|
||||
_trigs[l].sound = 0;
|
||||
|
||||
debug(5, "IMuseDigiTriggersHandler::processTriggers(): executing trigger for soundId %d and marker '%s'", soundId, marker);
|
||||
if (_trigs[l].opcode == DIMUSE_C_SCRIPT_CALLBACK) {
|
||||
// Call the script callback (a function which sets _stoppingSequence to 1)
|
||||
_engine->scriptTriggerCallback(_textBuffer);
|
||||
} else {
|
||||
if (_trigs[l].opcode < 30) {
|
||||
// Execute a command
|
||||
_engine->cmdsHandleCmd(_trigs[l].opcode, (uint8 *)textBuffer,
|
||||
_trigs[l].a, _trigs[l].b,
|
||||
_trigs[l].c, _trigs[l].d,
|
||||
_trigs[l].e, _trigs[l].f,
|
||||
_trigs[l].g, _trigs[l].h,
|
||||
_trigs[l].i, _trigs[l].j);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the global textBuffer
|
||||
r = 0;
|
||||
if (textBuffer[0] != '\0') {
|
||||
do {
|
||||
_textBuffer[r] = textBuffer[r];
|
||||
r++;
|
||||
} while (textBuffer[r] != '\0');
|
||||
}
|
||||
_textBuffer[r] = '\0';
|
||||
}
|
||||
if (--_midProcessing == 0) {
|
||||
for (int l = 0; l < DIMUSE_MAX_TRIGGERS; l++) {
|
||||
if (_trigs[l].clearLater) {
|
||||
_trigs[l].sound = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int IMuseDigiTriggersHandler::deferCommand(int count, int opcode, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
if (!count) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
for (int index = 0; index < DIMUSE_MAX_DEFERS; index++) {
|
||||
if (!_defers[index].counter) {
|
||||
_defers[index].counter = count;
|
||||
_defers[index].opcode = opcode;
|
||||
_defers[index].a = c;
|
||||
_defers[index].b = d;
|
||||
_defers[index].c = e;
|
||||
_defers[index].d = f;
|
||||
_defers[index].e = g;
|
||||
_defers[index].f = h;
|
||||
_defers[index].g = i;
|
||||
_defers[index].h = j;
|
||||
_defers[index].i = k;
|
||||
_defers[index].j = l;
|
||||
_defersOn = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
debug(5, "IMuseDigiTriggersHandler::deferCommand(): ERROR: couldn't allocate deferred command");
|
||||
return -6;
|
||||
}
|
||||
|
||||
void IMuseDigiTriggersHandler::loop() {
|
||||
if (!_defersOn)
|
||||
return;
|
||||
|
||||
_defersOn = 0;
|
||||
for (int l = 0; l < DIMUSE_MAX_DEFERS; l++) {
|
||||
if (_defers[l].counter == 0)
|
||||
continue;
|
||||
|
||||
_defersOn = 1;
|
||||
_defers[l].counter--;
|
||||
|
||||
if (_defers[l].counter == 1) {
|
||||
if (_defers[l].opcode == DIMUSE_C_SCRIPT_CALLBACK) {
|
||||
_engine->scriptTriggerCallback(_trigs[l].text);
|
||||
} else {
|
||||
if (_defers[l].opcode < 30) {
|
||||
_engine->cmdsHandleCmd(_trigs[l].opcode, nullptr,
|
||||
_trigs[l].a, _trigs[l].b,
|
||||
_trigs[l].c, _trigs[l].d,
|
||||
_trigs[l].e, _trigs[l].f,
|
||||
_trigs[l].g, _trigs[l].h,
|
||||
_trigs[l].i, _trigs[l].j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int IMuseDigiTriggersHandler::countPendingSounds(int soundId) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
int r = 0;
|
||||
for (int l = 0; l < DIMUSE_MAX_TRIGGERS; l++) {
|
||||
if (!_trigs[l].sound)
|
||||
continue;
|
||||
|
||||
int opcode = _trigs[l].opcode;
|
||||
if ((opcode == DIMUSE_C_START_SND && _trigs[l].a == soundId) ||
|
||||
(opcode == DIMUSE_C_SWITCH_STREAM && _trigs[l].b == soundId)) {
|
||||
r++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < DIMUSE_MAX_DEFERS; l++) {
|
||||
if (!_defers[l].counter)
|
||||
continue;
|
||||
|
||||
int opcode = _defers[l].opcode;
|
||||
if ((opcode == DIMUSE_C_START_SND && _defers[l].a == soundId) ||
|
||||
(opcode == DIMUSE_C_SWITCH_STREAM && _defers[l].b == soundId)) {
|
||||
r++;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
64
engines/scumm/imuse_digi/dimuse_triggers.h
Normal file
64
engines/scumm/imuse_digi/dimuse_triggers.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(SCUMM_IMUSE_DIGI_TRIGGERS_H) && defined(ENABLE_SCUMM_7_8)
|
||||
#define SCUMM_IMUSE_DIGI_TRIGGERS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "scumm/imuse_digi/dimuse_defs.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class IMuseDigiTriggersHandler {
|
||||
|
||||
private:
|
||||
IMuseDigital *_engine;
|
||||
Common::Mutex *_mutex;
|
||||
IMuseDigiTrigger _trigs[DIMUSE_MAX_TRIGGERS] = {};
|
||||
IMuseDigiDefer _defers[DIMUSE_MAX_DEFERS] = {};
|
||||
|
||||
int _defersOn = 0;
|
||||
int _midProcessing = 0;
|
||||
char _textBuffer[256] = {};
|
||||
char _emptyMarker[1] = {};
|
||||
|
||||
public:
|
||||
IMuseDigiTriggersHandler(IMuseDigital *engine, Common::Mutex *mutex);
|
||||
~IMuseDigiTriggersHandler();
|
||||
|
||||
int init();
|
||||
int clearAllTriggers();
|
||||
void saveLoad(Common::Serializer &ser);
|
||||
int setTrigger(int soundId, char *marker, int opcode, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n);
|
||||
int checkTrigger(int soundId, char *marker, int opcode);
|
||||
int clearTrigger(int soundId, char *marker, int opcode);
|
||||
void processTriggers(int soundId, char *marker);
|
||||
int deferCommand(int count, int opcode, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n);
|
||||
void loop();
|
||||
int countPendingSounds(int soundId);
|
||||
int deinit();
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
#endif
|
||||
187
engines/scumm/imuse_digi/dimuse_utils.cpp
Normal file
187
engines/scumm/imuse_digi/dimuse_utils.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
int IMuseDigital::addTrackToList(IMuseDigiTrack **listPtr, IMuseDigiTrack *listPtr_Item) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
// [0] is ->prev, [1] is ->next
|
||||
if (!listPtr_Item || listPtr_Item->prev || listPtr_Item->next) {
|
||||
debug(5, "IMuseDigital::addTrackToList(): ERROR: arguments might be null");
|
||||
return -5;
|
||||
} else {
|
||||
// Set item's next element to the list
|
||||
listPtr_Item->next = *listPtr;
|
||||
|
||||
if (*listPtr) {
|
||||
// If the list is empty, use this item as the list
|
||||
(*listPtr)->prev = listPtr_Item;
|
||||
}
|
||||
|
||||
// Set the previous element of the item as nullptr,
|
||||
// effectively making the item the first element of the list
|
||||
listPtr_Item->prev = nullptr;
|
||||
|
||||
// Update the list with the new data
|
||||
*listPtr = listPtr_Item;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::removeTrackFromList(IMuseDigiTrack **listPtr, IMuseDigiTrack *listPtr_Item) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
|
||||
IMuseDigiTrack *currentTrack = *listPtr;
|
||||
IMuseDigiTrack *nextTrack;
|
||||
if (listPtr_Item && currentTrack) {
|
||||
do {
|
||||
if (currentTrack == listPtr_Item)
|
||||
break;
|
||||
currentTrack = currentTrack->next;
|
||||
} while (currentTrack);
|
||||
|
||||
if (currentTrack) {
|
||||
nextTrack = listPtr_Item->next;
|
||||
|
||||
if (nextTrack)
|
||||
nextTrack->prev = listPtr_Item->prev;
|
||||
|
||||
if (listPtr_Item->prev) {
|
||||
listPtr_Item->prev->next = listPtr_Item->next;
|
||||
} else {
|
||||
*listPtr = listPtr_Item->next;
|
||||
}
|
||||
|
||||
listPtr_Item->prev = nullptr;
|
||||
listPtr_Item->next = nullptr;
|
||||
return 0;
|
||||
} else {
|
||||
debug(5, "IMuseDigital::removeTrackFromList(): ERROR: item not on list");
|
||||
return -3;
|
||||
}
|
||||
} else {
|
||||
debug(5, "IMuseDigital::removeTrackFromList(): ERROR: arguments might be null");
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
|
||||
int IMuseDigital::addStreamZoneToList(IMuseDigiStreamZone **listPtr, IMuseDigiStreamZone *listPtr_Item) {
|
||||
if (!listPtr_Item || listPtr_Item->prev || listPtr_Item->next) {
|
||||
debug(5, "IMuseDigital::addStreamZoneToList(): ERROR: arguments might be null");
|
||||
return -5;
|
||||
} else {
|
||||
// Set item's next element to the list
|
||||
listPtr_Item->next = *listPtr;
|
||||
|
||||
if (*listPtr) {
|
||||
// If the list is empty, use this item as the list
|
||||
(*listPtr)->prev = listPtr_Item;
|
||||
}
|
||||
|
||||
// Set the previous element of the item as nullptr,
|
||||
// effectively making the item the first element of the list
|
||||
listPtr_Item->prev = nullptr;
|
||||
|
||||
// Update the list with the new data
|
||||
*listPtr = listPtr_Item;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::removeStreamZoneFromList(IMuseDigiStreamZone **listPtr, IMuseDigiStreamZone *listPtr_Item) {
|
||||
IMuseDigiStreamZone *currentStrZone = *listPtr;
|
||||
IMuseDigiStreamZone *nextStrZone;
|
||||
if (listPtr_Item && currentStrZone) {
|
||||
do {
|
||||
if (currentStrZone == listPtr_Item)
|
||||
break;
|
||||
currentStrZone = currentStrZone->next;
|
||||
} while (currentStrZone);
|
||||
|
||||
if (currentStrZone) {
|
||||
nextStrZone = listPtr_Item->next;
|
||||
|
||||
if (nextStrZone)
|
||||
nextStrZone->prev = listPtr_Item->prev;
|
||||
|
||||
if (listPtr_Item->prev) {
|
||||
listPtr_Item->prev->next = listPtr_Item->next;
|
||||
} else {
|
||||
*listPtr = listPtr_Item->next;
|
||||
}
|
||||
|
||||
listPtr_Item->prev = nullptr;
|
||||
listPtr_Item->next = nullptr;
|
||||
return 0;
|
||||
} else {
|
||||
debug(5, "IMuseDigital::removeStreamZoneFromList(): ERROR: item not on list");
|
||||
return -3;
|
||||
}
|
||||
} else {
|
||||
debug(5, "IMuseDigital::removeStreamZoneFromList(): ERROR: arguments might be null");
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
|
||||
int IMuseDigital::clampNumber(int value, int minValue, int maxValue) {
|
||||
if (value < minValue)
|
||||
return minValue;
|
||||
|
||||
if (value > maxValue)
|
||||
return maxValue;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int IMuseDigital::clampTuning(int value, int minValue, int maxValue) {
|
||||
if (minValue > value) {
|
||||
value += (12 * ((minValue - value) + 11) / 12);
|
||||
}
|
||||
|
||||
if (maxValue < value) {
|
||||
value -= (12 * ((value - maxValue) + 11) / 12);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int IMuseDigital::checkHookId(int &trackHookId, int sampleHookId) {
|
||||
if (sampleHookId) {
|
||||
if (trackHookId == sampleHookId) {
|
||||
trackHookId = 0;
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if (trackHookId == 128) {
|
||||
trackHookId = 0;
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
132
engines/scumm/imuse_digi/dimuse_wave.cpp
Normal file
132
engines/scumm/imuse_digi/dimuse_wave.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
int IMuseDigital::waveInit() {
|
||||
if (tracksInit())
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::waveTerminate() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::wavePause() {
|
||||
Common::StackLock lock(*_mutex);
|
||||
tracksPause();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IMuseDigital::waveResume() {
|
||||
Common::StackLock lock(*_mutex);
|
||||
tracksResume();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigital::waveSaveLoad(Common::Serializer &ser) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
tracksSaveLoad(ser);
|
||||
}
|
||||
|
||||
void IMuseDigital::waveUpdateGroupVolumes() {
|
||||
Common::StackLock lock(*_mutex);
|
||||
tracksSetGroupVol();
|
||||
}
|
||||
|
||||
int IMuseDigital::waveStartSound(int soundId, int priority) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return tracksStartSound(soundId, priority, 0);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveStopSound(int soundId) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return tracksStopSound(soundId);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveStopAllSounds() {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return tracksStopAllSounds();
|
||||
}
|
||||
|
||||
int IMuseDigital::waveGetNextSound(int soundId) {
|
||||
return tracksGetNextSound(soundId);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveSetParam(int soundId, int opcode, int value) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return tracksSetParam(soundId, opcode, value);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveGetParam(int soundId, int opcode) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return tracksGetParam(soundId, opcode);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveSetHook(int soundId, int hookId) {
|
||||
return tracksSetHook(soundId, hookId);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveGetHook(int soundId) {
|
||||
return tracksGetHook(soundId);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveStartStream(int soundId, int priority, int bufferId) {
|
||||
if (soundId == 0)
|
||||
return -1;
|
||||
|
||||
Common::StackLock lock(*_mutex);
|
||||
return tracksStartSound(soundId, priority, bufferId);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveSwitchStream(int oldSoundId, int newSoundId, int fadeLengthMs, int fadeSyncFlag2, int fadeSyncFlag1) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return dispatchSwitchStream(oldSoundId, newSoundId, fadeLengthMs, fadeSyncFlag2, fadeSyncFlag1);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveSwitchStream(int oldSoundId, int newSoundId, uint8 *crossfadeBuffer, int crossfadeBufferSize, int vocLoopFlag) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return dispatchSwitchStream(oldSoundId, newSoundId, crossfadeBuffer, crossfadeBufferSize, vocLoopFlag);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveProcessStreams() {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return streamerProcessStreams();
|
||||
}
|
||||
|
||||
int IMuseDigital::waveQueryStream(int soundId, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return tracksQueryStream(soundId, bufSize, criticalSize, freeSpace, paused);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused) {
|
||||
Common::StackLock lock(*_mutex);
|
||||
return tracksFeedStream(soundId, srcBuf, sizeToFeed, paused);
|
||||
}
|
||||
|
||||
int IMuseDigital::waveLipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height) {
|
||||
return tracksLipSync(soundId, syncId, msPos, width, height);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
195
engines/scumm/imuse_digi/dimuse_waveout.cpp
Normal file
195
engines/scumm/imuse_digi/dimuse_waveout.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/* 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 "scumm/imuse_digi/dimuse_engine.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
int IMuseDigital::waveOutInit(waveOutParamsStruct *waveOutSettingsStruct) {
|
||||
_waveOutBytesPerSample = 2;
|
||||
_waveOutNumChannels = _mixer->getOutputStereo() ? 2 : 1;
|
||||
_waveOutZeroLevel = 0;
|
||||
|
||||
_waveOutSampleRate = _internalSampleRate;
|
||||
_waveOutPreferredFeedSize = _internalFeedSize;
|
||||
|
||||
_waveOutOutputBuffer = nullptr;
|
||||
_waveOutMixBuffer = nullptr;
|
||||
_waveOutLowLatencyOutputBuffer = nullptr;
|
||||
|
||||
if (!_lowLatencyMode || _isEarlyDiMUSE) {
|
||||
// Nine buffers (waveOutPreferredFeedSize * 4 bytes each), two will be used for the mixer
|
||||
_waveOutOutputBuffer = (uint8 *)malloc(_waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
|
||||
_waveOutMixBuffer = _waveOutOutputBuffer + (_waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 7); // 8-th buffer
|
||||
}
|
||||
|
||||
// Replicate another set of buffers for the low latency mode, we will use the previous ones for cutscenes if the mode is active
|
||||
if (_lowLatencyMode) {
|
||||
_waveOutLowLatencyOutputBuffer = (uint8 *)malloc(_waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
|
||||
}
|
||||
|
||||
// This information will be fed to the internal mixer during its initialization
|
||||
waveOutSettingsStruct->bytesPerSample = _waveOutBytesPerSample * 8;
|
||||
waveOutSettingsStruct->numChannels = _waveOutNumChannels;
|
||||
waveOutSettingsStruct->mixBufSize = (_waveOutBytesPerSample * _waveOutNumChannels) * _waveOutPreferredFeedSize;
|
||||
waveOutSettingsStruct->sizeSampleKB = 0;
|
||||
waveOutSettingsStruct->mixBuf = _waveOutMixBuffer; // Note: in low latency mode this initialization is a dummy
|
||||
|
||||
// Init the buffers filling them with zero volume samples
|
||||
if (!_lowLatencyMode || _isEarlyDiMUSE) {
|
||||
memset(_waveOutOutputBuffer, _waveOutZeroLevel, _waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
|
||||
}
|
||||
|
||||
if (_lowLatencyMode) {
|
||||
memset(_waveOutLowLatencyOutputBuffer, _waveOutZeroLevel, _waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
|
||||
}
|
||||
|
||||
_waveOutDisableWrite = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigital::waveOutWrite(uint8 **audioData, int &feedSize, int &sampleRate) {
|
||||
uint8 *curBufferBlock;
|
||||
if (_waveOutDisableWrite)
|
||||
return;
|
||||
|
||||
feedSize = 0;
|
||||
|
||||
// A bit of context for what follows:
|
||||
//
|
||||
// In COMI, when entering certain rooms (e.g. barber shop in Plunder Island), the game
|
||||
// sends a State music event followed immediately by a Sequence music event.
|
||||
// If the iMUSE callback happens to run between these two commands, it can cause a brief
|
||||
// audio glitch where a few milliseconds of the intermediate state music is heard
|
||||
// before being replaced by the sequence music, with the fade in between canceled.
|
||||
//
|
||||
// To prevent this, we skip the very first audio callback after any music change
|
||||
// by setting _waveOutXorTrigger to 1 in playComiMusic(). This gives the scripts
|
||||
// enough time to send any follow-up sequence events before iMUSE processes
|
||||
// the audio transition, ensuring the absence of glitches and most importantly
|
||||
// no permanent audio delay on other audio feeds.
|
||||
//
|
||||
// This is very strongly inspired by what the disassembly does:
|
||||
//
|
||||
// The original DirectSound implementation uses the XOR flag combined with play
|
||||
// cursor position checks that naturally skip audio processing cycles when the
|
||||
// play cursor gets too close to the write cursor. In addition to this, the main
|
||||
// portion of tracksCallback() is executed in loop until feedSide is finally 0.
|
||||
// This creates the exact timing conditions where rapid music events have sufficient
|
||||
// time to accumulate before iMUSE processes any audio transitions.
|
||||
//
|
||||
// We're not using DirectSound, so recreate this I could either simulate the
|
||||
// play cursor handling shenanigans, or I could just do exactly what I have done :-)
|
||||
if (_vm->_game.id == GID_CMI && _waveOutXorTrigger != 0) {
|
||||
_waveOutXorTrigger = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_isEarlyDiMUSE && _vm->_game.id == GID_DIG) {
|
||||
_waveOutXorTrigger ^= 1;
|
||||
if (!_waveOutXorTrigger)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mixer->isReady()) {
|
||||
curBufferBlock = &_waveOutOutputBuffer[_waveOutPreferredFeedSize * _waveOutWriteIndex * _waveOutBytesPerSample * _waveOutNumChannels];
|
||||
|
||||
*audioData = curBufferBlock;
|
||||
|
||||
sampleRate = _waveOutSampleRate;
|
||||
feedSize = _waveOutPreferredFeedSize;
|
||||
_waveOutWriteIndex = (_waveOutWriteIndex + 1) % 7;
|
||||
|
||||
byte *ptr = (byte *)malloc(_outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels);
|
||||
memcpy(ptr, curBufferBlock, _outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels);
|
||||
|
||||
_internalMixer->getStream(-1)->queueBuffer(ptr,
|
||||
_outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels,
|
||||
DisposeAfterUse::YES,
|
||||
waveOutGetStreamFlags());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int IMuseDigital::waveOutDeinit() {
|
||||
_waveOutDisableWrite = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMuseDigital::waveOutCallback() {
|
||||
Common::StackLock lock(*_mutex);
|
||||
if (_lowLatencyMode) {
|
||||
tracksLowLatencyCallback();
|
||||
} else {
|
||||
tracksCallback();
|
||||
}
|
||||
}
|
||||
|
||||
byte IMuseDigital::waveOutGetStreamFlags() {
|
||||
byte flags = Audio::FLAG_16BITS;
|
||||
|
||||
if (_mixer->getOutputStereo()) {
|
||||
flags |= Audio::FLAG_STEREO;
|
||||
}
|
||||
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
flags |= Audio::FLAG_LITTLE_ENDIAN;
|
||||
#endif
|
||||
return flags;
|
||||
}
|
||||
|
||||
void IMuseDigital::waveOutLowLatencyWrite(uint8 **audioData, int &feedSize, int &sampleRate, int idx) {
|
||||
uint8 *curBufferBlock;
|
||||
if (_waveOutDisableWrite)
|
||||
return;
|
||||
|
||||
if (!_isEarlyDiMUSE && _vm->_game.id == GID_DIG) {
|
||||
_waveOutXorTrigger ^= 1;
|
||||
if (!_waveOutXorTrigger)
|
||||
return;
|
||||
}
|
||||
|
||||
feedSize = 0;
|
||||
if (_mixer->isReady()) {
|
||||
curBufferBlock = &_waveOutLowLatencyOutputBuffer[_waveOutPreferredFeedSize * idx * _waveOutBytesPerSample * _waveOutNumChannels];
|
||||
|
||||
*audioData = curBufferBlock;
|
||||
|
||||
sampleRate = _waveOutSampleRate;
|
||||
feedSize = _waveOutPreferredFeedSize;
|
||||
|
||||
byte *ptr = (byte *)malloc(_outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels);
|
||||
memcpy(ptr, curBufferBlock, _outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels);
|
||||
|
||||
_internalMixer->getStream(idx)->queueBuffer(ptr,
|
||||
_outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels,
|
||||
DisposeAfterUse::YES,
|
||||
waveOutGetStreamFlags());
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::waveOutEmptyBuffer(int idx) {
|
||||
// This is necessary in low latency mode to clean-up the buffers of stale/finished sounds
|
||||
int bufferSize = _waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize;
|
||||
memset(&_waveOutLowLatencyOutputBuffer[bufferSize * idx], _waveOutZeroLevel, bufferSize);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
Reference in New Issue
Block a user