aboutsummaryrefslogtreecommitdiff
path: root/sword2
diff options
context:
space:
mode:
authorTorbjörn Andersson2004-08-25 06:55:15 +0000
committerTorbjörn Andersson2004-08-25 06:55:15 +0000
commit68acd13ad496615083b0d0c6e8204d4eced76e53 (patch)
treebdeb0abeed01af9c743a6332eca65cf3dda33007 /sword2
parent0d3d9b4c09e67f0ae157c31e5593b129ac5e0c5d (diff)
downloadscummvm-rg350-68acd13ad496615083b0d0c6e8204d4eced76e53.tar.gz
scummvm-rg350-68acd13ad496615083b0d0c6e8204d4eced76e53.tar.bz2
scummvm-rg350-68acd13ad496615083b0d0c6e8204d4eced76e53.zip
The original speech clusters are now decoded through a custom AudioStream
class, so they are handled the same way as the compressed clusters. The next step will be to migrate the music playback to use the same class, which means the fade-in/out logic needs to be separated from the decoding. Once this is done, adding support for compressed music should be a piece of cake. svn-id: r14740
Diffstat (limited to 'sword2')
-rw-r--r--sword2/driver/d_sound.cpp276
-rw-r--r--sword2/driver/d_sound.h4
2 files changed, 174 insertions, 106 deletions
diff --git a/sword2/driver/d_sound.cpp b/sword2/driver/d_sound.cpp
index d0fd81cec7..a0278817aa 100644
--- a/sword2/driver/d_sound.cpp
+++ b/sword2/driver/d_sound.cpp
@@ -52,6 +52,108 @@ static File fpMus;
#define GetCompressedSign(n) (((n) >> 3) & 1)
#define GetCompressedAmplitude(n) ((n) & 7)
+#define BUFFER_SIZE 4096U
+
+class CLUInputStream : public AudioStream {
+ File *_file;
+ uint _end_pos;
+ int16 _outbuf[BUFFER_SIZE];
+ byte _inbuf[BUFFER_SIZE];
+ const int16 *_bufferEnd;
+ const int16 *_pos;
+
+ uint16 _prev;
+
+ void refill();
+ inline bool eosIntern() const;
+public:
+ CLUInputStream(File *file, int duration);
+ ~CLUInputStream();
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ int16 read();
+ bool endOfData() const { return eosIntern(); }
+ bool isStereo() const { return false; }
+ int getRate() const { return 22050; }
+};
+
+CLUInputStream::CLUInputStream(File *file, int size)
+ : _file(file), _bufferEnd(_outbuf + BUFFER_SIZE) {
+
+ _file->incRef();
+
+ // Determine the end position.
+ _end_pos = file->pos() + size;
+
+ _prev = _file->readUint16LE();
+
+ // Read in initial data
+ refill();
+}
+
+CLUInputStream::~CLUInputStream() {
+ _file->decRef();
+}
+
+inline int16 CLUInputStream::read() {
+ assert(!eosIntern());
+
+ int16 sample = *_pos++;
+ if (_pos >= _bufferEnd) {
+ refill();
+ }
+ return sample;
+}
+
+inline bool CLUInputStream::eosIntern() const {
+ return _pos >= _bufferEnd;
+}
+
+int CLUInputStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samples = 0;
+ while (samples < numSamples && !eosIntern()) {
+ const int len = MIN(numSamples - samples, (int) (_bufferEnd - _pos));
+ memcpy(buffer, _pos, len * 2);
+ buffer += len;
+ _pos += len;
+ samples += len;
+ if (_pos >= _bufferEnd) {
+ refill();
+ }
+ }
+ return samples;
+}
+
+void CLUInputStream::refill() {
+ byte *in = _inbuf;
+ int16 *out = _outbuf;
+ uint len_left = _file->read(in, MIN(BUFFER_SIZE, _end_pos - _file->pos()));
+
+ while (len_left > 0) {
+ uint16 delta = GetCompressedAmplitude(*in) << GetCompressedShift(*in);
+ uint16 sample;
+
+ if (GetCompressedSign(*in))
+ sample = _prev - delta;
+ else
+ sample = _prev + delta;
+
+ _prev = sample;
+ *out++ = sample;
+ *in++;
+ len_left--;
+ }
+
+ _pos = _outbuf;
+ _bufferEnd = out;
+}
+
+AudioStream *makeCLUStream(File *file, int size) {
+ assert(size >= 2);
+ return new CLUInputStream(file, size);
+}
+
static void premix_proc(void *param, int16 *data, uint len) {
((Sound *) param)->streamMusic(data, len);
}
@@ -97,6 +199,9 @@ Sound::~Sound() {
_vm->_system->deleteMutex(_mutex);
}
+// FIXME: Merge openSoundFile() and getAudioStream() once compressed music has
+// been implemented?
+
int Sound::openSoundFile(File *fp, const char *base) {
struct {
const char *ext;
@@ -111,7 +216,7 @@ int Sound::openSoundFile(File *fp, const char *base) {
#ifdef USE_FLAC
{ "clf", kFlacMode },
#endif
- { "clu", kWAVMode }
+ { "clu", kCLUMode }
};
char filename[20];
@@ -133,6 +238,53 @@ int Sound::openSoundFile(File *fp, const char *base) {
return 0;
}
+AudioStream *Sound::getAudioStream(File *fp, const char *base, uint32 id, uint32 *numSamples) {
+ int soundMode = openSoundFile(fp, base);
+
+ if (!soundMode)
+ return NULL;
+
+ fp->seek((id + 1) * ((soundMode == kCLUMode) ? 8 : 12), SEEK_SET);
+
+ uint32 pos = fp->readUint32LE();
+ uint32 len = fp->readUint32LE();
+ uint32 enc_len;
+
+ if (numSamples)
+ *numSamples = len;
+
+ if (soundMode != kCLUMode)
+ enc_len = fp->readUint32LE();
+ else
+ enc_len = len + 1;
+
+ if (!pos || !len) {
+ fp->close();
+ return NULL;
+ }
+
+ fp->seek(pos, SEEK_SET);
+
+ switch (soundMode) {
+ case kCLUMode:
+ return makeCLUStream(fp, enc_len);
+#ifdef USE_MAD
+ case kMP3Mode:
+ return makeMP3Stream(fp, enc_len);
+#endif
+#ifdef USE_VORBIS
+ case kVorbisMode:
+ return makeVorbisStream(fp, enc_len);
+#endif
+#ifdef USE_FLAC
+ case kFlacMode:
+ return makeFlacStream(fp, enc_len);
+#endif
+ default:
+ return NULL;
+ }
+}
+
void Sound::streamMusic(int16 *data, uint len) {
Common::StackLock lock(_mutex);
@@ -784,68 +936,24 @@ int32 Sound::amISpeaking(void) {
/**
* This function loads and decompresses a list of speech from a cluster, but
- * does not play it. This is primarily used by playCompSpeech(), but also to
- * store the voice-overs for the animated cutscenes until they are played.
+ * does not play it. This is used for cutscene voice-overs, presumably to
+ * avoid having to read from more than one file on the CD during playback.
* @param filename the file name of the speech cluster file
* @param speechid the text line id used to reference the speech
* @param buf a pointer to the buffer that will be allocated for the sound
*/
uint32 Sound::preFetchCompSpeech(uint32 speechid, uint16 **buf) {
- AudioStream *input = NULL;
File fp;
- int soundMode = kWAVMode;
-
- *buf = NULL;
-
- // Open the speech cluster and find the data offset & size
-
- soundMode = openSoundFile(&fp, "speech");
- if (!soundMode)
- return 0;
-
- if (soundMode == kWAVMode)
- fp.seek((speechid + 1) * 8, SEEK_SET);
- else
- fp.seek((speechid + 1) * 12, SEEK_SET);
+ uint32 numSamples;
- uint32 speechPos = fp.readUint32LE();
- uint32 speechLength = fp.readUint32LE();
- uint32 encLength = 0;
+ AudioStream *input = getAudioStream(&fp, "speech", speechid, &numSamples);
- if (soundMode != kWAVMode)
- encLength = fp.readUint32LE();
-
- if (!speechPos || !speechLength) {
- fp.close();
- return 0;
- }
-
- fp.seek(speechPos, SEEK_SET);
-
- switch (soundMode) {
-#ifdef USE_MAD
- case kMP3Mode:
- input = makeMP3Stream(&fp, encLength);
- break;
-#endif
-#ifdef USE_VORBIS
- case kVorbisMode:
- input = makeVorbisStream(&fp, encLength);
- break;
-#endif
-#ifdef USE_FLAC
- case kFlacMode:
- input = makeFlacStream(&fp, encLength);
- break;
-#endif
- default:
- break;
- }
+ *buf = NULL;
// Decompress data into speech buffer.
- uint32 bufferSize = speechLength * 2;
+ uint32 bufferSize = 2 * numSamples;
*buf = (uint16 *) malloc(bufferSize);
if (!*buf) {
@@ -854,47 +962,12 @@ uint32 Sound::preFetchCompSpeech(uint32 speechid, uint16 **buf) {
return 0;
}
- if (input) {
- int numSamples = input->readBuffer((int16 *) *buf, speechLength);
- fp.close();
- delete input;
- return numSamples * 2;
- }
-
- uint16 *data16 = *buf;
-
- // Create a temporary buffer for compressed speech
- byte *data8 = (byte *) malloc(speechLength);
- if (!data8) {
- free(data16);
- fp.close();
- return 0;
- }
-
- // FIXME: Create an AudioStream-based class for this. This could then
- // also be used by the music code.
-
- if (fp.read(data8, speechLength) != speechLength) {
- fp.close();
- free(data16);
- free(data8);
- return 0;
- }
+ uint32 readSamples = input->readBuffer((int16 *) *buf, numSamples);
fp.close();
+ delete input;
- // Starting Value
- data16[0] = READ_LE_UINT16(data8);
-
- for (uint32 i = 1; i < speechLength; i++) {
- if (GetCompressedSign(data8[i + 1]))
- data16[i] = data16[i - 1] - (GetCompressedAmplitude(data8[i + 1]) << GetCompressedShift(data8[i + 1]));
- else
- data16[i] = data16[i - 1] + (GetCompressedAmplitude(data8[i + 1]) << GetCompressedShift(data8[i + 1]));
- }
-
- free(data8);
- return bufferSize;
+ return 2 * readSamples;
}
/**
@@ -910,17 +983,18 @@ int32 Sound::playCompSpeech(uint32 speechid, uint8 vol, int8 pan) {
if (_speechMuted)
return RD_OK;
- uint16 *data16;
- uint32 bufferSize;
-
if (getSpeechStatus() == RDERR_SPEECHPLAYING)
return RDERR_SPEECHPLAYING;
- bufferSize = preFetchCompSpeech(speechid, &data16);
+ File *fp = new File;
+ AudioStream *input = getAudioStream(fp, "speech", speechid, NULL);
- // We don't know exactly what went wrong here.
- if (bufferSize == 0)
- return RDERR_OUTOFMEMORY;
+ // Make the AudioStream object the sole owner of the file so that it
+ // will die along with the AudioStream when the speech has finished.
+ fp->decRef();
+
+ if (!input)
+ return RDERR_INVALIDID;
// Modify the volume according to the master volume
@@ -928,17 +1002,9 @@ int32 Sound::playCompSpeech(uint32 speechid, uint8 vol, int8 pan) {
int8 p = _panTable[pan + 16];
// Start the speech playing
-
_speechPaused = true;
-
- uint32 flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_AUTOFREE;
-
-#ifndef SCUMM_BIG_ENDIAN
- flags |= SoundMixer::FLAG_LITTLE_ENDIAN;
-#endif
-
- _vm->_mixer->playRaw(&_soundHandleSpeech, data16, bufferSize, 22050, flags, -1, volume, p);
+ _vm->_mixer->playInputStream(&_soundHandleSpeech, input, false, volume, p);
_speechStatus = true;
// DipMusic();
diff --git a/sword2/driver/d_sound.h b/sword2/driver/d_sound.h
index c530916877..210549e4df 100644
--- a/sword2/driver/d_sound.h
+++ b/sword2/driver/d_sound.h
@@ -20,6 +20,7 @@
#ifndef D_SOUND_H
#define D_SOUND_H
+#include "common/file.h"
#include "sound/audiostream.h"
#include "sound/mixer.h"
@@ -32,7 +33,7 @@ namespace Sword2 {
#define MAXMUS 2
enum {
- kWAVMode = 1,
+ kCLUMode = 1,
kMP3Mode,
kVorbisMode,
kFlacMode
@@ -116,6 +117,7 @@ private:
void stopFxHandle(int i);
int openSoundFile(File *fp, const char *base);
+ AudioStream *getAudioStream(File *fp, const char *base, uint32 id, uint32 *numSamples);
public:
Sound(Sword2Engine *vm);