aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);