aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Göffringmann2004-10-12 15:50:00 +0000
committerRobert Göffringmann2004-10-12 15:50:00 +0000
commitf5403e079fbfa5348d1e9f084ed215dc40314655 (patch)
tree26e26226a62300da230ace3ba52490581bc85d59
parentbeb1ed459892216f1655c6041adf25eb2bf9ae83 (diff)
downloadscummvm-rg350-f5403e079fbfa5348d1e9f084ed215dc40314655.tar.gz
scummvm-rg350-f5403e079fbfa5348d1e9f084ed215dc40314655.tar.bz2
scummvm-rg350-f5403e079fbfa5348d1e9f084ed215dc40314655.zip
added mp3 and ogg vorbis playback for music and speech.
compression tool will follow soon. svn-id: r15531
-rw-r--r--sword1/music.cpp201
-rw-r--r--sword1/music.h36
-rw-r--r--sword1/sound.cpp71
-rw-r--r--sword1/sound.h11
-rw-r--r--sword1/sword1.cpp2
5 files changed, 260 insertions, 61 deletions
diff --git a/sword1/music.cpp b/sword1/music.cpp
index b8b6d010fe..f49704a50d 100644
--- a/sword1/music.cpp
+++ b/sword1/music.cpp
@@ -24,15 +24,122 @@
#include "sound/mixer.h"
#include "common/util.h"
#include "common/file.h"
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
namespace Sword1 {
+WaveAudioStream *makeWaveStream(File *source, uint32 size) {
+ return new WaveAudioStream(source, size);
+}
+
+WaveAudioStream::WaveAudioStream(File *source, uint32 pSize) {
+ uint32 size;
+ uint8 wavHeader[WAVEHEADERSIZE];
+
+ _sourceFile = source;
+ _sourceFile->incRef();
+ if (_sourceFile->isOpen()) {
+ _sourceFile->read(wavHeader, WAVEHEADERSIZE);
+ _isStereo = (READ_LE_UINT16(wavHeader + 0x16) == 2);
+ _rate = READ_LE_UINT16(wavHeader + 0x18);
+ size = ((pSize) ? pSize : READ_LE_UINT32(wavHeader + 0x28));
+ assert(size <= (source->size() - source->pos()));
+ _bitsPerSample = READ_LE_UINT16(wavHeader + 0x22);
+ _samplesLeft = (size * 8) / _bitsPerSample;
+ if ((_bitsPerSample != 16) && (_bitsPerSample != 8))
+ error("WaveAudioStream: unknown wave type");
+ } else {
+ _samplesLeft = 0;
+ _isStereo = false;
+ _bitsPerSample = 16;
+ _rate = 22050;
+ }
+}
+
+WaveAudioStream::~WaveAudioStream(void) {
+ _sourceFile->decRef();
+}
+
+int WaveAudioStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samples = ((int)_samplesLeft < numSamples) ? (int)_samplesLeft : numSamples;
+ if (_bitsPerSample == 16)
+ for (int cnt = 0; cnt < samples; cnt++)
+ *buffer++ = (int16)_sourceFile->readUint16LE();
+ else
+ for (int cnt = 0; cnt < samples; cnt++)
+ *buffer++ = (int16)_sourceFile->readByte() << 8;
+ _samplesLeft -= samples;
+ return samples;
+}
+
+bool WaveAudioStream::endOfData(void) const {
+ if (_samplesLeft == 0)
+ return true;
+ else
+ return false;
+}
+
// This means fading takes 3 seconds.
#define FADE_LENGTH 3
// These functions are only called from Music, so I'm just going to
// assume that if locking is needed it has already been taken care of.
+AudioStream *MusicHandle::createAudioSource(void) {
+ _file.seek(0);
+ switch (_musicMode) {
+#ifdef USE_MAD
+ case MusicMp3:
+ printf("creating mp3 stream\n");
+ return makeMP3Stream(&_file, _file.size());
+#endif
+#ifdef USE_VORBIS
+ case MusicVorbis:
+ return makeVorbisStream(&_file, _file.size());
+#endif
+ case MusicWave:
+ return makeWaveStream(&_file, 0);
+ case MusicNone: // shouldn't happen
+ warning("createAudioSource ran into null create\n");
+ return NULL;
+ default:
+ error("MusicHandle::createAudioSource: called with illegal MusicMode");
+ }
+ return NULL; // never reached
+}
+
+bool MusicHandle::play(const char *fileBase, bool loop) {
+ char fileName[30];
+ stop();
+ _musicMode = MusicNone;
+#ifdef USE_MAD
+ sprintf(fileName, "%s.mp3", fileBase);
+ if (_file.open(fileName))
+ _musicMode = MusicMp3;
+#endif
+#ifdef USE_VORBIS
+ if (!_file.isOpen()) { // mp3 doesn't exist (or not compiled with MAD support)
+ sprintf(fileName, "%s.ogg", fileBase);
+ if (_file.open(fileName))
+ _musicMode = MusicVorbis;
+ }
+#endif
+ if (!_file.isOpen()) {
+ sprintf(fileName, "%s.wav", fileBase);
+ if (_file.open(fileName))
+ _musicMode = MusicWave;
+ else {
+ warning("Music file %s could not be opened", fileName);
+ return false;
+ }
+ }
+ _audioSource = createAudioSource();
+ _looping = loop;
+ fadeUp();
+ return true;
+}
+
void MusicHandle::fadeDown() {
if (streaming()) {
if (_fading < 0)
@@ -57,53 +164,67 @@ bool MusicHandle::endOfData() const {
return !streaming();
}
+// is we don't have an audiosource, return some dummy values.
+// shouldn't happen anyways.
+bool MusicHandle::streaming(void) const {
+ return (_audioSource) ? (!_audioSource->endOfStream()) : false;
+}
+
+bool MusicHandle::isStereo(void) const {
+ return (_audioSource) ? _audioSource->isStereo() : false;
+}
+
+int MusicHandle::getRate(void) const {
+ return (_audioSource) ? _audioSource->getRate() : 11025;
+}
+
int MusicHandle::readBuffer(int16 *buffer, const int numSamples) {
- int samples;
- for (samples = 0; samples < numSamples && !endOfData(); samples++) {
- int16 sample = _file.readUint16LE();
- if (_file.ioFailed()) {
- if (!_looping) {
- stop();
- sample = 0;
- } else {
- _file.clearIOFailed();
- _file.seek(WAVEHEADERSIZE);
- sample = _file.readUint16LE();
+ int totalSamples = 0;
+ int16 *bufStart = buffer;
+ if (!_audioSource)
+ return 0;
+ int expectedSamples = numSamples;
+ while ((expectedSamples > 0) && _audioSource) { // _audioSource becomes NULL if we reach EOF and aren't looping
+ int samplesReturned = _audioSource->readBuffer(buffer, expectedSamples);
+ buffer += samplesReturned;
+ totalSamples += samplesReturned;
+ expectedSamples -= samplesReturned;
+ if ((expectedSamples > 0) && _audioSource->endOfData()) {
+ debug(2, "Music reached EOF");
+ _audioSource->endOfData();
+ if (_looping) {
+ delete _audioSource; // recreate same source.
+ _audioSource = createAudioSource();
}
+ if ((!_looping) || (!_audioSource))
+ stop();
}
- if (_fading > 0) {
- if (--_fading == 0) {
- _looping = false;
- _file.close();
- }
- sample = (sample * _fading) / _fadeSamples;
- } else if (_fading < 0) {
- _fading--;
- sample = -(sample * _fading) / _fadeSamples;
- if (_fading <= -_fadeSamples)
- _fading = 0;
+ }
+ // buffer was filled, now do the fading (if necessary)
+ int samplePos = 0;
+ while ((_fading > 0) && (samplePos < totalSamples)) { // fade down
+ bufStart[samplePos] = (bufStart[samplePos] * --_fading) / _fadeSamples;
+ samplePos++;
+ if (_fading == 0) {
+ stop();
+ // clear the rest of the buffer
+ memset(bufStart + samplePos, 0, (totalSamples - samplePos) * 2);
+ return samplePos;
}
- *buffer++ = sample;
}
- return samples;
-}
-
-bool MusicHandle::play(const char *filename, bool loop) {
- uint8 wavHeader[WAVEHEADERSIZE];
- stop();
- if (!_file.open(filename)) {
- warning("Music file %s could not be opened", filename);
- return false;
+ while ((_fading < 0) && (samplePos < totalSamples)) { // fade up
+ bufStart[samplePos] = -(bufStart[samplePos] * --_fading) / _fadeSamples;
+ if (_fading <= -_fadeSamples)
+ _fading = 0;
}
- _file.read(wavHeader, WAVEHEADERSIZE);
- _stereo = (READ_LE_UINT16(wavHeader + 0x16) == 2);
- _rate = READ_LE_UINT16(wavHeader + 0x18);
- _looping = loop;
- fadeUp();
- return true;
+ return totalSamples;
}
void MusicHandle::stop() {
+ if (_audioSource) {
+ delete _audioSource;
+ _audioSource = NULL;
+ }
if (_file.isOpen())
_file.close();
_fading = 0;
@@ -183,9 +304,7 @@ void Music::startMusic(int32 tuneId, int32 loopFlag) {
_handles[1].fadeDown();
newStream = 0;
}
- char fName[20];
- sprintf(fName, "%s.wav", _tuneList[tuneId]);
- if (_handles[newStream].play(fName, loopFlag != 0)) {
+ if (_handles[newStream].play(_tuneList[tuneId], loopFlag != 0)) {
delete _converter[newStream];
_converter[newStream] = makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), false);
}
diff --git a/sword1/music.h b/sword1/music.h
index c050da4b2b..bb922b24a2 100644
--- a/sword1/music.h
+++ b/sword1/music.h
@@ -36,27 +36,51 @@ namespace Sword1 {
#define WAVEHEADERSIZE 0x2C
+enum MusicMode {
+ MusicNone = 0,
+ MusicWave,
+ MusicMp3,
+ MusicVorbis
+};
+
+class WaveAudioStream : public AudioStream {
+public:
+ WaveAudioStream(File *source, uint32 pSize);
+ virtual ~WaveAudioStream();
+ virtual int readBuffer(int16 *buffer, const int numSamples);
+ virtual bool isStereo(void) const { return _isStereo; };
+ virtual bool endOfData(void) const;
+ virtual int getRate(void) const { return _rate; };
+private:
+ File *_sourceFile;
+ uint32 _rate;
+ bool _isStereo;
+ uint32 _samplesLeft;
+ uint16 _bitsPerSample;
+};
+
class MusicHandle : public AudioStream {
private:
File _file;
bool _looping;
int32 _fading;
int32 _fadeSamples;
- int _rate;
- bool _stereo;
+ MusicMode _musicMode;
+ AudioStream *_audioSource;
+ AudioStream *createAudioSource(void);
public:
- MusicHandle() : _looping(false), _fading(0) {}
+ MusicHandle() : _looping(false), _fading(0), _audioSource(NULL) {}
virtual int readBuffer(int16 *buffer, const int numSamples);
bool play(const char *filename, bool loop);
void stop();
void fadeUp();
void fadeDown();
- bool streaming() const { return _file.isOpen(); }
+ bool streaming() const;
int32 fading() { return _fading; }
bool endOfData() const;
bool endOfStream() const { return false; }
- bool isStereo() const { return _stereo; }
- int getRate() const { return _rate; }
+ bool isStereo() const;
+ int getRate() const;
};
class Music {
diff --git a/sword1/sound.cpp b/sword1/sound.cpp
index 3c752f887e..1dbd6b7538 100644
--- a/sword1/sound.cpp
+++ b/sword1/sound.cpp
@@ -31,7 +31,7 @@ namespace Sword1 {
#define SOUND_SPEECH_ID 1
#define SPEECH_FLAGS (SoundMixer::FLAG_16BITS | SoundMixer::FLAG_AUTOFREE | SoundMixer::FLAG_LITTLE_ENDIAN)
-Sound::Sound(const char *searchPath, SoundMixer *mixer, ResMan *pResMan, bool isDemo) {
+Sound::Sound(const char *searchPath, SoundMixer *mixer, ResMan *pResMan) {
strcpy(_filePath, searchPath);
_mixer = mixer;
_resMan = pResMan;
@@ -39,7 +39,6 @@ Sound::Sound(const char *searchPath, SoundMixer *mixer, ResMan *pResMan, bool is
_endOfQueue = 0;
_currentCowFile = 0;
_speechVolL = _speechVolR = _sfxVolL = _sfxVolR = 192;
- _isDemo = isDemo;
}
int Sound::addToQueue(int32 fxNo) {
@@ -167,12 +166,35 @@ bool Sound::startSpeech(uint16 roomNo, uint16 localNo) {
uint32 index = _cowHeader[locIndex + (localNo * 2) - 1];
debug(6, "startSpeech(%d, %d): locIndex %d, sampleSize %d, index %d", roomNo, localNo, locIndex, sampleSize, index);
if (sampleSize) {
- uint32 size;
- int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size);
uint8 speechVol = (_speechVolR + _speechVolL) / 2;
int8 speechPan = (_speechVolR - _speechVolL) / 2;
- if (data)
- _mixer->playRaw(&_speechHandle, data, size, 11025, SPEECH_FLAGS, SOUND_SPEECH_ID, speechVol, speechPan);
+ if (_cowMode == CowWave) {
+ uint32 size;
+ int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size);
+ if (data)
+ _mixer->playRaw(&_speechHandle, data, size, 11025, SPEECH_FLAGS, SOUND_SPEECH_ID, speechVol, speechPan);
+ }
+#ifdef USE_MAD
+ else if (_cowMode == CowMp3) {
+ warning("playing mp3: Sample(%d/%d) Index %X, Size %d", roomNo, localNo, index, sampleSize);
+ _cowFile.seek(index);
+ _mixer->playMP3(&_speechHandle, &_cowFile, sampleSize, speechVol, speechPan, SOUND_SPEECH_ID);
+ // with compressed audio, we can't calculate the wave volume.
+ // so default to talking.
+ for (int cnt = 0; cnt < 480; cnt++)
+ _waveVolume[cnt] = true;
+ _waveVolPos = 0;
+ }
+#endif
+#ifdef USE_VORBIS
+ else if (_cowMode == CowVorbis) {
+ _cowFile.seek(index);
+ _mixer->playVorbis(&_speechHandle, &_cowFile, sampleSize, speechVol, speechPan, SOUND_SPEECH_ID);
+ for (int cnt = 0; cnt < 480; cnt++)
+ _waveVolume[cnt] = true;
+ _waveVolPos = 0;
+ }
+#endif
return true;
} else
return false;
@@ -187,8 +209,7 @@ int16 *Sound::uncompressSpeech(uint32 index, uint32 cSize, uint32 *size) {
headerPos++;
if (headerPos < 100) {
int32 resSize;
- // Demo uses slightly different headers
- if (_isDemo) {
+ if (_cowMode == CowDemo) { // Demo uses slightly different headers
resSize = READ_LE_UINT32(fBuf + headerPos + 6) >> 1;
headerPos += 2;
} else
@@ -259,12 +280,40 @@ void Sound::initCowSystem(void) {
/* look for speech1/2.clu in the data dir
and speech/speech.clu (running from cd or using cd layout)
*/
- sprintf(cowName, "SPEECH%d.CLU", SwordEngine::_systemVars.currentCD);
+#ifdef USE_MAD
+ sprintf(cowName, "SPEECH%d.CL3", SwordEngine::_systemVars.currentCD);
_cowFile.open(cowName);
+ if (_cowFile.isOpen()) {
+ debug(1, "Using MP3 compressed Speech Cluster");
+ _cowMode = CowMp3;
+ }
+#endif
+#ifdef USE_VORBIS
+ if (!_cowFile.isOpen()) {
+ sprintf(cowName, "SPEECH%d.CLV", SwordEngine::_systemVars.currentCD);
+ _cowFile.open(cowName);
+ if (_cowFile.isOpen()) {
+ debug(1, "Using Vorbis compressed Speech Cluster");
+ _cowMode = CowVorbis;
+ }
+ }
+#endif
+ if (!_cowFile.isOpen()) {
+ sprintf(cowName, "SPEECH%d.CLU", SwordEngine::_systemVars.currentCD);
+ _cowFile.open(cowName);
+ if (!_cowFile.isOpen()) {
+ _cowFile.open("speech.clu");
+ }
+ debug(1, "Using uncompressed Speech Cluster");
+ _cowMode = CowWave;
+ }
if (!_cowFile.isOpen())
_cowFile.open("speech.clu");
- if (!_cowFile.isOpen())
- _cowFile.open("cows.mad");
+ if (!_cowFile.isOpen()) {
+ _cowFile.open("cows.mad");
+ if (_cowFile.isOpen())
+ _cowMode = CowDemo;
+ }
if (_cowFile.isOpen()) {
_cowHeaderSize = _cowFile.readUint32LE();
_cowHeader = (uint32*)malloc(_cowHeaderSize);
diff --git a/sword1/sound.h b/sword1/sound.h
index df0d17376c..ece8f7e3e2 100644
--- a/sword1/sound.h
+++ b/sword1/sound.h
@@ -58,9 +58,16 @@ class ResMan;
#define WAVE_VOL_TAB_LENGTH 480
#define WAVE_VOL_THRESHOLD 190000 //120000
+enum CowMode {
+ CowWave = 0,
+ CowMp3,
+ CowVorbis,
+ CowDemo
+};
+
class Sound {
public:
- Sound(const char *searchPath, SoundMixer *mixer, ResMan *pResMan, bool isDemo);
+ Sound(const char *searchPath, SoundMixer *mixer, ResMan *pResMan);
~Sound(void);
void setSpeechVol(uint8 volL, uint8 volR) { _speechVolL = volL; _speechVolR = volR; };
void setSfxVol(uint8 volL, uint8 volR) { _sfxVolL = volL; _sfxVolR = volR; };
@@ -93,6 +100,7 @@ private:
uint32 *_cowHeader;
uint32 _cowHeaderSize;
uint8 _currentCowFile;
+ CowMode _cowMode;
PlayingSoundHandle _speechHandle, _fxHandle;
Common::RandomSource _rnd;
@@ -100,7 +108,6 @@ private:
uint8 _endOfQueue;
SoundMixer *_mixer;
ResMan *_resMan;
- bool _isDemo;
char _filePath[100];
static const char _musicList[270];
static const uint16 _roomsFixedFx[TOTAL_ROOMS][TOTAL_FX_PER_ROOM];
diff --git a/sword1/sword1.cpp b/sword1/sword1.cpp
index bc6e787b5a..28ea10326c 100644
--- a/sword1/sword1.cpp
+++ b/sword1/sword1.cpp
@@ -138,7 +138,7 @@ void SwordEngine::initialize(void) {
_mouse = new Mouse(_system, _resMan, _objectMan);
_screen = new Screen(_system, _resMan, _objectMan);
_music = new Music(_system, _mixer);
- _sound = new Sound("", _mixer, _resMan, (_features & GF_DEMO) != 0);
+ _sound = new Sound("", _mixer, _resMan);
_menu = new Menu(_screen, _mouse);
_logic = new Logic(_objectMan, _resMan, _screen, _mouse, _sound, _music, _menu, _system, _mixer);
_mouse->useLogicAndMenu(_logic, _menu);