261 lines
6.7 KiB
C++
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
|