aboutsummaryrefslogtreecommitdiff
path: root/sword2
diff options
context:
space:
mode:
authorTorbjörn Andersson2003-09-25 06:11:07 +0000
committerTorbjörn Andersson2003-09-25 06:11:07 +0000
commit99eee3fb8443798fe11e28176c2b4a90474308e1 (patch)
treeecb21b39a877373496f11701e4c2cc72da2ac25f /sword2
parent5f3f0bce7a6e7773ede023367de88f736cebe7b2 (diff)
downloadscummvm-rg350-99eee3fb8443798fe11e28176c2b4a90474308e1.tar.gz
scummvm-rg350-99eee3fb8443798fe11e28176c2b4a90474308e1.tar.bz2
scummvm-rg350-99eee3fb8443798fe11e28176c2b4a90474308e1.zip
Rewrote the music code to use a premix function instead of a timer. I'm
speculating that the timer didn't always fill the buffer quite fast enough and that this is what caused the occasional popping noises. Of course, I may have introduced all kinds of regressions - particularly since I don't have any big-endian computer to test on - but we're all friends here, right? :-) svn-id: r10404
Diffstat (limited to 'sword2')
-rw-r--r--sword2/driver/d_sound.cpp291
-rw-r--r--sword2/driver/d_sound.h35
-rw-r--r--sword2/driver/rdwin.cpp1
3 files changed, 98 insertions, 229 deletions
diff --git a/sword2/driver/d_sound.cpp b/sword2/driver/d_sound.cpp
index 7c9f98de67..4a3b465efc 100644
--- a/sword2/driver/d_sound.cpp
+++ b/sword2/driver/d_sound.cpp
@@ -155,10 +155,14 @@
#include "driver96.h"
#include "d_sound.h"
#include "../sword2.h"
-#include "common/timer.h"
+#include "sound/audiostream.h"
#include "sound/mixer.h"
+#include "sound/rate.h"
-// FIXME: Remove code duplication
+// Fade-out takes half a second. This may need some tuning.
+#define FADE_SAMPLES 11025
+
+static File fpMus;
// Decompression macros
#define GetCompressedShift(byte) ((byte) >> 4)
@@ -175,17 +179,48 @@ int32 musicVolTable[17] = {
0, 15, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255
};
-// FIXME: Is this really a good way? The music occasionally pops, and I have
-// a feeling it could be that the stream is emptied faster than we are filling
-// it. Maybe we should set up a premix function instead, but then we'd have to
-// do rate conversion and mixing here.
-//
-// Maybe it'd be useful if audio streams could have their own callbacks to fill
-// them with data?
+int16 MusicHandle::read() {
+ uint8 in;
+ uint16 delta, value;
+ int16 out;
+
+ if (!_streaming)
+ return 0;
+
+ // Assume the file handle has been correctly positioned already.
+
+ in = fpMus.readByte();
+ delta = GetCompressedAmplitude(in) << GetCompressedShift(in);
+
+ if (GetCompressedSign(in))
+ value = _lastSample - delta;
+ else
+ value = _lastSample + delta;
+
+ _filePos++;
+ _lastSample = value;
+
+ out = (int16) (value ^ 0x8000);
+
+ if (_fading > 0) {
+ if (--_fading == 0) {
+ _streaming = false;
+ _looping = false;
+ }
+ out = (out * _fading) / FADE_SAMPLES;
+ }
-void sword2_sound_handler(void *refCon) {
- Sword2Sound *sound = (Sword2Sound *) refCon;
- sound->FxServer();
+ return out;
+}
+
+bool MusicHandle::eos() const {
+ if (!_streaming || _filePos >= _fileEnd)
+ return true;
+ return false;
+}
+
+static void premix_proc(void *param, int16 *data, uint len) {
+ ((Sword2Sound *) param)->FxServer(data, len);
}
Sword2Sound::Sword2Sound(SoundMixer *mixer) {
@@ -204,20 +239,20 @@ Sword2Sound::Sword2Sound(SoundMixer *mixer) {
musicVol = 16;
musicMuted = 0;
- bufferSizeMusic = 4410;
_mixer = mixer;
memset(fx, 0, sizeof(fx));
- memset(music, 0, sizeof(music));
soundHandleSpeech = 0;
soundOn = 1;
- g_engine->_timer->installProcedure(sword2_sound_handler, 100000, this);
+
+ _converter = makeRateConverter(music[0].getRate(), _mixer->getOutputRate(), music[0].isStereo(), false);
+
+ _mixer->setupPremix(premix_proc, this);
}
Sword2Sound::~Sword2Sound() {
- g_engine->_timer->releaseProcedure(sword2_sound_handler);
if (_mutex)
g_system->delete_mutex(_mutex);
}
@@ -266,7 +301,7 @@ int32 Sword2Sound::IsFxOpen(int32 id) {
// a separate thread.
// --------------------------------------------------------------------------
-void Sword2Sound::FxServer(void) {
+void Sword2Sound::FxServer(int16 *data, uint len) {
StackLock lock(_mutex);
if (!soundOn)
@@ -274,7 +309,7 @@ void Sword2Sound::FxServer(void) {
if (!music[0]._paused && !music[1]._paused) {
if (compressedMusic == 1)
- UpdateCompSampleStreaming();
+ UpdateCompSampleStreaming(data, len);
}
if (!music[0]._streaming && !music[1]._streaming && fpMus.isOpen())
@@ -846,9 +881,6 @@ int32 Sword2Sound::StreamCompMusic(const char *filename, uint32 musicId, bool lo
int32 Sword2Sound::StreamCompMusicFromLock(const char *filename, uint32 musicId, bool looping) {
int32 primaryStream = -1;
int32 secondaryStream = -1;
- int32 i;
- uint16 *data16;
- uint8 *data8;
compressedMusic = 1;
@@ -861,8 +893,7 @@ int32 Sword2Sound::StreamCompMusicFromLock(const char *filename, uint32 musicId,
else
primaryStream = 1;
- music[primaryStream]._fading = false;
- g_engine->_mixer->stopHandle(music[primaryStream]._handle);
+ music[primaryStream]._fading = 0;
music[primaryStream]._streaming = false;
}
@@ -900,7 +931,7 @@ int32 Sword2Sound::StreamCompMusicFromLock(const char *filename, uint32 musicId,
// Start other music stream fading out
if (secondaryStream != -1 && !music[secondaryStream]._fading)
- music[secondaryStream]._fading = -16;
+ music[secondaryStream]._fading = FADE_SAMPLES;
fpMus.seek((musicId + 1) * 8, SEEK_SET);
music[primaryStream]._filePos = fpMus.readUint32LE();
@@ -912,180 +943,31 @@ int32 Sword2Sound::StreamCompMusicFromLock(const char *filename, uint32 musicId,
// Calculate the file position of the end of the music
music[primaryStream]._fileEnd += music[primaryStream]._filePos;
- // Create a temporary buffer
- data8 = (uint8 *) malloc(bufferSizeMusic / 2);
- if (!data8)
- return RDERR_OUTOFMEMORY;
-
- // Seek to start of the compressed music
- fpMus.seek(music[primaryStream]._filePos, SEEK_SET);
-
- // Read the compressed data in to the buffer
- if ((int32) fpMus.read(data8, bufferSizeMusic / 2) != bufferSizeMusic / 2) {
- free(data8);
- return RDERR_INVALIDID;
- }
-
- // Store the current position in the file for future streaming
- music[primaryStream]._filePos = fpMus.pos();
-
- // decompress the music into the music buffer.
- data16 = (uint16 *) malloc(bufferSizeMusic);
- if (!data16)
- return RDERR_OUTOFMEMORY;
-
- data16[0] = READ_LE_UINT16(data8); // First sample value
-
- for (i = 1; i < (bufferSizeMusic / 2) - 1; 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]));
- }
-
- // Store the value of the last sample ready for next batch of
- // decompression
- music[primaryStream]._lastSample = data16[i - 1];
-
- // Free the compressed sound buffer
- free(data8);
-
- // Modify the volume according to the master volume and music mute
- // state
-
- byte volume = musicMuted ? 0 : musicVolTable[musicVol];
-
-#ifndef SCUMM_BIG_ENDIAN
- // FIXME: Until the mixer supports LE samples natively, we need to
- // convert our LE ones to BE
- for (i = 0; i < bufferSizeMusic / 2; i++)
- data16[i] = SWAP_BYTES_16(data16[i]);
-#endif
-
- g_engine->_mixer->newStream(&music[primaryStream]._handle, data16,
- bufferSizeMusic, 22050, SoundMixer::FLAG_16BITS, 100000, volume, 0);
-
- free(data16);
-
- // Recorder some last variables
+ music[primaryStream]._lastSample = fpMus.readByte();
+ music[primaryStream]._filePos++;
music[primaryStream]._streaming = true;
+
return RD_OK;
}
-void Sword2Sound::UpdateCompSampleStreaming(void) {
- uint32 i,j;
- int32 len;
- uint16 *data16;
- uint8 *data8;
- int fade;
+void Sword2Sound::UpdateCompSampleStreaming(int16 *data, uint len) {
+ uint32 i;
for (i = 0; i < MAXMUS; i++) {
if (!music[i]._streaming)
continue;
- // If the music is fading, adjust the volume for it.
-
- if (music[i]._fading < 0) {
- if (++music[i]._fading == 0) {
- g_engine->_mixer->stopHandle(music[i]._handle);
- music[i]._streaming = false;
- music[i]._looping = false;
- continue;
- }
-
- // Modify the volume according to the master volume
- // and music mute state
-
- byte volume = musicMuted ? 0 : musicVolTable[musicVol * (0 - music[i]._fading ) / 16];
-
- g_engine->_mixer->setChannelVolume(music[i]._handle, volume);
- }
-
- // Re-fill the audio buffer.
-
- len = bufferSizeMusic;
-
- // Reduce length if it requires reading past the end of the
- // music
-
- if (music[i]._filePos + len >= music[i]._fileEnd) {
- // End of music reached so we'll need to fade and
- // repeat
- len = music[i]._fileEnd - music[i]._filePos;
- fade = 1;
- } else
- fade = 0;
-
- if (len > 0) {
- data8 = (uint8 *) malloc(len / 2);
- // Allocate a compressed data buffer
- if (data8 == NULL) {
- g_engine->_mixer->stopHandle(music[i]._handle);
- music[i]._streaming = false;
- music[i]._looping = false;
- continue;
- }
+ // Modify the volume according to the master volume and music
+ // mute state
- // Seek to update position of compressed music when
- // neccassary (probably never occurs)
- if ((int32) fpMus.pos() != music[i]._filePos)
- fpMus.seek(music[i]._filePos, SEEK_SET);
+ byte volume = musicMuted ? 0 : musicVolTable[musicVol];
- // Read the compressed data in to the buffer
- if ((int32) fpMus.read(data8, len / 2) != len / 2) {
- g_engine->_mixer->stopHandle(music[i]._handle);
- free(data8);
- music[i]._streaming = false;
- music[i]._looping = false;
- continue;
- }
-
- // Update the current position in the file for future
- // streaming
-
- music[i]._filePos = fpMus.pos();
-
- // decompress the music into the music buffer.
- data16 = (uint16 *) malloc(len);
-
- // Decompress the first byte using the last
- // decompressed sample
- if (GetCompressedSign(data8[0]))
- data16[0] = music[i]._lastSample - (GetCompressedAmplitude(data8[0]) << GetCompressedShift(data8[0]));
- else
- data16[0] = music[i]._lastSample + (GetCompressedAmplitude(data8[0]) << GetCompressedShift(data8[0]));
-
- for (j = 1; j < (uint32) len / 2; j++) {
- if (GetCompressedSign(data8[j]))
- data16[j] = data16[j - 1] - (GetCompressedAmplitude(data8[j]) << GetCompressedShift(data8[j]));
- else
- data16[j] = data16[j - 1] + (GetCompressedAmplitude(data8[j]) << GetCompressedShift(data8[j]));
- }
-
- music[i]._lastSample = data16[j - 1];
-
-#ifndef SCUMM_BIG_ENDIAN
- // Until the mixer supports LE samples natively, we
- // need to convert our LE ones to BE
- for (int32 y = 0; y < len / 2; y++)
- data16[y] = SWAP_BYTES_16(data16[y]);
-#endif
+ fpMus.seek(music[i]._filePos, SEEK_SET);
+ _converter->flow(music[i], data, len, volume, volume);
- // Paranoid check that seems to be necessary.
- if (len & 1)
- len--;
-
- g_engine->_mixer->appendStream(music[i]._handle, data16, len);
-
- free(data16);
- free(data8);
- }
-
- // End of the music so we need to start fading and start the
- // music again
-
- if (fade) {
- g_engine->_mixer->stopHandle(music[i]._handle);
+ if (music[i].eos()) {
+ // End of the music so we need to start fading and
+ // start the music again
// FIXME: The original code faded the music here, but
// to do that we need to start before we reach the end
@@ -1098,8 +980,10 @@ void Sword2Sound::UpdateCompSampleStreaming(void) {
// musFading[i] = -16;
// Loop if neccassary
- if (music[i]._looping)
+ if (music[i]._looping) {
+ music[i]._streaming = false;
StreamCompMusicFromLock(music[i]._fileName, music[i]._id, music[i]._looping);
+ }
}
}
@@ -1202,7 +1086,7 @@ void Sword2Sound::StopMusic(void) {
for (int i = 0; i < MAXMUS; i++) {
if (music[i]._streaming)
- music[i]._fading = -16;
+ music[i]._fading = FADE_SAMPLES;
else
music[i]._looping = false;
}
@@ -1215,7 +1099,6 @@ int32 Sword2Sound::PauseMusic(void) {
for (int i = 0; i < MAXMUS; i++) {
if (music[i]._streaming) {
music[i]._paused = true;
- g_engine->_mixer->pauseHandle(music[i]._handle, true);
} else {
music[i]._paused = false;
}
@@ -1228,25 +1111,14 @@ int32 Sword2Sound::UnpauseMusic(void) {
StackLock lock(_mutex);
if (soundOn) {
- for (int i = 0; i < MAXMUS; i++) {
- if (music[i]._paused) {
- g_engine->_mixer->pauseHandle(music[i]._handle, false);
- music[i]._paused = false;
- }
- }
+ for (int i = 0; i < MAXMUS; i++)
+ music[i]._paused = false;
}
return RD_OK;
}
void Sword2Sound::SetMusicVolume(uint8 volume) {
- StackLock lock(_mutex);
-
musicVol = volume;
-
- for (int i = 0; i < MAXMUS; i++) {
- if (music[i]._streaming && !music[i]._fading && !musicMuted)
- g_engine->_mixer->setChannelVolume(music[i]._handle, musicVolTable[volume]);
- }
}
uint8 Sword2Sound::GetMusicVolume() {
@@ -1254,22 +1126,7 @@ uint8 Sword2Sound::GetMusicVolume() {
}
void Sword2Sound::MuteMusic(uint8 mute) {
- StackLock lock(_mutex);
-
musicMuted = mute;
-
- for (int i = 0; i < MAXMUS; i++) {
- if (!mute) {
- if (!music[i]._streaming && music[i]._looping)
- StreamCompMusicFromLock(music[i]._fileName, music[i]._id, music[i]._looping);
- }
-
- if (music[i]._streaming && !music[i]._fading) {
- byte volume = mute ? 0 : musicVolTable[musicVol];
-
- g_engine->_mixer->setChannelVolume(music[i]._handle, volume);
- }
- }
}
uint8 Sword2Sound::IsMusicMute(void) {
diff --git a/sword2/driver/d_sound.h b/sword2/driver/d_sound.h
index 4555f0e554..c2d41dc7eb 100644
--- a/sword2/driver/d_sound.h
+++ b/sword2/driver/d_sound.h
@@ -34,7 +34,9 @@
#ifndef D_SOUND_H
#define D_SOUND_H
+#include "sound/audiostream.h"
#include "sound/mixer.h"
+#include "sound/rate.h"
#include "common/file.h"
extern void sword2_sound_handler(void *refCon);
@@ -48,26 +50,38 @@ typedef struct {
uint16 *_buf;
int32 _bufSize;
PlayingSoundHandle _handle;
-} fxHandle;
+} FxHandle;
-typedef struct {
+class MusicHandle : public MusicStream {
+public:
uint32 _id;
char _fileName[256];
bool _streaming;
bool _paused;
bool _looping;
- int16 _fading;
+ int32 _fading;
int32 _filePos;
int32 _fileEnd;
int16 _lastSample;
- PlayingSoundHandle _handle;
-} musicHandle;
+
+ bool isStereo() const { return false; }
+ int getRate() const { return 22050; }
+
+ int16 read();
+ bool eos() const;
+
+ MusicHandle() : MusicStream(), _streaming(false), _paused(false),
+ _looping(false), _fading(0), _filePos(0), _fileEnd(0),
+ _lastSample(0) {
+ _fileName[0] = 0;
+ }
+};
class Sword2Sound {
public:
Sword2Sound(SoundMixer *mixer);
~Sword2Sound();
- void FxServer(void);
+ void FxServer(int16 *data, uint len);
int32 PlaySpeech(uint8 *data, uint8 vol, int8 pan);
int32 PlayCompSpeech(const char *filename, uint32 speechid, uint8 vol, int8 pan);
uint32 PreFetchCompSpeech(const char *filename, uint32 speechid, uint16 **buf);
@@ -104,7 +118,7 @@ class Sword2Sound {
int32 IsFxOpen(int32 id);
int32 SetFxVolumePan(int32 id, uint8 vol, int8 pan);
int32 SetFxIdVolume(int32 id, uint8 vol);
- void UpdateCompSampleStreaming(void);
+ void UpdateCompSampleStreaming(int16 *data, uint len);
SoundMixer *_mixer;
private:
int32 StreamCompMusicFromLock(const char *filename, uint32 musicId, bool looping);
@@ -112,9 +126,10 @@ class Sword2Sound {
int32 DipMusic();
OSystem::MutexRef _mutex;
+ RateConverter *_converter;
- fxHandle fx[MAXFX];
- musicHandle music[MAXMUS];
+ FxHandle fx[MAXFX];
+ MusicHandle music[MAXMUS];
// We used to have two music volumes - one for each channel -
// but they were always set to the same value.
@@ -132,8 +147,6 @@ class Sword2Sound {
uint8 compressedMusic;
PlayingSoundHandle soundHandleSpeech;
- File fpMus;
- int bufferSizeMusic;
uint8 musicMuted;
};
diff --git a/sword2/driver/rdwin.cpp b/sword2/driver/rdwin.cpp
index 2450ee8144..488affb011 100644
--- a/sword2/driver/rdwin.cpp
+++ b/sword2/driver/rdwin.cpp
@@ -116,7 +116,6 @@ int32 CloseAppWindow(void)
DestroyWindow(hwnd);
*/
// just quit for now
- g_engine->_timer->releaseProcedure(sword2_sound_handler);
g_system->quit();
return(RD_OK);