Files
scummvm-cursorfix/engines/grim/emi/sound/vimatrack.cpp
2026-02-02 04:50:13 +01:00

261 lines
6.7 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/stream.h"
#include "common/mutex.h"
#include "common/textconsole.h"
#include "audio/audiostream.h"
#include "audio/mixer.h"
#include "audio/decoders/raw.h"
#include "engines/grim/debug.h"
#include "engines/grim/resource.h"
#include "engines/grim/imuse/imuse_mcmp_mgr.h"
#include "engines/grim/emi/sound/vimatrack.h"
namespace Grim {
struct Region {
int32 offset; // offset of region
int32 length; // length of region
};
struct SoundDesc {
uint16 freq; // frequency
byte channels; // stereo or mono
byte bits; // 8, 12, 16
int numRegions; // number of Regions
Region *region;
bool endFlag;
bool inUse;
char name[32];
McmpMgr *mcmpMgr;
int type;
int volGroupId;
bool mcmpData;
uint32 headerSize;
Common::SeekableReadStream *inStream;
};
bool VimaTrack::isPlaying() {
// FIXME: Actually clean up the data better
// (we don't currently handle the case where it isn't asked for through isPlaying, or deleted explicitly).
if (!_handle)
return false;
if (g_system->getMixer()->isSoundHandleActive(*_handle)) {
if (_stream->endOfData()) {
g_system->getMixer()->stopHandle(*_handle);
return false;
} else {
return true;
}
}
return false;
}
bool VimaTrack::openSound(const Common::String &filename, const Common::String &voiceName, const Audio::Timestamp *start) {
Common::SeekableReadStream *file = g_resourceloader->openNewStreamFile(filename);
if (!file) {
Debug::debug(Debug::Sound, "Stream for %s not open", voiceName.c_str());
return false;
}
_soundName = voiceName;
_mcmp = new McmpMgr();
_desc = new SoundDesc();
_desc->inStream = file;
_desc->mcmpData = true;
_desc->mcmpMgr = _mcmp;
int headerSize = 0;
if (_mcmp->openSound(voiceName.c_str(), file, headerSize)) {
parseSoundHeader(_desc, headerSize);
_stream = Audio::makeQueuingAudioStream(_desc->freq, (false));
playTrack(start);
return true;
} else {
return false;
}
}
void VimaTrack::parseSoundHeader(SoundDesc *sound, int &headerSize) {
Common::SeekableReadStream *data = sound->inStream;
uint32 tag = data->readUint32BE();
if (tag == MKTAG('R','I','F','F')) {
sound->endFlag = false;
sound->region = new Region[1];
sound->numRegions = 1;
sound->region[0].offset = 0;
data->seek(18, SEEK_CUR);
sound->channels = data->readByte();
data->readByte();
sound->freq = data->readUint32LE();
data->seek(6, SEEK_CUR);
sound->bits = data->readByte();
data->seek(5, SEEK_CUR);
sound->region[0].length = data->readUint32LE();
headerSize = 44;
} else {
assert(tag != MKTAG('i','M','U','S'));
error("VimaTrack::parseSoundHeader() Unknown sound format");
}
}
int32 VimaTrack::getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size, int32 *flags) {
//assert(checkForProperHandle(sound));
assert(buf && offset >= 0 && size >= 0);
assert(region >= 0 && region < sound->numRegions);
int32 region_offset = sound->region[region].offset;
int32 region_length = sound->region[region].length;
if (offset + size > region_length) {
size = region_length - offset;
sound->endFlag = true;
} else {
sound->endFlag = false;
}
if (sound->mcmpData) {
size = sound->mcmpMgr->decompressSample(region_offset + offset, size, buf);
*flags |= Audio::FLAG_LITTLE_ENDIAN;
} else {
*buf = new byte[size];
sound->inStream->seek(region_offset + offset + sound->headerSize, SEEK_SET);
sound->inStream->read(*buf, size);
*flags &= ~Audio::FLAG_LITTLE_ENDIAN;
}
return size;
}
void VimaTrack::playTrack(const Audio::Timestamp *start) {
//Common::StackLock lock(_mutex);
if (!_stream) {
error("Stream not loaded");
}
byte *data = nullptr;
int32 result = 0;
int32 curRegion = -1;
int32 regionOffset = 0;
int32 mixerFlags = Audio::FLAG_16BITS;
curRegion++;
int channels = _desc->channels;
//int32 mixer_size = track->feedSize / _callbackFps;
int32 mixer_size = _desc->freq * channels * 2;
if (start) {
regionOffset = (start->msecs() * mixer_size) / 1000;
regionOffset = (regionOffset / 2) * 2; // Ensure that the offset is divisible by 2.
while (regionOffset > _desc->region[curRegion].length) {
regionOffset -= _desc->region[curRegion].length;
++curRegion;
}
if (curRegion > _desc->numRegions - 1)
return;
}
if (_stream->endOfData()) { // FIXME: Currently we just allocate a bunch here, try to find the correct size instead.
mixer_size *= 8;
}
if (channels == 1)
mixer_size &= ~1;
if (channels == 2)
mixer_size &= ~3;
if (mixer_size == 0)
return;
do {
result = getDataFromRegion(_desc, curRegion, &data, regionOffset, mixer_size, &mixerFlags);
if (channels == 1) {
result &= ~1;
}
if (channels == 2) {
result &= ~3;
}
if (result > mixer_size)
result = mixer_size;
if (g_system->getMixer()->isReady()) {
((Audio::QueuingAudioStream *)_stream)->queueBuffer(data, result, DisposeAfterUse::YES, mixerFlags);
regionOffset += result;
} else
delete[] data;
if (curRegion >= 0 && curRegion < _desc->numRegions - 1) {
curRegion++;
regionOffset = 0;
if (!_stream) {
return;
}
}
mixer_size -= result;
assert(mixer_size >= 0);
} while (mixer_size && !_desc->endFlag);
if (g_system->getMixer()->isReady()) {
//g_system->getMixer()->setChannelVolume(track->handle, track->getVol());
//g_system->getMixer()->setChannelBalance(track->handle, track->getPan());
}
}
Audio::Timestamp VimaTrack::getPos() {
// FIXME: Return actual stream position.
return g_system->getMixer()->getSoundElapsedTime(*_handle);
}
VimaTrack::VimaTrack() {
_soundType = Audio::Mixer::kSpeechSoundType;
_handle = new Audio::SoundHandle();
_file = nullptr;
_mcmp = nullptr;
_desc = nullptr;
}
VimaTrack::~VimaTrack() {
stop();
delete _mcmp;
if (_desc) {
delete[] _desc->region;
delete _desc->inStream;
}
if (_handle) {
g_system->getMixer()->stopHandle(*_handle);
delete _handle;
}
delete _desc;
}
} // end of namespace Grim