aboutsummaryrefslogtreecommitdiff
path: root/engines/tsage
diff options
context:
space:
mode:
authorPaul Gilbert2013-10-14 22:58:29 -0400
committerPaul Gilbert2013-10-14 22:58:29 -0400
commitacc7cd58a203fb0c1991c3b8b35c2f296b739f06 (patch)
tree5a1d7b1ffe37b0c77977b015ff4b650d8f9a66dc /engines/tsage
parentc9aa875c8de12a9851a66433654f00c60a14ca14 (diff)
downloadscummvm-rg350-acc7cd58a203fb0c1991c3b8b35c2f296b739f06.tar.gz
scummvm-rg350-acc7cd58a203fb0c1991c3b8b35c2f296b739f06.tar.bz2
scummvm-rg350-acc7cd58a203fb0c1991c3b8b35c2f296b739f06.zip
TSAGE: Beginnings of R2R voice streaming
Diffstat (limited to 'engines/tsage')
-rw-r--r--engines/tsage/ringworld2/ringworld2_logic.cpp3
-rw-r--r--engines/tsage/ringworld2/ringworld2_scenes0.cpp2
-rw-r--r--engines/tsage/sound.cpp203
-rw-r--r--engines/tsage/sound.h47
4 files changed, 247 insertions, 8 deletions
diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp
index bd908504ad..90df72ab32 100644
--- a/engines/tsage/ringworld2/ringworld2_logic.cpp
+++ b/engines/tsage/ringworld2/ringworld2_logic.cpp
@@ -596,6 +596,9 @@ void SceneExt::loadBlankScene() {
void SceneHandlerExt::postInit(SceneObjectList *OwnerList) {
SceneHandler::postInit(OwnerList);
+
+ if (!R2_GLOBALS._playStream.setFile("SND4K.RES"))
+ warning("Could not find SND4K.RES voice file");
}
void SceneHandlerExt::process(Event &event) {
diff --git a/engines/tsage/ringworld2/ringworld2_scenes0.cpp b/engines/tsage/ringworld2/ringworld2_scenes0.cpp
index bdac160163..5e4b4e4191 100644
--- a/engines/tsage/ringworld2/ringworld2_scenes0.cpp
+++ b/engines/tsage/ringworld2/ringworld2_scenes0.cpp
@@ -836,7 +836,7 @@ void Scene125::process(Event &event) {
void Scene125::dispatch() {
if (_soundCount)
- R2_GLOBALS._playStream.proc1();
+ R2_GLOBALS._playStream.dispatch();
Scene::dispatch();
}
diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp
index b9567cece2..a5480fe6b4 100644
--- a/engines/tsage/sound.cpp
+++ b/engines/tsage/sound.cpp
@@ -1540,6 +1540,23 @@ void Sound::play(int soundNum) {
_soundManager->addToPlayList(this);
}
+bool Sound::playBuffers(const byte *soundData) {
+ prime(-2);
+ _soundManager->addToPlayList(this);
+
+ debug(1, "TODO: Sound::checkCannels");
+ if (/* _soundManager.checkChannels() */ true) {
+ uint32 size = READ_UINT32(soundData + 4);
+ if (size == 0 || size > 0xffff)
+ error("Sound::playBuffers() called with illegal buffer length");
+
+ warning("TODO: Implement Sound::primeSoundData for voice playback");
+ } else {
+ stop();
+ return false;
+ }
+}
+
void Sound::stop() {
g_globals->_soundManager.removeFromPlayList(this);
_unPrime();
@@ -2505,6 +2522,192 @@ void ASoundExt::changeSound(int soundNum) {
/*--------------------------------------------------------------------------*/
+void PlayStream::ResFileData::load(Common::SeekableReadStream &stream) {
+ // Validate that it's the correct data file
+ char header[4];
+ stream.read(&header[0], 4);
+ if (strncmp(header, "SPAM", 4))
+ error("Invalid voice resource data");
+
+ _fileChunkSize = stream.readUint32LE();
+ _field8 = stream.readUint16LE();
+ _indexSize = stream.readUint16LE();
+ _chunkSize = stream.readUint16LE();
+
+ stream.skip(20);
+}
+
+PlayStream::PlayStream(): EventHandler() {
+ _index = NULL;
+ _chunks[0] = _chunks[1] = NULL;
+ _voiceNum = 0;
+ _currentChunkIndex = 0;
+ _nextChunkIndex = 0;
+ _endAction = NULL;
+ _field2F0 = _field2FA = 0;
+}
+
+PlayStream::~PlayStream() {
+ remove();
+}
+
+bool PlayStream::setFile(const Common::String &filename) {
+ remove();
+
+ // Open the resource file for access
+ if (!_file.open(filename))
+ return false;
+
+ // Load header
+ _resData.load(_file);
+
+ // Load the index
+ _index = new uint16[_resData._indexSize / 2];
+ for (uint i = 0; i < (_resData._indexSize / 2); ++i)
+ _index[i] = _file.readUint16LE();
+
+ // Allocate space for loading voice chunks into
+ _chunks[0] = new byte[_resData._chunkSize];
+ _chunks[1] = new byte[_resData._chunkSize];
+
+ // Initialise variables
+ _voiceNum = 0;
+ _currentChunkIndex = _nextChunkIndex = 0;
+
+ return true;
+}
+
+bool PlayStream::play(int voiceNum, EventHandler *endAction) {
+ uint32 offset = getFileOffset(_index, _resData._fileChunkSize, voiceNum);
+ if (offset) {
+ _voiceNum = 0;
+ _currentChunkIndex = _nextChunkIndex = 0;
+ if (_sound.isPlaying())
+ _sound.stop();
+
+ _file.seek(offset);
+ _file.read(_chunks[0], _resData._chunkSize);
+ _file.read(_chunks[1], _resData._chunkSize);
+
+ _currentChunkIndex = 0;
+ _nextChunkIndex = 1;
+ _voiceNum = voiceNum;
+ _endAction = endAction;
+
+ if (!strncmp((const char *)_chunks[_currentChunkIndex], "FEED", 4)) {
+ // Valid sound data found
+ const byte *data = _chunks[_currentChunkIndex];
+
+ _field2F0 = _field2FA = READ_UINT16(data + 10);
+
+ //_fnP = proc2;
+ _field300 = this;
+ _soundData = data + 16;
+ _streamSize = READ_LE_UINT16(data + 4) - 16;
+
+ // Start playing the sound data
+ if (!_sound.playBuffers(_soundData)) {
+ _voiceNum = 0;
+ _currentChunkIndex = _nextChunkIndex = 0;
+ }
+
+ return true;
+ }
+ }
+
+ // If it reaches this point, no valid voice data found
+ return false;
+}
+
+void PlayStream::stop() {
+ _voiceNum = 0;
+ _currentChunkIndex = _nextChunkIndex = 0;
+ _endAction = NULL;
+
+ if (_sound.isPlaying())
+ _sound.stop();
+}
+
+bool PlayStream::isPlaying() const {
+ return _voiceNum != 0;
+}
+
+void PlayStream::remove() {
+ stop();
+ _file.close();
+
+ // Free data buffers
+ delete[] _index;
+ delete[] _chunks[0];
+ delete[] _chunks[1];
+ _index = NULL;
+ _chunks[0] = _chunks[1] = NULL;
+
+ _endAction = NULL;
+ _voiceNum = 0;
+ _currentChunkIndex = 0;
+ _nextChunkIndex = 0;
+}
+
+void PlayStream::dispatch() {
+ if ((_voiceNum != 0) && (_currentChunkIndex == _nextChunkIndex)) {
+ if (READ_LE_UINT16(_chunks[_currentChunkIndex] + 6) != 0) {
+ uint32 bytesRead = _file.read(_chunks[_nextChunkIndex ^ 1], _resData._chunkSize);
+
+ if (bytesRead != _resData._chunkSize) {
+ byte *data = _chunks[_currentChunkIndex];
+ Common::fill(data, data + 128, 0);
+
+ _voiceNum = 0;
+ }
+
+ _nextChunkIndex ^= 1;
+ } else if (!isPlaying()) {
+ // If voice has finished playing, reset fields
+ EventHandler *endAction = _endAction;
+ _endAction = NULL;
+ _voiceNum = 0;
+ _currentChunkIndex = 0;
+ _nextChunkIndex = 0;
+
+ if (endAction)
+ // Signal given end action
+ endAction->signal();
+ }
+ }
+}
+
+uint32 PlayStream::getFileOffset(const uint16 *data, int count, int voiceNum) {
+ assert(data);
+ int bitsIndex = voiceNum & 7;
+ int byteIndex = voiceNum >> 3;
+ int shiftAmount = bitsIndex * 2;
+ int bitMask = 3 << shiftAmount;
+ int v = (data[byteIndex] & bitMask) >> shiftAmount;
+ uint32 offset = 0;
+
+ if (!v)
+ return 0;
+
+ // Loop to figure out offsets from indexes skipped over
+ for (int i = 0; i < (voiceNum >> 3); ++i) {
+ for (int bit = 0; bit < 16; bit += 2)
+ offset += ((data[i] >> bit) & 3) * count;
+ }
+
+ // Bit index loop
+ for (int i = 0; i < bitsIndex; --i)
+ offset += ((data[byteIndex] >> (i * 2)) & 3) * count;
+
+ return offset;
+}
+
+void PlayStream::stream() {
+ // TODO
+}
+
+/*--------------------------------------------------------------------------*/
+
SoundDriver::SoundDriver() {
_driverResID = 0;
_minVersion = _maxVersion = 0;
diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h
index 2f59afb49b..9779f3c03c 100644
--- a/engines/tsage/sound.h
+++ b/engines/tsage/sound.h
@@ -259,6 +259,7 @@ public:
class Sound: public EventHandler {
private:
void _prime(int soundResID, bool dontQueue);
+ void _primeBuffer(const byte *soundData);
void _unPrime();
public:
bool _stoppedAsynchronously;
@@ -315,6 +316,7 @@ public:
void orientAfterRestore();
void play(int soundResID);
+ bool playBuffers(const byte *soundData);
void stop();
void prime(int soundResID);
void unPrime();
@@ -414,15 +416,46 @@ public:
virtual void signal();
};
-class PlayStream {
-public:
+class PlayStream: public EventHandler {
+ class ResFileData {
+ public:
+ int _fileChunkSize;
+ int _field8;
+ uint _indexSize;
+ uint _chunkSize;
+
+ void load(Common::SeekableReadStream &stream);
+ };
+ typedef void (PlayStream::*FunctionPtr)();
+private:
+ Common::File _file;
+ ResFileData _resData;
Sound _sound;
+ uint16 *_index;
+ byte *_chunks[2];
+ const byte *_soundData;
+ int _voiceNum;
+ int _currentChunkIndex;
+ int _nextChunkIndex;
+ EventHandler *_endAction;
+ EventHandler *_field300;
+ int _field2F0, _field2FA;
+ int _streamSize;
+ FunctionPtr _streamFn;
+
+ void stream();
+ static uint32 getFileOffset(const uint16 *data, int count, int voiceNum);
+public:
+ PlayStream();
+ virtual ~PlayStream();
- void setFile(const Common::String &filename) {}
- bool play(int soundNum, EventHandler *endAction) { return false; }
- void stop() {}
- void proc1() {}
- bool isPlaying() const { return false; }
+ bool setFile(const Common::String &filename);
+ bool play(int voiceNum, EventHandler *endAction);
+ void stop();
+ bool isPlaying() const;
+
+ virtual void remove();
+ virtual void dispatch();
};
#define ADLIB_CHANNEL_COUNT 9