aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm
diff options
context:
space:
mode:
authorFilippos Karapetis2014-07-03 00:14:28 +0300
committerFilippos Karapetis2014-07-04 23:33:43 +0300
commit53d3ee07df6eea863e93620ab4a406eb24fbfef1 (patch)
treea7dd94e4bfa6d953410d4f7392320065900f128f /engines/scumm
parent902a140f3e8058c582496296c7c1e0c55b243dda (diff)
downloadscummvm-rg350-53d3ee07df6eea863e93620ab4a406eb24fbfef1.tar.gz
scummvm-rg350-53d3ee07df6eea863e93620ab4a406eb24fbfef1.tar.bz2
scummvm-rg350-53d3ee07df6eea863e93620ab4a406eb24fbfef1.zip
SCUMM: Add support for CD audio tracks in the Steam versions of Loom
Many Thanks to Ben Castricum for the original patch
Diffstat (limited to 'engines/scumm')
-rw-r--r--engines/scumm/cdda.cpp120
-rw-r--r--engines/scumm/cdda.h58
-rw-r--r--engines/scumm/module.mk1
-rw-r--r--engines/scumm/saveload.cpp2
-rw-r--r--engines/scumm/sound.cpp66
-rw-r--r--engines/scumm/sound.h7
6 files changed, 246 insertions, 8 deletions
diff --git a/engines/scumm/cdda.cpp b/engines/scumm/cdda.cpp
new file mode 100644
index 0000000000..adb414ecce
--- /dev/null
+++ b/engines/scumm/cdda.cpp
@@ -0,0 +1,120 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scumm/cdda.h"
+#include "common/stream.h"
+#include "audio/audiostream.h"
+
+namespace Scumm {
+
+
+#pragma mark -
+#pragma mark --- CDDA stream ---
+#pragma mark -
+
+#define START_OF_CDDA_DATA 800
+#define BLOCK_SIZE 1177
+
+class CDDAStream : public Audio::SeekableAudioStream {
+private:
+ Common::SeekableReadStream *_stream;
+ DisposeAfterUse::Flag _disposeAfterUse;
+ byte _shiftLeft;
+ byte _shiftRight;
+ uint32 _pos;
+ Audio::Timestamp _length;
+
+public:
+ CDDAStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
+ virtual ~CDDAStream();
+
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return true; }
+ int getRate() const { return 44100; }
+ bool endOfData() const { return _stream->eos(); }
+ bool seek(const Audio::Timestamp &where);
+ Audio::Timestamp getLength() const { return _length; }
+};
+
+CDDAStream::CDDAStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) :
+ _stream(stream), _disposeAfterUse(disposeAfterUse), _pos(START_OF_CDDA_DATA) {
+ _stream->seek(START_OF_CDDA_DATA, SEEK_SET);
+ // The total size of CDDA.SOU is 289,808,802 bytes or (289808802 - 800) / 1177 = 246226 blocks
+ // We also deduct the shift values to return the correct length
+ uint32 blocks = (_stream->size() - START_OF_CDDA_DATA) / BLOCK_SIZE;
+ _length = Audio::Timestamp(0, (_stream->size() - START_OF_CDDA_DATA - blocks) / (isStereo() ? 2 : 1), getRate());
+}
+
+CDDAStream::~CDDAStream() {
+ if (_disposeAfterUse == DisposeAfterUse::YES)
+ delete _stream;
+}
+
+bool CDDAStream::seek(const Audio::Timestamp &where) {
+ const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
+ uint32 blocks = seekSample / 1176;
+
+ // Before seeking, read the shift values from the beginning of that block
+ _stream->seek(START_OF_CDDA_DATA + blocks * BLOCK_SIZE, SEEK_SET);
+ byte shiftVal = _stream->readByte();
+ _shiftLeft = shiftVal >> 4;
+ _shiftRight = shiftVal & 0x0F;
+
+ _pos = START_OF_CDDA_DATA + blocks + seekSample;
+ return _stream->seek(_pos, SEEK_SET);
+}
+
+int CDDAStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samples;
+
+ for (samples = 0 ; samples < numSamples && !_stream->eos() ; ) {
+ if (!((_pos - START_OF_CDDA_DATA) % BLOCK_SIZE)) {
+ byte shiftVal = _stream->readByte();
+ _shiftLeft = shiftVal >> 4;
+ _shiftRight = shiftVal & 0x0F;
+ _pos++;
+ }
+ buffer[samples++] = _stream->readSByte() << _shiftLeft;
+ buffer[samples++] = _stream->readSByte() << _shiftRight;
+ _pos += 2;
+ }
+ return samples;
+}
+
+#pragma mark -
+#pragma mark --- CDDA factory functions ---
+#pragma mark -
+
+Audio::SeekableAudioStream *makeCDDAStream(
+ Common::SeekableReadStream *stream,
+ DisposeAfterUse::Flag disposeAfterUse) {
+ Audio::SeekableAudioStream *s = new CDDAStream(stream, disposeAfterUse);
+ if (s && s->endOfData()) {
+ delete s;
+ return 0;
+ } else {
+ return s;
+ }
+ return 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/cdda.h b/engines/scumm/cdda.h
new file mode 100644
index 0000000000..2146777497
--- /dev/null
+++ b/engines/scumm/cdda.h
@@ -0,0 +1,58 @@
+
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/**
+ * @file
+ * CD audio decoder used in the Steam versions of Loom
+ */
+
+#ifndef SCUMM_CDDA_H
+#define SCUMM_CDDA_H
+
+#include "common/types.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Audio {
+class SeekableAudioStream;
+}
+
+namespace Scumm {
+
+/**
+ * Create a new SeekableAudioStream from the CDDA data in the given stream.
+ * Allows for seeking (which is why we require a SeekableReadStream).
+ *
+ * @param stream the SeekableReadStream from which to read the CDDA data
+ * @param disposeAfterUse whether to delete the stream after use
+ * @return a new SeekableAudioStream, or NULL, if an error occurred
+ */
+Audio::SeekableAudioStream *makeCDDAStream(
+ Common::SeekableReadStream *stream,
+ DisposeAfterUse::Flag disposeAfterUse);
+
+} // End of namespace Audio
+
+#endif // #ifndef SCUMM_CDDA_H
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index d43db1e5f1..416a8f7ef9 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
bomp.o \
boxes.o \
camera.o \
+ cdda.o \
charset.o \
charset-fontdata.o \
costume.o \
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 0aaff4c094..7eadb042fb 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -1453,7 +1453,7 @@ void ScummEngine::saveOrLoad(Serializer *s) {
// forever, then resume playing it. This helps a lot when the audio CD
// is used to provide ambient music (see bug #788195).
if (s->isLoading() && info.playing && info.numLoops < 0)
- _system->getAudioCDManager()->play(info.track, info.numLoops, info.start, info.duration);
+ _sound->playCDTrackInternal(info.track, info.numLoops, info.start, info.duration);
}
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 01bdefc7c0..21bf565a4e 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -27,6 +27,7 @@
#include "common/substream.h"
#include "scumm/actor.h"
+#include "scumm/cdda.h"
#include "scumm/file.h"
#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
@@ -36,8 +37,6 @@
#include "scumm/sound.h"
#include "scumm/util.h"
-#include "backends/audiocd/audiocd.h"
-
#include "audio/decoders/adpcm.h"
#include "audio/decoders/flac.h"
#include "audio/mididrv.h"
@@ -89,11 +88,21 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer)
memset(_mouthSyncTimes, 0, sizeof(_mouthSyncTimes));
_musicType = MDT_NONE;
+
+ _loomSteamCD.playing = false;
+ _loomSteamCD.track = 0;
+ _loomSteamCD.start = 0;
+ _loomSteamCD.duration = 0;
+ _loomSteamCD.numLoops = 0;
+ _loomSteamCD.volume = Audio::Mixer::kMaxChannelVolume;
+ _loomSteamCD.balance = 0;
+
+ _isLoomSteam = _vm->_game.id == GID_LOOM && Common::File::exists("CDDA.SOU");
}
Sound::~Sound() {
stopCDTimer();
- g_system->getAudioCDManager()->stop();
+ stopCD();
free(_offsetTable);
}
@@ -1033,7 +1042,7 @@ void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) {
// Play it
if (!_soundsPaused)
- g_system->getAudioCDManager()->play(track, numLoops, startFrame, duration);
+ playCDTrackInternal(track, numLoops, startFrame, duration);
// Start the timer after starting the track. Starting an MP3 track is
// almost instantaneous, but a CD player may take some time. Hopefully
@@ -1041,16 +1050,59 @@ void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) {
startCDTimer();
}
+void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int duration) {
+ _loomSteamCD.track = track;
+ _loomSteamCD.numLoops = numLoops;
+ _loomSteamCD.start = startFrame;
+ _loomSteamCD.duration = duration;
+
+ if (!_isLoomSteam) {
+ g_system->getAudioCDManager()->play(track, numLoops, startFrame, duration);
+ } else {
+ // Stop any currently playing track
+ _mixer->stopHandle(_loomSteamCDAudioHandle);
+
+ Common::File *cddaFile = new Common::File();
+ if (cddaFile->open("CDDA.SOU")) {
+ Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
+ Audio::Timestamp end = Audio::Timestamp(0, startFrame + duration, 75);
+ Audio::SeekableAudioStream *stream = makeCDDAStream(cddaFile, DisposeAfterUse::YES);
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_loomSteamCDAudioHandle,
+ Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops));
+ } else {
+ delete cddaFile;
+ }
+ }
+}
+
void Sound::stopCD() {
- g_system->getAudioCDManager()->stop();
+ if (!_isLoomSteam)
+ g_system->getAudioCDManager()->stop();
+ else
+ _mixer->stopHandle(_loomSteamCDAudioHandle);
}
int Sound::pollCD() const {
- return g_system->getAudioCDManager()->isPlaying();
+ if (!_isLoomSteam)
+ return g_system->getAudioCDManager()->isPlaying();
+ else
+ return _mixer->isSoundHandleActive(_loomSteamCDAudioHandle);
}
void Sound::updateCD() {
- g_system->getAudioCDManager()->updateCD();
+ if (!_isLoomSteam)
+ g_system->getAudioCDManager()->updateCD();
+}
+
+AudioCDManager::Status Sound::getCDStatus() {
+ if (!_isLoomSteam)
+ return g_system->getAudioCDManager()->getStatus();
+ else {
+ AudioCDManager::Status info = _loomSteamCD;
+ _loomSteamCD.playing = _mixer->isSoundHandleActive(_loomSteamCDAudioHandle);
+ return info;
+ }
}
void Sound::saveLoadWithSerializer(Serializer *ser) {
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index a96d2b8ed5..a479ad5731 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -27,6 +27,7 @@
#include "audio/audiostream.h"
#include "audio/mididrv.h"
#include "audio/mixer.h"
+#include "backends/audiocd/audiocd.h"
#include "scumm/saveload.h"
namespace Audio {
@@ -86,6 +87,10 @@ protected:
int16 _currentCDSound;
int16 _currentMusic;
+ Audio::SoundHandle _loomSteamCDAudioHandle;
+ bool _isLoomSteam;
+ AudioCDManager::Status _loomSteamCD;
+
public:
Audio::SoundHandle _talkChannelHandle; // Handle of mixer channel actor is talking on
@@ -119,9 +124,11 @@ public:
void stopCDTimer();
void playCDTrack(int track, int numLoops, int startFrame, int duration);
+ void playCDTrackInternal(int track, int numLoops, int startFrame, int duration);
void stopCD();
int pollCD() const;
void updateCD();
+ AudioCDManager::Status getCDStatus();
int getCurrentCDSound() const { return _currentCDSound; }
// Used by the save/load system: